mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-30 13:26:54 -06:00
#1460: commands and tools that pass their params on list what they pass
file.placeImage runs image.place's code and file.exportSvg file.export's, so their docs now list the names those read (width, height, target, mode; scale or size and the export options). MCP's create_node passes points, closed and select to node.create, and pointer and key read ctrl, so their schemas list them too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
27c11858e1
commit
3567bef862
@@ -118,13 +118,19 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
false,
|
||||
export_layers
|
||||
),
|
||||
spec!("file.exportSvg", "Export selection as SVG", "{ids?, path?}", false, |s, p| {
|
||||
let mut q = p.clone();
|
||||
if let Some(o) = q.as_object_mut() {
|
||||
o.insert("format".into(), json!("svg"));
|
||||
spec!(
|
||||
"file.exportSvg",
|
||||
"Export selection as SVG",
|
||||
"{ids?, path?, scale? | size?, contentsOnly?, includeBoundingBox?, svgOutlineText?, svgIdAttribute?, svgSimplifyStroke? (as file.export)}",
|
||||
false,
|
||||
|s, p| {
|
||||
let mut q = p.clone();
|
||||
if let Some(o) = q.as_object_mut() {
|
||||
o.insert("format".into(), json!("svg"));
|
||||
}
|
||||
export(s, &q)
|
||||
}
|
||||
export(s, &q)
|
||||
}),
|
||||
),
|
||||
spec!(
|
||||
"file.exportFramesPdf",
|
||||
"Export frames to PDF",
|
||||
@@ -139,7 +145,13 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
true,
|
||||
import
|
||||
),
|
||||
spec!("file.placeImage", "Place image", "{path | data, x?, y?}", true, super::create::place_image),
|
||||
spec!(
|
||||
"file.placeImage",
|
||||
"Place image",
|
||||
"{path | data, x?, y?, width?, height?, parent?, name?, target?, mode? (as image.place)}",
|
||||
true,
|
||||
super::create::place_image
|
||||
),
|
||||
spec!("file.close", "Close tab", "{index?, force? (discard unsaved changes)}", false, close),
|
||||
spec!("file.moveTab", "Move tab", "{from, to} (indices among open documents)", false, |s, p| {
|
||||
let from = req_index("file.moveTab", p, "from")?;
|
||||
|
||||
@@ -51,6 +51,9 @@ fn documented_alternatives_and_empty_params_are_taken() {
|
||||
assert_eq!(param_names("proto.interaction.add").map(|n| n.contains(&"delayMs") && n.contains(&"dest")), Some(true));
|
||||
assert_eq!(param_names("collab.join"), Some(&["code", "link"][..]));
|
||||
assert_eq!(param_names("proto.interaction.set").map(|n| n.contains(&"trigger") && n.contains(&"interaction")), Some(true));
|
||||
// Commands that run another command's code take its names too.
|
||||
assert_eq!(param_names("file.placeImage").map(|n| ["width", "height", "target", "mode"].iter().all(|k| n.contains(k))), Some(true));
|
||||
assert_eq!(param_names("file.exportSvg").map(|n| ["scale", "size", "contentsOnly", "svgOutlineText"].iter().all(|k| n.contains(k))), Some(true));
|
||||
// Alternative groups: `{x, y} | {points: …}`.
|
||||
assert_eq!(param_names("vector.edit.paint"), Some(&["x", "y", "points"][..]));
|
||||
assert_eq!(param_names("vector.edit.cut"), Some(&["segment", "t", "vertex", "from", "to"][..]));
|
||||
|
||||
@@ -154,6 +154,9 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
"name": string("Layer name"),
|
||||
"text": string("Characters, for type text"),
|
||||
"path": string("SVG path data, for type vector (e.g. \"M0 0 L100 0 L50 80 Z\")"),
|
||||
"points": {"type": "array", "items": {"type": "array", "items": {"type": "number"}}, "description": "For type vector, instead of path: the points [[x, y], ...]"},
|
||||
"closed": boolean("For type vector: close the path"),
|
||||
"select": boolean("Select the new layer (default true)"),
|
||||
"props": {"type": "object", "description": "Any set_properties keys applied at creation, e.g. {\"fill\":\"#3366FF\",\"cornerRadius\":12,\"fontSize\":24,\"layout\":\"vertical\",\"gap\":8,\"padding\":16}"},
|
||||
}),
|
||||
&["type"],
|
||||
@@ -288,6 +291,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
"shift": boolean("Shift held"),
|
||||
"alt": boolean("Alt/Option held"),
|
||||
"cmd": boolean("Cmd (macOS) / Ctrl held"),
|
||||
"ctrl": boolean("Ctrl held (the Control key on macOS)"),
|
||||
"pressure": num("Pen pressure 0-1 (the Pencil records it as the stroke's width profile; a drag starts with it)"),
|
||||
"pressureTo": num("Pen pressure 0-1 at a drag's end (interpolated along it)"),
|
||||
}),
|
||||
@@ -300,7 +304,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
"Press key",
|
||||
"Press a key or shortcut as if typed on the canvas: a menu shortcut (e.g. {\"key\":\"G\",\"cmd\":true} groups, {\"key\":\"A\",\"alt\":true,\"shift\":false} …), a tool key (R, O, T, F, V…), Escape, Delete, Enter, arrows (nudge; shift = big nudge). While editing text, keys go to the text editor (Backspace, Delete, Left, Right, Home, End, Enter, Escape, SelectAll).",
|
||||
obj(
|
||||
json!({"key": string("Key name: a letter/digit, Escape, Delete, Backspace, Enter, ArrowLeft…"), "shift": boolean("Shift"), "alt": boolean("Alt/Option"), "cmd": boolean("Cmd/Ctrl")}),
|
||||
json!({"key": string("Key name: a letter/digit, Escape, Delete, Backspace, Enter, ArrowLeft…"), "shift": boolean("Shift"), "alt": boolean("Alt/Option"), "cmd": boolean("Cmd/Ctrl"), "ctrl": boolean("Ctrl (the Control key on macOS)")}),
|
||||
&["key"],
|
||||
),
|
||||
false,
|
||||
|
||||
@@ -91,6 +91,10 @@ fn tools_call_happy_and_error_paths() {
|
||||
assert_eq!(r["error"]["code"], -32602);
|
||||
// The document is unharmed by all of that.
|
||||
assert_eq!(c.page_children().len(), 1);
|
||||
// Arguments the tools pass on are in their schemas, so they still go through.
|
||||
c.ok("create_node", json!({"type": "vector", "points": [[0, 0], [10, 0], [5, 8]], "closed": true, "select": false}));
|
||||
c.ok("key", json!({"key": "Escape", "ctrl": false}));
|
||||
c.ok("pointer", json!({"kind": "hover", "x": 1, "y": 1, "ctrl": false}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user