| name | moobird-canvas |
| description | Creates and edits Moobird file-based visual canvases by reading board folders and safely updating hidden .mooboard.json manifests. Use when the user mentions Moobird, .mooboard.json, arranging files on an infinite canvas, creating a visual map of a folder, adding notes or to-do lists, or connecting canvas objects with arrows. |
Moobird Canvas
Moobird turns real folders into infinite visual canvases. The folder is the source of truth; .mooboard.json stores only canvas layout, manifest-only objects, and board settings.
Moobird currently uses the filename .mooboard.json even though the product is named Moobird. Do not rename this file.
Before editing
- Identify the exact board folder.
- Prefer a path supplied by the user or copied from Moobird.
- The default library is
~/Documents/Moobard, but the user may have selected another folder.
- Do not assume the default path is active.
- Read:
- the board directory contents;
.mooboard.json, when present;
- SCHEMA.md.
- Preserve existing objects and unknown fields unless the user asks to change them.
- Never delete, move, or overwrite a real file unless the user explicitly requests it.
- Treat paths in the manifest as folder-relative POSIX paths. Never write an absolute path.
Core rules
Real files and folders
File-backed canvas objects point at real content inside the board folder.
- Copy or create the real file first.
- Then add or update its manifest object.
- Use a relative
path.
- Avoid filename collisions.
- Do not encode file contents in the manifest.
Flat board root (required): Put downloaded or created assets directly in the board folder. Use root-relative paths such as soft-minimalism-grasses.jpg, never images/soft-minimalism-grasses.jpg.
- Do not create asset subfolders (
images/, assets/, media/, files/, downloads/, etc.) for mood boards or research canvases.
- Moobird reconciles real subfolders as
folder cards. Nesting assets hides the individual files on the canvas and collapses them into one folder object.
- Prefer distinctive filenames at the board root to avoid collisions (
meadow-wildflowers.jpg, not img1.jpg).
- The only intentional subfolders are nested boards (a child folder with its own
.mooboard.json, referenced by a folder object). Do not use nested boards as a dump for media.
Path-backed types:
image
svg
video
frameStill
file
folder
Deleting one of these objects in Moobird can trash the underlying file or folder. Do not remove one from an existing board as a substitute for hiding it unless the user clearly requested deletion.
Manifest-only objects
These exist only in .mooboard.json:
text
shape
link
note
todo
arrow
ink
Removing one deletes only the canvas object.
IDs
- Preserve existing IDs.
- Generate new IDs with a collision-resistant form such as
obj_<uuid>.
- Every object ID must be unique within the board.
Geometry
Every object has:
x, y: top-left canvas coordinates;
width, height: positive dimensions;
rotation: degrees;
zIndex: drawing order;
- optional
locked.
Use integer coordinates unless fine precision is useful.
Recommended defaults:
- Keep at least
32 px between unrelated objects.
- Use
48–80 px between groups.
- Use
240–360 px widths for notes and file cards.
- Place headings above the content they describe.
- Avoid overlapping objects unless the overlap is intentional.
- Increase
zIndex monotonically for newly created objects.
Color palette (required)
Only use colors from Moobird’s annotation palette (renderer/lib/palette.ts). Do not invent pastels, grays, browns, navy, or arbitrary hex values — those break legibility on black and white canvases.
| Key | Label | Stored value |
|---|
default | Default | "" (empty string) or omit color |
red | Red | #e5484d |
orange | Orange | #f76b15 |
yellow | Yellow | #ffb224 |
green | Green | #30a46c |
teal | Teal | #14b8a6 |
blue | Blue | #3b82f6 |
indigo | Indigo | #6366f1 |
purple | Purple | #8e4ec6 |
pink | Pink | #ec4899 |
Rules:
- Prefer the hex strings above for
text, shape, link, note, todo, arrow, and ink colors.
- Use Default (
"" / omit) when the object should follow the theme (adapts to light/dark).
- Board
background should be "default" unless the user asks for a specific installed background token.
- Never use off-palette sticky colors (
#FFF2A8, #DDEBFF, #E8F5DF, #f5f4ef, #454545, etc.).
- Legacy
"accent" may appear in old boards; when editing, migrate arrows/ink to a palette hex or "".
- Theme grouping tip: assign one palette color per theme thread and reuse it for that cluster’s notes, links, and arrows.
Arrows
The current arrow schema is geometric, not semantically attached to objects.
x, y are the arrow origin.
ex, ey are the endpoint relative to the origin.
- Use
arrowStyle: "straight" or "elbow".
- Use
head: "none", "end", or "both".
Position arrows so they visually connect object edges. Moving an object later will not automatically move the arrow endpoint.
Nested boards
A board is a real subfolder containing its own .mooboard.json.
To create a nested board:
- Create the child folder.
- Create a valid child
.mooboard.json.
- Add a
folder object in the parent board pointing to that relative folder path.
- Never create a parent/child path cycle.
Editing workflow
Use this checklist:
- [ ] Confirm the board folder
- [ ] Read the existing manifest and directory contents
- [ ] Plan additions, updates, and any requested removals
- [ ] Copy or create real files at the board root (no asset subfolders)
- [ ] Use root-relative paths in the manifest (filename only, not images/…)
- [ ] Write a complete candidate manifest outside the board
- [ ] Validate the candidate
- [ ] Apply it atomically
- [ ] Re-read and validate the installed manifest
- [ ] Summarize what changed
Validate
Run:
python scripts/validate_manifest.py "/path/to/board"
To validate a candidate file:
python scripts/validate_manifest.py "/path/to/candidate.json" --board "/path/to/board"
Fix every error before applying. Warnings may be accepted only when they are intentional.
Apply safely
Do not overwrite .mooboard.json with an ad hoc shell redirect.
Use:
python scripts/apply_manifest.py \
"/path/to/board" \
"/path/to/candidate.json" \
--dry-run
Then apply:
python scripts/apply_manifest.py \
"/path/to/board" \
"/path/to/candidate.json"
The helper validates the candidate, creates a timestamped backup, and replaces the manifest atomically.
Creating a new board manifest
Use the existing board's version when modifying a board.
For a brand-new board, use this shape unless the installed app provides a newer example:
{
"version": 1,
"createdAt": "2026-07-22T12:00:00.000Z",
"lastOpenedAt": "2026-07-22T12:00:00.000Z",
"objects": [],
"camera": {
"x": 0,
"y": 0,
"zoom": 1
},
"background": "default",
"showGrid": false
}
Use current ISO-8601 timestamps (or match the installed app’s numeric epoch style if the existing board already uses it). Do not alter createdAt on an existing board. Always set background to "default" unless the user specifies otherwise.
Handling reconciliation
Moobird reconciles the manifest with the real folder:
- New real files may be auto-placed.
- Missing real files cause path-backed objects to disappear.
- Renaming a real file externally may look like removal plus addition.
- A real subfolder without a nested-board intent appears as a single
folder card, not as its contents.
Therefore:
- Update the real filesystem and manifest as one planned operation.
- Keep media files at the board root so each file can be an
image / file / video object.
- Avoid renaming existing files unless requested.
- After applying, inspect both the directory and manifest again.
Output to the user
Report:
- board path;
- objects created, updated, or removed;
- real files copied, created, moved, or deleted;
- warnings;
- whether validation passed;
- backup path created by the apply script.
Do not claim that Moobird rendered the board unless the app was opened or refreshed and this was observed.
Additional resources