From db3092ae2e19d985360b6145bf88af0e8aa742b0 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 17 Oct 2026 15:19:08 -0700 Subject: [PATCH] Patterns: Pattern Options refuses a name another swatch has pattern.options refused a new name that another pattern had, but not one that a color swatch, a gradient swatch, a second swatch of the pattern or a color group had. The rename then gave the pattern's swatch that name, so two swatches, or a swatch and a color group, had one name. The rename also gave the new name to every swatch that had the old one, a color swatch included, and art linked to that color swatch kept a link to a name no swatch had. Make and Save a Copy numbered a pattern's or a swatch's name, but not a color group's. pattern.options now refuses a name that a swatch or a color group has, with "a swatch or color group named `` exists". It takes a name that only the pattern's own swatches have, such as a name Swatch Options gave the pattern's swatch, when none of them still has the old name. A rename gives the new name only to the pattern's swatches that had the old one. Make and Save a Copy number a color group's name as they number a swatch's. The new message has a row in the 17 catalogs that translate every message (COMPLETE_MESSAGES) and in bg, da, id and nl. Docs: docs/mcp.md, docs/target-app-parity.md. --- crates/engine/src/cmd/patterncmds.rs | 34 +++++++++++----- crates/engine/src/tests_pattern.rs | 58 ++++++++++++++++++++++++++++ crates/ui-egui/src/i18n/bg.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/da.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fi.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/id.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/nl.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + docs/mcp.md | 9 +++-- docs/target-app-parity.md | 1 + 25 files changed, 111 insertions(+), 12 deletions(-) diff --git a/crates/engine/src/cmd/patterncmds.rs b/crates/engine/src/cmd/patterncmds.rs index b8d88fd9e..8ed9820b7 100644 --- a/crates/engine/src/cmd/patterncmds.rs +++ b/crates/engine/src/cmd/patterncmds.rs @@ -210,14 +210,14 @@ fn roots(s: &Session, p: &Value) -> Result> { } fn unique_pattern_name(d: &Document, base: &str) -> String { - if d.pattern(base).is_none() && d.swatch(base).is_none() { + if d.pattern(base).is_none() && !d.swatch_name_taken(base) { return base.to_string(); } - (2..).map(|i| format!("{base} {i}")).find(|n| d.pattern(n).is_none() && d.swatch(n).is_none()).unwrap_or_else(|| base.to_string()) + (2..).map(|i| format!("{base} {i}")).find(|n| d.pattern(n).is_none() && !d.swatch_name_taken(n)).unwrap_or_else(|| base.to_string()) } fn next_pattern_name(d: &Document) -> String { - (1..).map(|i| format!("New Pattern {i}")).find(|n| d.pattern(n).is_none() && d.swatch(n).is_none()).unwrap_or_else(|| "New Pattern".to_string()) + (1..).map(|i| format!("New Pattern {i}")).find(|n| d.pattern(n).is_none() && !d.swatch_name_taken(n)).unwrap_or_else(|| "New Pattern".to_string()) } /// Add pattern `def` and its swatch. @@ -420,10 +420,14 @@ fn pattern_options(s: &mut Session, p: &Value) -> Result { } } let new_name = str_param(p, "newName").map(str::trim).filter(|n| !n.is_empty() && *n != name).map(str::to_string); - if let Some(n) = &new_name - && s.doc()?.doc.pattern(n).is_some() - { - return Err(bad(C, format!("a pattern named `{n}` exists"))); + if let Some(n) = &new_name { + let d = &s.doc()?.doc; + if d.pattern(n).is_some() { + return Err(bad(C, format!("a pattern named `{n}` exists"))); + } + if swatch_name_clash(d, &name, n) { + return Err(bad(C, format!("a swatch or color group named `{n}` exists"))); + } } let overlap = match p.get("overlap") { Some(o) => Overlap { @@ -479,7 +483,19 @@ fn pattern_options(s: &mut Session, p: &Value) -> Result { Ok(json!({ "name": out_name })) } -/// Rename a pattern in swatches, paints and the edit record. +/// Is `new` taken for pattern `old` by a swatch or a color group (they share one namespace, +/// [`Document::swatch_name_taken`])? It is free when the swatches named `new` are one or more +/// swatches of the pattern and none of the pattern's swatches is named `old` (those take the new +/// name in [`rename_pattern_uses`]). +fn swatch_name_clash(d: &Document, old: &str, new: &str) -> bool { + let own = |w: &vectorcraft_color::Swatch| pattern_name(&w.paint).is_some_and(|p| p == old); + let mut holders = d.swatches_iter().filter(|w| w.name == new).peekable(); + let only_own = holders.peek().is_some() && holders.all(own); + d.swatch_name_taken(new) && !(only_own && !d.swatches_iter().any(|w| w.name == old && own(w))) +} + +/// Rename a pattern in swatches, paints and the edit record. Of the swatches named `old`, only the +/// pattern's own take the new name. fn rename_pattern_uses(d: &mut Document, old: &str, new: &str) { let fix = |p: &mut Paint| { if let Paint::Pattern { pattern, .. } = p @@ -489,7 +505,7 @@ fn rename_pattern_uses(d: &mut Document, old: &str, new: &str) { } }; for sw in d.swatches.iter_mut().chain(d.swatch_groups.iter_mut().flat_map(|g| g.swatches.iter_mut())) { - if sw.name == old { + if sw.name == old && pattern_name(&sw.paint).is_some_and(|p| p == old) { sw.name = new.to_string(); } fix(&mut sw.paint); diff --git a/crates/engine/src/tests_pattern.rs b/crates/engine/src/tests_pattern.rs index a65a115e6..178404e14 100644 --- a/crates/engine/src/tests_pattern.rs +++ b/crates/engine/src/tests_pattern.rs @@ -179,6 +179,50 @@ fn options_rename_updates_swatch_and_paints() { assert!(matches!(node(&s, big).appearance.fill_paint(), Paint::Pattern { pattern, .. } if pattern == "Spots")); } +/// Pattern Options refuses a new name that a swatch of another kind, a second swatch of the +/// pattern or a color group has, and takes a name that only the pattern's own swatch has. +#[test] +fn options_rename_refuses_a_name_another_swatch_has() { + let mut s = session(); + make_dots(&mut s); + s.execute("swatch.new", &json!({"name": "Brick", "color": "#aa3300"})).unwrap(); + s.execute("swatch.newGroup", &json!({"name": "Mine", "colors": ["#0000ff"]})).unwrap(); + // "Dots copy": a second swatch of the pattern. + s.execute("swatch.duplicate", &json!({"name": "Dots"})).unwrap(); + for taken in [" Brick ", "Mine", "Dots copy", "[Registration]"] { + let e = s.execute("pattern.options", &json!({"name": "Dots", "newName": taken})).unwrap_err().to_string(); + assert!(e.contains(&format!("a swatch or color group named `{}` exists", taken.trim())), "{e}"); + } + assert!(doc(&s).pattern("Dots").is_some()); + let named = |s: &Session, n: &str| doc(s).swatches_iter().filter(|w| w.name == n).count(); + assert_eq!((named(&s, "Brick"), named(&s, "Dots"), named(&s, "Dots copy")), (1, 1, 1)); + // The pattern's own swatch, renamed in Swatch Options: the pattern can take its name. + s.execute("swatch.delete", &json!({"name": "Dots copy"})).unwrap(); + s.execute("swatch.edit", &json!({"name": "Dots", "newName": "Spots"})).unwrap(); + assert_eq!(s.execute("pattern.options", &json!({"name": "Dots", "newName": "Spots"})).unwrap()["name"], "Spots"); + assert_eq!(named(&s, "Spots"), 1); + assert!(matches!(&doc(&s).swatch("Spots").unwrap().paint, Paint::Pattern { pattern, .. } if pattern == "Spots")); + // With no swatch of the pattern under its name, a color group's name is still refused. + s.execute("swatch.edit", &json!({"name": "Spots", "newName": "Spot Tiles"})).unwrap(); + assert!(s.execute("pattern.options", &json!({"name": "Spots", "newName": "Mine"})).is_err()); +} + +/// Renaming a pattern renames only its own swatches: a color swatch that has the pattern's old +/// name keeps it, and art linked to that swatch stays linked. +#[test] +fn options_rename_leaves_other_swatches_of_the_old_name() { + let mut s = session(); + make_dots(&mut s); + s.execute("swatch.edit", &json!({"name": "Dots", "newName": "Dot Tiles"})).unwrap(); + s.execute("swatch.new", &json!({"name": "Dots", "color": "#3366cc", "global": true})).unwrap(); + let r = rect(&mut s, 100.0, 100.0, 20.0, 20.0); + s.execute("paint.setFill", &json!({"ids": [r.0], "swatch": "Dots"})).unwrap(); + s.execute("pattern.options", &json!({"name": "Dots", "newName": "Spots"})).unwrap(); + assert!(doc(&s).swatch("Dots").is_some_and(|w| matches!(w.paint, Paint::Solid { .. })), "the color swatch keeps its name"); + assert!(matches!(&doc(&s).swatch("Dot Tiles").unwrap().paint, Paint::Pattern { pattern, .. } if pattern == "Spots")); + assert!(matches!(node(&s, r).appearance.fill_paint(), Paint::Solid { swatch: Some(l), .. } if l == "Dots")); +} + #[test] fn delete_pattern_unpaints_objects() { let mut s = session(); @@ -303,6 +347,20 @@ fn made_copied_and_renamed_patterns_trim_their_names() { assert!(doc(&s).swatch("Spots").is_some() && doc(&s).swatch(" Spots ").is_none()); } +/// Make and Save a Copy number a name that a color group has, as they number a swatch's name +/// (swatches and color groups share one namespace). +#[test] +fn made_and_copied_patterns_number_a_color_groups_name() { + let mut s = session(); + s.execute("swatch.newGroup", &json!({"name": "Mine", "colors": ["#0000ff"]})).unwrap(); + s.execute("swatch.newGroup", &json!({"name": "New Pattern 1", "colors": ["#00ff00"]})).unwrap(); + let r = rect(&mut s, 0.0, 0.0, 10.0, 10.0); + assert_eq!(s.execute("object.pattern.make", &json!({"ids": [r.0], "name": "Mine", "edit": false})).unwrap()["name"], "Mine 2"); + assert_eq!(s.execute("object.pattern.make", &json!({"ids": [r.0], "edit": false})).unwrap()["name"], "New Pattern 2"); + s.execute("object.pattern.edit", &json!({"name": "Mine 2"})).unwrap(); + assert_eq!(s.execute("object.pattern.saveCopy", &json!({"name": "Mine"})).unwrap()["name"], "Mine 3"); +} + #[test] fn undo_make_pattern() { let mut s = session(); diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 6819ea0be..82abae807 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -3763,6 +3763,7 @@ axis Both И двете @msg a page tree node has no kids въззел в дървото на страниците няма деца @msg a page's placement is not finite разположението на страницата не е крайно @msg a preset named `{_1}` exists предварителна настройка с име `{_1}` съществува +@msg a swatch or color group named `{n}` exists образец или цветова група с име `{n}` вече съществува @msg a slice size must be a positive number of points размерът на парчето трябва да бъде положително число в точки @msg a spine keeps at least two points оста запазва поне две точки @msg a stream runs past the end потокът преминава отвъд края diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 37c9810b8..110969143 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -4142,6 +4142,7 @@ imageTrace Outline Obrys @msg a page tree node has no kids uzel stromu stránek nemá potomky @msg a page's placement is not finite umístění stránky není konečné @msg a preset named `{_1}` exists přednastavení s názvem `{_1}` existuje +@msg a swatch or color group named `{n}` exists položka vzorníku nebo skupina barev s názvem `{n}` existuje @msg a slice size must be a positive number of points velikost řezu musí být kladný počet bodů @msg a spine keeps at least two points páteř si ponechává alespoň dva body @msg a stream runs past the end datový proud přesahuje konec souboru diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index e1e39ad11..aa5ab3ed5 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -3891,6 +3891,7 @@ imageTrace Outline Kontur @msg a page tree node has no kids en node i sidetræet har ingen børn @msg a page's placement is not finite en sides placering er ikke endelig @msg a preset named `{_1}` exists der findes en forudindstilling med navnet `{_1}` +@msg a swatch or color group named `{n}` exists der findes en farveprøve eller farvegruppe med navnet `{n}` @msg a slice size must be a positive number of points en udsnitsstørrelse skal være et positivt antal punkter @msg a stream runs past the end en strøm løber forbi slutningen @msg a stream's length is wrong længden på en strøm er forkert diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index f7403febf..9a6ca2ccc 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -3777,6 +3777,7 @@ theme Appearance Erscheinungsbild @msg a page tree node has no kids ein Knoten des Seitenbaums hat keine untergeordneten Einträge @msg a page's placement is not finite die Platzierung einer Seite ist nicht endlich @msg a preset named `{_1}` exists eine Vorgabe namens `{_1}` ist bereits vorhanden +@msg a swatch or color group named `{n}` exists ein Farbfeld oder eine Farbgruppe namens `{n}` ist bereits vorhanden @msg a slice size must be a positive number of points eine Slice-Größe muss eine positive Anzahl von Punkt sein @msg a spine keeps at least two points eine Achse behält mindestens zwei Punkte @msg a stream runs past the end ein Datenstrom reicht über das Ende hinaus diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 6b0278d3d..46de4f34d 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -3820,6 +3820,7 @@ theme Appearance Εμφάνιση @msg a page tree node has no kids ο κόμβος δέντρου σελίδων δεν έχει θυγατρικά στοιχεία @msg a page's placement is not finite η τοποθέτηση της σελίδας δεν έχει πεπερασμένες τιμές @msg a preset named `{_1}` exists υπάρχει ήδη προεπιλογή με όνομα `{_1}` +@msg a swatch or color group named `{n}` exists υπάρχει ήδη δείγμα χρώματος ή ομάδα χρωμάτων με όνομα `{n}` @msg a slice size must be a positive number of points το μέγεθος τομής πρέπει να είναι θετικός αριθμός στιγμών @msg a spine keeps at least two points απαιτούνται τουλάχιστον δύο σημεία κεντρικής καμπύλης @msg a stream runs past the end η ροή υπερβαίνει το τέλος diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 8a79f0b0b..4572e1348 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -3840,6 +3840,7 @@ axis Both Ambos @msg a page tree node has no kids un nodo del árbol de páginas no tiene hijos @msg a page's placement is not finite la posición de una página no es finita @msg a preset named `{_1}` exists ya existe un preajuste llamado `{_1}` +@msg a swatch or color group named `{n}` exists ya existe una muestra o un grupo de colores llamado `{n}` @msg a slice size must be a positive number of points el tamaño de un sector debe ser un número positivo de puntos @msg a spine keeps at least two points un eje conserva al menos dos puntos @msg a stream runs past the end un flujo se extiende más allá del final diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 69f05c350..0a090377d 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -4068,6 +4068,7 @@ imageTrace Outline Ääriviiva @msg a page tree node has no kids sivupuun solmulla ei ole lapsia @msg a page's placement is not finite sivun sijainti ei ole äärellinen @msg a preset named `{_1}` exists esiasetus nimeltä `{_1}` on olemassa +@msg a swatch or color group named `{n}` exists värimalli tai väriryhmä nimeltä `{n}` on jo olemassa @msg a slice size must be a positive number of points viipaleen koon on oltava positiivinen määrä pisteitä @msg a spine keeps at least two points selkärangassa on oltava vähintään kaksi pistettä @msg a stream runs past the end virta jatkuu loppumerkin yli diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 6cd44f9be..51def5e55 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -3837,6 +3837,7 @@ axis Both Les deux @msg a page tree node has no kids un nœud de l’arborescence des pages n’a pas d’enfants @msg a page's placement is not finite la position d’une page n’est pas finie @msg a preset named `{_1}` exists un paramètre prédéfini nommé `{_1}` existe déjà +@msg a swatch or color group named `{n}` exists une nuance ou un groupe de couleurs nommé `{n}` existe déjà @msg a slice size must be a positive number of points la taille d’une tranche doit être un nombre de points positif @msg a spine keeps at least two points une dorsale garde au moins deux points @msg a stream runs past the end un flux dépasse la fin du fichier diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index f03a7cda5..398de053b 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -4368,6 +4368,7 @@ imageTrace Outline Körvonal @msg a PostScript error ({name} in `{op}`: {at}), in {procs} PostScript-hiba ({name}, itt: `{op}`: {at}), eljárás: {procs} @msg a PostScript error ({name}) PostScript-hiba ({name}) @msg a preset named `{_1}` exists már van ilyen nevű előbeállítás: `{_1}` +@msg a swatch or color group named `{n}` exists már van ilyen nevű színminta vagy színcsoport: `{n}` @msg a slice size must be a positive number of points a szelet mérete pozitív pontérték kell legyen @msg a spine keeps at least two points a gerincen legalább két pont marad @msg a stream runs past the end egy adatfolyam túlnyúlik a végén diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index faeb00fb9..0caf7e5f6 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -3891,6 +3891,7 @@ imageTrace Outline Kontur @msg a page tree node has no kids simpul pohon halaman tidak memiliki anak @msg a page's placement is not finite penempatan halaman bukan bilangan hingga @msg a preset named `{_1}` exists preset bernama `{_1}` sudah ada +@msg a swatch or color group named `{n}` exists sampel atau grup warna bernama `{n}` sudah ada @msg a slice size must be a positive number of points ukuran irisan harus berupa bilangan positif dalam titik @msg a stream runs past the end aliran melewati akhir @msg a stream's length is wrong panjang aliran salah diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index b5a459847..3fdc91268 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -3879,6 +3879,7 @@ axis Both Entrambi @msg a page tree node has no kids un nodo dell'albero delle pagine non ha elementi figli @msg a page's placement is not finite la posizione di una pagina non è finita @msg a preset named `{_1}` exists esiste già un preset denominato `{_1}` +@msg a swatch or color group named `{n}` exists esiste già un campione o un gruppo colori denominato `{n}` @msg a slice size must be a positive number of points la dimensione di una sezione deve essere un numero positivo di punti @msg a spine keeps at least two points una dorsale mantiene almeno due punti @msg a stream runs past the end uno stream prosegue oltre la fine diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index c7d1a5b11..f670a4bb5 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -4251,6 +4251,7 @@ swatch Bright Violet ビビッドバイオレット @msg no plug-in {id} is installed プラグイン {id} はインストールされていません @msg {_1} is a live effect (Effect › Plug-ins) {_1} はライブ効果です (「効果 › プラグイン」から使用してください) @msg a preset named `{_1}` exists `{_1}` という名前のプリセットはすでに存在します +@msg a swatch or color group named `{n}` exists `{n}` という名前のスウォッチまたはカラーグループはすでに存在します @msg no print preset named `{base}` `{base}` という名前のプリントプリセットが見つかりません @msg no print preset named `{name}` `{name}` という名前のプリントプリセットが見つかりません @msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) `{name}` という名前の保存済みプリントプリセットが見つかりません ({DEFAULT_PRESET} は保護されています: これを元に新しいプリセットを作成してください) diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index cf479df03..c0907da2e 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -4183,6 +4183,7 @@ theme Appearance 외관 @msg no plug-in {id} is installed 플러그인 {id}이(가) 설치되어 있지 않습니다 @msg {_1} is a live effect (Effect › Plug-ins) {_1}은(는) 라이브 효과입니다(효과 › 플러그인) @msg a preset named `{_1}` exists `{_1}`이라는 프리셋이 이미 있습니다 +@msg a swatch or color group named `{n}` exists `{n}`이라는 견본 또는 색상 그룹이 이미 있습니다 @msg no print preset named `{base}` `{base}`라는 인쇄 프리셋이 없습니다 @msg no print preset named `{name}` `{name}`이라는 인쇄 프리셋이 없습니다 @msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) `{name}`이라는 저장된 인쇄 프리셋이 없습니다({DEFAULT_PRESET}은(는) 보호되어 있으니 이를 바탕으로 새로 만드세요) diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 7cb434430..989a8fa22 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -3905,6 +3905,7 @@ imageTrace Outline Contour @msg a page tree node has no kids een knooppunt van de paginaboom heeft geen kinderen @msg a page's placement is not finite de plaatsing van een pagina is niet eindig @msg a preset named `{_1}` exists er bestaat een voorinstelling met de naam `{_1}` +@msg a swatch or color group named `{n}` exists er bestaat een kleurstaal of kleurgroep met de naam `{n}` @msg a slice size must be a positive number of points een slicegrootte moet een positief aantal punten zijn @msg a stream runs past the end een stream loopt voorbij het einde @msg a stream's length is wrong de lengte van een stream is onjuist diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index fd9f8c4cb..3557705ca 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3964,6 +3964,7 @@ imageTrace Outline Kontur @msg a page tree node has no kids węzeł drzewa stron nie ma elementów potomnych @msg a page's placement is not finite położenie strony nie jest skończone @msg a preset named `{_1}` exists ustawienie o nazwie `{_1}` już istnieje +@msg a swatch or color group named `{n}` exists próbka lub grupa kolorów o nazwie `{n}` już istnieje @msg a slice size must be a positive number of points rozmiar plasterka musi być dodatnią liczbą punktów @msg a spine keeps at least two points grzbiet musi mieć co najmniej dwa punkty @msg a stream runs past the end strumień wykracza poza koniec diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 12574e352..aa47772df 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -3428,6 +3428,7 @@ axis Both Ambos @msg a page tree node has no kids um nó da árvore de páginas não tem filhos @msg a page's placement is not finite a posição de uma página não é finita @msg a preset named `{_1}` exists já existe uma predefinição chamada `{_1}` +@msg a swatch or color group named `{n}` exists já existe uma amostra ou um grupo de cores chamado `{n}` @msg a slice size must be a positive number of points o tamanho de uma fatia deve ser um número positivo de pontos @msg a spine keeps at least two points uma coluna mantém pelo menos dois pontos @msg a stream runs past the end um fluxo passa do fim diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 9f8bde5ac..798625027 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -3875,6 +3875,7 @@ axis Both Оба @msg a page tree node has no kids узел дерева страниц не имеет потомков @msg a page's placement is not finite размещение страницы не конечно @msg a preset named `{_1}` exists набор с именем `{_1}` уже существует +@msg a swatch or color group named `{n}` exists образец или цветовая группа с именем `{n}` уже существует @msg a slice size must be a positive number of points размер фрагмента должен быть положительным числом пунктов @msg a spine keeps at least two points остов сохраняет как минимум две точки @msg a stream runs past the end поток выходит за конец diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 190e3cce1..355c946b9 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -3768,6 +3768,7 @@ axis Both Båda @msg a page tree node has no kids en nod i sidträdet har inga barn @msg a page's placement is not finite en sidas placering är inte ändlig @msg a preset named `{_1}` exists det finns redan en förinställning med namnet `{_1}` +@msg a swatch or color group named `{n}` exists det finns redan en färgruta eller färggrupp med namnet `{n}` @msg a slice size must be a positive number of points en skivstorlek måste vara ett positivt antal punkter @msg a spine keeps at least two points en ryggrad behåller minst två punkter @msg a stream runs past the end en ström fortsätter förbi slutet diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index edb453fba..cffee06d5 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -3863,6 +3863,7 @@ axis Both Обидві @msg a page tree node has no kids вузол дерева сторінок не має нащадків @msg a page's placement is not finite розміщення сторінки не звісно @msg a preset named `{_1}` exists набір з іменем `{_1}` вже існує +@msg a swatch or color group named `{n}` exists зразок або колірна група з ім’ям `{n}` вже існує @msg a slice size must be a positive number of points розмір фрагмента має бути позитивним числом пунктів @msg a spine keeps at least two points вісь зберігає як мінімум дві точки @msg a stream runs past the end потік виходить за кінець diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 0815110e0..a57b0d135 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -4016,6 +4016,7 @@ theme Appearance Giao diện @msg a PostScript error ({name} in `{op}`: {at}), in {procs} lỗi PostScript ({name} trong `{op}`: {at}), trong {procs} @msg a PostScript error ({name}) lỗi PostScript ({name}) @msg a preset named `{_1}` exists đã có thiết lập sẵn tên `{_1}` +@msg a swatch or color group named `{n}` exists đã có mẫu màu hoặc nhóm màu tên `{n}` @msg a slice size must be a positive number of points kích thước lát cắt phải là số điểm dương @msg a spine keeps at least two points trục phải giữ ít nhất hai điểm @msg a stream runs past the end luồng dữ liệu vượt qua cuối diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 827f03dd3..b2bac87e2 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -3992,6 +3992,7 @@ imageTrace Outline 轮廓 @msg no plug-in {id} is installed 未安装增效工具 {id} @msg {_1} is a live effect (Effect › Plug-ins) {_1} 是实时效果 (效果 › 增效工具) @msg a preset named `{_1}` exists 名为 `{_1}` 的预设已存在 +@msg a swatch or color group named `{n}` exists 名为 `{n}` 的色板或颜色组已存在 @msg no print preset named `{base}` 没有名为 `{base}` 的打印预设 @msg no print preset named `{name}` 没有名为 `{name}` 的打印预设 @msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) 没有名为 `{name}` 的已保存打印预设 ({DEFAULT_PRESET} 受保护:请以它为基础新建一个) diff --git a/docs/mcp.md b/docs/mcp.md index 2a2d19b43..6ef5c0ab0 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -1581,9 +1581,12 @@ In pattern editing mode the tile edge (`pattern.options {showTileEdge}`) and the `patternTileEdgeColor`. `object.pattern.make {name?}`, `object.pattern.saveCopy {name?}` and `pattern.options {newName?}` trim the white space -around the name they are given. The first two number a name that a pattern or swatch has ("Dots 2"), and -`pattern.options` refuses a name that another pattern has. A blank name gives "New Pattern N" in -`object.pattern.make` and "`` copy" in `object.pattern.saveCopy`, and `pattern.options` keeps the name. +around the name they are given. The first two number a name that a pattern, a swatch or a color group has ("Dots 2"), +and `pattern.options` refuses a name that another pattern, a swatch or a color group has. It takes a name that only the +pattern's own swatches have (a name Swatch Options gave the pattern's swatch, for example) when none of them still has +the old name. A rename gives the new name to the pattern's swatches that had the old one; any other swatch that has the +old name keeps it. A blank name gives "New Pattern N" in `object.pattern.make` and "`` copy" in +`object.pattern.saveCopy`, and `pattern.options` keeps the name. ```json {"name":"run_command","arguments":{"command":"pattern.options","params":{"showSwatchBounds":true}}} diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 3752e851a..97cae5ccd 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -418,6 +418,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **Document Setup modal handoff:** Document Setup → Edit Artboards applies valid settings, closes the dialog and activates the Artboard tool. Pointer-click tests cover successful handoff, invalid settings, OK, Cancel and Undo in `dialogs/document_setup.rs`. - **Control bar with the Artboard tool:** while the Artboard tool is in use, the Control bar shows the active artboard's controls in place of the object controls: Preset, Portrait and Landscape, New Artboard, Delete Artboard, the Name field, Artboard Options, Rearrange All Artboards, the Transform link, Move and Scale Artwork with Artboard and, while the bar has room, the reference point with X and Y, and W and H with the Constrain Width and Height Proportions link. Each runs the command its Properties or Transform panel counterpart runs (`artboard.setProps`, `artboard.new`, `artboard.delete`, `tool.options {tool: "artboard"}`), and the reference point is the one the Transform panel uses. In an 800-point window the controls up to the Transform link fit, Move and Scale Artwork with Artboard show as their boxes alone with their names in tooltips, and the link opens the Transform panel with the artboard's position and size. Pointer and key tests in `panels/artboards.rs` cover the controls, W about the reference point, the orientation and the name. Public reference: [Using multiple artboards](https://web.archive.org/web/20210925094818/https://helpx.adobe.com/illustrator/using/using-multiple-artboards.html) (archived copy). - **Names of new and renamed artboards, brushes and patterns:** `artboard.new` and `artboard.setProps` trim the white space around the name they are given, as the Artboards panel's rename does, and a blank name gives the artboard its default name, Artboard N for the Nth artboard. `brush.new`, `brush.duplicate`, `brush.options`, `object.pattern.make`, `object.pattern.saveCopy` and `pattern.options` trim the name as the Brush Options dialog and the Pattern Options panel do, and compare the trimmed name with the names there are; a blank name gives the name each command gives when there is none, and Brush Options and Pattern Options keep the name. Tests in `tests_panelcmds.rs`, `tests_brushsym.rs` and `tests_pattern.rs` cover each command. Public references: [Using multiple artboards](https://web.archive.org/web/20210925094818/https://helpx.adobe.com/illustrator/using/using-multiple-artboards.html), [Brushes](https://web.archive.org/web/20251018101754/https://helpx.adobe.com/illustrator/using/brushes.html) and [Create and edit patterns](https://web.archive.org/web/20211031180358/https://helpx.adobe.com/illustrator/using/create-edit-patterns.html) (archived copies). +- **Pattern names and swatch names:** `pattern.options` refuses a new name that another pattern, a swatch or a color group has. It takes a name that only the pattern's own swatches have. The reference page does not say what happens when the new name is taken; the refusal follows VectorCraft's rule that swatches and color groups share one namespace (`Document::swatch_name_taken`), which `swatch.new` and `swatch.edit` also follow. A rename gives the new name only to the pattern's swatches that had the old one; any other swatch with the old name keeps it, and art linked to that swatch stays linked. `object.pattern.make` and `object.pattern.saveCopy` number a color group's name as they number a swatch's. Tests in `tests_pattern.rs` cover each command. Public reference: [Create and edit patterns](https://web.archive.org/web/20211031180358/https://helpx.adobe.com/illustrator/using/create-edit-patterns.html) (archived copy). - **Edit Artboards in Properties:** the active artboard's name is a field. Typing a new name and pressing Enter or clicking elsewhere renames that artboard in one undo step, as the Artboards panel's rename does (`artboard.setProps {index, name}`); pressing Escape or leaving the field empty keeps the old name. A click on the Artboard Options icon beside the field opens Artboard Options for the active artboard, as double-clicking the Artboard tool's button does (`tool.options {tool: "artboard"}`). Pointer and key tests in `panels/properties.rs` cover both. Public references: [Resize artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/resize-artboards.html) and [Create and add new artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/add-new-artboards.html). - **Artboard Escape routing:** Escape leaves Artboard editing for Selection; during a drag it first cancels the gesture. Headless egui key events cover idle Escape, active-drag cancellation and a subsequent Escape in `tests_distortkeys.rs`. - **Artboard deletion keys:** Delete/Backspace delete selected boards in one Undo step, retaining the original first board in the current order when all are selected, as in the public [Delete artboards guide](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/delete-artboards.html). The last board and active drags stay protected; neither key clears selected artwork. Headless shortcut regressions in `tests_distortkeys.rs` cover reordered boards, artwork, saved state and Undo/Redo.