From 2b93e8a930fd27efaddf8124198cd10cf32c8cb9 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 10 Oct 2026 19:02:55 -0700 Subject: [PATCH 1/2] Swatches: swatch.library.save counts only the swatches the file holds swatch.library.save replied with the number of solid colors and gradients it was asked to save, including the gradients the format leaves out. For a new document saved as .ase, count was 50 and the file held 46 colors. The .gpl and .ase writers keep solid colors only, and the CSS writer leaves freeform gradients out. count is now the number of swatches written. The new PaletteFormat::holds returns whether a file of a format keeps a swatch with a given paint, and swatch.library.save counts the swatches for which it returns true. The reply has the same fields as before. Tests: after a freeform gradient is added to a new document's swatches, the test checks that count has these values: 51 for .vcswatches, 46 for .gpl and .ase, and 50 for CSS. Each file holds that many swatches. Docs: docs/mcp.md. --- crates/color/src/palette_io.rs | 40 ++++++++++++++++++++++++++++ crates/engine/src/cmd/swatchlib.rs | 4 +-- crates/engine/src/tests_swatchlib.rs | 20 ++++++++++++++ docs/mcp.md | 3 ++- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/crates/color/src/palette_io.rs b/crates/color/src/palette_io.rs index fa193273..b37463b9 100644 --- a/crates/color/src/palette_io.rs +++ b/crates/color/src/palette_io.rs @@ -57,6 +57,19 @@ impl PaletteFormat { pub fn binary(self) -> bool { self == PaletteFormat::Ase } + /// Does [`write`] keep a swatch with `paint` in a file of this format? None and patterns are + /// never written; `.gpl` and `.ase` hold solid colors only, and CSS holds no freeform gradients. + pub fn holds(self, paint: &Paint) -> bool { + match paint { + Paint::Solid { .. } => true, + Paint::Gradient(g) => match self { + PaletteFormat::Native => true, + PaletteFormat::Css => g.gradient.kind != GradientKind::Freeform, + PaletteFormat::Gpl | PaletteFormat::Ase => false, + }, + Paint::None | Paint::Pattern { .. } => false, + } + } } /// The native file: a header around the library. @@ -792,6 +805,23 @@ mod tests { assert!(read(&text, "x").is_err(), "CSS is written only"); } + #[test] + fn holds_counts_the_swatches_each_format_writes() { + // `sample()` holds the spot color Ink, the linear gradient Fade, the pattern Tiles and two solid colors in a group. + let mut lib = sample(); + let Paint::Gradient(mut mesh) = lib.swatches[1].paint.clone() else { panic!("Fade is a gradient") }; + mesh.gradient.kind = GradientKind::Freeform; + lib.swatches.push(Swatch { name: "Mesh".into(), paint: Paint::Gradient(mesh), global: false, spot: false }); + lib.swatches.push(Swatch { name: "[None]".into(), paint: Paint::None, global: false, spot: false }); + let held = |f: PaletteFormat| lib.iter().filter(|w| f.holds(&w.paint)).count(); + assert_eq!(PaletteFormat::ALL.map(held), [5, 3, 3, 4], "vcswatches, gpl, ase, css"); + for f in [PaletteFormat::Native, PaletteFormat::Gpl, PaletteFormat::Ase] { + assert_eq!(read_bytes(&write(&lib, f).unwrap(), "x").unwrap().len(), held(f), "{f:?}"); + } + let css = written(&lib, PaletteFormat::Css); + assert_eq!(css.lines().filter(|l| l.starts_with(" --")).count(), held(PaletteFormat::Css), "one property each: {css}"); + } + /// A swatch exchange file of `blocks` (type, body). fn ase(blocks: &[(u16, Vec)]) -> Vec { let mut out = b"ASEF\0\x01\0\0".to_vec(); @@ -1088,5 +1118,15 @@ mod tests { prop_assert!(same_swatches(&g.swatches, &ase_expected(&want.swatches)), "{}: {:?}", g.name, g.swatches); } } + + /// A written `.vcswatches`, `.gpl` or `.ase` file reads back with as many swatches as + /// [`PaletteFormat::holds`] counts. + #[test] + fn written_files_read_back_with_the_swatches_holds_counts(lib in arb_library()) { + for f in [PaletteFormat::Native, PaletteFormat::Gpl, PaletteFormat::Ase] { + let back = read_bytes(&write(&lib, f).unwrap(), &lib.name).unwrap(); + prop_assert_eq!(back.len(), lib.iter().filter(|w| f.holds(&w.paint)).count(), "{:?}", f); + } + } } } diff --git a/crates/engine/src/cmd/swatchlib.rs b/crates/engine/src/cmd/swatchlib.rs index 9c1a9298..33adf24f 100644 --- a/crates/engine/src/cmd/swatchlib.rs +++ b/crates/engine/src/cmd/swatchlib.rs @@ -63,7 +63,7 @@ pub fn specs() -> Vec { "Save Swatch Library…", ["Window", "Swatches"], None, - "{path?, format?: \"vcswatches\" (lossless JSON: colour models, global, spot, gradients, groups) | \"gpl\" (8-bit RGB palette; groups as `# Group:` comments) | \"ase\" (swatch exchange, binary: solid colors in their own model (RGB, CMYK, Lab, Gray) as global, spot or process colors, and color groups; gradients are left out, and a tint swatch is saved as the color it shows) | \"css\" (custom properties on :root) (default: the path's extension, else vcswatches), names?: [swatch or colour group names] (default: all; None and patterns are never saved), name?: library name (default: the document's), user?: false (save into the user library folder, listed under User Defined)} save the document's swatches as a library → {path, format, count, library?: id when saved to the user folder}; without path or user → {data: the file's text, format, count}, or {dataBase64, format, count} for ase", + "{path?, format?: \"vcswatches\" (lossless JSON: colour models, global, spot, gradients, groups) | \"gpl\" (8-bit RGB palette; groups as `# Group:` comments) | \"ase\" (swatch exchange, binary: solid colors in their own model (RGB, CMYK, Lab, Gray) as global, spot or process colors, and color groups; gradients are left out, and a tint swatch is saved as the color it shows) | \"css\" (custom properties on :root) (default: the path's extension, else vcswatches), names?: [swatch or colour group names] (default: all; None and patterns are never saved), name?: library name (default: the document's), user?: false (save into the user library folder, listed under User Defined)} save the document's swatches as a library → {path, format, count: the swatches written (gpl and ase hold solid colors only, and css leaves freeform gradients out), library?: id when saved to the user folder}; without path or user → {data: the file's text, format, count}, or {dataBase64, format, count} for ase", has_doc, save ), @@ -454,7 +454,7 @@ fn save(s: &mut Session, p: &Value) -> Result { let lib = document_library(&st.doc, &str_list(p, "names"), name, C)?; let bytes = palette_io::write(&lib, format).map_err(|e| bad(C, e))?; let data = if format.binary() { FileData::Binary(bytes) } else { FileData::Text(String::from_utf8_lossy(&bytes).into_owned()) }; - let mut out = json!({"format": format.id(), "count": lib.len()}); + let mut out = json!({"format": format.id(), "count": lib.iter().filter(|w| format.holds(&w.paint)).count()}); s.swatch_libraries.write(p, &lib.name, format.id(), data, &mut out, C)?; Ok(out) } diff --git a/crates/engine/src/tests_swatchlib.rs b/crates/engine/src/tests_swatchlib.rs index 68f1d8ec..0783e159 100644 --- a/crates/engine/src/tests_swatchlib.rs +++ b/crates/engine/src/tests_swatchlib.rs @@ -283,6 +283,26 @@ fn swatch_exchange_files_save_with_models_kinds_and_groups() { let _ = std::fs::remove_dir_all(dir); } +#[test] +fn save_counts_the_swatches_the_file_holds() { + let mut s = session(); + // A new document has 46 solid colors and 4 linear or radial gradients besides None. Mesh is a freeform gradient. + run(&mut s, "swatch.new", json!({"name": "Mesh", "gradient": {"kind": "freeform"}})); + let mut counts = vec![]; + for format in ["vcswatches", "gpl", "ase", "css"] { + let r = run(&mut s, "swatch.library.save", json!({ "format": format })); + // The libraries load back with that many swatches, and the CSS file has one property for each. + let held = if format == "css" { + r["data"].as_str().unwrap().lines().filter(|l| l.starts_with(" --")).count() as u64 + } else { + let load = json!({"data": r["data"], "dataBase64": r["dataBase64"], "name": format!("Saved.{format}")}); + run(&mut s, "swatch.library.load", load)["count"].as_u64().unwrap() + }; + counts.push((format, r["count"].as_u64().unwrap(), held)); + } + assert_eq!(counts, [("vcswatches", 51, 51), ("gpl", 46, 46), ("ase", 46, 46), ("css", 50, 50)]); +} + #[test] fn loaded_libraries_copy_into_the_user_folder_without_replacing_files() { use vectorcraft_testkit::ase::{Block, ase, sample}; diff --git a/docs/mcp.md b/docs/mcp.md index b49165ce..249b9c9a 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -931,7 +931,8 @@ groups; `.gpl` is 8-bit RGB; a swatch exchange `.ase` file keeps solid colors in (RGB, CMYK, Lab or Gray) as global, spot or process colors, and color groups, writes a tint swatch as the color it shows and leaves gradients out; CSS writes custom properties); without `path` it returns `{data}`, or `{dataBase64}` for `.ase`, and `user: true` saves into the user library -folder of the desktop app (listed as category `user`, User Defined). `swatch.library.load {path? | data? | +folder of the desktop app (listed as category `user`, User Defined). The reply's `count` is the number of swatches +written: `.gpl` and `.ase` hold solid colors only, and CSS leaves freeform gradients out. `swatch.library.load {path? | data? | dataBase64?, name?}` loads a `.vcswatches`, `.gpl`, swatch exchange (`.ase`) or color book (`.acb`) file, or another document's swatches, as a library to add from. From an `.ase` file it reads RGB, CMYK, Lab and Gray colors as global, spot or process swatches and keeps their color groups. A color book (such as a Pantone book you own) gives its RGB, From c7b46dbb3e498ed5700cd930bdefb3ae7e627194 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sun, 11 Oct 2026 11:00:30 +0700 Subject: [PATCH 2/2] Review: palette writing keeps the swatches PaletteFormat::holds names; ROADMAP (#1110) Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- crates/color/src/palette_io.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 79b97597..1aafcaa9 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -127,7 +127,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap | Date | What landed | |---|---| -| 2026-10-11 | Effect › Brush Strokes: Accented Edges, Angled Strokes, Crosshatch, Dark Strokes, Ink Outlines, Spatter, Sprayed Strokes, Sumi-e (G2). Command parameters from agents and files checked before anything changes (paint, place, cuts, dashes, crop, asset ids, anchors, paths, distort; #984–#999); command dialogs keep whole numbers whole, so Create Object Mosaic takes the tiles typed (#1000); stepper arrows take Shift (×10) and Ctrl/Cmd (÷10) like the arrow keys (#991). Exports and Rasterize supersample Art Optimized edges, so shapes meeting edge to edge leave no seam (#983). Character and paragraph styles take leading, kerning, mojikumi and the other attributes left out at their defaults (#1001). Drawing, typing and pasting with no layer shown and unlocked is refused instead of adding art to a hidden or locked layer (#1002). MCP `add_text` gives type on or in a path the colour asked for (#1003). MCP `screenshot` of a connected app renders the artboard asked for (#1004). `vectorcraft-cli run` ends quietly when its reader closes early (#1005). Select › All on Active Artboard selects on the artboard made active, not always the first (#1006). Reverse Gradient keeps off-centre midpoints, mirrored (#1007). A polar grid with no radial dividers draws none (#1008). Use Precise Cursors also covers the Pen's join pointer (#1009). SVGs with one absolute and one unitless root side import at their size, unstretched (#1010). DXF US survey inches, yards and miles open at their size and name (#1011). A file placed into a document with no other links follows its first change even while the app sits idle (#1031). PDF import keeps the style a font name says when the file doesn't embed the font (#1023). Shift selects ruler guides and art together, and Delete removes both (#1022). Open filled paths are hit by their fill on every side, the rotate cursor bows around the corner it is at, the flip icons match their flips, the Color Picker previews on the selection, and Properties › Transform reaches Scale Corners and Scale Strokes & Effects (#1036). Offset Path and the other booleans finish on outlines where the sweep used to loop until memory ran out (linesweeper 0.5, #1028). Floating panel groups stack into sets that move together, collapse to their tabs and dock as extra dock columns that scroll (#1020). Ruler guides live on layers: listed in the Layers panel, shown, hidden, locked, duplicated, merged and deleted with their layer, and moved between layers (#1021). A `.ai` whose editing data uses global process colours (`Xk`/`XK`) or pattern fills keeps its sublayers: the colours come in as global swatches at their tint, the patterns as pattern swatches (#1025). A `.ai` in PostScript form opens through its layers, read from the program itself, also when it names the prolog defining its operators without including it, as Rhino and other CAD apps write it (#1027). A fill or stroke chosen while the Type tool has characters selected colours just those characters (#1063). Scaled type shows its scaled font size, leading, baseline shift and horizontal scale in the Character panel, and a size typed there is the size you get (#1034). Character panel menu › Proportional Metrics sets Japanese and Chinese full-width glyphs on the font's proportional widths in horizontal type (#966). Align, Distribute, Move, Arrange, Lock, Hide, Ungroup and other Object commands that would change nothing no longer record an undo step or mark the document changed (#1060). Spot colours defined in Lab open with their Lab values from `.ai` and PDF files, instead of as wrong RGB colours or their CMYK equivalents (#1032). Build scripts leave a generated source they wouldn't change untouched, so the next build doesn't compile most of the workspace again where file times lose their sub-second part (#1051). A fill colour recolours the gradient mesh point last clicked with the Mesh tool or Direct Selection, and a Mesh tool click on a mesh's point selects that mesh (#1052). The All Tools drawer stays two tools wide instead of filling a wide window (#1054). Text Wrap's Make, Release and Options refuse a list of object ids with a malformed or unknown id instead of skipping it (#1055). A freeform gradient line with a malformed point index is refused instead of being shortened to the indices that are valid (#1056). The Corners dialog (`ui.corners`) refuses a malformed object id or a listed anchor that isn't a corner instead of editing the selection or the other corners (#1057). New View refuses a centre that isn't exactly [x, y] and a rotation that isn't a number, instead of trimming the one and saving the other as 0° (#1058). The command sweeps try junk scale, spacing and artboard values too, which they had skipped since before those bugs were fixed (#1059). EPS and PostScript `arc` and `arcn` with huge angles no longer hang the import, and an arc whose angles are whole turns apart is empty, as the PostScript reference defines it (#1062). Align and Distribute Spacing keep the key object still also when it is inside a selected group or compound path (#1074). A `.ai` whose layers can't be read yet still opens with its artboards where they were (in a grid, say), not in a row (#1068). Layer Options (`ui.layerOptions`) refuses a malformed or unknown row id instead of skipping it or opening on the highlighted rows (#1077). Shaper scribbles given a list of object ids refuse a malformed or unknown id instead of skipping it and editing the shapes that remain (#1078). Delete Width Point (`stroke.widthPoint.remove`) refuses a list of indices with a malformed entry instead of deleting the points that remain, and no longer wraps a huge index round on the web (#1079). Preferences' checkboxes have an outline when the pointer isn't over them, in every interface brightness, as the app's other dialogs' checkboxes do (#1080). The last plain checkboxes (Properties panel snapping and Scale Strokes & Effects, Revolve, Save options, Export for Screens, Variables, plug-in forms) keep a visible box at rest in every theme too (#885). Setting or copying a width point with a malformed index is refused instead of adding a new point or wrapping a huge index round (follow-up to #1079). An Illustrator EPS opened through its layers keeps its own page size and the red outline of text stacked over a dark one, as 0.8.0 did (#1102). The macOS DMG is built so the app in it passes `codesign --verify --strict --deep`, and the release checks that it does (#1085). Swatch and graphic style libraries, Libraries panel files, presets and plug-ins dropped on the window open as File › Open opens them instead of being placed on the canvas (#1083). A key object needs another selected object outside it: a change that leaves one object selected lets the key go, and one selected object aligns to the artboard whatever Align To says (#1088). Align to Artboard aligns to the active artboard, and actions play their Align to Artboard, All on Active Artboard and Paste in Place, in Front and in Back steps on the artboard active when they play (#1105). Align refuses a call with neither direction, and Align, Distribute, Reflect and Shear refuse a direction, axis, target or bounds they don't take instead of falling back to a default (#1106). `object.setProps` deselects the objects it hides or locks, the key object included, as the Layers panel's eye and lock do (#1107). Align, Distribute and Distribute Spacing move pattern tiles with the art when General › Transform Pattern Tiles is on (#1108). Object › Lock › All Artwork Above and Object › Hide › All Artwork Above deselect the selected objects they lock or hide, the key object included (#1109). | +| 2026-10-11 | Effect › Brush Strokes: Accented Edges, Angled Strokes, Crosshatch, Dark Strokes, Ink Outlines, Spatter, Sprayed Strokes, Sumi-e (G2). Command parameters from agents and files checked before anything changes (paint, place, cuts, dashes, crop, asset ids, anchors, paths, distort; #984–#999); command dialogs keep whole numbers whole, so Create Object Mosaic takes the tiles typed (#1000); stepper arrows take Shift (×10) and Ctrl/Cmd (÷10) like the arrow keys (#991). Exports and Rasterize supersample Art Optimized edges, so shapes meeting edge to edge leave no seam (#983). Character and paragraph styles take leading, kerning, mojikumi and the other attributes left out at their defaults (#1001). Drawing, typing and pasting with no layer shown and unlocked is refused instead of adding art to a hidden or locked layer (#1002). MCP `add_text` gives type on or in a path the colour asked for (#1003). MCP `screenshot` of a connected app renders the artboard asked for (#1004). `vectorcraft-cli run` ends quietly when its reader closes early (#1005). Select › All on Active Artboard selects on the artboard made active, not always the first (#1006). Reverse Gradient keeps off-centre midpoints, mirrored (#1007). A polar grid with no radial dividers draws none (#1008). Use Precise Cursors also covers the Pen's join pointer (#1009). SVGs with one absolute and one unitless root side import at their size, unstretched (#1010). DXF US survey inches, yards and miles open at their size and name (#1011). A file placed into a document with no other links follows its first change even while the app sits idle (#1031). PDF import keeps the style a font name says when the file doesn't embed the font (#1023). Shift selects ruler guides and art together, and Delete removes both (#1022). Open filled paths are hit by their fill on every side, the rotate cursor bows around the corner it is at, the flip icons match their flips, the Color Picker previews on the selection, and Properties › Transform reaches Scale Corners and Scale Strokes & Effects (#1036). Offset Path and the other booleans finish on outlines where the sweep used to loop until memory ran out (linesweeper 0.5, #1028). Floating panel groups stack into sets that move together, collapse to their tabs and dock as extra dock columns that scroll (#1020). Ruler guides live on layers: listed in the Layers panel, shown, hidden, locked, duplicated, merged and deleted with their layer, and moved between layers (#1021). A `.ai` whose editing data uses global process colours (`Xk`/`XK`) or pattern fills keeps its sublayers: the colours come in as global swatches at their tint, the patterns as pattern swatches (#1025). A `.ai` in PostScript form opens through its layers, read from the program itself, also when it names the prolog defining its operators without including it, as Rhino and other CAD apps write it (#1027). A fill or stroke chosen while the Type tool has characters selected colours just those characters (#1063). Scaled type shows its scaled font size, leading, baseline shift and horizontal scale in the Character panel, and a size typed there is the size you get (#1034). Character panel menu › Proportional Metrics sets Japanese and Chinese full-width glyphs on the font's proportional widths in horizontal type (#966). Align, Distribute, Move, Arrange, Lock, Hide, Ungroup and other Object commands that would change nothing no longer record an undo step or mark the document changed (#1060). Spot colours defined in Lab open with their Lab values from `.ai` and PDF files, instead of as wrong RGB colours or their CMYK equivalents (#1032). Build scripts leave a generated source they wouldn't change untouched, so the next build doesn't compile most of the workspace again where file times lose their sub-second part (#1051). A fill colour recolours the gradient mesh point last clicked with the Mesh tool or Direct Selection, and a Mesh tool click on a mesh's point selects that mesh (#1052). The All Tools drawer stays two tools wide instead of filling a wide window (#1054). Text Wrap's Make, Release and Options refuse a list of object ids with a malformed or unknown id instead of skipping it (#1055). A freeform gradient line with a malformed point index is refused instead of being shortened to the indices that are valid (#1056). The Corners dialog (`ui.corners`) refuses a malformed object id or a listed anchor that isn't a corner instead of editing the selection or the other corners (#1057). New View refuses a centre that isn't exactly [x, y] and a rotation that isn't a number, instead of trimming the one and saving the other as 0° (#1058). The command sweeps try junk scale, spacing and artboard values too, which they had skipped since before those bugs were fixed (#1059). EPS and PostScript `arc` and `arcn` with huge angles no longer hang the import, and an arc whose angles are whole turns apart is empty, as the PostScript reference defines it (#1062). Align and Distribute Spacing keep the key object still also when it is inside a selected group or compound path (#1074). A `.ai` whose layers can't be read yet still opens with its artboards where they were (in a grid, say), not in a row (#1068). Layer Options (`ui.layerOptions`) refuses a malformed or unknown row id instead of skipping it or opening on the highlighted rows (#1077). Shaper scribbles given a list of object ids refuse a malformed or unknown id instead of skipping it and editing the shapes that remain (#1078). Delete Width Point (`stroke.widthPoint.remove`) refuses a list of indices with a malformed entry instead of deleting the points that remain, and no longer wraps a huge index round on the web (#1079). Preferences' checkboxes have an outline when the pointer isn't over them, in every interface brightness, as the app's other dialogs' checkboxes do (#1080). The last plain checkboxes (Properties panel snapping and Scale Strokes & Effects, Revolve, Save options, Export for Screens, Variables, plug-in forms) keep a visible box at rest in every theme too (#885). Setting or copying a width point with a malformed index is refused instead of adding a new point or wrapping a huge index round (follow-up to #1079). An Illustrator EPS opened through its layers keeps its own page size and the red outline of text stacked over a dark one, as 0.8.0 did (#1102). The macOS DMG is built so the app in it passes `codesign --verify --strict --deep`, and the release checks that it does (#1085). Swatch and graphic style libraries, Libraries panel files, presets and plug-ins dropped on the window open as File › Open opens them instead of being placed on the canvas (#1083). A key object needs another selected object outside it: a change that leaves one object selected lets the key go, and one selected object aligns to the artboard whatever Align To says (#1088). Align to Artboard aligns to the active artboard, and actions play their Align to Artboard, All on Active Artboard and Paste in Place, in Front and in Back steps on the artboard active when they play (#1105). Align refuses a call with neither direction, and Align, Distribute, Reflect and Shear refuse a direction, axis, target or bounds they don't take instead of falling back to a default (#1106). `object.setProps` deselects the objects it hides or locks, the key object included, as the Layers panel's eye and lock do (#1107). Align, Distribute and Distribute Spacing move pattern tiles with the art when General › Transform Pattern Tiles is on (#1108). Object › Lock › All Artwork Above and Object › Hide › All Artwork Above deselect the selected objects they lock or hide, the key object included (#1109). Save Swatch Library's `count` is the number of swatches the file holds: `.gpl` and `.ase` hold solid colors only, and CSS leaves freeform gradients out (#1110). | | 2026-10-10 | Initial live Revolve (Effect › 3D and Materials, #846, #605), in its own `three-d` crate. Progress docs re-measured and restructured to the craftrules standard (this page, `docs/target-app-parity.md`, `gaps.md`, `ui-parity.md`, the format, hardware, localization, effects and type checklists). Same day: v0.8.0; Effect › Distort, Pixelate and Texture filters (13); German interface; Variables (data merge); Graph Type value axes and tick marks; restart when the first frame never reaches the screen (#964) | | 2026-10-09 | Radial Blur, Smart Blur, Unsharp Mask (gap 2 begins); 340 commits, the busiest day | | 2026-10-08 | v0.5.0–v0.7.0; community contributions: Pen and shape-tool modifiers, Layers, Artboards, Japanese composition, Hebrew/Arabic type, interface languages, saved selections | diff --git a/crates/color/src/palette_io.rs b/crates/color/src/palette_io.rs index b37463b9..c8564e05 100644 --- a/crates/color/src/palette_io.rs +++ b/crates/color/src/palette_io.rs @@ -101,7 +101,7 @@ const ACB_CUT_SHORT: &str = "the color book is cut short"; /// colors in their own model. Writing `.ase` fails for a library with more colors and groups than /// [`read_bytes`] reads from one file. pub fn write(lib: &SwatchLibrary, format: PaletteFormat) -> Result, String> { - let keep = |w: &&Swatch| matches!(w.paint, Paint::Solid { .. } | Paint::Gradient(_)); + let keep = |w: &&Swatch| format.holds(&w.paint); let text = match format { PaletteFormat::Native => { let strip = |list: &[Swatch]| list.iter().filter(keep).cloned().collect::>();