Fix #4927: Save a Timestamped Copy

This commit is contained in:
bflatastic
2026-11-01 02:44:16 +07:00
parent 9e3f8dccec
commit 916b60898a
40 changed files with 486 additions and 4 deletions
+3 -1
View File
@@ -9,7 +9,7 @@
**Stage: alpha** · next: beta, ~13 points (ready for real work ~62% → ~75%, and reliable `.ai` exchange) and ~170–270 h away
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Progress log row for Divide into Dashes in the Path Precision Tools add-on; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Progress log row for the Document Tools add-on's Save a Timestamped Copy; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adobe Illustrator workflow. It runs
on macOS, Windows, Linux, FreeBSD and the web (WASM), and agents can drive all of it over MCP, the CLI and a JSON
@@ -129,6 +129,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap
## Progress log
- 2026-11-01: Document Tools, a built-in add-on (Object › Plug-ins › Document Tools), starts with Save a Timestamped Copy: a copy of the document named `<name>_YYYY-MM-DD_HH-MM` (UTC; `-2`, `-3`… when that name is taken) goes next to it, or into a folder picked for a document never saved, written as File › Save a Copy writes it, so the document keeps its path, title and modified state. Off on the web, which has no clock. A VectorCraft addition: headline numbers unchanged.
- 2026-11-01: Divide into Dashes joins the Path Precision Tools add-on (Object › Plug-ins › Path Precision Tools): the selected paths, or only their direct-selected segments, are cut into separate open paths of a dash length measured along the curve, with an optional gap (left empty, or kept as pieces), each keeping the path's appearance; Fit to Corners and Ends stretches or squeezes the lengths so every run starts and ends on a dash. A dialog with Preview, one undo step, at most 100000 pieces. A VectorCraft addition: headline numbers unchanged.
- 2026-11-01: Group Overlapping Objects joins the Arrange Tools add-on (Object › Plug-ins › Arrange Tools): the selected objects whose visual bounds overlap or touch, chains included, go into one group each, within their own layer or group, at the stacking place of their frontmost member; objects that overlap nothing stay as they are. One undo step, a sweep along x so thousands of loose pieces group quickly (at most 20000 objects). A VectorCraft addition: headline numbers unchanged.
- 2026-10-31: Export for Screens works as the reference app's dialog: Artboards and Assets tabs with a grid or list of thumbnails, Clear Selection and Prefix, Create Sub-folders by Scale or Format (on by default), Export PDFs As Single File (the chosen artboards as one PDF's pages, `pdfs: "single"`) or Multiple Files, iOS and Android rows, format presets in a ☰ menu, and a count of the files the export writes (#4895). About 55 community PRs and fixes landed the same day: Find Font, Replace Font, Outline Stroke and Expand leave hidden and locked art alone (with a warning); DXF export writes LINE and ELLIPSE entities and outlines only the strokes a CAD line can't draw; Blob Brush Angle and Roundness; Shift-constrained Perspective Selection; Grid Repeat Options Preview; a gradient mesh from a file budgeted at about a million quads; width-profile strokes keep their shape zoomed out; `.ai`/EPS import fidelity (area type, envelopes, transparency in dictionaries, character styles); PDF import finds hyphenated and CMap-suffixed font names; Windows renders follow the process's core affinity; Enter or Escape in a floating panel's field no longer reaches the canvas (#4803); Properties shows Blend Options for any selected blend (#4817); Artwork Cleanup finds doubled type (#4835) and joins stacked lines into one object (Join Point Text, #4826). Headline numbers unchanged.
@@ -206,6 +207,7 @@ The full earlier record of what landed is kept in [docs/roadmap.md](docs/roadmap
| Date | Change | Summary |
|---|---|---|
| 2026-11-01 | minor | Progress log row for the Document Tools add-on's Save a Timestamped Copy (#4926); headline numbers unchanged. |
| 2026-11-01 | minor | Progress log row for Divide into Dashes (Path Precision Tools add-on, #4950); headline numbers unchanged. |
| 2026-11-01 | minor | Progress log row for Group Overlapping Objects (Arrange Tools add-on, #4928); headline numbers unchanged. |
| 2026-10-31 | minor | Progress log row for Export for Screens laid out as the reference app's (#4895) and the 2026-10-31 community landings; headline numbers unchanged. |
+2
View File
@@ -4,6 +4,7 @@
mod arrange;
mod cleanup;
mod color;
mod document;
mod pathtools;
mod pen;
@@ -14,6 +15,7 @@ pub fn specs() -> Vec<CommandSpec> {
let mut v = arrange::specs();
v.extend(cleanup::specs());
v.extend(color::specs());
v.extend(document::specs());
v.extend(pathtools::specs());
v.extend(pen::specs());
v
+73
View File
@@ -0,0 +1,73 @@
//! Document Tools (`addon.document.*`), a built-in add-on: Save a Timestamped Copy writes a copy of
//! the document named with the date and time through File › Save a Copy's save. Its submenu
//! (Object › Plug-ins › Document Tools) is the UI's.
#[cfg(test)]
mod tests;
use serde_json::{Value, json};
use super::super::fileio::{self, SaveMode};
use super::super::{CommandSpec, bad, clock_date, cmd, date_param, has_doc, str_param};
use crate::{Result, Session};
/// The most numbers (`-2`, `-3`…) tried after a timestamped name that is taken.
const MAX_NUMBER: u32 = 999;
pub fn specs() -> Vec<CommandSpec> {
vec![cmd!(
"addon.document.saveTimestampedCopy",
"Save a Timestamped Copy",
["Object", "Plug-ins", "Document Tools"],
None,
"{folder? (default: the document's own folder; needed for a document never saved), format?: vectorcraft|pdf|svg|svgz|ai|eps (default: the document's own, else vectorcraft), time?: Unix seconds (default: now)} write a copy of the document named <name>_YYYY-MM-DD_HH-MM.<ext> (the date and time in UTC; -2, -3… added when that name is taken) as File › Save a Copy writes it: the document keeps its path, title and modified state, and no undo step is made. Off where there is no clock (the web) → {path, format, bytes, warnings}",
has_clock,
save_timestamped_copy
)]
}
/// A document is open and the time can be told.
fn has_clock(s: &Session) -> std::result::Result<(), String> {
has_doc(s)?;
vectorcraft_doc::metadata::output_time().map(|_| ()).ok_or_else(|| "there is no clock here".into())
}
/// `<base>.<ext>`, then `<base>-2.<ext>`… in `folder`: the first that `taken` doesn't hold.
fn free_name(folder: &str, base: &str, ext: &str, taken: impl Fn(&str) -> bool) -> Option<String> {
(1..=MAX_NUMBER)
.map(|i| {
let name = if i == 1 { format!("{base}.{ext}") } else { format!("{base}-{i}.{ext}") };
std::path::Path::new(folder).join(name).to_string_lossy().into_owned()
})
.find(|path| !taken(path))
}
fn save_timestamped_copy(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "addon.document.saveTimestampedCopy";
let st = s.doc()?;
let format = match str_param(p, "format") {
Some(f) => fileio::save_format(Some(f), None),
// A document opened from a format Save doesn't write (a picture, a drawing) copies as native.
None => fileio::save_format(Some(st.format), None).or_else(|_| fileio::save_format(None, None)),
}
.map_err(|e| bad(C, e))?;
let folder = match str_param(p, "folder").filter(|f| !f.trim().is_empty()) {
Some(f) => f.to_string(),
None => st
.path
.as_deref()
.and_then(|path| std::path::Path::new(path).parent())
.map(|f| f.to_string_lossy().into_owned())
.filter(|f| !f.is_empty())
.ok_or_else(|| bad(C, "the document has never been saved: give `folder`"))?,
};
let stem = fileio::file_stem(st.path.as_deref().unwrap_or(&st.doc.title));
let given = date_param(p, "time", C)?;
let t = clock_date(s, "time", given).ok_or_else(|| bad(C, "there is no clock here: give `time` (Unix seconds)"))?;
let [y, mo, d, h, mi, _] = vectorcraft_doc::metadata::civil(t);
let base = format!("{stem}_{y:04}-{mo:02}-{d:02}_{h:02}-{mi:02}");
// Only whether something is there: the write itself goes through the automation roots.
let path = free_name(&folder, &base, format.extensions[0], |p| std::path::Path::new(p).exists())
.ok_or_else(|| bad(C, "every numbered name for the copy is taken"))?;
fileio::save_document(s, SaveMode::Copy, &json!({ "path": path, "format": format.id }))
}
@@ -0,0 +1,98 @@
//! Document Tools' commands: the copy's name and place, the document left as it was, and bad
//! params refused.
use std::path::{Path, PathBuf};
use serde_json::{Value, json};
use crate::Session;
/// 2026-10-05 14:03:00 UTC.
const TIME: i64 = 1_791_208_980;
/// A new, empty folder of its own for test `name`.
fn folder(name: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!("vc-addon-document-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn session() -> Session {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 100, "height": 50})).unwrap();
s
}
fn stamp(s: &mut Session, p: Value) -> crate::Result<Value> {
s.execute("addon.document.saveTimestampedCopy", &p)
}
#[test]
fn a_timestamped_copy_goes_next_to_the_document_which_stays_as_it_was() {
let dir = folder("next-to");
let mut s = session();
let own = dir.join("poster.vectorcraft");
s.execute("document.save", &json!({"path": own})).unwrap();
s.execute("shape.ellipse", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap();
let (steps, title) = (s.doc().unwrap().history.undo.len(), s.doc().unwrap().doc.title.clone());
let r = stamp(&mut s, json!({"time": TIME})).unwrap();
let copy = dir.join("poster_2026-10-05_14-03.vectorcraft");
assert_eq!(Path::new(r["path"].as_str().unwrap()), copy);
assert!(copy.is_file());
let st = s.doc().unwrap();
assert_eq!(st.path.as_deref().map(Path::new), Some(own.as_path()), "the document keeps its path");
assert_eq!((st.history.undo.len(), &st.doc.title, st.is_dirty()), (steps, &title, true), "no step; still modified");
// The copy holds the document as it is now: the ellipse too.
let mut other = Session::new();
other.execute("document.open", &json!({"path": copy})).unwrap();
assert_eq!(other.doc().unwrap().doc.layers[0].children().unwrap().len(), 2);
// The same minute again: numbered.
let r = stamp(&mut s, json!({"time": TIME})).unwrap();
assert!(r["path"].as_str().unwrap().ends_with("poster_2026-10-05_14-03-2.vectorcraft"), "{r}");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn an_unsaved_document_needs_a_folder_and_other_formats_take_their_extension() {
let dir = folder("unsaved");
let mut s = session();
let before = s.doc().unwrap().doc.clone();
assert!(stamp(&mut s, json!({"time": TIME})).is_err(), "no folder to put it in");
let r = stamp(&mut s, json!({"time": TIME, "folder": dir, "format": "svg"})).unwrap();
let name = Path::new(r["path"].as_str().unwrap()).file_name().unwrap().to_string_lossy().into_owned();
assert!(name.ends_with("_2026-10-05_14-03.svg") && dir.join(&name).is_file(), "{name}");
let st = s.doc().unwrap();
assert!(st.path.is_none() && *st.doc == *before, "the document is unchanged");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn bad_params_are_refused_and_writes_stay_inside_the_automation_roots() {
let dir = folder("bad");
let mut s = session();
for p in [
json!({"folder": dir, "format": "png"}),
json!({"folder": dir, "format": "dxf"}),
json!({"folder": dir, "time": "now"}),
json!({"folder": dir, "time": 1.5}),
json!({"folder": dir.join("missing").join("deeper"), "time": TIME}),
] {
assert!(stamp(&mut s, p.clone()).is_err(), "{p}");
}
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 0, "nothing written");
let allowed = dir.join("allowed");
std::fs::create_dir_all(&allowed).unwrap();
let roots = crate::file_access::AutomationRoots::new(&[&dir], &[&allowed]).unwrap();
let outside = crate::file_access::confine(roots.as_ref(), || stamp(&mut s, json!({"folder": dir, "time": TIME})));
assert!(outside.is_err(), "outside the write root");
let inside = crate::file_access::confine(roots.as_ref(), || stamp(&mut s, json!({"folder": allowed, "time": TIME})));
assert!(inside.is_ok(), "{inside:?}");
assert!(!enabled_without_document(), "off with no document");
let _ = std::fs::remove_dir_all(&dir);
}
fn enabled_without_document() -> bool {
Session::new().commands().into_iter().find(|c| c.id == "addon.document.saveTimestampedCopy").unwrap().enabled
}
+1
View File
@@ -75,6 +75,7 @@ pub(crate) use native::preview_png;
pub use native::with_compression_pref;
pub use pdfimport::{LoadOptions, page_document};
pub(crate) use save::job_for;
pub(crate) use save::save as save_document;
pub use save::{
SAVE_FORMATS, SaveJob, SaveMode, SavePlan, StreamWrite, TEMPLATE_FORMATS, dialog_folder, export_folder, reads_option as save_reads_option,
remembered_save_options, save_filters, save_format, save_job, save_plan, save_with, stamp_save_dates, template_filters, templates_dialog_folder,
+1 -1
View File
@@ -897,7 +897,7 @@ pub fn save_with(s: &mut Session, plan: SavePlan, write: impl FnMut(&str, &[u8])
}
/// A save command: plan, then write with the file system (a native file as it is encoded).
fn save(s: &mut Session, mode: SaveMode, p: &Value) -> Result<Value> {
pub(crate) fn save(s: &mut Session, mode: SaveMode, p: &Value) -> Result<Value> {
let plan = save_plan(s, mode, p)?;
let job = save_job(s, plan)?;
let uid = s.doc()?.uid;
+3
View File
@@ -592,6 +592,9 @@ fn structured_junk() {
("addon.pathtools.divideDashes", json!({"dash": 1e-308, "gap": -1e308, "fit": "x"})),
("addon.pathtools.divideDashes", json!({"dash": 0.01, "gap": 1e6, "fit": true, "removeGaps": false})),
("addon.pathtools.divideDashes", json!({"dash": f64::MAX, "gap": [0], "removeGaps": null})),
// Refused before anything is written: no format of that name, a time that is no time.
("addon.document.saveTimestampedCopy", json!({"format": "png", "folder": 7})),
("addon.document.saveTimestampedCopy", json!({"format": "svg", "time": [1e308], "folder": null})),
// Smart Pen add-on.
("addon.pen.append", json!({"id": 2, "x": 1e308, "y": 0, "in": [1e308, -1e308]})),
("addon.pen.append", json!({"id": 2, "subpath": u64::MAX, "x": 0, "y": 0, "segment": "curved"})),
+2
View File
@@ -4,6 +4,7 @@
pub mod arrange;
pub mod cleanup;
pub mod color;
pub mod document;
pub mod pathtools;
pub mod pen;
@@ -15,6 +16,7 @@ pub fn plugin_menu() -> Vec<Item> {
Item::Sub("Arrange Tools", arrange::menu()),
Item::Sub("Artwork Cleanup", cleanup::menu()),
Item::Sub("Color Effects", color::menu()),
Item::Sub("Document Tools", document::menu()),
Item::Sub("Path Precision Tools", pathtools::menu()),
Item::Sub("Smart Pen", pen::menu()),
Item::Sep,
+30
View File
@@ -0,0 +1,30 @@
//! Document Tools in the interface: its submenu (Object › Plug-ins › Document Tools). Save a
//! Timestamped Copy of a document never saved asks for the folder to put it in ([`save_copy`]).
use serde_json::{Value, json};
use crate::VectorcraftApp;
use crate::menus::Item;
/// Save a Timestamped Copy.
pub const SAVE_COPY: &str = "addon.document.saveTimestampedCopy";
/// Object › Plug-ins › Document Tools.
pub fn menu() -> Vec<Item> {
vec![Item::Cmd("Save a Timestamped Copy", SAVE_COPY, json!({}))]
}
/// Save a Timestamped Copy with params `p`: a document never saved, given no `folder`, goes in the
/// folder picked. The status bar says where the copy went.
pub(crate) fn save_copy(app: &mut VectorcraftApp, p: &Value) -> Result<Value, String> {
let mut p = if p.is_object() { p.clone() } else { json!({}) };
if p.get("folder").is_none() && app.session.active().is_some_and(|st| st.path.is_none()) {
p["folder"] = json!(crate::picks::folder(app).ok_or("cancelled")?);
}
let r = app.session.execute(SAVE_COPY, &p).map_err(|e| e.to_string())?;
crate::io::report_saved(app, r["path"].as_str().unwrap_or_default(), &r);
Ok(r)
}
#[cfg(test)]
mod tests;
@@ -0,0 +1,63 @@
use std::path::PathBuf;
use serde_json::json;
use vectorcraft_engine::Session;
use super::SAVE_COPY;
use crate::{VectorcraftApp, menus};
/// A new, empty folder of its own for test `name`.
fn folder(name: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!("vc-ui-document-tools-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn app() -> VectorcraftApp {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
app
}
/// Click the Document Tools item `label`.
fn click(app: &mut VectorcraftApp, label: &str) {
let entries = menus::menu_entries(app);
let item = entries.iter().find(|e| e.path == ["Object", "Plug-ins", "Document Tools"] && e.label == label).expect(label);
let (id, p) = menus::click_target(&item.label, item.command.as_deref().unwrap(), &item.params);
menus::invoke(app, &id, p);
}
fn files(dir: &PathBuf) -> Vec<String> {
std::fs::read_dir(dir).unwrap().map(|e| e.unwrap().file_name().to_string_lossy().into_owned()).collect()
}
#[test]
fn save_a_timestamped_copy_writes_beside_the_document() {
let dir = folder("beside");
let mut app = app();
app.run("document.save", json!({"path": dir.join("card.vectorcraft")})).unwrap();
assert_eq!(menus::menu_entries(&app).iter().find(|e| e.label == "Save a Timestamped Copy").and_then(|e| e.command.as_deref()), Some(SAVE_COPY));
click(&mut app, "Save a Timestamped Copy");
let names = files(&dir);
assert_eq!(names.len(), 2, "{names:?}");
assert!(names.iter().any(|n| n.starts_with("card_20") && n.ends_with(".vectorcraft")), "{names:?}");
assert!(app.ui.status.starts_with("Saved "), "{}", app.ui.status);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn an_unsaved_document_asks_for_a_folder() {
let dir = folder("picked");
let mut app = app();
let picked = dir.to_string_lossy().into_owned();
app.services.pick_folder = Some(Box::new(move || Some(picked.clone())));
click(&mut app, "Save a Timestamped Copy");
assert_eq!(files(&dir).len(), 1);
assert!(app.session.doc().unwrap().path.is_none(), "the document stays unsaved");
// No folder picked: nothing written.
app.services.pick_folder = Some(Box::new(|| None));
assert_eq!(app.run(SAVE_COPY, json!({})).unwrap_err(), "cancelled");
assert_eq!(files(&dir).len(), 1);
let _ = std::fs::remove_dir_all(&dir);
}
+7
View File
@@ -5938,6 +5938,13 @@ breakText Glyphs Глифи
Remove Gaps Премахване на разстоянията
@msg that makes too many pieces (at most 100000): make the dashes longer така се получават твърде много части (най-много 100000): удължете тиретата
@msg select paths, or segments, to divide изберете пътища или сегменти за разделяне
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Инструменти за документи
Save a Timestamped Copy Запазване на копие с дата и час
@msg there is no clock here тук няма часовник
@msg there is no clock here: give `time` (Unix seconds) тук няма часовник: задайте `time` (Unix секунди)
@msg the document has never been saved: give `folder` документът никога не е запазван: задайте `folder`
@msg every numbered name for the copy is taken всички номерирани имена за копието са заети
Marathi Маратхи
Gujarati Гуджарати
Bengali Бенгалски
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5972,6 +5972,13 @@ shape Open Otvoren
Remove Gaps Ukloni razmake
@msg that makes too many pieces (at most 100000): make the dashes longer to daje previše dijelova (najviše 100000): produžite crtice
@msg select paths, or segments, to divide odaberite putanje ili segmente za dijeljenje
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Alati za dokument
Save a Timestamped Copy Sačuvaj kopiju s datumom i vremenom
@msg there is no clock here ovdje nema sata
@msg there is no clock here: give `time` (Unix seconds) ovdje nema sata: navedite `time` (Unix sekunde)
@msg the document has never been saved: give `folder` dokument nikad nije sačuvan: navedite `folder`
@msg every numbered name for the copy is taken sva numerisana imena za kopiju su zauzeta
Marathi Marathi
Gujarati Gudžarati
Bengali Bengalski
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6045,6 +6045,13 @@ breakText Glyphs Glifs
Remove Gaps Elimina les separacions
@msg that makes too many pieces (at most 100000): make the dashes longer això fa massa trossos (com a màxim 100000): allargueu els guions
@msg select paths, or segments, to divide seleccioneu camins o segments per dividir
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Eines del document
Save a Timestamped Copy Desa una còpia amb data i hora
@msg there is no clock here aquí no hi ha rellotge
@msg there is no clock here: give `time` (Unix seconds) aquí no hi ha rellotge: indiqueu `time` (segons Unix)
@msg the document has never been saved: give `folder` el document no s’ha desat mai: indiqueu `folder`
@msg every numbered name for the copy is taken tots els noms numerats per a la còpia estan ocupats
Marathi Marathi
Gujarati Gujarati
Bengali Bengalí
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6055,6 +6055,13 @@ breakText Glyphs Glyfy
Remove Gaps Odstranit mezery
@msg that makes too many pieces (at most 100000): make the dashes longer tím vznikne příliš mnoho dílů (nejvýše 100000): prodlužte čárky
@msg select paths, or segments, to divide vyberte cesty nebo segmenty k rozdělení
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Nástroje dokumentu
Save a Timestamped Copy Uložit kopii s datem a časem
@msg there is no clock here nejsou k dispozici hodiny
@msg there is no clock here: give `time` (Unix seconds) nejsou k dispozici hodiny: zadejte `time` (sekundy Unix)
@msg the document has never been saved: give `folder` dokument nebyl nikdy uložen: zadejte `folder`
@msg every numbered name for the copy is taken všechny číslované názvy kopie jsou obsazené
Marathi Maráthština
Gujarati Gudžarátština
Bengali Bengálština
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5952,6 +5952,13 @@ breakText Glyphs Glyffer
Remove Gaps Fjern mellemrum
@msg that makes too many pieces (at most 100000): make the dashes longer det giver for mange stykker (højst 100000): gør stregerne længere
@msg select paths, or segments, to divide vælg stier eller segmenter, der skal opdeles
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Dokumentværktøjer
Save a Timestamped Copy Gem en kopi med dato og klokkeslæt
@msg there is no clock here der er intet ur her
@msg there is no clock here: give `time` (Unix seconds) der er intet ur her: angiv `time` (Unix-sekunder)
@msg the document has never been saved: give `folder` dokumentet er aldrig blevet gemt: angiv `folder`
@msg every numbered name for the copy is taken alle nummererede navne til kopien er optaget
Marathi Marathi
Gujarati Gujarati
Bengali Bengali
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5640,6 +5640,13 @@ breakText Glyphs Glyphen
Remove Gaps Lücken entfernen
@msg that makes too many pieces (at most 100000): make the dashes longer das ergibt zu viele Teile (höchstens 100000): machen Sie die Striche länger
@msg select paths, or segments, to divide wählen Sie Pfade oder Segmente zum Aufteilen aus
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Dokumentwerkzeuge
Save a Timestamped Copy Kopie mit Zeitstempel speichern
@msg there is no clock here hier gibt es keine Uhr
@msg there is no clock here: give `time` (Unix seconds) hier gibt es keine Uhr: geben Sie `time` an (Unix-Sekunden)
@msg the document has never been saved: give `folder` das Dokument wurde nie gespeichert: geben Sie `folder` an
@msg every numbered name for the copy is taken alle nummerierten Namen für die Kopie sind vergeben
Marathi Marathi
Gujarati Gujarati
Bengali Bengalisch
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6007,6 +6007,13 @@ breakText Glyphs Γλύφοι
Remove Gaps Αφαίρεση κενών
@msg that makes too many pieces (at most 100000): make the dashes longer αυτό δίνει πάρα πολλά κομμάτια (το πολύ 100000): κάντε τις παύλες μεγαλύτερες
@msg select paths, or segments, to divide επιλέξτε διαδρομές ή τμήματα για διαίρεση
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Εργαλεία εγγράφου
Save a Timestamped Copy Αποθήκευση αντιγράφου με ημερομηνία και ώρα
@msg there is no clock here δεν υπάρχει ρολόι εδώ
@msg there is no clock here: give `time` (Unix seconds) δεν υπάρχει ρολόι εδώ: δώστε `time` (δευτερόλεπτα Unix)
@msg the document has never been saved: give `folder` το έγγραφο δεν έχει αποθηκευτεί ποτέ: δώστε `folder`
@msg every numbered name for the copy is taken όλα τα αριθμημένα ονόματα για το αντίγραφο είναι πιασμένα
Marathi Μαραθικά
Gujarati Γκουγιαράτι
Bengali Βεγγαλικά
Can't render this file because it contains an unexpected character in line 164 and column 5.
+7
View File
@@ -5997,6 +5997,13 @@ breakText Glyphs Glifos
Remove Gaps Quitar separaciones
@msg that makes too many pieces (at most 100000): make the dashes longer eso da demasiados trozos (como máximo 100000): alargue los guiones
@msg select paths, or segments, to divide seleccione trazados o segmentos para dividir
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Herramientas de documento
Save a Timestamped Copy Guardar una copia con fecha y hora
@msg there is no clock here aquí no hay reloj
@msg there is no clock here: give `time` (Unix seconds) aquí no hay reloj: indique `time` (segundos Unix)
@msg the document has never been saved: give `folder` el documento nunca se ha guardado: indique `folder`
@msg every numbered name for the copy is taken todos los nombres numerados para la copia están ocupados
Marathi Maratí
Gujarati Guyaratí
Bengali Bengalí
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6085,6 +6085,13 @@ breakText Glyphs Merkit
Remove Gaps Poista välit
@msg that makes too many pieces (at most 100000): make the dashes longer siitä tulee liian monta osaa (enintään 100000): pidennä viivoja
@msg select paths, or segments, to divide valitse jaettavat polut tai segmentit
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Asiakirjatyökalut
Save a Timestamped Copy Tallenna kopio aikaleimalla
@msg there is no clock here täällä ei ole kelloa
@msg there is no clock here: give `time` (Unix seconds) täällä ei ole kelloa: anna `time` (Unix-sekunteina)
@msg the document has never been saved: give `folder` asiakirjaa ei ole koskaan tallennettu: anna `folder`
@msg every numbered name for the copy is taken kaikki kopion numeroidut nimet ovat varattuja
Marathi Marathi
Gujarati Gudžarati
Bengali Bengali
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6052,6 +6052,13 @@ breakText Glyphs Glyphes
Remove Gaps Supprimer les écarts
@msg that makes too many pieces (at most 100000): make the dashes longer cela donne trop de morceaux (100000 au plus) : allongez les tirets
@msg select paths, or segments, to divide sélectionnez des tracés ou des segments à diviser
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Outils de document
Save a Timestamped Copy Enregistrer une copie horodatée
@msg there is no clock here il n’y a pas d’horloge ici
@msg there is no clock here: give `time` (Unix seconds) il n’y a pas d’horloge ici : indiquez `time` (secondes Unix)
@msg the document has never been saved: give `folder` le document n’a jamais été enregistré : indiquez `folder`
@msg every numbered name for the copy is taken tous les noms numérotés pour la copie sont pris
Marathi Marathi
Gujarati Goudjarati
Bengali Bengali
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6041,6 +6041,13 @@ breakText Glyphs Glifák
Remove Gaps Hézagok eltávolítása
@msg that makes too many pieces (at most 100000): make the dashes longer ez túl sok darabot adna (legfeljebb 100000): hosszabbítsa meg a szaggatásokat
@msg select paths, or segments, to divide jelöljön ki felosztandó útvonalakat vagy szakaszokat
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Dokumentumeszközök
Save a Timestamped Copy Másolat mentése időbélyeggel
@msg there is no clock here itt nincs óra
@msg there is no clock here: give `time` (Unix seconds) itt nincs óra: adja meg a `time` értéket (Unix-másodperc)
@msg the document has never been saved: give `folder` a dokumentum még nem volt mentve: adja meg a `folder` értéket
@msg every numbered name for the copy is taken a másolat összes számozott neve foglalt
Marathi Maráthi
Gujarati Gudzsaráti
Bengali Bengáli
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5952,6 +5952,13 @@ breakText Glyphs Glif
Remove Gaps Hapus celah
@msg that makes too many pieces (at most 100000): make the dashes longer itu menghasilkan terlalu banyak potongan (paling banyak 100000): perpanjang garisnya
@msg select paths, or segments, to divide pilih jalur atau segmen yang akan dibagi
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Alat dokumen
Save a Timestamped Copy Simpan salinan bercap waktu
@msg there is no clock here tidak ada jam di sini
@msg there is no clock here: give `time` (Unix seconds) tidak ada jam di sini: berikan `time` (detik Unix)
@msg the document has never been saved: give `folder` dokumen belum pernah disimpan: berikan `folder`
@msg every numbered name for the copy is taken semua nama bernomor untuk salinan sudah dipakai
Marathi Marathi
Gujarati Gujarati
Bengali Bengali
Can't render this file because it contains an unexpected character in line 2175 and column 5.
+7
View File
@@ -5674,6 +5674,13 @@ breakText Glyphs Glifi
Remove Gaps Rimuovi le distanze
@msg that makes too many pieces (at most 100000): make the dashes longer così si ottengono troppi pezzi (al massimo 100000): allunga i trattini
@msg select paths, or segments, to divide seleziona tracciati o segmenti da dividere
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Strumenti documento
Save a Timestamped Copy Salva una copia con data e ora
@msg there is no clock here qui non c’è un orologio
@msg there is no clock here: give `time` (Unix seconds) qui non c’è un orologio: indica `time` (secondi Unix)
@msg the document has never been saved: give `folder` il documento non è mai stato salvato: indica `folder`
@msg every numbered name for the copy is taken tutti i nomi numerati per la copia sono già usati
Marathi Marathi
Gujarati Gujarati
Bengali Bengalese
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5741,6 +5741,13 @@ breakText Glyphs 字形
Remove Gaps 間隔を削除
@msg that makes too many pieces (at most 100000): make the dashes longer 分割数が多すぎます(最大 100000):ダッシュを長くしてください
@msg select paths, or segments, to divide 分割するパスまたはセグメントを選択してください
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools ドキュメントツール
Save a Timestamped Copy 日時付きのコピーを保存
@msg there is no clock here ここには時計がありません
@msg there is no clock here: give `time` (Unix seconds) ここには時計がありません:`time`(Unix 秒)を指定してください
@msg the document has never been saved: give `folder` ドキュメントは一度も保存されていません:`folder` を指定してください
@msg every numbered name for the copy is taken コピー用の番号付きの名前はすべて使用されています
Marathi マラーティー語
Gujarati グジャラート語
Bengali ベンガル語
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6044,6 +6044,13 @@ breakText Glyphs გლიფები
Remove Gaps ღრეჩოების მოცილება
@msg that makes too many pieces (at most 100000): make the dashes longer ეს ძალიან ბევრ ნაწილს იძლევა (მაქსიმუმ 100000): გაზარდეთ ტირეების სიგრძე
@msg select paths, or segments, to divide აირჩიეთ დასაყოფი კონტურები ან სეგმენტები
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools დოკუმენტის ხელსაწყოები
Save a Timestamped Copy ასლის შენახვა თარიღითა და დროით
@msg there is no clock here აქ საათი არ არის
@msg there is no clock here: give `time` (Unix seconds) აქ საათი არ არის: მიუთითეთ `time` (Unix წამები)
@msg the document has never been saved: give `folder` დოკუმენტი არასოდეს შენახულა: მიუთითეთ `folder`
@msg every numbered name for the copy is taken ასლის ყველა დანომრილი სახელი დაკავებულია
Marathi მარათჰი
Gujarati გუჯარათი
Bengali ბენგალური
Can't render this file because it is too large.
+7
View File
@@ -5637,6 +5637,13 @@ breakText Glyphs 글리프
Remove Gaps 간격 제거
@msg that makes too many pieces (at most 100000): make the dashes longer 조각이 너무 많아집니다(최대 100000개). 대시를 더 길게 하십시오
@msg select paths, or segments, to divide 나눌 패스나 세그먼트를 선택하십시오
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools 문서 도구
Save a Timestamped Copy 날짜와 시간이 붙은 사본 저장
@msg there is no clock here 여기에는 시계가 없습니다
@msg there is no clock here: give `time` (Unix seconds) 여기에는 시계가 없습니다. `time`(Unix 초)을 지정하십시오
@msg the document has never been saved: give `folder` 문서를 저장한 적이 없습니다. `folder`를 지정하십시오
@msg every numbered name for the copy is taken 사본에 쓸 번호 붙은 이름이 모두 사용 중입니다
Marathi 마라티어
Gujarati 구자라트어
Bengali 벵골어
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5996,6 +5996,13 @@ breakText Glyphs Glyphs
Remove Gaps Tussenruimten verwijderen
@msg that makes too many pieces (at most 100000): make the dashes longer dat geeft te veel stukken (hoogstens 100000): maak de streepjes langer
@msg select paths, or segments, to divide selecteer paden of segmenten om te verdelen
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Documentgereedschappen
Save a Timestamped Copy Een kopie met tijdstempel opslaan
@msg there is no clock here er is hier geen klok
@msg there is no clock here: give `time` (Unix seconds) er is hier geen klok: geef `time` op (Unix-seconden)
@msg the document has never been saved: give `folder` het document is nooit opgeslagen: geef `folder` op
@msg every numbered name for the copy is taken alle genummerde namen voor de kopie zijn bezet
Marathi Marathi
Gujarati Gujarati
Bengali Bengaals
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5910,6 +5910,13 @@ breakText Glyphs Glify
Remove Gaps Usuń odstępy
@msg that makes too many pieces (at most 100000): make the dashes longer to daje za dużo części (najwyżej 100000): wydłuż kreski
@msg select paths, or segments, to divide zaznacz ścieżki lub segmenty do podzielenia
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Narzędzia dokumentu
Save a Timestamped Copy Zapisz kopię z datą i godziną
@msg there is no clock here brak tu zegara
@msg there is no clock here: give `time` (Unix seconds) brak tu zegara: podaj `time` (sekundy Unix)
@msg the document has never been saved: give `folder` dokument nigdy nie został zapisany: podaj `folder`
@msg every numbered name for the copy is taken wszystkie numerowane nazwy kopii są zajęte
Marathi Marathi
Gujarati Gudżarati
Bengali Bengalski
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5974,6 +5974,13 @@ breakText Glyphs Glifos
Remove Gaps Remover distâncias
@msg that makes too many pieces (at most 100000): make the dashes longer isso gera peças demais (no máximo 100000): aumente os traços
@msg select paths, or segments, to divide selecione demarcadores ou segmentos para dividir
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Ferramentas de Documento
Save a Timestamped Copy Salvar uma Cópia com Data e Hora
@msg there is no clock here não há relógio aqui
@msg there is no clock here: give `time` (Unix seconds) não há relógio aqui: informe `time` (segundos Unix)
@msg the document has never been saved: give `folder` o documento nunca foi salvo: informe `folder`
@msg every numbered name for the copy is taken todos os nomes numerados para a cópia estão em uso
Marathi Marati
Gujarati Guzerate
Bengali Bengali
Can't render this file because it contains an unexpected character in line 6 and column 47.
+7
View File
@@ -6032,6 +6032,13 @@ breakText Glyphs Глифы
Remove Gaps Убрать зазоры
@msg that makes too many pieces (at most 100000): make the dashes longer так получится слишком много частей (не более 100000): удлините штрихи
@msg select paths, or segments, to divide выделите контуры или сегменты для разделения
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Инструменты документа
Save a Timestamped Copy Сохранить копию с датой и временем
@msg there is no clock here здесь нет часов
@msg there is no clock here: give `time` (Unix seconds) здесь нет часов: укажите `time` (секунды Unix)
@msg the document has never been saved: give `folder` документ ни разу не сохранялся: укажите `folder`
@msg every numbered name for the copy is taken все нумерованные имена для копии заняты
Marathi Маратхи
Gujarati Гуджарати
Bengali Бенгальский
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -6107,6 +6107,13 @@ breakText Glyphs Glyfer
Remove Gaps Ta bort mellanrum
@msg that makes too many pieces (at most 100000): make the dashes longer det blir för många delar (högst 100000): gör strecken längre
@msg select paths, or segments, to divide markera banor eller segment att dela upp
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Dokumentverktyg
Save a Timestamped Copy Spara en kopia med tidsstämpel
@msg there is no clock here det finns ingen klocka här
@msg there is no clock here: give `time` (Unix seconds) det finns ingen klocka här: ange `time` (Unix-sekunder)
@msg the document has never been saved: give `folder` dokumentet har aldrig sparats: ange `folder`
@msg every numbered name for the copy is taken alla numrerade namn för kopian är upptagna
Marathi Marathi
Gujarati Gujarati
Bengali Bengali
Can't render this file because it contains an unexpected character in line 5 and column 25.
+7
View File
@@ -6012,6 +6012,13 @@ breakText Glyphs Glifler
Remove Gaps Boşlukları Kaldır
@msg that makes too many pieces (at most 100000): make the dashes longer bu çok fazla parça oluşturur (en fazla 100000): tireleri uzatın
@msg select paths, or segments, to divide bölünecek yolları veya bölümleri seçin
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Belge Araçları
Save a Timestamped Copy Zaman Damgalı Bir Kopya Kaydet
@msg there is no clock here burada saat yok
@msg there is no clock here: give `time` (Unix seconds) burada saat yok: `time` verin (Unix saniyesi)
@msg the document has never been saved: give `folder` belge hiç kaydedilmedi: `folder` verin
@msg every numbered name for the copy is taken kopya için tüm numaralı adlar kullanılıyor
Marathi Marathi dili
Gujarati Gücerat dili
Bengali Bengalce
Can't render this file because it contains an unexpected character in line 5 and column 25.
+7
View File
@@ -5654,6 +5654,13 @@ breakText Glyphs Гліфи
Remove Gaps Прибрати проміжки
@msg that makes too many pieces (at most 100000): make the dashes longer так вийде забагато частин (не більше 100000): подовжте штрихи
@msg select paths, or segments, to divide виділіть контури або сегменти для розділення
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Інструменти документа
Save a Timestamped Copy Зберегти копію з датою і часом
@msg there is no clock here тут немає годинника
@msg there is no clock here: give `time` (Unix seconds) тут немає годинника: укажіть `time` (секунди Unix)
@msg the document has never been saved: give `folder` документ жодного разу не зберігався: укажіть `folder`
@msg every numbered name for the copy is taken усі нумеровані назви для копії зайняті
Marathi Маратхі
Gujarati Гуджараті
Bengali Бенгальська
Can't render this file because it contains an unexpected character in line 178 and column 5.
+7
View File
@@ -5657,6 +5657,13 @@ breakText Glyphs Glyph
Remove Gaps Gỡ các khoảng hở
@msg that makes too many pieces (at most 100000): make the dashes longer như vậy tạo ra quá nhiều mảnh (tối đa 100000): hãy kéo dài nét
@msg select paths, or segments, to divide chọn các đường dẫn hoặc đoạn để chia
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools Công cụ tài liệu
Save a Timestamped Copy Lưu bản sao có ngày giờ
@msg there is no clock here ở đây không có đồng hồ
@msg there is no clock here: give `time` (Unix seconds) ở đây không có đồng hồ: hãy cho `time` (giây Unix)
@msg the document has never been saved: give `folder` tài liệu chưa từng được lưu: hãy cho `folder`
@msg every numbered name for the copy is taken mọi tên đánh số cho bản sao đều đã dùng
Marathi Tiếng Marathi
Gujarati Tiếng Gujarati
Bengali Tiếng Bengali
Can't render this file because it contains an unexpected character in line 200 and column 5.
+7
View File
@@ -5897,6 +5897,13 @@ breakText Glyphs 字形
Remove Gaps 移除间隙
@msg that makes too many pieces (at most 100000): make the dashes longer 这会产生过多片段(最多 100000 个):请加长短划线
@msg select paths, or segments, to divide 请选择要分割的路径或线段
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools 文档工具
Save a Timestamped Copy 保存带时间戳的副本
@msg there is no clock here 此处没有时钟
@msg there is no clock here: give `time` (Unix seconds) 此处没有时钟:请提供 `time`(Unix 秒)
@msg the document has never been saved: give `folder` 文档从未保存过:请提供 `folder`
@msg every numbered name for the copy is taken 副本的所有编号名称均已被占用
Marathi 马拉地语
Gujarati 古吉拉特语
Bengali 孟加拉语
Can't render this file because it contains an unexpected character in line 2 and column 73.
+7
View File
@@ -5694,6 +5694,13 @@ breakText Glyphs 字符
Remove Gaps 移除間隙
@msg that makes too many pieces (at most 100000): make the dashes longer 這會產生過多片段(最多 100000 個):請加長短破折號
@msg select paths, or segments, to divide 請選取要分割的路徑或線段
# Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy.
Document Tools 文件工具
Save a Timestamped Copy 儲存附時間戳記的拷貝
@msg there is no clock here 此處沒有時鐘
@msg there is no clock here: give `time` (Unix seconds) 此處沒有時鐘:請提供 `time`(Unix 秒)
@msg the document has never been saved: give `folder` 文件從未儲存過:請提供 `folder`
@msg every numbered name for the copy is taken 拷貝的所有編號名稱均已被佔用
Marathi 馬拉地文
Gujarati 古吉拉特文
Bengali 孟加拉文
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -530,6 +530,7 @@ fn run_ui_part6(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<
crate::addons::pathtools::corner::COMMAND => crate::addons::pathtools::corner::open(app),
crate::addons::pathtools::shared_edge::COMMAND => crate::addons::pathtools::shared_edge::open(app),
crate::addons::arrange::ZOOM => crate::addons::arrange::zoom(app),
crate::addons::document::SAVE_COPY => crate::addons::document::save_copy(app, p),
"tool.setOption" => app.session.set_tool_option_cmd(p),
"effect.dialog" => crate::dialogs::open_effect_dialog(app, p),
"ui.recolorDialog" => crate::dialogs::recolor::open(app, p),
+18
View File
@@ -6274,3 +6274,21 @@ command's parameters, with Preview; Set Size of Each's `__width` and `__height`
{"name":"run_command","arguments":{"command":"addon.arrange.replace","params":{"fit":"proportional"}}}
{"name":"run_command","arguments":{"command":"addon.arrange.size","params":{"width":72,"proportional":true}}}
```
## Document Tools add-on
Document Tools is a built-in add-on for whole-document chores: its commands are `addon.document.*`, and in the desktop
app it lives in Object › Plug-ins › Document Tools.
`addon.document.saveTimestampedCopy {folder?, format?, time?}` writes a copy of the document named
`<name>_YYYY-MM-DD_HH-MM.<ext>`, with `-2`, `-3`… added when that name is taken. The date and time are `time` (Unix
seconds; now by default) in UTC. The copy goes in the document's own folder, or in `folder`, which a document never
saved needs (the desktop app asks for one with the folder picker); `format` is a save format (`vectorcraft`, `pdf`,
`svg`, `svgz`, `ai`, `eps`; the document's own by default). It is written as `file.saveCopy` writes, inside the
automation roots in force: the document keeps its path, title and modified state, and no undo step is made. The
command is off where there is no clock (the web). → `{path, format, bytes, warnings}`
```json
{"name":"run_command","arguments":{"command":"addon.document.saveTimestampedCopy","params":{}}}
{"name":"run_command","arguments":{"command":"addon.document.saveTimestampedCopy","params":{"folder":"/Users/me/Versions","format":"pdf"}}}
```
+6 -1
View File
@@ -1,6 +1,6 @@
# Parity with Adobe Illustrator
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Path Precision Tools add-on: Divide into Dashes, #4950; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Document Tools add-on: Save a Timestamped Copy, #4926; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
## Feature submission evidence
@@ -1407,10 +1407,15 @@ A VectorCraft addition, not a feature of the reference app, so no area score mov
A VectorCraft addition, not a feature of the reference app, so no area score moves. Object › Plug-ins › Path Precision Tools › Divide into Dashes… cuts the selected paths into separate open paths along the curve (arc length, not straight distance): each `dash` long (0.01 to 1000000 pt, 10 by default) with `gap` between them (0 by default: the pieces touch); the gap pieces are left out unless Remove Gaps is off. With direct-selected segments (or anchors) only those runs are divided, the pattern running on across segments that follow one another, and the rest of such a path stays as pieces of its own. Fit to Corners and Ends gives every run between corners and ends a whole number of dashes, all stretched or squeezed alike so it starts and ends on a dash (a closed path without corners holds whole periods); otherwise the lengths are exact and each run's last piece is what is left. Every piece keeps the path's appearance and is selected; one undo step; at most 100000 pieces (more is refused). The dialog takes lengths in any unit and previews on the canvas. Tests: `dashes::tests` in `vectorcraft-pathops` (exact and fitted lengths, curves measured along them, picked segments on open and closed paths, the piece cap and bad patterns), `divide_into_dashes_*` in `cmd::addons::pathtools::tests` in `vectorcraft-engine` (gaps removed or kept, fitted lengths, the stroke kept, one undo step, selected segments only, bad params and too many pieces refused), `divide_into_dashes_opens_its_dialog_previews_and_runs_on_ok` in `vectorcraft-ui-egui` and `command_sweep`. Commands: `docs/mcp.md` › Path Precision Tools add-on.
## Document Tools add-on: Save a Timestamped Copy (2026-11-01)
A VectorCraft addition, not a feature of the reference app, so no area score moves. Object › Plug-ins › Document Tools › Save a Timestamped Copy writes a copy of the document named `<name>_YYYY-MM-DD_HH-MM.<ext>` (the date and time in UTC, through chrono; `-2`, `-3`… added when that name is taken) next to the document, or into a folder picked (the folder picker) when it was never saved, in the document's own format or another save format. It writes through File › Save a Copy's save, so the document keeps its path, title and modified state, no undo step is made, and the write stays inside the automation roots in force. It is off on the web, which has no clock. Tests: `cmd::addons::document::tests` in `vectorcraft-engine` (the name and place, numbered names, the document unchanged and its content in the copy, an unsaved document needing a folder, other formats, bad params refused, automation roots), `addons::document::tests` in `vectorcraft-ui-egui` (the submenu, beside the document, the folder picked or cancelled) and `command_sweep`. Commands: `docs/mcp.md` › Document Tools add-on.
## Revision history
| Date | Change | Summary |
|---|---|---|
| 2026-11-01 | minor | Document Tools add-on (a VectorCraft addition): Save a Timestamped Copy (#4926); evidence added; scores unchanged. |
| 2026-11-01 | minor | Path Precision Tools add-on (a VectorCraft addition): Divide into Dashes (#4950); evidence added; scores unchanged. |
| 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Group Overlapping Objects (#4928); evidence added; scores unchanged. |
| 2026-11-01 | minor | Type core: a Language per character (Character panel › Language, `text.setFormat` / `text.setRangeStyle {language}`, character styles), new type in the document's language; text is shaped in its language so fonts use their language forms (OpenType `locl`: Turkish `fi`, #4940, #4946); Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu in the language list (#4943); scores unchanged |
+3 -1
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Path Precision Tools add-on: Divide into Dashes, #4950) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Document Tools add-on: Save a Timestamped Copy, #4926) · **Target:** Adobe Illustrator 2026 (30.x)
## Feature submission evidence
@@ -330,6 +330,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| Outline view keeps template layers in Preview, dimmed | done | #1486; a picture placed to be traced stays visible under the outlines |
| Arrange Tools add-on (VectorCraft addition): Object › Plug-ins › Arrange Tools runs Arrange in Grid… (grid by row, one row or one column; columns, H and V spacing, Center on Artboard; Preview), Swap Objects, Swap Objects and Sizes, Replace with Top Object… (Actual Size, Bounds or Bounds (Fit Proportionally); Keep Original; Scale Strokes & Effects; Preview), Set Size of Each… (Width and Height each ticked on or off, Constrain Width and Height Proportions with one side, the reference point, Scale Strokes & Effects; Preview) and Zoom to Selection, each one undo step | done | `addon.arrange.*`, `crates/engine/src/cmd/addons/arrange.rs`, `crates/ui-egui/src/addons/arrange.rs` and their tests; not a feature of the reference app, so it is not scored |
| Arrange Tools add-on (VectorCraft addition): Object › Plug-ins › Arrange Tools › Group Overlapping Objects groups the selected objects whose visual bounds overlap or touch (chains included, within one layer or group), each group at its frontmost member's stacking place, one undo step | done | #4928; `addon.arrange.groupOverlapping`, `crates/engine/src/cmd/addons/arrange.rs` and its tests; not a feature of the reference app, so it is not scored |
| Document Tools add-on (VectorCraft addition): Object › Plug-ins › Document Tools › Save a Timestamped Copy writes `<name>_YYYY-MM-DD_HH-MM.<ext>` next to the document (a folder picked for one never saved), which keeps its path, title and modified state; off on the web | done | #4926; `addon.document.saveTimestampedCopy`, `crates/engine/src/cmd/addons/document.rs`, `crates/ui-egui/src/addons/document.rs` and their tests; not a feature of the reference app, so it is not scored |
| Smart Pen add-on (VectorCraft addition): Object › Plug-ins › Smart Pen selects the Smart Pen Tool, which the All Tools drawer lists under its own heading but neither toolbar shows as shipped (no shortcut), and opens the Smart Pen panel (next segment straight or curved; Remove Points Keep Shape, Make Smooth, Make Corner, Retract Handles, Close or Join). The tool draws (click: straight or curved segment, a curve after a straight one tangent; drag: smooth anchor, Alt breaks the handle, Shift 45°, Space moves it; Tab switches a segment; rubber band; close, join, continue) and edits without switching tools (click selects a point, drag moves it, Alt-click removes it keeping the shape, Shift+Alt-click deletes it, double-click toggles smooth/corner; handle drags with Shift and Alt; segment drags reshape, Alt-click adds a point) | done | `addon.pen.*`, `crates/tools/src/addons/smart_pen.rs`, `crates/ui-egui/src/addons/pen.rs` and their tests; not a feature of the reference app, so it is not scored |
| Path Precision Tools add-on (VectorCraft addition): Object › Plug-ins › Path Precision Tools selects its seven tools (Anchor Eraser Brush, Extend Path, Measure & Inspect, Parametric Shape, Corner Tool, Slide Anchor Along Path, Shared Edge Merge Tool), which the All Tools drawer lists under their own heading but neither toolbar shows as shipped, opens the Anchor Inspector panel (the selected anchor's X/Y and handle lengths and angles as fields; Make Smooth, Make Corner, Equalize Handles, Remove Points Keep Shape, Add Points, the three ways to close) and the Parametric Shape panel (kind, count, W/H, rotation, the kind's own values and corner radius of the selected parametric shape, or of the tool's next one), and runs Add Points at / Split at Crossings and Convert to Parametric Shape; the Parametric Shape Tool draws ten re-editable kinds from the centre (↑/↓ and ←/→ change their numbers while dragging) and edits a selected one with handles; the Corner Tool cuts editable corners on any path (hover shows the anchor, a click uses its radius and kind, a drag sizes the corner, Shift whole values; a corner's marker selects it and loads its values, drags to resize, to 0 or double-clicked removes it; never past what fits; Alt-marquee cuts every anchor inside) with a Corners section in the Anchor Inspector (radius, kind, selected count, Apply to Selected Anchors); Slide Anchor Along Path drags an anchor along its curve with the segments beside it refitted, Shift snapping where the tangent is a multiple of 45°; the Shared Edge Merge Tool shows, over two paths touching at two coincident anchors, the two stretches and the edge merging them, a click merges them (half way; Shift the upper path, Alt the lower), and near anchors meet or an anchor one path lacks is added to match (Shift+Alt asks for this inside a stretch), with a Shared Edges section in the Anchor Inspector (brush diameter, coincidence 0 to 3 pt, Keep Shared Points Together, which takes coincident anchors along with the add-on's own anchor moves); a double-click on the brush or Extend Path tool opens its options | done | `addon.pathtools.*`, `crates/tools/src/addons/`, `crates/ui-egui/src/addons/pathtools.rs`, `crates/ui-egui/src/addons/pathtools/` and their tests; not a feature of the reference app, so it is not scored |
| Path Precision Tools add-on (VectorCraft addition): Object › Plug-ins › Path Precision Tools › Divide into Dashes… (Dash and Gap in any unit, Fit to Corners and Ends, Remove Gaps; Preview) cuts the selected paths, or only their direct-selected segments, into open pieces along the curve, keeping their appearance, one undo step | done | #4950; `addon.pathtools.divideDashes`, `crates/pathops/src/addons/pathtools/dashes.rs`, `crates/ui-egui/src/addons/pathtools/dashes.rs` and their tests; not a feature of the reference app, so it is not scored |
@@ -383,6 +384,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
| Date | Change | Summary |
|---|---|---|
| 2026-11-01 | minor | Document Tools add-on (a VectorCraft addition): Save a Timestamped Copy (#4926); scores and estimates unchanged |
| 2026-11-01 | minor | Path Precision Tools add-on (a VectorCraft addition): Divide into Dashes (#4950); scores and estimates unchanged |
| 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Group Overlapping Objects (#4928); scores and estimates unchanged |
| 2026-11-01 | minor | Character panel › Language: the selected characters' language, from Document Setup's list, which gains Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu (#4946, #4943); the Digits menu's numeral names keep their own translations; scores unchanged |