From a670c3072bebef442cd7654d2b4595bf27d83642 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 13:18:17 -0800 Subject: [PATCH] MCP: document.json names linked JavaScript files outside the read roots by file name With automation read roots in force, document.json returned each entry of svg_scripts unchanged, so a JavaScript file linked from outside the read roots kept its full path in the reply. The reply copy now names such a file by its file name alone. An entry given as a URL is kept, and the stored document is not changed. --- crates/engine/src/cmd/edit.rs | 2 +- crates/engine/src/cmd/links.rs | 7 ++++++- crates/engine/src/tests/placed_document.rs | 11 ++++++++++- docs/mcp.md | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 157049aa7c..41949ddeb9 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -50,7 +50,7 @@ pub fn specs() -> Vec { has_doc, doc_compare ), - cmd!(query "document.json", "Document JSON", [], None, "{} → complete document model (with automation read roots, a linked or placed file outside them is named by its file name alone, without its relative path, size or date, in brush art too, and so is a data set's image value outside them; a relative path that leads outside them is left out; a stored brush entry that does not read as a brush is left out; a folder outside them is left out of export_settings)", has_doc, |s, _| { + cmd!(query "document.json", "Document JSON", [], None, "{} → complete document model (with automation read roots, a linked or placed file outside them is named by its file name alone, without its relative path, size or date, in brush art too, and so are a data set's image value and a linked JavaScript file in svg_scripts outside them (a URL is kept); a relative path that leads outside them is left out; a stored brush entry that does not read as a brush is left out; a folder outside them is left out of export_settings)", has_doc, |s, _| { let st = s.doc()?; let d = super::links::with_hidden_paths_named(&st.doc, super::links::LinkBase::of(st)); Ok(serde_json::to_value(&*d).unwrap_or(Value::Null)) diff --git a/crates/engine/src/cmd/links.rs b/crates/engine/src/cmd/links.rs index 162bb679f6..ab6dd3e56a 100644 --- a/crates/engine/src/cmd/links.rs +++ b/crates/engine/src/cmd/links.rs @@ -1200,7 +1200,8 @@ pub(crate) fn with_found_paths(d: &mut Document, base: LinkBase) { /// `d`, a document saved at `doc_path`, as a reply to the caller asking now may show it: without /// read roots in force, `d` itself; with them, a copy in which each link [`path_shown`] does not /// allow names its file alone, without its relative path or the size and date kept of its file, -/// and each data set's image value it does not allow names its file alone too. The folder Export +/// and each data set's image value and linked JavaScript file (`svg_scripts`) it does not allow +/// names its file alone too, while a JavaScript file given as a URL stays as it is. The folder Export /// for Screens last wrote to is left out of the export settings when it is outside the read roots. /// A link whose path is allowed but whose relative path leads outside the read roots loses its /// relative path. Links in the layers, symbols, graph designs, patterns, the pattern being edited @@ -1266,6 +1267,10 @@ pub(crate) fn with_hidden_paths_named<'a>(d: &'a Document, base: LinkBase) -> Co } } } + let script_hidden = |f: &str| !path_shown(f, doc_path) && !matches!(fileio::document_path(f, doc_path), Err(craft_docpath::Refused::Url)); + for f in d.svg_scripts.iter_mut().filter(|f| script_hidden(f)) { + *f = LinkInfo::new(f.as_str()).name().to_string(); + } Cow::Owned(d) } diff --git a/crates/engine/src/tests/placed_document.rs b/crates/engine/src/tests/placed_document.rs index 1a6ee921f2..2420ef265c 100644 --- a/crates/engine/src/tests/placed_document.rs +++ b/crates/engine/src/tests/placed_document.rs @@ -1102,6 +1102,13 @@ fn document_replies_leave_out_paths_outside_the_read_roots() { s.execute("variable.define", &json!({"name": "Photo", "kind": "image"})).unwrap(); s.execute("dataset.new", &json!({"name": "Row", "values": {"Photo": pic}})).unwrap(); let image = r["ids"][0].as_u64().unwrap(); + // A linked JavaScript file outside the read roots, and one given as a URL. + let script = away.file("menu.js"); + write(&script, b"void 0;"); + let url = "https://example.com/scripts/menu.js"; + for file in [script.as_str(), url] { + s.execute("svgInteractivity.addFile", &json!({"file": file})).unwrap(); + } let poster = away.file("poster.vectorcraft"); save(&mut s, &poster); // The folder Export for Screens wrote to, and a pattern of the linked image being edited: its @@ -1124,11 +1131,13 @@ fn document_replies_leave_out_paths_outside_the_read_roots() { assert!(model.contains(&format!("\"path\":\"{name}\"")), "{name}: {model}"); } assert!(model.contains("{\"image\":\"photo.png\"}"), "{model}"); + assert!(model.contains(&format!("\"svg_scripts\":[\"menu.js\",\"{url}\"]")), "{model}"); // The document keeps its links, and a caller whose read roots hold the files sees every path. assert_eq!(placed(&s, id).link.path, src); + assert_eq!(s.doc().unwrap().doc.svg_scripts, [script.as_str(), url]); for model in [ask(&mut s, &away.0, "document.json"), s.execute("document.json", &json!({})).unwrap()] { let model = model.to_string(); - for path in [&src, &pic, &out] { + for path in [&src, &pic, &out, &script] { assert!(model.contains(&json!(path).to_string()), "{path}: {model}"); } } diff --git a/docs/mcp.md b/docs/mcp.md index 9191ab8bd7..43840880bd 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -227,7 +227,8 @@ the suite) keep the agent to the folders you give it: leaves out the link's `relative`, `size` and `modified`. This covers the art of the brushes in `unknown.brushes` too. A link whose file is inside them keeps its `path` but leaves out a `relative` that leads outside them. A stored brush entry that does not read as a brush is left out of `unknown.brushes`. `document.json` also names a data set's image - value outside them by its file name alone, and leaves out the `folder` of `export_settings` (the settings Export + value and a linked JavaScript file in `svg_scripts` outside them by its file name alone (a JavaScript file given + as a URL is kept), and leaves out the `folder` of `export_settings` (the settings Export for Screens last used) when that folder is outside them. The document itself is not changed. - `plugin.list`, `plugin.safeMode`, `file.recovery.*` and `ui_inspect`'s `scriptReport` leave out the paths outside the read roots, also those the person or an earlier request put there. In `plugin.list`, the plug-in