# NapkinLab `.napkin` authoring essentials

Compact reference for agents hand-authoring a project outside NapkinLab. The
full in-repo format spec is longer; this page covers
what you need to relay a sketch.

## v2 envelope (target format)

Emit a **v2 envelope**, not a bare `Project` or legacy flat JSON:

```json
{
  "appVersion": "2.0.0",
  "metadata": { "format": "napkin-project" },
  "savedAt": "<ISO datetime>",
  "activeDocumentId": "<document-id>",
  "project": { /* Project — see below */ }
}
```

Bare JSON (no zip) is fine for relay upload. Zip bundles (`PK…`) are also
accepted on open.

## Project shape (minimal)

- `project.id`, `name`, `createdAt`, `updatedAt` — strings.
- `project.documents[]` — **at least one** document (empty `frames: []` is OK;
  zero documents is rejected).
- Each document: `id`, `name`, `frames[]`, `framePresets[]`, `comments[]`,
  `flows[]`, `canvasItems[]` (arrays; use `[]` when empty).

Each **frame**: `id`, `name`, `type` (`website` | `app` | `modal` |
`dashboard` | `social` | `custom` — **no `mobile`**; phone frames are `app`
at 390×844), `x`, `y`, `width`, `height`, `autoHeight`, `layout: { behavior:
"freeform" }`, `background: { type: "solid", color: "#111" }`, `elements[]`,
`annotations[]`, `commentIds[]`.

Each **element**: `id`, `frameId` (overwritten on load — put the element in
the correct frame's `elements` array), `type` (see below), `label`, `bounds:
{ x, y, width, height }`, `style` (use `{}`), numeric `z`, `createdAt`.

## Annotations (two shapes — do not mix)

Dispatch is `type === "annotation"` → markup pin; anything else (including a
missing `type`) → canvas pin. One invalid annotation rejects the **whole**
project. Pick one shape and fill every required field.

**Markup** (pinned to an element/frame — preferred for notes on UI):

```json
{
  "id": "ann-1",
  "type": "annotation",
  "target": {
    "frameId": "frame-home",
    "elementId": "el-search",
    "bounds": { "x": 16, "y": 116, "width": 160, "height": 18 }
  },
  "intent": "question",
  "category": "question",
  "note": "…",
  "createdAt": "2026-08-31T18:22:55.000Z"
}
```

`intent` / `category` must be one of: `add`, `remove`, `move`, `rename`,
`question`, `bug`, `flow`, `logic`, `data-model`.

**Canvas pin** (x/y on the frame): `id`, numeric `x`/`y`, `label`, `body`,
`color` (string, e.g. `"#f59e0b"`). Do not set `type: "annotation"` on this
shape or the validator will require markup fields instead.

## UIBlockType (25 values)

Stick to these element `type` values — anything else throws on open:

`header`, `nav`, `hero`, `button`, `card`, `table`, `form`, `input`, `sidebar`,
`modal`, `image`, `text`, `footer`, `searchbar`, `tabs`, `list`, `avatar`,
`stat`, `toggle`, `breadcrumb`, `logo`, `vnav`, `divider`, `box`, `dropdown`

## IdeaFrame vs FlowMap

Route by content **before** authoring:

**UI screens / mockups** → **IdeaFrame** shape (default): one frame per
screen; blocks live in each frame's `elements[]`.

**Flowcharts, state machines, pipelines, architecture diagrams** → **FlowMap**
shape:

- One **small** frame per node (`type: "custom"`, ~600×300, `elements: []` OK).
- Document-level `flows[]` for edges: `from` / `to` are `{ frameId }` or
  `{ itemId }` (canvas item). Agent op form uses `fromItemId` / `toItemId`.
  `interaction: "click"`; `intent` is the on-canvas arrow label.
- Optional `canvasItems[]` (`kind: "container"` or `"text"`) for grouping boxes.
- Leave **≥400px gutters** between node frames — FlowMap renders canvas
  geometry at ~0.28×; connector labels need clearance.
- Open in FlowMap: `relay.mjs --mode flowmap` or append `&mode=flowmap` to the
  open URL. Mode is ephemeral UI state (not stored in the file).

## Authoring rules

1. **Unique ids** for project, document, frame, element, flow, annotation.
2. Every element needs **`bounds`** and numeric **`z`** (no backfill).
3. **Flow/comment references** must point at real ids in the same document.
4. Images: prefer `data:` URIs unless you also build matching zip `assets/`
   entries.

## Relay (file-handoff fallback)

For an already-open sketch, prefer the native WebMCP tools described in
[the live tool guide](https://napkinlab.pages.dev/skill/webmcp.md). Use this
file workflow for initial imports or when the browser/agent lacks native tools.

From any project with Node:

```bash
node scripts/napkinlab/relay.mjs --project /tmp/sketch.napkin.json --agent Codex
```

Defaults to `https://napkinlab.pages.dev`. Prints a URL; user rearranges and
presses **Send to {agent}**. Relay polls and writes `<project>.returned.json`
plus a **CHANGE SUMMARY**. `--no-wait` / `--resume <session-id>` split create
and poll. Sessions expire after ~1h. Public anonymous dropbox — no secrets.

Install the portable skill pack:

```bash
curl -fsSL https://napkinlab.pages.dev/skill/install.sh | bash
```
