@zvada/cr8 0.1.0 → 0.3.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 +3 -5
- package/.codex-plugin/mcp.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +34 -16
- package/dist/cr8.mjs +460 -171
- package/docs/user/app-store-screenshots.md +0 -2
- package/docs/user/cli.md +48 -42
- package/docs/user/desktop.md +48 -8
- package/docs/user/feedback.md +5 -1
- package/docs/user/install.md +60 -34
- package/docs/user/mcp.md +52 -18
- package/package.json +27 -59
- 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 +5 -1
- package/skills/cr8-design/references/verify.md +16 -0
- package/skills/cr8-flows/SKILL.md +3 -3
- package/skills/cr8-flows/references/results.md +2 -2
- package/skills/cr8-flows/references/run.md +1 -1
package/docs/user/mcp.md
CHANGED
|
@@ -10,7 +10,7 @@ The MCP server calls the canonical command executor directly. It does not shell
|
|
|
10
10
|
|
|
11
11
|
The embedded App treats the runtime's document as canonical. Human edits that have not reached the runtime yet stay in memory while the App session is mounted; nothing is written to browser storage, so opening a different workspace cannot leak stale canvas state across sessions.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Install from npm
|
|
14
14
|
|
|
15
15
|
Node.js 20.19 or newer:
|
|
16
16
|
|
|
@@ -18,33 +18,38 @@ Node.js 20.19 or newer:
|
|
|
18
18
|
{
|
|
19
19
|
"mcpServers": {
|
|
20
20
|
"cr8": {
|
|
21
|
-
"command": "
|
|
21
|
+
"command": "npx",
|
|
22
22
|
"args": [
|
|
23
|
-
"
|
|
24
|
-
"
|
|
23
|
+
"--yes",
|
|
24
|
+
"@zvada/cr8@latest",
|
|
25
|
+
"mcp-connect"
|
|
25
26
|
]
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
The default stdio transport writes no readiness output because stdout belongs to MCP.
|
|
32
|
+
The default stdio transport writes no readiness output because stdout belongs to MCP. npm downloads the bundled runtime, which includes the CLI, local canvas, MCP App and matching skills. For a controlled rollout, replace `@latest` with a specific released version. See [agent setup](install.md#add-it-to-an-agent) for Codex and Claude Code commands.
|
|
32
33
|
|
|
33
|
-
Installation creates no document and starts no browser. `canvas_init` or `canvas_open` receives an explicit workspace-relative directory. The workspace is, in order, an explicit `--workspace /absolute/root`, the
|
|
34
|
+
Installation creates no document and starts no browser. `canvas_init` or `canvas_open` receives an explicit workspace-relative directory. The workspace is, in order, an explicit `--workspace /absolute/root`, `CLAUDE_PROJECT_DIR`, then the directory where the host launches CR8. The home directory and filesystem root are refused, including through symlinks. Hosts that launch outside the intended project must configure `--workspace`; CR8 does not request the deprecated MCP roots API. Absolute project paths, parent escapes and symlink escapes fail closed.
|
|
34
35
|
|
|
35
|
-
After a project opens, the same MCP process lazily starts its loopback human canvas and returns `canvasUrl`.
|
|
36
|
+
After a project opens, the same MCP process lazily starts its loopback human canvas and returns `canvasUrl`. `mcp-connect` joins a compatible runtime already open in the session's workspace. Server credentials never belong in an end-user MCP snippet.
|
|
36
37
|
|
|
37
38
|
## Model tools
|
|
38
39
|
|
|
39
40
|
- `canvas_projects`, `canvas_init`, `canvas_open`, `open_canvas`
|
|
40
41
|
- `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
|
-
- `canvas_selection`, `canvas_status`, `canvas_playbooks`, `canvas_playbook`, `canvas_styles`, `canvas_style`, `
|
|
42
|
-
- `
|
|
43
|
-
- `
|
|
42
|
+
- `canvas_selection`, `canvas_status`, `canvas_playbooks`, `canvas_playbook`, `canvas_styles`, `canvas_style`, `canvas_tree`, `canvas_find`, `canvas_inspect`, `canvas_doctor`
|
|
43
|
+
- `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.
|
|
44
|
+
- `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.
|
|
45
|
+
- `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.
|
|
46
|
+
- `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. Send `workspaceId` and `sequence` from `canvas_status`, and reuse `requestId` on retries; asset registration and placement commit together once. Placement does not support dry runs; 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.
|
|
47
|
+
- `list_skills`, `read_skill`: the skills that ship with CR8 for design, flows and feedback, read as text before the work they name (see [Skills](#skills))
|
|
44
48
|
- `canvas_import_image`
|
|
45
49
|
- `canvas_batch`, `canvas_compose`, `canvas_duplicate_artboard`, `canvas_generate`, `canvas_decompose`
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
50
|
+
- `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.
|
|
51
|
+
- `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]`).
|
|
52
|
+
- `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_place({ job, workspaceId, sequence, requestId })` copies it into the project, registers its asset and places a layer. `canvas_media_materialize` only copies bytes and returns their source and dimensions; it does not create a scene asset or layer. `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
53
|
- `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
54
|
- `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.
|
|
50
55
|
- `workflow_list`, `workflow_get`, `workflow_templates`, `workflow_template`, `workflow_create`, `workflow_replace`, `workflow_delete`, `workflow_preflight`, `workflow_run`, `workflow_run_status`, `workflow_cancel`, `workflow_place_output`
|
|
@@ -53,17 +58,33 @@ After a project opens, the same MCP process lazily starts its loopback human can
|
|
|
53
58
|
|
|
54
59
|
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
60
|
|
|
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. `
|
|
61
|
+
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_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
62
|
|
|
58
|
-
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
|
|
63
|
+
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 keeps its managed cloud outputs; only explicit placement copies an image or SVG 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.
|
|
64
|
+
|
|
65
|
+
`canvas_export` writes only PNGs inside the workspace, defaulting to `<project>/exports/<frame-name>.png`. It refuses absolute paths, traversal, symlinks, internal directories, immutable assets and existing non-PNG files. Re-exporting replaces an existing PNG atomically. The attached image always comes from the captured render, even if another writer replaces the file afterward. The local CLI's explicit `export --out` continues to support absolute output paths.
|
|
59
66
|
|
|
60
67
|
## Skills
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
Choose either ordinary MCP tools (`list_skills` and `read_skill`) or the [MCP Skills extension](https://github.com/modelcontextprotocol/ext-skills/blob/main/specification/stable/skills.mdx), `io.modelcontextprotocol/skills`. Both serve the same bundled files for all of CR8 and work before opening a project.
|
|
70
|
+
|
|
71
|
+
With tools, read `read_skill({ name: "cr8-design" })` before design work or `read_skill({ name: "cr8-flows" })` before building or running flows. Read a supporting file with `read_skill({ name: "cr8-flows", path: "references/models.md" })`. Feedback guidance uses `name: "cr8-feedback"`.
|
|
72
|
+
|
|
73
|
+
The extension advertises the same guidance at `skill://cr8-design/SKILL.md`, `skill://cr8-flows/SKILL.md` and `skill://cr8-feedback/SKILL.md`:
|
|
74
|
+
|
|
75
|
+
- `skills/list` returns the complete bundled catalog, including each skill's full YAML frontmatter as JSON and a manifest of every file's URI, UTF-8 byte size and SHA-256 digest.
|
|
76
|
+
- `skills/get({ uri })` returns the same entry for one `SKILL.md` URI. It is an alternative to listing; a list response already contains everything needed to read files.
|
|
77
|
+
- `resources/list` lists the files alongside the existing MCP App and canvas image resources. `resources/read({ uri })` reads one file, including supporting references such as `skill://cr8-flows/references/models.md`.
|
|
78
|
+
|
|
79
|
+
These are MCP protocol methods, not tools called through `tools/call`. Discovery works before a project is open and creates no files. The bundled catalog fits in one page, so no continuation cursors are issued; unknown cursors and skill URIs return `-32602`. The optional directory-read feature is not advertised: the manifest already enumerates every supporting file. Skill entries and content carry a five-minute public cache hint; digests cover the actual returned bytes. A new runtime version may change those digests.
|
|
80
|
+
|
|
81
|
+
Read only the skill and references relevant to the work. Reading a resource does not install or automatically activate a skill; the host controls activation and any permissions, including `allowed-tools`.
|
|
82
|
+
|
|
83
|
+
CR8 uses the stable MCP TypeScript SDK v2 and requires protocol `2026-07-28`. The SDK handles native `server/discover`, per-request metadata, cancellation and subscription streams for stdio and Streamable HTTP. Older `initialize` handshakes are rejected. A host must support this protocol; it can then choose either the Skills extension or the ordinary `list_skills` / `read_skill` tools. Both interfaces are current and share one catalog.
|
|
63
84
|
|
|
64
|
-
`
|
|
85
|
+
`list_skills` takes no input and returns each shipped skill's `name`, `description`, `readWhen` (the sentence of the description that says when it applies) and `files`. `read_skill` takes `{ name, path? }` and returns the skill's `SKILL.md` as text, or the file named by `path` (for example `agents/openai.yaml`); the structured result carries the same `content` with its `path`. Both are read-only. An unknown name or file is refused with `skill_not_found` or `skill_file_not_found` and a sentence naming what exists.
|
|
65
86
|
|
|
66
|
-
Each skill is a short contract in `SKILL.md` plus a `references/` folder the contract's last section lists: the design skill's start, build, verify, CLI and troubleshooting references; the flows skill's models (which model for which job, and what each does that the brief did not ask for), build, run, results, CLI and troubleshooting references. `
|
|
87
|
+
Each skill is a short contract in `SKILL.md` plus a `references/` folder the contract's last section lists: the design skill's start, build, verify, CLI and troubleshooting references; the flows skill's models (which model for which job, and what each does that the brief did not ask for), build, run, results, CLI and troubleshooting references. `read_skill` with `path` (`references/models.md`, say) returns one; `list_skills` lists every file a skill carries.
|
|
67
88
|
|
|
68
89
|
The skills are embedded in the runtime at build time, so the text always matches the version that serves it. They are the same files `cr8 skills get` prints and `cr8 skills install` puts into the hosts' skill directories; see the [CLI guide](cli.md#skills).
|
|
69
90
|
|
|
@@ -87,6 +108,14 @@ Selection does not mutate the scene, consume a collaboration sequence, send a ch
|
|
|
87
108
|
|
|
88
109
|
Hosts without Apps can still call `canvas_selection` and open the `canvasUrl` returned by `canvas_init`, `canvas_open`, or `open_canvas`. That browser is served by the same MCP process and shares the same selection state; no second command is required.
|
|
89
110
|
|
|
111
|
+
The agent should show that loopback link after opening a project. It opens the human editor on the same computer while the host-managed MCP process remains running; the optional desktop app is not required. An embedded canvas appears only when the host renders MCP Apps, so a successful MCP connection alone does not guarantee an inline editor. After rebuilding or updating an installed server, reconnect it in the host to load the new runtime and embedded UI.
|
|
112
|
+
|
|
113
|
+
## Join a human-owned session
|
|
114
|
+
|
|
115
|
+
Desktop onboarding and **Settings → Agents · MCP** register a stable stdio launcher at user scope. The saved configuration contains no fixed workspace or design: each agent session supplies its project folder. Browser Settings copies the equivalent version-matched `npx … mcp-connect` configuration, with a setup prompt for each agent. Native Connect uses the host's CLI administration commands without starting a chat or creating a design; saved configuration is distinct from a verified live connection.
|
|
116
|
+
|
|
117
|
+
The adapter discovers or starts the shared local runtime and forwards MCP over native Streamable HTTP; discovery, cancellation and MCP App calls stay on the canonical server. Browser users opt in with `cr8 start designs/first --mcp`; the readiness answer includes `mcpUrl`. Both use the existing project runtime, including atomic edits, selection, skills and `ui://cr8/canvas.html`. Disconnecting a client leaves the human’s session running. The browser’s direct HTTP URL is session-specific; the stable launcher discovers the current port and design across restarts and file switches. Workspace discovery verifies the existing runtime record and project leases; if several live runtimes share a workspace, or the bounded catalog cannot establish a unique one, use `mcp-connect --workspace DIR --project designs/name` to select the session explicitly. With no live runtime, setup starts an empty MCP process and leaves file selection to `canvas_projects`, `canvas_open` and `canvas_init`. Desktop, launcher and connected browser views retain a managed runtime until the last client leaves; a crashed native client is reaped. The stdio plugin route remains the default for a host-managed process; do not launch a second runtime for a project already open in the desktop.
|
|
118
|
+
|
|
90
119
|
## Development host
|
|
91
120
|
|
|
92
121
|
Expose Streamable HTTP on loopback:
|
|
@@ -95,6 +124,11 @@ Expose Streamable HTTP on loopback:
|
|
|
95
124
|
cr8 mcp --transport http --port 3001
|
|
96
125
|
```
|
|
97
126
|
|
|
98
|
-
The MCP endpoint is mounted at `/mcp` on the same loopback listener that serves the canvas, so the official MCP Apps basic host can be pointed at `http://127.0.0.1:3001/mcp` while the human canvas stays on the same origin.
|
|
127
|
+
The MCP endpoint is mounted at `/mcp` on the same loopback listener that serves the canvas, so the official MCP Apps basic host can be pointed at `http://127.0.0.1:3001/mcp` while the human canvas stays on the same origin.
|
|
128
|
+
|
|
129
|
+
After `npm run build:package`, run `npm run test:mcp-app` with Playwright Chromium installed. The acceptance test launches the packaged stdio server, joins its project through the CLI and browser, then loads its actual UI resource in an opaque-origin sandbox through the official MCP Apps host bridge. It verifies initial host capabilities without an extra context-change notification, compact and fullscreen modes, live agent edits on both surfaces, selection sharing, atomic undo, and host-mediated PNG export. This checks CR8's protocol integration; each supported host still needs its own rendering smoke test.
|
|
99
130
|
|
|
100
131
|
Non-loopback HTTP binding is rejected because authentication is outside the local product scope.
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
`workflow_run` with `nodeId` executes exactly one generation node, including an unconnected node with no Output node yet. It reuses available upstream generation results and refuses missing ones before any paid work. Its result carries `nodeId` and an output whose `outputNodeId` is that node ID; unrelated completed node previews remain marked `reused`. Use `nodeId` or the existing downstream `fromNodeId`, never both. The browser's generation-node action uses this same contract; input-node actions run the workflow.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zvada/cr8",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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,52 @@
|
|
|
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:
|
|
72
|
-
"
|
|
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",
|
|
77
|
-
"test:install": "node --import tsx --test tests/install/install.test.mjs",
|
|
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",
|
|
63
|
+
"test:desktop": "node --import tsx --test tests/install/desktop.test.mjs",
|
|
64
|
+
"test:install": "node --import tsx --test tests/install/install.test.mjs tests/install/account.test.mjs",
|
|
78
65
|
"test:eval": "node --import tsx --test scripts/eval/image-layers/evaluator.test.mjs",
|
|
79
66
|
"verify": "npm run lint && npm test && npm run build",
|
|
80
67
|
"bench": "node --import tsx scripts/bench/benchmark-scene.mjs && node --import tsx scripts/bench/benchmark-editor.mjs",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
68
|
+
"verify:catalogue": "node --import tsx scripts/verify-catalogue.mjs",
|
|
69
|
+
"version:set": "node scripts/set-version.mjs",
|
|
70
|
+
"cli": "node --import tsx bin/cr8.mjs",
|
|
71
|
+
"prepare": "npm run build:package",
|
|
72
|
+
"test:workspace": "node --import tsx --test --test-concurrency=1 tests/install/workspace.test.mjs tests/install/workspace-account.test.mjs tests/install/refresh.test.mjs tests/install/hover.test.mjs tests/install/workflow-nodes.test.mjs tests/install/feedback.test.mjs",
|
|
73
|
+
"test:mcp-app": "node --import tsx --test tests/install/mcp-app.test.mjs",
|
|
74
|
+
"test:account-ui": "npm run build --workspace @cr8/account-hub && node --import tsx --test tests/install/account-ui.test.mjs"
|
|
84
75
|
},
|
|
85
76
|
"devDependencies": {
|
|
77
|
+
"@babel/parser": "7.29.7",
|
|
86
78
|
"@cr8/contracts": "file:packages/contracts",
|
|
79
|
+
"@cr8/core": "file:packages/core",
|
|
87
80
|
"@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",
|
|
97
|
-
"@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",
|
|
81
|
+
"@cr8/runtime": "file:packages/runtime",
|
|
101
82
|
"@oxlint/plugins": "1.78.0",
|
|
102
|
-
"@
|
|
83
|
+
"@tanstack/eslint-plugin-query": "^5.102.8",
|
|
103
84
|
"@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
85
|
"ajv": "^8.20.0",
|
|
110
|
-
"electron": "^41.10.7",
|
|
111
|
-
"electron-builder": "^26.15.3",
|
|
112
86
|
"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
87
|
"linkedom": "^0.18.13",
|
|
118
88
|
"oxlint": "1.78.0",
|
|
119
89
|
"playwright-core": "^1.62.1",
|
|
120
90
|
"react": "19.2.0",
|
|
121
91
|
"react-dom": "19.2.0",
|
|
122
|
-
"ts-pattern": "^5.9.0",
|
|
123
92
|
"tsx": "^4.23.12",
|
|
124
93
|
"typescript": "^7.0.2",
|
|
125
94
|
"vite": "6.4.3",
|
|
126
|
-
"
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"zustand": "^5.0.15"
|
|
95
|
+
"zod": "^4.2.0",
|
|
96
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
97
|
+
"@modelcontextprotocol/core": "^2.0.0",
|
|
98
|
+
"@modelcontextprotocol/server": "^2.0.0"
|
|
131
99
|
}
|
|
132
100
|
}
|
|
@@ -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.3.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 (`read_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.
|
|
@@ -13,7 +13,7 @@ Choose by deliverable:
|
|
|
13
13
|
- `web-app`: dense web and desktop product surfaces.
|
|
14
14
|
- `canvas-foundations`: the default structure and verification discipline; enough on its own for a single simple artboard.
|
|
15
15
|
|
|
16
|
-
A playbook that lists no styles is used alone.
|
|
16
|
+
A playbook that lists no styles is used alone.
|
|
17
17
|
|
|
18
18
|
## Projects
|
|
19
19
|
|
|
@@ -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.
|
|
@@ -10,16 +10,32 @@
|
|
|
10
10
|
|
|
11
11
|
A warning makes the design unhealthy (`healthy: false`; the CLI exits 2); a notice is listed and does not. Fix existing nodes directly and run doctor again.
|
|
12
12
|
|
|
13
|
+
Doctor checks measured line boxes when the local renderer is available, including labels in unclipped frames. Read `textMeasurement`: `unavailable` means fixed-text overflow was estimated. Adaptive agent work refuses unavailable measurement. Inspect the pixels even with complete measurements: typography, hierarchy and visual balance still require judgment.
|
|
14
|
+
|
|
13
15
|
## Looking
|
|
14
16
|
|
|
15
17
|
Diagnostics and full-resolution inspection alone are insufficient. Perform the visual acceptance gate the loaded playbook and style name, in the human canvas or on the export: a hero at 50 percent, a link preview at 400 px wide, an app icon at 32 px and in one colour, an App Store strip reduced to its first three frames. When a same-size local reference exists, `cr8 verify --reference <path> --root FRAME_ID` makes the canonical renderer and the diagnostics one reproducible gate.
|
|
16
18
|
|
|
17
19
|
`open_canvas` (CLI `cr8 open`) answers the canvas URL for the person. When `agent-browser` is how you look at it yourself, pass `--session <name>` on every call so a shared browser never shows another agent's page; the canvas opens fitted to everything, so a single artboard reads as a thumbnail there, and the export is the real-scale check.
|
|
18
20
|
|
|
21
|
+
For layout readback, use `canvas_tree` with `rootId` (CLI `tree --root ID`). `resolved.localGeometry` is the actual box relative to its parent, and `resolved.worldGeometry` locates it on the canvas. The top-level geometry fields are stored inputs and may differ under flow layout.
|
|
22
|
+
|
|
23
|
+
## Copy and resize
|
|
24
|
+
|
|
25
|
+
Before calling product UI reusable or responsive, test a duplicate with a longer realistic heading and action label, then at the narrowest intended width. Look for lost words, crowded controls, broken spacing, and a primary action pushed out of view. Fix the original and repeat only the cases affected by that fix. A single good-looking screenshot does not prove the design survives ordinary edits.
|
|
26
|
+
|
|
27
|
+
Use auto-height text and hug containers where the design should adapt. Duplicate the artboard, lengthen a heading and action label, then narrow the outer frame. Text should wrap, the button should retain its label, and the content column should grow without overlap. Inspect the first preview after each acknowledged edit and check its resolved dimensions. Keep deliberately fixed marketing compositions fixed; the resilience check applies where adaptation is intended.
|
|
28
|
+
|
|
19
29
|
## Export
|
|
20
30
|
|
|
21
31
|
`canvas_export` (CLI `cr8 export --root FRAME_ID [--out PATH] [--scale 1|2]`) renders one root artboard with the canonical renderer, the same one the Export button and `verify` use, to a PNG under the project's `exports/` (or the path given), and is how a finished frame is handed over. Export the exact artboard that was accepted, open the file, and look at it at the size it will be seen. `--scale 2` doubles the pixels; a 512 px tile exported at scale 2 is the 1024 px icon.
|
|
22
32
|
|
|
33
|
+
Over MCP, `canvas_export` answers with the picture attached: look at it after every finished section, not once at the end, and fix what you see before composing the next. Over the CLI, read the exported file.
|
|
34
|
+
|
|
23
35
|
## Review boards
|
|
24
36
|
|
|
25
37
|
A flow's `workflow_place_output` with `all: true` (CLI `workflow-place ID --all`) puts every output of the flow's last run on one review artboard, named after the flow, side by side at equal height, to the right of everything; the answer carries the board's geometry and each output's layer and file. Judge there, then place the chosen output on your own artboard. Delete the review board when it has served, or leave it clear of your work.
|
|
38
|
+
|
|
39
|
+
## Verify against a reference
|
|
40
|
+
|
|
41
|
+
`verify --reference IMAGE --root ID` renders the artboard and compares it with the reference at the same size. Read `inkMismatchedFraction` before the whole-viewport number: a screen is mostly background, so one percent of all pixels can be a fifth of the text. `regions` names the cells that differ most; the difference image at `diffPath` shows every differing pixel in red over the dimmed render. Look at it before deciding what to fix, and fix the largest region first.
|