From 03dcaea88ea89474c1d461f1ef6e373348579d6c Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 4 Apr 2026 19:23:12 +0200 Subject: [PATCH] feat: more work on UI --- Cargo.lock | 135 ++++++++++++++++- Cargo.toml | 2 +- crates/bds-ui/assets/icons/chat.svg | 6 + crates/bds-ui/assets/icons/git.svg | 3 + crates/bds-ui/assets/icons/import.svg | 3 + crates/bds-ui/assets/icons/media.svg | 3 + crates/bds-ui/assets/icons/pages.svg | 3 + crates/bds-ui/assets/icons/posts.svg | 4 + crates/bds-ui/assets/icons/scripts.svg | 3 + crates/bds-ui/assets/icons/settings.svg | 3 + crates/bds-ui/assets/icons/tags.svg | 3 + crates/bds-ui/assets/icons/templates.svg | 3 + crates/bds-ui/src/main.rs | 1 + crates/bds-ui/src/views/activity_bar.rs | 141 +++++++++++++----- crates/bds-ui/src/views/panel.rs | 113 ++++++++++---- crates/bds-ui/src/views/sidebar.rs | 60 +++++--- crates/bds-ui/src/views/status_bar.rs | 181 ++++++++++++++++++----- crates/bds-ui/src/views/tab_bar.rs | 91 ++++++++++-- crates/bds-ui/src/views/welcome.rs | 14 +- crates/bds-ui/src/views/workspace.rs | 69 ++++++--- locales/ui/de.json | 10 +- locales/ui/en.json | 10 +- locales/ui/es.json | 10 +- locales/ui/fr.json | 10 +- locales/ui/it.json | 10 +- 25 files changed, 713 insertions(+), 178 deletions(-) create mode 100644 crates/bds-ui/assets/icons/chat.svg create mode 100644 crates/bds-ui/assets/icons/git.svg create mode 100644 crates/bds-ui/assets/icons/import.svg create mode 100644 crates/bds-ui/assets/icons/media.svg create mode 100644 crates/bds-ui/assets/icons/pages.svg create mode 100644 crates/bds-ui/assets/icons/posts.svg create mode 100644 crates/bds-ui/assets/icons/scripts.svg create mode 100644 crates/bds-ui/assets/icons/settings.svg create mode 100644 crates/bds-ui/assets/icons/tags.svg create mode 100644 crates/bds-ui/assets/icons/templates.svg diff --git a/Cargo.lock b/Cargo.lock index 71ec013..d8c8141 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -950,7 +950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" dependencies = [ "bitflags 2.11.0", - "fontdb", + "fontdb 0.16.2", "log", "rangemap", "rayon", @@ -1073,6 +1073,12 @@ dependencies = [ "zbus 4.4.0", ] +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + [[package]] name = "dconf_rs" version = "0.3.0" @@ -1456,6 +1462,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "fnv" version = "1.0.7" @@ -1500,6 +1512,20 @@ dependencies = [ "ttf-parser 0.20.0", ] +[[package]] +name = "fontdb" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.21.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -2256,8 +2282,9 @@ dependencies = [ "bytemuck", "cosmic-text", "iced_graphics", - "kurbo", + "kurbo 0.10.4", "log", + "resvg", "rustc-hash 2.1.2", "softbuffer", "tiny-skia", @@ -2278,6 +2305,7 @@ dependencies = [ "iced_graphics", "log", "once_cell", + "resvg", "rustc-hash 2.1.2", "thiserror 1.0.69", "wgpu", @@ -2479,6 +2507,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + [[package]] name = "imgref" version = "1.12.0" @@ -2687,6 +2721,17 @@ dependencies = [ "smallvec", ] +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + [[package]] name = "leb128fmt" version = "0.1.0" @@ -3751,6 +3796,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.11" @@ -4327,6 +4378,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "resvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" +dependencies = [ + "gif 0.13.3", + "jpeg-decoder", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + [[package]] name = "rfd" version = "0.15.4" @@ -4356,6 +4423,9 @@ name = "rgb" version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] [[package]] name = "ropey" @@ -4674,6 +4744,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "1.0.2" @@ -4857,6 +4936,9 @@ name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] [[package]] name = "svg_fmt" @@ -4864,6 +4946,16 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo 0.11.3", + "siphasher", +] + [[package]] name = "swash" version = "0.1.19" @@ -5357,6 +5449,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.14" @@ -5394,6 +5492,33 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "usvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb 0.18.0", + "imagesize", + "kurbo 0.11.3", + "log", + "pico-args", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6398,6 +6523,12 @@ version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "y4m" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 7711441..ccc2ea3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ dirs = "5" open = "5" # UI framework -iced = { version = "0.13", features = ["wgpu", "advanced", "image", "tokio"] } +iced = { version = "0.13", features = ["wgpu", "advanced", "image", "svg", "tokio"] } # Editor widget cosmic-text = "0.12" diff --git a/crates/bds-ui/assets/icons/chat.svg b/crates/bds-ui/assets/icons/chat.svg new file mode 100644 index 0000000..61326f2 --- /dev/null +++ b/crates/bds-ui/assets/icons/chat.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/crates/bds-ui/assets/icons/git.svg b/crates/bds-ui/assets/icons/git.svg new file mode 100644 index 0000000..e85e558 --- /dev/null +++ b/crates/bds-ui/assets/icons/git.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/assets/icons/import.svg b/crates/bds-ui/assets/icons/import.svg new file mode 100644 index 0000000..bdd1478 --- /dev/null +++ b/crates/bds-ui/assets/icons/import.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/assets/icons/media.svg b/crates/bds-ui/assets/icons/media.svg new file mode 100644 index 0000000..42bfe0b --- /dev/null +++ b/crates/bds-ui/assets/icons/media.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/assets/icons/pages.svg b/crates/bds-ui/assets/icons/pages.svg new file mode 100644 index 0000000..c0cf019 --- /dev/null +++ b/crates/bds-ui/assets/icons/pages.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/assets/icons/posts.svg b/crates/bds-ui/assets/icons/posts.svg new file mode 100644 index 0000000..72e1354 --- /dev/null +++ b/crates/bds-ui/assets/icons/posts.svg @@ -0,0 +1,4 @@ + + + + diff --git a/crates/bds-ui/assets/icons/scripts.svg b/crates/bds-ui/assets/icons/scripts.svg new file mode 100644 index 0000000..036b86f --- /dev/null +++ b/crates/bds-ui/assets/icons/scripts.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/assets/icons/settings.svg b/crates/bds-ui/assets/icons/settings.svg new file mode 100644 index 0000000..00e461f --- /dev/null +++ b/crates/bds-ui/assets/icons/settings.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/assets/icons/tags.svg b/crates/bds-ui/assets/icons/tags.svg new file mode 100644 index 0000000..b435d49 --- /dev/null +++ b/crates/bds-ui/assets/icons/tags.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/assets/icons/templates.svg b/crates/bds-ui/assets/icons/templates.svg new file mode 100644 index 0000000..f530ee0 --- /dev/null +++ b/crates/bds-ui/assets/icons/templates.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/bds-ui/src/main.rs b/crates/bds-ui/src/main.rs index 6cf10c8..dbea182 100644 --- a/crates/bds-ui/src/main.rs +++ b/crates/bds-ui/src/main.rs @@ -3,6 +3,7 @@ use bds_ui::BdsApp; fn main() -> iced::Result { iced::application("bDS", BdsApp::update, BdsApp::view) .subscription(BdsApp::subscription) + .theme(|_| iced::Theme::Dark) .window_size((1200.0, 800.0)) .run_with(BdsApp::new) } diff --git a/crates/bds-ui/src/views/activity_bar.rs b/crates/bds-ui/src/views/activity_bar.rs index f865253..f10e32e 100644 --- a/crates/bds-ui/src/views/activity_bar.rs +++ b/crates/bds-ui/src/views/activity_bar.rs @@ -1,24 +1,27 @@ -use iced::widget::{button, column, container, text, Column, Space}; -use iced::{Alignment, Element, Length}; +use iced::widget::{button, column, container, svg, Column, Space}; +use iced::{Background, Border, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; use crate::app::Message; use crate::state::navigation::SidebarView; -/// Activity-bar short labels used in M2 (no SVG icons yet). -fn short_label(view: SidebarView) -> &'static str { +// --------------------------------------------------------------------------- +// SVG icon data — ported from bDS ActivityBar.tsx inline SVGs +// --------------------------------------------------------------------------- + +fn icon_svg(view: SidebarView) -> &'static [u8] { match view { - SidebarView::Posts => "Po", - SidebarView::Pages => "Pa", - SidebarView::Media => "Me", - SidebarView::Scripts => "Sc", - SidebarView::Templates => "Tp", - SidebarView::Tags => "Ta", - SidebarView::Chat => "AI", - SidebarView::Import => "Im", - SidebarView::Git => "Gi", - SidebarView::Settings => "Se", + SidebarView::Posts => include_bytes!("../../assets/icons/posts.svg"), + SidebarView::Pages => include_bytes!("../../assets/icons/pages.svg"), + SidebarView::Media => include_bytes!("../../assets/icons/media.svg"), + SidebarView::Scripts => include_bytes!("../../assets/icons/scripts.svg"), + SidebarView::Templates => include_bytes!("../../assets/icons/templates.svg"), + SidebarView::Tags => include_bytes!("../../assets/icons/tags.svg"), + SidebarView::Chat => include_bytes!("../../assets/icons/chat.svg"), + SidebarView::Import => include_bytes!("../../assets/icons/import.svg"), + SidebarView::Git => include_bytes!("../../assets/icons/git.svg"), + SidebarView::Settings => include_bytes!("../../assets/icons/settings.svg"), } } @@ -40,28 +43,93 @@ const BOTTOM_ACTIVITIES: &[SidebarView] = &[ SidebarView::Settings, ]; +// --------------------------------------------------------------------------- +// Styles — matching bDS ActivityBar.css +// --------------------------------------------------------------------------- + +/// Active button: transparent bg. +fn active_button_style(_theme: &Theme, _status: button::Status) -> button::Style { + button::Style { + background: Some(Background::Color(Color::TRANSPARENT)), + text_color: Color::WHITE, + border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 0.0.into(), + }, + ..button::Style::default() + } +} + +/// Inactive button: hover brightens background. +fn inactive_button_style(_theme: &Theme, status: button::Status) -> button::Style { + let bg = match status { + button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + _ => Color::TRANSPARENT, + }; + button::Style { + background: Some(Background::Color(bg)), + text_color: Color::from_rgb(0.55, 0.55, 0.60), + border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 0.0.into(), + }, + ..button::Style::default() + } +} + +/// Activity bar container: dark background. +fn bar_background_style(_theme: &Theme) -> container::Style { + container::Style { + background: Some(Background::Color(Color::from_rgb(0.14, 0.14, 0.18))), + ..container::Style::default() + } +} + +// --------------------------------------------------------------------------- +// View +// --------------------------------------------------------------------------- + pub fn view( active_view: SidebarView, _locale: UiLocale, ) -> Element<'static, Message> { let make_btn = |view: SidebarView| -> Element<'static, Message> { - let label = short_label(view); - let mut btn = button( - text(label).size(12).center(), + let handle = svg::Handle::from_memory(icon_svg(view)); + let is_active = view == active_view; + + // Render SVG: active = full opacity, inactive = 0.4 opacity (like bDS 60% opacity) + let icon = svg(handle) + .width(Length::Fixed(24.0)) + .height(Length::Fixed(24.0)) + .opacity(if is_active { 1.0 } else { 0.4 }); + + let btn = button( + container(icon) + .center_x(Length::Fixed(48.0)) + .center_y(Length::Fixed(48.0)), ) .width(Length::Fixed(48.0)) .height(Length::Fixed(48.0)) - .on_press(Message::SetActiveView(view)); + .padding(0) + .on_press(Message::SetActiveView(view)) + .style(if is_active { active_button_style } else { inactive_button_style }); - if view == active_view { - btn = btn.style(button::primary); + // Active indicator: 2px left border (like bDS/VS Code) + if is_active { + let indicator = container(Space::new(0, 0)) + .width(Length::Fixed(2.0)) + .height(Length::Fixed(48.0)) + .style(|_theme: &Theme| container::Style { + background: Some(Background::Color(Color::WHITE)), + ..container::Style::default() + }); + iced::widget::row![indicator, btn].into() } else { - btn = btn.style(button::secondary); + let spacer = Space::with_width(2.0); + iced::widget::row![spacer, btn].into() } - - container(btn) - .center_x(Length::Fixed(48.0)) - .into() }; let top_items: Vec> = TOP_ACTIVITIES @@ -74,16 +142,21 @@ pub fn view( .map(|v| make_btn(*v)) .collect(); - let top = Column::with_children(top_items).spacing(2); - let bottom = Column::with_children(bottom_items).spacing(2); + let top = Column::with_children(top_items).spacing(0); + let bottom = Column::with_children(bottom_items).spacing(0); - column![ - top, - Space::with_height(Length::Fill), - bottom, - ] - .width(Length::Fixed(48.0)) + container( + column![ + top, + Space::with_height(Length::Fill), + bottom, + ] + .width(Length::Fixed(50.0)) + .height(Length::Fill) + .padding([4, 0]), + ) + .width(Length::Fixed(50.0)) .height(Length::Fill) - .align_x(Alignment::Center) + .style(bar_background_style) .into() } diff --git a/crates/bds-ui/src/views/panel.rs b/crates/bds-ui/src/views/panel.rs index 18c01c5..da63848 100644 --- a/crates/bds-ui/src/views/panel.rs +++ b/crates/bds-ui/src/views/panel.rs @@ -1,5 +1,5 @@ use iced::widget::{button, column, container, row, scrollable, text, Space}; -use iced::{Alignment, Element, Length}; +use iced::{Alignment, Background, Border, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; @@ -7,41 +7,83 @@ use crate::app::Message; use crate::i18n::t; use crate::state::navigation::{OutputEntry, PanelTab, TaskSnapshot}; +/// Panel background style. +fn panel_style(_theme: &Theme) -> container::Style { + container::Style { + background: Some(Background::Color(Color::from_rgb(0.13, 0.13, 0.16))), + ..container::Style::default() + } +} + +/// Panel tab button — active. +fn tab_active(_theme: &Theme, _status: button::Status) -> button::Style { + button::Style { + background: Some(Background::Color(Color::from_rgb(0.20, 0.20, 0.25))), + text_color: Color::WHITE, + border: Border { + color: Color::from_rgb(0.30, 0.55, 0.90), + width: 0.0, + radius: 3.0.into(), + }, + ..button::Style::default() + } +} + +/// Panel tab button — inactive. +fn tab_inactive(_theme: &Theme, status: button::Status) -> button::Style { + let bg = match status { + button::Status::Hovered => Color::from_rgb(0.18, 0.18, 0.22), + _ => Color::TRANSPARENT, + }; + button::Style { + background: Some(Background::Color(bg)), + text_color: Color::from_rgb(0.60, 0.60, 0.65), + border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 3.0.into(), + }, + ..button::Style::default() + } +} + +/// Close button style. +fn close_btn_style(_theme: &Theme, status: button::Status) -> button::Style { + let color = match status { + button::Status::Hovered => Color::WHITE, + _ => Color::from_rgb(0.50, 0.50, 0.55), + }; + button::Style { + background: Some(Background::Color(Color::TRANSPARENT)), + text_color: color, + border: Border::default(), + ..button::Style::default() + } +} + pub fn view( panel_tab: PanelTab, task_snapshots: &[TaskSnapshot], output_entries: &[OutputEntry], locale: UiLocale, ) -> Element<'static, Message> { + let muted = Color::from_rgb(0.50, 0.50, 0.55); + // Tab header - let tasks_btn = { - let mut btn = button(text(t(locale, "common.tasks")).size(12)) - .on_press(Message::SetPanelTab(PanelTab::Tasks)) - .padding([4, 8]); - if panel_tab == PanelTab::Tasks { - btn = btn.style(button::primary); - } else { - btn = btn.style(button::secondary); - } - btn - }; + let tasks_btn = button(text(t(locale, "common.tasks")).size(12)) + .on_press(Message::SetPanelTab(PanelTab::Tasks)) + .padding([4, 8]) + .style(if panel_tab == PanelTab::Tasks { tab_active } else { tab_inactive }); - let output_btn = { - let mut btn = button(text(t(locale, "panel.output")).size(12)) - .on_press(Message::SetPanelTab(PanelTab::Output)) - .padding([4, 8]); - if panel_tab == PanelTab::Output { - btn = btn.style(button::primary); - } else { - btn = btn.style(button::secondary); - } - btn - }; + let output_btn = button(text(t(locale, "panel.output")).size(12)) + .on_press(Message::SetPanelTab(PanelTab::Output)) + .padding([4, 8]) + .style(if panel_tab == PanelTab::Output { tab_active } else { tab_inactive }); - let close_btn = button(text("×").size(12)) + let close_btn = button(text("\u{2715}").size(12)) .on_press(Message::TogglePanel) .padding([4, 6]) - .style(button::text); + .style(close_btn_style); let tab_header = row![ tasks_btn, @@ -50,13 +92,14 @@ pub fn view( close_btn, ] .spacing(4) - .align_y(Alignment::Center); + .align_y(Alignment::Center) + .padding([4, 8]); // Tab content let content: Element<'static, Message> = match panel_tab { PanelTab::Tasks => { if task_snapshots.is_empty() { - container(text(t(locale, "tasks.noActive")).size(12)) + container(text(t(locale, "tasks.noActive")).size(12).color(muted)) .padding(8) .into() } else { @@ -64,14 +107,14 @@ pub fn view( .iter() .map(|snap| { let status_text = format!( - "{} — {}{}", + "{} \u{2014} {}{}", snap.label, snap.status, snap.progress .map(|p| format!(" ({:.0}%)", p * 100.0)) .unwrap_or_default(), ); - text(status_text).size(11).into() + text(status_text).size(11).color(Color::from_rgb(0.70, 0.70, 0.75)).into() }) .collect(); scrollable( @@ -84,13 +127,18 @@ pub fn view( } PanelTab::Output => { if output_entries.is_empty() { - container(text(t(locale, "panel.noOutput")).size(12)) + container(text(t(locale, "panel.noOutput")).size(12).color(muted)) .padding(8) .into() } else { let items: Vec> = output_entries .iter() - .map(|entry| text(entry.text.clone()).size(11).into()) + .map(|entry| { + text(entry.text.clone()) + .size(11) + .color(Color::from_rgb(0.70, 0.70, 0.75)) + .into() + }) .collect(); scrollable( iced::widget::Column::with_children(items) @@ -103,9 +151,10 @@ pub fn view( }; container( - column![tab_header, content].spacing(4), + column![tab_header, content].spacing(0), ) .width(Length::Fill) .height(Length::Fixed(200.0)) + .style(panel_style) .into() } diff --git a/crates/bds-ui/src/views/sidebar.rs b/crates/bds-ui/src/views/sidebar.rs index b7a54e1..6fac3b7 100644 --- a/crates/bds-ui/src/views/sidebar.rs +++ b/crates/bds-ui/src/views/sidebar.rs @@ -1,5 +1,5 @@ -use iced::widget::{column, container, scrollable, text}; -use iced::{Element, Length}; +use iced::widget::{column, container, scrollable, text, Space}; +use iced::{Background, Border, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; @@ -7,31 +7,57 @@ use crate::app::Message; use crate::i18n::t; use crate::state::navigation::SidebarView; +/// Sidebar container style — dark background with right border separator. +fn sidebar_style(_theme: &Theme) -> container::Style { + container::Style { + background: Some(Background::Color(Color::from_rgb(0.16, 0.16, 0.20))), + border: Border { + color: Color::from_rgb(0.25, 0.25, 0.30), + width: 1.0, + radius: 0.0.into(), + }, + ..container::Style::default() + } +} + +/// Get the appropriate empty-state message key for each sidebar view. +fn placeholder_key(view: SidebarView) -> &'static str { + match view { + SidebarView::Posts => "sidebar.noPostsYet", + SidebarView::Pages => "sidebar.noPagesYet", + SidebarView::Media => "sidebar.noMediaYet", + SidebarView::Scripts => "sidebar.noScriptsYet", + SidebarView::Templates => "sidebar.noTemplatesYet", + SidebarView::Tags => "sidebar.tagsHeader", + SidebarView::Chat => "sidebar.chatPlaceholder", + SidebarView::Import => "sidebar.importPlaceholder", + SidebarView::Git => "sidebar.gitPlaceholder", + SidebarView::Settings => "sidebar.settingsHeader", + } +} + pub fn view( sidebar_view: SidebarView, locale: UiLocale, ) -> Element<'static, Message> { - let header_key = sidebar_view.i18n_key(); - let header_text = t(locale, header_key); - - let placeholder = match sidebar_view { - SidebarView::Posts => t(locale, "sidebar.noPostsYet"), - SidebarView::Pages => t(locale, "sidebar.noPagesYet"), - SidebarView::Media => t(locale, "sidebar.noMediaYet"), - SidebarView::Settings => t(locale, "sidebar.settingsHeader"), - SidebarView::Tags => t(locale, "sidebar.tagsHeader"), - _ => t(locale, "sidebar.loading"), - }; + let header_text = t(locale, sidebar_view.i18n_key()); + let placeholder = t(locale, placeholder_key(sidebar_view)); let content = column![ - text(header_text).size(14), - text(placeholder).size(12), + text(header_text) + .size(13) + .color(Color::from_rgb(0.85, 0.85, 0.90)), + Space::with_height(8.0), + text(placeholder) + .size(12) + .color(Color::from_rgb(0.50, 0.50, 0.55)), ] - .spacing(8) + .spacing(4) .padding(12); container(scrollable(content)) - .width(Length::Fixed(280.0)) + .width(Length::Fixed(240.0)) .height(Length::Fill) + .style(sidebar_style) .into() } diff --git a/crates/bds-ui/src/views/status_bar.rs b/crates/bds-ui/src/views/status_bar.rs index 9db45d1..6d22ebe 100644 --- a/crates/bds-ui/src/views/status_bar.rs +++ b/crates/bds-ui/src/views/status_bar.rs @@ -1,5 +1,5 @@ -use iced::widget::{button, container, row, text, Space}; -use iced::{Alignment, Element, Length}; +use iced::widget::{button, container, pick_list, row, text, Space}; +use iced::{Alignment, Background, Border, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; @@ -7,6 +7,110 @@ use crate::app::Message; use crate::i18n::{t, tw}; use crate::state::navigation::TaskSnapshot; +// --------------------------------------------------------------------------- +// Locale option wrapper for pick_list (needs ToString + PartialEq + Clone) +// --------------------------------------------------------------------------- + +/// Wraps a UiLocale with a display label for the pick_list dropdown. +#[derive(Debug, Clone, PartialEq)] +struct LocaleOption { + locale: UiLocale, + label: String, +} + +impl ToString for LocaleOption { + fn to_string(&self) -> String { + self.label.clone() + } +} + +impl LocaleOption { + fn new(locale: UiLocale, display_locale: UiLocale) -> Self { + let key = match locale { + UiLocale::En => "language.en", + UiLocale::De => "language.de", + UiLocale::Fr => "language.fr", + UiLocale::It => "language.it", + UiLocale::Es => "language.es", + }; + Self { + locale, + label: t(display_locale, key), + } + } +} + +// --------------------------------------------------------------------------- +// Styles +// --------------------------------------------------------------------------- + +/// Status bar background. +fn bar_style(_theme: &Theme) -> container::Style { + container::Style { + background: Some(Background::Color(Color::from_rgb(0.12, 0.12, 0.16))), + ..container::Style::default() + } +} + +/// Airplane button — inactive (dim). +fn airplane_inactive(_theme: &Theme, status: button::Status) -> button::Style { + let bg = match status { + button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + _ => Color::TRANSPARENT, + }; + button::Style { + background: Some(Background::Color(bg)), + text_color: Color::from_rgba(0.70, 0.70, 0.75, 0.4), + border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 3.0.into(), + }, + ..button::Style::default() + } +} + +/// Airplane button — active (warning color). +fn airplane_active(_theme: &Theme, status: button::Status) -> button::Style { + let bg = match status { + button::Status::Hovered => Color::from_rgb(0.85, 0.65, 0.10), + _ => Color::from_rgb(0.75, 0.55, 0.05), + }; + button::Style { + background: Some(Background::Color(bg)), + text_color: Color::WHITE, + border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 3.0.into(), + }, + ..button::Style::default() + } +} + +/// Pick list style matching status bar. +fn locale_pick_list_style(_theme: &Theme, status: pick_list::Status) -> pick_list::Style { + let bg = match status { + pick_list::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + _ => Color::TRANSPARENT, + }; + pick_list::Style { + text_color: Color::from_rgb(0.70, 0.70, 0.75), + placeholder_color: Color::from_rgb(0.50, 0.50, 0.55), + handle_color: Color::from_rgb(0.50, 0.50, 0.55), + background: Background::Color(bg), + border: Border { + color: Color::from_rgb(0.25, 0.25, 0.30), + width: 1.0, + radius: 3.0.into(), + }, + } +} + +// --------------------------------------------------------------------------- +// View +// --------------------------------------------------------------------------- + pub fn view( active_project_name: Option<&str>, post_count: usize, @@ -15,9 +119,11 @@ pub fn view( offline_mode: bool, task_snapshots: &[TaskSnapshot], ) -> Element<'static, Message> { - // Left: project name + task indicator - let project_label = active_project_name.unwrap_or("—").to_string(); - let project_text = text(project_label).size(12); + let label_color = Color::from_rgb(0.60, 0.60, 0.65); + + // ── Left: project name + task indicator ── + let project_label = active_project_name.unwrap_or("\u{2014}").to_string(); + let project_text = text(project_label).size(12).color(Color::from_rgb(0.80, 0.80, 0.85)); let running: Vec<&TaskSnapshot> = task_snapshots .iter() @@ -26,9 +132,9 @@ pub fn view( let task_indicator: Element<'static, Message> = if !running.is_empty() { let first = running[0].label.clone(); if running.len() > 1 { - text(format!("{first} (+{})", running.len() - 1)).size(11).into() + text(format!("{first} (+{})", running.len() - 1)).size(11).color(label_color).into() } else { - text(first).size(11).into() + text(first).size(11).color(label_color).into() } } else { Space::with_width(0).into() @@ -36,50 +142,44 @@ pub fn view( let left = row![project_text, task_indicator].spacing(12); - // Right: post count, media count, airplane mode, locale selector, brand + // ── Right side ── + + // Post + media counts let posts_label = tw(locale, "statusBar.posts", &[("count", &post_count.to_string())]); let media_label = tw(locale, "statusBar.media", &[("count", &media_count.to_string())]); - let airplane_label = if offline_mode { - t(locale, "statusBar.offlineModeActive") - } else { - t(locale, "statusBar.offlineMode") - }; - - let airplane_btn = button(text(airplane_label).size(11)) + // Airplane mode toggle — just the ✈ icon, like bDS + let airplane_btn = button(text("\u{2708}").size(13)) .on_press(Message::SetOfflineMode(!offline_mode)) - .padding([2, 6]) - .style(if offline_mode { button::primary } else { button::text }); + .padding([2, 4]) + .style(if offline_mode { airplane_active } else { airplane_inactive }); - let ui_label = t(locale, "statusBar.ui"); - - let locale_buttons: Vec> = UiLocale::all() + // Language selector — "UI" label + pick_list dropdown, like bDS + let options: Vec = UiLocale::all() .iter() - .map(|l| { - let code = l.code().to_string(); - let mut btn = button(text(code).size(11)) - .on_press(Message::SetUiLocale(*l)) - .padding([2, 4]); - if *l == locale { - btn = btn.style(button::primary); - } else { - btn = btn.style(button::text); - } - btn.into() - }) + .map(|l| LocaleOption::new(*l, locale)) .collect(); + let selected = Some(LocaleOption::new(locale, locale)); - let locale_row = iced::widget::Row::with_children(locale_buttons).spacing(2); + let locale_picker = pick_list( + options, + selected, + |opt: LocaleOption| Message::SetUiLocale(opt.locale), + ) + .text_size(11) + .padding([2, 4]) + .width(Length::Shrink) + .style(locale_pick_list_style); let right = row![ - text(posts_label).size(11), - text(media_label).size(11), + text(posts_label).size(11).color(label_color), + text(media_label).size(11).color(label_color), airplane_btn, - text(ui_label).size(11), - locale_row, - text("bDS").size(11), + text(t(locale, "statusBar.ui")).size(11).color(label_color), + locale_picker, + text("bDS").size(11).color(Color::from_rgb(0.45, 0.45, 0.50)), ] - .spacing(12) + .spacing(8) .align_y(Alignment::Center); container( @@ -92,6 +192,7 @@ pub fn view( .padding([0, 8]), ) .width(Length::Fill) - .height(Length::Fixed(22.0)) + .height(Length::Fixed(24.0)) + .style(bar_style) .into() } diff --git a/crates/bds-ui/src/views/tab_bar.rs b/crates/bds-ui/src/views/tab_bar.rs index 9b100d9..c9b16f4 100644 --- a/crates/bds-ui/src/views/tab_bar.rs +++ b/crates/bds-ui/src/views/tab_bar.rs @@ -1,15 +1,78 @@ use iced::widget::{button, container, row, text, Space}; -use iced::{Element, Length}; +use iced::{Background, Border, Color, Element, Length, Theme}; use crate::app::Message; use crate::state::tabs::Tab; +/// Tab bar background. +fn bar_style(_theme: &Theme) -> container::Style { + container::Style { + background: Some(Background::Color(Color::from_rgb(0.14, 0.14, 0.18))), + border: Border { + color: Color::from_rgb(0.25, 0.25, 0.30), + width: 0.0, + radius: 0.0.into(), + }, + ..container::Style::default() + } +} + +/// Active tab style. +fn tab_active(_theme: &Theme, _status: button::Status) -> button::Style { + button::Style { + background: Some(Background::Color(Color::from_rgb(0.11, 0.11, 0.14))), + text_color: Color::WHITE, + border: Border { + color: Color::from_rgb(0.30, 0.55, 0.90), + width: 0.0, + radius: 4.0.into(), + }, + ..button::Style::default() + } +} + +/// Inactive tab style. +fn tab_inactive(_theme: &Theme, status: button::Status) -> button::Style { + let bg = match status { + button::Status::Hovered => Color::from_rgb(0.18, 0.18, 0.22), + _ => Color::from_rgb(0.14, 0.14, 0.18), + }; + button::Style { + background: Some(Background::Color(bg)), + text_color: Color::from_rgb(0.60, 0.60, 0.65), + border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 4.0.into(), + }, + ..button::Style::default() + } +} + +/// Close button on tab. +fn close_style(_theme: &Theme, status: button::Status) -> button::Style { + let color = match status { + button::Status::Hovered => Color::WHITE, + _ => Color::from_rgb(0.45, 0.45, 0.50), + }; + button::Style { + background: Some(Background::Color(Color::TRANSPARENT)), + text_color: color, + border: Border::default(), + ..button::Style::default() + } +} + pub fn view( tabs: &[Tab], active_tab: Option<&str>, ) -> Element<'static, Message> { if tabs.is_empty() { - return Space::with_height(0).into(); + return container(Space::new(0, 0)) + .width(Length::Fill) + .height(Length::Fixed(35.0)) + .style(bar_style) + .into(); } let tab_buttons: Vec> = tabs @@ -17,7 +80,7 @@ pub fn view( .map(|tab| { let is_active = active_tab == Some(tab.id.as_str()); let title_text = if tab.is_transient { - format!("{} (preview)", tab.title) + format!("{} \u{25CB}", tab.title) // hollow circle for transient } else { tab.title.clone() }; @@ -26,24 +89,19 @@ pub fn view( let label = row![ text(title_text).size(12), - button(text("×").size(12)) + button(text("\u{2715}").size(10)) .on_press(Message::CloseTab(close_id)) .padding(2) - .style(button::text), + .style(close_style), ] - .spacing(4); + .spacing(6) + .align_y(iced::Alignment::Center); - let mut btn = button(label) + button(label) .on_press(Message::SelectTab(tab_id)) - .padding([4, 8]); - - if is_active { - btn = btn.style(button::primary); - } else { - btn = btn.style(button::secondary); - } - - btn.into() + .padding([6, 12]) + .style(if is_active { tab_active } else { tab_inactive }) + .into() }) .collect(); @@ -54,5 +112,6 @@ pub fn view( ) .width(Length::Fill) .height(Length::Fixed(35.0)) + .style(bar_style) .into() } diff --git a/crates/bds-ui/src/views/welcome.rs b/crates/bds-ui/src/views/welcome.rs index fb549bc..247dc1e 100644 --- a/crates/bds-ui/src/views/welcome.rs +++ b/crates/bds-ui/src/views/welcome.rs @@ -1,5 +1,5 @@ use iced::widget::{column, container, text}; -use iced::{Element, Length}; +use iced::{Background, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; @@ -12,8 +12,12 @@ pub fn view(locale: UiLocale) -> Element<'static, Message> { container( column![ - text(title).size(28), - text(subtitle).size(14), + text(title) + .size(28) + .color(Color::from_rgb(0.85, 0.85, 0.90)), + text(subtitle) + .size(14) + .color(Color::from_rgb(0.55, 0.55, 0.60)), ] .spacing(12) .align_x(iced::Alignment::Center), @@ -22,5 +26,9 @@ pub fn view(locale: UiLocale) -> Element<'static, Message> { .height(Length::Fill) .center_x(Length::Fill) .center_y(Length::Fill) + .style(|_theme: &Theme| container::Style { + background: Some(Background::Color(Color::from_rgb(0.11, 0.11, 0.14))), + ..container::Style::default() + }) .into() } diff --git a/crates/bds-ui/src/views/workspace.rs b/crates/bds-ui/src/views/workspace.rs index f62aaba..5af1035 100644 --- a/crates/bds-ui/src/views/workspace.rs +++ b/crates/bds-ui/src/views/workspace.rs @@ -1,5 +1,5 @@ -use iced::widget::{column, row}; -use iced::{Element, Length}; +use iced::widget::{column, container, row, Space}; +use iced::{Background, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; @@ -8,6 +8,36 @@ use crate::state::navigation::{OutputEntry, PanelTab, SidebarView, TaskSnapshot} use crate::state::tabs::Tab; use crate::views::{activity_bar, panel, sidebar, status_bar, tab_bar, welcome}; +/// Main content area background. +fn content_bg(_theme: &Theme) -> container::Style { + container::Style { + background: Some(Background::Color(Color::from_rgb(0.11, 0.11, 0.14))), + ..container::Style::default() + } +} + +/// Horizontal separator line between regions. +fn separator_v() -> iced::widget::Container<'static, Message> { + container(Space::new(0, 0)) + .width(Length::Fixed(1.0)) + .height(Length::Fill) + .style(|_theme: &Theme| container::Style { + background: Some(Background::Color(Color::from_rgb(0.25, 0.25, 0.30))), + ..container::Style::default() + }) +} + +/// Horizontal line separator (full width). +fn separator_h() -> iced::widget::Container<'static, Message> { + container(Space::new(0, 0)) + .width(Length::Fill) + .height(Length::Fixed(1.0)) + .style(|_theme: &Theme| container::Style { + background: Some(Background::Color(Color::from_rgb(0.25, 0.25, 0.30))), + ..container::Style::default() + }) +} + /// Compose the full workspace layout. pub fn view( // Navigation @@ -32,38 +62,31 @@ pub fn view( // Activity bar (leftmost column) let activity = activity_bar::view(sidebar_view, locale); - // Sidebar (conditionally visible) - let sidebar_el: Option> = if sidebar_visible { - Some(sidebar::view(sidebar_view, locale)) - } else { - None - }; - // Tab bar let tabs_el = tab_bar::view(tabs, active_tab); // Content area let content_area = welcome::view(locale); - // Panel (conditionally visible) - let panel_el: Option> = if panel_visible { - Some(panel::view(panel_tab, task_snapshots, output_entries, locale)) - } else { - None - }; - // Right column: tab bar + content + panel let mut right_col = column![tabs_el, content_area]; - if let Some(p) = panel_el { - right_col = right_col.push(p); + if panel_visible { + right_col = right_col.push(separator_h()); + right_col = right_col.push(panel::view(panel_tab, task_snapshots, output_entries, locale)); } - let right = right_col.width(Length::Fill).height(Length::Fill); + let right = container(right_col.width(Length::Fill).height(Length::Fill)) + .width(Length::Fill) + .height(Length::Fill) + .style(content_bg); - // Main row: activity bar + sidebar? + right column + // Main row: activity bar | separator | sidebar? | separator | right column let mut main_row = row![activity]; - if let Some(sb) = sidebar_el { - main_row = main_row.push(sb); + + if sidebar_visible { + main_row = main_row.push(sidebar::view(sidebar_view, locale)); + main_row = main_row.push(separator_v()); } + main_row = main_row.push(right); let main_row = main_row.height(Length::Fill); @@ -77,7 +100,7 @@ pub fn view( task_snapshots, ); - column![main_row, status] + column![main_row, separator_h(), status] .width(Length::Fill) .height(Length::Fill) .into() diff --git a/locales/ui/de.json b/locales/ui/de.json index 72b9e0a..5f665ac 100644 --- a/locales/ui/de.json +++ b/locales/ui/de.json @@ -38,9 +38,10 @@ "activity.scripts": "Skripte", "activity.tags": "Schlagwörter", "activity.templates": "Vorlagen", - "activity.aiAssistant": "KI-Assistent", + "activity.chat": "KI-Assistent", "activity.import": "Importieren", - "activity.sourceControl": "Versionskontrolle", + "activity.git": "Versionskontrolle", + "activity.settings": "Einstellungen", "common.save": "Speichern", "common.cancel": "Abbrechen", "common.settings": "Einstellungen", @@ -85,6 +86,11 @@ "sidebar.noPostsYet": "Noch keine Beiträge", "sidebar.noPagesYet": "Noch keine Seiten", "sidebar.noMediaYet": "Noch keine Medien", + "sidebar.noScriptsYet": "Noch keine Skripte", + "sidebar.noTemplatesYet": "Noch keine Vorlagen", + "sidebar.chatPlaceholder": "KI-Assistent erscheint hier", + "sidebar.importPlaceholder": "Inhalte aus externen Quellen importieren", + "sidebar.gitPlaceholder": "Versionskontrolle-Integration", "sidebar.loading": "Lädt...", "sidebar.settingsHeader": "Einstellungen", "sidebar.tagsHeader": "Schlagwörter", diff --git a/locales/ui/en.json b/locales/ui/en.json index e6568c2..217821d 100644 --- a/locales/ui/en.json +++ b/locales/ui/en.json @@ -38,9 +38,10 @@ "activity.scripts": "Scripts", "activity.tags": "Tags", "activity.templates": "Templates", - "activity.aiAssistant": "AI Assistant", + "activity.chat": "AI Assistant", "activity.import": "Import", - "activity.sourceControl": "Source Control", + "activity.git": "Source Control", + "activity.settings": "Settings", "common.save": "Save", "common.cancel": "Cancel", "common.settings": "Settings", @@ -85,6 +86,11 @@ "sidebar.noPostsYet": "No posts yet", "sidebar.noPagesYet": "No pages yet", "sidebar.noMediaYet": "No media yet", + "sidebar.noScriptsYet": "No scripts yet", + "sidebar.noTemplatesYet": "No templates yet", + "sidebar.chatPlaceholder": "AI assistant will appear here", + "sidebar.importPlaceholder": "Import content from external sources", + "sidebar.gitPlaceholder": "Source control integration", "sidebar.loading": "Loading...", "sidebar.settingsHeader": "Settings", "sidebar.tagsHeader": "Tags", diff --git a/locales/ui/es.json b/locales/ui/es.json index 70d66f1..b3af9e7 100644 --- a/locales/ui/es.json +++ b/locales/ui/es.json @@ -38,9 +38,10 @@ "activity.scripts": "Scripts", "activity.tags": "Etiquetas", "activity.templates": "Plantillas", - "activity.aiAssistant": "Asistente IA", + "activity.chat": "Asistente IA", "activity.import": "Importar", - "activity.sourceControl": "Control de código fuente", + "activity.git": "Control de código fuente", + "activity.settings": "Configuración", "common.save": "Guardar", "common.cancel": "Cancelar", "common.settings": "Configuración", @@ -85,6 +86,11 @@ "sidebar.noPostsYet": "Aún no hay entradas", "sidebar.noPagesYet": "Aún no hay páginas", "sidebar.noMediaYet": "Aún no hay medios", + "sidebar.noScriptsYet": "Aún no hay scripts", + "sidebar.noTemplatesYet": "Aún no hay plantillas", + "sidebar.chatPlaceholder": "El asistente IA aparecerá aquí", + "sidebar.importPlaceholder": "Importar contenido desde fuentes externas", + "sidebar.gitPlaceholder": "Integración del control de código fuente", "sidebar.loading": "Cargando...", "sidebar.settingsHeader": "Configuración", "sidebar.tagsHeader": "Etiquetas", diff --git a/locales/ui/fr.json b/locales/ui/fr.json index c809a6d..a67b60d 100644 --- a/locales/ui/fr.json +++ b/locales/ui/fr.json @@ -38,9 +38,10 @@ "activity.scripts": "Scripts", "activity.tags": "Étiquettes", "activity.templates": "Modèles", - "activity.aiAssistant": "Assistant IA", + "activity.chat": "Assistant IA", "activity.import": "Importation", - "activity.sourceControl": "Contrôle de source", + "activity.git": "Contrôle de source", + "activity.settings": "Paramètres", "common.save": "Enregistrer", "common.cancel": "Annuler", "common.settings": "Paramètres", @@ -85,6 +86,11 @@ "sidebar.noPostsYet": "Aucun article pour le moment", "sidebar.noPagesYet": "Aucune page pour le moment", "sidebar.noMediaYet": "Aucun média pour le moment", + "sidebar.noScriptsYet": "Aucun script", + "sidebar.noTemplatesYet": "Aucun modèle", + "sidebar.chatPlaceholder": "L'assistant IA apparaîtra ici", + "sidebar.importPlaceholder": "Importer du contenu depuis des sources externes", + "sidebar.gitPlaceholder": "Intégration du contrôle de source", "sidebar.loading": "Chargement...", "sidebar.settingsHeader": "Paramètres", "sidebar.tagsHeader": "Étiquettes", diff --git a/locales/ui/it.json b/locales/ui/it.json index ee812ee..1bca935 100644 --- a/locales/ui/it.json +++ b/locales/ui/it.json @@ -38,9 +38,10 @@ "activity.scripts": "Script", "activity.tags": "Tag", "activity.templates": "Modelli", - "activity.aiAssistant": "Assistente IA", + "activity.chat": "Assistente IA", "activity.import": "Importa", - "activity.sourceControl": "Controllo sorgente", + "activity.git": "Controllo sorgente", + "activity.settings": "Impostazioni", "common.save": "Salva", "common.cancel": "Annulla", "common.settings": "Impostazioni", @@ -85,6 +86,11 @@ "sidebar.noPostsYet": "Nessun post", "sidebar.noPagesYet": "Nessuna pagina", "sidebar.noMediaYet": "Nessun media", + "sidebar.noScriptsYet": "Nessuno script", + "sidebar.noTemplatesYet": "Nessun modello", + "sidebar.chatPlaceholder": "L'assistente IA apparirà qui", + "sidebar.importPlaceholder": "Importa contenuti da fonti esterne", + "sidebar.gitPlaceholder": "Integrazione del controllo sorgente", "sidebar.loading": "Caricamento...", "sidebar.settingsHeader": "Impostazioni", "sidebar.tagsHeader": "Tag",