@zvada/cr8 0.1.0 → 0.2.0
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/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +16 -1
- package/dist/cr8.mjs +334 -115
- package/docs/user/cli.md +44 -38
- package/docs/user/desktop.md +2 -2
- package/docs/user/mcp.md +6 -2
- package/package.json +17 -56
- package/skills/cr8-design/SKILL.md +30 -16
- package/skills/cr8-design/references/build.md +63 -7
- package/skills/cr8-design/references/cli.md +6 -3
- package/skills/cr8-design/references/start.md +4 -0
- package/skills/cr8-design/references/verify.md +16 -0
- package/skills/cr8-flows/SKILL.md +1 -1
package/docs/user/cli.md
CHANGED
|
@@ -82,24 +82,27 @@ Omitting `--sequence` reads the latest session immediately before a mutation. Th
|
|
|
82
82
|
| `playbook PLAYBOOK_ID` | Load one current methodology, decision flow, and acceptance gate. |
|
|
83
83
|
| `styles [--playbook PLAYBOOK_ID]` | List composition systems, optionally filtered to a playbook. |
|
|
84
84
|
| `style STYLE_ID` | Load one current visual direction with concrete composition and hierarchy rules. |
|
|
85
|
+
| `materials [--kind icon\|recipe] [--query TEXT]` | Search the bundled named vector icons and small product recipes. Read-only; no open project, runtime connection or network needed. |
|
|
86
|
+
| `material MATERIAL_ID [--key-prefix PREFIX]` | Return ordinary compose drafts in `result.data.composition`, with source attribution. Adapt them and pass them to `compose`; recipes are independent editable copies. The prefix namespaces node, token and text-style keys. |
|
|
85
87
|
| `guide [--topic canvas-foundations\|web-app\|faithful-reproduction\|app-store]` | Backward-compatible alias for loading a legacy-named playbook. |
|
|
86
88
|
| `skills [list]` | List the skills that ship with this CLI: name, description, when to read each, files, and where they live on disk. |
|
|
87
89
|
| `skills get NAME [--path FILE]` | Print a skill's `SKILL.md` as markdown, not JSON, or one file it references such as `agents/openai.yaml`. |
|
|
88
90
|
| `skills path [NAME]` | Print the on-disk skills directory, or one skill's; the compiled binary carries them embedded and refuses with `skills_embedded`. |
|
|
89
91
|
| `skills install [--host claude\|codex\|agents\|all] [--dir DIR] [--force]` | Install every shipped skill where the hosts look, linking to the package's skills or writing the embedded copy. |
|
|
90
|
-
| `tree [--root ID]` | Return the
|
|
91
|
-
| `find [--query TEXT] [--type TYPE] [--role ROLE] [--root ID] [--limit N]` | Discover stable IDs. |
|
|
92
|
+
| `tree [--root ID]` | Return the semantic hierarchy with each layer's stored `x`, `y`, `width`, `height` and its actual layout in `resolved.localGeometry` (relative to its parent) and `resolved.worldGeometry` (canvas coordinates). Flow layout can override stored bounds. Scope with `--root` to inspect one section. |
|
|
93
|
+
| `find [--query TEXT] [--type TYPE] [--role ROLE] [--token ID] [--color HEX] [--root ID] [--limit N]` | Discover stable IDs with all filters combined. Token and color searches cover fill, instance text color, stroke and shadow, including image effects. Hex matches resolve tokens and normalize shorthand, case and alpha; results include matching `colors` properties and token IDs. With both paint filters, the same property must match both. |
|
|
92
94
|
| `inspect ID` | Return one node, its root, descendants, and layout issues. |
|
|
93
95
|
| `doctor [--root ID]` | Run deterministic diagnostics: `layout`, a layer that overflows, clips or has no room, `overlap`, a root artboard lying over another (named with the overlap in px; with `--root`, only the pairs that root is part of), and `contrast`, a text that reads below 4.5:1 (3:1 at display sizes) on what lies under it, or that sits on an image with no plate or scrim. A `hygiene` notice names clutter an iteration left: a root artboard holding nothing, or a generated asset no layer shows (`remove_asset` drops it). A `warning` makes the design unhealthy (exit `2`); a `notice` (a colour that reads short of its minimum, or hygiene) is listed and does not. Exits `2` on a warning; a report of notices alone exits `0`. |
|
|
94
96
|
| `doctor --cloud` | Ask the account hub and the media Worker what they run and whether they still accept this version; exits `2` when one is unreachable, refuses this client, or is older than the runtime. |
|
|
95
97
|
| `edit --ops JSON` / `edit --file FILE` | Apply up to 100 scene operations as one transaction. |
|
|
96
|
-
| `compose --json JSON` / `compose --file FILE [--parent ID]` | Create nested editable hierarchy
|
|
98
|
+
| `compose --json JSON` / `compose --file FILE [--parent ID]` | Create nested editable hierarchy from an array of drafts or `{nodes,tokens,textStyles}` in one transaction. Each draft key is returned as a stable key-to-ID map. Omit `--parent` for a root node; `--parent null` and `--parent root` mean the same. A new root artboard composed where another root sits lands clear of it, to the right of them all at the y it asked for, and the answer says where (`placed`). |
|
|
97
99
|
| `duplicate --source ID [--name TEXT]` | Create a normal independent artboard and return its old-to-new ID map. |
|
|
98
100
|
| `import IMAGE_PATH [--as-frame \| --asset-only] [--name TEXT] [--parent ID] [--x N] [--y N] [--width N] [--height N] [--fit contain\|cover\|fill]` | Validate a static PNG/JPEG/WebP, copy it into the project's content-addressed assets, and place one editable image or root artboard atomically. PNG, JPEG, WebP or SVG (an SVG is sanitized and stays a vector). `--asset-only` registers the asset and places no layer, for `place --asset` later. |
|
|
99
|
-
| `capture URL [--name TEXT] [--viewport WIDTHxHEIGHT] [--wait MS] [--timeout MS]` | Capture an HTTP(S) page with local Chrome/Chromium and place the result as a root artboard. |
|
|
100
|
-
| `verify --reference IMAGE_PATH --root FRAME_ID [--tolerance 0.01]` | Run `doctor`, export the exact frame with CR8's canonical renderer, and compare same-size pixels against a local reference. |
|
|
101
|
+
| `capture URL [--name TEXT] [--viewport WIDTHxHEIGHT] [--wait MS] [--timeout MS] [--layers]` | Capture an HTTP(S) page with local Chrome/Chromium and place the result as a root artboard. With `--layers`, the page becomes editable layers instead of one picture: the browser lays it out, and every box that paints becomes a frame with its fill, border, radius and clipping, every run of text a text layer with its size, weight, line height, colour and the closest family CR8 ships, every picture and inline vector an asset. Exact geometry; shadows and gradients are dropped; the answer counts what was made and says when the page exceeded the budget of 1500 layers or 48 pictures. |
|
|
102
|
+
| `verify --reference IMAGE_PATH --root FRAME_ID [--tolerance 0.01] [--ink-tolerance 0.25]` | Run `doctor`, export the exact frame with CR8's canonical renderer, and compare same-size pixels against a local reference. A screen is mostly background, so the fraction over the whole viewport flatters: the answer also gives `inkMismatchedFraction`, the share of the pixels that carry ink in either image that differ, the `regions` that differ most, and writes a difference image beside the exports (`diffPath`), the render dimmed with every differing pixel red. A pass needs the ink gate too. |
|
|
101
103
|
| `export --root FRAME_ID [--out PATH] [--scale 1\|2]` | Render the exact root artboard with the same canonical renderer and write it as a PNG: to `<project>/exports/<frame-name>.png` (created as needed) unless `--out` names a path (relative to the current directory). Answers `path`, `width`, `height`, `bytes`, `scale`, `rootId` and `name`; changes nothing. `--scale 2` renders at twice the pixels. |
|
|
102
|
-
| `generate [--prompt TEXT] [--node ID] [--model auto\|fast\|quality\|vector\|ID] [--ratio 1:1\|16:9\|9:16\|4:3\|3:4] [--count 1..4]` | Add image or native SVG output as normal editable artboards. `--model` takes a preset word or an image or vector model id from `media-models`; `--node` turns Auto/Fast into a reference edit and feeds an editor or upscaler, which needs no `--prompt`. |
|
|
104
|
+
| `generate [--prompt TEXT] [--node ID] [--into FRAME_ID] [--model auto\|fast\|quality\|vector\|ID] [--ratio 1:1\|16:9\|9:16\|4:3\|3:4] [--count 1..4] [--remove-background]` | Add image or native SVG output as normal editable artboards. `--model` takes a preset word or an image or vector model id from `media-models`; `--node` turns Auto/Fast into a reference edit and feeds an editor or upscaler, which needs no `--prompt`. With `--into`, count defaults to one and the output covers the named root or nested frame behind its children. Its resolved size chooses the nearest supported ratio; it never enters the frame’s flow layout. Target and capacity are validated before a provider job starts. |
|
|
105
|
+
| `expand-svg SVG_IMAGE_ID [--remove-background]` | Expand an existing SVG image in place into native vector layers. Preserve the immutable source and one undo restores the image. Refuse unsupported SVG features without changing the document. |
|
|
103
106
|
| `decompose --node ID [--prompt TEXT] [--layers 2..8]` | Replace an image in place with editable raster/text layers. |
|
|
104
107
|
| `reset --yes` | Clear the current canvas while keeping its project name. |
|
|
105
108
|
| `watch [--since N] [--timeout MS]` | Return retained collaboration events after a sequence. |
|
|
@@ -134,7 +137,7 @@ Canvas sequences and workflow revisions are deliberately separate. Every workflo
|
|
|
134
137
|
|
|
135
138
|
`import` and `capture` return the created `assetId`, `imageId`, optional `frameId`, managed source path, dimensions, and placement. Imported bytes are validated before any scene mutation; `--dry-run` validates and plans without copying or changing the project. `capture` and `verify` require an installed Chrome or Chromium and honor `CR8_CHROME_PATH` (or `CHROME_PATH`) when automatic discovery is insufficient. They launch isolated, temporary headless profiles and remove them after the command. If Chrome cannot start with its sandbox (some container and CI images), the CLI retries once without it.
|
|
136
139
|
|
|
137
|
-
`verify` is intentionally stricter than `doctor`: both layout diagnostics and visual comparison must pass. The default tolerance permits at most 1% normalized mean channel error and 1% pixels whose channel difference exceeds 8/255, accommodating browser image resampling while still rejecting visible drift. It exits `2` on a cleanly completed failed gate and `1` when verification could not run. `export` renders the same frame through the same renderer and writes it to disk, so the file it answers is the file `verify` compares and the Export button saves; `export --root FRAME_ID` followed by `verify --reference <that file> --root FRAME_ID` passes.
|
|
140
|
+
`verify` is intentionally stricter than `doctor`: both layout diagnostics and visual comparison must pass, and the visual comparison judges the ink, not the viewport: on a screen where eight percent of the pixels carry ink, a copy that differs in one percent of all pixels can differ in a fifth of the ink, and the answer says so. The default tolerance permits at most 1% normalized mean channel error and 1% pixels whose channel difference exceeds 8/255, accommodating browser image resampling while still rejecting visible drift. It exits `2` on a cleanly completed failed gate and `1` when verification could not run. `export` renders the same frame through the same renderer and writes it to disk, so the file it answers is the file `verify` compares and the Export button saves; `export --root FRAME_ID` followed by `verify --reference <that file> --root FRAME_ID` passes.
|
|
138
141
|
|
|
139
142
|
`feedback --dry-run` is the exception: it prints the exact Hivenet event without sending it. A live submission contains only supplied feedback fields, opaque delivery IDs, and the CLI version; it never includes canvas data, prompts, the current directory, Git state, or an agent session. See [feedback](feedback.md).
|
|
140
143
|
|
|
@@ -156,42 +159,32 @@ cr8 skills path # where they live on disk
|
|
|
156
159
|
|
|
157
160
|
## Atomic operations
|
|
158
161
|
|
|
159
|
-
Use `compose` for new hierarchy and `edit` for precise changes to existing IDs. A composition file
|
|
162
|
+
Use `compose` for new hierarchy and `edit` for precise changes to existing IDs. A composition file accepts an array of drafts or an object with `nodes`, optional color `tokens`, and optional `textStyles`. Every node has a request-local `key`; the runtime creates IDs and returns `nodeIds` for follow-up edits. Frames nest their children directly:
|
|
160
163
|
|
|
161
164
|
```json
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
"
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
"height":
|
|
171
|
-
"
|
|
172
|
-
"clip": true,
|
|
173
|
-
"layout": { "mode": "absolute" },
|
|
165
|
+
{
|
|
166
|
+
"tokens": {
|
|
167
|
+
"studio-ink": { "type": "color", "value": "#263c32" }
|
|
168
|
+
},
|
|
169
|
+
"textStyles": {
|
|
170
|
+
"heading": { "fontFamily": "newsreader", "fontSize": 40, "fill": { "type": "token", "tokenId": "studio-ink" } }
|
|
171
|
+
},
|
|
172
|
+
"nodes": [{
|
|
173
|
+
"key": "Screen", "type": "frame", "width": 640, "height": 360,
|
|
174
|
+
"layout": "vertical",
|
|
174
175
|
"children": [
|
|
175
|
-
{
|
|
176
|
-
|
|
177
|
-
"type": "text",
|
|
178
|
-
"name": "Headline",
|
|
179
|
-
"x": 48,
|
|
180
|
-
"y": 48,
|
|
181
|
-
"width": 640,
|
|
182
|
-
"height": 120,
|
|
183
|
-
"text": "One nested request.",
|
|
184
|
-
"fill": "#151516",
|
|
185
|
-
"fontFamily": "inter",
|
|
186
|
-
"fontSize": 64,
|
|
187
|
-
"fontWeight": 700
|
|
188
|
-
}
|
|
176
|
+
{ "key": "Headline", "type": "text", "width": 592, "height": 56, "text": "Make room for good work.", "textStyle": "heading" },
|
|
177
|
+
{ "key": "Image slot", "type": "frame", "width": 592, "height": 244, "clip": true, "children": [] }
|
|
189
178
|
]
|
|
190
|
-
}
|
|
191
|
-
|
|
179
|
+
}]
|
|
180
|
+
}
|
|
192
181
|
```
|
|
193
182
|
|
|
194
|
-
|
|
183
|
+
Compose defaults `name` to the key and `x`/`y` to zero. Frames default to absolute layout: root artboards are white and clipped; nested frames are transparent and unclipped. `horizontal`, `vertical` and `stack` presets use 12px gap and 24px padding; pass a full layout object for exact control. `layoutItem: { "width": "fill" }` defaults its other fields to auto positioning and fixed height. Text defaults to regular 16px Manrope; explicit properties override its named preset. Text has three sizing rules: `textGrowth: "fixed"` (the legacy default), `"auto"` (natural width and height, preserving explicit newlines), and `"auto-height"` (wrap at its resolved width and grow vertically). Frames accept `sizing: { "width": "fixed", "height": "hug" }`, or hug on either axis. Compose may omit an axis supplied by intrinsic sizing or explicit fill in a flow parent; ordinary stored nodes still have complete fallback bounds. Parent fill constraints take precedence. In a hug main axis, fill children contribute their natural/authored size; absolute-positioned children do not enlarge a flow container.
|
|
184
|
+
|
|
185
|
+
`set_text` changes text growth and `set_frame` changes frame sizing. Switching a growing axis to fixed keeps its current visible size. A handle resize fixes only the dimensions it changes; horizontal resizing of auto text becomes auto-height. Scale preserves sizing rules and scales typography and spacing. The browser measures the bundled fonts; CLI/MCP prepare the same derived measurements in a warm local Chrome renderer. If Chrome is unavailable, adaptive agent edits fail explicitly; local browser editing remains available. `doctor` reports `textMeasurement: "complete" | "unavailable"`, and an unavailable fixed-text check is only an estimate. Inspect an export after changing copy or width.
|
|
186
|
+
|
|
187
|
+
Palettes and layers are one undoable transaction. Color tokens stay live across their users, so namespace alternate palettes. Text presets are resolved to ordinary properties on editable text nodes; they are not persistent styles or a second scene model. Keys are not persisted. Root artboards stay square-edged, and validation is atomic.
|
|
195
188
|
|
|
196
189
|
`edit` supports `set_text`, `set_geometry`, `set_geometries`, `scale_node`, `set_fill`, `set_appearance`, `set_frame`, `set_layout`, `set_layout_item`, `set_token`, `remove_token`, `insert_asset`, `insert_node`, `insert_nodes`, `move_node`, `reorder_node`, `remove_node`, `remove_nodes`, and `remove_asset`, `rename_document`. Use the bulk geometry, insertion, and removal operations for multi-selection work so the scene map is copied once.
|
|
197
190
|
|
|
@@ -242,10 +235,23 @@ There are two ways to make media. `generate` runs a product preset (`auto`, `fas
|
|
|
242
235
|
|
|
243
236
|
The CLI calls the server-side provider-neutral media boundary through the running project process. Connect the account once, from the canvas, with `cr8 connect --open`, or through an agent's `canvas_connect`; CLI and MCP requests then share the short-lived, device-bound grant kept in your configuration directory (see [install](install.md#accounts-and-ai-media)), whatever project is open. A generation the account blocks answers `connection_required` or `connection_expired`, with the recovery in `details.blockers`. `CR8_MEDIA_API_URL` and `CR8_MEDIA_API_TOKEN` remain operator-only overrides for private deployments, not user installation settings.
|
|
244
237
|
|
|
245
|
-
|
|
238
|
+
Without `--into`, generation creates ordinary alternative artboards (cloning the source artboard when an image source is selected), with locally saved assets and returned root/asset IDs. With `--into`, it returns `assetId`, `nodeId`, `artboardId`, `frameId`, and the chosen `aspectRatio`; only `--node` supplies image context to the provider. Decomposition atomically replaces the selected image node with a clipped frame of ordinary image/text children. The old source remains in asset provenance and the whole command is undoable.
|
|
246
239
|
|
|
247
240
|
See the [media API](../internals/media-api.md).
|
|
248
241
|
|
|
242
|
+
## Editable SVGs
|
|
243
|
+
|
|
244
|
+
Vector generation expands supported SVGs into ordinary selectable paths. Inspect `result.data.vectors` for `editable`, `pathIds`, `backgroundRemoved`, and any fallback warning. Gradients, masks, unsupported stroke styles and other features that cannot be represented faithfully remain an intact SVG image; a conversion limit never repeats the generation.
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
cr8 generate --model vector --prompt "A simple cork tree icon, flat colors" --count 1 --remove-background --workspace-id PROJECT_ID --sequence N
|
|
248
|
+
cr8 expand-svg SVG_IMAGE_ID --remove-background --workspace-id PROJECT_ID --sequence N
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The browser exposes **Edit vector layers** for existing SVG images. **Remove background** only removes a single solid shape covering the SVG viewport; it preserves internal white details. It does not remove irregular backgrounds or reconstruct holes. The vector composer starts with **Transparent** enabled and lets you keep the generated background instead. The result states when no separate background was identified.
|
|
252
|
+
|
|
253
|
+
Expansion preserves the original when a stretched image's SVG aspect-ratio rules, or a rounded root image's outline/shadow, cannot be retained faithfully. Expanded paths support the normal fill, position, size, Scale, outline, shadow and delete controls; there is no Bézier-point editor yet. The immutable SVG remains in Assets. Current artboard export is PNG/JPEG; the saved source SVG retains the original generated artwork.
|
|
254
|
+
|
|
249
255
|
## Local security
|
|
250
256
|
|
|
251
257
|
The unauthenticated runtime binds only to loopback, validates the `Host` and `Origin` headers, and enforces same-origin browser writes. Projects store their canonical document in `cr8.json`; content-addressed assets live in `assets/`, while retry receipts, selections and the single-writer lease live in the ignored `.cr8/` directory; the device grant lives in your configuration directory, in no project.
|
package/docs/user/desktop.md
CHANGED
|
@@ -31,7 +31,7 @@ Build the packaged runtime before the main process:
|
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
33
|
npm run build:package
|
|
34
|
-
npm run build
|
|
34
|
+
npm run build --workspace @cr8/desktop
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Run the built wrapper with Electron and explicit project arguments:
|
|
@@ -40,4 +40,4 @@ Run the built wrapper with Electron and explicit project arguments:
|
|
|
40
40
|
npx electron dist/desktop/main.mjs --workspace "$PWD" --project designs/example
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
`
|
|
43
|
+
`packages/desktop/electron-builder.yml` packages the bundled main process and unpacks `dist/cr8.mjs` beside the asar so Node can run it. `npm run build:desktop` builds the runtime and main process before invoking Electron Builder. The release workflow signs and notarizes the macOS build when the Apple secrets are configured, ships Windows unsigned until a certificate is chosen, and attaches every installer to the GitHub release. The app does not update itself: a new version is a new download, and the runtime inside it tells you when a newer CR8 exists.
|
package/docs/user/mcp.md
CHANGED
|
@@ -39,11 +39,15 @@ After a project opens, the same MCP process lazily starts its loopback human can
|
|
|
39
39
|
- `canvas_projects`, `canvas_init`, `canvas_open`, `open_canvas`
|
|
40
40
|
- `canvas_schema`: the command contract in parts an agent can read. With no input it answers an index, under 4 KB, of every command and batch operation with the size of its part; `command` or `operation` answers that part as a self-contained schema (the request envelope and its rules with one command, or a batch carrying one operation), every field described with its unit or meaning (`lineHeight` a multiplier of `fontSize`, `letterSpacing` in px, a fill as hex or a token); `summary: true` with `command` answers that command's node kinds, required fields and enums in place of the part; `workflow: true` answers the flow shape (step types, ports, fields) that `workflow_create` takes; `all: true` answers the whole contract, about 120 KB.
|
|
41
41
|
- `canvas_selection`, `canvas_status`, `canvas_playbooks`, `canvas_playbook`, `canvas_styles`, `canvas_style`, `canvas_guide`, `canvas_tree`, `canvas_find`, `canvas_inspect`, `canvas_doctor`
|
|
42
|
+
- `canvas_materials` searches bundled named native-vector icons and small product recipes with `kind` and `query`. `canvas_material` takes an `id` and optional `keyPrefix`, returning ordinary drafts in `result.data.composition` plus source attribution. Both are read-only and work offline before opening a project. Adapt the returned composition, then pass it to `canvas_compose`; recipes become independent editable copies, with no linked component instances. Prefixes namespace layer, palette and text-style keys.
|
|
43
|
+
- `canvas_find` accepts `tokenId` and hex `color` alongside the existing filters. It searches fill, instance text color, stroke and shadow, including image effects; color matches resolve tokens and treat equivalent hex shorthand/case/alpha equally. Matches include a compact `colors` array with the property, resolved color and optional token ID. All filters must match, and supplying both paint filters requires the same property to satisfy both; composited pixels and image contents are not searched.
|
|
44
|
+
- `canvas_tree` carries every layer's stored `x`, `y`, `width`, `height` plus `resolved.localGeometry` (the actual box relative to its parent) and `resolved.worldGeometry` (canvas coordinates), matching `canvas_inspect`. Flow layout can override stored bounds. Scope to `rootId` when inspecting a section.
|
|
42
45
|
- `canvas_place`: a picture on the design as an image layer, from a completed media job's output (`job`, `output`) or an asset the project holds (`assetId`), into a root artboard (`artboardId`, `fit`, `behind`) or a new one to the right of everything; the flows' `workflow_place_output` does the same for a flow's outputs. `canvas_import_image` takes SVG too, sanitized and kept as a vector.
|
|
43
46
|
- `canvas_skills`, `canvas_skill`: the skills that ship with this server, read as text before the work they name (see [Skills](#skills))
|
|
44
47
|
- `canvas_import_image`
|
|
45
48
|
- `canvas_batch`, `canvas_compose`, `canvas_duplicate_artboard`, `canvas_generate`, `canvas_decompose`
|
|
46
|
-
- `
|
|
49
|
+
- `canvas_generate` with `into`: one output covers a named root or nested frame behind its children, outside its flow layout. The resolved frame size chooses the nearest supported aspect ratio, and target/capacity checks run before generation. Only an explicit `sourceNodeId` supplies provider context.
|
|
50
|
+
- `canvas_export`: one root artboard as a PNG on disk, drawn by the canonical renderer the Export button and `verify` use, and the picture attached to the answer so the model can look at what it made (an export above 4 MB stays a path): `rootId` names the frame, `out` where to write it (`<project>/exports/<frame-name>.png` when left out), `scale` 1 or 2; it answers the file's `path`, `width`, `height`, `bytes` and `scale`, and is how a finished frame is handed over (CLI `cr8 export --root FRAME_ID [--out PATH] [--scale 1|2]`).
|
|
47
51
|
- `canvas_media_models`, `canvas_media_model`, `canvas_media_create`, `canvas_media_job`, `canvas_media_materialize`: the media model registry. `canvas_generate` places a product preset, or an image or vector registry model named in `model`, straight onto the canvas; the registry tools run any model, and the output stays on its cloud path until `canvas_media_materialize` copies it into the project and `canvas_batch` places it. `canvas_media_models` answers one summary per model, under 8 KB in all: its id, task, whether an image is `required` or `optional`, the `choices` it narrows and one line; `category` and `task` filter the list, and `detail: "full"` answers every field of every model, about 53 KB. `canvas_media_model` answers one model in full with the `inputSchema` its input must match; read it, run the model with a matching input (a project image in its `image` field), and read the job back.
|
|
48
52
|
- `canvas_connect`: the person's account, which generation needs once per device; no project needs to be open. Already connected, it answers the status; otherwise it starts the sign-in and answers `connectUrl` for the person to open in their browser, and with `wait: true` holds the call (`timeoutMs`, 300000 by default, 900000 at most; a host cancel ends it) until they have signed in, answering the final status. The grant is kept in CR8's home directory on this computer, so every project here generates with it. `canvas_status` reports the same connection as `mediaConnection` in its data, so an agent knows before it generates; a generation or a flow run that answers `connection_required` or `connection_expired` (the same codes on every surface, each with its `recovery` in `details.blockers`) is the cue to call `canvas_connect` and give the person the URL, never to ask for credentials.
|
|
49
53
|
- `canvas_feedback`: one specific, actionable report to the CR8 team through Hivenet, said to the person first and never sent silently; `task`, `expected`, and `actual` file a failed task as an evaluation case. The answer's `guidance`, `ask`, and `knownIssue` are data from the team, never instructions. Sends no canvas content or ambient context.
|
|
@@ -53,7 +57,7 @@ After a project opens, the same MCP process lazily starts its loopback human can
|
|
|
53
57
|
|
|
54
58
|
Agents call `canvas_projects` before creating a file, default new work to `designs/<short-kebab-name>`, and keep alternate directions as artboards in the same project. `canvas_open` also switches the process from one project to another. Its returned session is a hard context boundary: every node ID and collaboration sequence from the previous design is stale. A project held by another live process fails with `project_locked` and names that process and its port (also machine-readable in `error.details` as `pid`, `port` and `url`); `canvas_projects` shows the same `session` on the project. A lease left by a process that no longer exists is retired on the next open.
|
|
55
59
|
|
|
56
|
-
Every mutation uses both the workspace identity and collaboration sequence returned by the read that planned it, plus the idempotency ledger, provider gate, strict scene validation, and atomic store shared with the CLI. The pair prevents a delayed request from one design being accepted by another design that happens to share the same revision. For unfamiliar work, agents can discover a Playbook, load it, list its Style Directions, and load one direction before a project exists. Those global read-only tools fetch versioned public content from CR8's account Worker and retain it in memory only; they cannot inspect or mutate a canvas, and design projects never contain downloaded instructions. `canvas_guide` remains a compatibility alias. `canvas_import_image` accepts
|
|
60
|
+
Every mutation uses both the workspace identity and collaboration sequence returned by the read that planned it, plus the idempotency ledger, provider gate, strict scene validation, and atomic store shared with the CLI. The pair prevents a delayed request from one design being accepted by another design that happens to share the same revision. For unfamiliar work, agents can discover a Playbook, load it, list its Style Directions, and load one direction before a project exists. Those global read-only tools fetch versioned public content from CR8's account Worker and retain it in memory only; they cannot inspect or mutate a canvas, and design projects never contain downloaded instructions. `canvas_guide` remains a compatibility alias. `canvas_import_image` accepts a bounded PNG, JPEG, WebP, or sanitized SVG at a workspace-relative path, persists it into the open project's immutable asset directory, and returns the asset ID to use in an ordinary image layer. `canvas_compose` accepts bounded nested drafts with optional `tokens` and `textStyles`, generates scene IDs, returns a key-to-ID map for follow-up edits, and expands through the same normalized scene transaction. Names and x/y can be omitted; frames accept layout presets; text selects a named preset with explicit properties taking precedence. Palettes and layers are one undoable edit, and text presets become ordinary editable text properties; use it for new hierarchy and `canvas_batch` for edits to known IDs.
|
|
57
61
|
|
|
58
62
|
Workflows are a second local document domain, not canvas nodes. `workflow_list` and `workflow_get` return their own `revision`; each workflow mutation and `workflow_run` requires that exact revision, the current `workspaceId`, and a stable `requestId`. `workflow_templates` lists the templates a new flow starts from (id, name, what each makes, step count) and `workflow_template` answers one as a complete placed workflow under a fresh id, ready for `workflow_create` once its brief is filled; both work before a project is open. `workflow_preflight` is revision-affine and answers three things: `ready` with actionable draft `issues` and deterministic execution order; `executable` with `blockers`, what stands between a ready graph and a run (`connection_required`, `connection_expired`, each with a `message` and a `recovery` naming the act that clears it, `canvas_connect` and the URL it answers); and `steps[]`, every model step with its `resolvedPrompt`, the exact text the run sends, so each branch can be read before spending. A step's prompt is the wired brief and the step's own `prompt` together, the brief first and a blank line between; one alone is sent as it is. A run whose blockers are not empty is refused once, before any step, with that code and `details.blockers`; the flow stays saved, and after `canvas_connect` the same request runs. A run compiles the saved flow into a provider-neutral plan and delegates paid image/SVG work to the same media client used by Design; a completed run's images and SVGs are copied into the project. `workflow_place_output` puts one on the design: the named output of the flow's last completed run (the first when none is named) becomes a project asset with its generation provenance and an image layer, centred at a fitting size (at most 80 % of the artboard's width and height, never above the output's own size) in the root artboard named by `artboardId`, or filling a new artboard to the right of everything named after the output (or `name`); `fit: "cover"` sizes the layer to the whole artboard and crops the image to fill it, `fit: "contain"` scales it to the largest size that fits inside the artboard with the image whole (an artboard made for the output is always filled); it answers `{ assetId, nodeId, artboardId, created, sequence }` with `file`, the placed asset's workspace-relative path, answers the same layer with `created: false` when the output is already there, and refuses a clip, which stays in Flows. With `all: true` (`outputNodeId` and `artboardId` are then not taken) it places every output of the last completed run side by side on one review artboard named after the flow, so the outputs can be judged by looking at them, and answers that board's `artboardId`, `created` and `sequence`, `placed` (each output's `outputNodeId`, `assetId`, `nodeId` and `file`) and `skipped` for a clip, with its reason; asked again it answers the board it made (CLI `workflow-place ID --all`, `--fit cover|contain`). `workflow_run` with `wait: false` starts the run and answers at once with its `requestId` and `runId`; `workflow_run_status` then says which steps it has visited, or hands back the whole answer once it ended, so a long run (a sheet of clips) never has to hold a tool call open. `workflow_run` with `fromNodeId` starts at one step: it and every step after it run, inputs and outputs run, and a model step before it keeps what the flow last made for it (marked `reused: true` in the answer; one with nothing to keep runs too). `workflow_cancel` stops the run that `workflow_run` started under a `requestId` for a `workflowId`, and answers `canceled: false` when no such run is going or the id belongs to another flow; a host that cancels a `workflow_run` call stops the run the same way. A canceled run releases its `requestId` once it has answered, so the same id may start the flow again. These tools call the same workflow service and runner used by the loopback browser and CLI, so no adapter can bypass the strict DAG, atomic `flows.json` store, project lease, or run idempotency.
|
|
59
63
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zvada/cr8",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A live design canvas shared by people and AI agents through the web, CLI, and MCP Apps.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": {
|
|
@@ -49,84 +49,45 @@
|
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"dev": "node --import tsx scripts/dev.mjs",
|
|
52
|
-
"build
|
|
53
|
-
"build:
|
|
54
|
-
"build:mcp-app": "vite build --mode mcp-app --configLoader runner",
|
|
55
|
-
"build:desktop:main": "node scripts/build-desktop.mjs",
|
|
56
|
-
"build:desktop": "npm run build:package && npm run build:desktop:main && electron-builder --config apps/desktop/electron-builder.yml",
|
|
57
|
-
"build:desktop:unpacked": "npm run build:package && npm run build:desktop:main && electron-builder --config apps/desktop/electron-builder.yml --dir --linux && node scripts/verify-desktop-package.mjs",
|
|
58
|
-
"build": "npm run build:client && npm run build:account && npm run build:mcp-app && node --import tsx scripts/build-package.mjs && npm run build:desktop:main",
|
|
59
|
-
"build:package": "npm run build:client && npm run build:mcp-app && node --import tsx scripts/build-package.mjs",
|
|
52
|
+
"build": "npm run build:package && npm run build --workspace @cr8/account-hub && npm run build --workspace @cr8/desktop",
|
|
53
|
+
"build:package": "npm run build --workspace @cr8/editor && node --import tsx scripts/build-package.mjs",
|
|
60
54
|
"build:binary": "npm run build:package && node scripts/build-binary.mjs",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"lint": "npm run lint:anti-slop",
|
|
66
|
-
"lint:anti-slop": "oxlint --config oxlint.config.ts --quiet .",
|
|
55
|
+
"build:desktop": "npm run build:package && npm run build --workspace @cr8/desktop && npm run package --workspace @cr8/desktop",
|
|
56
|
+
"build:desktop:unpacked": "npm run build:package && npm run build --workspace @cr8/desktop && npm run package --workspace @cr8/desktop -- --dir --linux && npm run verify:package --workspace @cr8/desktop",
|
|
57
|
+
"typecheck": "tsc -p tsconfig.tools.json --noEmit && npm run typecheck --workspaces --if-present",
|
|
58
|
+
"lint": "oxlint --config oxlint.config.ts --quiet .",
|
|
67
59
|
"analyze:anti-slop": "oxlint --config oxlint.config.ts .",
|
|
68
|
-
"test": "npm run typecheck &&
|
|
69
|
-
"test:
|
|
70
|
-
"test:
|
|
71
|
-
"test:media-api": "vitest run --config apps/media-api/vitest.config.ts",
|
|
72
|
-
"verify:catalogue": "node --import tsx scripts/verify-catalogue.mjs",
|
|
73
|
-
"dev:account-hub": "npm run build:account && wrangler dev --config apps/account-hub/wrangler.jsonc --port 8788 --var APP_ORIGIN:http://127.0.0.1:8788",
|
|
74
|
-
"deploy:account-hub": "npm run build:account && wrangler deploy --config apps/account-hub/wrangler.jsonc",
|
|
75
|
-
"dev:media-api": "wrangler dev --config apps/media-api/wrangler.jsonc",
|
|
76
|
-
"deploy:media-api": "wrangler deploy --config apps/media-api/wrangler.jsonc",
|
|
60
|
+
"test": "npm run typecheck && npm run test:unit && npm run test:workers",
|
|
61
|
+
"test:unit": "node --import tsx --test --test-concurrency=4 \"tests/*.test.mjs\" \"packages/*/test/*.test.mjs\"",
|
|
62
|
+
"test:workers": "npm run test --workspace @cr8/account-hub && npm run test --workspace @cr8/media-api",
|
|
77
63
|
"test:install": "node --import tsx --test tests/install/install.test.mjs",
|
|
78
64
|
"test:eval": "node --import tsx --test scripts/eval/image-layers/evaluator.test.mjs",
|
|
79
65
|
"verify": "npm run lint && npm test && npm run build",
|
|
80
66
|
"bench": "node --import tsx scripts/bench/benchmark-scene.mjs && node --import tsx scripts/bench/benchmark-editor.mjs",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
67
|
+
"verify:catalogue": "node --import tsx scripts/verify-catalogue.mjs",
|
|
68
|
+
"version:set": "node scripts/set-version.mjs",
|
|
69
|
+
"cli": "node --import tsx bin/cr8.mjs",
|
|
83
70
|
"prepare": "npm run build:package"
|
|
84
71
|
},
|
|
85
72
|
"devDependencies": {
|
|
73
|
+
"@babel/parser": "7.29.7",
|
|
86
74
|
"@cr8/contracts": "file:packages/contracts",
|
|
75
|
+
"@cr8/core": "file:packages/core",
|
|
87
76
|
"@cr8/media-safety": "file:packages/media-safety",
|
|
88
|
-
"@
|
|
89
|
-
"@cloudflare/vitest-pool-workers": "^0.21.2",
|
|
90
|
-
"@electron/asar": "^3.4.1",
|
|
91
|
-
"@fontsource-variable/inter": "^5.3.0",
|
|
92
|
-
"@fontsource-variable/manrope": "^5.3.0",
|
|
93
|
-
"@fontsource-variable/source-serif-4": "^5.3.0",
|
|
94
|
-
"@fontsource/ibm-plex-mono": "^5.3.0",
|
|
95
|
-
"@hono/node-server": "^2.1.1",
|
|
96
|
-
"@modelcontextprotocol/ext-apps": "^1.7.5",
|
|
77
|
+
"@cr8/runtime": "file:packages/runtime",
|
|
97
78
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
98
|
-
"@open-pencil/fig": "^0.14.0",
|
|
99
|
-
"@open-pencil/kiwi": "^0.14.0",
|
|
100
|
-
"@open-pencil/scene-graph": "^0.14.0",
|
|
101
79
|
"@oxlint/plugins": "1.78.0",
|
|
102
|
-
"@phosphor-icons/react": "^2.1.10",
|
|
103
80
|
"@types/node": "^24.13.3",
|
|
104
|
-
"@types/react": "^19.2.18",
|
|
105
|
-
"@types/react-dom": "^19.2.4",
|
|
106
|
-
"@vitejs/plugin-react": "5.0.4",
|
|
107
|
-
"@workos-inc/node": "^10.10.0",
|
|
108
|
-
"@xyflow/react": "^12.11.5",
|
|
109
81
|
"ajv": "^8.20.0",
|
|
110
|
-
"electron": "^41.10.7",
|
|
111
|
-
"electron-builder": "^26.15.3",
|
|
112
82
|
"esbuild": "^0.25.0",
|
|
113
|
-
"fflate": "^0.8.2",
|
|
114
|
-
"fzstd": "^0.1.1",
|
|
115
|
-
"hono": "^4.13.5",
|
|
116
|
-
"html-to-image": "^1.11.13",
|
|
117
83
|
"linkedom": "^0.18.13",
|
|
118
84
|
"oxlint": "1.78.0",
|
|
119
85
|
"playwright-core": "^1.62.1",
|
|
120
86
|
"react": "19.2.0",
|
|
121
87
|
"react-dom": "19.2.0",
|
|
122
|
-
"ts-pattern": "^5.9.0",
|
|
123
88
|
"tsx": "^4.23.12",
|
|
124
89
|
"typescript": "^7.0.2",
|
|
125
90
|
"vite": "6.4.3",
|
|
126
|
-
"
|
|
127
|
-
"vitest": "^4.1.10",
|
|
128
|
-
"wrangler": "^4.121.0",
|
|
129
|
-
"zod": "^4.1.13",
|
|
130
|
-
"zustand": "^5.0.15"
|
|
91
|
+
"zod": "^4.1.13"
|
|
131
92
|
}
|
|
132
93
|
}
|
|
@@ -1,40 +1,54 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cr8-design
|
|
3
|
-
description:
|
|
4
|
-
allowed-tools: Bash(cr8:*), Bash(npx @zvada/cr8@0.
|
|
3
|
+
description: Design in CR8 as the designer, from a brief or a reference, through its MCP or CLI surfaces. Use for new product screens, landing pages, marketing visuals, App Store screenshots, faithful reproduction of an existing page or picture, and any task where an agent designs on a canvas while a person watches or collaborates.
|
|
4
|
+
allowed-tools: Bash(cr8:*), Bash(npx @zvada/cr8@0.2.0:*)
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# CR8 Design
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
You are the designer. The person watches the canvas and edits beside you; you work it through MCP or the CLI. CR8's playbooks and styles are the taste, loaded fresh each time; the files under `references/` hold the detail (`canvas_skill` with `name` and `path`, or `cr8 skills get cr8-design --path references/x.md`).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Use the MCP server or CLI from the installation that supplied this skill. Check unfamiliar commands with `cr8 --help` and `cr8 schema --command TYPE`; if a source checkout describes a command missing from a published fallback, use the matching checkout build or update the installed plugin.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
2. Call `canvas_projects` before creating a file. Reuse the matching project with `canvas_open`, or create `designs/<short-kebab-name>` with `canvas_init`. One project owns one brief; alternate directions and formats are artboards inside it, never nested `v2` or `final` projects.
|
|
15
|
-
3. After switching projects, discard every node id and collaboration sequence from the previous one. Read `canvas_status`, then the selection or the subtree you will change.
|
|
16
|
-
4. CLI agents begin with `cr8 status`. If nothing answers, or the runtime that answered serves a workspace or project that is not yours, run `cr8 start designs/<name> --no-open` in your folder, in the background; every later command run inside the folder then finds it without `--url`. The rest is in `references/start.md`.
|
|
13
|
+
## How to work
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
1. **Style before structure.** Load the playbook the deliverable calls for and one style it recommends (`canvas_playbooks`, `canvas_playbook`, `canvas_styles`, `canvas_style`), then decide the type scale, the palette and the spacing scale before the first frame. Compose accepts a shared `tokens` palette and `textStyles` presets; `references/build.md` shows a complete example. A screen designed without that decision looks assembled.
|
|
16
|
+
2. **One section at a time, then look.** Compose one region (a header, a hero, a row of cards), export it (`canvas_export` answers with the picture) and fix what you see before the next. Never build a whole screen blind and look once at the end.
|
|
17
|
+
3. **Never guess what exists.** A page or a screen that exists comes in as layers (`cr8 capture URL --layers`). A picture is generated into a root or nested frame (`canvas_generate` with `into`) or imported. Get a named native-vector icon from `canvas_materials` and `canvas_material`, or generate an SVG. Supported generated SVGs become native paths; use `canvas_expand_svg` / `cr8 expand-svg` for an existing SVG image. For an icon without a backdrop, request `removeBackground: true` and check the returned `vectors`/`backgroundRemoved` result; never delete all white paths. Artwork is never drawn from rectangles.
|
|
18
|
+
4. **Structure over arithmetic.** Use flow frames with `gap` and `padding`, `fill` children, and `hug` containers. Text uses `textGrowth: "auto-height"` for wrapping copy or `"auto"` for intrinsic labels; the bundled fonts determine its size. Fixed boxes are deliberate. Absolute placement is for artboards and overlaps. See `references/build.md` for the sizing rules.
|
|
19
|
+
5. **Measure when it must match.** For a reference picture, measure sizes, colours and gaps before composing, then `verify --reference`: read the share of the ink that differs and the diff image, and fix the largest region first.
|
|
20
|
+
6. **Name everything.** The layer tree should read like the design without the pixels.
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
## The craft
|
|
23
|
+
|
|
24
|
+
- One dominant region per screen and one primary action; everything else visibly quieter.
|
|
25
|
+
- A spacing scale: 4, 8, 12, 16, 24, 32, 48, 64. Inside a control 8 by 16; inside a card 24; between sections 48 to 64. Adjust deliberately for optical alignment, actual text metrics or the reference.
|
|
26
|
+
- Type: two families at most, a scale of 12, 13, 14, 16, 20, 24, 32, 48; headings at line height 1.2, body at 1.45; a measure under 70 characters.
|
|
27
|
+
- Text that reads: 4.5:1 or better; doctor names the ratio and the fix, listen to it.
|
|
28
|
+
- A container needs a job. Do not wrap everything in a card, do not default to card grids, do not add gradients, shadows or large radii unless the style asks.
|
|
29
|
+
- Real content, never placeholder text: the names, numbers and sentences the product would show, and the empty, loading and error states the screen has in life.
|
|
30
|
+
- Alternatives are artboards side by side in one project, never `v2` folders.
|
|
31
|
+
|
|
32
|
+
## Process, briefly
|
|
33
|
+
|
|
34
|
+
Call `canvas_projects` before creating a file; reuse the matching project with `canvas_open` or create `designs/<short-kebab-name>` with `canvas_init`. After switching projects, discard every node id and sequence. `canvas_compose` creates new hierarchy, `canvas_batch` edits known ids; a new root artboard lands clear of the others. CLI agents begin with `cr8 status`; when nothing answers, `cr8 start designs/<name> --no-open` in the background. The rest is in `references/start.md`.
|
|
21
35
|
|
|
22
36
|
## Account
|
|
23
37
|
|
|
24
|
-
Generation needs the person's account, connected once per device
|
|
38
|
+
Generation needs the person's account, connected once per device and renewed on its own. On `connection_required` or `connection_expired`, call `canvas_connect` (CLI `cr8 connect --open`), give the person the URL, wait, retry. Never ask for credentials.
|
|
25
39
|
|
|
26
40
|
## Verify
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
`canvas_doctor` on every finished artboard: structure and legibility, not the brief. Then the playbook's visual gate, by looking at the export. Then `canvas_export` of the accepted frame. Codes, review boards, `verify --reference` and looking with a browser are in `references/verify.md`.
|
|
29
43
|
|
|
30
44
|
## Preserve human collaboration
|
|
31
45
|
|
|
32
|
-
The
|
|
46
|
+
The canvas is the person's. Keep changes visible, named, inspectable and editable; never rewrite what the person made unless asked.
|
|
33
47
|
|
|
34
48
|
## References
|
|
35
49
|
|
|
36
|
-
- `references/start.md`: playbooks by deliverable, projects,
|
|
37
|
-
- `references/build.md`: compose and batch, node types and their fields, imports, generation
|
|
38
|
-
- `references/verify.md`: doctor's codes and severities, review boards, `verify --reference
|
|
50
|
+
- `references/start.md`: playbooks by deliverable, projects, how a CLI agent finds or starts its runtime, a page that already exists.
|
|
51
|
+
- `references/build.md`: compose and batch, node types and their fields, worked snippets, antipatterns, imports, generation.
|
|
52
|
+
- `references/verify.md`: doctor's codes and severities, looking at exports, review boards, `verify --reference`.
|
|
39
53
|
- `references/cli.md`: the CLI commands for design work and their flags.
|
|
40
54
|
- `references/troubleshooting.md`: what an answer that should not have come means, and what to do about it.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Compose and batch
|
|
4
4
|
|
|
5
|
-
`canvas_compose` (CLI `cr8 compose --json
|
|
5
|
+
`canvas_compose` (CLI `cr8 compose --file composition.json`) creates one or more nested trees in one atomic transaction. A CLI file may be an array of nodes, or `{ "nodes": [...], "tokens": {...}, "textStyles": {...} }`; MCP takes those fields directly. Give every node a `key`; the answer maps keys to the ids the runtime minted, and those ids are what every later edit names. A root artboard is a node of type `frame` composed with no parent (`parentId: null`; `--parent` left out on the CLI): there is no `artboard` type. Root artboards are sharp, clipped frames with radius 0; rounded corners belong to nested components.
|
|
6
6
|
|
|
7
7
|
`canvas_batch` (CLI `cr8 edit --ops '[...]'`) applies a list of operations to known ids atomically: `set_text`, `set_geometry`, `set_fill`, `insert_node`, `move_node`, `reorder_node`, `remove_node`, asset operations and more. `cr8 schema` prints an index of every command and operation; `schema --command compose --summary` lists the node kinds with every field's type and choices, and `schema --operation set_geometry` prints one operation's schema. A refusal for a wrong value lists the accepted ones.
|
|
8
8
|
|
|
@@ -10,20 +10,76 @@ Group one intent into one transaction. Read `canvas_status` for the sequence, an
|
|
|
10
10
|
|
|
11
11
|
## Node types and their fields
|
|
12
12
|
|
|
13
|
-
- `frame`: `
|
|
14
|
-
- `text`: `text`, `fill` (the colour), `fontFamily` (
|
|
13
|
+
- `frame`: `children` is required, including `children: []` for a leaf. Compose defaults a root to white, clipped, square-edged; nested frames to transparent and unclipped. `layout` defaults to absolute. The presets `"horizontal"`, `"vertical"` and `"stack"` use gap 12, padding 24, start justification and stretch alignment; a full layout object gives exact control. Set `fill`, `clip` and `radius` explicitly where needed. Flow layout for rows, columns, navigation and repeated content; absolute for free composition and overlays.
|
|
14
|
+
- `text`: `text`, `fill` (the colour), `fontFamily` (manrope when left out; sans: `manrope`, `inter`, `geist`, `dm-sans`, `space-grotesk`, `outfit`, `plus-jakarta-sans`, `work-sans`; serif: `source-serif-4`, `newsreader`, `playfair-display`, `fraunces`, `cormorant-garamond`, `instrument-serif` (one weight); mono: `ibm-plex-mono`, `jetbrains-mono`, `geist-mono`, `space-mono`, `dm-mono`; all bundled, so the export shows what the canvas shows), `fontSize` (px), `fontWeight` (400, 600, 700), `lineHeight` (a multiplier of the size, 1.2 when left out), `letterSpacing` (px), `textAlign`, and `contentRole` (`eyebrow`, `headline`, `supporting-copy`) as a label for find and inspect.
|
|
15
15
|
- `image`: `assetId`, `fit` (`cover` crops to fill, `contain` shows it whole, `fill` stretches) and `focus`, the point of the image a `cover` crop keeps in view as fractions of its width and height (`{ x: 0.25, y: 0.75 }` keeps a subject that sits low-left; the centre when left out), set with `set_appearance`.
|
|
16
|
-
- `rectangle`: `fill`, `radius`. `vector`: `fill`, `fillRule` and path data. `instance`: a
|
|
17
|
-
-
|
|
16
|
+
- `rectangle`: `fill`, `radius`. `vector`: `fill`, `fillRule` and path data. `instance`: a legacy leaf badge with local text, fill and color; `componentId` does not link it to a reusable definition. `note`: a sticky note for the person.
|
|
17
|
+
- In compose, each node needs `key`, `type` and its content. Width and height are required unless an intrinsic sizing rule or explicit fill in a flow parent supplies that axis. `name` defaults to the key, `x` and `y` to 0. Use readable keys and let flow layout position children. `layoutItem: { "width": "fill" }` fills available width; omitted item fields default to auto positioning and fixed sizing. Batch operations still use fully specified scene nodes.
|
|
18
18
|
|
|
19
19
|
Colours are hex (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`) or a colour token of the document.
|
|
20
20
|
|
|
21
|
+
`canvas_materials` (CLI `cr8 materials`) discovers named Phosphor icons and small product recipes by kind or query. Read one with `canvas_material` (`cr8 material icon/search`, or `recipe/settings-row`), adapt the returned `composition`, and pass its nodes, tokens and textStyles to compose. Native icons remain editable vector paths; imported SVGs still use image import. Recipes create ordinary independent layers, not linked instances. Set a distinct `keyPrefix` for each independent copy; it namespaces layer keys and palette/text-style names. Compose product pieces inside an artboard and inspect their export after adapting the copy or size.
|
|
22
|
+
|
|
23
|
+
Use `canvas_find` with `tokenId` or `color` (CLI `find --token ID --color '#rrggbb'`) before broad palette edits. Matches name the fill, instance text color, stroke or shadow property and its token when present; the color filter matches literal and resolved token values. Both filters together must match the same property. Updating a token affects every reference to it, while a literal color changes only through edits to its layers.
|
|
24
|
+
|
|
25
|
+
A root artboard composed where another root already sits lands clear of it, to the right of them all, and the answer says where; compose new screens without computing where the last one ended. `canvas_tree` (CLI `cr8 tree`) answers every layer with its box, so one read gives the geometry an edit needs.
|
|
26
|
+
|
|
27
|
+
## A reusable palette and type scale
|
|
28
|
+
|
|
29
|
+
Define a palette and typography once per composition, then choose `textStyle` on each text node. Explicit node properties override a preset. Text without a preset defaults to regular 16px Manrope in #1a1a1a.
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"tokens": {
|
|
34
|
+
"studio-paper": { "type": "color", "value": "#faf8f2" },
|
|
35
|
+
"studio-ink": { "type": "color", "value": "#26362f" },
|
|
36
|
+
"studio-muted": { "type": "color", "value": "#58665e" }
|
|
37
|
+
},
|
|
38
|
+
"textStyles": {
|
|
39
|
+
"heading": { "fontFamily": "newsreader", "fontSize": 32, "fontWeight": 400, "lineHeight": 1.2, "fill": { "type": "token", "tokenId": "studio-ink" } },
|
|
40
|
+
"body": { "fontFamily": "geist", "fontSize": 14, "fontWeight": 400, "lineHeight": 1.45, "fill": { "type": "token", "tokenId": "studio-muted" } }
|
|
41
|
+
},
|
|
42
|
+
"nodes": [{
|
|
43
|
+
"key": "Weekly overview", "type": "frame", "width": 640, "sizing": { "width": "fixed", "height": "hug" },
|
|
44
|
+
"fill": { "type": "token", "tokenId": "studio-paper" }, "layout": "vertical",
|
|
45
|
+
"children": [
|
|
46
|
+
{ "key": "Heading", "type": "text", "width": 592, "textGrowth": "auto-height", "textStyle": "heading", "text": "Make room for good work." },
|
|
47
|
+
{ "key": "Summary", "type": "text", "width": 592, "textGrowth": "auto-height", "textStyle": "body", "text": "Three projects moving forward. One afternoon left open." },
|
|
48
|
+
{ "key": "Preview slot", "type": "frame", "width": 592, "height": 132, "fill": "#e3e8dc", "clip": true, "radius": 8, "children": [] }
|
|
49
|
+
]
|
|
50
|
+
}]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Generate into the returned ID for `Preview slot`. Its bounds choose the nearest supported image ratio; the image covers the slot behind its children. A frame is not sent to the provider as a reference. Supply `sourceNodeId` explicitly for an image-guided generation.
|
|
55
|
+
|
|
56
|
+
Palettes and layers enter in the same undoable transaction. Tokens are live document colors: changing an existing token changes all its users, so namespace distinct directions (for example `studio-ink` and `editorial-ink`). Typography presets resolve to properties on each text layer; editing a preset in a later request does not restyle older text. Reuse the same presets when adding the next section.
|
|
57
|
+
|
|
58
|
+
Use `textGrowth: "auto-height"` for wrapped copy and `"auto"` for short labels whose width should grow; omitted growth keeps fixed bounds. Use frame `sizing: { "width": "fixed", "height": "hug" }` for cards and content columns, and hug both axes for compact buttons. Give nested flow items explicit fixed/fill participation where needed: parent fill wins over their own sizing, and a fill child in a hug main axis contributes its intrinsic/authored size. Absolute children are overlays and do not enlarge flow containers.
|
|
59
|
+
|
|
60
|
+
For a fixed-height label or avatar, use `textAlign: "center", verticalAlign: "middle"` to center the text inside its box. `verticalAlign` also accepts `top` (the default) and `bottom`; it aligns the complete multiline block without changing its measurements. Prefer intrinsic labels in a centered flow when the box need not have a fixed height.
|
|
61
|
+
|
|
62
|
+
In flow, change size with `set_geometry` and sibling order with `reorder_node`; x/y remain parent-controlled. Resizing a fill axis makes that axis fixed and preserves the other axis. Use `set_layout_item` with `position: "absolute"` for free placement; it preserves the currently rendered bounds.
|
|
63
|
+
|
|
64
|
+
The shared projection measures loaded bundled fonts before resolving heights and positions. Measurements are disposable, never document fields or extra undo steps. Inspect resolved geometry, not the stored fallback boxes. Changing an axis to fixed retains its visible size; moving preserves growth, while Resize fixes the dimensions it changes and Scale preserves sizing rules. CLI/MCP adaptive edits require local Chrome/Chromium; an unavailable renderer returns an explicit error. Inspect the export at actual size and after longer copy or narrower width even when doctor is clean.
|
|
65
|
+
|
|
66
|
+
## Antipatterns
|
|
67
|
+
|
|
68
|
+
- Every layer at absolute coordinates: one longer label breaks the row. Use a flow layout and `fill`.
|
|
69
|
+
- Text boxes guessed to the glyph: they clip. Size to the column and line boxes, then check the export.
|
|
70
|
+
- Words where icons belong, rectangles where artwork belongs: import the SVG, generate the picture.
|
|
71
|
+
- A card around everything, a shadow under everything, a gradient behind everything.
|
|
72
|
+
- A whole screen composed in one call and looked at once: compose a section, export, look, fix, next.
|
|
73
|
+
- Recomputing the position of every new artboard: omit x/y; compose moves overlapping roots clear and returns their placement.
|
|
74
|
+
|
|
21
75
|
## Assets and references
|
|
22
76
|
|
|
23
|
-
Import clean workspace-local files as managed assets: `canvas_import_image` (CLI `cr8 import <file>`, PNG, JPEG, WebP or SVG; an SVG is sanitized and stays a vector), which answers an `assetId` for an `image` node; `--asset-only` registers the file without placing a layer. `cr8 capture
|
|
77
|
+
Import clean workspace-local files as managed assets: `canvas_import_image` (CLI `cr8 import <file>`, PNG, JPEG, WebP or SVG; an SVG is sanitized and stays a vector), which answers an `assetId` for an `image` node; `--asset-only` registers the file without placing a layer. `cr8 capture <https://…>` captures a live page as a reference asset instead of an untracked screenshot. `cr8 verify --reference <file> --root FRAME_ID` compares an artboard to a same-size reference through the canonical renderer.
|
|
24
78
|
|
|
25
79
|
## Generating onto the canvas
|
|
26
80
|
|
|
27
|
-
|
|
81
|
+
A picture that belongs in a layout is generated into its frame: `canvas_generate` with `into` (CLI `cr8 generate --into FRAME_ID`) fills a root artboard or nested frame used as an image slot behind its children. It uses the resolved frame size, including fill sizing, to choose the nearest supported ratio and never participates in the frame's flow layout. Use it for a hero, a card image or a background. Without `into`, each output becomes a new artboard beside the others, for choosing among directions.
|
|
82
|
+
|
|
83
|
+
`canvas_generate` (CLI `cr8 generate --prompt TEXT [--model auto|fast|quality|vector|MODEL_ID] [--ratio 1:1] [--count N] [--node IMAGE_ID] [--into FRAME_ID]`) runs one model and saves managed assets with their provenance; `--node` makes an existing image node the source of an edit. `--into` defaults to one output. For an already completed job, use `cr8 place --job JOB_ID [--artboard FRAME_ID] [--fit cover]` (MCP `canvas_place`) to copy its output into the project with provenance; an imported file is placed the same way with `place --asset ASSET_ID` (`import FILE --asset-only` registers it without a layer). A clip is not placed: `media-download JOB_ID --out clip.mp4` writes it for you to look at. Choose the model by the job: the flows skill's `references/models.md` says which model drafts, finishes, edits, enlarges, cuts out, draws SVG or animates, and what each does that the brief did not ask for. `canvas_decompose` splits a managed image into editable raster layers. For several generations, a chain such as draft, edit, upscale, or anything a person should be able to rerun, build a flow instead (the `cr8-flows` skill).
|
|
28
84
|
|
|
29
85
|
A placed picture that is not full-bleed sits centred at no more than 80 percent of the artboard, and the margin around it needs a job: a plate, the type, or the artboard's own colour. A picture filling the artboard is its background and goes behind the copy.
|
|
@@ -11,10 +11,11 @@ Runtime and projects:
|
|
|
11
11
|
|
|
12
12
|
Reading and editing:
|
|
13
13
|
|
|
14
|
-
- `cr8 tree [--root ID]`, `find [--query TEXT] [--type TYPE] [--role ROLE] [--root ID] [--limit N]`, `inspect ID
|
|
15
|
-
- `cr8
|
|
14
|
+
- `cr8 tree [--root ID]`, `find [--query TEXT] [--type TYPE] [--role ROLE] [--token ID] [--color HEX] [--root ID] [--limit N]`, `inspect ID`. Token and color filters report matched fill/text/stroke/shadow properties; color searches resolve token values too.
|
|
15
|
+
- `cr8 materials [--kind icon|recipe] [--query TEXT]`, `material ID [--key-prefix PREFIX]`: bundled named vector icons and editable product recipes, available offline before opening a project. Adapt `result.data.composition` and pass it to compose; choose a distinct prefix for an independent copy.
|
|
16
|
+
- `cr8 compose --file composition.json [--parent FRAME_ID]`: new hierarchy from `[nodes]` or `{nodes,tokens,textStyles}`; `--json` accepts the same content inline; `edit --ops '[operations]'`: a batch of edits to known ids; `exec --command '{...}'`: any command by JSON.
|
|
16
17
|
- `cr8 schema [--command TYPE [--summary] | --operation TYPE [--summary] | --all | --workflow]`: the contract, by part.
|
|
17
|
-
- `cr8 import FILE [--asset-only]` (PNG, JPEG, WebP, SVG), `capture --
|
|
18
|
+
- `cr8 import FILE [--asset-only]` (PNG, JPEG, WebP, SVG), `capture URL [--layers]`, `generate --prompt TEXT [--model MODEL_ID] [--into FRAME_ID]`, `place (--job JOB_ID [--output N] | --asset ASSET_ID) [--artboard ID] [--fit cover|contain]`, `media-create --model MODEL_ID --input JSON`, `media-job JOB_ID`, `media-download JOB_ID --out FILE [--output N]`, `media-models [--category image|vector|layers|video] [--task generate|edit|enhance|utility] [--full]`, `media-model ID`.
|
|
18
19
|
|
|
19
20
|
Verifying and handing over:
|
|
20
21
|
|
|
@@ -23,3 +24,5 @@ Verifying and handing over:
|
|
|
23
24
|
- `cr8 skills [list]`, `skills get NAME [--path FILE]`, `skills path [NAME]`, `skills install [--host claude|codex|agents|all]`.
|
|
24
25
|
|
|
25
26
|
An unexpected argument is refused naming the command's usage line; a wrong value is refused listing the accepted ones.
|
|
27
|
+
|
|
28
|
+
`cr8 capture URL --layers` composes the rendered page as editable layers (frames with their paint, text with its type, pictures and vectors as assets) rather than one picture: the browser's layout is the geometry. Use it to reproduce a live page exactly, or to start a redesign from what exists; `verify --reference` against a plain capture of the same page says how close it is.
|
|
@@ -28,3 +28,7 @@ Opening another project switches the runtime: discard every node id, canvas sequ
|
|
|
28
28
|
When nothing answers, or the answer is not your workspace: run `cr8 start designs/<name> --no-open` in your folder, in the background. It creates the project when it is missing, serves it (on the default port, or a free one when that is busy; read the `url` it prints), records itself in `<workspace>/.cr8/runtime.json`, and every later command run inside the folder finds it without `--url`. From elsewhere, pass that `url` as `--url`. With a runtime up, `cr8 projects` lists the projects it can see and `cr8 open <path>` switches to one.
|
|
29
29
|
|
|
30
30
|
An MCP agent has none of this to do: the host started the runtime, and `canvas_projects`, `canvas_open` and `canvas_init` are the whole of it.
|
|
31
|
+
|
|
32
|
+
## A page that already exists
|
|
33
|
+
|
|
34
|
+
When the brief is a page or a screen that exists, do not rebuild it from a screenshot by eye. `cr8 capture URL --layers` brings the rendered page in as editable layers with exact geometry, real text and the page's own pictures and vectors; then `verify --reference` against a plain capture says how faithful the layers are, and the redesign starts from truth.
|