diff --git a/AGENTS.md b/AGENTS.md index f6afcc1..202e67f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ npm run typecheck # tsc --noEmit on @opencanva/core ``` - The demo app runs the CLI directly: `opencanva ` (defaults to `dev`). Regenerate the committed skills with **`npm run sync`** (it runs in `apps/demo`); bare `opencanva sync` inside this monorepo is refused (it would write a stray skills tree). -- `npm run sync` copies the skills bundled in `packages/core/skills/` into the demo workspace's **`.agents/skills/`** (vendor-neutral, committed) **and `.claude/skills/`** (Claude-specific, git-ignored): `canva-authoring`, `create-design`, `apply-comments`, `current-design`, `create-theme`. +- `npm run sync` copies the skills bundled in `packages/core/skills/` into the demo workspace's **`.agents/skills/`** (vendor-neutral, committed) **and `.claude/skills/`** (Claude-specific, git-ignored): `canva-authoring`, `create-design`, `apply-comments`, `current-design`, `create-theme`, `svg-illustration`. - `opencanva init [dir]` scaffolds a fresh OpenCanva project (config, starter design, and both skill dirs) so a new project is multi-agent ready out of the box. - After changing `designPresets` in `packages/core/src/design.ts` (every preset needs `vibe` + `tags`), run **`npm run gen:catalog && npm run sync`** — it regenerates the theme catalog table inside the `create-design` skill from the presets; `check:sync` fails CI if the table is stale. - To typecheck the designs specifically: `npx tsc --noEmit -p apps/demo` (this is what CI runs alongside the core typecheck). diff --git a/CLAUDE.md b/CLAUDE.md index f6afcc1..202e67f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ npm run typecheck # tsc --noEmit on @opencanva/core ``` - The demo app runs the CLI directly: `opencanva ` (defaults to `dev`). Regenerate the committed skills with **`npm run sync`** (it runs in `apps/demo`); bare `opencanva sync` inside this monorepo is refused (it would write a stray skills tree). -- `npm run sync` copies the skills bundled in `packages/core/skills/` into the demo workspace's **`.agents/skills/`** (vendor-neutral, committed) **and `.claude/skills/`** (Claude-specific, git-ignored): `canva-authoring`, `create-design`, `apply-comments`, `current-design`, `create-theme`. +- `npm run sync` copies the skills bundled in `packages/core/skills/` into the demo workspace's **`.agents/skills/`** (vendor-neutral, committed) **and `.claude/skills/`** (Claude-specific, git-ignored): `canva-authoring`, `create-design`, `apply-comments`, `current-design`, `create-theme`, `svg-illustration`. - `opencanva init [dir]` scaffolds a fresh OpenCanva project (config, starter design, and both skill dirs) so a new project is multi-agent ready out of the box. - After changing `designPresets` in `packages/core/src/design.ts` (every preset needs `vibe` + `tags`), run **`npm run gen:catalog && npm run sync`** — it regenerates the theme catalog table inside the `create-design` skill from the presets; `check:sync` fails CI if the table is stale. - To typecheck the designs specifically: `npx tsc --noEmit -p apps/demo` (this is what CI runs alongside the core typecheck). diff --git a/README.md b/README.md index 7d12e56..b236575 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ export default [Poster] satisfies Scene[]; ``` - `export default` is `Scene[]` — one component per artboard (most designs have one; several = a carousel / size set). -- Objects: ``, ``, ``, ``, ``, ``, `` — positioned with `x/y/w/h`, rotated with `rotate`, themed with `var(--ox-*)` tokens. +- Objects: ``, ``, ``, ``, ``, ``, ``, `` — positioned with `x/y/w/h`, rotated with `rotate`, themed with `var(--ox-*)` tokens. - Themes: `ember`, `noir`, `sunset`, `mint`, `blueprint`, `bubblegum` (see `/themes`), or roll your own `DesignSystem`. ## Why it's agent-native @@ -65,7 +65,7 @@ export default [Poster] satisfies Scene[]; - **Click-to-source.** Every object you click resolves to the exact ``/`` in your source (via injected `data-ox-loc` read off the React fiber — works across files and components). Drag/resize/recolor rewrite the object's props **in place** through a dev-only write-back API; the source stays byte-stable so HMR is surgical. - **Comments → edits.** Leave a `@canva-comment` on any object; the bundled `apply-comments` skill applies them. - **Current-design pointer.** The dev server writes `.opencanva/current.json` so an agent can resolve "this design / the object I selected". -- **Works with any agent.** A root `AGENTS.md` (the cross-tool standard read by Codex, Cursor, Copilot, …) carries the repo guide, and `CLAUDE.md` is a byte-identical copy of it (kept in sync by CI). The authoring skills — `canva-authoring`, `create-design`, `apply-comments`, `current-design`, `create-theme` — ship as Agent Skills: `npm run sync` copies them into both `.agents/skills/` (vendor-neutral, committed) and `.claude/skills/` (local). `opencanva init` scaffolds a new project with all of it baked in. +- **Works with any agent.** A root `AGENTS.md` (the cross-tool standard read by Codex, Cursor, Copilot, …) carries the repo guide, and `CLAUDE.md` is a byte-identical copy of it (kept in sync by CI). The authoring skills — `canva-authoring`, `create-design`, `apply-comments`, `current-design`, `create-theme`, `svg-illustration` — ship as Agent Skills: `npm run sync` copies them into both `.agents/skills/` (vendor-neutral, committed) and `.claude/skills/` (local). `opencanva init` scaffolds a new project with all of it baked in. ## Layout diff --git a/apps/demo/.agents/skills/apply-comments/SKILL.md b/apps/demo/.agents/skills/apply-comments/SKILL.md index 3997e1c..b25fda8 100644 --- a/apps/demo/.agents/skills/apply-comments/SKILL.md +++ b/apps/demo/.agents/skills/apply-comments/SKILL.md @@ -31,7 +31,7 @@ The comment text is the string after `@canva-comment:` (or the `data-ox-comment` ## Workflow 1. **Collect** every marker with `grep` (above). Note the file, line, and the object each marker is attached to. -2. **Apply edits bottom-up** — process markers in **descending line order within each file** so earlier edits don't shift later line numbers. For each: read the surrounding code, make the change the comment asks for (consult `canva-authoring` for how — adjust props like `x`/`y`/`w`/`h`/`size`/`fill`/`color`, swap a token, restructure objects), following the design's existing palette and style. +2. **Apply edits bottom-up** — process markers in **descending line order within each file** so earlier edits don't shift later line numbers. For each: read the surrounding code, make the change the comment asks for (consult `canva-authoring` for how — adjust props like `x`/`y`/`w`/`h`/`size`/`fill`/`color`/`background`, swap a token, restructure objects), following the design's existing palette and style. 3. **Remove the marker** you just satisfied — delete the `{/* @canva-comment: … */}` child or the `data-ox-comment="…"` attribute. 4. **Verify none remain**: ```bash diff --git a/apps/demo/.agents/skills/canva-authoring/SKILL.md b/apps/demo/.agents/skills/canva-authoring/SKILL.md index 400879b..e37cb05 100644 --- a/apps/demo/.agents/skills/canva-authoring/SKILL.md +++ b/apps/demo/.agents/skills/canva-authoring/SKILL.md @@ -73,7 +73,8 @@ Every object takes `x, y` (position), `w, h` (size), and optional `rotate` (deg, | `` | `w` (length), `thickness`, `color`, `dash`, `rotate` | Dividers, rules, accents. | | `` | `src`, `fit` (`cover`\|`contain`), `radius` | Photos/logos. Put files in `designs//assets/` and use `src="./assets/photo.jpg"`. | | `` | (position only) | A transparent container for a **logical unit** (a card's parts, a labelled stat, a header cluster). Move/resize/rotate it as one object; the editor can select it as a unit, and multi-select → **Group**/**Ungroup** wraps or dissolves one. Children position **relative to the group**. | -| `` | `glyph` (emoji/char), `size`, `color` | Emoji or a single glyph. Pass SVG as children for custom marks. | +| `` | `glyph` (emoji/char), `size`, `color` | Emoji or a single glyph, or a small custom SVG mark as children — legacy for anything larger. | +| `` | `background` (mat color behind the SVG) | A full-bleed host for a hand-authored inline SVG illustration. Read the **`svg-illustration`** skill before using this. | ## Authoring rules (these keep the inspector working) diff --git a/apps/demo/.agents/skills/svg-illustration/SKILL.md b/apps/demo/.agents/skills/svg-illustration/SKILL.md new file mode 100644 index 0000000..cd1760d --- /dev/null +++ b/apps/demo/.agents/skills/svg-illustration/SKILL.md @@ -0,0 +1,149 @@ +--- +name: svg-illustration +description: Use when authoring a hand-drawn SVG illustration, vector art, or a scene/character graphic for an OpenCanva design — "draw an illustration of X", "make a vector graphic", "illustrate this concept", "add a hero illustration". Not for diagrams, flowcharts, or dividers — those use the existing Box/Line/Text primitives per canva-authoring. +--- + +# Draw an SVG illustration + +This skill covers organic/illustrative vector art — a hero graphic, a scene, a character, a decorative +mark bigger than a simple glyph. Diagrams, flowcharts, and dividers stay with `Box`/`Line`/`Text` per +**`canva-authoring`** — that's a different discipline (layout math), not this one (composition/lighting +technique). + +## Hosting + +Use `` (from `@opencanva/core`), not ``: + +```tsx +import { useId } from 'react'; +import { Illustration } from '@opencanva/core'; + +function CoffeeCup() { + const uid = useId().replace(/:/g, ''); + return ( + + + + + + + + + + ); +} + + + + +``` + +- The inner `` always declares `width="100%" height="100%"` and its own `viewBox="0 0 W H"` — an + internal coordinate space independent of the object's outer `w`/`h` (artboard pixels). The board's + stylesheet also sizes `.ox-illustration > svg` to fill the box by default, but set the attributes + anyway — it's the documented contract, not a fallback to lean on. +- Set `name="…"` for a real layers-panel label. +- **If you factor the illustration into a reusable component** (the pattern `canva-authoring` teaches + for repeated shapes), its return value must be a single root `` — not wrapped in a `
`, not + a `Fragment` with siblings. The sizing rule above targets a *direct* SVG child; a wrapper silently + defeats it, with nothing to signal why the illustration renders at the wrong size. +- **The inspector's fill swatch on an `` colors the "Background" mat behind the SVG, not + the artwork itself** — visible only if the SVG has transparent margins. Recolor the illustration by + editing its own `fill`/`stroke`/`stop-color`/`color-mix()` values directly, not through the swatch. + +## This is JSX, not raw SVG + +Multi-word attributes are camelCase — `stopColor`, `strokeLinecap`, `strokeWidth`, `patternUnits`, +`clipPath` — never kebab-case (`stop-color`, `stroke-linecap`), and `class` is `className`. Most SVG +reference material online is written in raw-SVG kebab-case; copying it verbatim fails `tsc`. + +## Every `` id must be unique per rendered instance + +Gradients, masks, `clipPath`s, and filters are referenced by `url(#id)`. A hardcoded id collides the +moment the same illustration renders twice — a reusable component used more than once in one design, +or this app's own live-canvas-plus-thumbnail double render. Call `useId()` once at the top of the +component and interpolate it into every id, stripping the colons `useId()` includes by default (safe +as a plain attribute value, but not worth depending on being safe inside a `url(#…)` reference too): + +```tsx +import { useId } from 'react'; +const uid = useId().replace(/:/g, ''); +// id={`${uid}-sky`} ... fill={`url(#${uid}-sky)`} +``` + +## No external references, scripts, or event handlers; no nested `` + +No ``, no `@import`, no `