diff --git a/ROADMAP.md b/ROADMAP.md index bce1cab0c..304db22dc 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,7 @@ # VectorCraft Roadmap +- UI: Window → Help Bar toggles the tool-help strip separately from the status bar and saves visibility with workspaces. Scores and estimates unchanged. + - PDF.R1: editable PDF import uses the shared reader, scans page content in windows, and rejects known incomplete input before returning a document. - PDF.W1: PDF export and print composition use the shared optional writer for configuration, metadata, font preparation and guarded finalization; drawing and output policy remain app-owned. @@ -9,7 +11,7 @@ **Stage: alpha** · next: beta, ~13 points (ready for real work ~62% → ~75%, and reliable `.ai` exchange) and ~170–270 h away -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-02 · **Change:** minor (font-family fields accept the mouse wheel, #5253; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (independent Help Bar visibility; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adobe Illustrator workflow. It runs on macOS, Windows, Linux, FreeBSD and the web (WASM), and agents can drive all of it over MCP, the CLI and a JSON diff --git a/crates/ui-egui/src/app.rs b/crates/ui-egui/src/app.rs index 63bb4de7b..0a178359b 100644 --- a/crates/ui-egui/src/app.rs +++ b/crates/ui-egui/src/app.rs @@ -511,8 +511,11 @@ impl VectorcraftApp { } if self.ui.status_bar && self.ui.screen_mode < 3 { chrome::status_bar(self, ui); + } + if self.ui.help_bar && self.ui.screen_mode < 3 { chrome::hint_bar(self, ui); - } else { + } + if self.ui.screen_mode >= 3 || (!self.ui.status_bar && !self.ui.help_bar) { // Keeps the previous frame's value valid for menus drawn above, before the bars. widgets::set_menu_floor(&ctx, ctx.content_rect().bottom()); } diff --git a/crates/ui-egui/src/chrome/status_bar.rs b/crates/ui-egui/src/chrome/status_bar.rs index 9a1ffbfbb..9fbd02327 100644 --- a/crates/ui-egui/src/chrome/status_bar.rs +++ b/crates/ui-egui/src/chrome/status_bar.rs @@ -4,7 +4,7 @@ use super::{Arc, ROTATE_VIEW_ANGLES, Sense, Stroke, Tokens, Ui, VectorcraftApp, /// Draw the status message, bounded readouts and artboard navigation controls. pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) { let t = Tokens::get(ui.ctx()); - egui::Panel::bottom("status_bar") + let panel = egui::Panel::bottom("status_bar") .exact_size(24.0) .frame(egui::Frame::NONE.fill(t.panel).inner_margin(egui::Margin::symmetric(8, 0)).stroke(Stroke::new(1.0, t.border))) .show(ui, |ui| { @@ -84,6 +84,7 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) { status_readouts(app, ui, &t); }); }); + widgets::set_menu_floor(ui.ctx(), panel.response.rect.top()); } /// The most recently measured status widths, scoped to this UI and display settings. diff --git a/crates/ui-egui/src/chrome/tests.rs b/crates/ui-egui/src/chrome/tests.rs index 774de7540..b4e6fcc34 100644 --- a/crates/ui-egui/src/chrome/tests.rs +++ b/crates/ui-egui/src/chrome/tests.rs @@ -14,6 +14,68 @@ use crate::theme::Tokens; use serde_json::json; use vectorcraft_engine::Session; +#[test] +fn help_bar_and_status_bar_render_independently() { + for (help, status, screen_mode) in [(true, true, 0), (false, true, 0), (true, false, 0), (false, false, 0), (true, true, 3)] { + let (mut app, ctx) = status_readout_app(); + app.ui.help_bar = help; + app.ui.status_bar = status; + app.ui.screen_mode = screen_mode; + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1200.0, 800.0)); + let mut parent = None; + let mut output = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), ..Default::default() }, |ui| { + parent = Some(ui.scope_id()); + app.ui(ui); + }); + output.textures_delta.clear(); + for _ in 0..2 { + output = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), ..Default::default() }, |ui| app.ui(ui)); + output.textures_delta.clear(); + } + fn text(shape: &egui::Shape, out: &mut Vec) { + match shape { + egui::Shape::Text(t) => out.push(t.galley.text().to_string()), + egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| text(s, out)), + _ => {} + } + } + let mut shown = Vec::new(); + for shape in &output.shapes { + text(&shape.shape, &mut shown); + } + assert_eq!(shown.iter().any(|t| t == "?"), help && screen_mode < 3, "{help}/{status}/{screen_mode}: {shown:?}"); + assert_eq!(shown.iter().any(|t| t.contains("Saved recovery data")), status && screen_mode < 3, "{help}/{status}/{screen_mode}: {shown:?}"); + let floor = ctx.data(|data| data.get_temp::(egui::Id::unique("menu_floor"))).unwrap(); + let expected = if screen_mode < 3 && (help || status) { + let id = parent.unwrap().with(if help { "hint_bar" } else { "status_bar" }); + egui::containers::panel::PanelState::load(&ctx, id).unwrap().outer_rect.top() + } else { + screen.bottom() + }; + assert!((floor - expected).abs() < 1.0, "menu floor {floor}, expected {expected}"); + } +} + +#[test] +#[ignore = "Renders the Help Bar visibility examples; set HELP_BAR_SCREENSHOTS to an output directory"] +fn help_bar_visibility_screenshots() { + let dir = std::path::PathBuf::from(std::env::var_os("HELP_BAR_SCREENSHOTS").expect("output directory")); + for shown in [true, false] { + let mut app = crate::VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 500, "height": 400})).unwrap(); + if !shown { + crate::menus::run_ui_command(&mut app, "window.helpBar", &serde_json::Value::Null).unwrap().unwrap(); + } + let mut h = egui_kittest::Harness::builder() + .with_size(egui::vec2(1000.0, 700.0)) + .with_accessibility_check(false) + .wgpu() + .build_ui_state(|ui, app: &mut crate::VectorcraftApp| app.ui(ui), app); + h.run_steps(6); + h.render().unwrap().save(dir.join(if shown { "help-visible.png" } else { "help-hidden.png" })).unwrap(); + } +} + #[test] fn hints_name_keys_as_the_menus_do() { let text = |tool: &str| super::hint_segments(tool).into_iter().map(|(s, _)| s).collect::(); diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 614054f41..df04d2843 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -6111,3 +6111,4 @@ breakText Glyphs Глифи Create Swatch Legend Създаване на легенда на образците Create Swatch Legend… Създаване на легенда на образците… HEX HEX + Help Bar Лента за помощ diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index a0b4e236d..95c7fadfa 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -6145,3 +6145,4 @@ shape Open Otvoren Create Swatch Legend Kreiraj legendu uzoraka boja Create Swatch Legend… Kreiraj legendu uzoraka boja… HEX HEX + Help Bar Traka pomoći diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 2a1f0a518..b656559e4 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -6245,3 +6245,4 @@ breakText Glyphs Glifs Create Swatch Legend Crea una llegenda de mostres Create Swatch Legend… Crea una llegenda de mostres… HEX HEX + Help Bar Barra d’ajuda diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index b9e6fd740..fc7698da6 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -6259,3 +6259,4 @@ breakText Glyphs Glyfy Create Swatch Legend Vytvořit legendu vzorníku Create Swatch Legend… Vytvořit legendu vzorníku… HEX HEX + Help Bar Panel nápovědy diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 20a0a4dfe..c8a92d7d3 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -6127,3 +6127,4 @@ breakText Glyphs Glyffer Create Swatch Legend Opret farveprøveforklaring Create Swatch Legend… Opret farveprøveforklaring… HEX HEX + Help Bar Hjælpelinje diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 94430d21e..1504af382 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5845,3 +5845,4 @@ breakText Glyphs Glyphen Create Swatch Legend Farbfeldlegende erstellen Create Swatch Legend… Farbfeldlegende erstellen… HEX HEX + Help Bar Hilfeleiste diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 0a7d2be71..de8b95f0a 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -6211,3 +6211,4 @@ breakText Glyphs Γλύφοι Create Swatch Legend Δημιουργία υπομνήματος δειγμάτων χρωμάτων Create Swatch Legend… Δημιουργία υπομνήματος δειγμάτων χρωμάτων… HEX HEX + Help Bar Γραμμή βοήθειας diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 4e5ba28d4..ccf04f1f4 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -6201,3 +6201,4 @@ breakText Glyphs Glifos Create Swatch Legend Crear leyenda de muestras Create Swatch Legend… Crear leyenda de muestras… HEX HEX + Help Bar Barra de ayuda diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index c75f15ba5..048efe166 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -6289,3 +6289,4 @@ breakText Glyphs Merkit Create Swatch Legend Luo värimallien selite Create Swatch Legend… Luo värimallien selite… HEX HEX + Help Bar Ohjepalkki diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 20b608bb4..a42c01e8d 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -6254,3 +6254,4 @@ breakText Glyphs Glyphes Create Swatch Legend Créer une légende des nuances Create Swatch Legend… Créer une légende des nuances… HEX HEX + Help Bar Barre d’aide diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 8e4aa69f2..19d46d86c 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -6127,3 +6127,4 @@ breakText Glyphs Glif Create Swatch Legend Buat Legenda Swatch Create Swatch Legend… Buat Legenda Swatch… HEX HEX + Help Bar Bilah Bantuan diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 6fd38b0f7..6ff9da125 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5879,3 +5879,4 @@ breakText Glyphs Glifi Create Swatch Legend Crea legenda campioni Create Swatch Legend… Crea legenda campioni… HEX HEX + Help Bar Barra della guida diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 5096fef13..53c779802 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5944,3 +5944,4 @@ breakText Glyphs 字形 Create Swatch Legend スウォッチの凡例を作成 Create Swatch Legend… スウォッチの凡例を作成… HEX HEX + Help Bar ヘルプバー diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 5389e1f09..937db59f0 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -6247,3 +6247,4 @@ breakText Glyphs გლიფები Create Swatch Legend ფერის ნიმუშების ლეგენდის შექმნა Create Swatch Legend… ფერის ნიმუშების ლეგენდის შექმნა… HEX HEX + Help Bar დახმარების ზოლი diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 8596570c7..550556091 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5840,3 +5840,4 @@ breakText Glyphs 글리프 Create Swatch Legend 견본 범례 만들기 Create Swatch Legend… 견본 범례 만들기… HEX HEX + Help Bar 도움말 표시줄 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 4cde5d403..a32aa110e 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -6171,3 +6171,4 @@ breakText Glyphs Glyphs Create Swatch Legend Stalenlegenda maken Create Swatch Legend… Stalenlegenda maken… HEX HEX + Help Bar Helpbalk diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 52800ade0..0ca41d1c3 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -6114,3 +6114,4 @@ breakText Glyphs Glify Create Swatch Legend Utwórz legendę próbek Create Swatch Legend… Utwórz legendę próbek… HEX HEX + Help Bar Pasek pomocy diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 8369f4d23..2639bf40e 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -6332,3 +6332,4 @@ swatch Yellow Green Verde-amarelado Create Swatch Legend Criar Legenda de Amostras Create Swatch Legend… Criar Legenda de Amostras… HEX HEX + Help Bar Barra de ajuda diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index e6bf3f77c..5b00ad23f 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -6348,3 +6348,4 @@ breakText Glyphs Глифы digits Hindi Хинди font-script Japanese Японский shape Open Открытый + Help Bar Панель подсказок diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index f12351040..49f70795a 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -6315,3 +6315,4 @@ breakText Glyphs Glyfer # The strings the report listed as missing for sv. Font Size: Teckenstorlek: digits Hindi Hindi + Help Bar Hjälpfält diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index b198e1af1..9e9a07513 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -6216,3 +6216,4 @@ breakText Glyphs Glifler Create Swatch Legend Renk Örneği Açıklaması Oluştur Create Swatch Legend… Renk Örneği Açıklaması Oluştur… HEX HEX + Help Bar Yardım Çubuğu diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index d468a63d7..cc19692ed 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5859,3 +5859,4 @@ breakText Glyphs Гліфи Create Swatch Legend Створити легенду зразків Create Swatch Legend… Створити легенду зразків… HEX HEX + Help Bar Панель підказок diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 9d4acddcb..cb106b4ef 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5860,3 +5860,4 @@ breakText Glyphs Glyph Create Swatch Legend Tạo chú giải mẫu màu Create Swatch Legend… Tạo chú giải mẫu màu… HEX HEX + Help Bar Thanh trợ giúp diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 0426b415f..234d02339 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -6100,3 +6100,4 @@ breakText Glyphs 字形 Create Swatch Legend 创建色板图例 Create Swatch Legend… 创建色板图例… HEX HEX + Help Bar 帮助栏 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 92a1a9808..21f1b12af 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5897,3 +5897,4 @@ breakText Glyphs 字符 Create Swatch Legend 建立色票圖例 Create Swatch Legend… 建立色票圖例… HEX HEX + Help Bar 說明列 diff --git a/crates/ui-egui/src/layout_migration.rs b/crates/ui-egui/src/layout_migration.rs index e5c6b1f9c..b56b46720 100644 --- a/crates/ui-egui/src/layout_migration.rs +++ b/crates/ui-egui/src/layout_migration.rs @@ -44,10 +44,12 @@ impl UiState { pub fn read_preferences(bytes: &[u8]) -> Option { let mut saved: Value = serde_json::from_slice(bytes).ok()?; let fields = saved.as_object_mut()?; + help_bar_visibility(fields, "status_bar", "help_bar"); let mut intact = recover(fields, Keys::SNAKE); for key in ["custom_workspaces", "workspace_changes"] { if let Some(Value::Array(workspaces)) = fields.get_mut(key) { for workspace in workspaces.iter_mut().filter_map(Value::as_object_mut) { + help_bar_visibility(workspace, "statusBar", "helpBar"); intact &= recover(workspace, Keys::CAMEL); } } @@ -58,6 +60,15 @@ impl UiState { } } +/// Older layouts used status-bar visibility for both bottom bars. Preserve that choice until +/// the layout has an independent Help Bar setting. +fn help_bar_visibility(fields: &mut Map, status: &str, help: &str) { + if !fields.contains_key(help) { + let visible = fields.get(status).and_then(Value::as_bool).unwrap_or(true); + fields.insert(help.into(), Value::Bool(visible)); + } +} + /// The JSON keys of the docking fields: `UiState` is snake_case, [`Workspace`] camelCase. struct Keys { hidden: &'static str, diff --git a/crates/ui-egui/src/layout_migration/tests.rs b/crates/ui-egui/src/layout_migration/tests.rs index d97567eb1..9540f57bf 100644 --- a/crates/ui-egui/src/layout_migration/tests.rs +++ b/crates/ui-egui/src/layout_migration/tests.rs @@ -14,6 +14,26 @@ fn read(saved: &Value) -> ReadPreferences { UiState::read_preferences(&serde_json::to_vec(saved).unwrap()).unwrap() } +#[test] +fn legacy_help_bar_visibility_follows_each_layouts_status_bar() { + let prefs = read(&json!({ + "status_bar": false, + "custom_workspaces": [{"name":"Hidden", "statusBar": false}, {"name":"Shown", "statusBar":true}], + "workspace_changes": [{"name":"Changed", "statusBar":false}] + })); + assert!(!prefs.ui.help_bar); + assert!(!prefs.ui.custom_workspaces[0].help_bar); + assert!(prefs.ui.custom_workspaces[1].help_bar); + assert!(!prefs.ui.workspace_changes[0].help_bar); + assert!(read(&json!({})).ui.help_bar); + for (status, help) in [(true, false), (false, true)] { + let prefs = read(&json!({"status_bar":status, "help_bar":help, + "custom_workspaces":[{"name":"Explicit", "statusBar":status, "helpBar":help}]})); + assert_eq!(prefs.ui.help_bar, help); + assert_eq!(prefs.ui.custom_workspaces[0].help_bar, help); + } +} + /// The dock of a layout in the current shape: the canvas beside a dock at the end edge. fn dock(layout: &Layout) -> &[Column] { assert!(docking::valid(layout), "{layout:?}"); diff --git a/crates/ui-egui/src/menus/run.rs b/crates/ui-egui/src/menus/run.rs index b788cd58b..1d403f889 100644 --- a/crates/ui-egui/src/menus/run.rs +++ b/crates/ui-egui/src/menus/run.rs @@ -433,6 +433,7 @@ fn run_ui_part5(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option flag(&mut app.ui.control_bar), + "window.helpBar" => flag(&mut app.ui.help_bar), "window.toolbar" => flag(&mut app.ui.toolbar), "window.toolbarColumns" => { let toolbar = match crate::custom_toolbars::target(&app.ui, p) { diff --git a/crates/ui-egui/src/menus/state.rs b/crates/ui-egui/src/menus/state.rs index 523d930e0..0c50df321 100644 --- a/crates/ui-egui/src/menus/state.rs +++ b/crates/ui-egui/src/menus/state.rs @@ -59,6 +59,7 @@ pub fn checked(app: &VectorcraftApp, id: &str, p: &Value) -> Option { // a document no item is checked. "view.rotate" => app.view().zip(p.get("angle").and_then(Value::as_f64)).is_some_and(|(view, a)| (view.rotation - a).abs() < 1e-9), "window.control" => app.ui.control_bar, + "window.helpBar" => app.ui.help_bar, "window.toolbar" => app.ui.toolbar, "window.toolbarAdvanced" => app.ui.toolbar_advanced, "window.toolbarColumns" => match p.get("toolbar").and_then(Value::as_str) { diff --git a/crates/ui-egui/src/menus/tests.rs b/crates/ui-egui/src/menus/tests.rs index 4b46453ca..c3a05c6ed 100644 --- a/crates/ui-egui/src/menus/tests.rs +++ b/crates/ui-egui/src/menus/tests.rs @@ -1,6 +1,29 @@ use super::*; use crate::theme; +#[test] +fn help_bar_toggle_is_independent_and_saved_in_workspaces() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default()); + assert!(app.ui.help_bar); + assert_eq!(checked(&app, "window.helpBar", &Value::Null), Some(true)); + assert_eq!(run_ui_command(&mut app, "window.helpBar", &Value::Null).unwrap().unwrap(), json!(false)); + assert!(app.ui.status_bar); + assert_eq!(checked(&app, "window.helpBar", &Value::Null), Some(false)); + let saved = crate::workspaces::capture(&app.ui, "No help"); + let saved: crate::workspaces::Workspace = serde_json::from_value(serde_json::to_value(saved).unwrap()).unwrap(); + app.ui.help_bar = true; + app.ui.status_bar = false; + crate::workspaces::apply(&mut app.ui, &saved); + assert!(!app.ui.help_bar && app.ui.status_bar); + assert_eq!(run_ui_command(&mut app, "window.helpBar", &Value::Null).unwrap().unwrap(), json!(true)); + let legacy: crate::workspaces::Workspace = serde_json::from_value(json!({"name":"Old workspace"})).unwrap(); + assert!(legacy.help_bar); + let restored: crate::state::UiState = serde_json::from_value(serde_json::to_value(&app.ui).unwrap()).unwrap(); + assert!(restored.help_bar); + let (_, window) = menu_tree().into_iter().find(|(title, _)| *title == "Window").unwrap(); + assert!(window.iter().any(|item| matches!(item, Item::Cmd("Help Bar", "window.helpBar", _)))); +} + /// One headless frame of the in-window menu bar; returns its titles (left to right) as /// (rect, id of the title's popup). fn bar_frame(app: &mut VectorcraftApp, ctx: &egui::Context, events: Vec) -> Vec<(egui::Rect, egui::Id)> { diff --git a/crates/ui-egui/src/menus/tree.rs b/crates/ui-egui/src/menus/tree.rs index 3a55ab37c..987453567 100644 --- a/crates/ui-egui/src/menus/tree.rs +++ b/crates/ui-egui/src/menus/tree.rs @@ -622,6 +622,7 @@ pub fn menu_tree_named(english_names: bool) -> Vec<(&'static str, Vec)> { sub("Workspace", crate::workspaces::menu_items()), Sep, c("Control", "window.control"), + c("Help Bar", "window.helpBar"), c("Contextual Task Bar", "window.taskBar"), c("Tools", "window.toolbar"), sub("Toolbars", crate::custom_toolbars::menu_items()), diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index 97b7926e8..4242f29b2 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -441,6 +441,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ ), ("view.rotateReset", "Reset Rotate View", "Cmd+Shift+1", "{}"), ("window.control", "Control", "", "{}"), + ("window.helpBar", "Help Bar", "", "{} toggle the tool-help strip independently of the status bar; returns the new visibility"), ("window.toolbar", "Tools", "", "{}"), ( "window.toolbarColumns", diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index 4dd6d9012..83dede42b 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -432,6 +432,7 @@ pub struct UiState { #[serde(default)] pub toolbar_pos: Option<[f32; 2]>, pub status_bar: bool, + pub help_bar: bool, pub dock: bool, /// The bars that Tab (`window.dock`) hid, which the next Tab shows again; none when Tab has not /// hidden them since it last showed them, or a workspace was applied since. @@ -940,6 +941,7 @@ impl Default for UiState { dock_width: None, toolbar_pos: None, status_bar: true, + help_bar: true, dock: true, tab_hid: None, view: ViewFlags::default(), diff --git a/crates/ui-egui/src/workspaces.rs b/crates/ui-egui/src/workspaces.rs index 28abe518b..0251bc82d 100644 --- a/crates/ui-egui/src/workspaces.rs +++ b/crates/ui-egui/src/workspaces.rs @@ -32,6 +32,7 @@ pub struct Workspace { pub dock_collapsed: bool, pub open_panel: Option, pub status_bar: bool, + pub help_bar: bool, /// Tool groups torn off the toolbar into floating strips (built-in workspaces: none). pub floating_flyouts: Vec, /// Floating panel groups in workspaces saved before the canvas joined `docking`; read only @@ -185,6 +186,7 @@ pub fn capture(ui: &UiState, name: &str) -> Workspace { dock_collapsed: ui.dock_collapsed, open_panel: ui.open_panel.clone(), status_bar: ui.status_bar, + help_bar: ui.help_bar, floating_flyouts: ui.floating_flyouts.clone(), floating_panels: Vec::new(), docking: ui.docking.clone(), @@ -212,6 +214,7 @@ pub fn apply(ui: &mut UiState, w: &Workspace) { ui.dock_collapsed = w.dock_collapsed; ui.open_panel = w.open_panel.clone(); ui.status_bar = w.status_bar; + ui.help_bar = w.help_bar; ui.floating_flyouts = w.floating_flyouts.clone(); ui.docking_generation = ui.docking_generation.wrapping_add(1); ui.docking = w.docking.clone().filter(crate::panel_docking::valid); diff --git a/docs/gaps.md b/docs/gaps.md index 783dc9711..f5c455306 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -1,9 +1,11 @@ # Gaps -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (G15: a compound path's assembled path is kept between frames; estimate unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (independent Help Bar visibility; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence +Window → Help Bar toggles the bottom tool-help strip independently of the status bar. Its visibility is saved with workspaces and UI preferences; Presentation Mode hides both bars. Scores and estimates are unchanged. + The closed font-family fields in Character, Properties and Control now accept the mouse wheel for selected text (#5253). Each effective notch applies one family and adds one undo step; the list follows saved font filters, ignores search text, and @@ -26,6 +28,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | Date | Change | Summary | |---|---|---| +| 2026-11-03 | minor | Help Bar has its own Window menu toggle and workspace visibility setting. Scores and estimates unchanged. | | 2026-11-02 | minor | Font-family fields step selected text with the mouse wheel (#5253); no scored gap or estimate reassessed. | | 2026-10-24 | minor | G12: General › Corner Radius is the Rounded Rectangle tool's radius, a change while the tool is active reaches it, and the radius ↑/↓ set while drawing (or a Tool Options value) becomes the preference, within 0–1296 pt (#394, `crates/engine/src/tests/constrain_tools.rs`); no entry closed, estimate unchanged. | | 2026-10-22 | minor | Reconcile upstream documentation after review F-3; preserve every dated revision and the original scaleArt runtime evidence without repeating completed jobs. | diff --git a/docs/roadmap.md b/docs/roadmap.md index bf5f67279..18cb6a340 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,9 +1,11 @@ # Roadmap: milestones and what's next -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (font-family fields accept the mouse wheel, #5253; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (independent Help Bar visibility; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence +Window → Help Bar toggles the bottom tool-help strip independently of the status bar. Its visibility is saved with workspaces and UI preferences; Presentation Mode hides both bars. Scores and estimates are unchanged. + **Feature evidence last updated:** 2026-11-03. The shared assessment status line and revision history are current upstream; this dated section records the submission's feature evidence and revisions without rescoring upstream or claiming a new combined build or runtime measurement. @@ -19,6 +21,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | Date | Change | Summary | |---|---|---| +| 2026-11-03 | minor | Help Bar has its own Window menu toggle and workspace visibility setting. Scores and estimates unchanged. | | 2026-10-30 | minor | M4: G9: `.ai`/EPS editing data: a blend takes the transparency (opacity, blend mode, knockout) in its state block after its record, so its steps knock each other out as the page draws them; a user's file comes in 0.6% from its page instead of 1.3%. Scores and estimates unchanged. | | 2026-10-30 | minor | M4: File formats: `.ai`/EPS editing-data fidelity from an audit of a user's files: All Caps and Small Caps (#4708); placed files at their box's corner, kept where they draw closer to the page (#4664, #4723, #4742); pages cropped to the art, with `UserUnit` (#4658, #4727); gradient mesh corners with handle weights of 0 and the state written after a mesh (#4738, #4744, #4748); type on a path spaced as the file lays it out (#4743); synthetic regressions in `crates/eps`; scores and estimates unchanged. | | 2026-10-30 | minor | Raster effects: bounded length fields in effect dialogs clamp on commit and reject invalid inputs visibly (#4638); estimates unchanged. | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 15d40489a..497b507b5 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1,9 +1,11 @@ # Parity with Adobe Illustrator -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (font-family fields accept the mouse wheel, #5253; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026; Units › East Asian Type acts, with Q and Ha units; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (independent Help Bar visibility; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026; Units › East Asian Type acts, with Q and Ha units; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026) ## Feature submission evidence +Window → Help Bar toggles the bottom tool-help strip independently of the status bar. Its visibility is saved with workspaces and UI preferences; Presentation Mode hides both bars. Scores and estimates are unchanged. + **Feature evidence last updated:** 2026-11-03. The shared assessment status line and revision history are current upstream; this dated section records the submission's feature evidence and revisions without rescoring upstream or claiming a new combined build or runtime measurement. @@ -19,6 +21,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | Date | Change | Summary | |---|---|---| +| 2026-11-03 | minor | Help Bar has its own Window menu toggle and workspace visibility setting. Scores and estimates unchanged. | | 2026-11-02 | minor | Legacy `.ai` saves (version 7) write type with rotated characters as outlines that keep each character's rotation, with a warning unless `outlineText` is on (#5186); scores unchanged. | | 2026-10-31 | minor | Object › Path › Smooth smooths a path along its length before refitting it, so Auto-Smooth takes a shaky line to one smooth curve instead of leaving its jags, and with anchor points direct-selected it smooths only that section; scores unchanged. | | 2026-10-31 | minor | New Document: More Settings has the Save Preset button beside the name too, saving its settings (artboard layout included) as a preset (#4893); scores unchanged. | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index ae4eb5626..1fdcc0b9e 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,9 +1,11 @@ # UI and interaction parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (font-family fields accept the mouse wheel, #5253; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-03 · **Change:** minor (independent Help Bar visibility; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence +Window → Help Bar toggles the bottom tool-help strip independently of the status bar. Its visibility is saved with workspaces and UI preferences; Presentation Mode hides both bars. Scores and estimates are unchanged. + **Feature evidence last updated:** 2026-11-03. The shared assessment status line and revision history are current upstream; this dated section records the submission's feature evidence and revisions without rescoring upstream or claiming a new combined build or runtime measurement. @@ -19,6 +21,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | Date | Change | Summary | |---|---|---| +| 2026-11-03 | minor | Help Bar has its own Window menu toggle and workspace visibility setting. Scores and estimates unchanged. | | 2026-10-26 | minor | Pencil Fidelity goes down to 0.01 pt (dialog, tool and engine; Smooth keeps 0.05) on a logarithmic slider across the Tool Options row, with hundredths in the field and key steps that fit the range (#3328); scores and estimates unchanged (estimated) | | 2026-10-22 | minor | Reconcile upstream documentation after review F-3; preserve every dated revision and the original scaleArt runtime evidence without repeating completed jobs. | | 2026-10-22 | minor | Reconcile upstream documentation after review F-2; preserve every dated revision and the original scaleArt runtime evidence without repeating completed jobs. |