From 1900c9429d5d065b8d6f0c4eef59c805fb1135de Mon Sep 17 00:00:00 2001 From: Don Omondi <282319807+mockydevs@users.noreply.github.com> Date: Thu, 12 Nov 2026 18:04:20 -0500 Subject: [PATCH] Swatches: the panel menu's Show Recent Colors shows or hides the Recent Colors row (#6617) --- crates/ui-egui/src/panels/swatches.rs | 5 +- crates/ui-egui/src/panels/swatches/actions.rs | 4 ++ crates/ui-egui/src/panels/swatches/body.rs | 12 ++-- crates/ui-egui/src/panels/swatches/tests.rs | 58 +++++++++++++++++++ crates/ui-egui/src/state.rs | 5 ++ docs/target-app-parity.md | 4 +- 6 files changed, 80 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/panels/swatches.rs b/crates/ui-egui/src/panels/swatches.rs index 9682904538..bfc17c6687 100644 --- a/crates/ui-egui/src/panels/swatches.rs +++ b/crates/ui-egui/src/panels/swatches.rs @@ -1,5 +1,6 @@ -//! Swatches panel: proxy, Recent Colors, find field, thumbnail grid (15.5 pt tiles on a 17 pt pitch) -//! or list, colour groups as folders, None/Registration first, bottom bar and panel menu. +//! Swatches panel: proxy, Recent Colors (hidden by the panel menu's Show Recent Colors), find +//! field, thumbnail grid (15.5 pt tiles on a 17 pt pitch) or list, colour groups as folders, +//! None/Registration first, bottom bar and panel menu. //! //! Swatches drag to reorder, into and out of colour groups (`swatch.move`) and onto art (the active //! proxy's paint command with the object's id) and onto the Gradient panel's ramp; a Fill/Stroke diff --git a/crates/ui-egui/src/panels/swatches/actions.rs b/crates/ui-egui/src/panels/swatches/actions.rs index be275c108b..06c8935d30 100644 --- a/crates/ui-egui/src/panels/swatches/actions.rs +++ b/crates/ui-egui/src/panels/swatches/actions.rs @@ -182,6 +182,10 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) { if menu_item(ui, tl!("Show Find Field"), true, find) { set_pstate(ui.ctx(), "swatch-show-find", !find); } + let recent = app.ui.swatches_show_recent_colors; + if menu_item(ui, tl!("Show Recent Colors"), true, recent) { + app.ui.swatches_show_recent_colors = !recent; + } ui.separator(); if menu_item(ui, tl!("Swatch Options…"), opts.is_some(), false) && let Some(n) = opts diff --git a/crates/ui-egui/src/panels/swatches/body.rs b/crates/ui-egui/src/panels/swatches/body.rs index 611b12d6d3..b97b10ab90 100644 --- a/crates/ui-egui/src/panels/swatches/body.rs +++ b/crates/ui-egui/src/panels/swatches/body.rs @@ -31,12 +31,14 @@ pub(super) fn body(app: &mut VectorcraftApp, ui: &mut Ui, salt: &'static str) { }); ui.add_space(4.0); // A recent colour paints the active proxy (Alt: the inactive one), as a swatch does, and - // becomes the Color Guide's base color. - if let Some(c) = crate::panels::recent_colors_row(app, ui) { - crate::panels::color_guide::choose_base(app, c); - crate::panels::apply_click(app, ui, json!({"color": crate::panels::color_json(&c)})); + // becomes the Color Guide's base color. The panel menu's Show Recent Colors hides the row. + if app.ui.swatches_show_recent_colors { + if let Some(c) = crate::panels::recent_colors_row(app, ui) { + crate::panels::color_guide::choose_base(app, c); + crate::panels::apply_click(app, ui, json!({"color": crate::panels::color_json(&c)})); + } + widgets::divider(ui); } - widgets::divider(ui); widgets::subheader(ui, tl!("Swatch Tiles")); let query = if pstate(ui.ctx(), "swatch-show-find") { widgets::search_field(ui, find_id(), tl!("Find")) } else { String::new() }; let items = entries(app, kind, &query); diff --git a/crates/ui-egui/src/panels/swatches/tests.rs b/crates/ui-egui/src/panels/swatches/tests.rs index 3586f91607..719262f451 100644 --- a/crates/ui-egui/src/panels/swatches/tests.rs +++ b/crates/ui-egui/src/panels/swatches/tests.rs @@ -901,3 +901,61 @@ fn lab_spot_tiles_show_the_colour_the_spot_colors_options_pick() { assert_eq!(tile(&app), app.session.active().unwrap().doc.global_color("Ink").unwrap()); assert!(matches!(tile(&app), Color::Cmyk { .. })); } + +/// One headless frame of the panel menu; returns its painted labels. +fn menu_frame(app: &mut VectorcraftApp, ctx: &egui::Context, events: Vec) -> Vec<(String, Rect)> { + let raw = egui::RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(236.0, 700.0))), events, ..Default::default() }; + let mut out = ctx.run_ui(raw, |ui| menu(app, ui)); + out.textures_delta.clear(); + crate::tests_removeanchors::shapes_text(&out.shapes.iter().map(|c| c.shape.clone()).collect::>()) +} + +/// Click the panel menu's item `label`. +fn click_menu_item(app: &mut VectorcraftApp, ctx: &egui::Context, label: &str) { + let labels = menu_frame(app, ctx, vec![]); + let at = labels.iter().find(|(t, _)| t.trim() == label).map(|(_, r)| r.center()).unwrap_or_else(|| panic!("no {label} in the menu")); + let button = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE }; + menu_frame(app, ctx, vec![Event::PointerMoved(at), button(true)]); + menu_frame(app, ctx, vec![button(false)]); +} + +/// The panel menu's Show Recent Colors hides and shows the panel's Recent Colors row (#6617): +/// checked by default, applied colors still join the list while the row is hidden, and the Color +/// panel keeps its own row and setting. +#[test] +fn show_recent_colors_hides_and_shows_the_swatches_row() { + let mut app = app(); + app.run("paint.setFill", json!({"color": "#ff0000"})).unwrap(); + let ctx = context(); + let menu_ctx = context(); + // The first recent chip, if the panel draws one (the second frame's answer counts, since egui + // also answers for a widget the frame before drew). + let chip = |app: &mut VectorcraftApp| { + let mut chip = None; + for _ in 0..2 { + let screen_rect = Some(Rect::from_min_size(Pos2::ZERO, vec2(280.0, 700.0))); + let mut out = ctx.run_ui(egui::RawInput { screen_rect, ..Default::default() }, |ui| { + show(app, ui); + chip = ctx.read_response(ui.scope_id().with(("recent", 0usize))).map(|r| r.rect); + }); + out.textures_delta.clear(); + } + chip + }; + assert!(chip(&mut app).is_some(), "the row shows by default"); + click_menu_item(&mut app, &menu_ctx, "✓ Show Recent Colors"); + assert!(!app.ui.swatches_show_recent_colors); + assert!(chip(&mut app).is_none(), "unchecked, the row is hidden"); + app.run("paint.setFill", json!({"color": "#00ff00"})).unwrap(); + assert_eq!(app.session.recent_colors.first().map(|c| c.to_hex()).as_deref(), Some("#00ff00"), "colors still join the list"); + assert_eq!(app.run("ui.colorPanel", json!({})).unwrap()["recentColors"], true, "the Color panel keeps its setting"); + click_menu_item(&mut app, &menu_ctx, "Show Recent Colors"); + assert!(chip(&mut app).is_some(), "checking the item shows the row again"); + // The app keeps it with the preferences; a saved value that isn't true or false reads as on. + let saved = |v: Value| { + let mut prefs = serde_json::to_value(crate::UiState::default()).unwrap(); + prefs["swatches_show_recent_colors"] = v; + serde_json::from_value::(prefs).unwrap().swatches_show_recent_colors + }; + assert_eq!((saved(json!(false)), saved(json!(true)), saved(json!("no"))), (false, true, true)); +} diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index f55db4f5e7..60e651ae79 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -699,6 +699,10 @@ pub struct UiState { /// recent colors keeps filling while the row is hidden. #[serde(default = "yes", deserialize_with = "crate::panels::color::deserialize_show_recent_colors")] pub color_show_recent_colors: bool, + /// Swatches panel: Show Recent Colors in its panel menu, which draws the panel's Recent Colors + /// row (#6617; read as [`Self::color_show_recent_colors`] is). Separate from the Color panel's. + #[serde(default = "yes", deserialize_with = "crate::panels::color::deserialize_show_recent_colors")] + pub swatches_show_recent_colors: bool, /// The SVG Options chosen last (`svg` object of the export/save commands; null: never used). #[serde(default)] pub svg_options: Value, @@ -1148,6 +1152,7 @@ impl Default for UiState { color_mode: None, color_hide_options: false, color_show_recent_colors: true, + swatches_show_recent_colors: true, svg_options: Value::Null, place_link: true, dialog_file: None, diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 8e501889db..28c750f0df 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1,6 +1,6 @@ # Parity with Adobe Illustrator -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-12 · **Change:** minor (#6865: explicitly requested oversized editing artboard; macOS: the canvas cursor stays after the pointer moves, #6362; scores and estimates unchanged; previously minor: Type tool: kerning at a caret at the start of a line moves the character after it, #5111; scores and estimates unchanged; previously minor: Rulers: the zero point dragged out of the rulers' corner snaps as a drawn point does; previously minor: Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: Envelopes: with Distort Appearance, type's strokes paint along the bent glyph outlines at their own weight, as users' `.ai` files draw them, which changes how VectorCraft's own envelopes of stroked type draw (#6644; scores and estimates unchanged); previously minor: G5: Energy Beams brush library; bundled content ~44% → ~46%; previously minor: G5: Skill Icons symbol library; bundled content ~42% → ~44%; previously minor: `.ai` and EPS import: an envelope saved in Edit Contents whose mesh lies away from its drawn art by a move opens live on that art (#6570; scores and estimates unchanged); previously minor: ruler double-click adds a guide at the pointer, #5239; G5: Status Effects graphic style library; bundled content ~40% → ~42%; MCP symbol libraries list UI Frames and Map Markers; previously minor: G5: Inventory Icons built-in symbol library; bundled content ~38% → ~40%; previously minor: `.ai` and EPS import: stroked type in an envelope maps from its glyphs grown by half its stroke (#3179; scores and estimates unchanged); previously minor: `.ai` and EPS import: warps of type open as mesh envelopes on their saved grid where VectorCraft's warp doesn't draw as their drawn art (#3179; scores and estimates unchanged); G5: Game Trails brush library; bundled content ~36% → ~38%; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: `.ai` and EPS import: envelopes of type open live where they draw as their drawn art and the layers with them draw as the page (#3179; scores and estimates unchanged); G5: Game HUD graphic style library; bundled content ~34% → ~36%; previously minor: Line Arrow arrowhead (#5421; scores and estimates unchanged); G5: Map Markers built-in symbol library; bundled content ~32% → ~34%; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: Canvas: a 3D effect changed on type or a group redraws where its solid was; Fit to Artwork Bounds and per-artboard SVG export build each solid once for all artboards; previously minor: Effect Gallery: a thumbnail for each filter and a preview area inside the dialog, on an original drawing; Raster effects 52% → 55%, hours 1–2 → none; headline numbers unchanged; Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; G5: UI Frames built-in symbol library with default 9-slice; bundled content ~32%; previously minor: G5: dynamic symbol part on-canvas opacity/blend/gradient/pattern; strokes remaining no longer lists Overlap/auto corners; symbols ~88%; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; previously minor: harakat paint fallback clarified; previously minor: current Guides feature-area rows consolidated using existing ruler evidence; scores and estimates unchanged; previously minor: Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; previously minor: G5 docs: Art brush Overlap and Pattern brush auto corners removed from strokes remaining; symbols remaining lists dynamic on-canvas beyond solid and guides outside the art; previously minor: custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: EPS import: embedded fonts (composite, Type 42, CID-keyed, Type 1, CFF FontSet) draw their glyphs; spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; `.ai` and EPS import: older Art brush objects read; numeric fields step on wheel hover without taking keyboard focus, and a panel wheel turn cannot seed a canvas zoom glide, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; the canvas keeps selection outlines and anchors between frames; Indian-language hyphenation review and license allowlist, #4947; Stroke gradients: an arrowhead aligned to the tip is painted to the corners of its base, #5964; Drop Shadow and glow dialogs: spinner arrows, #5367, #5368; Stroke Weight Up and Down shortcuts, #5797; Hindi catalog and contributor loanword preferences; full wording review in progress; Properties in Edit Artboards: a Transform section with the reference point above the Artboards section, #5219; the Control bar's Stroke: and Opacity: links scrub their fields, #4088; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window, #4155; Control bar Paragraph section with type selected, #5372; translating unchanged type reuses its glyph and character-stroke outlines; 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) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-13 · **Change:** minor (#6617: Swatches panel menu: Show Recent Colors; scores unchanged; previously minor: #6865: explicitly requested oversized editing artboard; macOS: the canvas cursor stays after the pointer moves, #6362; scores and estimates unchanged; previously minor: Type tool: kerning at a caret at the start of a line moves the character after it, #5111; scores and estimates unchanged; previously minor: Rulers: the zero point dragged out of the rulers' corner snaps as a drawn point does; previously minor: Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: Envelopes: with Distort Appearance, type's strokes paint along the bent glyph outlines at their own weight, as users' `.ai` files draw them, which changes how VectorCraft's own envelopes of stroked type draw (#6644; scores and estimates unchanged); previously minor: G5: Energy Beams brush library; bundled content ~44% → ~46%; previously minor: G5: Skill Icons symbol library; bundled content ~42% → ~44%; previously minor: `.ai` and EPS import: an envelope saved in Edit Contents whose mesh lies away from its drawn art by a move opens live on that art (#6570; scores and estimates unchanged); previously minor: ruler double-click adds a guide at the pointer, #5239; G5: Status Effects graphic style library; bundled content ~40% → ~42%; MCP symbol libraries list UI Frames and Map Markers; previously minor: G5: Inventory Icons built-in symbol library; bundled content ~38% → ~40%; previously minor: `.ai` and EPS import: stroked type in an envelope maps from its glyphs grown by half its stroke (#3179; scores and estimates unchanged); previously minor: `.ai` and EPS import: warps of type open as mesh envelopes on their saved grid where VectorCraft's warp doesn't draw as their drawn art (#3179; scores and estimates unchanged); G5: Game Trails brush library; bundled content ~36% → ~38%; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: `.ai` and EPS import: envelopes of type open live where they draw as their drawn art and the layers with them draw as the page (#3179; scores and estimates unchanged); G5: Game HUD graphic style library; bundled content ~34% → ~36%; previously minor: Line Arrow arrowhead (#5421; scores and estimates unchanged); G5: Map Markers built-in symbol library; bundled content ~32% → ~34%; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: Canvas: a 3D effect changed on type or a group redraws where its solid was; Fit to Artwork Bounds and per-artboard SVG export build each solid once for all artboards; previously minor: Effect Gallery: a thumbnail for each filter and a preview area inside the dialog, on an original drawing; Raster effects 52% → 55%, hours 1–2 → none; headline numbers unchanged; Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; G5: UI Frames built-in symbol library with default 9-slice; bundled content ~32%; previously minor: G5: dynamic symbol part on-canvas opacity/blend/gradient/pattern; strokes remaining no longer lists Overlap/auto corners; symbols ~88%; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; previously minor: harakat paint fallback clarified; previously minor: current Guides feature-area rows consolidated using existing ruler evidence; scores and estimates unchanged; previously minor: Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; previously minor: G5 docs: Art brush Overlap and Pattern brush auto corners removed from strokes remaining; symbols remaining lists dynamic on-canvas beyond solid and guides outside the art; previously minor: custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: EPS import: embedded fonts (composite, Type 42, CID-keyed, Type 1, CFF FontSet) draw their glyphs; spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; `.ai` and EPS import: older Art brush objects read; numeric fields step on wheel hover without taking keyboard focus, and a panel wheel turn cannot seed a canvas zoom glide, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; the canvas keeps selection outlines and anchors between frames; Indian-language hyphenation review and license allowlist, #4947; Stroke gradients: an arrowhead aligned to the tip is painted to the corners of its base, #5964; Drop Shadow and glow dialogs: spinner arrows, #5367, #5368; Stroke Weight Up and Down shortcuts, #5797; Hindi catalog and contributor loanword preferences; full wording review in progress; Properties in Edit Artboards: a Transform section with the reference point above the Artboards section, #5219; the Control bar's Stroke: and Opacity: links scrub their fields, #4088; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window, #4155; Control bar Paragraph section with type selected, #5372; translating unchanged type reuses its glyph and character-stroke outlines; 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) > > Oversized artboard import update (2026-11-12): Explicit editing-data import retains a single artboard beyond a capped 14,400 pt PDF page (#6865). > @@ -53,6 +53,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | Date | Change | Summary | |---|---|---| +| 2026-11-13 | minor | Swatches panel: its menu's Show Recent Colors hides and shows the panel's Recent Colors row, checked by default and kept between sessions apart from the Color panel's (#6617); scores unchanged. | | 2026-11-09 | minor | Stroke panel: Line Arrow, a 41st generated arrowhead: two arms of the head's weight (stroke weight × arrowhead scale, at least 0.25 pt: the line's weight at 100 % for lines of 0.25 pt or more) at right angles, a sharp tip and square-cut ends, twice as wide as the other heads; bounds and clicks follow each head's own reach (`stroke.set` `LineArrow`, #5421); scores and estimates unchanged | | 2026-11-09 | minor | Canvas: a 3D effect (Extrude & Bevel, Inflate, Rotate, Revolve) changed on type or a group draws again where its art was and is, measured by that art; the canvas measures through its renderer and draws the part with the art it made to measure it. Exports and Object › Rasterize take the solid of 3D type. Fit to Artwork Bounds and per-artboard SVG export measure every artboard through one renderer, building each solid once. A 3D effect on a fill or stroke that builds nothing (Revolve after Roughen) no longer overflows the stack on the canvas, in Object › Rasterize and in raster and EPS exports: the fill or stroke draws as without it. Tests `tests::changed::a_3d_effect_changed_on_type_or_a_group_changes_where_its_solid_was`, `fx::tests::a_renderer_measures_the_art_on_each_artboard_building_each_solid_once`, `canvas::tests::an_edit_to_a_3d_effect_redraws_with_the_art_measured`, `fx::tests::a_3d_effect_that_builds_nothing_draws_the_item_as_without_it`. Scores and estimates unchanged. | | 2026-11-08 | minor | Style panels: a double-click on a user character style's row opens its Character Style Options (`ui.charStyleOptions`), and on a paragraph style's row its Paragraph Style Options (rename); the Character Style Options dialog keeps a fixed width and scrolls its sections in a shorter window, so OK and Cancel stay on screen. Tests `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options` and `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list`. Scores and estimates unchanged. | @@ -1347,6 +1348,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur Plain Comma and Period, and also Shift+Comma and Shift+Period, apply the last solid color and the last gradient to the active Fill or Stroke proxy in VectorCraft ([Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html), "Select color fill mode" and "Select gradient fill mode"). Recent Colors keeps the last 30 solid colors applied, newest first and each once, and the list stays the same when another document is opened or made active; a 31st color drops the oldest ([Select colors using the Color panel](https://helpx.adobe.com/illustrator/desktop/manage-colors/select-and-adjust-colors/select-colors-using-the-color-panel.html)). The list lasts for the session. In the Color and Swatches panels the chips wrap onto further rows when the panel is too narrow for one row (`paint.recent`). Tests: `recent_colors_keep_thirty_colors_across_documents` in `crates/engine/src/tests/paintproxy.rs`, `all_thirty_recent_colors_wrap_into_a_narrow_panel` in `crates/ui-egui/src/panels/color/tests.rs`. Unchecking Show Recent Colors in the Color panel's menu hides its Recent Colors row, and checking it shows the row again ([Select colors using the Color panel](https://helpx.adobe.com/illustrator/desktop/manage-colors/select-and-adjust-colors/select-colors-using-the-color-panel.html)). In VectorCraft the item is checked by default and kept between sessions, applied colors still join the list while the row is hidden, Hide Options hides the row either way, and the Swatches panel keeps its row (`ui.colorPanel {recentColors}`). Tests: `show_recent_colors_hides_and_shows_the_row` in `crates/ui-egui/src/panels/color/tests.rs`. + The Swatches panel's menu has its own Show Recent Colors item: unchecking it hides the panel's Recent Colors row and checking it shows the row again ([Color Enhancements in Illustrator](https://community.adobe.com/announcements-651/color-enhancements-in-illustrator-816797) puts recent colors in the Color and Swatches panels; [Select colors using the Color panel](https://helpx.adobe.com/illustrator/desktop/manage-colors/select-and-adjust-colors/select-colors-using-the-color-panel.html) hides them with Show Recent Colors in the panel menu; a [community thread](https://community.adobe.com/questions-652/swatches-disappear-in-swatches-panel-when-switching-between-documents-in-29-7-1-on-macos-816823) turns it off from the Swatches panel). In VectorCraft it is checked by default, kept between sessions apart from the Color panel's, and applied colors still join the list while the row is hidden (#6617). Tests: `show_recent_colors_hides_and_shows_the_swatches_row` in `crates/ui-egui/src/panels/swatches/tests.rs`. A Recent Colors chip in the Color or Swatches panel drags into the Swatches panel's list to become a swatch of its color, in its color model, as one undo step ([Select colors using the Color panel](https://helpx.adobe.com/illustrator/desktop/manage-colors/select-and-adjust-colors/select-colors-using-the-color-panel.html)). In VectorCraft a chip dropped on a color group or one of its swatches joins that group, anywhere else in the list it joins the ungrouped swatches at the end, a color that is already a swatch is added again under another name, and dragging a chip does not paint the selected art (`swatch.new`). Tests: `a_recent_color_dragged_onto_the_list_becomes_a_swatch` and `a_recent_color_dragged_from_the_color_panel_becomes_a_swatch` in `crates/ui-egui/src/panels/swatches/tests.rs`. A Cmd/Ctrl-click on the Color panel's spectrum applies Complement and a Cmd/Ctrl+Shift-click applies Invert to the active Fill or Stroke (`paint.complement`, `paint.invert`); with Alt also held, the click recolors the inactive one, as the reference app's [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) list them. View › Proof Setup › Color blindness – Tritanopia-type turns Proof Colors on and proofs the canvas with the tritanopia simulation of Brettel, Viénot and Mollon (1997). The Separations Preview panel's Proof Setup list offers the same target (`view.proofSetup {target: "tritanopia"}`). Grays keep their value.