Remove remote editing from the Git sidebar
This commit is contained in:
@@ -27,7 +27,7 @@ The project is under active development. Core blogging workflows are broadly ava
|
||||
- Optional one-shot AI translation, description, analysis, taxonomy, and language-detection operations run in background tasks with editor-level waiting indicators, using provider-portable JSON-only requests through independent online and local OpenAI-compatible profiles. Each profile has secure credentials, persistently discovered chat/title/image model selections, explicit tool/vision overrides, chat testing, and restart-persistent status-bar airplane-mode routing.
|
||||
- Persistent conversational AI with safe Markdown, streamed and cancellable responses, model/session/token tracking, bounded project-aware blog tools, and localized conversation management in the Chat workspace. Allowlisted render tools add persistent native cards, charts, forms, lists, metrics, mind maps, tables, and tabs without executing assistant-provided HTML or JavaScript.
|
||||
- SSH-agent-based SCP or rsync publishing.
|
||||
- Integrated Git workflow with repository initialization, Git LFS image tracking, status and diffs, branch/file history, commits, remotes, cancellable fetch/pull/push, and post-pull filesystem reconciliation; network actions respect airplane mode.
|
||||
- Integrated Git workflow for each blog project's current repository, with repository initialization, read-only origin discovery, Git LFS image tracking, status and diffs, branch/file history, commits, cancellable fetch/pull/push, and post-pull filesystem reconciliation; network actions respect airplane mode.
|
||||
- Site, media, and translation validation plus `ruds://new-post` Blogmark capture and Lua transforms; captures open directly in the post editor and defer automatic translation until an explicit manual save, which queues one task per still-missing language. Publishing never starts automatic translation. bDS2 keeps its separate `bds2://` bookmarklet protocol.
|
||||
|
||||
RuDS uses no JavaScript application runtime and loads no CSS or JavaScript from CDNs. The preview is served by the Rust application and displayed by the operating-system webview.
|
||||
|
||||
@@ -502,22 +502,6 @@ impl GitEngine {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_remote(&self, remote_url: &str) -> Result<(), GitError> {
|
||||
let remote_url = remote_url.trim();
|
||||
if remote_url.is_empty() {
|
||||
return Err(GitError::Validation("remote URL is required".into()));
|
||||
}
|
||||
let args = if self
|
||||
.optional_output(&["remote", "get-url", "origin"], GitOperation::Remote)?
|
||||
.is_some()
|
||||
{
|
||||
["remote", "set-url", "origin", remote_url]
|
||||
} else {
|
||||
["remote", "add", "origin", remote_url]
|
||||
};
|
||||
self.run_local(&args, GitOperation::Remote).map(|_| ())
|
||||
}
|
||||
|
||||
pub fn commit_all(&self, message: &str) -> Result<NetworkResult, GitError> {
|
||||
let message = message.trim();
|
||||
if message.is_empty() {
|
||||
|
||||
@@ -134,7 +134,10 @@ fn remote_state_history_fetch_and_fast_forward_pull_use_real_refs() {
|
||||
git(&local, &["add", "-A"]);
|
||||
git(&local, &["commit", "-m", "local one"]);
|
||||
let engine = GitEngine::new(&local);
|
||||
engine.set_remote(remote.to_str().unwrap()).unwrap();
|
||||
git(
|
||||
&local,
|
||||
&["remote", "add", "origin", remote.to_str().unwrap()],
|
||||
);
|
||||
git(&local, &["push", "-u", "origin", "master"]);
|
||||
|
||||
git(
|
||||
|
||||
@@ -311,10 +311,8 @@ pub enum Message {
|
||||
repository_dir: PathBuf,
|
||||
result: Result<GitSnapshot, String>,
|
||||
},
|
||||
GitRemoteInputChanged(String),
|
||||
GitCommitMessageChanged(String),
|
||||
GitInitialize,
|
||||
GitSetRemote,
|
||||
GitCommit,
|
||||
GitFetch,
|
||||
GitPull,
|
||||
@@ -2855,10 +2853,8 @@ impl BdsApp {
|
||||
// ── Git ──
|
||||
message @ (Message::GitRefresh
|
||||
| Message::GitLoaded { .. }
|
||||
| Message::GitRemoteInputChanged(_)
|
||||
| Message::GitCommitMessageChanged(_)
|
||||
| Message::GitInitialize
|
||||
| Message::GitSetRemote
|
||||
| Message::GitCommit
|
||||
| Message::GitFetch
|
||||
| Message::GitPull
|
||||
|
||||
@@ -25,30 +25,14 @@ impl BdsApp {
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GitRemoteInputChanged(value) => {
|
||||
self.git_state.remote_input = value;
|
||||
Task::none()
|
||||
}
|
||||
Message::GitCommitMessageChanged(value) => {
|
||||
self.git_state.commit_message = value;
|
||||
Task::none()
|
||||
}
|
||||
Message::GitInitialize => {
|
||||
let remote = self.git_state.remote_input.trim().to_string();
|
||||
self.run_local_git_action(GitOperation::Initialize, move |engine| {
|
||||
engine.initialize()?;
|
||||
if !remote.is_empty() {
|
||||
engine.set_remote(&remote)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Message::GitSetRemote => {
|
||||
let remote = self.git_state.remote_input.trim().to_string();
|
||||
self.run_local_git_action(GitOperation::Remote, move |engine| {
|
||||
engine.set_remote(&remote)
|
||||
})
|
||||
}
|
||||
Message::GitInitialize => self
|
||||
.run_local_git_action(GitOperation::Initialize, |engine| {
|
||||
engine.initialize().map(|_| ())
|
||||
}),
|
||||
Message::GitCommit => {
|
||||
let message = self.git_state.commit_message.clone();
|
||||
self.run_local_git_action(GitOperation::Commit, move |engine| {
|
||||
|
||||
@@ -35,7 +35,6 @@ pub struct GitUiState {
|
||||
pub files: Vec<GitFileStatus>,
|
||||
pub history: Vec<GitCommit>,
|
||||
pub remote: GitRemoteState,
|
||||
pub remote_input: String,
|
||||
pub commit_message: String,
|
||||
pub loading: bool,
|
||||
pub error: Option<String>,
|
||||
@@ -61,7 +60,6 @@ impl Default for GitUiState {
|
||||
ahead: 0,
|
||||
behind: 0,
|
||||
},
|
||||
remote_input: String::new(),
|
||||
commit_message: String::new(),
|
||||
loading: false,
|
||||
error: None,
|
||||
@@ -72,7 +70,6 @@ impl Default for GitUiState {
|
||||
|
||||
impl GitUiState {
|
||||
pub fn apply_snapshot(&mut self, snapshot: GitSnapshot) {
|
||||
self.remote_input = snapshot.repository.remote_url.clone().unwrap_or_default();
|
||||
self.repository = snapshot.repository;
|
||||
self.files = snapshot.files;
|
||||
self.history = snapshot.history;
|
||||
@@ -151,11 +148,6 @@ pub fn sidebar_view(
|
||||
text(t(locale, "git.notRepository"))
|
||||
.size(12)
|
||||
.color(Color::from_rgb(0.6, 0.6, 0.65)),
|
||||
text_input(&t(locale, "git.remoteOptional"), &state.remote_input)
|
||||
.on_input(Message::GitRemoteInputChanged)
|
||||
.size(12)
|
||||
.padding([6, 8])
|
||||
.style(inputs::field_style),
|
||||
button(text(t(locale, "git.initialize")).size(12))
|
||||
.on_press(Message::GitInitialize)
|
||||
.padding([5, 8])
|
||||
@@ -209,21 +201,6 @@ pub fn sidebar_view(
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
content.push(
|
||||
row![
|
||||
text_input(&t(locale, "git.remoteUrl"), &state.remote_input)
|
||||
.on_input(Message::GitRemoteInputChanged)
|
||||
.size(11)
|
||||
.padding([5, 7])
|
||||
.style(inputs::field_style),
|
||||
button(text(t(locale, "git.saveRemote")).size(11))
|
||||
.on_press(Message::GitSetRemote)
|
||||
.padding([5, 7])
|
||||
.style(inputs::secondary_button),
|
||||
]
|
||||
.spacing(4)
|
||||
.into(),
|
||||
);
|
||||
content.push(section_title(
|
||||
tw(
|
||||
locale,
|
||||
@@ -529,3 +506,32 @@ impl fmt::Display for PathTitle<'_> {
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use iced::advanced::widget::{Tree, tree::Tag};
|
||||
use iced::widget::text_input;
|
||||
|
||||
type TextInputState =
|
||||
text_input::State<<iced::Renderer as iced::advanced::text::Renderer>::Paragraph>;
|
||||
|
||||
fn count_text_inputs(tree: &Tree) -> usize {
|
||||
usize::from(tree.tag == Tag::of::<TextInputState>())
|
||||
+ tree.children.iter().map(count_text_inputs).sum::<usize>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repository_location_is_never_editable_in_the_git_sidebar() {
|
||||
let not_initialized = GitUiState::default();
|
||||
let inactive_tree =
|
||||
Tree::new(sidebar_view(¬_initialized, false, UiLocale::En).as_widget());
|
||||
assert_eq!(count_text_inputs(&inactive_tree), 0);
|
||||
|
||||
let mut initialized = GitUiState::default();
|
||||
initialized.repository.is_initialized = true;
|
||||
initialized.repository.remote_url = Some("ssh://example.test/blog.git".into());
|
||||
let active_tree = Tree::new(sidebar_view(&initialized, false, UiLocale::En).as_widget());
|
||||
assert_eq!(count_text_inputs(&active_tree), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,14 +700,12 @@ find-noMatches = Keine Treffer gefunden
|
||||
find-replacedCount = { $count } Treffer ersetzt
|
||||
git-loading = Repository wird geladen…
|
||||
git-notRepository = Dieses Projekt ist noch kein Git-Repository.
|
||||
git-remoteOptional = Remote-URL (optional)
|
||||
git-initialize = Git initialisieren
|
||||
git-fetch = Abrufen
|
||||
git-pull = Herunterladen
|
||||
git-push = Hochladen
|
||||
git-airplaneBlocked = Netzwerk-Git-Aktionen sind im Flugmodus nicht verfügbar.
|
||||
git-remoteUrl = Origin-URL
|
||||
git-saveRemote = Speichern
|
||||
git-changesCount = Änderungen ({ $count })
|
||||
git-commitMessage = Commit-Nachricht
|
||||
git-commit = Commit
|
||||
|
||||
@@ -700,14 +700,12 @@ find-noMatches = No matches found
|
||||
find-replacedCount = Replaced { $count } matches
|
||||
git-loading = Loading repository…
|
||||
git-notRepository = This project is not a Git repository yet.
|
||||
git-remoteOptional = Remote URL (optional)
|
||||
git-initialize = Initialize Git
|
||||
git-fetch = Fetch
|
||||
git-pull = Pull
|
||||
git-push = Push
|
||||
git-airplaneBlocked = Network Git actions are unavailable in airplane mode.
|
||||
git-remoteUrl = Origin URL
|
||||
git-saveRemote = Save
|
||||
git-changesCount = Changes ({ $count })
|
||||
git-commitMessage = Commit message
|
||||
git-commit = Commit
|
||||
|
||||
@@ -700,14 +700,12 @@ find-noMatches = No se encontraron coincidencias
|
||||
find-replacedCount = Se reemplazaron { $count } coincidencias
|
||||
git-loading = Cargando repositorio…
|
||||
git-notRepository = Este proyecto aún no es un repositorio Git.
|
||||
git-remoteOptional = URL remota (opcional)
|
||||
git-initialize = Inicializar Git
|
||||
git-fetch = Obtener
|
||||
git-pull = Descargar
|
||||
git-push = Subir
|
||||
git-airplaneBlocked = Las acciones Git de red no están disponibles en modo avión.
|
||||
git-remoteUrl = URL de origin
|
||||
git-saveRemote = Guardar
|
||||
git-changesCount = Cambios ({ $count })
|
||||
git-commitMessage = Mensaje de commit
|
||||
git-commit = Commit
|
||||
|
||||
@@ -700,14 +700,12 @@ find-noMatches = Aucun résultat
|
||||
find-replacedCount = { $count } résultats remplacés
|
||||
git-loading = Chargement du dépôt…
|
||||
git-notRepository = Ce projet n’est pas encore un dépôt Git.
|
||||
git-remoteOptional = URL distante (facultative)
|
||||
git-initialize = Initialiser Git
|
||||
git-fetch = Récupérer
|
||||
git-pull = Tirer
|
||||
git-push = Pousser
|
||||
git-airplaneBlocked = Les actions Git réseau sont indisponibles en mode avion.
|
||||
git-remoteUrl = URL d’origine
|
||||
git-saveRemote = Enregistrer
|
||||
git-changesCount = Modifications ({ $count })
|
||||
git-commitMessage = Message de commit
|
||||
git-commit = Commit
|
||||
|
||||
@@ -700,14 +700,12 @@ find-noMatches = Nessuna corrispondenza
|
||||
find-replacedCount = Sostituite { $count } corrispondenze
|
||||
git-loading = Caricamento del repository…
|
||||
git-notRepository = Questo progetto non è ancora un repository Git.
|
||||
git-remoteOptional = URL remoto (facoltativo)
|
||||
git-initialize = Inizializza Git
|
||||
git-fetch = Recupera
|
||||
git-pull = Scarica
|
||||
git-push = Carica
|
||||
git-airplaneBlocked = Le azioni Git di rete non sono disponibili in modalità aereo.
|
||||
git-remoteUrl = URL origin
|
||||
git-saveRemote = Salva
|
||||
git-changesCount = Modifiche ({ $count })
|
||||
git-commitMessage = Messaggio di commit
|
||||
git-commit = Commit
|
||||
|
||||
@@ -87,7 +87,6 @@ surface GitControlSurface {
|
||||
GitHistoryRequested(project, branch)
|
||||
GitFileHistoryRequested(project, file_path)
|
||||
GitRemoteStateRequested(project)
|
||||
GitRemoteSetRequested(project, remote_url)
|
||||
GitFetchRequested(project)
|
||||
GitPullRequested(project)
|
||||
GitPushRequested(project)
|
||||
@@ -153,12 +152,6 @@ rule GetRemoteState {
|
||||
ensures: GitRemoteStateReport(project, local_branch, upstream_branch, ahead, behind)
|
||||
}
|
||||
|
||||
rule SetRemote {
|
||||
when: GitRemoteSetRequested(project, remote_url)
|
||||
-- Creates origin when absent and replaces its URL when already configured.
|
||||
ensures: GitRemoteUpdated(project, remote_url)
|
||||
}
|
||||
|
||||
rule Fetch {
|
||||
when: GitFetchRequested(project)
|
||||
ensures: RemoteRefsUpdated(project)
|
||||
|
||||
@@ -674,8 +674,10 @@ rule ImportListClick {
|
||||
-- The sidebar must surface these capabilities directly.
|
||||
|
||||
-- State: not_a_repo
|
||||
-- Remote URL text input + "Initialize Git" button.
|
||||
-- "Initialize Git" button for the current blog project directory.
|
||||
-- Init progress with phase/percentage/detail, collapsible transcript.
|
||||
-- The remote URL is discovered from the repository and is never editable
|
||||
-- from the sidebar.
|
||||
|
||||
-- State: active_repo
|
||||
value GitActiveView {
|
||||
@@ -702,6 +704,11 @@ surface GitActiveViewSurface {
|
||||
|
||||
provides:
|
||||
GitCommitRequested(message)
|
||||
|
||||
@guarantee CurrentProjectRepository
|
||||
-- Git operations always target the current blog project's directory.
|
||||
-- Any origin URL is discovered from that repository and cannot be
|
||||
-- entered or changed in this surface.
|
||||
}
|
||||
|
||||
value GitStatusFile {
|
||||
|
||||
Reference in New Issue
Block a user