mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
#400: a nested batch keeps its own step references
The outer batch passes a command.batch step on as written, so the inner steps' `$step` references point at the inner batch's steps. The docs say that `$step` is a reserved key in step params.
This commit is contained in:
@@ -89,3 +89,24 @@ fn text_that_looks_like_a_reference_is_left_alone() {
|
||||
let n = s.doc().unwrap().node(id(&results[0])).unwrap().clone();
|
||||
assert!(matches!(n.kind, figcraft_doc::NodeKind::Text(t) if t.characters == "$step 0 {\"$step\": 0}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_nested_batch_refers_to_its_own_steps() {
|
||||
let mut s = Session::new();
|
||||
let frame = |name: &str| json!({"command": "node.create", "params": {"type": "frame", "name": name, "width": 100, "height": 100}});
|
||||
let results = s
|
||||
.execute(
|
||||
"command.batch",
|
||||
&json!({"commands": [
|
||||
frame("Outer"),
|
||||
{"command": "command.batch", "params": {"commands": [
|
||||
frame("Inner"),
|
||||
{"command": "node.create", "params": {"type": "rectangle", "parent": {"$step": 0}, "width": 10, "height": 10}}
|
||||
]}}
|
||||
]}),
|
||||
)
|
||||
.unwrap();
|
||||
let inner = &results[1];
|
||||
let rect = s.doc().unwrap().node(id(&inner[1])).unwrap().clone();
|
||||
assert_eq!(rect.parent, Some(id(&inner[0])), "in the nested batch's own first frame, not the outer one");
|
||||
}
|
||||
|
||||
@@ -1124,12 +1124,14 @@ impl Session {
|
||||
|
||||
/// Run several commands as one undo step. A param value `{"$step": n}` is replaced by the `id`
|
||||
/// in the result of the earlier step `n` (`{"$step": n, "field": "…"}` picks another field),
|
||||
/// so a step can target a layer that an earlier step of the same batch created.
|
||||
/// so a step can target a layer that an earlier step of the same batch created. Any object
|
||||
/// with a `$step` key in a step's params is such a reference, except inside a nested
|
||||
/// `command.batch` step: its references are to its own steps.
|
||||
pub fn batch(&mut self, label: &str, cmds: &[(String, Value)]) -> Result<Vec<Value>> {
|
||||
self.grouped(label, |s| {
|
||||
let mut results: Vec<Value> = Vec::with_capacity(cmds.len());
|
||||
for (step, (id, p)) in cmds.iter().enumerate() {
|
||||
let p = resolve_step_refs(p, &results, step, 0)?;
|
||||
let p = if id == "command.batch" { p.clone() } else { resolve_step_refs(p, &results, step, 0)? };
|
||||
results.push(s.execute(id, &p)?);
|
||||
}
|
||||
Ok(results)
|
||||
|
||||
@@ -63,8 +63,9 @@ JSON or the gradient shorthand `{"type":"linearGradient","stops":["#hex","#hex"]
|
||||
```
|
||||
|
||||
Inside `command.batch`, `{"$step": n}` in a step's params stands for the `id` in step `n`'s result
|
||||
(zero-based; `{"$step": n, "field": "…"}` for another field), so one batch can create a frame and
|
||||
fill it:
|
||||
(zero-based; `{"$step": n, "field": "…"}` for another field; any object with a `$step` key is read
|
||||
as one, and a nested `command.batch` step keeps its own references to its own steps), so one batch
|
||||
can create a frame and fill it:
|
||||
|
||||
```json
|
||||
{"id":4,"method":"engine.execute","params":{"command":"command.batch","params":{"commands":[{"command":"node.create","params":{"type":"frame","width":200,"height":100}},{"command":"node.create","params":{"type":"rectangle","parent":{"$step":0},"width":20,"height":20}}]}}}
|
||||
|
||||
+4
-1
@@ -1532,7 +1532,10 @@ result. This builds a nested tree in one call and one undo step, without guessin
|
||||
|
||||
A reference to the same or a later step, a negative or non-integer index, a missing `field` or any
|
||||
other key next to `$step` fails the whole batch, which is rolled back. Only an object whose keys are
|
||||
`$step` (and optionally `field`) is a reference: text such as `"$step 0"` is left as written.
|
||||
`$step` (and optionally `field`) is a reference: text such as `"$step 0"` is left as written. The
|
||||
`$step` key is reserved in step params: an object holding it anywhere (in plugin data or a variable
|
||||
value too) is read as a reference. A step that is itself a `command.batch` is passed on as written,
|
||||
so the references in its steps are to its own steps, not the outer batch's.
|
||||
|
||||
The end-to-end tests in `crates/mcp/tests/agent_tasks.rs` are worked examples of realistic tasks
|
||||
(auto layout, a card built in one batch with step references, components and overrides, gradients
|
||||
|
||||
Reference in New Issue
Block a user