mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
MCP: headless open_file reads .pdf and PDF-compatible .ai, as File ▸ Open does
Headless `open_file` (and `file.open` through run_command) had its own loader that stopped at .vectorcraft and .svg, so an agent could not open a PDF or an .ai file without the desktop app, although the engine and `vectorcraft-cli` already import them through vectorcraft-pdf. The headless loader now imports .pdf, .ai and any file starting with %PDF through vectorcraft_pdf::import_with_report (default options, as File ▸ Open uses), and returns the import warnings like the SVG path. The open_file tool description lists the new types. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
604c20ed90
commit
1fd9b79a9d
5 files changed
+24
-3
No files matched your search
Generated
+1
@@ -4185,6 +4185,7 @@ dependencies = [
|
||||
"vectorcraft-engine",
|
||||
"vectorcraft-format",
|
||||
"vectorcraft-geom",
|
||||
"vectorcraft-pdf",
|
||||
"vectorcraft-render",
|
||||
"vectorcraft-svg",
|
||||
"vectorcraft-testkit",
|
||||
|
||||
@@ -12,6 +12,7 @@ workspace = true
|
||||
vectorcraft-engine = { workspace = true }
|
||||
vectorcraft-render = { workspace = true }
|
||||
vectorcraft-svg = { workspace = true }
|
||||
vectorcraft-pdf = { workspace = true }
|
||||
vectorcraft-format = { workspace = true }
|
||||
vectorcraft-tools = { workspace = true }
|
||||
vectorcraft-geom = { workspace = true }
|
||||
|
||||
@@ -215,7 +215,7 @@ impl Headless {
|
||||
}
|
||||
}
|
||||
|
||||
/// `app.open {path}`: `.vectorcraft` or `.svg` as a new active document.
|
||||
/// `app.open {path}`: `.vectorcraft`, `.svg`, `.pdf` or PDF-compatible `.ai` as a new active document.
|
||||
pub fn open(&mut self, p: &Value) -> Result<Value, String> {
|
||||
let path = s(p, "path").ok_or("missing `path`")?;
|
||||
let bytes = std::fs::read(path).map_err(|e| format!("read {path}: {e}"))?;
|
||||
@@ -234,8 +234,15 @@ impl Headless {
|
||||
d.title = name.clone();
|
||||
warnings = w;
|
||||
(d, false)
|
||||
} else if e == "pdf" || e == "ai" || bytes.starts_with(b"%PDF") {
|
||||
// PDF, or the PDF-compatible part of an Illustrator `.ai`, as in the app's File ▸ Open.
|
||||
let r = vectorcraft_pdf::import_with_report(&bytes, &vectorcraft_pdf::ImportOptions::default()).map_err(|e| e.to_string())?;
|
||||
let mut d = r.document;
|
||||
d.title = name.clone();
|
||||
warnings = r.warnings;
|
||||
(d, false)
|
||||
} else {
|
||||
return Err(format!("headless mode can open .vectorcraft and .svg files, not .{e}"));
|
||||
return Err(format!("headless mode can open .vectorcraft, .svg, .pdf and .ai files, not .{e}"));
|
||||
};
|
||||
let index = self.session.add_document(doc, keep_path.then(|| path.to_string()));
|
||||
Ok(json!({"index": index, "title": name, "warnings": warnings}))
|
||||
|
||||
@@ -200,6 +200,18 @@ fn headless_end_to_end() {
|
||||
let r = call(&mut s, 9, "open_file", json!({"path": svg_path.to_str().unwrap()}));
|
||||
assert_eq!(r["isError"], false, "{r}");
|
||||
|
||||
// PDF and PDF-compatible .ai open headless too.
|
||||
let pdf_path = tmp("out.pdf");
|
||||
assert_eq!(call(&mut s, 20, "export", json!({"path": pdf_path.to_str().unwrap()}))["isError"], false);
|
||||
let ai_path = tmp("out.ai");
|
||||
std::fs::copy(&pdf_path, &ai_path).unwrap();
|
||||
for (id, p) in [(21, &pdf_path), (22, &ai_path)] {
|
||||
let r = call(&mut s, id, "open_file", json!({"path": p.to_str().unwrap()}));
|
||||
assert_eq!(r["isError"], false, "{r}");
|
||||
let doc: Value = serde_json::from_str(&text_of(&call(&mut s, id + 10, "inspect_document", json!({})))).unwrap();
|
||||
assert!(doc["objects"].as_u64().unwrap() >= 2, "{doc}");
|
||||
}
|
||||
|
||||
// Resources.
|
||||
let v = rpc(&mut s, 10, "resources/list", json!({}));
|
||||
assert_eq!(v["result"]["resources"].as_array().unwrap().len(), 2);
|
||||
|
||||
@@ -246,7 +246,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
tool(
|
||||
"open_file",
|
||||
"Open file",
|
||||
"Open a .vectorcraft or .svg file as a new, active document.",
|
||||
"Open a .vectorcraft, .svg, .pdf or PDF-compatible .ai file as a new, active document.",
|
||||
obj(json!({"path": string("File path")}), &["path"]),
|
||||
false,
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user