diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..4109164
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,266 @@
+# Contributing to Open Design
+
+Thanks for thinking about contributing. OD is small on purpose — most of the value lives in **files** (skills, design systems, prompt fragments) rather than framework code. That means the highest-leverage contributions are usually one folder, one Markdown file, or one PR-sized adapter.
+
+This guide tells you exactly where to look for each type of contribution and what bar a PR has to clear before we merge it.
+
+
+
+---
+
+## Three things you can ship in one afternoon
+
+| If you want to… | You're really adding | Where it lives | Ship size |
+|---|---|---|---|
+| Make OD render a new kind of artifact (an invoice, an iOS Settings screen, a one-pager…) | a **Skill** | [`skills//`](skills/) | one folder, ~2 files |
+| Make OD speak a new brand's visual language | a **Design System** | [`design-systems//DESIGN.md`](design-systems/) | one Markdown file |
+| Hook up a new coding-agent CLI | an **Agent adapter** | [`daemon/agents.js`](daemon/agents.js) | ~10 lines in one array |
+| Add a feature, fix a bug, lift a UX pattern from [`open-codesign`][ocod] | code | `src/`, `daemon/` | normal PR |
+| Improve docs, port a section to 中文, fix typos | docs | `README.md`, `README.zh-CN.md`, `docs/`, `QUICKSTART.md` | one PR |
+
+If you're not sure which bucket your idea is in, [open a discussion / issue first](https://github.com/nexu-io/open-design/issues/new) and we'll point you at the right surface.
+
+---
+
+## Local setup
+
+The full one-page setup lives in [`QUICKSTART.md`](QUICKSTART.md). The TL;DR for contributors:
+
+```bash
+git clone https://github.com/nexu-io/open-design.git
+cd open-design
+pnpm install # or npm install
+pnpm dev:all # daemon (:7456) + Vite (:5173)
+pnpm typecheck # tsc -b --noEmit
+pnpm build # production build
+```
+
+Node 18+ is required. macOS, Linux, and WSL2 are tested daily. Windows native should work but isn't a primary target — file an issue if it doesn't.
+
+You don't need any agent CLI on your `PATH` to develop OD itself — the daemon will tell you "no agents found" and fall back to the **Anthropic API · BYOK** path, which is the fastest dev loop anyway.
+
+---
+
+## Adding a new Skill
+
+A skill is a folder under [`skills/`](skills/) with a `SKILL.md` at the root, following Claude Code's [`SKILL.md` convention][skill] plus our optional `od:` extension. **No registration step.** Drop the folder in, restart the daemon, the picker shows it.
+
+### Skill folder layout
+
+```text
+skills/your-skill/
+├── SKILL.md # required
+├── assets/template.html # optional but recommended — the seed file
+├── references/ # optional — knowledge files the agent reads
+│ ├── layouts.md
+│ ├── components.md
+│ └── checklist.md
+└── example.html # strongly recommended — a real, hand-built sample
+```
+
+### `SKILL.md` frontmatter
+
+The first three keys are the Claude Code base spec — `name`, `description`, `triggers`. Everything under `od:` is OD-specific and optional, but **`od.mode`** decides which group the skill shows up in (Prototype / Deck / Template / Design system).
+
+```yaml
+---
+name: your-skill
+description: |
+ One-paragraph elevator pitch. The agent reads this verbatim to decide
+ if the user's brief matches. Be concrete: surface, audience, what's in
+ the artifact, what's not.
+triggers:
+ - "your trigger phrase"
+ - "another phrase"
+ - "中文触发词"
+od:
+ mode: prototype # prototype | deck | template | design-system
+ platform: desktop # desktop | mobile
+ scenario: marketing # free-form tag for grouping
+ featured: 1 # any positive integer surfaces it under "Showcase examples"
+ preview:
+ type: html # html | jsx | pptx | markdown
+ entry: index.html
+ design_system:
+ requires: true # does the skill read the active DESIGN.md?
+ sections: [color, typography, layout, components]
+ example_prompt: "A copy-pastable prompt that nicely shows what this skill does."
+---
+
+# Your Skill
+
+Body is free-form Markdown describing the workflow the agent should follow…
+```
+
+The full grammar — typed inputs, slider parameters, capability gating — lives in [`docs/skills-protocol.md`](docs/skills-protocol.md).
+
+### Bar for merging a new skill
+
+We're picky about skills because they're the user-facing surface. A new skill must:
+
+1. **Ship a real `example.html`.** Hand-built, opens straight from disk, looks like something a designer would actually deliver. No lorem ipsum, no `` placeholder hero. If you can't build the example yourself, the skill probably isn't ready.
+2. **Pass the anti-AI-slop checklist** in the body. No purple gradients, no generic emoji icons, no rounded card with a left-border accent, no Inter as a *display* face, no invented stats. Read the **Anti-AI-slop machinery** section of the README for the full list.
+3. **Honest placeholders.** When the agent doesn't have a real number, write `—` or a labelled grey block, not "10× faster".
+4. **Have a `references/checklist.md`** with at least P0 gates (the stuff the agent has to pass before emitting ``). Lift the format from [`skills/guizang-ppt/references/checklist.md`](skills/guizang-ppt/) or [`skills/dating-web/references/checklist.md`](skills/dating-web/).
+5. **Add a screenshot** at `docs/screenshots/skills/.png` if the skill is featured. PNG, ~1024×640 retina, captured from the real `example.html` at zoomed-out browser scale.
+6. **Be a single self-contained folder.** No CDN imports beyond what other skills already use; no fonts you didn't license; no images larger than ~250 KB.
+
+If you fork an existing skill (e.g. start from `dating-web` and remix into a `recruiting-web`), keep the original LICENSE and authorship in `references/` and call it out in your PR description.
+
+### Skills that already ship — pick one to imitate
+
+- Visual showcase, single-screen prototype: [`skills/dating-web/`](skills/dating-web/), [`skills/digital-eguide/`](skills/digital-eguide/)
+- Multi-frame mobile flow: [`skills/mobile-onboarding/`](skills/mobile-onboarding/), [`skills/gamified-app/`](skills/gamified-app/)
+- Document / template (no design system required): [`skills/pm-spec/`](skills/pm-spec/), [`skills/weekly-update/`](skills/weekly-update/)
+- Deck mode: [`skills/guizang-ppt/`](skills/guizang-ppt/) (bundled verbatim from [op7418/guizang-ppt-skill][guizang]) and [`skills/simple-deck/`](skills/simple-deck/)
+
+---
+
+## Adding a new Design System
+
+A design system is a single [`DESIGN.md`](design-systems/README.md) file under `design-systems//`. **One file, no code.** Drop it in, restart the daemon, the picker shows it grouped by category.
+
+### Design system folder layout
+
+```text
+design-systems/your-brand/
+└── DESIGN.md
+```
+
+### `DESIGN.md` shape
+
+```markdown
+# Design System Inspired by YourBrand
+
+> Category: Developer Tools
+> One-line summary that shows in the picker preview.
+
+## 1. Visual Theme & Atmosphere
+…
+
+## 2. Color
+- Primary: `#hex` / `oklch(...)`
+- …
+
+## 3. Typography
+…
+
+## 4. Spacing & Grid
+## 5. Layout & Composition
+## 6. Components
+## 7. Motion & Interaction
+## 8. Voice & Brand
+## 9. Anti-patterns
+```
+
+The 9-section schema is fixed — that's what skill bodies grep for. The first H1 becomes the picker label (the `Design System Inspired by` prefix is stripped automatically), and the `> Category: …` line decides which group it lands in. Existing categories are listed in [`design-systems/README.md`](design-systems/README.md); if your brand truly doesn't fit, you can introduce a new one, but **try existing categories first**.
+
+### Bar for merging a new design system
+
+1. **All 9 sections present.** Empty section bodies are fine for hard-to-find data (e.g. motion tokens), but the headings have to be there or the prompt grep breaks.
+2. **Hex codes are real.** Sample directly from the brand's site or product, not from memory or AI guesses. The README's "brand-spec extraction" 5-step protocol applies to maintainers too.
+3. **OKLch values for accent colors** are nice-to-have. They make palettes lerp predictably across light/dark.
+4. **No marketing fluff.** The brand's tagline is not a design token. Cut it.
+5. **Slug uses ASCII** — `linear.app` becomes `linear-app`, `x.ai` becomes `x-ai`. The 69 imported systems already follow this convention; mirror it.
+
+The 69 product systems we ship are imported from [`VoltAgent/awesome-design-md`][acd2] via [`scripts/sync-design-systems.mjs`](scripts/sync-design-systems.mjs). If your brand belongs upstream, **send the PR there first** — we'll pick it up automatically on the next sync. The `design-systems/` folder is for systems that don't fit upstream, plus our two hand-authored starters.
+
+---
+
+## Adding a new coding-agent CLI
+
+Hooking up a new agent (e.g. some new shop's `foo-coder` CLI) is one entry in [`daemon/agents.js`](daemon/agents.js):
+
+```javascript
+{
+ id: 'foo',
+ name: 'Foo Coder',
+ bin: 'foo',
+ versionArgs: ['--version'],
+ buildArgs: (prompt) => ['exec', '-p', prompt],
+ streamFormat: 'plain', // or 'claude-stream-json' if it speaks that
+}
+```
+
+That's it — daemon will detect it on `PATH`, the picker shows it, the chat path works. If the CLI emits **typed events** (like Claude Code's `--output-format stream-json`), wire a parser in [`daemon/claude-stream.js`](daemon/claude-stream.js) and set `streamFormat: 'claude-stream-json'`.
+
+Bar for merging:
+
+1. **A real session works end-to-end** with the new agent — paste the daemon log into the PR description showing it streamed an artifact through.
+2. **`docs/agent-adapters.md`** is updated with the CLI's quirks (does it require a key file? does it support image input? what's its non-interactive flag?).
+3. **The README's "Supported coding agents" table** gets one row.
+
+---
+
+## Code style
+
+We're not pedantic about formatting (Prettier on save is fine), but two rules are non-negotiable because they show up in the prompt stack and the user-facing API:
+
+1. **Single quotes in JS/TS.** Strings are single-quoted unless escaping makes them ugly. The codebase is already consistent — please match.
+2. **Comments in English.** Even if the PR is translating something into 中文, code comments stay in English so we can keep one set of greppable references.
+
+Beyond that:
+
+- **Don't narrate.** No `// import the module`, no `// loop through items`. If the code reads obviously, the comment is noise. Save comments for non-obvious intent or constraints the code can't express.
+- **TypeScript** for `src/`. The daemon (`daemon/`) is plain ESM JavaScript with JSDoc when types matter — keep it that way.
+- **No new top-level dependencies** without a paragraph in the PR description on what we get vs. what bytes we ship. The dep list in [`package.json`](package.json) is small on purpose.
+- **Run `pnpm typecheck`** before pushing. CI runs it; failing it earns a "please fix" comment.
+
+---
+
+## Commits & pull requests
+
+- **One concern per PR.** Adding a skill + refactoring the parser + bumping a dep is three PRs.
+- **Title is imperative + scope.** `add dating-web skill`, `fix daemon SSE backpressure when CLI hangs`, `docs: clarify .od layout`.
+- **Body explains the why.** "What does this do" is usually obvious from the diff; "why does this need to exist" rarely is.
+- **Reference an issue** if there is one. If there isn't and the PR is non-trivial, open one first so we can agree the change is wanted before you spend the time.
+- **No squash-during-review.** Push fixups; we'll squash on merge.
+- **No force-push to a shared branch** unless the reviewer asked.
+
+We don't enforce a CLA. Apache-2.0 covers us; your contribution is licensed under the same.
+
+---
+
+## Reporting bugs
+
+Open an issue with:
+
+- What you ran (the exact `pnpm dev:all` / `npm start` invocation).
+- Which agent CLI was selected (or whether you were on the BYOK path).
+- The skill + design system pair that triggered it.
+- The relevant **daemon stderr tail** — most "the artifact never rendered" reports get diagnosed in 30 seconds when we can see `spawn ENOENT` or the CLI's actual error.
+- A screenshot if it's UI.
+
+For prompt-stack bugs ("the agent emitted a purple gradient hero, the slop blacklist was supposed to forbid that"), include the **full assistant message** so we can see whether the violation was the model or the prompt.
+
+---
+
+## Asking questions
+
+- Architecture question, design question, "is this a bug or a misuse" → [GitHub Discussions](https://github.com/nexu-io/open-design/discussions) (preferred — searchable for the next person).
+- "How do I write a skill that does X" → Open a discussion. We'll answer it and turn the answer into [`docs/skills-protocol.md`](docs/skills-protocol.md) if it's a missing pattern.
+
+---
+
+## What we don't accept
+
+To keep the project focused, please don't open PRs that:
+
+- **Vendor a model runtime.** OD's whole bet is "your existing CLI is enough". We don't ship `pi-ai`, OpenAI keys, or model loaders.
+- **Add a new framework to the frontend.** Vite + React 18 + TS is the line. No Next.js, Astro, Solid, Svelte rewrites.
+- **Replace the daemon with a serverless function.** The daemon's whole point is owning a real `cwd` and spawning a real CLI. Vercel deployment of the SPA is fine; the daemon stays a daemon.
+- **Add telemetry / analytics / phone-home.** OD is local-first. The only outbound calls are to providers the user explicitly configured.
+- **Bundle a binary** without a license file and authorship attribution next to it.
+
+If you're not sure whether your idea fits, open a discussion before writing the code.
+
+---
+
+## License
+
+By contributing, you agree your contribution is licensed under the [Apache-2.0 License](LICENSE) of this repository, with the exception of files inside [`skills/guizang-ppt/`](skills/guizang-ppt/), which retain their original MIT license and authorship attribution to [op7418](https://github.com/op7418).
+
+[skill]: https://docs.anthropic.com/en/docs/claude-code/skills
+[guizang]: https://github.com/op7418/guizang-ppt-skill
+[acd2]: https://github.com/VoltAgent/awesome-design-md
+[ocod]: https://github.com/OpenCoworkAI/open-codesign
diff --git a/CONTRIBUTING.zh-CN.md b/CONTRIBUTING.zh-CN.md
new file mode 100644
index 0000000..5e44c39
--- /dev/null
+++ b/CONTRIBUTING.zh-CN.md
@@ -0,0 +1,265 @@
+# 贡献指南 · Contributing to Open Design
+
+谢谢你愿意参与。OD 是有意做小的 —— 大部分价值在 **文件** 里(skill、design system、提示词片段),而不是框架代码。这意味着收益最高的贡献往往就是一个文件夹、一份 Markdown,或者一个 PR 大小的 adapter。
+
+这份指南会告诉你:每种贡献该往哪里看、合并之前 PR 需要过哪些线。
+
+
@@ -20,13 +20,13 @@
## Why this exists
-Anthropic's [Claude Design][cd] (released 2026-04-17, Opus 4.7) showed what happens when an LLM stops writing prose and starts shipping design artifacts. It went viral — and stayed closed, paid-only, cloud-only, locked to Anthropic's model and Anthropic's skills.
+Anthropic's [Claude Design][cd] (released 2026-04-17, Opus 4.7) showed what happens when an LLM stops writing prose and starts shipping design artifacts. It went viral — and stayed closed-source, paid-only, cloud-only, locked to Anthropic's model and Anthropic's skills. There is no checkout, no self-host, no Vercel deploy, no swap-in-your-own-agent.
-**Open Design (OD) is the open substrate.** We don't build an agent — the strongest coding agents already live on your laptop. We wire them into a skill-driven design workflow that runs on `pnpm dev`, deploys to Vercel, and stays BYOK at every layer.
+**Open Design (OD) is the open-source alternative.** Same loop, same artifact-first mental model, none of the lock-in. We don't ship an agent — the strongest coding agents already live on your laptop. We wire them into a skill-driven design workflow that runs on `pnpm dev`, deploys to Vercel, and stays BYOK at every layer.
Type `make me a magazine-style pitch deck for our seed round`. The interactive question form pops up before the model improvises a single pixel. The agent picks one of five curated visual directions. A live `TodoWrite` plan streams into the UI. The daemon builds a real on-disk project folder with a seed template, layout library, and self-check checklist. The agent reads them — pre-flight enforced — runs a five-dimensional critique against its own output, and emits a single `` that renders in a sandboxed iframe seconds later.
-That's not "AI tries to design something". That's an AI that has been trained, by the prompt stack, to behave like a senior designer with a working filesystem, a deterministic palette library, and a checklist culture.
+That's not "AI tries to design something". That's an AI that has been trained, by the prompt stack, to behave like a senior designer with a working filesystem, a deterministic palette library, and a checklist culture — exactly the bar Claude Design set, but open and yours.
OD stands on four open-source shoulders:
@@ -52,51 +52,130 @@ OD stands on four open-source shoulders:
## Demo
-> **Screenshots pending.** Every card below is a placeholder SVG — replace `docs/screenshots/*.svg` with real `.png`/`.jpg` captures and bump the `` extension when you do.
-
-
+ Entry view — pick a skill, pick a design system, type the brief. The same surface for prototypes, decks, mobile apps, dashboards, and editorial pages.
-
+ Turn-1 discovery form — before the model writes a pixel, OD locks the brief: surface, audience, tone, brand context, scale. 30 seconds of radios beats 30 minutes of redirects.
-
+ Direction picker — when the user has no brand, the agent emits a second form with 5 curated directions (Monocle / Modern Minimal / Tech Utility / Brutalist / Soft Warm). One radio click → a deterministic palette + font stack, no model freestyle.
-
+ Live todo progress — the agent's plan streams as a live card. in_progress → completed updates land in real time. The user can redirect cheaply, mid-flight.
-
+ Sandboxed preview — every <artifact> renders in a clean srcdoc iframe. Editable in place via the file workspace; downloadable as HTML, PDF, ZIP.
-
+ 71-system library — every product system shows its 4-color signature. Click for the full DESIGN.md, swatch grid, and live showcase.
-
+ Deck mode (guizang-ppt) — the bundled guizang-ppt-skill drops in unchanged. Magazine layouts, WebGL hero backgrounds, single-file HTML output, PDF export.
-
+ Mobile prototype — pixel-accurate iPhone 15 Pro chrome (Dynamic Island, status bar SVGs, home indicator). Multi-screen prototypes use the shared /frames/ assets so the agent never re-draws a phone.
+## Skills
+
+19 skills ship in the box. Each is a folder under [`skills/`](skills/) following the Claude Code [`SKILL.md`][skill] convention with an extended `od:` frontmatter (`mode`, `platform`, `scenario`, `preview`, `design_system`).
+
+### Showcase examples
+
+The visually distinctive skills you'll most likely run first. Each ships a real `example.html` you can open straight from the repo to see exactly what the agent will produce — no auth, no setup.
+
+
+
+
+
+dating-web · prototype Consumer dating / matchmaking dashboard — left rail nav, ticker bar, KPIs, 30-day mutual-matches chart, editorial typography.
+
+
+
+digital-eguide · template Two-spread digital e-guide — cover (title, author, TOC teaser) + lesson spread with pull-quote and step list. Creator / lifestyle tone.
+
+
+gamified-app · prototype Three-frame gamified mobile-app prototype on a dark showcase stage — cover, today's quests with XP ribbons + level bar, quest detail.
+
+
+
+
+
+mobile-onboarding · prototype Three-frame mobile onboarding flow — splash, value-prop, sign-in. Status bar, swipe dots, primary CTA.
+
+
+
+motion-frames · prototype Single-frame motion-design hero with looping CSS animations — rotating type ring, animated globe, ticking timer. Hand-off ready for HyperFrames.
+
+
+
+
+
+social-carousel · prototype Three-card 1080×1080 social-media carousel — cinematic panels with display headlines that connect across the series, brand mark, loop affordance.
+
+
+### Design surfaces
+
+| Skill | Mode | Default for | What it produces |
+|---|---|---|---|
+| [`web-prototype`](skills/web-prototype/) | prototype | desktop | Single-page HTML — landings, marketing, hero pages |
+| [`saas-landing`](skills/saas-landing/) | prototype | desktop | Hero / features / pricing / CTA marketing layout |
+| [`dashboard`](skills/dashboard/) | prototype | desktop | Admin / analytics with sidebar + data dense layout |
+| [`pricing-page`](skills/pricing-page/) | prototype | desktop | Standalone pricing + comparison tables |
+| [`docs-page`](skills/docs-page/) | prototype | desktop | 3-column documentation layout |
+| [`blog-post`](skills/blog-post/) | prototype | desktop | Editorial long-form |
+| [`mobile-app`](skills/mobile-app/) | prototype | mobile | iPhone 15 Pro / Pixel framed app screen(s) |
+| [`simple-deck`](skills/simple-deck/) | deck | desktop | Minimal horizontal-swipe deck |
+| [`guizang-ppt`](skills/guizang-ppt/) | deck | **default** for deck | Magazine-style web PPT — bundled from [op7418/guizang-ppt-skill][guizang] |
+
+### Document / work-product surfaces
+
+| Skill | Mode | What it produces |
+|---|---|---|
+| [`pm-spec`](skills/pm-spec/) | template | PM specification doc with TOC + decision log |
+| [`weekly-update`](skills/weekly-update/) | template | Team weekly with progress / blockers / next |
+| [`meeting-notes`](skills/meeting-notes/) | template | Meeting decision log |
+| [`eng-runbook`](skills/eng-runbook/) | template | Incident runbook |
+| [`finance-report`](skills/finance-report/) | template | Exec finance summary |
+| [`hr-onboarding`](skills/hr-onboarding/) | template | Role onboarding plan |
+| [`invoice`](skills/invoice/) | template | Single-page invoice |
+| [`kanban-board`](skills/kanban-board/) | template | Board snapshot |
+| [`team-okrs`](skills/team-okrs/) | template | OKR scoresheet |
+
+Adding a skill takes one folder. Read [`docs/skills-protocol.md`](docs/skills-protocol.md) for the extended frontmatter, fork an existing skill, restart the daemon, it appears in the picker.
+
## Six load-bearing ideas
### 1 · We don't ship an agent. Yours is good enough.
@@ -320,59 +399,10 @@ open-design/
└── artifacts/ ← saved one-off renders
```
-## Skills
-
-19 skills ship in the box. Each is a folder under [`skills/`](skills/) following the Claude Code [`SKILL.md`][skill] convention with an extended `od:` frontmatter (`mode`, `platform`, `scenario`, `preview`, `design_system`).
-
-### Showcase examples
-
-The visually distinctive skills you'll most likely run first. Each ships a real `example.html` you can open straight from the repo to see what the agent will produce.
-
-| Skill | Mode | What it produces |
-|---|---|---|
-| [`dating-web`](skills/dating-web/) | prototype | Consumer dating / matchmaking dashboard — left rail nav, ticker bar, KPIs, 30-day mutual-matches chart, editorial typography |
-| [`digital-eguide`](skills/digital-eguide/) | template | Two-spread digital e-guide — cover (title, author, TOC teaser) + lesson spread with pull-quote and step list. Creator / lifestyle tone |
-| [`email-marketing`](skills/email-marketing/) | prototype | Brand product-launch HTML email — masthead, hero image, headline lockup, CTA, specs grid. Centered single-column, table-fallback safe |
-| [`gamified-app`](skills/gamified-app/) | prototype | Three-frame gamified mobile-app prototype on a dark showcase stage — cover, today's quests with XP ribbons + level bar, quest detail |
-| [`mobile-onboarding`](skills/mobile-onboarding/) | prototype | Three-frame mobile onboarding flow — splash, value-prop, sign-in. Status bar, swipe dots, primary CTA |
-| [`motion-frames`](skills/motion-frames/) | prototype | Single-frame motion-design hero with looping CSS animations — rotating type ring, animated globe, ticking timer. Hand-off ready for HyperFrames |
-| [`social-carousel`](skills/social-carousel/) | prototype | Three-card 1080×1080 social-media carousel — cinematic panels with display headlines that connect across the series, brand mark, loop affordance |
-| [`sprite-animation`](skills/sprite-animation/) | prototype | Pixel / 8-bit animated explainer slide — full-bleed cream stage, animated pixel mascot, kinetic Japanese display type, looping CSS keyframes |
-
-### Design surfaces
-
-| Skill | Mode | Default for | What it produces |
-|---|---|---|---|
-| [`web-prototype`](skills/web-prototype/) | prototype | desktop | Single-page HTML — landings, marketing, hero pages |
-| [`saas-landing`](skills/saas-landing/) | prototype | desktop | Hero / features / pricing / CTA marketing layout |
-| [`dashboard`](skills/dashboard/) | prototype | desktop | Admin / analytics with sidebar + data dense layout |
-| [`pricing-page`](skills/pricing-page/) | prototype | desktop | Standalone pricing + comparison tables |
-| [`docs-page`](skills/docs-page/) | prototype | desktop | 3-column documentation layout |
-| [`blog-post`](skills/blog-post/) | prototype | desktop | Editorial long-form |
-| [`mobile-app`](skills/mobile-app/) | prototype | mobile | iPhone 15 Pro / Pixel framed app screen(s) |
-| [`simple-deck`](skills/simple-deck/) | deck | desktop | Minimal horizontal-swipe deck |
-| [`guizang-ppt`](skills/guizang-ppt/) | deck | **default** for deck | Magazine-style web PPT — bundled from [op7418/guizang-ppt-skill][guizang] |
-
-### Document / work-product surfaces
-
-| Skill | Mode | What it produces |
-|---|---|---|
-| [`pm-spec`](skills/pm-spec/) | template | PM specification doc with TOC + decision log |
-| [`weekly-update`](skills/weekly-update/) | template | Team weekly with progress / blockers / next |
-| [`meeting-notes`](skills/meeting-notes/) | template | Meeting decision log |
-| [`eng-runbook`](skills/eng-runbook/) | template | Incident runbook |
-| [`finance-report`](skills/finance-report/) | template | Exec finance summary |
-| [`hr-onboarding`](skills/hr-onboarding/) | template | Role onboarding plan |
-| [`invoice`](skills/invoice/) | template | Single-page invoice |
-| [`kanban-board`](skills/kanban-board/) | template | Board snapshot |
-| [`team-okrs`](skills/team-okrs/) | template | OKR scoresheet |
-
-Adding a skill takes one folder. Read [`docs/skills-protocol.md`](docs/skills-protocol.md) for the extended frontmatter, fork an existing skill, restart the daemon, it appears in the picker.
-
## Design Systems
-
+
71 systems out of the box, each as a single [`DESIGN.md`](design-systems/README.md):
@@ -450,7 +480,7 @@ The whole machinery below is the [`huashu-design`](https://github.com/alchaincyf
| PPT skill reuse | N/A | Built-in | **[`guizang-ppt-skill`][guizang] drops in** |
| Minimum billing | Pro / Max / Team | BYOK | **BYOK** |
-[cd]: https://www.anthropic.com/news/claude-design
+[cd]: https://x.com/claudeai/status/2045156267690213649
[ocod]: https://github.com/OpenCoworkAI/open-codesign
[piai]: https://github.com/mariozechner/pi-ai
[acd]: https://github.com/VoltAgent/awesome-claude-design
@@ -479,7 +509,7 @@ Every external project this repo borrows from. Each link goes to the source so y
| Project | Role here |
|---|---|
-| [`Claude Design`][cd] | The closed product this repo provides an open substrate for. |
+| [`Claude Design`][cd] | The closed-source product this repo is the open-source alternative to. |
| [**`alchaincyf/huashu-design`**](https://github.com/alchaincyf/huashu-design) | The design-philosophy core. Junior-Designer workflow, the 5-step brand-asset protocol, anti-AI-slop checklist, 5-dimensional self-critique, and the "5 schools × 20 design philosophies" library behind our direction picker — all distilled into [`src/prompts/discovery.ts`](src/prompts/discovery.ts) and [`src/prompts/directions.ts`](src/prompts/directions.ts). |
| [**`op7418/guizang-ppt-skill`**][guizang] | Magazine-web-PPT skill bundled verbatim under [`skills/guizang-ppt/`](skills/guizang-ppt/) with original LICENSE preserved. Default for deck mode. P0/P1/P2 checklist culture borrowed for every other skill. |
| [**`multica-ai/multica`**](https://github.com/multica-ai/multica) | The daemon + adapter architecture. PATH-scan agent detection, local daemon as the only privileged process, agent-as-teammate worldview. We adopt the model; we do not vendor the code. |
@@ -508,7 +538,15 @@ Phased delivery → [`docs/roadmap.md`](docs/roadmap.md).
This is an early implementation — the closed loop (detect → pick skill + design system → chat → parse `` → preview → save) runs end-to-end. The prompt stack and skill library are where most of the value lives, and they're stable. The component-level UI is shipping daily.
-Issues, PRs, new skills, and new design systems are all welcome.
+## Contributing
+
+Issues, PRs, new skills, and new design systems are all welcome. The highest-leverage contributions are usually one folder, one Markdown file, or one PR-sized adapter:
+
+- **Add a skill** — drop a folder into [`skills/`](skills/) following the [`SKILL.md`][skill] convention.
+- **Add a design system** — drop a `DESIGN.md` into [`design-systems//`](design-systems/) using the 9-section schema.
+- **Wire up a new coding-agent CLI** — one entry in [`daemon/agents.js`](daemon/agents.js).
+
+Full walkthrough, bar-for-merging, code style, and what we don't accept → [`CONTRIBUTING.md`](CONTRIBUTING.md) ([简体中文](CONTRIBUTING.zh-CN.md)).
## License
diff --git a/README.zh-CN.md b/README.zh-CN.md
index ecfebb9..763c2ca 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,9 +1,9 @@
# Open Design
-> **给设计的 Claude Code。** 一个本地优先、可部署到 Vercel 的开源 [Claude Design][cd] 复刻 —— 你机器上已经装好的 coding agent(Claude Code、Codex、Cursor Agent、Gemini CLI、OpenCode、Qwen)就是设计引擎,由可组合的 **Skills** 和 **71 套品牌级 Design System** 驱动。
+> **[Claude Design][cd] 的开源替代品。** 本地优先、可部署到 Vercel、每一层都 BYOK —— 你机器上已经装好的 coding agent(Claude Code、Codex、Cursor Agent、Gemini CLI、OpenCode、Qwen)就是设计引擎,由 **19 个可组合 Skills** 和 **71 套品牌级 Design System** 驱动。
71 套开箱即用,每套一个 [`DESIGN.md`](design-systems/README.md):
@@ -450,7 +480,7 @@ open-design/
| PPT skill 复用 | N/A | 内置 | **[`guizang-ppt-skill`][guizang] 直接接入** |
| 计费门槛 | Pro / Max / Team | BYOK | **BYOK** |
-[cd]: https://www.anthropic.com/news/claude-design
+[cd]: https://x.com/claudeai/status/2045156267690213649
[ocod]: https://github.com/OpenCoworkAI/open-codesign
[piai]: https://github.com/mariozechner/pi-ai
[acd]: https://github.com/VoltAgent/awesome-claude-design
@@ -479,7 +509,7 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。
| 项目 | 在这里的角色 |
|---|---|
-| [`Claude Design`][cd] | 本仓库为它提供开源底座的闭源产品。 |
+| [`Claude Design`][cd] | 本仓库为之提供开源替代的闭源产品。 |
| [**`alchaincyf/huashu-design`**(花叔的画术)](https://github.com/alchaincyf/huashu-design) | 设计哲学的核心。Junior-Designer 工作流、5 步品牌资产协议、anti-AI-slop checklist、五维自评审、以及方向选择器背后的「5 流派 × 20 种设计哲学」库 —— 全部蒸馏进 [`src/prompts/discovery.ts`](src/prompts/discovery.ts) 与 [`src/prompts/directions.ts`](src/prompts/directions.ts)。 |
| [**`op7418/guizang-ppt-skill`**(歸藏)][guizang] | Magazine-web-PPT skill 原样捆绑在 [`skills/guizang-ppt/`](skills/guizang-ppt/) 下,原 LICENSE 保留。Deck 模式默认。P0/P1/P2 checklist 文化也被借给了所有其他 skill。 |
| [**`multica-ai/multica`**](https://github.com/multica-ai/multica) | Daemon + adapter 架构。PATH 扫描式 agent 检测、本地 daemon 作为唯一特权进程、agent-as-teammate 世界观。我们采纳模型,不 vendor 代码。 |
@@ -508,7 +538,15 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。
这是一个早期实现 —— 闭环(检测 → 选 skill + design system → 对话 → 解析 `` → 预览 → 保存)已经端到端跑通。提示词栈和 skill 库是价值最重的部分,目前已稳定。组件级 UI 仍在每天迭代。
-欢迎 issue、PR、新 skill、新 design system。
+## 贡献
+
+欢迎 issue、PR、新 skill、新 design system。收益最高的贡献往往就是一个文件夹、一份 Markdown,或者一个 PR 大小的 adapter:
+
+- **加一个 skill** —— 往 [`skills/`](skills/) 丢一个文件夹,遵循 [`SKILL.md`][skill] 规范。
+- **加一套 design system** —— 往 [`design-systems//`](design-systems/) 丢一份 `DESIGN.md`,用 9 段式 schema。
+- **接入一个新的 coding-agent CLI** —— 在 [`daemon/agents.js`](daemon/agents.js) 里加一项。
+
+完整流程、合并硬线、代码风格、我们不接收的 PR 类型 → [`CONTRIBUTING.zh-CN.md`](CONTRIBUTING.zh-CN.md)([English](CONTRIBUTING.md))。
## License
diff --git a/daemon/skills.js b/daemon/skills.js
index c20c592..ff0fa85 100644
--- a/daemon/skills.js
+++ b/daemon/skills.js
@@ -2,9 +2,9 @@
// front-matter, returns listing. No watching in this MVP — re-scans on every
// GET /api/skills, which is fine for dozens of skills.
-import { readdir, readFile, stat } from 'node:fs/promises';
-import path from 'node:path';
-import { parseFrontmatter } from './frontmatter.js';
+import { readdir, readFile, stat } from "node:fs/promises";
+import path from "node:path";
+import { parseFrontmatter } from "./frontmatter.js";
export async function listSkills(skillsRoot) {
const out = [];
@@ -17,27 +17,40 @@ export async function listSkills(skillsRoot) {
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const dir = path.join(skillsRoot, entry.name);
- const skillPath = path.join(dir, 'SKILL.md');
+ const skillPath = path.join(dir, "SKILL.md");
try {
const stats = await stat(skillPath);
if (!stats.isFile()) continue;
- const raw = await readFile(skillPath, 'utf8');
+ const raw = await readFile(skillPath, "utf8");
const { data, body } = parseFrontmatter(raw);
const hasAttachments = await dirHasAttachments(dir);
const mode = data.od?.mode || inferMode(body, data.description);
out.push({
id: data.name || entry.name,
name: data.name || entry.name,
- description: data.description || '',
+ description: data.description || "",
triggers: Array.isArray(data.triggers) ? data.triggers : [],
mode,
- platform: normalizePlatform(data.od?.platform, mode, body, data.description),
+ platform: normalizePlatform(
+ data.od?.platform,
+ mode,
+ body,
+ data.description
+ ),
scenario: normalizeScenario(data.od?.scenario, body, data.description),
- previewType: data.od?.preview?.type || 'html',
+ previewType: data.od?.preview?.type || "html",
designSystemRequired: data.od?.design_system?.requires ?? true,
defaultFor: normalizeDefaultFor(data.od?.default_for),
- upstream: typeof data.od?.upstream === 'string' ? data.od.upstream : null,
+ upstream:
+ typeof data.od?.upstream === "string" ? data.od.upstream : null,
featured: normalizeFeatured(data.od?.featured),
+ // Optional metadata hints used by 'Use this prompt' fast-create so
+ // the resulting project mirrors the shipped example.html. Each hint
+ // is only consumed when its kind matches the skill mode; missing
+ // hints fall back to the same defaults the new-project form uses.
+ fidelity: normalizeFidelity(data.od?.fidelity),
+ speakerNotes: normalizeBoolHint(data.od?.speaker_notes),
+ animations: normalizeBoolHint(data.od?.animations),
examplePrompt: derivePrompt(data),
body: hasAttachments ? withSkillRootPreamble(body, dir) : body,
dir,
@@ -56,15 +69,15 @@ export async function listSkills(skillsRoot) {
// open those files via absolute paths.
function withSkillRootPreamble(body, dir) {
const preamble = [
- '> **Skill root (absolute):** `' + dir + '`',
- '>',
- '> This skill ships side files alongside `SKILL.md`. When the workflow',
- '> below references relative paths such as `assets/template.html` or',
- '> `references/layouts.md`, resolve them against the skill root above and',
- '> open them via their full absolute path.',
- '',
- '',
- ].join('\n');
+ "> **Skill root (absolute):** `" + dir + "`",
+ ">",
+ "> This skill ships side files alongside `SKILL.md`. When the workflow",
+ "> below references relative paths such as `assets/template.html` or",
+ "> `references/layouts.md`, resolve them against the skill root above and",
+ "> open them via their full absolute path.",
+ "",
+ "",
+ ].join("\n");
return preamble + body;
}
@@ -72,7 +85,9 @@ async function dirHasAttachments(dir) {
try {
const entries = await readdir(dir, { withFileTypes: true });
return entries.some(
- (e) => e.name !== 'SKILL.md' && (e.isDirectory() || /\.(md|html|css|js|json|txt)$/i.test(e.name)),
+ (e) =>
+ e.name !== "SKILL.md" &&
+ (e.isDirectory() || /\.(md|html|css|js|json|txt)$/i.test(e.name))
);
} catch {
return false;
@@ -85,14 +100,35 @@ function normalizeDefaultFor(value) {
return [String(value)];
}
+// Optional `od.fidelity` hint for prototype skills. Only 'wireframe' and
+// 'high-fidelity' are meaningful — anything else collapses to null so the
+// caller falls back to the form default ('high-fidelity').
+function normalizeFidelity(value) {
+ if (value === "wireframe" || value === "high-fidelity") return value;
+ return null;
+}
+
+// Coerce truthy / falsy strings ("true", "yes", "false", "no") and booleans
+// to a real boolean. Returns null for anything we can't interpret so the
+// caller knows to fall back to the form default.
+function normalizeBoolHint(value) {
+ if (typeof value === "boolean") return value;
+ if (typeof value === "string") {
+ const v = value.trim().toLowerCase();
+ if (v === "true" || v === "yes" || v === "1") return true;
+ if (v === "false" || v === "no" || v === "0") return false;
+ }
+ return null;
+}
+
// Coerce `od.featured` into a numeric priority. Lower numbers float to the
// top of the Examples gallery; `true` is treated as priority 1; anything
// missing/unrecognised becomes null so non-featured skills keep their
// natural alphabetical order.
function normalizeFeatured(value) {
if (value === true) return 1;
- if (typeof value === 'number' && Number.isFinite(value)) return value;
- if (typeof value === 'string' && value.trim()) {
+ if (typeof value === "number" && Number.isFinite(value)) return value;
+ if (typeof value === "string" && value.trim()) {
const n = Number(value);
if (Number.isFinite(n)) return n;
}
@@ -105,66 +141,70 @@ function normalizeFeatured(value) {
// serves as a passable starter prompt.
function derivePrompt(data) {
const explicit = data.od?.example_prompt;
- if (typeof explicit === 'string' && explicit.trim()) return explicit.trim();
- const desc = typeof data.description === 'string' ? data.description.trim() : '';
- if (!desc) return '';
- const collapsed = desc.replace(/\s+/g, ' ').trim();
+ if (typeof explicit === "string" && explicit.trim()) return explicit.trim();
+ const desc =
+ typeof data.description === "string" ? data.description.trim() : "";
+ if (!desc) return "";
+ const collapsed = desc.replace(/\s+/g, " ").trim();
const firstSentence = collapsed.match(/^.+?[.!?。!?](?:\s|$)/)?.[0]?.trim();
return (firstSentence || collapsed).slice(0, 320);
}
function inferMode(body, description) {
- const hay = `${description ?? ''}\n${body ?? ''}`.toLowerCase();
- if (/\bppt|deck|slide|presentation|幻灯|投影/.test(hay)) return 'deck';
- if (/\bdesign[- ]system|\bdesign\.md|\bdesign tokens/.test(hay)) return 'design-system';
- if (/\btemplate\b/.test(hay)) return 'template';
- return 'prototype';
+ const hay = `${description ?? ""}\n${body ?? ""}`.toLowerCase();
+ if (/\bppt|deck|slide|presentation|幻灯|投影/.test(hay)) return "deck";
+ if (/\bdesign[- ]system|\bdesign\.md|\bdesign tokens/.test(hay))
+ return "design-system";
+ if (/\btemplate\b/.test(hay)) return "template";
+ return "prototype";
}
// Validate platform tag — only desktop / mobile are meaningful for the
// Examples gallery. Falls back to autodetecting "mobile" from descriptions
// so legacy skills sort under the right pill without authoring changes.
function normalizePlatform(value, mode, body, description) {
- if (value === 'desktop' || value === 'mobile') return value;
- if (mode !== 'prototype') return null;
- const hay = `${description ?? ''}\n${body ?? ''}`.toLowerCase();
- if (/mobile|phone|ios|android|手机|移动端/.test(hay)) return 'mobile';
- return 'desktop';
+ if (value === "desktop" || value === "mobile") return value;
+ if (mode !== "prototype") return null;
+ const hay = `${description ?? ""}\n${body ?? ""}`.toLowerCase();
+ if (/mobile|phone|ios|android|手机|移动端/.test(hay)) return "mobile";
+ return "desktop";
}
// Normalise a scenario tag to a small fixed vocabulary so the filter pills
// stay tidy. Unknown values pass through verbatim so authors can experiment;
// missing values default to "general".
const KNOWN_SCENARIOS = new Set([
- 'general',
- 'engineering',
- 'product',
- 'design',
- 'marketing',
- 'sales',
- 'finance',
- 'hr',
- 'operations',
- 'support',
- 'legal',
- 'education',
- 'personal',
+ "general",
+ "engineering",
+ "product",
+ "design",
+ "marketing",
+ "sales",
+ "finance",
+ "hr",
+ "operations",
+ "support",
+ "legal",
+ "education",
+ "personal",
]);
function normalizeScenario(value, body, description) {
- if (typeof value === 'string') {
+ if (typeof value === "string") {
const v = value.trim().toLowerCase();
if (v) return v;
}
- const hay = `${description ?? ''}\n${body ?? ''}`.toLowerCase();
- if (/finance|invoice|expense|budget|p&l|revenue/.test(hay)) return 'finance';
- if (/\bhr\b|onboarding|payroll|employee|人事/.test(hay)) return 'hr';
- if (/marketing|campaign|brand|landing/.test(hay)) return 'marketing';
- if (/runbook|incident|deploy|engineering|sre|api/.test(hay)) return 'engineering';
- if (/spec|prd|roadmap|product manager|product team/.test(hay)) return 'product';
- if (/design system|moodboard|mockup|ui kit/.test(hay)) return 'design';
- if (/sales|quote|proposal|lead/.test(hay)) return 'sales';
- if (/operations|ops|logistics|inventory/.test(hay)) return 'operations';
- return 'general';
+ const hay = `${description ?? ""}\n${body ?? ""}`.toLowerCase();
+ if (/finance|invoice|expense|budget|p&l|revenue/.test(hay)) return "finance";
+ if (/\bhr\b|onboarding|payroll|employee|人事/.test(hay)) return "hr";
+ if (/marketing|campaign|brand|landing/.test(hay)) return "marketing";
+ if (/runbook|incident|deploy|engineering|sre|api/.test(hay))
+ return "engineering";
+ if (/spec|prd|roadmap|product manager|product team/.test(hay))
+ return "product";
+ if (/design system|moodboard|mockup|ui kit/.test(hay)) return "design";
+ if (/sales|quote|proposal|lead/.test(hay)) return "sales";
+ if (/operations|ops|logistics|inventory/.test(hay)) return "operations";
+ return "general";
}
// Surface the vocabulary so callers (frontend filter UI) could mirror it
// later if they want to. Not exported today, kept here for documentation.
diff --git a/docs/assets/_cover/banner.html b/docs/assets/_cover/banner.html
new file mode 100644
index 0000000..a3f57b0
--- /dev/null
+++ b/docs/assets/_cover/banner.html
@@ -0,0 +1,381 @@
+
+
+
+
+Open Design — cover
+
+
+
+
+
+
+
+
Open Design · Manifesto · 2026 Edition
+
open.design
+
Cover · 01 / 08 · OSS Alternative
+
+
+
+
+
+
+ · APACHE 2.0
+ Local-first · BYOK
+
+
+ Design with the
+ agent already
+ on your laptop.
+
+
+ Open Design is the open-source alternative to Claude Design.
+ Your existing coding agent — Claude Code · Codex · Cursor · Gemini · OpenCode · Qwen —
+ becomes the design engine, driven by 19 composable Skills and 71 brand-grade Design Systems.
+
+ Linear, Stripe, Vercel, Airbnb, Tesla, Notion, Anthropic, Apple, Cursor, Supabase, Figma —
+ seventy-one brand-grade systems, one open library, zero lock-in.
+
+
By Open Design · Maintained on GitHub · 19 / 04 / 2026
+
+
+
+
71
+
Design Systems
+
+
+
19
+
Composable Skills
+
+
+
1
+
Library, zero vendor cloud
+
+
+
+
What's inside.
+
+
Tokens, palettes, motion04
+
Pick a direction12
+
Tone & typography18
+
71 systems index24
+
Bring-your-own-key40
+
The anti-AI-slop checklist52
+
+
+
+
+
+
+
+
+
Chapter 02 · Index
+
71 entries · A → Z
+
+
+
All systems — one library.
+
+ Every system ships a deterministic OKLch palette, a font stack, and a tone profile.
+ Pick one tile and the agent inherits the whole brand.
+
+
+
+ S
+ eventy-one product systems, two hand-authored starters, five visual directions. Imported and curated
+ from awesome-design-md, hand-tuned for Open Design's discovery loop. Drop one in,
+ every artifact downstream changes accordingly — no model freestyle.
+
+
+
+
+ 03
+ Linear
+ · graphite · violet
+
+
+ 07
+ Stripe
+ · payments · indigo
+
+
+ 09
+ Vercel
+ · void · grayscale
+
+
+ 14
+ Airbnb
+ · rausch · rounded
+
+
+ 18
+ Tesla
+ · red · industrial
+
+
+ 22
+ Notion
+ · paper · serif
+
+
+ 27
+ Anthropic
+ · clay · serif
+
+
+ 31
+ Apple
+ · chrome · sf pro
+
+
+ 34
+ Cursor
+ · terminal · mono
+
+
+ 41
+ Supabase
+ · emerald · rounded
+
+
+ 48
+ Figma
+ · spectrum · canvas
+
+
+ 57
+ OpenAI
+ · void · sober
+
+
+
+
+
+
+
+
+
+
+
+
Spotlight · Linear · 03 / 71
+
Graphite + electric violet.
+
IBM Plex Sans · Inter Display · 4-step OKLch palette · 16/24 grid · square radius. The agent inherits the full token tree the moment you tap the tile.
+
+
+
+
+
+
71 SYSTEMS
+
+
+
+
diff --git a/docs/assets/banner.png b/docs/assets/banner.png
new file mode 100644
index 0000000..5218331
Binary files /dev/null and b/docs/assets/banner.png differ
diff --git a/docs/assets/design-systems-library.png b/docs/assets/design-systems-library.png
new file mode 100644
index 0000000..01fb739
Binary files /dev/null and b/docs/assets/design-systems-library.png differ
diff --git a/docs/references.md b/docs/references.md
index 9daddc4..b2c544f 100644
--- a/docs/references.md
+++ b/docs/references.md
@@ -11,7 +11,7 @@ Every external project this spec leans on. Three questions per entry: what is it
### [Anthropic Claude Design][cd]
- **URL:** [claude.ai/design][cd] · [release announcement](https://www.infoq.cn/article/TH0QVHpvVGZ7VP3hAEmm) · [ifanr review](https://www.ifanr.com/1662860)
-[cd]: https://www.anthropic.com/news/claude-design
+[cd]: https://x.com/claudeai/status/2045156267690213649
- **What it is:** Anthropic's closed-source AI design product. Released 2026-04-17. Powered by Opus 4.7. Web-only (claude.ai). Generates prototypes, wireframes, decks, marketing pages, complex prototypes with voice/video/3D/shaders.
- **Why it matters to us:** Defines the category. Its viral moment (~60M X impressions week 1) proves the market.
- **What we borrow:** The high-level value prop — "natural language → editable visual design." Feature inspiration for modes (prototype, deck, marketing). UI ideas around inline editing and custom sliders.
diff --git a/docs/screenshots/01-entry-view.png b/docs/screenshots/01-entry-view.png
new file mode 100644
index 0000000..60b5f90
Binary files /dev/null and b/docs/screenshots/01-entry-view.png differ
diff --git a/docs/screenshots/02-question-form.png b/docs/screenshots/02-question-form.png
new file mode 100644
index 0000000..6f73342
Binary files /dev/null and b/docs/screenshots/02-question-form.png differ
diff --git a/docs/screenshots/03-direction-picker.png b/docs/screenshots/03-direction-picker.png
new file mode 100644
index 0000000..b014041
Binary files /dev/null and b/docs/screenshots/03-direction-picker.png differ
diff --git a/docs/screenshots/04-todo-progress.png b/docs/screenshots/04-todo-progress.png
new file mode 100644
index 0000000..9e98a67
Binary files /dev/null and b/docs/screenshots/04-todo-progress.png differ
diff --git a/docs/screenshots/05-preview-iframe.png b/docs/screenshots/05-preview-iframe.png
new file mode 100644
index 0000000..0d509c6
Binary files /dev/null and b/docs/screenshots/05-preview-iframe.png differ
diff --git a/docs/screenshots/06-design-systems-library.png b/docs/screenshots/06-design-systems-library.png
new file mode 100644
index 0000000..e4de137
Binary files /dev/null and b/docs/screenshots/06-design-systems-library.png differ
diff --git a/docs/screenshots/07-magazine-deck.png b/docs/screenshots/07-magazine-deck.png
new file mode 100644
index 0000000..2bfcbc4
Binary files /dev/null and b/docs/screenshots/07-magazine-deck.png differ
diff --git a/docs/screenshots/08-mobile-app.png b/docs/screenshots/08-mobile-app.png
new file mode 100644
index 0000000..48f8c9d
Binary files /dev/null and b/docs/screenshots/08-mobile-app.png differ
diff --git a/docs/screenshots/skills/dating-web.png b/docs/screenshots/skills/dating-web.png
new file mode 100644
index 0000000..87bbcf6
Binary files /dev/null and b/docs/screenshots/skills/dating-web.png differ
diff --git a/docs/screenshots/skills/digital-eguide.png b/docs/screenshots/skills/digital-eguide.png
new file mode 100644
index 0000000..c5efe2b
Binary files /dev/null and b/docs/screenshots/skills/digital-eguide.png differ
diff --git a/docs/screenshots/skills/email-marketing.png b/docs/screenshots/skills/email-marketing.png
new file mode 100644
index 0000000..8f435b2
Binary files /dev/null and b/docs/screenshots/skills/email-marketing.png differ
diff --git a/docs/screenshots/skills/gamified-app.png b/docs/screenshots/skills/gamified-app.png
new file mode 100644
index 0000000..dddf081
Binary files /dev/null and b/docs/screenshots/skills/gamified-app.png differ
diff --git a/docs/screenshots/skills/mobile-onboarding.png b/docs/screenshots/skills/mobile-onboarding.png
new file mode 100644
index 0000000..0d49e9f
Binary files /dev/null and b/docs/screenshots/skills/mobile-onboarding.png differ
diff --git a/docs/screenshots/skills/motion-frames.png b/docs/screenshots/skills/motion-frames.png
new file mode 100644
index 0000000..e83de16
Binary files /dev/null and b/docs/screenshots/skills/motion-frames.png differ
diff --git a/docs/screenshots/skills/social-carousel.png b/docs/screenshots/skills/social-carousel.png
new file mode 100644
index 0000000..219c4dd
Binary files /dev/null and b/docs/screenshots/skills/social-carousel.png differ
diff --git a/docs/screenshots/skills/sprite-animation.png b/docs/screenshots/skills/sprite-animation.png
new file mode 100644
index 0000000..1c158d2
Binary files /dev/null and b/docs/screenshots/skills/sprite-animation.png differ
diff --git a/docs/spec.md b/docs/spec.md
index 06b354a..806a0ef 100644
--- a/docs/spec.md
+++ b/docs/spec.md
@@ -3,7 +3,7 @@
**Status:** Draft v0.1 · 2026-04-24
**Scope:** Product definition, scenarios, non-goals, high-level modules, and positioning against both [Anthropic's Claude Design][cd] and the existing open-source alternative ([Open CoDesign][ocod]).
-[cd]: https://www.anthropic.com/news/claude-design
+[cd]: https://x.com/claudeai/status/2045156267690213649
[ocod]: https://github.com/OpenCoworkAI/open-codesign
[guizang]: https://github.com/op7418/guizang-ppt-skill
[multica]: https://github.com/multica-ai/multica
diff --git a/skills/wireframe-sketch/SKILL.md b/skills/wireframe-sketch/SKILL.md
index 3512542..decb05e 100644
--- a/skills/wireframe-sketch/SKILL.md
+++ b/skills/wireframe-sketch/SKILL.md
@@ -21,6 +21,7 @@ od:
mode: prototype
platform: desktop
scenario: design
+ fidelity: wireframe
preview:
type: html
entry: index.html
diff --git a/src/components/ChatComposer.tsx b/src/components/ChatComposer.tsx
index bb6bd2d..d199ab9 100644
--- a/src/components/ChatComposer.tsx
+++ b/src/components/ChatComposer.tsx
@@ -76,8 +76,12 @@ export const ChatComposer = forwardRef(
const importTriggerRef = useRef(null);
// initialDraft is only honored on the first non-empty value the parent
// hands us. After we seed once, the composer is fully under user control
- // — re-renders that pass the same prompt back must not reseed.
- const seededRef = useRef(false);
+ // — re-renders that pass the same prompt back must not reseed. If the
+ // initial useState above already consumed a non-empty initialDraft we
+ // mark it seeded immediately, so an early clear by the user (typing or
+ // backspace before the parent stops passing initialDraft) does not get
+ // overwritten by the effect.
+ const seededRef = useRef(Boolean(initialDraft));
useEffect(() => {
if (seededRef.current) return;
diff --git a/src/components/EntryView.tsx b/src/components/EntryView.tsx
index c5d6c49..29dd1a8 100644
--- a/src/components/EntryView.tsx
+++ b/src/components/EntryView.tsx
@@ -5,6 +5,8 @@ import type {
AppConfig,
DesignSystemSummary,
Project,
+ ProjectKind,
+ ProjectMetadata,
ProjectTemplate,
SkillSummary,
} from '../types';
@@ -15,7 +17,7 @@ import { ExamplesTab } from './ExamplesTab';
import { Icon } from './Icon';
import { LanguageMenu } from './LanguageMenu';
import { CenteredLoader } from './Loading';
-import { NewProjectPanel, type CreateInput, type CreateTab } from './NewProjectPanel';
+import { NewProjectPanel, type CreateInput } from './NewProjectPanel';
type TopTab = 'designs' | 'examples' | 'design-systems';
@@ -70,13 +72,6 @@ export function EntryView({
const t = useT();
const [topTab, setTopTab] = useState('designs');
const [previewSystemId, setPreviewSystemId] = useState(null);
- const [panelPreset, setPanelPreset] = useState<{
- tab: CreateTab;
- skillId: string | null;
- name: string;
- pendingPrompt?: string;
- nonce: number;
- } | null>(null);
const [sidebarWidth, setSidebarWidth] = useState(() => loadSidebarWidth());
const [resizing, setResizing] = useState(false);
@@ -98,13 +93,16 @@ export function EntryView({
: t('settings.noAgentSelected');
}, [config.mode, config.model, config.baseUrl, currentAgent, t]);
+ // 'Use this prompt' on an example card is a fast path — skip the form and
+ // create the project immediately with sane defaults derived from the skill,
+ // seeding the chat composer with the example prompt via pendingPrompt.
function usePromptFromSkill(skill: SkillSummary) {
- setPanelPreset({
- tab: tabForSkill(skill),
- skillId: skill.id,
+ onCreateProject({
name: skill.name,
+ skillId: skill.id,
+ designSystemId: null,
+ metadata: metadataForSkill(skill),
pendingPrompt: skill.examplePrompt || skill.description,
- nonce: Date.now(),
});
}
@@ -118,10 +116,7 @@ export function EntryView({
);
function handleCreate(input: CreateInput) {
- onCreateProject({
- ...input,
- pendingPrompt: panelPreset?.pendingPrompt,
- });
+ onCreateProject(input);
}
const startWidthRef = useRef(0);
@@ -177,15 +172,11 @@ export function EntryView({
@@ -313,8 +304,38 @@ function TopTabButton({
);
}
-function tabForSkill(skill: SkillSummary): CreateTab {
+// Map a skill's declared mode to project metadata. Falls back to the same
+// defaults the new-project form would apply (high-fidelity prototype, no
+// speaker notes on decks, no template animations) so 'Use this prompt'
+// produces a project indistinguishable from one created via the form. Per-
+// skill hints in SKILL.md frontmatter (od.fidelity, od.speaker_notes,
+// od.animations) override the defaults so each example reproduces the
+// shipped example.html — e.g. wireframe-sketch declares fidelity:wireframe.
+function metadataForSkill(skill: SkillSummary): ProjectMetadata {
+ const kind = kindForSkill(skill);
+ if (kind === 'prototype') {
+ return { kind, fidelity: skill.fidelity ?? 'high-fidelity' };
+ }
+ if (kind === 'deck') {
+ return {
+ kind,
+ speakerNotes:
+ typeof skill.speakerNotes === 'boolean' ? skill.speakerNotes : false,
+ };
+ }
+ if (kind === 'template') {
+ return {
+ kind,
+ animations:
+ typeof skill.animations === 'boolean' ? skill.animations : false,
+ };
+ }
+ return { kind: 'other' };
+}
+
+function kindForSkill(skill: SkillSummary): ProjectKind {
if (skill.mode === 'deck') return 'deck';
if (skill.mode === 'prototype') return 'prototype';
- return 'template';
+ if (skill.mode === 'template') return 'template';
+ return 'other';
}
diff --git a/src/components/FileViewer.tsx b/src/components/FileViewer.tsx
index c35a82c..d3b2aae 100644
--- a/src/components/FileViewer.tsx
+++ b/src/components/FileViewer.tsx
@@ -141,13 +141,23 @@ function HtmlViewer({
};
}, [projectId, file.name, file.mtime, liveHtml, reloadKey]);
+ // Detect deck-shaped HTML even when the project's skill didn't declare
+ // `mode: deck`. Freeform projects often produce a deck because the user
+ // asked for one in plain prose; without this, prev/next and Present
+ // never surface and the deck becomes a static, unnavigable preview.
+ const looksLikeDeck = useMemo(() => {
+ if (!source) return false;
+ return /class\s*=\s*['"][^'"]*\bslide\b/i.test(source);
+ }, [source]);
+ const effectiveDeck = isDeck || looksLikeDeck;
+
const srcDoc = useMemo(
- () => (source ? buildSrcdoc(source, { deck: isDeck }) : ''),
- [source, isDeck],
+ () => (source ? buildSrcdoc(source, { deck: effectiveDeck }) : ''),
+ [source, effectiveDeck],
);
useEffect(() => {
- if (!isDeck) {
+ if (!effectiveDeck) {
setSlideState(null);
return;
}
@@ -161,7 +171,7 @@ function HtmlViewer({
}
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
- }, [isDeck]);
+ }, [effectiveDeck]);
function postSlide(action: 'next' | 'prev' | 'first' | 'last') {
const win = iframeRef.current?.contentWindow;
@@ -172,7 +182,7 @@ function HtmlViewer({
// Keyboard nav on the host, so the user can press ←/→ even when focus
// is on the chat composer or any other host control.
useEffect(() => {
- if (!isDeck || mode !== 'preview') return;
+ if (!effectiveDeck || mode !== 'preview') return;
function onKey(e: KeyboardEvent) {
const target = e.target as HTMLElement | null;
if (target) {
@@ -195,7 +205,7 @@ function HtmlViewer({
}
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
- }, [isDeck, mode]);
+ }, [effectiveDeck, mode]);
useEffect(() => {
if (!presentMenuOpen) return;
@@ -309,7 +319,7 @@ function HtmlViewer({
setZoom((z) => Math.max(25, Math.min(200, z + delta)));
}
- const showPresent = isDeck && source !== null;
+ const showPresent = effectiveDeck && source !== null;
const canShare = source !== null;
const exportTitle = file.name.replace(/\.html?$/i, '') || file.name;
const canPptx = canShare && Boolean(onExportAsPptx) && !streaming;
@@ -328,7 +338,7 @@ function HtmlViewer({
>
- {isDeck ? (
+ {effectiveDeck ? (
{
setShareMenuOpen(false);
- exportAsPdf(source ?? '', exportTitle, { deck: isDeck });
+ exportAsPdf(source ?? '', exportTitle, { deck: effectiveDeck });
}}
>
- {isDeck
+ {effectiveDeck
? t('fileViewer.exportPdfAllSlides')
: t('fileViewer.exportPdf')}
diff --git a/src/components/NewProjectPanel.tsx b/src/components/NewProjectPanel.tsx
index d0283f0..cde451a 100644
--- a/src/components/NewProjectPanel.tsx
+++ b/src/components/NewProjectPanel.tsx
@@ -28,9 +28,6 @@ interface Props {
defaultDesignSystemId: string | null;
templates: ProjectTemplate[];
onCreate: (input: CreateInput) => void;
- presetTab?: CreateTab;
- presetSkillId?: string | null;
- presetName?: string;
loading?: boolean;
}
@@ -47,14 +44,11 @@ export function NewProjectPanel({
defaultDesignSystemId,
templates,
onCreate,
- presetTab,
- presetSkillId,
- presetName,
loading = false,
}: Props) {
const t = useT();
- const [tab, setTab] = useState(presetTab ?? 'prototype');
- const [name, setName] = useState(presetName ?? '');
+ const [tab, setTab] = useState('prototype');
+ const [name, setName] = useState('');
// Design-system selection is now an *array* internally so the same
// component can drive both single-select and multi-select modes without
// duplicating state. Single-select coerces to length 0/1.
@@ -89,7 +83,6 @@ export function NewProjectPanel({
// pick a default-rendered skill (so the agent gets the right SKILL.md
// body) without requiring the user to choose one explicitly.
const skillIdForTab = useMemo(() => {
- if (presetSkillId !== undefined) return presetSkillId;
if (tab === 'other') return null;
if (tab === 'prototype') {
const list = skills.filter((s) => s.mode === 'prototype');
@@ -104,7 +97,7 @@ export function NewProjectPanel({
?? null;
}
return null;
- }, [tab, skills, presetSkillId]);
+ }, [tab, skills]);
const canCreate =
!loading && (tab !== 'template' || templateId != null);
diff --git a/src/components/ProjectView.tsx b/src/components/ProjectView.tsx
index a31c29b..634869a 100644
--- a/src/components/ProjectView.tsx
+++ b/src/components/ProjectView.tsx
@@ -666,9 +666,22 @@ export function ProjectView({
[skills, project.skillId],
);
- // Hand the pending prompt to ChatPane exactly once. After the first render
- // we tell App to clear it so re-entering the project later doesn't reseed.
- const initialDraft = project.pendingPrompt;
+ // Hand the pending prompt to ChatPane exactly once. We snapshot the value
+ // into local state on mount so it survives the ChatPane remount triggered
+ // when `activeConversationId` resolves from `null` to a real id (the
+ // `key={activeConversationId}` on ChatPane otherwise wipes the freshly
+ // seeded composer draft). Once the conversation id is in place — meaning
+ // ChatPane has remounted with the seed still available — we clear both
+ // the local snapshot and the persisted pendingPrompt so future
+ // conversation switches don't keep re-seeding the composer.
+ const [initialDraft, setInitialDraft] = useState(
+ project.pendingPrompt,
+ );
+ useEffect(() => {
+ if (initialDraft && activeConversationId) {
+ setInitialDraft(undefined);
+ }
+ }, [initialDraft, activeConversationId]);
useEffect(() => {
if (project.pendingPrompt) onClearPendingPrompt();
}, [project.pendingPrompt, onClearPendingPrompt]);
diff --git a/src/runtime/srcdoc.ts b/src/runtime/srcdoc.ts
index c6ab634..80414dc 100644
--- a/src/runtime/srcdoc.ts
+++ b/src/runtime/srcdoc.ts
@@ -16,10 +16,10 @@
*/
export function buildSrcdoc(
html: string,
- options: { deck?: boolean } = {},
+ options: { deck?: boolean } = {}
): string {
const head = html.trimStart().slice(0, 64).toLowerCase();
- const isFullDoc = head.startsWith('
@@ -30,33 +30,174 @@ export function buildSrcdoc(
${html}