@pixerate/editor 0.223.8 → 0.223.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools/skill-evals.d.cts +8 -1
- package/dist/tools/skill-evals.d.ts +8 -1
- package/package.json +1 -1
- package/skills/README.md +4 -1
- package/skills/pixerate-canvas/SKILL.md +8 -0
- package/skills/pixerate-canvas-design-system-components/SKILL.md +103 -0
- package/skills/pixerate-canvas-design-system-components/evals/add-variant.json +172 -0
- package/skills/pixerate-canvas-design-system-components/evals/combine-variants.json +172 -0
- package/skills/pixerate-canvas-design-system-components/evals/make-component.json +103 -0
- package/skills/pixerate-canvas-layout/SKILL.md +95 -0
- package/skills/pixerate-canvas-layout/evals/align-tops.json +98 -0
- package/skills/pixerate-canvas-layout/evals/distribute-evenly.json +98 -0
- package/skills/pixerate-canvas-layout/evals/order-and-lock.json +110 -0
|
@@ -20,7 +20,7 @@ export interface SkillEvalCheck extends SkillEvalToolCall {
|
|
|
20
20
|
}
|
|
21
21
|
export interface SkillEval {
|
|
22
22
|
prompt: string;
|
|
23
|
-
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture;
|
|
23
|
+
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture | CanvasEvalFixture;
|
|
24
24
|
checks: SkillEvalCheck[];
|
|
25
25
|
solution: SkillEvalToolCall[];
|
|
26
26
|
}
|
|
@@ -109,6 +109,13 @@ export interface EffectEvalFixture {
|
|
|
109
109
|
/** Tool calls that prepare the session, run in order. */
|
|
110
110
|
setup?: SkillEvalToolCall[];
|
|
111
111
|
}
|
|
112
|
+
export interface CanvasEvalFixture {
|
|
113
|
+
editor: 'canvas';
|
|
114
|
+
/** The starting nodes (React Flow style: `id`, `type`, `position`, `width`, `height`, `data`, `parentId`). */
|
|
115
|
+
nodes: Array<Record<string, unknown>>;
|
|
116
|
+
/** Tool calls that prepare the canvas (components, instances), run in order. */
|
|
117
|
+
setup?: SkillEvalToolCall[];
|
|
118
|
+
}
|
|
112
119
|
/** One eval file of a skill. */
|
|
113
120
|
export interface SkillEvalFile {
|
|
114
121
|
skill: string;
|
|
@@ -20,7 +20,7 @@ export interface SkillEvalCheck extends SkillEvalToolCall {
|
|
|
20
20
|
}
|
|
21
21
|
export interface SkillEval {
|
|
22
22
|
prompt: string;
|
|
23
|
-
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture;
|
|
23
|
+
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture | CanvasEvalFixture;
|
|
24
24
|
checks: SkillEvalCheck[];
|
|
25
25
|
solution: SkillEvalToolCall[];
|
|
26
26
|
}
|
|
@@ -109,6 +109,13 @@ export interface EffectEvalFixture {
|
|
|
109
109
|
/** Tool calls that prepare the session, run in order. */
|
|
110
110
|
setup?: SkillEvalToolCall[];
|
|
111
111
|
}
|
|
112
|
+
export interface CanvasEvalFixture {
|
|
113
|
+
editor: 'canvas';
|
|
114
|
+
/** The starting nodes (React Flow style: `id`, `type`, `position`, `width`, `height`, `data`, `parentId`). */
|
|
115
|
+
nodes: Array<Record<string, unknown>>;
|
|
116
|
+
/** Tool calls that prepare the canvas (components, instances), run in order. */
|
|
117
|
+
setup?: SkillEvalToolCall[];
|
|
118
|
+
}
|
|
112
119
|
/** One eval file of a skill. */
|
|
113
120
|
export interface SkillEvalFile {
|
|
114
121
|
skill: string;
|
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -67,6 +67,8 @@ request like "clean up this export" finds it.
|
|
|
67
67
|
| [`pixerate-effect-look-development`](pixerate-effect-look-development/SKILL.md) | Effects (`pixerate-effect-authoring`) | Tune a look in a few moves, save it as a preset, animate with eased keyframes, seamless loops. |
|
|
68
68
|
| [`pixerate-accessibility-audit`](pixerate-accessibility-audit/SKILL.md) | Cross-domain (`pixerate-color`) | Text and non-text contrast, colour-blind-safe meaning, both modes, smallest fixes and a report, in any editor. |
|
|
69
69
|
| [`pixerate-brand-application`](pixerate-brand-application/SKILL.md) | Cross-domain (`pixerate-color`) | Brand roles mapped to exact palette values, near-misses snapped, readable text on brand colours, derivatives and house-style checks. |
|
|
70
|
+
| [`pixerate-canvas-design-system-components`](pixerate-canvas-design-system-components/SKILL.md) | Canvas (`pixerate-canvas`) | Main components and linked instances, component sets with named variant properties, overrides kept linked. |
|
|
71
|
+
| [`pixerate-canvas-layout`](pixerate-canvas-layout/SKILL.md) | Canvas (`pixerate-canvas`) | Align, distribute, order and lock with the arrange commands, changing only the intended axis. |
|
|
70
72
|
| [`pixerate-talking-head-edit`](pixerate-talking-head-edit/SKILL.md) | Video (`pixerate-video-editor`) | Talking-head cleanup by transcript: fillers, long pauses and stutters found in one pass and cut as one plan reviewed before it applies. Owns the dialogue tools. |
|
|
71
73
|
| [`pixerate-bgm-alignment`](pixerate-bgm-alignment/SKILL.md) | Video (`pixerate-video-editor`) | Background music: beats and cuts on the beat, ducking under speech, loudness in LUFS. Owns the music tools. |
|
|
72
74
|
|
|
@@ -135,7 +137,8 @@ same change.
|
|
|
135
137
|
"chart", "setup" }` `{ "editor": "diagram", "setup" }` with tool calls,
|
|
136
138
|
`{ "editor": "rich-text", "html" }`,
|
|
137
139
|
`{ "editor": "animation", "setup" }`, `{ "editor": "generation", "models", "prices", "history" }`
|
|
138
|
-
(a fake host that records each generation)
|
|
140
|
+
(a fake host that records each generation) `{ "editor": "effect", "effectId", "setup" }` or `{ "editor": "canvas",
|
|
141
|
+
"nodes", "setup" }` (a headless canvas with the component, arrange and document tools)), `checks` (a read-only
|
|
139
142
|
tool, or `run_command` with a read-only command such as `vector.list`, its `args`, what its result must match in `expect`, and `why`) and a `solution` (the
|
|
140
143
|
tool calls an expert would make). The test proves each eval: some check fails on the fixture,
|
|
141
144
|
and all pass after the solution. A model harness runs the same file with the model in place
|
|
@@ -25,6 +25,14 @@ from `get_editor_state`; component and set ids from `list_components`. Tools tha
|
|
|
25
25
|
just selected with `canvas_select`. See `pixerate-agent-tools` for namespacing, `run_commands`
|
|
26
26
|
batching and error codes.
|
|
27
27
|
|
|
28
|
+
## Workflow skills
|
|
29
|
+
|
|
30
|
+
For these jobs, load the workflow skill too: it has the method, the checks between steps and
|
|
31
|
+
the pitfalls, and uses the tools below.
|
|
32
|
+
|
|
33
|
+
- `pixerate-canvas-design-system-components`: components, instances, variant sets and overrides.
|
|
34
|
+
- `pixerate-canvas-layout`: align, distribute, order and lock.
|
|
35
|
+
|
|
28
36
|
## Working method
|
|
29
37
|
|
|
30
38
|
1. **Read.** `get_editor_state` with `includeDocument: false` returns `canvas.nodes` and
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-canvas-design-system-components
|
|
3
|
+
description: The design system components workflow in the Pixerate canvas - turning repeated design into main components, placing instances instead of copies, combining versions into a component set with named variant properties and values, adding variants, keeping instances linked, swapping, resetting and pushing overrides, and keeping the library tidy and named. Use when asked to build or tidy a design system, component library or UI kit, make a component, instance, variant or component set, add a state (hover, disabled) or size, rename variant properties, or replace copies with instances, or when create_component, combine_as_variants, add_variant or create_instance are available. Load with pixerate-canvas, which lists every tool.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-canvas
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Building design system components
|
|
10
|
+
|
|
11
|
+
A design system replaces copies with components. A main component holds the design; instances
|
|
12
|
+
show it elsewhere and update when it changes, with per-instance overrides where they differ.
|
|
13
|
+
Versions of one thing (styles, states, sizes) become variants in a component set, described by
|
|
14
|
+
properties ("Style=Primary, State=Disabled") a person or agent can switch between. Names and
|
|
15
|
+
properties are the interface: keep them clear and consistent.
|
|
16
|
+
|
|
17
|
+
This skill is the method. `pixerate-canvas` has the full tool reference.
|
|
18
|
+
|
|
19
|
+
## Goal and quality bar
|
|
20
|
+
|
|
21
|
+
- Anything used more than once is a main component; its uses are linked instances
|
|
22
|
+
(`inspect_instance` `status: "linked"`), not copies.
|
|
23
|
+
- Versions of one element are variants in one component set, with named properties (Style,
|
|
24
|
+
State, Size) and named values (Primary, Disabled, Large), never "Property 1" and "Variant2".
|
|
25
|
+
- Every combination of property values is unique; the default variant is the most common one.
|
|
26
|
+
- Instances keep their link; detach only when one must diverge for good.
|
|
27
|
+
- Components are named by what they are ("Button", "Card"), sets list their options clearly.
|
|
28
|
+
|
|
29
|
+
## Inputs to confirm
|
|
30
|
+
|
|
31
|
+
- **What becomes a component**, and which existing copies should become its instances.
|
|
32
|
+
- **Properties and values** for variants (Style: Primary / Secondary; State: Default / Hover /
|
|
33
|
+
Disabled; Size: Small / Medium / Large).
|
|
34
|
+
- **Naming conventions** of the team's library.
|
|
35
|
+
|
|
36
|
+
## Working method
|
|
37
|
+
|
|
38
|
+
1. **Read.** `list_components` (components, sets, their variants and options) and
|
|
39
|
+
`get_canvas_document` for the nodes.
|
|
40
|
+
**Check:** you know which nodes are already components and which are copies.
|
|
41
|
+
2. **Make components.** `create_component` with the frame's id(s).
|
|
42
|
+
**Check:** `list_components` lists it with its name.
|
|
43
|
+
3. **Replace copies with instances.** `create_instance` with `componentId`, `x`, `y`; remove the
|
|
44
|
+
copies once instances stand in their place.
|
|
45
|
+
**Check:** `get_canvas_document` shows instance nodes where the copies were.
|
|
46
|
+
4. **Combine versions into variants.** `create_component` each version, then
|
|
47
|
+
`combine_as_variants` with their ids; the set gets generic names ("Property 1", "Default",
|
|
48
|
+
"Variant2"). Rename at once: `rename_variant_property` (`name`, `newName`, `setId`) and
|
|
49
|
+
`rename_variant_value` (`property`, `value`, `newValue`, `setId`).
|
|
50
|
+
**Check:** `list_components` `sets[].options` shows the real names; each variant's `variant`
|
|
51
|
+
map is right.
|
|
52
|
+
5. **Add variants.** `add_variant` with `setId` and the new `variant` values (it duplicates the
|
|
53
|
+
default, or `from`); `set_variant_properties` to change a variant's values.
|
|
54
|
+
**Check:** the set lists every combination once.
|
|
55
|
+
6. **Maintain instances.** `swap_instance` to another component or variant, `reset_overrides` to
|
|
56
|
+
go back to the main, `push_overrides` to promote an instance's change to the main,
|
|
57
|
+
`go_to_main_component` to edit the source.
|
|
58
|
+
|
|
59
|
+
## Decision table
|
|
60
|
+
|
|
61
|
+
| Situation | Tool |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| The same element appears several times | `create_component`, then `create_instance` for each use |
|
|
64
|
+
| Versions of one element (styles, states, sizes) | `combine_as_variants`, then rename properties and values |
|
|
65
|
+
| A new state or size | `add_variant` with `variant` values |
|
|
66
|
+
| An instance should look like another variant | `set_instance_properties` or `swap_instance` |
|
|
67
|
+
| An instance's change belongs in the main | `push_overrides` |
|
|
68
|
+
| An instance drifted by accident | `reset_overrides` |
|
|
69
|
+
| One copy must diverge permanently | `detach_instance` (last resort) |
|
|
70
|
+
|
|
71
|
+
## Recipes
|
|
72
|
+
|
|
73
|
+
**Card component with two instances.** `create_component` `{ ids: ["card"] }` →
|
|
74
|
+
`create_instance` `{ componentId: "card", x: 0, y: 150 }` and `{ componentId: "card", x: 240,
|
|
75
|
+
y: 150 }`.
|
|
76
|
+
|
|
77
|
+
**Primary and Secondary variants.** `create_component` each button → `combine_as_variants`
|
|
78
|
+
`{ ids }` → `rename_variant_property` `{ name: "Property 1", newName: "Style", setId }` →
|
|
79
|
+
`rename_variant_value` `Default` → `Primary`, `Variant2` → `Secondary`.
|
|
80
|
+
|
|
81
|
+
**A Disabled variant.** `add_variant` `{ setId, variant: { Style: "Disabled" } }`.
|
|
82
|
+
|
|
83
|
+
## Pitfalls
|
|
84
|
+
|
|
85
|
+
- **Copies instead of instances** drift apart and cannot be updated together.
|
|
86
|
+
- **Generic variant names** ("Property 1", "Variant2") left after combining make the set unusable;
|
|
87
|
+
rename right away.
|
|
88
|
+
- **The rename and add tools need the set** (`setId` from `list_components`) when nothing is
|
|
89
|
+
selected.
|
|
90
|
+
- **Duplicate combinations** are refused; every variant needs a unique set of values.
|
|
91
|
+
- **Detaching to make a small change** loses future updates; override instead.
|
|
92
|
+
- **Combining unrelated components** into one set; a set is one element's versions.
|
|
93
|
+
|
|
94
|
+
## Tools used
|
|
95
|
+
|
|
96
|
+
- `list_components`, `inspect_instance`: components, sets, variants and instance status
|
|
97
|
+
- `get_canvas_document`: the nodes, to check placement
|
|
98
|
+
- `create_component`, `create_instance`: components and their instances
|
|
99
|
+
- `combine_as_variants`, `add_variant`, `remove_variant`, `set_variant_properties`: component sets
|
|
100
|
+
- `rename_variant_property`, `rename_variant_value`: clear property and value names
|
|
101
|
+
- `set_instance_properties`, `swap_instance`, `reset_overrides`, `push_overrides`: instances and overrides
|
|
102
|
+
- `detach_instance`, `go_to_main_component`, `restore_main_component`: break links, edit or restore mains
|
|
103
|
+
- `undo_canvas`: take back a step
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Add a Disabled style to the button component set, as a third variant of its Style property.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "canvas",
|
|
5
|
+
"nodes": [
|
|
6
|
+
{
|
|
7
|
+
"id": "primary",
|
|
8
|
+
"type": "group",
|
|
9
|
+
"position": {
|
|
10
|
+
"x": 0,
|
|
11
|
+
"y": 0
|
|
12
|
+
},
|
|
13
|
+
"width": 120,
|
|
14
|
+
"height": 40,
|
|
15
|
+
"data": {
|
|
16
|
+
"label": "Button / Primary"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "pbg",
|
|
21
|
+
"type": "shape",
|
|
22
|
+
"parentId": "primary",
|
|
23
|
+
"position": {
|
|
24
|
+
"x": 0,
|
|
25
|
+
"y": 0
|
|
26
|
+
},
|
|
27
|
+
"width": 120,
|
|
28
|
+
"height": 40,
|
|
29
|
+
"data": {
|
|
30
|
+
"label": "Bg",
|
|
31
|
+
"shape": {
|
|
32
|
+
"kind": "rect",
|
|
33
|
+
"fill": "#1d3557"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "secondary",
|
|
39
|
+
"type": "group",
|
|
40
|
+
"position": {
|
|
41
|
+
"x": 0,
|
|
42
|
+
"y": 60
|
|
43
|
+
},
|
|
44
|
+
"width": 120,
|
|
45
|
+
"height": 40,
|
|
46
|
+
"data": {
|
|
47
|
+
"label": "Button / Secondary"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "sbg",
|
|
52
|
+
"type": "shape",
|
|
53
|
+
"parentId": "secondary",
|
|
54
|
+
"position": {
|
|
55
|
+
"x": 0,
|
|
56
|
+
"y": 0
|
|
57
|
+
},
|
|
58
|
+
"width": 120,
|
|
59
|
+
"height": 40,
|
|
60
|
+
"data": {
|
|
61
|
+
"label": "Bg",
|
|
62
|
+
"shape": {
|
|
63
|
+
"kind": "rect",
|
|
64
|
+
"fill": "#ffffff"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"setup": [
|
|
70
|
+
{
|
|
71
|
+
"tool": "create_component",
|
|
72
|
+
"args": {
|
|
73
|
+
"ids": [
|
|
74
|
+
"primary"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"tool": "create_component",
|
|
80
|
+
"args": {
|
|
81
|
+
"ids": [
|
|
82
|
+
"secondary"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"tool": "combine_as_variants",
|
|
88
|
+
"args": {
|
|
89
|
+
"ids": [
|
|
90
|
+
"primary",
|
|
91
|
+
"secondary"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"tool": "rename_variant_property",
|
|
97
|
+
"args": {
|
|
98
|
+
"name": "Property 1",
|
|
99
|
+
"newName": "Style",
|
|
100
|
+
"setId": {
|
|
101
|
+
"$read": "list_components",
|
|
102
|
+
"path": "sets.0.id"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"tool": "rename_variant_value",
|
|
108
|
+
"args": {
|
|
109
|
+
"property": "Style",
|
|
110
|
+
"value": "Default",
|
|
111
|
+
"newValue": "Primary",
|
|
112
|
+
"setId": {
|
|
113
|
+
"$read": "list_components",
|
|
114
|
+
"path": "sets.0.id"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"tool": "rename_variant_value",
|
|
120
|
+
"args": {
|
|
121
|
+
"property": "Style",
|
|
122
|
+
"value": "Variant2",
|
|
123
|
+
"newValue": "Secondary",
|
|
124
|
+
"setId": {
|
|
125
|
+
"$read": "list_components",
|
|
126
|
+
"path": "sets.0.id"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
"checks": [
|
|
133
|
+
{
|
|
134
|
+
"why": "three variants: Primary, Secondary and Disabled",
|
|
135
|
+
"tool": "list_components",
|
|
136
|
+
"expect": {
|
|
137
|
+
"sets": [
|
|
138
|
+
{
|
|
139
|
+
"variants": {
|
|
140
|
+
"$length": 3
|
|
141
|
+
},
|
|
142
|
+
"options": [
|
|
143
|
+
{
|
|
144
|
+
"name": "Style",
|
|
145
|
+
"values": {
|
|
146
|
+
"$expr": "[...v].sort().join() === 'Disabled,Primary,Secondary'"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
"solution": [
|
|
156
|
+
{
|
|
157
|
+
"tool": "list_components"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"tool": "add_variant",
|
|
161
|
+
"args": {
|
|
162
|
+
"setId": {
|
|
163
|
+
"$read": "list_components",
|
|
164
|
+
"path": "sets.0.id"
|
|
165
|
+
},
|
|
166
|
+
"variant": {
|
|
167
|
+
"Style": "Disabled"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "These two buttons are versions of one button. Combine them into a single component with a 'Style' property whose values are Primary and Secondary.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "canvas",
|
|
5
|
+
"nodes": [
|
|
6
|
+
{
|
|
7
|
+
"id": "primary",
|
|
8
|
+
"type": "group",
|
|
9
|
+
"position": {
|
|
10
|
+
"x": 0,
|
|
11
|
+
"y": 0
|
|
12
|
+
},
|
|
13
|
+
"width": 120,
|
|
14
|
+
"height": 40,
|
|
15
|
+
"data": {
|
|
16
|
+
"label": "Button / Primary"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "pbg",
|
|
21
|
+
"type": "shape",
|
|
22
|
+
"position": {
|
|
23
|
+
"x": 0,
|
|
24
|
+
"y": 0
|
|
25
|
+
},
|
|
26
|
+
"width": 120,
|
|
27
|
+
"height": 40,
|
|
28
|
+
"data": {
|
|
29
|
+
"label": "Bg",
|
|
30
|
+
"shape": {
|
|
31
|
+
"kind": "rect",
|
|
32
|
+
"fill": "#1d3557"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"parentId": "primary"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "secondary",
|
|
39
|
+
"type": "group",
|
|
40
|
+
"position": {
|
|
41
|
+
"x": 0,
|
|
42
|
+
"y": 60
|
|
43
|
+
},
|
|
44
|
+
"width": 120,
|
|
45
|
+
"height": 40,
|
|
46
|
+
"data": {
|
|
47
|
+
"label": "Button / Secondary"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "sbg",
|
|
52
|
+
"type": "shape",
|
|
53
|
+
"position": {
|
|
54
|
+
"x": 0,
|
|
55
|
+
"y": 0
|
|
56
|
+
},
|
|
57
|
+
"width": 120,
|
|
58
|
+
"height": 40,
|
|
59
|
+
"data": {
|
|
60
|
+
"label": "Bg",
|
|
61
|
+
"shape": {
|
|
62
|
+
"kind": "rect",
|
|
63
|
+
"fill": "#ffffff"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"parentId": "secondary"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"setup": [
|
|
70
|
+
{
|
|
71
|
+
"tool": "create_component",
|
|
72
|
+
"args": {
|
|
73
|
+
"ids": [
|
|
74
|
+
"primary"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"tool": "create_component",
|
|
80
|
+
"args": {
|
|
81
|
+
"ids": [
|
|
82
|
+
"secondary"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"checks": [
|
|
89
|
+
{
|
|
90
|
+
"why": "one component set with a Style property whose values are Primary and Secondary",
|
|
91
|
+
"tool": "list_components",
|
|
92
|
+
"expect": {
|
|
93
|
+
"sets": {
|
|
94
|
+
"$length": 1,
|
|
95
|
+
"$some": {
|
|
96
|
+
"options": [
|
|
97
|
+
{
|
|
98
|
+
"name": "Style",
|
|
99
|
+
"values": {
|
|
100
|
+
"$expr": "[...v].sort().join() === 'Primary,Secondary'"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"why": "the primary button is the Primary variant",
|
|
110
|
+
"tool": "list_components",
|
|
111
|
+
"expect": {
|
|
112
|
+
"components": {
|
|
113
|
+
"$some": {
|
|
114
|
+
"id": "primary",
|
|
115
|
+
"variant": {
|
|
116
|
+
"Style": "Primary"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"solution": [
|
|
124
|
+
{
|
|
125
|
+
"tool": "combine_as_variants",
|
|
126
|
+
"args": {
|
|
127
|
+
"ids": [
|
|
128
|
+
"primary",
|
|
129
|
+
"secondary"
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"tool": "list_components"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"tool": "rename_variant_property",
|
|
138
|
+
"args": {
|
|
139
|
+
"name": "Property 1",
|
|
140
|
+
"newName": "Style",
|
|
141
|
+
"setId": {
|
|
142
|
+
"$read": "list_components",
|
|
143
|
+
"path": "sets.0.id"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"tool": "rename_variant_value",
|
|
149
|
+
"args": {
|
|
150
|
+
"property": "Style",
|
|
151
|
+
"value": "Default",
|
|
152
|
+
"newValue": "Primary",
|
|
153
|
+
"setId": {
|
|
154
|
+
"$read": "list_components",
|
|
155
|
+
"path": "sets.0.id"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"tool": "rename_variant_value",
|
|
161
|
+
"args": {
|
|
162
|
+
"property": "Style",
|
|
163
|
+
"value": "Variant2",
|
|
164
|
+
"newValue": "Secondary",
|
|
165
|
+
"setId": {
|
|
166
|
+
"$read": "list_components",
|
|
167
|
+
"path": "sets.0.id"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Turn the card into a reusable component and place two instances of it below the original, side by side.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "canvas",
|
|
5
|
+
"nodes": [
|
|
6
|
+
{
|
|
7
|
+
"id": "card",
|
|
8
|
+
"type": "group",
|
|
9
|
+
"position": {
|
|
10
|
+
"x": 0,
|
|
11
|
+
"y": 0
|
|
12
|
+
},
|
|
13
|
+
"width": 200,
|
|
14
|
+
"height": 100,
|
|
15
|
+
"data": {
|
|
16
|
+
"label": "Card"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "bg",
|
|
21
|
+
"type": "shape",
|
|
22
|
+
"position": {
|
|
23
|
+
"x": 0,
|
|
24
|
+
"y": 0
|
|
25
|
+
},
|
|
26
|
+
"width": 200,
|
|
27
|
+
"height": 100,
|
|
28
|
+
"data": {
|
|
29
|
+
"label": "Bg",
|
|
30
|
+
"shape": {
|
|
31
|
+
"kind": "rect",
|
|
32
|
+
"fill": "#eeeeee"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"parentId": "card"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "title",
|
|
39
|
+
"type": "text",
|
|
40
|
+
"parentId": "card",
|
|
41
|
+
"position": {
|
|
42
|
+
"x": 10,
|
|
43
|
+
"y": 10
|
|
44
|
+
},
|
|
45
|
+
"data": {
|
|
46
|
+
"label": "Hello",
|
|
47
|
+
"name": "Title"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"checks": [
|
|
53
|
+
{
|
|
54
|
+
"why": "one component, made from the card",
|
|
55
|
+
"tool": "list_components",
|
|
56
|
+
"expect": {
|
|
57
|
+
"components": {
|
|
58
|
+
"$length": 1,
|
|
59
|
+
"$some": {
|
|
60
|
+
"id": "card"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"why": "two instances below the card",
|
|
67
|
+
"tool": "get_canvas_document",
|
|
68
|
+
"expect": {
|
|
69
|
+
"document": {
|
|
70
|
+
"nodes": {
|
|
71
|
+
"$expr": "v.filter((n) => n.data && n.data.instance && n.position.y >= 100).length === 2"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"solution": [
|
|
78
|
+
{
|
|
79
|
+
"tool": "create_component",
|
|
80
|
+
"args": {
|
|
81
|
+
"ids": [
|
|
82
|
+
"card"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"tool": "create_instance",
|
|
88
|
+
"args": {
|
|
89
|
+
"componentId": "card",
|
|
90
|
+
"x": 0,
|
|
91
|
+
"y": 150
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"tool": "create_instance",
|
|
96
|
+
"args": {
|
|
97
|
+
"componentId": "card",
|
|
98
|
+
"x": 240,
|
|
99
|
+
"y": 150
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-canvas-layout
|
|
3
|
+
description: The layout workflow in the Pixerate canvas - tidying a board or screen by aligning edges and centres, distributing elements evenly, keeping consistent spacing on a grid, ordering layers so the right things are on top, grouping related elements, locking backgrounds and guides, and checking the result. Use when asked to align, line up, centre, distribute, space evenly, tidy, clean up or lay out elements on a canvas, board, screen, slide or wireframe, bring something to the front or send it back, or lock a layer, or when canvas.alignLeft, canvas.distributeHorizontal, canvas.bringToFront or canvas.lock are available. Load with pixerate-canvas, which lists every tool.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-canvas
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Laying out a canvas
|
|
10
|
+
|
|
11
|
+
A tidy layout comes from a few relations applied deliberately: shared edges and centres,
|
|
12
|
+
equal gaps, a consistent spacing scale, and a stacking order where what matters is on top.
|
|
13
|
+
Use the arrange commands rather than moving nodes by hand: they act on exact geometry, keep
|
|
14
|
+
other axes untouched, and are one undo step each.
|
|
15
|
+
|
|
16
|
+
This skill is the method. `pixerate-canvas` has the full tool reference; run the `canvas.*`
|
|
17
|
+
commands with `run_command` (`params: { ids }`) or several in one `run_commands` batch.
|
|
18
|
+
|
|
19
|
+
## Goal and quality bar
|
|
20
|
+
|
|
21
|
+
- Elements in a row share a top, centre or bottom; elements in a column share a left, centre or
|
|
22
|
+
right edge.
|
|
23
|
+
- Gaps between repeated elements are equal, on the spacing scale (multiples of 4 or 8).
|
|
24
|
+
- The stacking order matches meaning: backgrounds at the back, content above, badges, overlays
|
|
25
|
+
and annotations on top.
|
|
26
|
+
- Backgrounds, guides and finished areas are locked so they cannot be moved by accident.
|
|
27
|
+
- Only the intended axis changes: aligning tops does not move anything sideways.
|
|
28
|
+
|
|
29
|
+
## Inputs to confirm
|
|
30
|
+
|
|
31
|
+
- **Which elements** belong together (a row of cards, a form column) and the anchor (the first
|
|
32
|
+
element, the container, the canvas).
|
|
33
|
+
- **Spacing:** equal distribution between fixed ends, or a set gap.
|
|
34
|
+
- **What must stay put**, and what to lock.
|
|
35
|
+
|
|
36
|
+
## Working method
|
|
37
|
+
|
|
38
|
+
1. **Read.** `get_canvas_document` for node ids, positions, sizes, parents and z-order.
|
|
39
|
+
**Check:** you know the rows, columns and stacking you want.
|
|
40
|
+
2. **Align** rows and columns: `canvas.alignTop`, `canvas.alignMiddle`, `canvas.alignBottom`,
|
|
41
|
+
`canvas.alignLeft`, `canvas.alignCenter`, `canvas.alignRight` with the group's `ids`.
|
|
42
|
+
**Check:** the shared coordinate is equal; the other axis is unchanged.
|
|
43
|
+
3. **Distribute** repeated elements: `canvas.distributeHorizontal` or `canvas.distributeVertical`
|
|
44
|
+
(the outer elements stay, the gaps between them become equal).
|
|
45
|
+
**Check:** the gaps are equal within a pixel.
|
|
46
|
+
4. **Order layers.** `canvas.bringToFront`, `canvas.bringForward`, `canvas.sendBackward`,
|
|
47
|
+
`canvas.sendToBack`; order is by `zIndex`, then document order.
|
|
48
|
+
**Check:** what must be on top has the highest effective order.
|
|
49
|
+
5. **Lock** backgrounds and finished parts: `canvas.lock` (`canvas.unlock` to edit again).
|
|
50
|
+
**Check:** locked nodes are no longer draggable in `get_canvas_document`.
|
|
51
|
+
6. **Look** with the host's view (`capture_view`, `view_editor`) when available.
|
|
52
|
+
|
|
53
|
+
## Decision table
|
|
54
|
+
|
|
55
|
+
| Want | Command |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| A row lined up | `canvas.alignTop` (or `canvas.alignMiddle` for mixed heights) |
|
|
58
|
+
| A column lined up | `canvas.alignLeft` (or `canvas.alignCenter`) |
|
|
59
|
+
| Even gaps between fixed ends | `canvas.distributeHorizontal` / `canvas.distributeVertical` |
|
|
60
|
+
| Something hidden behind | `canvas.bringToFront` |
|
|
61
|
+
| A background covering content | `canvas.sendToBack` |
|
|
62
|
+
| One step up or down | `canvas.bringForward` / `canvas.sendBackward` |
|
|
63
|
+
| Protect a background or guide | `canvas.lock` |
|
|
64
|
+
|
|
65
|
+
## Recipes
|
|
66
|
+
|
|
67
|
+
**Line up a row of cards.** `run_command` `{ id: "canvas.alignTop", params: { ids: ["a", "b",
|
|
68
|
+
"c"] } }`.
|
|
69
|
+
|
|
70
|
+
**Even spacing.** `run_command` `{ id: "canvas.distributeHorizontal", params: { ids } }`; the
|
|
71
|
+
leftmost and rightmost stay where they are.
|
|
72
|
+
|
|
73
|
+
**Badge on top, background locked.** `run_commands` with `canvas.bringToFront` `{ ids: ["badge"]
|
|
74
|
+
}` and `canvas.lock` `{ ids: ["backdrop"] }`.
|
|
75
|
+
|
|
76
|
+
## Pitfalls
|
|
77
|
+
|
|
78
|
+
- **Moving by hand** with rounded numbers drifts; align and distribute act on exact geometry.
|
|
79
|
+
- **Distributing two elements** does nothing; it needs at least three.
|
|
80
|
+
- **Aligning across different parents** aligns in each parent's own space; group or reparent
|
|
81
|
+
first.
|
|
82
|
+
- **Stacking by array order only**: `zIndex` wins over order; read the effective order.
|
|
83
|
+
- **Locked nodes** refuse moves; unlock, change, lock again.
|
|
84
|
+
- **Aligning everything to one edge** of the canvas when the group should align to itself.
|
|
85
|
+
|
|
86
|
+
## Tools used
|
|
87
|
+
|
|
88
|
+
- `get_canvas_document`: node positions, sizes, parents, z-order and locks
|
|
89
|
+
- `canvas.alignLeft`, `canvas.alignCenter`, `canvas.alignRight`: column alignment
|
|
90
|
+
- `canvas.alignTop`, `canvas.alignMiddle`, `canvas.alignBottom`: row alignment
|
|
91
|
+
- `canvas.distributeHorizontal`, `canvas.distributeVertical`: even gaps
|
|
92
|
+
- `canvas.bringToFront`, `canvas.bringForward`, `canvas.sendBackward`, `canvas.sendToBack`: stacking order
|
|
93
|
+
- `canvas.lock`, `canvas.unlock`: protect backgrounds and finished parts
|
|
94
|
+
- `capture_view`, `view_editor`: look at the result
|
|
95
|
+
- `undo_canvas`: take back a step
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Line up the tops of the three cards.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "canvas",
|
|
5
|
+
"nodes": [
|
|
6
|
+
{
|
|
7
|
+
"id": "a",
|
|
8
|
+
"type": "shape",
|
|
9
|
+
"position": {
|
|
10
|
+
"x": 0,
|
|
11
|
+
"y": 10
|
|
12
|
+
},
|
|
13
|
+
"width": 100,
|
|
14
|
+
"height": 80,
|
|
15
|
+
"data": {
|
|
16
|
+
"label": "Card A",
|
|
17
|
+
"shape": {
|
|
18
|
+
"kind": "rect",
|
|
19
|
+
"fill": "#eeeeee"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "b",
|
|
25
|
+
"type": "shape",
|
|
26
|
+
"position": {
|
|
27
|
+
"x": 130,
|
|
28
|
+
"y": 30
|
|
29
|
+
},
|
|
30
|
+
"width": 100,
|
|
31
|
+
"height": 80,
|
|
32
|
+
"data": {
|
|
33
|
+
"label": "Card B",
|
|
34
|
+
"shape": {
|
|
35
|
+
"kind": "rect",
|
|
36
|
+
"fill": "#eeeeee"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "c",
|
|
42
|
+
"type": "shape",
|
|
43
|
+
"position": {
|
|
44
|
+
"x": 300,
|
|
45
|
+
"y": 0
|
|
46
|
+
},
|
|
47
|
+
"width": 100,
|
|
48
|
+
"height": 80,
|
|
49
|
+
"data": {
|
|
50
|
+
"label": "Card C",
|
|
51
|
+
"shape": {
|
|
52
|
+
"kind": "rect",
|
|
53
|
+
"fill": "#eeeeee"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"checks": [
|
|
60
|
+
{
|
|
61
|
+
"why": "all three tops at the same y",
|
|
62
|
+
"tool": "get_canvas_document",
|
|
63
|
+
"expect": {
|
|
64
|
+
"document": {
|
|
65
|
+
"nodes": {
|
|
66
|
+
"$expr": "new Set(v.map((n) => n.position.y)).size === 1"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"why": "the horizontal positions are unchanged",
|
|
73
|
+
"tool": "get_canvas_document",
|
|
74
|
+
"expect": {
|
|
75
|
+
"document": {
|
|
76
|
+
"nodes": {
|
|
77
|
+
"$expr": "v.map((n) => n.position.x).join() === '0,130,300'"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"solution": [
|
|
84
|
+
{
|
|
85
|
+
"tool": "run_command",
|
|
86
|
+
"args": {
|
|
87
|
+
"id": "canvas.alignTop",
|
|
88
|
+
"params": {
|
|
89
|
+
"ids": [
|
|
90
|
+
"a",
|
|
91
|
+
"b",
|
|
92
|
+
"c"
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Space the three cards evenly across, keeping the first and last where they are.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "canvas",
|
|
5
|
+
"nodes": [
|
|
6
|
+
{
|
|
7
|
+
"id": "a",
|
|
8
|
+
"type": "shape",
|
|
9
|
+
"position": {
|
|
10
|
+
"x": 0,
|
|
11
|
+
"y": 0
|
|
12
|
+
},
|
|
13
|
+
"width": 100,
|
|
14
|
+
"height": 80,
|
|
15
|
+
"data": {
|
|
16
|
+
"label": "Card A",
|
|
17
|
+
"shape": {
|
|
18
|
+
"kind": "rect",
|
|
19
|
+
"fill": "#eeeeee"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "b",
|
|
25
|
+
"type": "shape",
|
|
26
|
+
"position": {
|
|
27
|
+
"x": 120,
|
|
28
|
+
"y": 0
|
|
29
|
+
},
|
|
30
|
+
"width": 100,
|
|
31
|
+
"height": 80,
|
|
32
|
+
"data": {
|
|
33
|
+
"label": "Card B",
|
|
34
|
+
"shape": {
|
|
35
|
+
"kind": "rect",
|
|
36
|
+
"fill": "#eeeeee"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "c",
|
|
42
|
+
"type": "shape",
|
|
43
|
+
"position": {
|
|
44
|
+
"x": 300,
|
|
45
|
+
"y": 0
|
|
46
|
+
},
|
|
47
|
+
"width": 100,
|
|
48
|
+
"height": 80,
|
|
49
|
+
"data": {
|
|
50
|
+
"label": "Card C",
|
|
51
|
+
"shape": {
|
|
52
|
+
"kind": "rect",
|
|
53
|
+
"fill": "#eeeeee"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"checks": [
|
|
60
|
+
{
|
|
61
|
+
"why": "equal gaps between the cards",
|
|
62
|
+
"tool": "get_canvas_document",
|
|
63
|
+
"expect": {
|
|
64
|
+
"document": {
|
|
65
|
+
"nodes": {
|
|
66
|
+
"$expr": "(() => { const xs = v.map((n) => n.position.x).sort((p, q) => p - q); return Math.abs((xs[1] - xs[0]) - (xs[2] - xs[1])) <= 1; })()"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"why": "the outer cards stay put",
|
|
73
|
+
"tool": "get_canvas_document",
|
|
74
|
+
"expect": {
|
|
75
|
+
"document": {
|
|
76
|
+
"nodes": {
|
|
77
|
+
"$expr": "v.find((n) => n.id === 'a').position.x === 0 && v.find((n) => n.id === 'c').position.x === 300"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"solution": [
|
|
84
|
+
{
|
|
85
|
+
"tool": "run_command",
|
|
86
|
+
"args": {
|
|
87
|
+
"id": "canvas.distributeHorizontal",
|
|
88
|
+
"params": {
|
|
89
|
+
"ids": [
|
|
90
|
+
"a",
|
|
91
|
+
"b",
|
|
92
|
+
"c"
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "The badge is hidden behind the card: bring it to the front, and lock the background so it can't be moved by accident.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "canvas",
|
|
5
|
+
"nodes": [
|
|
6
|
+
{
|
|
7
|
+
"id": "backdrop",
|
|
8
|
+
"type": "shape",
|
|
9
|
+
"position": {
|
|
10
|
+
"x": 0,
|
|
11
|
+
"y": 0
|
|
12
|
+
},
|
|
13
|
+
"width": 400,
|
|
14
|
+
"height": 300,
|
|
15
|
+
"data": {
|
|
16
|
+
"label": "Background",
|
|
17
|
+
"shape": {
|
|
18
|
+
"kind": "rect",
|
|
19
|
+
"fill": "#f1faee"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "badge",
|
|
25
|
+
"type": "shape",
|
|
26
|
+
"position": {
|
|
27
|
+
"x": 280,
|
|
28
|
+
"y": 20
|
|
29
|
+
},
|
|
30
|
+
"width": 40,
|
|
31
|
+
"height": 40,
|
|
32
|
+
"data": {
|
|
33
|
+
"label": "Badge",
|
|
34
|
+
"shape": {
|
|
35
|
+
"kind": "rect",
|
|
36
|
+
"fill": "#e63946"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "card",
|
|
42
|
+
"type": "shape",
|
|
43
|
+
"position": {
|
|
44
|
+
"x": 40,
|
|
45
|
+
"y": 20
|
|
46
|
+
},
|
|
47
|
+
"width": 300,
|
|
48
|
+
"height": 200,
|
|
49
|
+
"data": {
|
|
50
|
+
"label": "Card",
|
|
51
|
+
"shape": {
|
|
52
|
+
"kind": "rect",
|
|
53
|
+
"fill": "#ffffff"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"checks": [
|
|
60
|
+
{
|
|
61
|
+
"why": "the badge is drawn last (on top)",
|
|
62
|
+
"tool": "get_canvas_document",
|
|
63
|
+
"expect": {
|
|
64
|
+
"document": {
|
|
65
|
+
"nodes": {
|
|
66
|
+
"$expr": "(() => { const z = (n) => (typeof n.zIndex === 'number' ? n.zIndex : 0); const order = v.map((n, i) => ({ id: n.id, z: z(n), i })).sort((a, b) => a.z - b.z || a.i - b.i); return order[order.length - 1].id === 'badge'; })()"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"why": "the background is locked",
|
|
73
|
+
"tool": "get_canvas_document",
|
|
74
|
+
"expect": {
|
|
75
|
+
"document": {
|
|
76
|
+
"nodes": {
|
|
77
|
+
"$some": {
|
|
78
|
+
"id": "backdrop",
|
|
79
|
+
"$expr": "v.draggable === false || (v.data && v.data.locked === true) || v.locked === true"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"solution": [
|
|
87
|
+
{
|
|
88
|
+
"tool": "run_command",
|
|
89
|
+
"args": {
|
|
90
|
+
"id": "canvas.bringToFront",
|
|
91
|
+
"params": {
|
|
92
|
+
"ids": [
|
|
93
|
+
"badge"
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"tool": "run_command",
|
|
100
|
+
"args": {
|
|
101
|
+
"id": "canvas.lock",
|
|
102
|
+
"params": {
|
|
103
|
+
"ids": [
|
|
104
|
+
"backdrop"
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|