| name | mooboard-create-board |
| description | Creates a new Moobard/Moobird moodboard folder with a `.mooboard.json` in the Hybrid-picks collage style — open interactive image collage, interleaved link cards, curved bound arrows (image→link and callout→image), handwritten labels, and a to-do. Use when the user asks for /mooboard-create-board, a shopping or research moodboard, visual collage board, or a new board like Hybrid Cars. |
| disable-model-invocation | true |
Mooboard Create Board
Create a new moodboard under the Moobard library in the same visual style as
~/Documents/Moobard/Hybrid Cars.
Canonical reference (read before inventing structure):
What “Hybrid picks” style means
A loose decision / shopping collage with breathing room — not a framed cluster
map, not a repo onboarding diagram, and not a tight magazine scrapbook.
| Ingredient | Hybrid-picks pattern |
|---|
| Images | 4–6 at the board root, presentation: "interactive", display height ~300–400; portrait or landscape OK; width = round(height * nw / nh) |
| Composition | Open collage — stagger x/y widely, ≥ 40px gaps between content boxes, no overlaps; avoid a packed center cloud or spreadsheet grid |
| Title | Native text, font: "draw", size: "xl", black — short theme name top-left |
| Callouts | Short judgment labels near items ("Cheapest", "Cool car!", "mpg king"); black ink; size: "m" or "l"; multi-line OK |
| Callout arrows | Native arrow kind: "arc" from callout text → image (this is the signature of the style) |
| Product sources | link cards 260×200, previewMode: "card", real previewImage; interleave near related images, not only a bottom strip |
| Source arrows | Also arc arrows image → link where a review/shop card backs a tile |
| Actions | One todo (~280×200) near the title / top of the collage with 3–5 concrete next steps |
| Envelope | version: 1, background: "default", showGrid: true, camera zoom ~0.4–0.55 framing the whole spread |
| Optional | Filled geo color swatch behind a hero image — fine for fashion boards, skip by default for this style |
| Avoid | note objects, asset subfolders, rigid frames-as-content, plain/button links, unbound arrows, inventing freehand draw stroke paths |
Annotation ink is black. Skip white-on-dark onboarding-map conventions. Accents like light-red / yellow swatches are optional, not required.
Workflow
Copy this checklist and track it:
Moodboard Progress:
- [ ] 1. Confirm theme + board name + destination folder
- [ ] 2. Create board folder under Moobard library
- [ ] 3. Gather/copy images to board root + measure natural sizes + display widths
- [ ] 4. Resolve product/source URLs with preview images
- [ ] 5. Draft non-overlapping layout (AABB + min gap), then write .mooboard.json
- [ ] 6. Run validate_manifest + check_layout; fix until both ok
- [ ] 7. Report board path + how to open it
1. Confirm inputs
Ask only for what is missing:
- Theme / brief (e.g. “hybrid cars I could get”, “wedding guest suits”)
- Board name (folder name)
- Images — user-provided paths, paste assets, or URLs to download
- Links / products (optional) — shop URLs or research sources
- To-do items (optional) — otherwise invent 3 theme-relevant actions
- Destination — default
~/Documents/Moobard/<Board Name> unless the user specifies another library root
Do not overwrite an existing board folder unless the user explicitly asks.
2. Create the folder
mkdir -p "$HOME/Documents/Moobard/<Board Name>"
Put every media file at the board root (filename only in the manifest). Do not create images/, assets/, etc. — Moobard would show those as a single folder card.
Prefer distinctive filenames: toyota-prius.jpg, not img1.jpg.
3. Images
For each image object:
- Copy/download the real file into the board folder first
- Set
type: "image", storage: "owned", path: "<filename>"
- Set
presentation: "interactive"
- Set
naturalWidth / naturalHeight from the file (measure with Pillow/sips/file)
- Size the card to keep aspect ratio; target display height 300–400
- Compute display width before placing:
width = round(height * naturalWidth / naturalHeight)
- Write width/height into a placement table before assigning x/y — do not guess width
Layout rule of thumb (adapt, don’t clone Hybrid Cars coords):
- Spread images across a wide field (~200–1800 x, ~-150–700 y)
- No content overlaps. Treat each image/link/todo/text/geo as an axis-aligned box
- Enforce ≥ 40px clear gap between content boxes (80–200px is better). Arrows may cross freely
- Stagger Y by 100–250px; when a landscape tile is wide (e.g. 500–700px), push neighbors farther on x
- Nest the todo near the title (top center / upper middle) without covering either
- Park link cards beside or under the images they support — only where the box fits with gap
Placement algorithm (required)
Before writing JSON, keep a list of placed boxes (id, x, y, w, h). For each new content object:
- Pick a candidate
(x, y)
- Reject if it intersects any existing box inflated by
min_gap/2 (default min_gap = 40)
- If rejected, nudge (+dx / +dy) or pick a new lane — do not “hope” overlaps look ok
- Only then append the object
Optional geo swatch under an image is the only allowed intentional overlap, and only when the geo zIndex is lower than that image.
4. Links
Every canvas link must be an embed card:
{
"type": "link",
"width": 260,
"height": 200,
"previewMode": "card",
"previewImage": "https://…",
"url": "https://…",
"label": "example.com",
"domain": "example.com",
"title": "",
"color": ""
}
If a URL has no reliable preview image, keep it out of the canvas (mention it in the summary) rather than adding a plain/button link.
Prefer review/editorial pages (or manufacturer pages) that expose a real og:image. Decode & → & in preview URLs.
5. Annotations (native shapes)
Use type: "native" for tldraw shapes. See templates.md.
Minimum set for a Hybrid-picks board:
- Title — native
text, font: "draw", size: "xl", black, theme name
- 2–4 callouts — short native
text (size: "m" / "l") with opinion / fact labels
- Callout → image arrows —
kind: "arc", arrowheadStart: "none", bind start to the text shape and end to the image
- 1–3 image → link arrows — same arc style, bind image → related link card
- Todo — top of collage near title
Do not synthesize freehand draw stroke path blobs; use text + font: "draw" instead.
Color swatch (geo rectangle) is optional — omit unless the theme benefits from a fashion-poster accent.
Arrow bindings (required for presentation connectors):
Image → link (source card):
"bindings": {
"start": {
"objectId": "<image-id>",
"props": {
"isPrecise": false,
"isExact": false,
"normalizedAnchor": { "x": 0.5, "y": 0.9 },
"snap": "center",
"terminal": "start"
}
},
"end": {
"objectId": "<link-id>",
"props": {
"isPrecise": true
Callout text → image (judgment label):
"bindings": {
"start": {
"objectId": "<text-shape-id>",
"props": {
"isPrecise": false,
"isExact": false,
"normalizedAnchor": { "x": 0.5, "y": 0.5 },
"snap": "center",
"terminal": "start"
}
},
"end": {
"objectId": "<image-id>",
"props": {
"isPrecise": true
Use non-zero bend (often |bend| ≈ 40–170) so arcs feel hand-drawn. Prefer arrowheadStart: "none", arrowheadEnd: "arrow".
6. To-do
One card, theme-specific items, ~280×200:
{
"type": "todo",
"title": "To-do",
"color": "",
"dueDate": null,
"width": 280,
"height": 200,
"items": [
{ "id": "<uuid>", "order": 0, "completed": false, "text": "…" }
]
}
Place it near the title at the top of the collage (Hybrid Cars: ~x 840, y -230), not orphaned far off-canvas.
7. Write the manifest
Envelope:
{
"version": 1,
"createdAt": 0,
"lastOpenedAt": 0,
"objects": [],
"camera": { "x": 450, "y": 500, "zoom": 0.45 },
"background": "default",
"showGrid": true
}
- Use epoch-ms timestamps (
Date.now() style)
- Object IDs: UUIDs for moobard objects;
shape:<tldrawId> for native shapes
- Monotonic
zIndex starting at 1
- If a swatch is used, give it a lower zIndex than the image it sits under
- Paths are folder-relative filenames only
Write atomically:
python3 - <<'PY'
import json, os, tempfile, time
board = os.path.expanduser("~/Documents/Moobard/<Board Name>")
doc = { /* full document */ }
doc["createdAt"] = int(time.time() * 1000)
doc["lastOpenedAt"] = doc["createdAt"]
path = os.path.join(board, ".mooboard.json")
fd, tmp = tempfile.mkstemp(dir=board, suffix=".tmp")
with os.fdopen(fd, "w") as f:
json.dump(doc, f, indent=2)
f.write("\n")
os.replace(tmp, path)
print(path)
PY
Run both validators and fix until ok: true. Overlaps are a hard fail — do not ship a board with check_layout errors.
python3 ~/.cursor/skills/moobird-canvas/scripts/validate_manifest.py \
"$HOME/Documents/Moobard/<Board Name>"
python3 ~/.cursor/skills/mooboard-create-board/scripts/check_layout.py \
"$HOME/Documents/Moobard/<Board Name>" --min-gap 40
check_layout.py verifies:
- Every
image display width matches height * naturalWidth / naturalHeight (±2px)
- No AABB overlaps between content boxes (
image, link, todo, native text/geo) with < min-gap clearance
- Arrows/
draw strokes are ignored (connectors may cross)
- Geo under an image with lower
zIndex is warned, not failed
If overlaps are reported, move the later object(s) and re-run until clean.
8. Validate before done
Output to the user
Report:
- Absolute board path
- Counts: images, links, todos, native annotations
- Any URLs skipped for missing previews
- That they should open/refresh the folder in Moobard to see it
Do not claim the app rendered the board unless that was observed.
Anti-patterns
- Overlapping images / links / todos / callouts (fix with
check_layout.py)
- Guessing image display width instead of deriving it from natural aspect ratio
- Cloning Hybrid Cars or Fashion coordinates with different labels (vary the collage)
- Packing everything into a tight center cloud (leave open space)
- Building framed “cluster geography” boards (that’s onboarding/research style)
- White handwritten headers meant for dark diagram boards
- Compact/plain links or links without
previewImage
- Putting media in nested folders
- Unbound arrows that only look attached
- Regenerating opaque
draw.path stroke data by hand
- Requiring a yellow swatch or price-tag callouts on every board
- Shipping after
validate_manifest alone without running check_layout