diff --git a/README.md b/README.md index 928c732..1cd934b 100644 --- a/README.md +++ b/README.md @@ -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 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. +- 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 with bDS2-compatible sync-status colors, 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. diff --git a/crates/bds-ui/src/views/git.rs b/crates/bds-ui/src/views/git.rs index fb6d458..0ff9d95 100644 --- a/crates/bds-ui/src/views/git.rs +++ b/crates/bds-ui/src/views/git.rs @@ -7,7 +7,7 @@ use bds_core::engine::git::{ use bds_core::i18n::UiLocale; use iced::widget::text::{Shaping, Wrapping}; use iced::widget::{Space, button, column, container, row, scrollable, text, text_input}; -use iced::{Color, Element, Font, Length}; +use iced::{Alignment, Background, Color, Element, Font, Length}; use crate::app::Message; use crate::components::inputs; @@ -192,7 +192,16 @@ pub fn sidebar_view( .spacing(4) .wrap(); - let mut content: Vec> = vec![header.into(), actions.into()]; + let sync_legend = row![ + sync_legend_item(SyncStatus::Both, locale), + sync_legend_item(SyncStatus::LocalOnly, locale), + sync_legend_item(SyncStatus::RemoteOnly, locale), + ] + .spacing(8) + .wrap(); + + let mut content: Vec> = + vec![header.into(), sync_legend.into(), actions.into()]; if offline_mode { content.push( text(t(locale, "git.airplaneBlocked")) @@ -299,17 +308,15 @@ fn history_button(commit: &GitCommit) -> Element<'static, Message> { let hash = commit.hash.clone(); let subject = commit.subject.clone().unwrap_or_else(|| hash.clone()); let short = hash.chars().take(7).collect::(); - let marker = match commit.sync_status { - SyncStatus::Both => "●", - SyncStatus::LocalOnly => "↑", - SyncStatus::RemoteOnly => "↓", - }; button( column![ text(subject.clone()).size(11), - text(format!("{marker} {short}")) - .size(10) - .color(Color::from_rgb(0.5, 0.5, 0.56)), + row![ + sync_status_dot(commit.sync_status), + text(short).size(10).color(Color::from_rgb(0.5, 0.5, 0.56)), + ] + .spacing(4) + .align_y(Alignment::Center), ] .spacing(1), ) @@ -320,6 +327,45 @@ fn history_button(commit: &GitCommit) -> Element<'static, Message> { .into() } +fn sync_legend_item(status: SyncStatus, locale: UiLocale) -> Element<'static, Message> { + let key = match status { + SyncStatus::Both => "git.synced", + SyncStatus::LocalOnly => "git.localOnly", + SyncStatus::RemoteOnly => "git.remoteOnly", + }; + row![ + sync_status_dot(status), + text(t(locale, key)) + .size(10) + .color(Color::from_rgb(0.5, 0.5, 0.56)), + ] + .spacing(4) + .align_y(Alignment::Center) + .into() +} + +fn sync_status_dot(status: SyncStatus) -> Element<'static, Message> { + let color = sync_status_color(status); + container(Space::new(8, 8)) + .style(move |_: &iced::Theme| container::Style { + background: Some(Background::Color(color)), + border: iced::Border { + radius: 4.0.into(), + ..iced::Border::default() + }, + ..container::Style::default() + }) + .into() +} + +fn sync_status_color(status: SyncStatus) -> Color { + match status { + SyncStatus::Both => Color::from_rgb8(0x73, 0xC9, 0x91), + SyncStatus::LocalOnly => Color::from_rgb8(0xCC, 0xA7, 0x00), + SyncStatus::RemoteOnly => Color::from_rgb8(0x75, 0xBE, 0xFF), + } +} + fn network_output(run: &GitNetworkRunState, locale: UiLocale) -> Element<'static, Message> { let output = run .output @@ -534,4 +580,20 @@ mod tests { let active_tree = Tree::new(sidebar_view(&initialized, false, UiLocale::En).as_widget()); assert_eq!(count_text_inputs(&active_tree), 1); } + + #[test] + fn commit_sync_colors_match_bds2() { + assert_eq!( + sync_status_color(SyncStatus::Both), + Color::from_rgb8(0x73, 0xC9, 0x91) + ); + assert_eq!( + sync_status_color(SyncStatus::LocalOnly), + Color::from_rgb8(0xCC, 0xA7, 0x00) + ); + assert_eq!( + sync_status_color(SyncStatus::RemoteOnly), + Color::from_rgb8(0x75, 0xBE, 0xFF) + ); + } } diff --git a/locales/ui/de.ftl b/locales/ui/de.ftl index 0128f82..4431922 100644 --- a/locales/ui/de.ftl +++ b/locales/ui/de.ftl @@ -705,6 +705,9 @@ git-fetch = Abrufen git-pull = Herunterladen git-push = Hochladen git-airplaneBlocked = Netzwerk-Git-Aktionen sind im Flugmodus nicht verfügbar. +git-synced = Synchronisiert +git-localOnly = Nur lokal +git-remoteOnly = Nur remote git-remoteUrl = Origin-URL git-changesCount = Änderungen ({ $count }) git-commitMessage = Commit-Nachricht diff --git a/locales/ui/en.ftl b/locales/ui/en.ftl index 987b7c0..77e0eba 100644 --- a/locales/ui/en.ftl +++ b/locales/ui/en.ftl @@ -705,6 +705,9 @@ git-fetch = Fetch git-pull = Pull git-push = Push git-airplaneBlocked = Network Git actions are unavailable in airplane mode. +git-synced = Synced +git-localOnly = Local only +git-remoteOnly = Remote only git-remoteUrl = Origin URL git-changesCount = Changes ({ $count }) git-commitMessage = Commit message diff --git a/locales/ui/es.ftl b/locales/ui/es.ftl index 5060ef3..05e3723 100644 --- a/locales/ui/es.ftl +++ b/locales/ui/es.ftl @@ -705,6 +705,9 @@ 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-synced = Sincronizado +git-localOnly = Solo local +git-remoteOnly = Solo remoto git-remoteUrl = URL de origin git-changesCount = Cambios ({ $count }) git-commitMessage = Mensaje de commit diff --git a/locales/ui/fr.ftl b/locales/ui/fr.ftl index c38b0c3..b81baad 100644 --- a/locales/ui/fr.ftl +++ b/locales/ui/fr.ftl @@ -705,6 +705,9 @@ git-fetch = Récupérer git-pull = Tirer git-push = Pousser git-airplaneBlocked = Les actions Git réseau sont indisponibles en mode avion. +git-synced = Synchronisé +git-localOnly = Local uniquement +git-remoteOnly = Distant uniquement git-remoteUrl = URL d’origine git-changesCount = Modifications ({ $count }) git-commitMessage = Message de commit diff --git a/locales/ui/it.ftl b/locales/ui/it.ftl index 9c7b398..57d8306 100644 --- a/locales/ui/it.ftl +++ b/locales/ui/it.ftl @@ -705,6 +705,9 @@ git-fetch = Recupera git-pull = Scarica git-push = Carica git-airplaneBlocked = Le azioni Git di rete non sono disponibili in modalità aereo. +git-synced = Sincronizzato +git-localOnly = Solo locale +git-remoteOnly = Solo remoto git-remoteUrl = URL origin git-changesCount = Modifiche ({ $count }) git-commitMessage = Messaggio di commit diff --git a/specs/sidebar_views.allium b/specs/sidebar_views.allium index 07a517c..4b8e43c 100644 --- a/specs/sidebar_views.allium +++ b/specs/sidebar_views.allium @@ -765,7 +765,9 @@ surface GitHistoryEntryView { @guarantee SyncStatusIndicator -- sync_status rendered as a coloured dot. - -- "synced" = both local and remote. "local_only" = local only. "remote_only" = remote only. + -- "synced" = both local and remote, green #73c991. + -- "local_only" = local only, warning yellow #cca700. + -- "remote_only" = remote only, information blue #75beff. -- A colour legend is shown in the git view header. }