Merge pull request #7775 from dotCooCoo/feat/recolor-new-row

Recolor Artwork: New Row adds a row that current colors can be merged into
This commit is contained in:
BFlat
2026-11-15 14:42:46 +07:00
committed by GitHub
33 changed files with 186 additions and 9 deletions
+8 -4
View File
@@ -34,7 +34,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Recolor Artwork",
["Edit", "Edit Colors"],
None,
"{map: rows [{from: [colour keys (recolor.colors) or \"#rrggbb\" (every colour shown as that hex)], to: colour (hex, key, {c,m,y,k} or {l,a,b}) | null (in from, to and groupColors, #rrggbbaa and #rgba are refused), exclude?: false (the row's colours are kept), tint?: 100 (percent; with limitTo, art linked to the global or spot swatch the new color is takes this share of its tint)}] | {\"key or #rrggbb\": colour, …} (one row per colour), method?: \"exact\" (default)|\"preserveTints\"|\"scaleTints\"|\"tintsShades\"|\"hueShift\" (how a row's colours take its new colour, relative to the row's darkest, average or most saturated colour), limitTo?: swatch library id or name; or \"document\" (the document's swatches): new colours snap to its nearest colour, group?: colour group to rewrite, groupColors?: [colour] (what `group` is rewritten with; default: the rows' new colours, in order, excluded rows left out), rename?: the group's new name, includeImages?: true, includePatterns?: true, globalAdjust?: {saturation?: 0, brightness?: 0, temperature?: 0, luminosity?: 0} (Global Adjust: percent from -100 to 100, 0 changes nothing; every colour a row that isn't excluded recolours, and the group's new colours, take the same change after the row's new colour: saturation scales HSB saturation by 1 + saturation/100, brightness moves HSB brightness that share of the way to full or to black, temperature adds 0.2 × temperature/100 to red and takes it from blue, luminosity moves Lab lightness that share of the way to 100 or 0 keeping a and b; with limitTo each row's new colour takes the change before it snaps to the library)} recolour the selection (gradients, text, meshes, image pixels and pattern tiles included; each new colour keeps the model of the one it replaces (model?: \"keep\" default | \"new\" (the new colour's own model, so a CMYK colour replacing RGB art gives CMYK art) | \"document\" (the document's colour mode)), except a library swatch's with limitTo: art takes the swatch itself, linked to a global or spot colour at its tints, and the swatch joins the document's swatches; art linked to a global swatch of `group` that is rewritten with its row's new color stays linked to it, at its tint, unless the row converts the color into the art's model; other recolored art, and art in a row linked to a global swatch of `group` that is rewritten with another color, keeps its row's new color and loses its swatch link) and the group, as one undo step (none when nothing changes). On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke, and on a graph label the new paint of its characters and rows for every label of its kind (docs/mcp.md) → {changed, swatches: [names added to the document's swatches], relinked?: with group, the other paints linked to its global swatches that took their new colors, note?: set when a colour kept a model other than its new colour's (see model), notChanged: {locked, hidden} (as edit.colors.invert), warning?: set when notChanged counts any object}",
"{map: rows [{from: [colour keys (recolor.colors) or \"#rrggbb\" (every colour shown as that hex); may be empty: an empty row recolours nothing, adds no library swatch and gives `group` no colour], to: colour (hex, key, {c,m,y,k} or {l,a,b}) | null (in from, to and groupColors, #rrggbbaa and #rgba are refused), exclude?: false (the row's colours are kept), tint?: 100 (percent; with limitTo, art linked to the global or spot swatch the new color is takes this share of its tint)}] | {\"key or #rrggbb\": colour, …} (one row per colour), method?: \"exact\" (default)|\"preserveTints\"|\"scaleTints\"|\"tintsShades\"|\"hueShift\" (how a row's colours take its new colour, relative to the row's darkest, average or most saturated colour), limitTo?: swatch library id or name; or \"document\" (the document's swatches): new colours snap to its nearest colour, group?: colour group to rewrite, groupColors?: [colour] (what `group` is rewritten with; default: the rows' new colours, in order, excluded and empty rows left out), rename?: the group's new name, includeImages?: true, includePatterns?: true, globalAdjust?: {saturation?: 0, brightness?: 0, temperature?: 0, luminosity?: 0} (Global Adjust: percent from -100 to 100, 0 changes nothing; every colour a row that isn't excluded recolours, and the group's new colours, take the same change after the row's new colour: saturation scales HSB saturation by 1 + saturation/100, brightness moves HSB brightness that share of the way to full or to black, temperature adds 0.2 × temperature/100 to red and takes it from blue, luminosity moves Lab lightness that share of the way to 100 or 0 keeping a and b; with limitTo each row's new colour takes the change before it snaps to the library)} recolour the selection (gradients, text, meshes, image pixels and pattern tiles included; each new colour keeps the model of the one it replaces (model?: \"keep\" default | \"new\" (the new colour's own model, so a CMYK colour replacing RGB art gives CMYK art) | \"document\" (the document's colour mode)), except a library swatch's with limitTo: art takes the swatch itself, linked to a global or spot colour at its tints, and the swatch joins the document's swatches; art linked to a global swatch of `group` that is rewritten with its row's new color stays linked to it, at its tint, unless the row converts the color into the art's model; other recolored art, and art in a row linked to a global swatch of `group` that is rewritten with another color, keeps its row's new color and loses its swatch link) and the group, as one undo step (none when nothing changes). On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke, and on a graph label the new paint of its characters and rows for every label of its kind (docs/mcp.md) → {changed, swatches: [names added to the document's swatches], relinked?: with group, the other paints linked to its global swatches that took their new colors, note?: set when a colour kept a model other than its new colour's (see model), notChanged: {locked, hidden} (as edit.colors.invert), warning?: set when notChanged counts any object}",
has_doc,
apply
),
@@ -1614,8 +1614,11 @@ fn apply(s: &mut Session, p: &Value) -> Result<Value> {
Some(k) => super::swatchlib::library_swatches(s, k).unwrap_or_default(),
None => vec![],
};
let row_swatches: Vec<Option<Swatch>> =
rows.iter().map(|r| r.new_color().and_then(|c| lib_swatches.iter().find(|w| w.paint.color() == Some(c)).cloned())).collect();
// A row with no current colors recolors nothing, so its swatch does not join the document.
let row_swatches: Vec<Option<Swatch>> = rows
.iter()
.map(|r| r.new_color().filter(|_| !r.from.is_empty()).and_then(|c| lib_swatches.iter().find(|w| w.paint.color() == Some(c)).cloned()))
.collect();
// While an object of a dynamic symbol instance's art is selected, the selection is refused, as
// `selected_roots` refuses it.
super::edit::refuse_symbol_part(s)?;
@@ -1657,7 +1660,8 @@ fn apply(s: &mut Session, p: &Value) -> Result<Value> {
.ok_or_else(|| bad(C, format!("bad colour {v} in groupColors")))
})
.collect::<Result<_>>()?,
None => rows.iter().filter_map(Row::new_color).map(|c| mapped_adjust.apply(c)).collect(),
// A row with no current colors gives the group no color.
None => rows.iter().filter(|r| !r.from.is_empty()).filter_map(Row::new_color).map(|c| mapped_adjust.apply(c)).collect(),
};
let rename = text_param(p, "rename", C)?;
let colorized_ok = super::paint::check_colorized(&s.doc()?.doc).is_ok();
+56
View File
@@ -303,6 +303,62 @@ fn methods_and_bad_params() {
assert!(s.execute("recolor.apply", &json!({"map": {}})).is_err(), "needs art or a group");
}
/// A row with no current colors (Recolor Artwork's New Row) recolors nothing, and records no
/// undo step on its own; beside it the other rows recolor as before, and the weights and
/// randomize keep it. A row without `from`, or with a bad current color, is still refused.
#[test]
fn an_empty_row_recolors_nothing() {
let mut s = session();
let red = rect(&mut s, json!("#ff0000"));
let blue = rect(&mut s, json!("#0000ff"));
select_all(&mut s);
let undo = s.doc().unwrap().history.undo.len();
let empty = json!({"from": [], "to": "rgb 0 255 0"});
let r = run(&mut s, "recolor.apply", json!({"map": [empty]}));
assert_eq!(r["changed"], json!(0));
assert_eq!(s.doc().unwrap().history.undo.len(), undo, "no undo step");
let map = json!([empty, {"from": ["rgb 0 0 255"], "to": "rgb 255 255 0"}]);
let w = run(&mut s, "recolor.weigh", json!({"map": map}));
assert_eq!(w["map"], map, "the rows as given");
assert_eq!(w["shares"], json!([0.0, 100.0]));
let shuffled = run(&mut s, "recolor.randomize", json!({"map": map, "seed": 1}));
assert_eq!(shuffled["map"][0]["from"], json!([]));
run(&mut s, "recolor.apply", json!({"map": map}));
assert_eq!((fill(&s, red).to_hex(), fill(&s, blue).to_hex()), ("#ff0000".into(), "#ffff00".into()));
assert_eq!(s.doc().unwrap().history.undo.len(), undo + 1);
for bad in [json!([{"to": "rgb 0 255 0"}]), json!([{"from": ["nonsense"], "to": "rgb 0 255 0"}])] {
assert!(s.execute("recolor.apply", &json!({"map": bad})).is_err(), "{bad}");
}
}
/// An empty row adds no library swatch with `limitTo`, and gives a rewritten group no color: the
/// group keeps one color per row that holds current colors.
#[test]
fn an_empty_row_adds_no_swatch_and_no_group_color() {
let mut s = session();
rect(&mut s, json!("#ff0000"));
rect(&mut s, json!("#0000ff"));
select_all(&mut s);
let undo = s.doc().unwrap().history.undo.len();
let swatches = s.doc().unwrap().doc.swatches.len();
let r = run(&mut s, "recolor.apply", json!({"map": [{"from": [], "to": "#ff0033"}], "limitTo": "web-safe-216"}));
assert_eq!(r["swatches"], json!([]), "{r}");
assert_eq!(s.doc().unwrap().doc.swatches.len(), swatches);
assert_eq!(s.doc().unwrap().history.undo.len(), undo, "no undo step");
run(&mut s, "swatch.newGroup", json!({"name": "Pair", "colors": ["#ff0000", "#0000ff"]}));
select_all(&mut s);
let map = json!([
{"from": [], "to": "rgb 0 255 0"},
{"from": ["rgb 255 0 0"], "to": "rgb 255 0 0"},
{"from": ["rgb 0 0 255"], "to": "rgb 0 0 255"},
]);
run(&mut s, "recolor.apply", json!({"map": map, "group": "Pair"}));
let d = &s.doc().unwrap().doc;
let g = d.swatch_groups.iter().find(|g| g.name == "Pair").unwrap();
let hexes: Vec<String> = g.swatches.iter().map(|w| w.paint.color().unwrap().to_hex()).collect();
assert_eq!(hexes, ["#ff0000", "#0000ff"]);
}
#[test]
fn randomize_shuffles_new_colours_and_skips_excluded_rows() {
let mut s = session();
+88 -4
View File
@@ -8,7 +8,9 @@
//! whose OK sets it in the model it was picked in), the color mode menu and its sliders (HSB, RGB,
//! CMYK or Lab, and Tint when the new color is a spot color of the library), which edit the
//! first selected row's new color (once they have, OK gives the art each new color in its own
//! model), Merge and Separate, Randomize order or
//! model), New Row (a row with no current colors above the first selected row, or at the top,
//! with that row's new color; it recolors nothing until Merge moves colors into it), Merge and
//! Separate, Randomize order or
//! saturation and brightness, Reset, the Method and Limit to Library (its folder button imports a
//! library file and limits to it; a library's colours apply as its swatches: a spot colour stays a
//! spot colour, and joins the document's swatches). Edit tab: a hue and
@@ -566,9 +568,11 @@ fn selection(d: &Dialog) -> Vec<usize> {
fn apply_params(d: &Dialog) -> Value {
let rs = rows(d);
let group = d.fields.get("group").and_then(Value::as_str).map(|g| {
let rest =
d.fields.get("__assign").and_then(Value::as_array).filter(|_| d.bool("__art")).map_or(&[][..], |a| a.get(rs.len()..).unwrap_or_default());
let colors: Vec<&Value> = rs.iter().map(|r| &r["to"]).chain(rest).collect();
// A row made with New Row that holds no current colors gives the group no color.
let filled: Vec<&Value> = rs.iter().filter(|r| !r["from"].as_array().is_some_and(Vec::is_empty)).map(|r| &r["to"]).collect();
let assign = d.fields.get("__assign").and_then(Value::as_array).filter(|_| d.bool("__art"));
let rest = assign.map_or(&[][..], |a| a.get(filled.len()..).unwrap_or_default());
let colors: Vec<&Value> = filled.into_iter().chain(rest).collect();
let name = d.str("groupName");
let rename = (!name.trim().is_empty() && name != g).then_some(name);
json!({"group": g, "groupColors": colors, "rename": rename})
@@ -778,6 +782,10 @@ fn assign_tab(app: &mut VectorcraftApp, ui: &mut Ui, d: &mut Dialog, rows: &mut
let x = cur.left() + j as f32 * w;
chip(ui, Rect::from_min_max(pos2(x, cur.top()), pos2(x + w - if w > 4.0 { 2.0 } else { 0.0 }, cur.bottom())), *c, false);
}
// A row made with New Row shows an empty slot until colors are merged into it.
if from.is_empty() {
ui.painter().rect_stroke(cur, 1.0, egui::Stroke::new(1.0, t.border), egui::StrokeKind::Inside);
}
if cr.clicked() {
clicked = Some(i);
}
@@ -826,6 +834,14 @@ fn assign_tab(app: &mut VectorcraftApp, ui: &mut Ui, d: &mut Dialog, rows: &mut
}
ui.add_space(4.0);
ui.horizontal(|ui| {
// New Row is available only with art.
if widgets::icon_button_enabled(ui, "plus", tl!("New Row"), false, d.bool("__art"), 24.0).clicked() {
let at = new_row(rows, &sel);
d.fields.insert("__sel".into(), json!([at]));
// The slider edit was kept for a row index that now names another row.
d.fields.remove(COMPS);
changed = true;
}
let can_merge = sel.len() > 1;
if widgets::icon_button_enabled(ui, "combine", tl!("Merge colors into one row"), false, can_merge, 24.0).clicked() {
merge(rows, &sel);
@@ -1218,6 +1234,16 @@ fn separate(rows: &mut Vec<Value>, sel: &[usize]) {
*rows = out;
}
/// New Row: insert a row with no current colors above the first selected row, or at the top
/// when none is selected, and return its index. Its new color is the new color of the row it goes
/// above (black without one). It recolors nothing until current colors are merged into it.
fn new_row(rows: &mut Vec<Value>, sel: &[usize]) -> usize {
let at = sel.iter().copied().filter(|i| *i < rows.len()).min().unwrap_or(0);
let to = rows.get(at).and_then(to_color).unwrap_or_default();
rows.insert(at, json!({"from": [], "to": key(&to)}));
at
}
/// The rows the Edit tab shows: those with a new colour, not excluded.
fn active_rows(rows: &[Value]) -> Vec<usize> {
(0..rows.len()).filter(|i| !excluded(&rows[*i]) && to_color(&rows[*i]).is_some()).collect()
@@ -1540,6 +1566,35 @@ mod tests {
assert_eq!(r.len(), 2);
}
/// New Row goes above the first selected row with that row's new color, or at the top; it has
/// no current colors, and a merge with it as the first selected row fills it.
#[test]
fn a_new_row_goes_above_the_selection_and_takes_merged_colors() {
let mut r = rows();
assert_eq!(new_row(&mut r, &[2, 1]), 1);
assert_eq!(r.len(), 4);
assert_eq!(r[1], json!({"from": [], "to": "rgb 0 0 255"}));
assert_eq!(r[2]["from"], json!(["rgb 0 0 255", "rgb 0 0 128"]), "the selected row moved down");
merge(&mut r, &[1, 3]);
assert_eq!(r[1], json!({"from": ["rgb 0 255 0"], "to": "rgb 0 0 255"}));
assert_eq!(new_row(&mut r, &[]), 0);
assert_eq!(r[0], json!({"from": [], "to": "rgb 255 0 0"}));
let mut none = vec![];
assert_eq!(new_row(&mut none, &[5]), 0);
assert_eq!(none, vec![json!({"from": [], "to": key(&Color::BLACK)})]);
}
/// Opened on a color group, OK rewrites it with the new colors of the rows that hold current
/// colors, then the group's colors no such row took: an empty row gives it no color.
#[test]
fn an_empty_row_gives_the_group_no_color() {
let mut r = rows();
new_row(&mut r, &[]);
let fields = json!({"rows": r, "group": "Trio", "__art": true, "__assign": ["rgb 255 0 0", "rgb 0 0 255", "rgb 0 255 0", "rgb 9 9 9"]});
let p = apply_params(&Dialog::new("recolor", fields));
assert_eq!(p["groupColors"], json!(["rgb 255 0 0", "rgb 0 0 255", "rgb 0 255 0", "rgb 9 9 9"]));
}
/// Randomize after a seed past `u64::MAX` keeps the largest seed.
#[test]
fn next_seed_holds_at_the_largest() {
@@ -1825,6 +1880,35 @@ mod tests {
assert_eq!(app.session.doc().unwrap().history.undo.len(), undo, "the art is unchanged: no undo step");
}
/// A row made with New Row recolors nothing while it is empty, and OK keeps it; the blue art's
/// color merged into it takes its new color, in the preview and at OK.
#[test]
fn colors_merged_into_a_new_row_take_its_new_color() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
let ctx = egui::Context::default();
let [red, blue] = theme_setup(&mut app, &ctx);
let undo = app.session.doc().unwrap().history.undo.len();
let set_rows = |app: &mut VectorcraftApp, rs: Vec<Value>| {
app.ui.dialog.as_mut().unwrap().fields.insert("rows".into(), Value::Array(rs));
};
let mut rs = super::rows(app.ui.dialog.as_ref().unwrap());
let at = new_row(&mut rs, &[]);
rs[at]["to"] = json!("rgb 0 255 0");
set_rows(&mut app, rs.clone());
frame(&mut app, &ctx, 0.1, vec![], 0);
assert_eq!((fill_hex(&app, red), fill_hex(&app, blue)), ("#ff0000".into(), "#0000ff".into()), "an empty row recolors nothing");
let b = rs.iter().position(|r| r["from"] == json!([BLUE])).unwrap();
merge(&mut rs, &[at, b]);
// A second empty row stays empty and is sent at OK too.
new_row(&mut rs, &[]);
set_rows(&mut app, rs);
frame(&mut app, &ctx, 0.2, vec![], 0);
assert_eq!(fill_hex(&app, blue), "#00ff00", "the art previews the merge");
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!((fill_hex(&app, red), fill_hex(&app, blue)), ("#ff0000".into(), "#00ff00".into()));
assert_eq!(app.session.doc().unwrap().history.undo.len(), undo + 1, "one undo step");
}
const RED: &str = "rgb 255 0 0";
const PURPLE: &str = "rgb 128 0 204";
const BLUE: &str = "rgb 0 0 255";
+1
View File
@@ -1981,6 +1981,7 @@ font-technology Variable Променливи
Menu Dialog Диалогов прозорец на менюто
Merge Сливане
Merge colors into one row Сливане на цветовете в един ред
New Row Нов ред
Merge Graphic Styles Сливане на графични стилове
Merge Graphic Styles… Сливане на графични стилове…
Merge Layers Сливане на слоеве
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2893,6 +2893,7 @@ digits Malayalam Malajalam
Limit to Library: Ograniči na biblioteku:
Link harmony colors Poveži boje harmonije
Merge colors into one row Spoji boje u jedan red
New Row Novi red
No colors to recolor (preserved colors are left out). Nema boja za ponovno bojenje (sačuvane boje su izostavljene).
Preserve: Očuvaj:
Randomly change color order Nasumično promijeni redoslijed boja
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2595,6 +2595,7 @@ digits Malayalam Malaiàlam
Menu Dialog Diàleg de menú
Merge Fusiona
Merge colors into one row Fusiona els colors en una fila
New Row Fila nova
Merge Graphic Styles Fusiona els estils gràfics
Merge Graphic Styles… Fusiona els estils gràfics…
Merge Layers Fusiona les capes
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2971,6 +2971,7 @@ digits Malayalam Malajálamské
Limit to Library: Omezit na knihovnu:
Link harmony colors Propojit barvy harmonie
Merge colors into one row Sloučit barvy do jednoho řádku
New Row Nový řádek
No colors to recolor (preserved colors are left out). Žádné barvy k přebarvení (zachované barvy jsou vynechány).
Preserve: Zachovat:
Randomly change color order Náhodně změnit pořadí barev
Can't render this file because it is too large.
+1
View File
@@ -3363,6 +3363,7 @@ digits Malayalam Malayalam
Limit to Library: Begræns til bibliotek:
Link harmony colors Sammenkæd harmonifarver
Merge colors into one row Flet farver til én række
New Row Ny række
No colors to recolor (preserved colors are left out). Ingen farver at omfarve (bevarede farver er udeladt).
Preserve: Bevar:
Randomly change color order Ændr farverækkefølgen tilfældigt
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2057,6 +2057,7 @@ file-filter Templates Vorlagen
Menu Dialog Menüdialog
Merge Zusammenfügen
Merge colors into one row Farben in einer Zeile zusammenführen
New Row Neue Zeile
Merge Graphic Styles Grafikstile zusammenfügen
Merge Graphic Styles… Grafikstile zusammenfügen…
Merge Layers Ebenen zusammenfügen
Can't render this file because it is too large.
+1
View File
@@ -3034,6 +3034,7 @@ theme Appearance Εμφάνιση
Menu Dialog Παράθυρο μενού
Merge Συγχώνευση
Merge colors into one row Συγχώνευση χρωμάτων σε μία γραμμή
New Row Νέα γραμμή
Merge Graphic Styles Συγχώνευση στυλ γραφικών
Merge Graphic Styles… Συγχώνευση στυλ γραφικών…
Merge Selected Συγχώνευση επιλεγμένων
Can't render this file because it is too large.
+1
View File
@@ -2051,6 +2051,7 @@ font-technology Variable Variables
Menu Dialog Cuadro de diálogo de menú
Merge Combinar
Merge colors into one row Combinar colores en una fila
New Row Nueva fila
Merge Graphic Styles Combinar estilos gráficos
Merge Graphic Styles… Combinar estilos gráficos…
Merge Layers Combinar capas
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2915,6 +2915,7 @@ digits Malayalam Malajalam
Limit to Library: Rajoita kirjastoon:
Link harmony colors Linkitä harmoniavärit
Merge colors into one row Yhdistä värit yhdelle riville
New Row Uusi rivi
No colors to recolor (preserved colors are left out). Ei uudelleenväritettäviä värejä (säilytetyt värit jätetään pois).
Preserve: Säilytä:
Randomly change color order Muuta värijärjestystä satunnaisesti
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2045,6 +2045,7 @@ font-technology Variable Variables
Menu Dialog Boîte de dialogue du menu
Merge Fusionner
Merge colors into one row Fusionner les couleurs sur une ligne
New Row Nouvelle ligne
Merge Graphic Styles Fusionner les styles graphiques
Merge Graphic Styles… Fusionner les styles graphiques…
Merge Layers Fusionner les calques
Can't render this file because it is too large.
+1
View File
@@ -2142,6 +2142,7 @@
Merge Shared Edge શેર કરેલી કિનાર મર્જ કરો
Merge Swatches સ્વેચ મર્જ કરો
Merge colors into one row રંગોને એક હરોળમાં મર્જ કરો
New Row નવી હરોળ
Merge only with selection માત્ર પસંદગી સાથે મર્જ કરો
Merged PRs મર્જ થયેલા PR
Mesh Point Color મેશ બિંદુનો રંગ
Can't render this file because it contains an unexpected character in line 72 and column 5.
+1
View File
@@ -2853,6 +2853,7 @@ antiAlias Sharp शार्प
Menu Dialog मेन्यू डायलॉग
Merge मर्ज करें
Merge colors into one row रंगों को एक पंक्ति में मिलाएं
New Row नई पंक्ति
Merge Graphic Styles मर्ज ग्राफिक स्टाइल्स
Merge Graphic Styles… मर्ज ग्राफिक स्टाइल्स…
Merge Layers लेयर्स मर्ज करें
Can't render this file because it is too large.
+1
View File
@@ -2563,6 +2563,7 @@ file-filter Templates Sablonok
Menu Dialog Menü párbeszédablaka
Merge Összeolvasztás
Merge colors into one row Színek egyesítése egy sorba
New Row Új sor
Merge Graphic Styles Grafikai stílusok egyesítése
Merge Graphic Styles… Grafikai stílusok egyesítése…
Merge Layers Rétegek egyesítése
Can't render this file because it is too large.
+1
View File
@@ -2711,6 +2711,7 @@ digits Malayalam Malayalam
Limit to Library: Batasi ke pustaka:
Link harmony colors Tautkan warna harmoni
Merge colors into one row Gabungkan warna ke dalam satu baris
New Row Baris baru
No colors to recolor (preserved colors are left out). Tidak ada warna untuk diwarnai ulang (warna yang dipertahankan dikesampingkan).
Preserve: Pertahankan:
Randomly change color order Ubah urutan warna secara acak
Can't render this file because it contains an unexpected character in line 2459 and column 5.
+1
View File
@@ -2089,6 +2089,7 @@ file-filter Templates Modelli
Menu Dialog Finestra di dialogo menu
Merge Fondi
Merge colors into one row Unisci colori in una riga
New Row Nuova riga
Merge Graphic Styles Unisci stili grafici
Merge Graphic Styles… Unisci stili grafici…
Merge Layers Unisci livelli
Can't render this file because it is too large.
+1
View File
@@ -2322,6 +2322,7 @@ antiAlias Sharp シャープ
Merge Selected 選択レイヤーを結合
Merge Swatches スウォッチを統合
Merge colors into one row カラーを 1 行に統合
New Row 新規行
Mesh Tool メッシュツール
Message: メッセージ:
Metadata: メタデータ:
Can't render this file because it is too large.
+1
View File
@@ -2669,6 +2669,7 @@ digits Malayalam მალაიალამური
Limit to Library: ბიბლიოთეკით შეზღუდვა:
Link harmony colors ჰარმონიის ფერების დაკავშირება
Merge colors into one row ფერების ერთ სტრიქონში შერწყმა
New Row ახალი სტრიქონი
No colors to recolor (preserved colors are left out). ხელახლა შესაფერადებელი ფერები არ არის (შენარჩუნებული ფერები გამოტოვებულია).
Preserve: შენარჩუნება:
Randomly change color order ფერების რიგის შემთხვევითი შეცვლა
Can't render this file because it is too large.
+1
View File
@@ -2312,6 +2312,7 @@ antiAlias Sharp 날카롭게
Merge Selected 선택 항목 병합
Merge Swatches 견본 병합
Merge colors into one row 색상을 한 행으로 병합
New Row 새 행
Mesh Tool 메시 도구
Message: 메시지:
Metadata: 메타데이터:
Can't render this file because it is too large.
+1
View File
@@ -3271,6 +3271,7 @@ digits Malayalam Malayalam
Limit to Library: Beperken tot bibliotheek:
Link harmony colors Harmoniekleuren koppelen
Merge colors into one row Kleuren samenvoegen in één rij
New Row Nieuwe rij
No colors to recolor (preserved colors are left out). Geen kleuren om opnieuw te kleuren (behouden kleuren zijn weggelaten).
Preserve: Behouden:
Randomly change color order Kleurvolgorde willekeurig wijzigen
Can't render this file because it is too large.
+1
View File
@@ -2880,6 +2880,7 @@ digits Malayalam Malajalam
Limit to Library: Ogranicz do biblioteki:
Link harmony colors Połącz kolory harmonii
Merge colors into one row Scal kolory w jeden wiersz
New Row Nowy wiersz
No colors to recolor (preserved colors are left out). Brak kolorów do zmiany (zachowane kolory są pomijane).
Preserve: Zachowaj:
Randomly change color order Losowo zmień kolejność kolorów
Can't render this file because it is too large.
+1
View File
@@ -1727,6 +1727,7 @@ antiAlias Sharp Nítido
Merge Layers Mesclar Camadas
Merge Swatches Mesclar Amostras
Merge colors into one row Mesclar cores em uma linha
New Row Nova linha
Mesh Tool Ferramenta Malha
Message: Mensagem:
Metadata: Metadados:
Can't render this file because it is too large.
+1
View File
@@ -2078,6 +2078,7 @@ file-filter Templates Шаблоны
Menu Dialog Диалоговое окно меню
Merge Объединить
Merge colors into one row Объединить цвета в одну строку
New Row Новая строка
Merge Graphic Styles Объединить графические стили
Merge Graphic Styles… Объединить графические стили…
Merge Layers Объединить слои
Can't render this file because it is too large.
+1
View File
@@ -1992,6 +1992,7 @@ font-technology Variable Variabla
Menu Dialog Menydialog
Merge Sammanfoga
Merge colors into one row Sammanfoga färger till en rad
New Row Ny rad
Merge Graphic Styles Sammanfoga grafikstilar
Merge Graphic Styles… Sammanfoga grafikstilar…
Merge Layers Sammanfoga lager
Can't render this file because it is too large.
+1
View File
@@ -1883,6 +1883,7 @@ font-technology Variable Değişken
Menu Dialog Menü İletişim Kutusu
Merge Birleştir
Merge colors into one row Renkleri tek satırda birleştirme
New Row Yeni satır
Merge Graphic Styles Grafik Stillerini Birleştir
Merge Graphic Styles… Grafik Stillerini Birleştir…
Merge Layers Katmanları Birleştir
Can't render this file because it is too large.
+1
View File
@@ -2083,6 +2083,7 @@ file-filter Templates Шаблони
Menu Dialog Діалогове вікно меню
Merge Об’єднати
Merge colors into one row Об’єднати кольори в один рядок
New Row Новий рядок
Merge Graphic Styles Об’єднати графічні стилі
Merge Graphic Styles… Об’єднати графічні стилі…
Merge Layers Об’єднати шари
Can't render this file because it is too large.
+1
View File
@@ -2388,6 +2388,7 @@ digits Hindi Hindi
Menu Dialog Hộp thoại menu
Merge Hợp nhất
Merge colors into one row Hợp nhất màu vào một hàng
New Row Hàng mới
Merge Graphic Styles Hợp nhất kiểu đồ họa
Merge Graphic Styles… Hợp nhất kiểu đồ họa…
Merge Layers Hợp nhất lớp
Can't render this file because it contains an unexpected character in line 204 and column 5.
+1
View File
@@ -1960,6 +1960,7 @@ file-filter Templates 模板
Menu Dialog 菜单对话框
Merge 合并
Merge colors into one row 将颜色合并为一行
New Row 新建行
Merge Graphic Styles 合并图形样式
Merge Graphic Styles… 合并图形样式…
Merge Layers 合并图层
Can't render this file because it is too large.
+1
View File
@@ -3147,6 +3147,7 @@ file-filter Templates 範本
Menu Dialog 選單對話框
Merge 合併
Merge colors into one row 將顏色合併為一列
New Row 新增列
Merge Graphic Styles 合併繪圖樣式
Merge Graphic Styles… 合併繪圖樣式…
Merge Layers 合併圖層
Can't render this file because it is too large.
+5 -1
View File
@@ -4432,7 +4432,11 @@ stay tints), `scaleTints` (every colour becomes a tint as light, relative to the
darker than the row's average become tints and shades) or `hueShift` (the most saturated colour takes the new colour,
the others turn by the same hue). `limitTo` snaps new colours to a swatch library's nearest colour;
`recolor.randomize {map, order?, saturationBrightness?, seed?}` shuffles or varies new colours; a row with
`exclude: true` keeps its colours. With `limitTo`, a row's `tint` (a percentage, default 100) gives the art
`exclude: true` keeps its colours. A row's `from` may be an empty list: the row recolors nothing, adds no library swatch with
`limitTo` and gives `group` no color, and `recolor.randomize` and `recolor.weigh` keep it. A row without `from`, or with
a current color that is not a color, is refused. With art selected, the Recolor Artwork dialog's New Row button (beside
Merge) adds such a row above the first selected row, or at the top, with that row's new color, and selects it; Shift-click another row and Merge to move its colors into it. An
empty row does not count toward Colors. With `limitTo`, a row's `tint` (a percentage, default 100) gives the art
linked to the global or spot swatch its new color is that share of its tint; `tint` must be a number. `recolor.randomize`
moves a row's `tint` with its new color and removes it from a color it changes, and moves a row's `model` with its new color. `swatch.editGroup {group, colors, rename?}` rewrites a colour group in one undo
step (art linked to its global swatches follows), and `recolor.apply` with `group` recolours the art and rewrites the
+1
View File
@@ -1601,6 +1601,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Document Color Mode for effects and graphs:** File › Document Color Mode and `document.open {colorMode}` convert the colors of effects (Drop Shadow, Inner Glow, Outer Glow and the other effects with a color, on objects, their fills and strokes and in graphic styles) and the series paints and label styles that a graph keeps, so a graph drawn again from new data keeps the converted colors; File › Document Color Mode also converts the fill and stroke that new art takes. Undo restores the original new-art fill and stroke and Redo applies the conversion again.
- **Recolor Artwork layout (#3416):** the Edit chip list uses the modal scroll helper and its actual remaining height; the tab's minimum-height space scrolls with the chips. `tests_recolor` covers 5/20/200/400 colors, window resizing, 100/125/150/200% UI scaling, scrolling to and editing the final chip, Preview, Cancel, Confirm and Undo, alongside the existing #1733 drag regressions. Scores and estimates unchanged.
- **Recolor Artwork Edit displays:** buttons beside the Edit tab's wheel show the colors on a smooth color wheel, a segmented color wheel or as color bars ([Edit colors using Recolor Artwork](https://helpx.adobe.com/illustrator/desktop/manage-colors/modify-colors/edit-colors-using-edit-colors-dialog.html)). The segmented wheel draws 24 hues by 5 saturations as solid patches, and its markers move freely as on the smooth wheel. The bars show one solid bar per recolored color in row order: a click selects a bar for the color sliders, and a drag moves its row without changing a color ([Advanced recolor options overview](https://helpx.adobe.com/illustrator/desktop/manage-colors/learn-color-basics/edit-colors-dialog-overview.html)). The dialog opens on the smooth wheel; `display` (`smooth`, `segmented`, `bars`) is the dialog field. `tests_recolor` covers switching, the dialog height, segmented dragging and Brightness, and bar selection, editing, reordering, scrolling, Preview, Cancel and OK. Scores and estimates unchanged.
- **Recolor Artwork New Row:** the Assign tab's New Row button adds a row with no current colors, so current colors can be moved into it and take its new color ([Advanced recolor options overview](https://helpx.adobe.com/illustrator/desktop/manage-colors/learn-color-basics/edit-colors-dialog-overview.html), [Assign new colors to selected artworks](https://helpx.adobe.com/illustrator/desktop/manage-colors/modify-colors/assign-new-colors-to-selected-artwork.html)). VectorCraft's own rules: the row goes above the first selected row (at the top when none is selected) with that row's new color and becomes the selection, and Merge with it as the first selected row moves the other rows' colors into it. The button needs selected art. An empty row recolors nothing, does not count toward Colors, adds no library swatch and gives a rewritten color group no color. Tests: `a_new_row_goes_above_the_selection_and_takes_merged_colors`, `an_empty_row_gives_the_group_no_color` and `colors_merged_into_a_new_row_take_its_new_color` in `crates/ui-egui/src/dialogs/recolor.rs`, `an_empty_row_recolors_nothing` and `an_empty_row_adds_no_swatch_and_no_group_color` in `crates/engine/src/tests/recolor.rs`.
- **Edit Colors parameters:** `edit.colors.saturate` needs a number `intensity`, the value from -100% to 100% that Saturate takes ([Adjusting colors](https://web.archive.org/web/20230128232631/https://helpx.adobe.com/illustrator/using/adjusting-colors.html)), and answers "missing number `intensity`" without one; a number outside the range counts as its nearest end. A value that is not a number, such as `"40"`, is refused with `` `intensity` must be a number, not "40" ``, as in every command that needs a number (`tests::params::a_malformed_required_number_is_not_reported_as_missing`). `edit.colors.adjustBalance` refuses a channel and `color.harmony` refuses `steps` or `amount` that is not a number; both read `null` as a value left out. `tests::editcolors` and `tests::colorguide` cover them.
- **Convert to Indexed Color:** Edit › Edit Colors › Convert to Indexed Color… (`edit.colors.toIndexed`) stores the embedded images in the selection as indexed-color PNGs of at most 256 colors. The palette is chosen as for a PNG-8 export, as evenly spaced levels of red, green and blue, or from the document's swatches, with diffusion, pattern or noise dithering; pixels under half opacity become transparent. The dialog previews live, and OK is one undo step. This is VectorCraft's own command and counts toward no score. `tests::editcolors` and `encode::tests_palette` cover it. Scores and estimates unchanged.
- **Working RGB numeric controls:** the Color panel and Color Picker read RGB, HSB, hex and Web Safe RGB values in the active working RGB space. Editing one RGB channel preserves the other native channels, including colors outside display sRGB. Regressions cover sRGB, Display P3, Wide Gamut RGB and ProPhoto RGB, picker initialization, native numeric round trips, and a slider click with one Undo step. Scores and estimates unchanged.