From 1fe78aec6679b49791aae326dcde93e86e400da3 Mon Sep 17 00:00:00 2001 From: Yarila Date: Sat, 17 Oct 2026 22:01:33 +0500 Subject: [PATCH 1/3] M7: respect theme in presenter controls and backdrop --- ROADMAP.md | 1 + crates/doc/src/proto.rs | 11 +++++++++++ crates/engine/src/cmd/proto.rs | 1 + crates/engine/src/proto_present_tests.rs | 15 +++++++++++++++ crates/ui-egui/src/present.rs | 17 +++++------------ crates/ui-egui/src/proto_panel.rs | 7 ++++++- docs/ui-parity.md | 1 + 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 8a587286..74953dd5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -316,6 +316,7 @@ Ranked; detail and milestones in [`docs/roadmap.md`](docs/roadmap.md). | Date | Change | Summary | |---|---|---| +| 2026-10-17 | minor | The presenter device and scale menus follow the active UI theme. The default presentation backdrop uses the theme canvas token, while a background explicitly selected in Prototype settings remains saved. Numbers unchanged. | | 2026-10-17 | minor | Community batch #753 (10 PRs): Selection colors for mixed selections (#403); smart-selection spacing fields and canvas handles (#402); a Present tab with device and scale controls (#537); adding auto layout to an empty frame keeps its size (#369); `node.set` applies auto layout keys to frames only and warns about font styles a family lacks (#603); canvas images on whole device pixels (#359); Adwaita window controls on Wayland without server decorations (#592, not run on GNOME or KDE by the maintainers); the keyboard shortcuts panel closes on the home screen (#548); macOS paste modifiers in tests (#750); credits column tests (#347). `cargo xtask parity` re-run: 212 / 212 menu items (100%; the catalog gained View ▸ Full screen with #227 on 2026-10-14), 561 commands. Language table re-measured from the catalogs (2,272 entries): the drafts read 95–99% (strings added since 2026-10-12 show English), Indonesian has a draft (99%; it read none), and Russian, Swedish (its command labels from #599), Turkish and Czech are listed with their counts. Per-area parity rows for the day's landings in `docs/gaps.md`, `target-app-parity.md` and the area docs. Numbers unchanged | | 2026-10-17 | minor | Control channel while the window is minimised (M8, #720): a minimised or covered window answers every request that acts on the app's state (edits, `ui.inspect`, `ui.pointer`, `ui.key`, `ui.type`, `ui.set`, `app.quit`…) without drawing, and what needs a drawn frame (`ui.screenshot`, `ui.input`, `window.new`) is an error at once; before, those waited up to 60 s and held up every later request on the connection (an attached agent's `inspect_ui` or window screenshot stalled until the window came back). Measured on Windows 11, minimised: requests ~100 ms each (eframe wakes a minimised window at most ten times a second; ~2 ms on screen), a window screenshot fails in ~100 ms instead of 60 s, idle CPU 0. Numbers unchanged. | | 2026-10-17 | minor | Community fixes from #524 up (batches #727, #728, #733, #735, #736 and #729; 55 PRs) and a crash guard. `.fig` import: explicit instance identity, overrides by key, text style precedence, grid track sizes, slot content, connector labels, skip ink, paragraph options and hanging lists, list start numbers, vertical trim, per-point stroke joins, text on a path, image adjustments and embedded images; large files open within budgets from the file's own size; an import report after opening; imported bytes stay out of the command journal. Files and paste: save a copy as `.fig`; Cmd/Ctrl+V pastes images copied in other apps and layers copied from another design app (`edit.pasteDesignLayers`); paste over and paste properties; clipboard reads on Linux and the BSDs time out after 2 s. Editor: live font preview on hover; Shift+I and Resources open Assets; Shift-drag moves the selection; thin strokes take a nearby double-click; dropped points merge; Flatten keeps open paths; the Actions palette gets tabs, ranked search, a keyboard highlight and recents; the colour picker opens on a new fill or stroke; text strokes follow their alignment; selection highlights after line breaks; a draggable pages/layers divider; variant properties reorder by drag; submenus keep their hover with a safe triangle; menu actions that need a value ask for it; File ▸ Rename edits the name in place. Engine: bad paint indexes, non-finite geometry and pages in copy/cut are refused; a param naming a layer says so when it isn't one; a CLI script is one undo step and resolves `$step`. Server and collaboration: message limits follow `max_file_mb` (client capped at 1 GiB); a damaged generation field no longer drops flushed edits; the web build joins a session from a `#join=` link. Also Swedish command labels, Nix CI through the shared workflow, paint bounds once per render, and a guard that turns a panic inside the boolean-op library into bad input (the stroke draws unclipped). Numbers unchanged. | diff --git a/crates/doc/src/proto.rs b/crates/doc/src/proto.rs index a844774c..e89a3ed7 100644 --- a/crates/doc/src/proto.rs +++ b/crates/doc/src/proto.rs @@ -803,6 +803,10 @@ impl DeviceRotation { pub struct PrototypeSettings { pub device: String, pub background: figcraft_color::Color, + /// Distinguishes a user-selected colour from the legacy default so the presenter can use + /// its theme canvas token until a background is explicitly chosen. + #[serde(skip_serializing_if = "is_false")] + pub background_custom: bool, /// Applies to devices that turn (phones and tablets). pub rotation: DeviceRotation, /// The device's colour: one of its model's [`Device::colors`] by name (empty or one the model @@ -823,6 +827,7 @@ impl Default for PrototypeSettings { Self { device: "None".into(), background: figcraft_color::Color::from_rgb8_const(0x1E, 0x1E, 0x1E), + background_custom: false, rotation: DeviceRotation::Portrait, color: String::new(), custom_size: PrototypeSettings::CUSTOM_DEFAULT, @@ -859,6 +864,12 @@ pub struct Device { } impl PrototypeSettings { + /// Whether presentation should use the UI theme's canvas token. Old files only stored a + /// colour, so their original default is also treated as automatic. + pub fn uses_theme_background(&self) -> bool { + !self.background_custom && self.background == Self::default().background + } + /// The devices to pick from ("None" presents the frame at its own size). pub const DEVICES: [Device; 19] = [ Device { diff --git a/crates/engine/src/cmd/proto.rs b/crates/engine/src/cmd/proto.rs index be6907b5..438290e6 100644 --- a/crates/engine/src/cmd/proto.rs +++ b/crates/engine/src/cmd/proto.rs @@ -187,6 +187,7 @@ fn settings(s: &mut Session, p: &Value) -> Result { let mut next = prototype.clone(); if let Some(c) = bg { next.background = c; + next.background_custom = true; } if let Some(d) = dev { next.device = d; diff --git a/crates/engine/src/proto_present_tests.rs b/crates/engine/src/proto_present_tests.rs index 9d2bc48b..c48f3ead 100644 --- a/crates/engine/src/proto_present_tests.rs +++ b/crates/engine/src/proto_present_tests.rs @@ -16,6 +16,21 @@ fn settings(s: &Session) -> PrototypeSettings { } } +#[test] +fn prototype_background_uses_the_theme_until_a_colour_is_chosen() { + let mut s = Session::new(); + assert!(settings(&s).uses_theme_background()); + // Choosing even the old default explicitly must keep it through a file round trip. + s.execute("proto.settings", &json!({"background": "#1E1E1E"})).unwrap(); + assert!(!settings(&s).uses_theme_background()); + let bytes = figcraft_format::save_bytes(s.doc().unwrap(), None).unwrap(); + let (doc, _) = figcraft_format::load_bytes(&bytes).unwrap(); + let NodeKind::Page { prototype, .. } = &doc.node(doc.pages[0]).unwrap().kind else { panic!("not a page") }; + assert!(!prototype.uses_theme_background()); + s.execute("edit.undo", &json!({})).unwrap(); + assert!(settings(&s).uses_theme_background()); +} + #[test] fn device_rotation_turns_phones_and_tablets_and_is_saved() { let mut s = Session::new(); diff --git a/crates/ui-egui/src/present.rs b/crates/ui-egui/src/present.rs index cc78c86b..7ce5e9b7 100644 --- a/crates/ui-egui/src/present.rs +++ b/crates/ui-egui/src/present.rs @@ -1788,8 +1788,7 @@ fn navigation_bar(p: &mut Presenter, ctx: &egui::Context, now: f64) -> Option prototype.clone(), _ => proto::PrototypeSettings::default(), }; - let bg = settings.background; + let bg = if settings.uses_theme_background() { + let canvas = Tokens::get(ui.ctx()).canvas; + figcraft_color::Color::from_rgb8_const(canvas.r(), canvas.g(), canvas.b()) + } else { + settings.background + }; let model = settings.model(); // Phones and tablets turn: a toggle beside the device picker. let rotates = model.is_some_and(|d| d.rotates); diff --git a/docs/ui-parity.md b/docs/ui-parity.md index dcda2c4e..188b2168 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -174,6 +174,7 @@ in the table. `crates/ui-egui/src/shortcut_keys_tests.rs` drives the keys a look |---|---|---| | 2026-10-17 | minor | Escape or Enter while the pen draws ends the path and keeps its vector in edit mode with the pen, so the next path is drawn into the same layer (unconnected); another Escape leaves edit mode (#760). Entering edit mode on a layer that already is a vector no longer records an empty undo step. Numbers unchanged | | 2026-10-17 | minor | Text layers keep the platform IME on for every frame of an edit, not only frames with input (an idle frame turned it off and macOS dropped each composition, #747); the candidate window opens at the caret instead of the canvas corner; keys pressed while composing go to the IME | +| 2026-10-17 | minor | Presenter device and scale menus use the active theme; the default backdrop uses the theme canvas token, preserving explicit Prototype background choices. Checked in the Windows app. Estimates unchanged. | | 2026-10-17 | minor | The community batches #733, #735, #736, #729 and #753: submenus open after a short hover, keep their place along a safe triangle and reopen closed (main and context menus); the Actions palette's tabs, ranked search, keyboard highlight and recents; menu items that need a value ask for it, File ▸ Rename edits in place; Shift+I and Resources open Assets; a draggable Pages/Layers divider; Shift-drag moves the selection; a double-click near a thin stroke, merging dropped points and Flatten keeping open paths in vector editing; live font preview and the selection highlight after line breaks; the colour picker on a new fill or stroke; Paste over and Paste properties from the keyboard; Selection colors (partial: no library styles or variables there) and smart-selection spacing fields and canvas handles (partial: no reorder rings), both also checked by their author on macOS; the keyboard shortcuts panel closes on the home screen; Adwaita window controls on Wayland compositors without server decorations (checked by its author on GNOME; KDE not run). Checked in the app on Windows (submenus by their author), not side by side with Figma. Summary and hours unchanged | | 2026-10-15 | minor | Hex fields take CSS colour names (`red` → `FF0000`): fills, strokes, effects, grids, variables, backgrounds and the colour picker | | 2026-10-15 | minor | Typography: drag the line-height, letter-spacing and paragraph-spacing icons to scrub their values; shared numeric-field rates, resize cursor and one Undo step per gesture (#373). | From 0f8e29bc9fd804f13d957bcf9c3ab6342fd709ff Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sun, 18 Oct 2026 11:35:34 +0700 Subject: [PATCH 2/3] #772: drop the progress-doc edits (maintainer adds them) --- ROADMAP.md | 1 - docs/ui-parity.md | 1 - 2 files changed, 2 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 74953dd5..8a587286 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -316,7 +316,6 @@ Ranked; detail and milestones in [`docs/roadmap.md`](docs/roadmap.md). | Date | Change | Summary | |---|---|---| -| 2026-10-17 | minor | The presenter device and scale menus follow the active UI theme. The default presentation backdrop uses the theme canvas token, while a background explicitly selected in Prototype settings remains saved. Numbers unchanged. | | 2026-10-17 | minor | Community batch #753 (10 PRs): Selection colors for mixed selections (#403); smart-selection spacing fields and canvas handles (#402); a Present tab with device and scale controls (#537); adding auto layout to an empty frame keeps its size (#369); `node.set` applies auto layout keys to frames only and warns about font styles a family lacks (#603); canvas images on whole device pixels (#359); Adwaita window controls on Wayland without server decorations (#592, not run on GNOME or KDE by the maintainers); the keyboard shortcuts panel closes on the home screen (#548); macOS paste modifiers in tests (#750); credits column tests (#347). `cargo xtask parity` re-run: 212 / 212 menu items (100%; the catalog gained View ▸ Full screen with #227 on 2026-10-14), 561 commands. Language table re-measured from the catalogs (2,272 entries): the drafts read 95–99% (strings added since 2026-10-12 show English), Indonesian has a draft (99%; it read none), and Russian, Swedish (its command labels from #599), Turkish and Czech are listed with their counts. Per-area parity rows for the day's landings in `docs/gaps.md`, `target-app-parity.md` and the area docs. Numbers unchanged | | 2026-10-17 | minor | Control channel while the window is minimised (M8, #720): a minimised or covered window answers every request that acts on the app's state (edits, `ui.inspect`, `ui.pointer`, `ui.key`, `ui.type`, `ui.set`, `app.quit`…) without drawing, and what needs a drawn frame (`ui.screenshot`, `ui.input`, `window.new`) is an error at once; before, those waited up to 60 s and held up every later request on the connection (an attached agent's `inspect_ui` or window screenshot stalled until the window came back). Measured on Windows 11, minimised: requests ~100 ms each (eframe wakes a minimised window at most ten times a second; ~2 ms on screen), a window screenshot fails in ~100 ms instead of 60 s, idle CPU 0. Numbers unchanged. | | 2026-10-17 | minor | Community fixes from #524 up (batches #727, #728, #733, #735, #736 and #729; 55 PRs) and a crash guard. `.fig` import: explicit instance identity, overrides by key, text style precedence, grid track sizes, slot content, connector labels, skip ink, paragraph options and hanging lists, list start numbers, vertical trim, per-point stroke joins, text on a path, image adjustments and embedded images; large files open within budgets from the file's own size; an import report after opening; imported bytes stay out of the command journal. Files and paste: save a copy as `.fig`; Cmd/Ctrl+V pastes images copied in other apps and layers copied from another design app (`edit.pasteDesignLayers`); paste over and paste properties; clipboard reads on Linux and the BSDs time out after 2 s. Editor: live font preview on hover; Shift+I and Resources open Assets; Shift-drag moves the selection; thin strokes take a nearby double-click; dropped points merge; Flatten keeps open paths; the Actions palette gets tabs, ranked search, a keyboard highlight and recents; the colour picker opens on a new fill or stroke; text strokes follow their alignment; selection highlights after line breaks; a draggable pages/layers divider; variant properties reorder by drag; submenus keep their hover with a safe triangle; menu actions that need a value ask for it; File ▸ Rename edits the name in place. Engine: bad paint indexes, non-finite geometry and pages in copy/cut are refused; a param naming a layer says so when it isn't one; a CLI script is one undo step and resolves `$step`. Server and collaboration: message limits follow `max_file_mb` (client capped at 1 GiB); a damaged generation field no longer drops flushed edits; the web build joins a session from a `#join=` link. Also Swedish command labels, Nix CI through the shared workflow, paint bounds once per render, and a guard that turns a panic inside the boolean-op library into bad input (the stroke draws unclipped). Numbers unchanged. | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 188b2168..dcda2c4e 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -174,7 +174,6 @@ in the table. `crates/ui-egui/src/shortcut_keys_tests.rs` drives the keys a look |---|---|---| | 2026-10-17 | minor | Escape or Enter while the pen draws ends the path and keeps its vector in edit mode with the pen, so the next path is drawn into the same layer (unconnected); another Escape leaves edit mode (#760). Entering edit mode on a layer that already is a vector no longer records an empty undo step. Numbers unchanged | | 2026-10-17 | minor | Text layers keep the platform IME on for every frame of an edit, not only frames with input (an idle frame turned it off and macOS dropped each composition, #747); the candidate window opens at the caret instead of the canvas corner; keys pressed while composing go to the IME | -| 2026-10-17 | minor | Presenter device and scale menus use the active theme; the default backdrop uses the theme canvas token, preserving explicit Prototype background choices. Checked in the Windows app. Estimates unchanged. | | 2026-10-17 | minor | The community batches #733, #735, #736, #729 and #753: submenus open after a short hover, keep their place along a safe triangle and reopen closed (main and context menus); the Actions palette's tabs, ranked search, keyboard highlight and recents; menu items that need a value ask for it, File ▸ Rename edits in place; Shift+I and Resources open Assets; a draggable Pages/Layers divider; Shift-drag moves the selection; a double-click near a thin stroke, merging dropped points and Flatten keeping open paths in vector editing; live font preview and the selection highlight after line breaks; the colour picker on a new fill or stroke; Paste over and Paste properties from the keyboard; Selection colors (partial: no library styles or variables there) and smart-selection spacing fields and canvas handles (partial: no reorder rings), both also checked by their author on macOS; the keyboard shortcuts panel closes on the home screen; Adwaita window controls on Wayland compositors without server decorations (checked by its author on GNOME; KDE not run). Checked in the app on Windows (submenus by their author), not side by side with Figma. Summary and hours unchanged | | 2026-10-15 | minor | Hex fields take CSS colour names (`red` → `FF0000`): fills, strokes, effects, grids, variables, backgrounds and the colour picker | | 2026-10-15 | minor | Typography: drag the line-height, letter-spacing and paragraph-spacing icons to scrub their values; shared numeric-field rates, resize cursor and one Undo step per gesture (#373). | From 1768be296522671489f755e0c9c566eb3b411ef0 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sun, 18 Oct 2026 11:35:38 +0700 Subject: [PATCH 3/3] #772: leave the presentation backdrop as the file sets it (owner decision) --- crates/doc/src/proto.rs | 11 ----------- crates/engine/src/cmd/proto.rs | 1 - crates/engine/src/proto_present_tests.rs | 15 --------------- crates/ui-egui/src/present.rs | 3 +-- crates/ui-egui/src/proto_panel.rs | 7 +------ 5 files changed, 2 insertions(+), 35 deletions(-) diff --git a/crates/doc/src/proto.rs b/crates/doc/src/proto.rs index e89a3ed7..a844774c 100644 --- a/crates/doc/src/proto.rs +++ b/crates/doc/src/proto.rs @@ -803,10 +803,6 @@ impl DeviceRotation { pub struct PrototypeSettings { pub device: String, pub background: figcraft_color::Color, - /// Distinguishes a user-selected colour from the legacy default so the presenter can use - /// its theme canvas token until a background is explicitly chosen. - #[serde(skip_serializing_if = "is_false")] - pub background_custom: bool, /// Applies to devices that turn (phones and tablets). pub rotation: DeviceRotation, /// The device's colour: one of its model's [`Device::colors`] by name (empty or one the model @@ -827,7 +823,6 @@ impl Default for PrototypeSettings { Self { device: "None".into(), background: figcraft_color::Color::from_rgb8_const(0x1E, 0x1E, 0x1E), - background_custom: false, rotation: DeviceRotation::Portrait, color: String::new(), custom_size: PrototypeSettings::CUSTOM_DEFAULT, @@ -864,12 +859,6 @@ pub struct Device { } impl PrototypeSettings { - /// Whether presentation should use the UI theme's canvas token. Old files only stored a - /// colour, so their original default is also treated as automatic. - pub fn uses_theme_background(&self) -> bool { - !self.background_custom && self.background == Self::default().background - } - /// The devices to pick from ("None" presents the frame at its own size). pub const DEVICES: [Device; 19] = [ Device { diff --git a/crates/engine/src/cmd/proto.rs b/crates/engine/src/cmd/proto.rs index 438290e6..be6907b5 100644 --- a/crates/engine/src/cmd/proto.rs +++ b/crates/engine/src/cmd/proto.rs @@ -187,7 +187,6 @@ fn settings(s: &mut Session, p: &Value) -> Result { let mut next = prototype.clone(); if let Some(c) = bg { next.background = c; - next.background_custom = true; } if let Some(d) = dev { next.device = d; diff --git a/crates/engine/src/proto_present_tests.rs b/crates/engine/src/proto_present_tests.rs index c48f3ead..9d2bc48b 100644 --- a/crates/engine/src/proto_present_tests.rs +++ b/crates/engine/src/proto_present_tests.rs @@ -16,21 +16,6 @@ fn settings(s: &Session) -> PrototypeSettings { } } -#[test] -fn prototype_background_uses_the_theme_until_a_colour_is_chosen() { - let mut s = Session::new(); - assert!(settings(&s).uses_theme_background()); - // Choosing even the old default explicitly must keep it through a file round trip. - s.execute("proto.settings", &json!({"background": "#1E1E1E"})).unwrap(); - assert!(!settings(&s).uses_theme_background()); - let bytes = figcraft_format::save_bytes(s.doc().unwrap(), None).unwrap(); - let (doc, _) = figcraft_format::load_bytes(&bytes).unwrap(); - let NodeKind::Page { prototype, .. } = &doc.node(doc.pages[0]).unwrap().kind else { panic!("not a page") }; - assert!(!prototype.uses_theme_background()); - s.execute("edit.undo", &json!({})).unwrap(); - assert!(settings(&s).uses_theme_background()); -} - #[test] fn device_rotation_turns_phones_and_tablets_and_is_saved() { let mut s = Session::new(); diff --git a/crates/ui-egui/src/present.rs b/crates/ui-egui/src/present.rs index 7ce5e9b7..36eceb86 100644 --- a/crates/ui-egui/src/present.rs +++ b/crates/ui-egui/src/present.rs @@ -2088,8 +2088,7 @@ fn player(p: &mut Presenter, ui: &mut Ui, rect: Rect, now: f64, chrome: Chrome) p.clock = now; let bar = Rect::from_min_size(rect.min, vec2(rect.width(), chrome.bar_h)); let area = Rect::from_min_max(pos2(rect.min.x, bar.max.y), rect.max); - let settings = p.doc.as_ref().map(|d| page_settings(d, p.current)).unwrap_or_default(); - let bg = if settings.uses_theme_background() { Tokens::get(ui.ctx()).canvas } else { crate::theme::doc_c32(ui.ctx(), settings.background) }; + let bg = crate::theme::doc_c32(ui.ctx(), p.doc.as_ref().map(|d| page_settings(d, p.current)).unwrap_or_default().background); let r = chrome.round; ui.painter().rect_filled(area, CornerRadius { nw: 0, ne: 0, sw: r, se: r }, bg); let action = top_bar(p, ui, bar, now, chrome); diff --git a/crates/ui-egui/src/proto_panel.rs b/crates/ui-egui/src/proto_panel.rs index 3bec9f87..47312e99 100644 --- a/crates/ui-egui/src/proto_panel.rs +++ b/crates/ui-egui/src/proto_panel.rs @@ -401,12 +401,7 @@ fn settings(app: &mut FigApp, ui: &mut Ui, doc: &Document, page: NodeId) { Some(NodeKind::Page { prototype, .. }) => prototype.clone(), _ => proto::PrototypeSettings::default(), }; - let bg = if settings.uses_theme_background() { - let canvas = Tokens::get(ui.ctx()).canvas; - figcraft_color::Color::from_rgb8_const(canvas.r(), canvas.g(), canvas.b()) - } else { - settings.background - }; + let bg = settings.background; let model = settings.model(); // Phones and tablets turn: a toggle beside the device picker. let rotates = model.is_some_and(|d| d.rotates);