Observed
Changing the shape: of an existing node via canvas_write_d2 is a no-op — the diff engine returns ops_applied: 0, warnings: [] and the node's Figma shapeType is unchanged.
Repro
- Create a node with
shape: diamond (e.g. "C2F indexing" { shape: diamond }).
- Call
canvas_write_d2 with the same full D2, only changing shape: diamond → shape: rounded_rectangle.
- Result:
ops_applied: 0. Canvas unchanged.
Expected
Shape property changes on existing shape_with_text nodes should be applied — either:
- emitted by the D2 diff engine as a
set_shape op, or
- exposed as an explicit
apply_patch op.
Root cause
bridge/d2-diff.js doesn't treat shape: differences on existing nodes as a diff (only create/delete/rename/reparent/edge changes are emitted).
plugin/code.js sets shape.shapeType only at node creation (lines ~521, ~1163, ~1395). No handler exists for a post-creation shape mutation.
SHAPE_MAP already exists in bridge/d2-vocabulary.js — the D2 keyword ↔ Figma shapeType mapping is in place.
Feasibility
Figma plugin API allows mutating shape.shapeType on an existing ShapeWithTextNode, so this is a pure bridge/plugin-side change. No roundtrip or geometry implications (post-processor can reroute edges if shape bounds shift via canvas_relayout).
Proposed implementation
apply_patch op:
{ type: 'set_shape', node_id, after: 'ROUNDED_RECTANGLE' }
Handler in plugin's ops.forEach that guards node.type === 'SHAPE_WITH_TEXT' and sets node.shapeType = op.after.
d2-diff.js: when an existing node's D2 shape: keyword differs from its current shapeType, emit a set_shape op (map via SHAPE_MAP).
- Validator: reject set_shape on non-shape_with_text nodes (SECTION, CONNECTOR, etc.).
Workaround today
Delete the node (omit from target_d2) → re-add with the new shape in a second write. Loses position; must follow with canvas_relayout. Or change the shape manually in FigJam.
Observed
Changing the
shape:of an existing node viacanvas_write_d2is a no-op — the diff engine returnsops_applied: 0, warnings: []and the node's FigmashapeTypeis unchanged.Repro
shape: diamond(e.g."C2F indexing" { shape: diamond }).canvas_write_d2with the same full D2, only changingshape: diamond→shape: rounded_rectangle.ops_applied: 0. Canvas unchanged.Expected
Shape property changes on existing
shape_with_textnodes should be applied — either:set_shapeop, orapply_patchop.Root cause
bridge/d2-diff.jsdoesn't treatshape:differences on existing nodes as a diff (only create/delete/rename/reparent/edge changes are emitted).plugin/code.jssetsshape.shapeTypeonly at node creation (lines ~521, ~1163, ~1395). No handler exists for a post-creation shape mutation.SHAPE_MAPalready exists inbridge/d2-vocabulary.js— the D2 keyword ↔ Figma shapeType mapping is in place.Feasibility
Figma plugin API allows mutating
shape.shapeTypeon an existingShapeWithTextNode, so this is a pure bridge/plugin-side change. No roundtrip or geometry implications (post-processor can reroute edges if shape bounds shift viacanvas_relayout).Proposed implementation
apply_patchop:node.type === 'SHAPE_WITH_TEXT'and setsnode.shapeType = op.after.d2-diff.js: when an existing node's D2shape:keyword differs from its currentshapeType, emit aset_shapeop (map via SHAPE_MAP).Workaround today
Delete the node (omit from target_d2) → re-add with the new shape in a second write. Loses position; must follow with
canvas_relayout. Or change the shape manually in FigJam.