Scripts: File › Scripts › Other Script… runs JavaScript files

File › Scripts › Other Script… picks a .js or .jsx file, shows a warning
that can be turned off in Preferences › General, and runs the script
against the open documents through the `vc` object. The new
`script.run` and `script.stop` commands run and stop a script over MCP.

- A run is one transaction. Each document it changed gets one undo step
  named Run Script. An uncaught error, a limit or a stop puts every
  document, the paint, the clipboard, menu state and preferences back.
- A command that fails inside a run, or whose result is too large to
  reach the script, is undone and throws an error the script can catch.
- Files a command wrote stay on disk and the report names them, also
  when the run is stopped from the quit question.
- While a script runs, other commands that change anything fail with a
  busy error; MCP reports it as JSON-RPC error -32003.
- In the app the run advances one slice per frame with a Stop button in
  the status bar. A run that adds a great many objects asks whether to
  continue; Stop and Escape end it.
- Commands that change state outside the documents, such as prefs.*,
  plugin.* and file.place.queue, are refused to scripts.

Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-11-09 18:35:41 -08:00
parent 25cfa9d3cf
commit 2c0d09e079
77 changed files with 4093 additions and 58 deletions
+6
View File
@@ -1,5 +1,8 @@
use super::{NodeId, cancel};
/// The text of [`EngineError::Busy`], which ends every error a frontend reports for it.
pub const SCRIPT_BUSY: &str = "a script is running: stop it or wait for it to finish";
#[derive(Debug, thiserror::Error)]
pub enum EngineError {
#[error("unknown command `{0}`")]
@@ -20,6 +23,9 @@ pub enum EngineError {
/// The caller asked for the command to stop ([`cancel`]). It changed nothing.
#[error("cancelled; nothing was changed")]
Cancelled,
/// A running script holds the documents until it ends. Nothing was changed.
#[error("{}", SCRIPT_BUSY)]
Busy,
/// A bug: the command panicked. The document was rolled back to its state before the command.
#[error("internal error in `{cmd}`: {msg} (the document was left as it was before; please report this bug)")]
Internal { cmd: String, msg: String },