From 19ce2b25472248a44cc701777d6c2cb0a6399fad Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Mon, 9 Nov 2026 23:09:51 -0800 Subject: [PATCH] Scripts: a command's savepoint copies no undo steps but the newest Each command a script runs keeps a savepoint of the state it may need to put back if it fails. The savepoint cloned every open document's whole undo history, so a script that made N edits did O(N^2) work. - The savepoint now clones each document's state without its history and keeps a mark: the newest undo and redo steps and the serials of the steps under them. Until the command ends, the history trims no step, and a new step sets the redo steps aside instead of dropping them. A failed command truncates both stacks back to the mark. - file.close, file.closeAll, document.close and file.revert still keep a whole copy, since they remove the history or the document. - When a failed command removed or changed a step under the newest one, the history cannot be put back exactly, and the run stops with an error; its rollback restores the documents as they were before the run. - run_guarded no longer copies the histories during a script's run, and a committed run moves the histories from before it back instead of copying them. - Symbol editing mode shares its saved redo steps between copies of a document's state. - A script cannot run edit.clearHistory. A run keeps each document's undo steps from before it, so the command reported a clear that the commit then undid. --- .../engine/src/cmd/distortcmds/perspective.rs | 1 + crates/engine/src/cmd/symboledit.rs | 7 +- crates/engine/src/doc_state.rs | 2 +- crates/engine/src/history.rs | 122 +++++++++++++++++- crates/engine/src/script/mod.rs | 97 +++++++++++--- crates/engine/src/session/execute.rs | 13 +- crates/engine/src/tests/script_run.rs | 93 +++++++++++++ crates/ui-egui/src/i18n/ca.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fi.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hi.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ka.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/tr.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + docs/mcp.md | 5 +- 30 files changed, 336 insertions(+), 26 deletions(-) diff --git a/crates/engine/src/cmd/distortcmds/perspective.rs b/crates/engine/src/cmd/distortcmds/perspective.rs index a561e5099..8c3525449 100644 --- a/crates/engine/src/cmd/distortcmds/perspective.rs +++ b/crates/engine/src/cmd/distortcmds/perspective.rs @@ -635,6 +635,7 @@ pub(crate) fn finish_edit_text(st: &mut crate::DocState, id: NodeId) { } }; st.change_unsaved(clear); + st.history.rewrites += 1; for e in st.history.undo.iter_mut().chain(st.history.redo.iter_mut()) { let saved = Arc::ptr_eq(&e.doc, &st.saved_doc); clear(&mut e.doc); diff --git a/crates/engine/src/cmd/symboledit.rs b/crates/engine/src/cmd/symboledit.rs index a3745975b..22cfc74e9 100644 --- a/crates/engine/src/cmd/symboledit.rs +++ b/crates/engine/src/cmd/symboledit.rs @@ -140,7 +140,7 @@ pub(crate) fn enter(s: &mut Session, p: &Value) -> Result { } s.end_text_edit(); let st = s.doc()?; - let back = Return { redo: st.history.redo.clone(), before: Some(st.doc.clone()) }; + let back = Return { redo: Arc::new(st.history.redo.clone()), before: Some(st.doc.clone()) }; let (isolation, active_layer) = (st.isolation, st.active_layer); let layer = s.edit(ENTER, |d, sel| { let layer = begin(d, sel, &name, xf, instance)?; @@ -229,7 +229,7 @@ pub(crate) fn leave(s: &mut Session, _: &Value) -> Result { } st.selection = Arc::unwrap_or_clone(entry.selection); st.selection.prune(&st.doc); - st.history.redo = redo; + st.history.redo = Arc::unwrap_or_clone(redo); st.revision += 1; } else { s.edit("Exit Symbol Editing Mode", |d, sel| { @@ -257,9 +257,10 @@ pub(crate) fn finish(d: &mut Document, sel: &mut Selection) { /// The redo steps entering symbol editing mode cleared, which leaving without an edit gives back, /// and the document entering started from, which tells the entering step apart from any other. +/// Copies of the document's state share the redo steps. #[derive(Clone, Debug, Default)] pub(crate) struct Return { - redo: Vec, + redo: Arc>, before: Option>, } diff --git a/crates/engine/src/doc_state.rs b/crates/engine/src/doc_state.rs index 736b873d0..d0e486240 100644 --- a/crates/engine/src/doc_state.rs +++ b/crates/engine/src/doc_state.rs @@ -446,7 +446,7 @@ impl DocState { } let e = HistoryEntry::kept(label, doc, selection, &self.doc); self.history.undo.push(e); - self.history.redo.clear(); + self.history.clear_redo(); self.history.trim(); } /// End the interaction in progress, undoing what it changed. diff --git a/crates/engine/src/history.rs b/crates/engine/src/history.rs index 8df6060e0..8dced7fb9 100644 --- a/crates/engine/src/history.rs +++ b/crates/engine/src/history.rs @@ -1,7 +1,13 @@ use super::{Arc, Document, Selection}; +#[cfg(test)] +thread_local! { + /// Tests: how many undo steps were copied on this thread. + pub(crate) static ENTRY_CLONES: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + /// One undo step. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct HistoryEntry { pub label: String, /// The document as it was. It keeps no lookup cache ([`Document::drop_lookup_cache`]): a @@ -27,6 +33,25 @@ pub struct HistoryEntry { color_mode_bytes: usize, } +impl Clone for HistoryEntry { + fn clone(&self) -> Self { + #[cfg(test)] + ENTRY_CLONES.with(|n| n.set(n.get() + 1)); + HistoryEntry { + label: self.label.clone(), + doc: self.doc.clone(), + selection: self.selection.clone(), + bytes: self.bytes, + next_bytes: self.next_bytes, + next: self.next, + named: self.named.clone(), + serial: self.serial, + color_mode_paints: self.color_mode_paints.clone(), + color_mode_bytes: self.color_mode_bytes, + } + } +} + /// The next [`HistoryEntry::serial`]: 1 for the first step made, so 0 never names a step. fn next_serial() -> u64 { static NEXT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1); @@ -148,11 +173,52 @@ pub struct History { /// About the most memory the steps may keep ([`HistoryEntry::bytes`]): past it the oldest /// steps go, but never the last [`History::MIN_STEPS`] undo (and redo) steps. pub byte_budget: usize, + /// Counts the changes made in place to steps below the newest ([`History::put_back`]). + pub(crate) rewrites: u64, + /// Between [`History::mark`] and [`History::release`] or [`History::put_back`]. + pub(crate) held: bool, + /// The redo steps a new step cleared while held ([`History::clear_redo`]). + pub(crate) set_aside: Option>, } impl Default for History { fn default() -> Self { - History { undo: vec![], redo: vec![], limit: 0, byte_budget: default_history_budget() } + History { undo: vec![], redo: vec![], limit: 0, byte_budget: default_history_budget(), rewrites: 0, held: false, set_aside: None } + } +} + +/// What [`History::put_back`] needs to give a history back as it was when [`History::mark`] took +/// it: the newest undo and redo steps, and where each stack stood under them. It holds no other +/// step, so taking one costs the same however many the history holds. +pub(crate) struct HistoryMark { + undo: StackMark, + redo: StackMark, + limit: usize, + byte_budget: usize, + rewrites: u64, +} + +/// One stack of a [`HistoryMark`]. +struct StackMark { + len: usize, + /// [`HistoryEntry::serial`] of the step under the newest one, 0 for none. + below: u64, + newest: Option, +} + +impl StackMark { + fn of(steps: &[HistoryEntry]) -> Self { + let len = steps.len(); + StackMark { len, below: len.checked_sub(2).and_then(|i| steps.get(i)).map_or(0, |e| e.serial), newest: steps.last().cloned() } + } + + /// Puts `steps` back as they were. → `false` when a step under the newest one was removed since. + fn put_back(self, steps: &mut Vec) -> bool { + let kept = self.len.saturating_sub(1); + let intact = steps.len() >= kept && kept.checked_sub(1).is_none_or(|i| steps.get(i).is_some_and(|e| e.serial == self.below)); + steps.truncate(kept); + steps.extend(self.newest); + intact } } @@ -208,6 +274,58 @@ impl History { self.redo.drain(..n); } + /// A [`HistoryMark`] of the history now. Until [`History::release`] or [`History::put_back`], + /// [`History::trim`] lets no step go, so the steps under the newest one stay where they are, and + /// [`History::clear_redo`] sets the redo steps aside. + pub(crate) fn mark(&mut self) -> HistoryMark { + let mark = HistoryMark { + undo: StackMark::of(&self.undo), + redo: StackMark::of(&self.redo), + limit: self.limit, + byte_budget: self.byte_budget, + rewrites: self.rewrites, + }; + self.limit = usize::MAX; + self.byte_budget = usize::MAX; + self.held = true; + self.set_aside = None; + mark + } + + /// Removes the redo steps, as a new step does. While held ([`History::mark`]) the first call + /// keeps them for [`History::put_back`]. + pub(crate) fn clear_redo(&mut self) { + if self.held && self.set_aside.is_none() { + self.set_aside = Some(std::mem::take(&mut self.redo)); + } else { + self.redo.clear(); + } + } + + /// Keeps the history as it is now: the limits `mark` held back apply again. + pub(crate) fn release(&mut self, mark: &HistoryMark) { + self.held = false; + self.set_aside = None; + self.limit = mark.limit; + self.byte_budget = mark.byte_budget; + self.trim(); + } + + /// Gives the history back as it was at `mark`. → `false` when that is not possible: an undo or + /// redo step under the newest one was removed or changed in place since. + pub(crate) fn put_back(&mut self, mark: HistoryMark) -> bool { + if let Some(redo) = self.set_aside.take() { + self.redo = redo; + } + let undo = mark.undo.put_back(&mut self.undo); + let redo = mark.redo.put_back(&mut self.redo); + let intact = self.rewrites == mark.rewrites && undo && redo; + self.held = false; + self.limit = mark.limit; + self.byte_budget = mark.byte_budget; + intact + } + /// Estimate the newest undo step again, its next state now being `now` (the steps after it were /// merged into it, or an undo group extended it). It takes a new number ([`History::step`]). pub(crate) fn recost_last(&mut self, now: &Document) { diff --git a/crates/engine/src/script/mod.rs b/crates/engine/src/script/mod.rs index 632d45de0..35e93c1ad 100644 --- a/crates/engine/src/script/mod.rs +++ b/crates/engine/src/script/mod.rs @@ -78,7 +78,8 @@ pub const API_FEATURES: &[&str] = &["run", "document", "commands", "log", "args" pub const STEP_LABEL: &str = "Run Script"; /// `id`, as `vc.run` is given it, is one a script may not run: a command that would leave the run's -/// transaction or change something outside the documents that a rollback could not put back. +/// transaction, change something outside the documents that a rollback could not put back, or clear +/// the undo steps from before the run, which the run keeps. pub fn refused_to_scripts(id: &str) -> bool { const PREFIXES: &[&str] = &[ "script.", @@ -107,6 +108,7 @@ pub fn refused_to_scripts(id: &str) -> bool { "view.proofColors", "view.overprintPreview", "view.separationsPreview", + "edit.clearHistory", ]; let saved_preset = (id.contains("resets.") || id.contains(".preset.")) && !id.ends_with(".list") && !id.ends_with(".apply"); id != "script.check" && (saved_preset || PREFIXES.iter().any(|p| id.starts_with(p)) || IDS.contains(&id)) @@ -302,10 +304,23 @@ impl World { /// themselves. The clipboard can hold a great deal, so a savepoint copies it only for these. const CAN_FAIL_AFTER_COPYING: &[&str] = &["action.batch", "edit.cut"]; +/// The commands that remove a document's undo history, or the document: a savepoint keeps a whole +/// copy of the histories for these. Any other command that removes or changes a step under the +/// newest undo or redo step and then fails ends the run ([`HISTORY_LOST`]). +const DROP_HISTORY: &[&str] = &["file.close", "file.closeAll", "document.close", "file.revert"]; + +/// A document's state before a command: everything but its undo history, which `mark` gives back, +/// or `whole` for the commands in [`DROP_HISTORY`]. +struct SavedDoc { + state: DocState, + mark: crate::history::HistoryMark, + whole: Option, +} + /// What one command may need to put back when it fails inside a run: the state of [`World`] the /// command can reach. pub(crate) struct Savepoint { - docs: Vec, + docs: Vec, active: Option, paint: PaintDefaults, fill_active: bool, @@ -323,9 +338,23 @@ pub(crate) struct Savepoint { } impl Savepoint { - pub(crate) fn take(s: &Session, id: &str) -> Self { + /// The state before command `id`. Until [`Savepoint::keep`] or [`Savepoint::restore`], the + /// documents' undo histories let no step go. + pub(crate) fn take(s: &mut Session, id: &str) -> Self { + let whole = DROP_HISTORY.contains(&id); + let docs = s + .docs + .iter_mut() + .map(|st| { + let history = std::mem::take(&mut st.history); + let state = st.clone(); + st.history = history; + let whole = whole.then(|| st.history.clone()); + SavedDoc { state, mark: st.history.mark(), whole } + }) + .collect(); Self { - docs: s.docs.clone(), + docs, active: s.active, paint: s.paint.clone(), fill_active: s.fill_active, @@ -343,8 +372,39 @@ impl Savepoint { } } - pub(crate) fn restore(self, s: &mut Session) { - s.docs = self.docs; + /// Keeps what the command did: the histories' limits apply again. + pub(crate) fn keep(self, s: &mut Session) { + for saved in &self.docs { + if let Some(st) = s.docs.iter_mut().find(|d| d.uid == saved.state.uid) { + st.history.release(&saved.mark); + } + } + } + + /// Puts back the state before the command. → `false` when an undo history could not be given + /// back as it was (a step under the newest one was removed or changed in place, or its + /// document was closed); the rest is put back all the same. + pub(crate) fn restore(self, s: &mut Session) -> bool { + let mut exact = true; + let mut docs = Vec::with_capacity(self.docs.len()); + for SavedDoc { mut state, mark, whole } in self.docs { + let now = s.docs.iter_mut().find(|d| d.uid == state.uid).map(|d| std::mem::take(&mut d.history)); + state.history = match (whole, now) { + (Some(history), _) => history, + (None, Some(mut history)) => { + exact &= history.put_back(mark); + history + } + (None, None) => { + exact = false; + let mut history = crate::History::default(); + history.put_back(mark); + history + } + }; + docs.push(state); + } + s.docs = docs; s.active = self.active; s.paint = self.paint; s.fill_active = self.fill_active; @@ -361,6 +421,7 @@ impl Savepoint { s.pending_paint = self.pending_paint; self.choices.put(s); s.journal.truncate(self.journal); + exact } } @@ -620,8 +681,10 @@ fn step(s: &mut Session, host: &Host, id: &str, params: &Value) -> Result<(Value Ok((v, objects, bytes, save)) } Err(e) => { - if let Some(save) = save { - save.restore(s); + if let Some(save) = save + && !save.restore(s) + { + return Err(EngineError::Internal { cmd: id.to_string(), msg: HISTORY_LOST.to_string() }); } Err(e) } @@ -660,10 +723,13 @@ fn vc_run(host: &Rc, args: Vec) -> std::result::Result Result) -> Result { - let snapshot: Vec<_> = - self.docs.iter().map(|d| (d.uid, d.doc.clone(), d.selection.clone(), d.interaction.clone(), d.history.clone())).collect(); + // In a script's run a panic ends the run, and the run's rollback puts the histories back. + let in_run = self.script_in_run; + let snapshot: Vec<_> = self + .docs + .iter() + .map(|d| (d.uid, d.doc.clone(), d.selection.clone(), d.interaction.clone(), (!in_run).then(|| d.history.clone()))) + .collect(); let active = self.active().map(|d| d.uid); let r = match guard::catch_panic(|| self.run_nested(f)) { Ok(r) => r, @@ -324,7 +329,9 @@ impl Session { st.doc = doc; st.selection = selection; st.interaction = interaction; - st.history = history; + if let Some(history) = history { + st.history = history; + } cmd::globaledit::follow_history(st); st.drop_stale_variables_highlight(); st.revision += 1; diff --git a/crates/engine/src/tests/script_run.rs b/crates/engine/src/tests/script_run.rs index 81b224628..5480ba499 100644 --- a/crates/engine/src/tests/script_run.rs +++ b/crates/engine/src/tests/script_run.rs @@ -370,6 +370,88 @@ fn a_command_that_can_fail_after_copying_has_the_clipboard_put_back() { assert!(s.clipboard.source_artboard.is_some(), "only those commands pay for a copy of the clipboard"); } +#[test] +fn a_commands_savepoint_copies_the_same_number_of_undo_steps_however_long_the_runs_history() { + let copied = |n: usize| { + let mut s = two_docs(); + crate::history::ENTRY_CLONES.with(|c| c.set(0)); + let r = run(&mut s, &format!("for (var i = 0; i < {n}; i++) vc.run('shape.rectangle', {{x:i,y:1,width:1,height:1}});")).unwrap(); + assert_eq!(r["ok"], true); + assert_eq!(objects(&s, 1), n + 1); + crate::history::ENTRY_CLONES.with(Cell::get) + }; + let (few, many) = (copied(20), copied(200)); + // Each command copies at most its document's newest step, whatever the history holds. + assert!(many <= 200 + few, "{few} steps copied for 20 commands, {many} for 200"); +} + +#[test] +fn undo_redo_and_a_long_redo_stack_copy_the_same_number_of_steps_per_command() { + let copied = |n: usize| { + let mut s = two_docs(); + crate::history::ENTRY_CLONES.with(|c| c.set(0)); + let source = format!( + "for (var i = 0; i < {n}; i++) vc.run('shape.rectangle', {{x:i,y:1,width:1,height:1}});\ + for (var i = 0; i < {n}; i++) vc.run('edit.undo');\ + for (var i = 0; i < {n}; i++) vc.run('select.none');\ + for (var i = 0; i < {n}; i++) vc.run('edit.redo');" + ); + let r = run(&mut s, &source).unwrap(); + assert_eq!(r["ok"], true, "{r}"); + assert_eq!(objects(&s, 1), n + 1); + crate::history::ENTRY_CLONES.with(Cell::get) + }; + let (few, many) = (copied(20), copied(200)); + // Ten times the commands copy about ten times the steps: each command copies its document's + // newest undo and redo steps, whatever the stacks hold. + assert!(many <= 12 * few, "{few} steps copied for 80 commands, {many} for 800"); +} + +#[test] +fn a_failed_command_gives_back_the_redo_steps_and_an_undo_it_made() { + let mut s = two_docs(); + for x in 1..=3 { + s.execute("shape.rectangle", &rect(x)).unwrap(); + } + for _ in 0..2 { + s.execute("edit.undo", &json!({})).unwrap(); + } + let before = state(&s); + let save = script::Savepoint::take(&mut s, "shape.rectangle"); + s.execute("shape.rectangle", &rect(9)).unwrap(); + assert_eq!(s.documents()[1].history.redo.len(), 0); + assert!(save.restore(&mut s), "the redo steps the new step cleared are put back"); + assert_eq!(state(&s), before); + let save = script::Savepoint::take(&mut s, "edit.undo"); + s.execute("edit.undo", &json!({})).unwrap(); + s.execute("edit.redo", &json!({})).unwrap(); + s.execute("edit.redo", &json!({})).unwrap(); + assert!(save.restore(&mut s)); + assert_eq!(state(&s), before); + for _ in 0..2 { + s.execute("edit.redo", &json!({})).unwrap(); + } + assert_eq!(objects(&s, 1), 4); +} + +#[test] +fn a_failed_command_at_the_history_limit_keeps_the_oldest_step() { + let mut s = two_docs(); + s.docs[1].history.limit = 5; + let before = state(&s); + for x in 1..=5 { + s.execute("shape.rectangle", &rect(x)).unwrap(); + } + let save = script::Savepoint::take(&mut s, "shape.rectangle"); + s.execute("shape.rectangle", &rect(9)).unwrap(); + assert!(save.restore(&mut s), "the step the limit would have let go is put back"); + assert_eq!(objects(&s, 1), 6); + for _ in 0..5 { + s.execute("edit.undo", &json!({})).unwrap(); + } + assert_eq!(state(&s)["docs"][1]["art"], before["docs"][1]["art"]); +} + #[test] fn a_snapshot_too_large_to_cross_throws_instead_of_reading_as_no_document() { let mut s = two_docs(); @@ -436,6 +518,17 @@ fn commands_outside_the_documents_are_refused_in_a_batch_too() { assert_eq!(r["log"], json!([0, true])); } +#[test] +fn a_script_cannot_clear_the_undo_steps_from_before_its_run() { + let mut s = two_docs(); + let steps = s.documents()[1].history.undo.len(); + let source = "vc.run('shape.rectangle', {x:9,y:9,width:1,height:1});\ + var m = ''; try { vc.run('edit.clearHistory'); } catch (e) { m = e.message; } vc.log(m);"; + let r = run(&mut s, source).unwrap(); + assert_eq!(r["log"], json!(["a script cannot run `edit.clearHistory`"]), "{r}"); + assert_eq!(s.documents()[1].history.undo.len(), steps + 1); +} + #[test] fn the_vc_object_reads_the_active_document_and_cannot_be_changed() { let mut s = two_docs(); diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index f3d64f4a7..2a0a57373 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -5172,6 +5172,7 @@ digits Malayalam Malaiàlam @msg a drag or an undo group is open; finish it before running a script hi ha un arrossegament o un grup de desfer obert; acabeu-lo abans d'executar un script @msg the script left a document that cannot be kept l'script ha deixat un document que no es pot conservar @msg the script's run is no longer available l'execució de l'script ja no està disponible +@msg a failed command changed undo steps that could not be put back, so the script was stopped una ordre fallida ha canviat passos de desfer que no s'han pogut restaurar, així que l'script s'ha aturat @msg a recovery copy could not be written, so the script was not run no s'ha pogut escriure una còpia de recuperació, per tant l'script no s'ha executat @msg Running a script S'està executant un script @msg Copied the script report S’ha copiat l’informe de l’script diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 797f18454..59690ce3f 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -4395,6 +4395,7 @@ imageTrace Outline Obrys @msg a drag or an undo group is open; finish it before running a script je otevřené tažení nebo skupina kroků zpět; před spuštěním skriptu ji dokončete @msg the script left a document that cannot be kept skript zanechal dokument, který nelze zachovat @msg the script's run is no longer available běh skriptu již není k dispozici +@msg a failed command changed undo steps that could not be put back, so the script was stopped neúspěšný příkaz změnil kroky zpět, které nebylo možné obnovit, proto byl skript zastaven @msg a recovery copy could not be written, so the script was not run záložní kopii se nepodařilo zapsat, proto skript nebyl spuštěn @msg Running a script Běží skript @msg Copied the script report Zpráva o skriptu zkopírována diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index af6e07891..8c34e8d4a 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -4000,6 +4000,7 @@ theme Appearance Erscheinungsbild @msg a drag or an undo group is open; finish it before running a script ein Ziehvorgang oder eine Rückgängig-Gruppe ist offen; schließen Sie sie ab, bevor Sie ein Skript ausführen @msg the script left a document that cannot be kept das Skript hat ein Dokument hinterlassen, das nicht behalten werden kann @msg the script's run is no longer available der Lauf des Skripts ist nicht mehr verfügbar +@msg a failed command changed undo steps that could not be put back, so the script was stopped ein fehlgeschlagener Befehl hat Rückgängig-Schritte geändert, die nicht wiederhergestellt werden konnten, daher wurde das Skript angehalten @msg a recovery copy could not be written, so the script was not run eine Wiederherstellungskopie konnte nicht geschrieben werden, daher wurde das Skript nicht ausgeführt @msg Running a script Skript wird ausgeführt @msg Copied the script report Skriptbericht kopiert diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 8cda31957..5434392ce 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -2250,6 +2250,7 @@ axis Both Και τα δύο @msg a drag or an undo group is open; finish it before running a script είναι ανοιχτή μια μεταφορά ή ομάδα αναίρεσης· ολοκληρώστε την πριν εκτελέσετε σενάριο @msg the script left a document that cannot be kept το σενάριο άφησε ένα έγγραφο που δεν μπορεί να διατηρηθεί @msg the script's run is no longer available η εκτέλεση του σεναρίου δεν είναι πλέον διαθέσιμη +@msg a failed command changed undo steps that could not be put back, so the script was stopped μια εντολή που απέτυχε άλλαξε βήματα αναίρεσης που δεν ήταν δυνατό να επαναφερθούν, οπότε το σενάριο σταμάτησε @msg a recovery copy could not be written, so the script was not run δεν ήταν δυνατή η εγγραφή αντιγράφου ανάκτησης, οπότε το σενάριο δεν εκτελέστηκε @msg Running a script Εκτελείται σενάριο @msg Copied the script report Η αναφορά του σεναρίου αντιγράφηκε diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 07b6ad664..c6a71b564 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -4085,6 +4085,7 @@ axis Both Ambos @msg a drag or an undo group is open; finish it before running a script hay un arrastre o un grupo de deshacer abierto; termínelo antes de ejecutar un script @msg the script left a document that cannot be kept el script dejó un documento que no se puede conservar @msg the script's run is no longer available la ejecución del script ya no está disponible +@msg a failed command changed undo steps that could not be put back, so the script was stopped un comando fallido cambió pasos de deshacer que no se pudieron restaurar, así que el script se detuvo @msg a recovery copy could not be written, so the script was not run no se pudo escribir una copia de recuperación, así que el script no se ejecutó @msg Running a script Ejecutando un script @msg Copied the script report Se copió el informe del script diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index a2bfbb558..f7e755f0f 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -4309,6 +4309,7 @@ imageTrace Outline Ääriviiva @msg a drag or an undo group is open; finish it before running a script vetäminen tai kumousryhmä on kesken; viimeistele se ennen komentosarjan suorittamista @msg the script left a document that cannot be kept komentosarja jätti asiakirjan, jota ei voi säilyttää @msg the script's run is no longer available komentosarjan suoritus ei ole enää käytettävissä +@msg a failed command changed undo steps that could not be put back, so the script was stopped epäonnistunut komento muutti kumoamisvaiheita, joita ei voitu palauttaa, joten komentosarja pysäytettiin @msg a recovery copy could not be written, so the script was not run palautuskopiota ei voitu kirjoittaa, joten komentosarjaa ei suoritettu @msg Running a script Suoritetaan komentosarjaa @msg Copied the script report Komentosarjan raportti kopioitiin diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 9539af763..5c5159f2d 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -4074,6 +4074,7 @@ axis Both Les deux @msg a drag or an undo group is open; finish it before running a script un glissement ou un groupe d’annulation est ouvert ; terminez-le avant d’exécuter un script @msg the script left a document that cannot be kept le script a laissé un document qui ne peut pas être conservé @msg the script's run is no longer available l’exécution du script n’est plus disponible +@msg a failed command changed undo steps that could not be put back, so the script was stopped une commande en échec a modifié des étapes d’annulation qui n’ont pas pu être rétablies, le script a donc été arrêté @msg a recovery copy could not be written, so the script was not run une copie de récupération n’a pas pu être écrite, le script n’a donc pas été exécuté @msg Running a script Exécution d’un script @msg Copied the script report Rapport du script copié diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index b55907dd3..c535bcf40 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -6146,6 +6146,7 @@ antiAlias Sharp शार्प @msg a drag or an undo group is open; finish it before running a script कोई ड्रैग या पूर्ववत समूह खुला है; स्क्रिप्ट चलाने से पहले उसे पूरा करें @msg the script left a document that cannot be kept स्क्रिप्ट ने ऐसा दस्तावेज़ छोड़ा है जिसे रखा नहीं जा सकता @msg the script's run is no longer available स्क्रिप्ट का रन अब उपलब्ध नहीं है +@msg a failed command changed undo steps that could not be put back, so the script was stopped एक विफल कमांड ने पूर्ववत चरण बदल दिए जिन्हें वापस नहीं रखा जा सका, इसलिए स्क्रिप्ट रोक दी गई @msg a recovery copy could not be written, so the script was not run रिकवरी प्रति नहीं लिखी जा सकी, इसलिए स्क्रिप्ट नहीं चलाई गई @msg Running a script स्क्रिप्ट चल रही है @msg Copied the script report Copied स्क्रिप्ट रिपोर्ट diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index d77cd233a..d7f2c9900 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -4880,6 +4880,7 @@ imageTrace Outline Körvonal @msg a drag or an undo group is open; finish it before running a script húzás vagy visszavonási csoport van folyamatban; fejezze be a parancsfájl futtatása előtt @msg the script left a document that cannot be kept a parancsfájl olyan dokumentumot hagyott hátra, amely nem tartható meg @msg the script's run is no longer available a parancsfájl futása már nem érhető el +@msg a failed command changed undo steps that could not be put back, so the script was stopped egy sikertelen parancs olyan visszavonási lépéseket módosított, amelyeket nem lehetett visszaállítani, ezért a parancsfájl leállt @msg a recovery copy could not be written, so the script was not run a helyreállítási másolatot nem sikerült kiírni, ezért a parancsfájl nem futott le @msg Running a script Parancsfájl futtatása @msg Copied the script report Szkriptjelentés másolva diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index faaadc0fc..7edc0aedf 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -4127,6 +4127,7 @@ axis Both Entrambi @msg a drag or an undo group is open; finish it before running a script è aperto un trascinamento o un gruppo di annullamento; completalo prima di eseguire uno script @msg the script left a document that cannot be kept lo script ha lasciato un documento che non può essere conservato @msg the script's run is no longer available l'esecuzione dello script non è più disponibile +@msg a failed command changed undo steps that could not be put back, so the script was stopped un comando non riuscito ha modificato passaggi di annullamento che non è stato possibile ripristinare, quindi lo script è stato interrotto @msg a recovery copy could not be written, so the script was not run non è stato possibile scrivere una copia di recupero, quindi lo script non è stato eseguito @msg Running a script Esecuzione di uno script @msg Copied the script report Rapporto dello script copiato diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 954cb80f4..04e9c773a 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -4836,6 +4836,7 @@ swatch Bright Violet ビビッドバイオレット @msg a drag or an undo group is open; finish it before running a script ドラッグまたは取り消しグループが開いています。スクリプトを実行する前に完了してください @msg the script left a document that cannot be kept スクリプトが保持できないドキュメントを残しました @msg the script's run is no longer available スクリプトの実行は利用できなくなりました +@msg a failed command changed undo steps that could not be put back, so the script was stopped 失敗したコマンドが元に戻せない取り消し手順を変更したため、スクリプトを停止しました @msg a recovery copy could not be written, so the script was not run 復元用のコピーを書き込めなかったため、スクリプトは実行されませんでした @msg Running a script スクリプトを実行中 @msg Copied the script report スクリプトのレポートをコピーしました diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 06e88aeac..91001ebb5 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5150,6 +5150,7 @@ imageTrace Outline კონტური @msg a drag or an undo group is open; finish it before running a script ღიაა გადათრევა ან გაუქმების ჯგუფი; დაასრულეთ სკრიპტის გაშვებამდე @msg the script left a document that cannot be kept სკრიპტმა დატოვა დოკუმენტი, რომლის შენახვა შეუძლებელია @msg the script's run is no longer available სკრიპტის გაშვება აღარ არის ხელმისაწვდომი +@msg a failed command changed undo steps that could not be put back, so the script was stopped წარუმატებელმა ბრძანებამ შეცვალა გაუქმების ნაბიჯები, რომელთა აღდგენა ვერ მოხერხდა, ამიტომ სკრიპტი შეჩერდა @msg a recovery copy could not be written, so the script was not run აღდგენის ასლის ჩაწერა ვერ მოხერხდა, ამიტომ სკრიპტი არ გაეშვა @msg Running a script სკრიპტი მუშაობს @msg Copied the script report კოპირებულია სკრიპტის ანგარიში diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index fcadee74e..0568e4bd7 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -4763,6 +4763,7 @@ theme Appearance 외관 @msg a drag or an undo group is open; finish it before running a script 드래그 또는 실행 취소 그룹이 열려 있습니다. 스크립트를 실행하기 전에 완료하세요 @msg the script left a document that cannot be kept 스크립트가 유지할 수 없는 문서를 남겼습니다 @msg the script's run is no longer available 스크립트 실행을 더 이상 사용할 수 없습니다 +@msg a failed command changed undo steps that could not be put back, so the script was stopped 실패한 명령이 되돌릴 수 없는 실행 취소 단계를 변경하여 스크립트를 중지했습니다 @msg a recovery copy could not be written, so the script was not run 복구 사본을 쓸 수 없어 스크립트를 실행하지 않았습니다 @msg Running a script 스크립트 실행 중 @msg Copied the script report 스크립트 보고서를 복사했습니다 diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 7c260ffcd..bb7df5984 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -4218,6 +4218,7 @@ imageTrace Outline Kontur @msg a drag or an undo group is open; finish it before running a script otwarte jest przeciąganie lub grupa cofania; zakończ je przed uruchomieniem skryptu @msg the script left a document that cannot be kept skrypt pozostawił dokument, którego nie można zachować @msg the script's run is no longer available uruchomienie skryptu nie jest już dostępne +@msg a failed command changed undo steps that could not be put back, so the script was stopped nieudane polecenie zmieniło kroki cofania, których nie udało się przywrócić, więc skrypt został zatrzymany @msg a recovery copy could not be written, so the script was not run nie udało się zapisać kopii odzyskiwania, więc skrypt nie został uruchomiony @msg Running a script Uruchamianie skryptu @msg Copied the script report Skopiowano raport skryptu diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 15eb0845f..33046d022 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5041,6 +5041,7 @@ axis Both Ambos @msg a drag or an undo group is open; finish it before running a script há um arraste ou um grupo de desfazer aberto; conclua-o antes de executar um script @msg the script left a document that cannot be kept o script deixou um documento que não pode ser mantido @msg the script's run is no longer available a execução do script não está mais disponível +@msg a failed command changed undo steps that could not be put back, so the script was stopped um comando que falhou alterou etapas de desfazer que não puderam ser restauradas, então o script foi interrompido @msg a recovery copy could not be written, so the script was not run não foi possível gravar uma cópia de recuperação, portanto o script não foi executado @msg Running a script Executando um script @msg Copied the script report Relatório do script copiado diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 8d2d42272..421623c3f 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -4121,6 +4121,7 @@ axis Both Оба @msg a drag or an undo group is open; finish it before running a script открыто перетаскивание или группа отмены; завершите их перед запуском скрипта @msg the script left a document that cannot be kept скрипт оставил документ, который нельзя сохранить @msg the script's run is no longer available выполнение скрипта больше недоступно +@msg a failed command changed undo steps that could not be put back, so the script was stopped неудавшаяся команда изменила шаги отмены, которые не удалось восстановить, поэтому скрипт остановлен @msg a recovery copy could not be written, so the script was not run не удалось записать копию для восстановления, поэтому скрипт не выполнен @msg Running a script Выполняется скрипт @msg Copied the script report Отчёт о сценарии скопирован diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 76bee54a5..55139c2db 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -3995,6 +3995,7 @@ axis Both Båda @msg a drag or an undo group is open; finish it before running a script en dragning eller ångragrupp är öppen; avsluta den innan du kör ett skript @msg the script left a document that cannot be kept skriptet lämnade ett dokument som inte kan behållas @msg the script's run is no longer available skriptets körning är inte längre tillgänglig +@msg a failed command changed undo steps that could not be put back, so the script was stopped ett misslyckat kommando ändrade ångrasteg som inte kunde återställas, så skriptet stoppades @msg a recovery copy could not be written, so the script was not run en återställningskopia kunde inte skrivas, så skriptet kördes inte @msg Running a script Kör ett skript @msg Copied the script report Kopierade skriptrapporten diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 1d6d077f6..1011a62b7 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -3877,6 +3877,7 @@ axis Both Her İkisi @msg a drag or an undo group is open; finish it before running a script bir sürükleme veya geri alma grubu açık; betiği çalıştırmadan önce tamamlayın @msg the script left a document that cannot be kept betik, saklanamayan bir belge bıraktı @msg the script's run is no longer available betiğin çalıştırması artık kullanılamıyor +@msg a failed command changed undo steps that could not be put back, so the script was stopped başarısız bir komut geri yüklenemeyen geri alma adımlarını değiştirdi, bu yüzden betik durduruldu @msg a recovery copy could not be written, so the script was not run kurtarma kopyası yazılamadı, bu nedenle betik çalıştırılmadı @msg Running a script Betik çalıştırılıyor @msg Copied the script report Komut dosyası raporu kopyalandı diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 756803b23..37c68f7ef 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -4111,6 +4111,7 @@ axis Both Обидві @msg a drag or an undo group is open; finish it before running a script відкрито перетягування або групу скасування; завершіть їх перед запуском скрипта @msg the script left a document that cannot be kept скрипт залишив документ, який не можна зберегти @msg the script's run is no longer available виконання скрипта більше не доступне +@msg a failed command changed undo steps that could not be put back, so the script was stopped невдала команда змінила кроки скасування, які не вдалося відновити, тому скрипт зупинено @msg a recovery copy could not be written, so the script was not run не вдалося записати копію для відновлення, тому скрипт не виконано @msg Running a script Виконується скрипт @msg Copied the script report Звіт про сценарій скопійовано diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index a728220dc..ec51dce96 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -4529,6 +4529,7 @@ theme Appearance Giao diện @msg a drag or an undo group is open; finish it before running a script đang có thao tác kéo hoặc nhóm hoàn tác; hãy hoàn thành trước khi chạy tập lệnh @msg the script left a document that cannot be kept tập lệnh để lại một tài liệu không thể giữ @msg the script's run is no longer available lần chạy tập lệnh không còn khả dụng +@msg a failed command changed undo steps that could not be put back, so the script was stopped một lệnh thất bại đã thay đổi các bước hoàn tác không thể khôi phục, nên tập lệnh đã bị dừng @msg a recovery copy could not be written, so the script was not run không thể ghi bản sao khôi phục nên tập lệnh không được chạy @msg Running a script Đang chạy tập lệnh @msg Copied the script report Đã sao chép báo cáo script diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index e26fcf6ed..cde1e1e87 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -4543,6 +4543,7 @@ imageTrace Outline 轮廓 @msg a drag or an undo group is open; finish it before running a script 拖动或撤销组尚未结束;请在运行脚本前完成 @msg the script left a document that cannot be kept 脚本留下了无法保留的文档 @msg the script's run is no longer available 脚本运行已不可用 +@msg a failed command changed undo steps that could not be put back, so the script was stopped 失败的命令更改了无法恢复的撤销步骤,因此脚本已停止 @msg a recovery copy could not be written, so the script was not run 无法写入恢复副本,因此未运行脚本 @msg Running a script 正在运行脚本 @msg Copied the script report 已复制脚本报告 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index f8cea77f3..d80047d06 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2387,6 +2387,7 @@ file-filter Templates 範本 @msg a drag or an undo group is open; finish it before running a script 拖曳或還原群組尚未結束;請在執行指令碼前完成 @msg the script left a document that cannot be kept 指令碼留下了無法保留的文件 @msg the script's run is no longer available 指令碼執行已無法使用 +@msg a failed command changed undo steps that could not be put back, so the script was stopped 失敗的命令變更了無法還原的復原步驟,因此指令碼已停止 @msg a recovery copy could not be written, so the script was not run 無法寫入復原副本,因此未執行指令碼 @msg Running a script 正在執行指令碼 @msg Copied the script report 已複製指令碼報告 diff --git a/docs/mcp.md b/docs/mcp.md index ade82a6d2..6e78d8522 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -1527,8 +1527,9 @@ change anything outside the documents: `script.*` except `script.check`, `plugin `shortcutSet.*`, `help.*`, `print.*`, `library.*`, `importMatches.*`, `edit.undoGroup.*`, `file.recovery.*`, `stroke.widthProfile.*`, saving, importing or deleting a preset, `text.addFontFiles`, `text.findFontFiles`, `text.rescanFonts`, `links.findFiles`, `file.place.queue`, `color.loadProfile`, `color.removeProfile`, `file.print`, -`view.proofSetup`, `view.proofColors`, `view.overprintPreview` and `view.separationsPreview`. Calling one throws an -error. With Data Recovery off for complex documents, a run in the web build does not start while a complex document +`view.proofSetup`, `view.proofColors`, `view.overprintPreview` and `view.separationsPreview`. It also cannot run +`edit.clearHistory`: a run keeps each document's undo steps from before it and adds one. Calling one of these throws +an error. With Data Recovery off for complex documents, a run in the web build does not start while a complex document has unsaved changes. VectorCraft sets these limits itself: