commit a98096a042388b74e422d4b1a750fce6894f9a5d Author: pftom <1043269994@qq.com> Date: Tue Apr 28 12:25:59 2026 +0800 Add initial project structure with essential files - Created .gitignore to exclude build artifacts and dependencies. - Added index.html as the main entry point for the application. - Included LICENSE file with Apache 2.0 terms. - Initialized package.json and package-lock.json for project dependencies. - Added pnpm-lock.yaml for package management. - Created QUICKSTART.md for setup instructions. - Added README.md and README.zh-CN.md for project documentation in English and Chinese. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36998c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules +dist +.DS_Store +*.log +.vite +.ocd +tsconfig.tsbuildinfo + +.claude-sessions/* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4e6e7ae --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for describing the origin of the Work and + reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Support. While redistributing the Work or + Derivative Works thereof, You may choose to offer, and charge a + fee for, acceptance of support, warranty, indemnity, or other + liability obligations and/or rights consistent with this License. + However, in accepting such obligations, You may act only on Your + own behalf and on Your sole responsibility, not on behalf of any + other Contributor, and only if You agree to indemnify, defend, + and hold each Contributor harmless for any liability incurred by, + or claims asserted against, such Contributor by reason of your + accepting any such warranty or support. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Open Claude Design contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..fd10ecc --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,116 @@ +# Quickstart + +Run the full product locally. + +## One-shot (dev mode) + +```bash +npm install +npm run dev:all # starts daemon (:7456) + Vite (:5173) together +open http://localhost:5173 +``` + +On first load, the app detects your installed code-agent CLI (Claude Code / Codex / Gemini / OpenCode / Cursor Agent / Qwen), picks it automatically, and defaults to `web-prototype` skill + `Neutral Modern` design system. Type a prompt and hit **Send**. The agent streams into the left pane; the `` tag is parsed out and the HTML renders live on the right. When it finishes, click **Save to disk** to persist the artifact under `./.ocd/artifacts/-/index.html`. + +The **Design system** dropdown ships with 71 built-in systems — 2 hand-authored starters (Neutral Modern, Warm Editorial) and 69 product systems imported from [`awesome-design-md`](https://github.com/VoltAgent/awesome-design-md), grouped by category (AI & LLM, Developer Tools, Productivity, Backend, Design Tools, Fintech, E-Commerce, Media, Automotive). Pick one to skin every prototype in that brand's aesthetic. + +The **Skill** dropdown groups by mode (Prototype / Deck / Template / Design system) and shows the default skill per mode with a `· default` suffix. Bundled skills: + +- **Prototype** — `web-prototype` (generic), `saas-landing`, `dashboard`, `pricing-page`, `docs-page`, `blog-post`, `mobile-app`. +- **Deck / PPT** — `simple-deck` (single-file horizontal swipe) and `magazine-web-ppt` (the `guizang-ppt` bundle from [`op7418/guizang-ppt-skill`](https://github.com/op7418/guizang-ppt-skill) — default for deck mode, ships its own assets/template + 4 references). Skills with side files get an automatic "Skill root (absolute)" preamble so the agent can resolve `assets/template.html` and `references/*.md` against the real on-disk path instead of its CWD. + +Pair a skill with a design system and a single prompt produces a layout-appropriate prototype or deck in the chosen visual language. + +## Other scripts + +```bash +npm run daemon # just the daemon (no web UI build) +npm run dev # just Vite (fails /api calls unless daemon is up) +npm run build # production build of the frontend → dist/ +npm run start # build + daemon serving dist/ (single-process prod mode) +npm run typecheck # tsc -b --noEmit +``` + +For the daemon-only production mode, the daemon serves the built SPA itself at `http://localhost:7456`, so no proxy involved. + +## Two execution modes + +| Mode | Picker value | How a request flows | +|---|---|---| +| **Local CLI** (default when daemon detects an agent) | "Local CLI" | Frontend → daemon `/api/chat` → `spawn(, ...)` → stdout → SSE → artifact parser → preview | +| **Anthropic API** (fallback / no CLI) | "Anthropic API · BYOK" | Frontend → `@anthropic-ai/sdk` direct (`dangerouslyAllowBrowser`) → artifact parser → preview | + +Both modes feed the **same** `` parser and the **same** sandboxed iframe. The only thing that differs is the transport and the system-prompt delivery (local CLIs have no separate system channel, so the composed prompt is folded into the user message). + +## Prompt composition + +For every send, the app builds a system prompt from three layers and sends it to the provider: + +``` +BASE_SYSTEM_PROMPT (output contract: wrap in , no code fences) + + active design system body (DESIGN.md — palette/type/layout) + + active skill body (SKILL.md — workflow and output rules) +``` + +Swap the skill or the design system in the top bar and the next send uses the new stack. Bodies are cached in-memory per session so this is a single daemon fetch per pick. + +## File map + +``` +open-claude-design/ +├── daemon/ # Node/Express — spawns local agents + serves APIs +│ ├── cli.js # `ocd` bin entry (also used by npm scripts) +│ ├── server.js # /api/agents /api/skills /api/design-systems /api/chat /api/upload /api/artifacts/save +│ ├── agents.js # PATH scanner for claude/codex/gemini/opencode/cursor-agent/qwen +│ ├── skills.js # SKILL.md loader (frontmatter parser) +│ ├── design-systems.js # DESIGN.md loader +│ └── frontmatter.js # tiny YAML-subset parser (no deps) +├── skills/ # SKILL.md — drops in from any Claude Code skill repo +│ ├── web-prototype/ # generic single-screen prototype (default for prototype mode) +│ ├── saas-landing/ # marketing page (hero / features / pricing / CTA) +│ ├── dashboard/ # admin / analytics dashboard +│ ├── pricing-page/ # standalone pricing + comparison +│ ├── docs-page/ # 3-column documentation layout +│ ├── blog-post/ # editorial long-form +│ ├── mobile-app/ # phone-frame single screen +│ ├── simple-deck/ # minimal horizontal-swipe deck +│ └── guizang-ppt/ # magazine-web-ppt — bundled deck/PPT default +│ ├── SKILL.md +│ ├── assets/template.html +│ └── references/{themes,layouts,components,checklist}.md +├── design-systems/ # DESIGN.md — 9-section schema (awesome-claude-design) +│ ├── default/ # Neutral Modern (starter) +│ ├── warm-editorial/ # Warm Editorial (starter) +│ ├── README.md # catalog overview +│ └── …69 product systems # claude · cohere · linear-app · vercel · stripe · airbnb … +├── scripts/sync-design-systems.mjs # re-import from upstream getdesign tarball +├── src/ # Vite + React SPA +│ ├── App.tsx # orchestrates mode / skill / DS pickers + send +│ ├── providers/ +│ │ ├── anthropic.ts # SDK stream (BYOK path) +│ │ ├── daemon.ts # fetch-SSE against /api/chat (local-CLI path) +│ │ └── registry.ts # /api/agents /api/skills /api/design-systems fetchers +│ ├── prompts/system.ts # composeSystemPrompt(base, skill, DS) +│ ├── artifacts/parser.ts # streaming parser +│ ├── runtime/srcdoc.ts # sandbox wrapper for iframe srcDoc +│ ├── components/ # ChatPane, PreviewPane, AgentPicker, SkillPicker, DesignSystemPicker, SettingsDialog +│ └── state/config.ts # localStorage persistence +├── docs/ # product vision + spec +├── .ocd/artifacts/ # saved HTML outputs (gitignored) +└── vite.config.ts # /api proxy to :7456 +``` + +## Troubleshooting + +- **"no agents found on PATH"** — install one of: `claude`, `codex`, `gemini`, `opencode`, `cursor-agent`, `qwen`. Or switch to "Anthropic API · BYOK" in the top bar and paste a key in **Settings**. +- **daemon 500 on /api/chat** — check the daemon terminal for the stderr tail; usually the CLI rejected its args. Different CLIs take different argv shapes; see `daemon/agents.js` `buildArgs` if you need to tweak. +- **artifact never renders** — the model produced text without wrapping in ``. Confirm the system prompt is going through (check daemon log) and consider switching to a more capable model or a stricter skill. + +## Mapping back to the vision + +This Quickstart is the runnable seed of the spec in [`docs/`](docs/). The spec describes where this grows (see [`docs/roadmap.md`](docs/roadmap.md)). Highlights: + +- `docs/architecture.md` proposes Next.js; we picked Vite for a simpler dev loop. The daemon contract is identical, so migrating is a port, not a rewrite. +- `docs/skills-protocol.md` describes the full `ocd:` frontmatter (typed inputs, sliders, capability gating). This MVP reads `name` / `description` / `triggers` / `ocd.mode` / `ocd.design_system.requires` only — extend `daemon/skills.js` to add the rest. +- `docs/agent-adapters.md` foresees richer dispatch (capability detection, streaming tool-calls). Our `daemon/agents.js` is a minimal dispatcher — enough to prove the wiring. +- `docs/modes.md` lists four modes: prototype / deck / template / design-system. We ship skills for the first two; the picker already filters by `mode`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c65be08 --- /dev/null +++ b/README.md @@ -0,0 +1,497 @@ +# Open Claude Design + +> **Claude Code, but for design.** A local-first, web-deployable open replica of Anthropic's [Claude Design][cd] — your existing coding agent (Claude Code, Codex, Cursor Agent, Gemini CLI, OpenCode, Qwen) becomes the design engine, driven by composable **Skills** and **71 brand-grade Design Systems**. + +

+ Open Claude Design banner +

+ +

+ License + Agents + Design systems + Skills + Quickstart +

+ +

English · 简体中文

+ +--- + +## 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. + +**Open Claude Design (OCD) 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. + +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. + +OCD stands on four open-source shoulders: + +- [**`alchaincyf/huashu-design`**](https://github.com/alchaincyf/huashu-design) — the design-philosophy compass. Junior-Designer workflow, the 5-step brand-asset protocol, the anti-AI-slop checklist, the 5-dimensional self-critique, and the "5 schools × 20 design philosophies" idea behind our direction picker — all distilled into [`src/prompts/discovery.ts`](src/prompts/discovery.ts). +- [**`op7418/guizang-ppt-skill`**](https://github.com/op7418/guizang-ppt-skill) — the deck mode. Bundled verbatim under [`skills/guizang-ppt/`](skills/guizang-ppt/) with original LICENSE preserved; magazine-style layouts, WebGL hero, P0/P1/P2 checklists. +- [**`OpenCoworkAI/open-codesign`**](https://github.com/OpenCoworkAI/open-codesign) — the UX north star and our closest peer. The first open-source Claude-Design alternative. We borrow its streaming-artifact loop, its sandboxed-iframe preview pattern (vendored React 18 + Babel), its live agent panel (todos + tool calls + interruptible generation), and its five-format export list (HTML / PDF / PPTX / ZIP / Markdown). We deliberately diverge on form factor — they are a desktop Electron app bundling [`pi-ai`][piai]; we are a web app + local daemon that delegates to your existing CLI. +- [**`multica-ai/multica`**](https://github.com/multica-ai/multica) — the daemon-and-runtime architecture. PATH-scan agent detection, the local daemon as the only privileged process, the agent-as-teammate worldview. + +## At a glance + +| | What you get | +|---|---| +| **Coding agents supported** | Claude Code · Codex CLI · Cursor Agent · Gemini CLI · OpenCode · Qwen Code · Anthropic API (BYOK fallback) | +| **Design systems built-in** | **71** — 2 hand-authored starters + 69 product systems (Linear, Stripe, Vercel, Airbnb, Tesla, Notion, Anthropic, Apple, Cursor, Supabase, Figma, …) imported from [`awesome-design-md`][acd2] | +| **Skills built-in** | **19** — prototype, deck, mobile, dashboard, pricing, docs, blog, SaaS landing, plus 10 document/work-product templates (PM spec, weekly update, OKRs, runbook, kanban, …) | +| **Visual directions** | 5 curated schools (Editorial Monocle · Modern Minimal · Tech Utility · Brutalist · Soft Warm) — each ships a deterministic OKLch palette + font stack | +| **Device frames** | iPhone 15 Pro · Pixel · iPad Pro · MacBook · Browser Chrome — pixel-accurate, shared across screens | +| **Agent runtime** | Local daemon spawns the CLI in your project folder — agent gets real `Read`, `Write`, `Bash`, `WebFetch` against a real on-disk environment | +| **Deployable to** | Local (`pnpm dev`) · Vercel · Single-process prod (`npm start`) | +| **License** | Apache-2.0 | + +[acd2]: https://github.com/VoltAgent/awesome-design-md + +## Demo + +> Screenshots are placeholders — the live UI is what the screenshots will be of. Replace the `docs/screenshots/*.png` files in PR. + + + + + + + + + + + + + + + + + + +
+Entry view: pick skill + design system + brief
+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 question form
+Turn-1 discovery form — before the model writes a pixel, OCD locks the brief: surface, audience, tone, brand context, scale. 30 seconds of radios beats 30 minutes of redirects. +
+Direction picker — 5 deterministic visual directions
+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 TodoWrite plan streaming into the UI
+Live todo progress — the agent's plan streams as a live card. in_progresscompleted updates land in real time. The user can redirect cheaply, mid-flight. +
+Sandboxed iframe preview of the generated artifact
+Sandboxed preview — every <artifact> renders in a clean srcdoc iframe. Editable in place via the file workspace; downloadable as HTML, PDF, ZIP. +
+71-system design-system library with palette swatches
+71-system library — every product system shows its 4-color signature. Click for the full DESIGN.md, swatch grid, and live showcase. +
+Magazine-style horizontal deck output
+Deck mode (guizang-ppt) — the bundled guizang-ppt-skill drops in unchanged. Magazine layouts, WebGL hero backgrounds, single-file HTML output, PDF export. +
+Mobile app prototype with iPhone 15 Pro frame
+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. +
+ +## Six load-bearing ideas + +### 1 · We don't ship an agent. Yours is good enough. + +The daemon scans your `PATH` for [`claude`](https://docs.anthropic.com/en/docs/claude-code), [`codex`](https://github.com/openai/codex), [`cursor-agent`](https://www.cursor.com/cli), [`gemini`](https://github.com/google-gemini/gemini-cli), [`opencode`](https://opencode.ai/), and [`qwen`](https://github.com/QwenLM/qwen-code) on startup. Whichever it finds becomes the design engine — driven via stdio, with one adapter per CLI. Inspired by [`multica`](https://github.com/multica-ai/multica) and [`cc-switch`](https://github.com/farion1231/cc-switch). No CLI? `Anthropic API · BYOK` is the same pipeline minus the spawn. + +### 2 · Skills are files, not plugins. + +Following Claude Code's [`SKILL.md` convention](https://docs.anthropic.com/en/docs/claude-code/skills), each skill is `SKILL.md` + `assets/` + `references/`. Drop a folder into [`skills/`](skills/), restart the daemon, it appears in the picker. The bundled `magazine-web-ppt` is [`op7418/guizang-ppt-skill`](https://github.com/op7418/guizang-ppt-skill) committed verbatim — original license preserved, attribution preserved. + +### 3 · Design Systems are portable Markdown, not theme JSON. + +The 9-section `DESIGN.md` schema from [`VoltAgent/awesome-design-md`][acd2] — color, typography, spacing, layout, components, motion, voice, brand, anti-patterns. Every artifact reads from the active system. Switch system → next render uses the new tokens. The dropdown ships with **Linear, Stripe, Vercel, Airbnb, Tesla, Notion, Apple, Anthropic, Cursor, Supabase, Figma, Resend, Raycast, Lovable, Cohere, Mistral, ElevenLabs, X.AI, Spotify, Webflow, Sanity, PostHog, Sentry, MongoDB, ClickHouse, Cal, Replicate, Clay, Composio…** — 71 in total. + +### 4 · The interactive question form prevents 80% of redirects. + +OCD's prompt stack hard-codes a `RULE 1`: every fresh design brief begins with a `` instead of code. Surface · audience · tone · brand context · scale · constraints. A long brief still leaves design decisions open — visual tone, color stance, scale — exactly the things the form locks down in 30 seconds. The cost of a wrong direction is one chat round, not one finished deck. + +This is the **Junior-Designer mode** distilled from [`huashu-design`](https://github.com/alchaincyf/huashu-design): batch the questions up front, show something visible early (even a wireframe with grey blocks), let the user redirect cheaply. Combined with the brand-asset protocol (locate · download · `grep` hex · write `brand-spec.md` · vocalise), it's the single biggest reason output stops feeling like AI freestyle and starts feeling like a designer who paid attention before painting. + +### 5 · The daemon makes the agent feel like it's on your laptop, because it is. + +The daemon spawns the CLI with `cwd` set to the project's artifact folder under `.ocd/projects//`. The agent gets `Read`, `Write`, `Bash`, `WebFetch` — real tools against a real filesystem. It can `Read` the skill's `assets/template.html`, `grep` your CSS for hex values, write a `brand-spec.md`, drop generated images, and produce `.pptx` / `.zip` / `.pdf` files that show up in the file workspace as download chips when the turn ends. Sessions, conversations, messages, tabs persist in a local SQLite DB — pop the project open tomorrow and the agent's todo card is right where you left it. + +### 6 · The prompt stack is the product. + +What you compose at send time isn't "system + user". It's: + +``` +DISCOVERY directives (turn-1 form, turn-2 brand branch, TodoWrite, 5-dim critique) + + identity charter (OFFICIAL_DESIGNER_PROMPT, anti-AI-slop, junior-pass) + + active DESIGN.md (71 systems available) + + active SKILL.md (19 skills available) + + project metadata (kind, fidelity, speakerNotes, animations, inspiration ids) + + skill side files (auto-injected pre-flight: read assets/template.html + references/*.md) + + (deck mode only) DECK_FRAMEWORK_DIRECTIVE (nav / counter / scroll / print) +``` + +Every layer is composable. Every layer is a file you can edit. Read [`src/prompts/system.ts`](src/prompts/system.ts) and [`src/prompts/discovery.ts`](src/prompts/discovery.ts) to see the actual contract. + +## Architecture + +``` +┌────────────────────────── browser ─────────────────────────────┐ +│ │ +│ Vite + React SPA (chat · file workspace · iframe preview) │ +│ │ +└──────────────┬───────────────────────────────────┬─────────────┘ + │ /api/* (proxied in dev) │ direct (BYOK) + ▼ ▼ + ┌──────────────────────┐ ┌──────────────────────┐ + │ Local daemon │ │ Anthropic SDK │ + │ (Express + SQLite) │ │ (browser fallback) │ + │ │ └──────────────────────┘ + │ /api/agents │ + │ /api/skills │ + │ /api/design-systems│ + │ /api/projects/... │ + │ /api/chat (SSE) │ + │ │ + └─────────┬────────────┘ + │ spawn(cli, [...], { cwd: .ocd/projects/ }) + ▼ + ┌──────────────────────────────────────────────────────────┐ + │ claude · codex · cursor-agent · gemini · opencode · qwen│ + │ reads SKILL.md + DESIGN.md, writes artifacts to disk │ + └──────────────────────────────────────────────────────────┘ +``` + +| Layer | Stack | +|---|---| +| Frontend | Vite 5 + React 18 + TypeScript | +| Daemon | Node 18+ · Express · SSE streaming · `better-sqlite3` for projects/conversations/messages/tabs | +| Agent transport | `child_process.spawn` with `claude-stream-json` parser for Claude Code, line-buffered plain stdout for the rest | +| Storage | Plain files in `.ocd/projects//` + SQLite at `.ocd/db.sqlite` (gitignored) | +| Preview | Sandboxed iframe via `srcdoc` + per-skill `` parser | +| Export | HTML (inline assets) · PDF (browser print) · PPTX (skill-defined) · ZIP (archiver) | + +## Quickstart + +```bash +git clone https://github.com//open-claude-design.git +cd open-claude-design +pnpm install # or npm install +pnpm dev:all # daemon (:7456) + Vite (:5173) +open http://localhost:5173 +``` + +The first load: + +1. Detects which agent CLIs you have on `PATH` and picks one automatically. +2. Loads 19 skills + 71 design systems. +3. Pops the welcome dialog so you can paste an Anthropic key (only needed for the BYOK fallback path). + +Type a prompt, hit **Send**, watch the question form arrive, fill it, watch the todo card stream, watch the artifact render. Click **Save to disk** or download as a project ZIP. + +Full file map, scripts, and troubleshooting → [`QUICKSTART.md`](QUICKSTART.md). + +## Repository structure + +``` +open-claude-design/ +├── README.md ← this file +├── README.zh-CN.md ← 简体中文 +├── QUICKSTART.md ← run / build / deploy guide +├── package.json ← pnpm workspace, single bin: ocd +│ +├── daemon/ ← Node + Express, the only server +│ ├── cli.js ← `ocd` bin entry point +│ ├── server.js ← /api/* routes (projects, chat, files, exports) +│ ├── agents.js ← PATH scanner + per-CLI argv builders +│ ├── claude-stream.js ← streaming JSON parser for Claude Code stdout +│ ├── skills.js ← SKILL.md frontmatter loader +│ ├── design-systems.js ← DESIGN.md loader + swatch extractor +│ ├── design-system-preview.js ← live one-shot showcase per system +│ ├── design-system-showcase.js ← multi-section gallery render +│ ├── lint-artifact.js ← P0/P1 self-check on agent output +│ ├── projects.js ← per-project filesystem helpers +│ ├── db.js ← SQLite schema (projects/messages/templates/tabs) +│ └── frontmatter.js ← zero-dep YAML-subset parser +│ +├── src/ ← Vite + React + TS frontend +│ ├── App.tsx ← routing, bootstrap, settings +│ ├── components/ ← 27 components (chat, composer, picker, preview, sketch, …) +│ ├── prompts/ +│ │ ├── system.ts ← composeSystemPrompt(base, skill, DS, metadata) +│ │ ├── official-system.ts ← identity charter +│ │ ├── discovery.ts ← turn-1 form + turn-2 branch + 5-dim critique +│ │ ├── directions.ts ← 5 visual directions × OKLch palette + font stack +│ │ └── deck-framework.ts ← deck nav / counter / print stylesheet +│ ├── artifacts/ +│ │ ├── parser.ts ← streaming tag extractor +│ │ └── question-form.ts ← JSON schema + replay +│ ├── runtime/ +│ │ ├── srcdoc.ts ← iframe sandbox wrapper +│ │ ├── markdown.tsx ← assistant message renderer +│ │ ├── exports.ts ← HTML / PDF / ZIP export helpers +│ │ └── zip.ts ← project archive +│ ├── providers/ +│ │ ├── daemon.ts ← /api/chat SSE stream consumer +│ │ ├── anthropic.ts ← BYOK Anthropic SDK path +│ │ └── registry.ts ← /api/agents, /api/skills, /api/design-systems +│ └── state/ ← config + projects (localStorage + daemon-backed) +│ +├── skills/ ← 19 SKILL.md skill bundles +│ ├── web-prototype/ ← default for prototype mode +│ ├── saas-landing/ ← marketing page (hero / features / pricing / CTA) +│ ├── dashboard/ ← admin / analytics +│ ├── pricing-page/ ← standalone pricing + comparison +│ ├── docs-page/ ← 3-column documentation +│ ├── blog-post/ ← editorial long-form +│ ├── mobile-app/ ← phone-frame screen(s) +│ ├── simple-deck/ ← horizontal-swipe minimal +│ ├── guizang-ppt/ ← bundled magazine-web-ppt (default for deck) +│ │ ├── SKILL.md +│ │ ├── assets/template.html ← seed +│ │ └── references/{themes,layouts,components,checklist}.md +│ ├── pm-spec/ ← PM specification doc +│ ├── weekly-update/ ← team weekly +│ ├── meeting-notes/ ← decision log +│ ├── eng-runbook/ ← incident / runbook +│ ├── finance-report/ ← exec summary +│ ├── hr-onboarding/ ← role onboarding +│ ├── invoice/ ← single-page invoice +│ ├── kanban-board/ ← board snapshot +│ ├── mobile-onboarding/ ← multi-screen mobile flow +│ └── team-okrs/ ← OKR scoresheet +│ +├── design-systems/ ← 71 DESIGN.md systems +│ ├── default/ ← Neutral Modern (starter) +│ ├── warm-editorial/ ← Warm Editorial (starter) +│ ├── linear-app/ vercel/ stripe/ airbnb/ notion/ cursor/ apple/ … +│ └── README.md ← catalog overview +│ +├── assets/ +│ └── frames/ ← shared device frames (used cross-skill) +│ ├── iphone-15-pro.html +│ ├── android-pixel.html +│ ├── ipad-pro.html +│ ├── macbook.html +│ └── browser-chrome.html +│ +├── templates/ +│ └── deck-framework.html ← deck baseline (nav / counter / print) +│ +├── scripts/ +│ └── sync-design-systems.mjs ← re-import upstream awesome-design-md tarball +│ +├── docs/ +│ ├── spec.md ← product spec, scenarios, differentiation +│ ├── architecture.md ← topologies, data flow, components +│ ├── skills-protocol.md ← extended SKILL.md ocd: frontmatter +│ ├── agent-adapters.md ← per-CLI detection + dispatch +│ ├── modes.md ← prototype / deck / template / design-system +│ ├── references.md ← long-form provenance +│ ├── roadmap.md ← phased delivery +│ ├── schemas/ ← JSON schemas +│ └── examples/ ← canonical artifact examples +│ +└── .ocd/ ← runtime data, gitignored + ├── projects// ← per-project working folder (agent's cwd) + ├── artifacts/ ← saved one-off renders + └── db.sqlite ← projects / conversations / messages / tabs +``` + +## 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 `ocd:` 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 design systems library +

+ +71 systems out of the box, each as a single [`DESIGN.md`](design-systems/README.md): + +
+Full catalog (click to expand) + +**AI & LLM** — `claude` · `cohere` · `mistral-ai` · `minimax` · `together-ai` · `replicate` · `runwayml` · `elevenlabs` · `ollama` · `x-ai` + +**Developer Tools** — `cursor` · `vercel` · `linear-app` · `framer` · `expo` · `clickhouse` · `mongodb` · `supabase` · `hashicorp` · `posthog` · `sentry` · `warp` · `webflow` · `sanity` · `mintlify` · `lovable` · `composio` · `opencode-ai` · `voltagent` + +**Productivity** — `notion` · `figma` · `miro` · `airtable` · `superhuman` · `intercom` · `zapier` · `cal` · `clay` · `raycast` + +**Fintech** — `stripe` · `coinbase` · `binance` · `kraken` · `mastercard` · `revolut` · `wise` + +**E-Commerce** — `shopify` · `airbnb` · `uber` · `nike` · `starbucks` · `pinterest` + +**Media** — `spotify` · `playstation` · `wired` · `theverge` · `meta` + +**Automotive** — `tesla` · `bmw` · `ferrari` · `lamborghini` · `bugatti` · `renault` + +**Other** — `apple` · `ibm` · `nvidia` · `vodafone` · `sentry` · `resend` · `spacex` + +**Starters** — `default` (Neutral Modern) · `warm-editorial` + +
+ +The library is imported via [`scripts/sync-design-systems.mjs`](scripts/sync-design-systems.mjs) from [`VoltAgent/awesome-design-md`][acd2]. Re-run to refresh. + +## Visual directions + +When the user has no brand spec, the agent emits a second form with five curated directions — the OCD adaptation of [`huashu-design`'s "5 schools × 20 design philosophies" fallback](https://github.com/alchaincyf/huashu-design#%E8%AE%BE%E8%AE%A1%E6%96%B9%E5%90%91%E9%A1%BE%E9%97%AE-fallback). Each direction is a deterministic spec — palette in OKLch, font stack, layout posture cues, references — that the agent binds verbatim into the seed template's `:root`. One radio click → a fully specified visual system. No improvisation, no AI-slop. + +| Direction | Mood | Refs | +|---|---|---| +| Editorial — Monocle / FT | Print magazine, ink + cream + warm rust | Monocle · FT Weekend · NYT Magazine | +| Modern minimal — Linear / Vercel | Cool, structured, minimal accent | Linear · Vercel · Stripe | +| Tech utility | Information density, monospace, terminal | Bloomberg · Bauhaus tools | +| Brutalist | Raw, oversized type, no shadows, harsh accents | Bloomberg Businessweek · Achtung | +| Soft warm | Generous, low contrast, peachy neutrals | Notion marketing · Apple Health | + +Full spec → [`src/prompts/directions.ts`](src/prompts/directions.ts). + +## Anti-AI-slop machinery + +The whole machinery below is the [`huashu-design`](https://github.com/alchaincyf/huashu-design) playbook, ported into OCD's prompt-stack and made enforceable per-skill via the side-file pre-flight. Read [`src/prompts/discovery.ts`](src/prompts/discovery.ts) for the live wording: + +- **Question form first.** Turn 1 is `` only — no thinking, no tools, no narration. The user chooses defaults at radio speed. +- **Brand-spec extraction.** When the user attaches a screenshot or URL, the agent runs a five-step protocol (locate · download · grep hex · codify `brand-spec.md` · vocalise) before writing CSS. **Never guesses brand colors from memory.** +- **Five-dim critique.** Before emitting ``, the agent silently scores its output 1–5 across philosophy / hierarchy / execution / specificity / restraint. Anything under 3/5 is a regression — fix and rescore. Two passes is normal. +- **P0/P1/P2 checklist.** Every skill ships a `references/checklist.md` with hard P0 gates. The agent must pass P0 before emitting. +- **Slop blacklist.** Aggressive purple gradients, generic emoji icons, rounded card with left-border accent, hand-drawn SVG humans, Inter as a *display* face, invented metrics — explicitly forbidden in the prompt. +- **Honest placeholders > fake stats.** When the agent doesn't have a real number, it writes `—` or a labelled grey block, not "10× faster". + +## Comparison + +| Axis | [Claude Design][cd] (Anthropic) | [Open CoDesign][ocod] | **Open Claude Design** | +|---|---|---|---| +| License | Closed | MIT | **Apache-2.0** | +| Form factor | Web (claude.ai) | Desktop (Electron) | **Web app + local daemon** | +| Deployable on Vercel | ❌ | ❌ | **✅** | +| Agent runtime | Bundled (Opus 4.7) | Bundled ([`pi-ai`][piai]) | **Delegated to user's existing CLI** | +| Skills | Proprietary | 12 custom TS modules + `SKILL.md` | **19 file-based [`SKILL.md`][skill] bundles, droppable** | +| Design system | Proprietary | `DESIGN.md` (v0.2 roadmap) | **`DESIGN.md` × 71 systems shipped** | +| Provider flexibility | Anthropic only | 7+ via [`pi-ai`][piai] | **Whatever your agent supports** | +| Init question form | ❌ | ❌ | **✅ Hard rule, turn 1** | +| Direction picker | ❌ | ❌ | **✅ 5 deterministic directions** | +| Live todo progress + tool stream | ❌ | ✅ | **✅** (UX pattern from open-codesign) | +| Sandboxed iframe preview | ❌ | ✅ | **✅** (pattern from open-codesign) | +| Comment-mode surgical edits | ❌ | ✅ | 🚧 roadmap (lift from open-codesign) | +| AI-emitted tweaks panel | ❌ | ✅ | 🚧 roadmap (lift from open-codesign) | +| Filesystem-grade workspace | ❌ | partial (Electron sandbox) | **✅ Real cwd, real tools, persisted SQLite** | +| 5-dim self-critique | ❌ | ❌ | **✅ Pre-emit gate** | +| Export formats | Limited | HTML / PDF / PPTX / ZIP / Markdown | **HTML / PDF / PPTX / ZIP / Markdown** | +| 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 +[ocod]: https://github.com/OpenCoworkAI/open-codesign +[piai]: https://github.com/mariozechner/pi-ai +[acd]: https://github.com/VoltAgent/awesome-claude-design +[guizang]: https://github.com/op7418/guizang-ppt-skill +[skill]: https://docs.anthropic.com/en/docs/claude-code/skills + +## Supported coding agents + +Auto-detected from `PATH` on daemon boot. No config required. + +| Agent | Bin | Streaming | Notes | +|---|---|---|---| +| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude` | `--output-format stream-json` (typed events) | First-class — best fidelity | +| [Codex CLI](https://github.com/openai/codex) | `codex` | line-buffered | `codex exec ` | +| [Cursor Agent](https://www.cursor.com/cli) | `cursor-agent` | line-buffered | `cursor-agent -p` | +| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | line-buffered | `gemini -p` | +| [OpenCode](https://opencode.ai/) | `opencode` | line-buffered | `opencode run` | +| [Qwen Code](https://github.com/QwenLM/qwen-code) | `qwen` | line-buffered | `qwen -p` | +| Anthropic API · BYOK | n/a | SSE direct | Browser fallback when no CLI is on PATH | + +Adding a new CLI is one entry in [`daemon/agents.js`](daemon/agents.js). Streaming format is one of `claude-stream-json` (typed events) or `plain` (raw text). + +## References & lineage + +Every external project this repo borrows from. Each link goes to the source so you can verify the provenance. + +| Project | Role here | +|---|---| +| [`Claude Design`][cd] | The closed product this repo provides an open substrate for. | +| [**`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. | +| [**`OpenCoworkAI/open-codesign`**][ocod] | The first open-source Claude-Design alternative and our closest peer. UX patterns adopted: streaming-artifact loop, sandboxed-iframe preview (vendored React 18 + Babel), live agent panel (todos + tool calls + interruptible), five-format export list (HTML/PDF/PPTX/ZIP/Markdown), local-first storage hub, `SKILL.md` taste-injection. UX patterns on our roadmap: comment-mode surgical edits, AI-emitted tweaks panel. **We deliberately do not vendor [`pi-ai`][piai]** — open-codesign bundles it as the agent runtime; we delegate to whichever CLI the user already has. | +| [`VoltAgent/awesome-claude-design`][acd] / [`awesome-design-md`][acd2] | Source of the 9-section `DESIGN.md` schema and 69 product systems imported via [`scripts/sync-design-systems.mjs`](scripts/sync-design-systems.mjs). | +| [`farion1231/cc-switch`](https://github.com/farion1231/cc-switch) | Inspiration for symlink-based skill distribution across multiple agent CLIs. | +| [Claude Code skills][skill] | The `SKILL.md` convention adopted verbatim — any Claude Code skill drops into `skills/` and is picked up by the daemon. | + +Long-form provenance write-up — what we take from each, what we deliberately don't — lives at [`docs/references.md`](docs/references.md). + +## Roadmap + +- [x] Daemon + agent detection + skill registry + design-system catalog +- [x] Web app + chat + question form + todo progress + sandboxed preview +- [x] 19 skills + 71 design systems + 5 visual directions + 5 device frames +- [x] SQLite-backed projects · conversations · messages · tabs · templates +- [ ] Comment-mode surgical edits (click element → instruction → patch) — pattern from [`open-codesign`][ocod] +- [ ] AI-emitted tweaks panel (model surfaces the parameters worth tweaking) — pattern from [`open-codesign`][ocod] +- [ ] Vercel + tunnel deployment recipe (Topology B) +- [ ] One-command `npx ocd init` to scaffold a project with `DESIGN.md` +- [ ] Skill marketplace (`ocd skills install `) + +Phased delivery → [`docs/roadmap.md`](docs/roadmap.md). + +## Status + +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. + +## License + +Apache-2.0. The bundled `skills/guizang-ppt/` retains its original [LICENSE](skills/guizang-ppt/LICENSE) (MIT) and authorship attribution to [op7418](https://github.com/op7418). diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..2cda340 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,497 @@ +# Open Claude Design + +> **给设计的 Claude Code。** 一个本地优先、可部署到 Vercel 的开源 [Claude Design][cd] 复刻 —— 你机器上已经装好的 coding agent(Claude Code、Codex、Cursor Agent、Gemini CLI、OpenCode、Qwen)就是设计引擎,由可组合的 **Skills** 和 **71 套品牌级 Design System** 驱动。 + +

+ Open Claude Design banner +

+ +

+ License + Agents + Design systems + Skills + Quickstart +

+ +

English · 简体中文

+ +--- + +## 为什么要做这个 + +Anthropic 的 [Claude Design][cd](2026-04-17 发布,基于 Opus 4.7)让大家第一次看到:当一个 LLM 不再写废话、开始直接交付设计成品,会是什么样子。它瞬间出圈 —— 然后保持闭源、付费、只跑在云上、绑定 Anthropic 的模型和 Anthropic 的内部 skill。 + +**Open Claude Design(OCD)是它的开源底座。** 我们不做 agent —— 你笔记本上最强的 coding agent 已经装好了。我们要做的,是把它接进一个 skill 驱动的设计工作流,跑在一个普通的 Web 应用里:本地 `pnpm dev`,云端 `vercel deploy`,每一层都 BYOK(自带 Key)。 + +输入「帮我做一份杂志风的种子轮 pitch deck」。在模型挥洒第一个像素之前,**初始化问题表单**已经先跳出来。Agent 从 5 套精挑的视觉方向里选一个。一张活的 `TodoWrite` 计划卡片实时流入 UI。Daemon 在磁盘上构建出一个真实的项目目录,里面有 seed 模板、布局库、自检 checklist。Agent **强制 pre-flight** 读取它们,对自己的输出跑一轮**五维评审**,几秒后吐出一个 ``,渲染在沙盒 iframe 里。 + +这不是「AI 试图做点设计」。这是一个被提示词栈训练得像高级设计师一样工作的 AI —— 有可用的文件系统、有确定性的色板库、有 checklist 文化。 + +OCD 站在四个开源项目的肩膀上: + +- [**`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)。 +- [**`op7418/guizang-ppt-skill`**(歸藏的杂志风 PPT skill)](https://github.com/op7418/guizang-ppt-skill) —— Deck 模式。原样捆绑在 [`skills/guizang-ppt/`](skills/guizang-ppt/) 下,原 LICENSE 保留;杂志版式、WebGL hero、P0/P1/P2 checklist。 +- [**`OpenCoworkAI/open-codesign`**](https://github.com/OpenCoworkAI/open-codesign) —— UX 北极星,也是我们最接近的同类。第一个开源的 Claude-Design 替代品。我们借鉴了它的流式 artifact 循环、沙盒 iframe 预览模式(自带 React 18 + Babel)、实时 agent 面板(todos + tool calls + 可中断生成)、5 种导出格式列表(HTML / PDF / PPTX / ZIP / Markdown)。我们刻意在形态上分流 —— 它是桌面 Electron 应用,把 [`pi-ai`][piai] 打包进去做 agent;我们是 Web 应用 + 本地 daemon,把 agent 运行时**委托**给你已经装好的 CLI。 +- [**`multica-ai/multica`**](https://github.com/multica-ai/multica) —— Daemon 与运行时架构。PATH 扫描式 agent 检测,本地 daemon 作为唯一的特权进程,agent-as-teammate 的世界观。 + +## 一眼概览 + +| | 你拿到的 | +|---|---| +| **支持的 coding agent** | Claude Code · Codex CLI · Cursor Agent · Gemini CLI · OpenCode · Qwen Code · Anthropic API(BYOK 兜底) | +| **内置 design system** | **71 套** —— 2 套手写起手 + 69 套从 [`awesome-design-md`][acd2] 导入的产品系统(Linear、Stripe、Vercel、Airbnb、Tesla、Notion、Anthropic、Apple、Cursor、Supabase、Figma…) | +| **内置 skill** | **19 个** —— 原型 / deck / 移动端 / dashboard / pricing / docs / blog / SaaS landing,外加 10 个文档与办公产物模板(PM 规范、周报、OKR、runbook、看板…) | +| **视觉方向** | 5 套精选流派(Editorial Monocle · Modern Minimal · Tech Utility · Brutalist · Soft Warm),每一套自带 OKLch 色板 + 字体栈 | +| **设备外壳** | iPhone 15 Pro · Pixel · iPad Pro · MacBook · Browser Chrome —— 像素级精确,跨 skill 共享 | +| **Agent 运行时** | 本地 daemon 在你的项目目录里 spawn CLI —— agent 拥有真实的 `Read` / `Write` / `Bash` / `WebFetch`,作用在真实磁盘上 | +| **部署目标** | 本地 `pnpm dev` · Vercel · 单进程生产 (`npm start`) | +| **License** | Apache-2.0 | + +[acd2]: https://github.com/VoltAgent/awesome-design-md + +## 效果展示 + +> 截图位为占位 —— 真实 UI 就是这些截图要拍的对象,请在 PR 里替换 `docs/screenshots/*.png`。 + + + + + + + + + + + + + + + + + + +
+入口页:选 skill + 选 design system + 写需求
+入口页 —— 选 skill、选 design system、写一行需求。同一个表面服务原型、deck、移动端、dashboard、editorial 页面所有 mode。 +
+第一轮 discovery 表单
+初始化问题表单 —— 模型动笔之前,OCD 先把需求锁住:surface、受众、调性、品牌上下文、规模。30 秒勾选项秒杀 30 分钟来回返工。 +
+5 套确定性视觉方向选择器
+方向选择器 —— 用户没有品牌上下文时,agent 自动跳第二个表单,5 套精选方向(Monocle / Modern Minimal / Tech Utility / Brutalist / Soft Warm)一个 radio 选完,色板 + 字体栈直接锁定,没有 freestyle 空间。 +
+Live TodoWrite 进度卡片
+实时 todo 进度 —— Agent 的计划以活卡片形式流入 UI。in_progresscompleted 实时切换。用户能在中途以极低成本介入纠偏。 +
+沙盒 iframe 预览生成的 artifact
+沙盒预览 —— 每个 <artifact> 都在干净的 srcdoc iframe 里渲染。可在文件工作区里就地编辑;可下载为 HTML / PDF / ZIP。 +
+71 套 design system 库 + 调色板
+71 套 design system 库 —— 每套产品系统都展示 4 色色卡。点进去看完整的 DESIGN.md、色板网格、live showcase。 +
+杂志风横向翻页 deck 输出
+Deck 模式(guizang-ppt) —— 内置的 guizang-ppt-skill 原样接入。杂志版式、WebGL hero 背景、单文件 HTML 输出、可导 PDF。 +
+带 iPhone 15 Pro 外壳的移动端原型
+移动端原型 —— 像素级精确的 iPhone 15 Pro chrome(灵动岛、状态栏 SVG、Home Indicator)。多屏原型直接复用 /frames/ 共享资源,agent 永远不需要重新画一遍手机。 +
+ +## 六个底层设计 + +### 1 · 我们不带 agent,你的就够好 + +Daemon 启动时扫 `PATH`,找 [`claude`](https://docs.anthropic.com/en/docs/claude-code)、[`codex`](https://github.com/openai/codex)、[`cursor-agent`](https://www.cursor.com/cli)、[`gemini`](https://github.com/google-gemini/gemini-cli)、[`opencode`](https://opencode.ai/)、[`qwen`](https://github.com/QwenLM/qwen-code)。哪个在就用哪个 —— 通过 stdio 驱动,每个 CLI 一个 adapter。灵感来自 [`multica`](https://github.com/multica-ai/multica) 和 [`cc-switch`](https://github.com/farion1231/cc-switch)。一个 CLI 都没有?`Anthropic API · BYOK` 就是同一条管线减去 spawn。 + +### 2 · Skill 是文件,不是插件 + +遵循 Claude Code [`SKILL.md` 规范](https://docs.anthropic.com/en/docs/claude-code/skills),每个 skill = `SKILL.md` + `assets/` + `references/`。把一个文件夹丢进 [`skills/`](skills/),重启 daemon,picker 里就能看到。内置的 `magazine-web-ppt` 就是 [`op7418/guizang-ppt-skill`](https://github.com/op7418/guizang-ppt-skill) **原样**捆绑 —— 原 LICENSE 保留、原作者归属保留。 + +### 3 · Design System 是可移植的 Markdown,不是 theme JSON + +[`VoltAgent/awesome-design-md`][acd2] 的 9 段式 `DESIGN.md` —— color、typography、spacing、layout、components、motion、voice、brand、anti-patterns。每个 artifact 都从激活的 system 里读 token。切换 system → 下一次渲染就用新的 token。下拉框里现成的有:**Linear、Stripe、Vercel、Airbnb、Tesla、Notion、Apple、Anthropic、Cursor、Supabase、Figma、Resend、Raycast、Lovable、Cohere、Mistral、ElevenLabs、X.AI、Spotify、Webflow、Sanity、PostHog、Sentry、MongoDB、ClickHouse、Cal、Replicate、Clay、Composio…** 共 71 套。 + +### 4 · 初始化问题表单干掉 80% 的来回返工 + +OCD 的提示词栈把 `RULE 1` 写死了:每个新设计任务都从 `` 开始,**不是代码**。Surface · 受众 · 调性 · 品牌上下文 · 规模 · 约束。一段写得很长的需求里仍然有大量留白:视觉调性、色彩立场、规模 —— 而表单恰恰把这些用 30 秒勾选项锁死。错方向的代价是一轮对话,不是一份做完的 deck。 + +这就是从 [`huashu-design`](https://github.com/alchaincyf/huashu-design) 蒸馏出来的 **Junior-Designer 模式**:开工前一次性批量问完,尽早 show 出一些可见的东西(哪怕只是灰色方块的 wireframe),让用户用最低成本介入纠偏。再叠加品牌资产协议(定位 · 下载 · `grep` hex · 写 `brand-spec.md` · 复述),这是输出从「AI freestyle」跳到「先看资料再画图的设计师」最关键的一步。 + +### 5 · Daemon 让 agent 感觉自己就在你笔记本上 —— 因为它就是 + +Daemon `spawn` CLI 时,`cwd` 设到该项目在 `.ocd/projects//` 下的 artifact 文件夹。Agent 拿到的 `Read` / `Write` / `Bash` / `WebFetch` 都是真工具,作用在真文件系统上。它能 `Read` skill 的 `assets/template.html`,能 `grep` 你的 CSS 拿 hex,能写一份 `brand-spec.md`,能落地生成的图片,能产出 `.pptx` / `.zip` / `.pdf` —— 这些文件在 turn 结束的时候作为下载 chip 出现在文件工作区里。Session、对话、消息、tab 都持久化在本地 SQLite 里 —— 明天再打开这个项目,agent 的 todo 卡片还在你昨天停下的地方。 + +### 6 · 提示词栈本身就是产品 + +发送时拼装的不是「system + user」。它是: + +``` +DISCOVERY 指令 (turn-1 表单、turn-2 品牌分支、TodoWrite、五维评审) + + 身份与工作流宪章 (OFFICIAL_DESIGNER_PROMPT、anti-AI-slop、Junior Designer 模式) + + 激活的 DESIGN.md (71 套备选) + + 激活的 SKILL.md (19 套备选) + + 项目元数据 (kind、fidelity、speakerNotes、animations、灵感 system id) + + Skill 副文件 (自动注入 pre-flight:先读 assets/template.html + references/*.md) + + (仅 deck 模式) DECK_FRAMEWORK_DIRECTIVE (nav / counter / scroll / print) +``` + +每一层都可组合。每一层都是一个你能改的文件。看 [`src/prompts/system.ts`](src/prompts/system.ts) 和 [`src/prompts/discovery.ts`](src/prompts/discovery.ts) 就知道真实契约长什么样。 + +## 技术架构 + +``` +┌────────────────────────── 浏览器 ──────────────────────────────┐ +│ │ +│ Vite + React SPA (chat · 文件工作区 · iframe 预览) │ +│ │ +└──────────────┬───────────────────────────────────┬─────────────┘ + │ /api/* (dev 走代理) │ direct (BYOK) + ▼ ▼ + ┌──────────────────────┐ ┌──────────────────────┐ + │ 本地 daemon │ │ Anthropic SDK │ + │ (Express + SQLite)│ │ (浏览器兜底) │ + │ │ └──────────────────────┘ + │ /api/agents │ + │ /api/skills │ + │ /api/design-systems│ + │ /api/projects/... │ + │ /api/chat (SSE) │ + │ │ + └─────────┬────────────┘ + │ spawn(cli, [...], { cwd: .ocd/projects/ }) + ▼ + ┌──────────────────────────────────────────────────────────┐ + │ claude · codex · cursor-agent · gemini · opencode · qwen│ + │ 读 SKILL.md + DESIGN.md,把 artifact 写到磁盘 │ + └──────────────────────────────────────────────────────────┘ +``` + +| 层 | 技术栈 | +|---|---| +| 前端 | Vite 5 + React 18 + TypeScript | +| Daemon | Node 18+ · Express · SSE 流 · `better-sqlite3` 存项目/对话/消息/tab | +| Agent 传输层 | `child_process.spawn`,Claude Code 走 `claude-stream-json` 解析器,其余走 line-buffered plain stdout | +| 存储 | 纯文件 `.ocd/projects//` + SQLite `.ocd/db.sqlite`(已 gitignore) | +| 预览 | 沙盒 iframe(`srcdoc`)+ 每个 skill 的 `` parser | +| 导出 | HTML(内联资源)· PDF(浏览器打印)· PPTX(skill 自定义)· ZIP(archiver) | + +## Quickstart + +```bash +git clone https://github.com//open-claude-design.git +cd open-claude-design +pnpm install # 或 npm install +pnpm dev:all # daemon (:7456) + Vite (:5173) 一起起 +open http://localhost:5173 +``` + +第一次加载会: + +1. 检测你 `PATH` 上有哪些 agent CLI,自动选一个。 +2. 加载 19 个 skill + 71 套 design system。 +3. 弹欢迎对话框,让你贴 Anthropic key(仅 BYOK 兜底路径需要)。 + +输入需求,回车,看 question form 跳出来,填,看 todo 卡片流动,看 artifact 渲染。点 **Save to disk** 或导出整个项目 ZIP。 + +完整文件地图、脚本、排错 → [`QUICKSTART.md`](QUICKSTART.md)。 + +## 仓库结构 + +``` +open-claude-design/ +├── README.md ← 英文 +├── README.zh-CN.md ← 本文件 +├── QUICKSTART.md ← 跑 / 构建 / 部署 +├── package.json ← 单 bin: ocd +│ +├── daemon/ ← Node + Express,唯一的服务端 +│ ├── cli.js ← `ocd` 二进制入口 +│ ├── server.js ← /api/* 路由(projects、chat、files、exports) +│ ├── agents.js ← PATH 扫描器 + 各 CLI 的 argv 拼装 +│ ├── claude-stream.js ← Claude Code stdout 流式 JSON 解析 +│ ├── skills.js ← SKILL.md frontmatter 加载器 +│ ├── design-systems.js ← DESIGN.md 加载器 + swatch 提取 +│ ├── design-system-preview.js ← 单系统 live showcase +│ ├── design-system-showcase.js ← 多 section 画廊渲染 +│ ├── lint-artifact.js ← 输出的 P0/P1 自检 +│ ├── projects.js ← 项目级文件系统辅助 +│ ├── db.js ← SQLite schema(projects/messages/templates/tabs) +│ └── frontmatter.js ← 零依赖 YAML 子集解析 +│ +├── src/ ← Vite + React + TS 前端 +│ ├── App.tsx ← 路由、bootstrap、设置 +│ ├── components/ ← 27 个组件(chat、composer、picker、preview、sketch…) +│ ├── prompts/ +│ │ ├── system.ts ← composeSystemPrompt(base, skill, DS, metadata) +│ │ ├── official-system.ts ← 身份宪章 +│ │ ├── discovery.ts ← turn-1 表单 + turn-2 分支 + 五维评审 +│ │ ├── directions.ts ← 5 套视觉方向 × OKLch 色板 + 字体栈 +│ │ └── deck-framework.ts ← deck 导航 / 计数 / 打印样式 +│ ├── artifacts/ +│ │ ├── parser.ts ← 流式 标签解析 +│ │ └── question-form.ts ← JSON 协议 + 重放 +│ ├── runtime/ +│ │ ├── srcdoc.ts ← iframe 沙盒包装 +│ │ ├── markdown.tsx ← 助手消息渲染 +│ │ ├── exports.ts ← HTML / PDF / ZIP 导出 +│ │ └── zip.ts ← 项目打包 +│ ├── providers/ +│ │ ├── daemon.ts ← /api/chat SSE 流消费者 +│ │ ├── anthropic.ts ← BYOK Anthropic SDK 路径 +│ │ └── registry.ts ← /api/agents、/api/skills、/api/design-systems +│ └── state/ ← config + projects(localStorage + daemon 持久化) +│ +├── skills/ ← 19 个 SKILL.md skill 包 +│ ├── web-prototype/ ← 原型默认 +│ ├── saas-landing/ ← 营销页(hero / features / pricing / CTA) +│ ├── dashboard/ ← 后台 / 数据看板 +│ ├── pricing-page/ ← 独立定价页 + 对比 +│ ├── docs-page/ ← 三栏文档 +│ ├── blog-post/ ← 长文 editorial +│ ├── mobile-app/ ← 带手机外壳的 app 屏 +│ ├── simple-deck/ ← 极简横滑 deck +│ ├── guizang-ppt/ ← 内置 magazine-web-ppt(deck 默认) +│ │ ├── SKILL.md +│ │ ├── assets/template.html ← seed +│ │ └── references/{themes,layouts,components,checklist}.md +│ ├── pm-spec/ ← PM 规范文档 +│ ├── weekly-update/ ← 团队周报 +│ ├── meeting-notes/ ← 会议纪要 +│ ├── eng-runbook/ ← 故障 / runbook +│ ├── finance-report/ ← 财务摘要 +│ ├── hr-onboarding/ ← 入职计划 +│ ├── invoice/ ← 单页发票 +│ ├── kanban-board/ ← 看板快照 +│ ├── mobile-onboarding/ ← 多屏移动流 +│ └── team-okrs/ ← OKR 计分表 +│ +├── design-systems/ ← 71 套 DESIGN.md +│ ├── default/ ← Neutral Modern(起手) +│ ├── warm-editorial/ ← Warm Editorial(起手) +│ ├── linear-app/ vercel/ stripe/ airbnb/ notion/ cursor/ apple/ … +│ └── README.md +│ +├── assets/ +│ └── frames/ ← 跨 skill 共享设备外壳 +│ ├── iphone-15-pro.html +│ ├── android-pixel.html +│ ├── ipad-pro.html +│ ├── macbook.html +│ └── browser-chrome.html +│ +├── templates/ +│ └── deck-framework.html ← deck 基线(nav / counter / print) +│ +├── scripts/ +│ └── sync-design-systems.mjs ← 从上游 awesome-design-md tarball 重新导入 +│ +├── docs/ +│ ├── spec.md ← 产品定义、场景、差异化 +│ ├── architecture.md ← 拓扑、数据流、组件 +│ ├── skills-protocol.md ← 扩展 SKILL.md 的 ocd: frontmatter +│ ├── agent-adapters.md ← 各 CLI 检测 + 派发 +│ ├── modes.md ← prototype / deck / template / design-system +│ ├── references.md ← 详尽的引用与师承 +│ ├── roadmap.md ← 分阶段交付 +│ ├── schemas/ ← JSON schema +│ └── examples/ ← 标准 artifact 样例 +│ +└── .ocd/ ← 运行时数据,已 gitignore + ├── projects// ← 每个项目的工作目录(agent 的 cwd) + ├── artifacts/ ← 单次保存的 artifact + └── db.sqlite ← 项目 / 对话 / 消息 / tab +``` + +## 内置 Skills + +19 个 skill,每个一个文件夹,都遵循 Claude Code 的 [`SKILL.md`][skill] 规范,并叠加 OCD 的 `ocd:` frontmatter(`mode`、`platform`、`scenario`、`preview`、`design_system`)。 + +### 示例展示(Showcase examples) + +视觉表现最强、最适合上手第一跑的几条 skill。每条都附带可直接打开的 `example.html`,先看产出再下单。 + +| Skill | Mode | 产出 | +|---|---|---| +| [`dating-web`](skills/dating-web/) | prototype | 消费级约会 / 婚恋仪表盘 —— 左侧栏、社区动态 ticker、头部 KPI、30 天双向匹配柱状图,editorial 字体,克制点缀色 | +| [`digital-eguide`](skills/digital-eguide/) | template | 两页数字 e-guide —— 封面(标题、作者、TOC 预告)+ 内文跨页(pull-quote + 步骤列表),创作者 / 生活方式风 | +| [`email-marketing`](skills/email-marketing/) | prototype | 品牌新品发布邮件 —— 顶部 wordmark、hero 图、标题锁排、主 CTA、规格网格。居中单列 + 表格降级,邮件客户端安全 | +| [`gamified-app`](skills/gamified-app/) | prototype | 三屏游戏化移动 app 原型,黑色舞台 —— 封面 / 今日任务(XP 缎带 + 等级条)/ 任务详情 | +| [`mobile-onboarding`](skills/mobile-onboarding/) | prototype | 三屏移动端引导流 —— splash、价值主张、登录。状态栏、滑动点、主 CTA | +| [`motion-frames`](skills/motion-frames/) | prototype | 单帧 motion 设计 hero,CSS 循环动画 —— 旋转字环、地球、计时器。可直接交给 HyperFrames 等关键帧导出 | +| [`social-carousel`](skills/social-carousel/) | prototype | 1080×1080 三连社媒轮播图 —— 三张电影感面板,标题前后呼应,品牌标识、loop 标记 | +| [`sprite-animation`](skills/sprite-animation/) | prototype | 像素 / 8-bit 动画解释器单帧 —— 米白通屏、像素吉祥物、动感日文标题、循环 CSS keyframes,可直接录屏成竖版视频 | + +### 设计交付类 + +| Skill | Mode | 默认场景 | 产出 | +|---|---|---|---| +| [`web-prototype`](skills/web-prototype/) | prototype | 桌面 | 单页 HTML —— landing、营销、hero | +| [`saas-landing`](skills/saas-landing/) | prototype | 桌面 | hero / features / pricing / CTA 营销版式 | +| [`dashboard`](skills/dashboard/) | prototype | 桌面 | 带侧栏 + 数据密集型的后台 | +| [`pricing-page`](skills/pricing-page/) | prototype | 桌面 | 独立定价页 + 对比表 | +| [`docs-page`](skills/docs-page/) | prototype | 桌面 | 三栏文档版式 | +| [`blog-post`](skills/blog-post/) | prototype | 桌面 | 长文 editorial | +| [`mobile-app`](skills/mobile-app/) | prototype | 移动 | 带 iPhone 15 Pro / Pixel 外壳的 app 屏 | +| [`simple-deck`](skills/simple-deck/) | deck | 桌面 | 极简横滑 deck | +| [`guizang-ppt`](skills/guizang-ppt/) | deck | **deck 默认** | 杂志风网页 PPT —— 来自 [op7418/guizang-ppt-skill][guizang] | + +### 文档与办公产物类 + +| Skill | Mode | 产出 | +|---|---|---| +| [`pm-spec`](skills/pm-spec/) | template | PM 规范文档 + 目录 + 决策日志 | +| [`weekly-update`](skills/weekly-update/) | template | 团队周报:进度 / 阻塞 / 下一步 | +| [`meeting-notes`](skills/meeting-notes/) | template | 会议决策纪要 | +| [`eng-runbook`](skills/eng-runbook/) | template | 故障 runbook | +| [`finance-report`](skills/finance-report/) | template | 高管财务摘要 | +| [`hr-onboarding`](skills/hr-onboarding/) | template | 岗位入职计划 | +| [`invoice`](skills/invoice/) | template | 单页发票 | +| [`kanban-board`](skills/kanban-board/) | template | 看板快照 | +| [`team-okrs`](skills/team-okrs/) | template | OKR 计分表 | + +新增一个 skill 就是新增一个文件夹。读 [`docs/skills-protocol.md`](docs/skills-protocol.md) 了解扩展 frontmatter,fork 一个现有 skill,重启 daemon 即生效。 + +## Design System + +

+ 71 套 design system 库 +

+ +71 套开箱即用,每套一个 [`DESIGN.md`](design-systems/README.md): + +
+完整目录(点击展开) + +**AI & LLM** —— `claude` · `cohere` · `mistral-ai` · `minimax` · `together-ai` · `replicate` · `runwayml` · `elevenlabs` · `ollama` · `x-ai` + +**开发者工具** —— `cursor` · `vercel` · `linear-app` · `framer` · `expo` · `clickhouse` · `mongodb` · `supabase` · `hashicorp` · `posthog` · `sentry` · `warp` · `webflow` · `sanity` · `mintlify` · `lovable` · `composio` · `opencode-ai` · `voltagent` + +**生产力** —— `notion` · `figma` · `miro` · `airtable` · `superhuman` · `intercom` · `zapier` · `cal` · `clay` · `raycast` + +**金融科技** —— `stripe` · `coinbase` · `binance` · `kraken` · `mastercard` · `revolut` · `wise` + +**电商 / 出行** —— `shopify` · `airbnb` · `uber` · `nike` · `starbucks` · `pinterest` + +**媒体** —— `spotify` · `playstation` · `wired` · `theverge` · `meta` + +**汽车** —— `tesla` · `bmw` · `ferrari` · `lamborghini` · `bugatti` · `renault` + +**其他** —— `apple` · `ibm` · `nvidia` · `vodafone` · `sentry` · `resend` · `spacex` + +**起手** —— `default`(Neutral Modern)· `warm-editorial` + +
+ +整个库通过 [`scripts/sync-design-systems.mjs`](scripts/sync-design-systems.mjs) 从 [`VoltAgent/awesome-design-md`][acd2] 导入。重新执行即可刷新。 + +## 视觉方向 + +当用户没有品牌资产时,agent 会跳第二个表单,5 套精选方向 —— 这是 [`huashu-design` 的「设计方向顾问 · 5 流派 × 20 种设计哲学」 fallback](https://github.com/alchaincyf/huashu-design#%E8%AE%BE%E8%AE%A1%E6%96%B9%E5%90%91%E9%A1%BE%E9%97%AE-fallback) 在 OCD 里的落地。每一套都是确定性 spec —— OKLch 色板、字体栈、版式姿态、参考列表 —— agent 直接把它**原样**绑进 seed 模板的 `:root`。一个 radio 选完,整套视觉系统全部锁定。零 freestyle,零 AI slop。 + +| 方向 | 调性 | 参考 | +|---|---|---| +| Editorial — Monocle / FT | 印刷杂志,墨水 + 米色纸 + 暖红强调 | Monocle · FT Weekend · NYT Magazine | +| Modern minimal — Linear / Vercel | 冷调、结构化、克制强调 | Linear · Vercel · Stripe | +| Tech utility | 信息密度、等宽、终端感 | Bloomberg · Bauhaus 工具 | +| Brutalist | 粗粝、巨字、无阴影、刺眼强调 | Bloomberg Businessweek · Achtung | +| Soft warm | 大方、低对比、桃色中性 | Notion 营销页 · Apple Health | + +完整 spec → [`src/prompts/directions.ts`](src/prompts/directions.ts)。 + +## 反 AI Slop 机制 + +下面整套机制都是 [`huashu-design`](https://github.com/alchaincyf/huashu-design) 的 playbook,被移植进 OCD 的提示词栈,并通过 skill 副文件 pre-flight 让每个 skill 都能落地执行。看 [`src/prompts/discovery.ts`](src/prompts/discovery.ts) 是真实文案: + +- **先表单。** Turn 1 必须是 ``,**不准** thinking、不准 tools、不准旁白。用户用 radio 速度选默认。 +- **品牌资产协议。** 用户贴截图或 URL 时,agent 走 5 步流程(定位 · 下载 · grep hex · 写 `brand-spec.md` · 复述)才能开始写 CSS。**绝不从记忆里猜品牌色**。 +- **五维评审。** 在吐 `` 之前,agent 默默给自己 1–5 分打分,五个维度:哲学 / 层级 / 执行 / 具体度 / 克制。任一维 < 3/5 视为退步 —— 修完再评。两轮是常态。 +- **P0/P1/P2 checklist。** 每个 skill 都自带 `references/checklist.md`,含硬性 P0。Agent 必须 P0 全过才能 emit。 +- **Slop 黑名单。** 暴力紫渐变、通用 emoji 图标、左 border 圆角卡片、手绘 SVG 真人脸、Inter 当 *display* 字体、自编指标 —— 提示词里全部明令禁止。 +- **诚实占位 > 假数据。** Agent 没真数字时写 `—` 或一个标注的灰块,绝不写「快 10 倍」。 + +## 横向对比 + +| 维度 | [Claude Design][cd](Anthropic) | [Open CoDesign][ocod] | **Open Claude Design** | +|---|---|---|---| +| License | 闭源 | MIT | **Apache-2.0** | +| 形态 | Web (claude.ai) | 桌面 (Electron) | **Web 应用 + 本地 daemon** | +| 可部署 Vercel | ❌ | ❌ | **✅** | +| Agent 运行时 | 内置 (Opus 4.7) | 内置 ([`pi-ai`][piai]) | **委托给用户已装好的 CLI** | +| Skill | 私有 | 12 套自定义 TS 模块 + `SKILL.md` | **19 套基于文件的 [`SKILL.md`][skill],可丢入** | +| Design system | 私有 | `DESIGN.md`(v0.2 路线图) | **`DESIGN.md` × 71 套,开箱即有** | +| Provider 灵活度 | 仅 Anthropic | 7+([`pi-ai`][piai]) | **取决于你的 agent** | +| 初始化问题表单 | ❌ | ❌ | **✅ 硬规则 turn 1** | +| 方向选择器 | ❌ | ❌ | **✅ 5 套确定性方向** | +| 实时 todo 进度 + tool 流 | ❌ | ✅ | **✅**(UX 模式来自 open-codesign) | +| 沙盒 iframe 预览 | ❌ | ✅ | **✅**(模式来自 open-codesign) | +| 评论模式手术刀编辑 | ❌ | ✅ | 🚧 路线图(移植自 open-codesign) | +| AI 自吐 tweaks 面板 | ❌ | ✅ | 🚧 路线图(移植自 open-codesign) | +| 文件系统级工作区 | ❌ | 部分(Electron 沙盒) | **✅ 真 cwd、真工具、SQLite 持久化** | +| 五维自评审 | ❌ | ❌ | **✅ Emit 前必跑** | +| 导出格式 | 受限 | HTML / PDF / PPTX / ZIP / Markdown | **HTML / PDF / PPTX / ZIP / Markdown** | +| PPT skill 复用 | N/A | 内置 | **[`guizang-ppt-skill`][guizang] 直接接入** | +| 计费门槛 | Pro / Max / Team | BYOK | **BYOK** | + +[cd]: https://www.anthropic.com/news/claude-design +[ocod]: https://github.com/OpenCoworkAI/open-codesign +[piai]: https://github.com/mariozechner/pi-ai +[acd]: https://github.com/VoltAgent/awesome-claude-design +[guizang]: https://github.com/op7418/guizang-ppt-skill +[skill]: https://docs.anthropic.com/en/docs/claude-code/skills + +## 支持的 Coding Agent + +Daemon 启动时从 `PATH` 自动检测,无需配置。 + +| Agent | 二进制 | 流式 | 备注 | +|---|---|---|---| +| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude` | `--output-format stream-json`(类型化事件) | 一等公民,最佳保真度 | +| [Codex CLI](https://github.com/openai/codex) | `codex` | line-buffered | `codex exec ` | +| [Cursor Agent](https://www.cursor.com/cli) | `cursor-agent` | line-buffered | `cursor-agent -p` | +| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | line-buffered | `gemini -p` | +| [OpenCode](https://opencode.ai/) | `opencode` | line-buffered | `opencode run` | +| [Qwen Code](https://github.com/QwenLM/qwen-code) | `qwen` | line-buffered | `qwen -p` | +| Anthropic API · BYOK | n/a | SSE 直连 | 没装任何 CLI 时的浏览器兜底 | + +加一个新 CLI = 在 [`daemon/agents.js`](daemon/agents.js) 里加一项。流式格式从 `claude-stream-json`(类型化事件)和 `plain`(原始文本)两种里选一个。 + +## 引用与师承 + +每一个被借鉴的开源项目都列在这里。点链接可以验证师承。 + +| 项目 | 在这里的角色 | +|---|---| +| [`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 代码。 | +| [**`OpenCoworkAI/open-codesign`**][ocod] | 第一个开源的 Claude-Design 替代品,也是我们最接近的同类。已采纳的 UX 模式:流式 artifact 循环、沙盒 iframe 预览(自带 React 18 + Babel)、实时 agent 面板(todos + tool calls + 可中断)、5 种导出格式列表(HTML/PDF/PPTX/ZIP/Markdown)、本地优先的 designs hub、`SKILL.md` 品味注入。路线图上的 UX 模式:评论模式手术刀编辑、AI 自吐 tweaks 面板。**我们刻意不 vendor [`pi-ai`][piai]** —— open-codesign 把它打包成 agent 运行时;我们则委托给用户已经装好的 CLI。 | +| [`VoltAgent/awesome-claude-design`][acd] / [`awesome-design-md`][acd2] | 9 段式 `DESIGN.md` schema 的来源,69 套产品系统通过 [`scripts/sync-design-systems.mjs`](scripts/sync-design-systems.mjs) 导入。 | +| [`farion1231/cc-switch`](https://github.com/farion1231/cc-switch) | 跨多个 agent CLI 的 symlink 式 skill 分发灵感来源。 | +| [Claude Code skills][skill] | `SKILL.md` 规范原样采纳 —— 任何 Claude Code skill 丢进 `skills/` 都能被 daemon 识别。 | + +详尽的师承说明(每一项我们采纳了什么、刻意没采纳什么)在 [`docs/references.md`](docs/references.md)。 + +## Roadmap + +- [x] Daemon + agent 检测 + skill registry + design-system 目录 +- [x] Web 应用 + 对话 + question form + todo progress + 沙盒预览 +- [x] 19 个 skill + 71 套 design system + 5 套视觉方向 + 5 个设备外壳 +- [x] SQLite 后端的 projects · conversations · messages · tabs · templates +- [ ] 评论模式手术刀编辑(点元素 → 指令 → 局部 patch)—— 模式来自 [`open-codesign`][ocod] +- [ ] AI 自吐 tweaks 面板(模型自己抛出值得调的参数)—— 模式来自 [`open-codesign`][ocod] +- [ ] Vercel + 隧道部署食谱(Topology B) +- [ ] 一行 `npx ocd init` 脚手架带 `DESIGN.md` +- [ ] Skill 市场(`ocd skills install `) + +分阶段交付计划在 [`docs/roadmap.md`](docs/roadmap.md)。 + +## 项目状态 + +这是一个早期实现 —— 闭环(检测 → 选 skill + design system → 对话 → 解析 `` → 预览 → 保存)已经端到端跑通。提示词栈和 skill 库是价值最重的部分,目前已稳定。组件级 UI 仍在每天迭代。 + +欢迎 issue、PR、新 skill、新 design system。 + +## License + +Apache-2.0。内置的 [`skills/guizang-ppt/`](skills/guizang-ppt/) 保留它原始的 [LICENSE](skills/guizang-ppt/LICENSE)(MIT)和原作者 [op7418](https://github.com/op7418) 的归属。 diff --git a/assets/frames/README.md b/assets/frames/README.md new file mode 100644 index 0000000..84d87fd --- /dev/null +++ b/assets/frames/README.md @@ -0,0 +1,77 @@ +# Shared device frames + +Reusable, pixel-accurate device chrome that any skill can compose into a +multi-device or multi-screen layout. Each frame is a self-contained HTML +snippet that renders a device shell and embeds its inner screen via an +\` +\`\`\` + +In an OCD-managed project, the recommended pattern is: + +\`\`\` +my-project/ +├── index.html ← gallery view: composes 3+ frames in a row +├── screens/ +│ ├── home.html ← inner content rendered inside iphone-15-pro.html +│ ├── search.html +│ └── detail.html +└── (no copy of frames — point at the shared assets folder) +\`\`\` + +## Design tokens + +Each frame reads its inner screen's tokens via \`postMessage\` if you want +the bezel to tint with the active palette. The default state is "phone in +hand" — neutral metallic — which works against any background. + +## Authoring rules + +When extending this library: + +1. **No external assets.** Inline all SVG. No font imports. No image URLs. +2. **One frame per file.** Don't bundle iPhone + Android in one HTML. +3. **\`?screen=\` query is the only contract.** Don't introduce other + query params; the harness has to be predictable for skills to use. +4. **The frame is decorative chrome only.** All content lives in the inner + screen file. The frame must work with `?screen=about:blank` (showing + just the device shell). +5. **Match real device dimensions.** iPhone 15 Pro is 390×844 logical + pixels. iPad Pro 11" is 834×1194. Don't ship a "looks like" frame — + the seed has to match. diff --git a/assets/frames/android-pixel.html b/assets/frames/android-pixel.html new file mode 100644 index 0000000..85df84c --- /dev/null +++ b/assets/frames/android-pixel.html @@ -0,0 +1,158 @@ + + + + + + + Pixel 8 Pro frame + + + +
+ + +
+
+ 9:41 + + + + + + + + + +
+ + + + +
+
+ + + + diff --git a/assets/frames/browser-chrome.html b/assets/frames/browser-chrome.html new file mode 100644 index 0000000..6ad76ff --- /dev/null +++ b/assets/frames/browser-chrome.html @@ -0,0 +1,130 @@ + + + + + + + Browser frame + + + +
+
+ + + + + + + + + +
🔒example.com
+ + + +
+ + +
+ + + + diff --git a/assets/frames/ipad-pro.html b/assets/frames/ipad-pro.html new file mode 100644 index 0000000..256db5a --- /dev/null +++ b/assets/frames/ipad-pro.html @@ -0,0 +1,96 @@ + + + + + + + iPad Pro frame + + + +
+ +
+ +
+
+ + + diff --git a/assets/frames/iphone-15-pro.html b/assets/frames/iphone-15-pro.html new file mode 100644 index 0000000..60963ce --- /dev/null +++ b/assets/frames/iphone-15-pro.html @@ -0,0 +1,175 @@ + + + + + + + iPhone 15 Pro frame + + + +
+ + + + + + +
+
+ 9:41 + + + + + + + + + + + + + + + + +
+ + + +
+
+
+ + + + diff --git a/assets/frames/macbook.html b/assets/frames/macbook.html new file mode 100644 index 0000000..f94628f --- /dev/null +++ b/assets/frames/macbook.html @@ -0,0 +1,135 @@ + + + + + + + MacBook frame + + + +
+
+
+ +
+
+
+
+ + + + diff --git a/daemon/agents.js b/daemon/agents.js new file mode 100644 index 0000000..1b35b32 --- /dev/null +++ b/daemon/agents.js @@ -0,0 +1,113 @@ +import { execFile } from 'node:child_process'; +import { promisify } from 'node:util'; +import { existsSync } from 'node:fs'; +import { delimiter } from 'node:path'; +import path from 'node:path'; + +const execFileP = promisify(execFile); + +// Each entry defines how to invoke the agent in non-interactive "one-shot" mode. +// `buildArgs(prompt, imagePaths)` returns argv for the child process. +// `streamFormat` hints to the daemon how to interpret stdout: +// - 'claude-stream-json' : line-delimited JSON emitted by Claude Code's +// `--output-format stream-json`. Daemon parses it into typed events +// (text / thinking / tool_use / tool_result / status) for the UI. +// - 'plain' (default) : raw text, forwarded chunk-by-chunk. +export const AGENT_DEFS = [ + { + id: 'claude', + name: 'Claude Code', + bin: 'claude', + versionArgs: ['--version'], + buildArgs: (prompt) => [ + '-p', + prompt, + '--output-format', + 'stream-json', + '--verbose', + '--include-partial-messages', + ], + streamFormat: 'claude-stream-json', + }, + { + id: 'codex', + name: 'Codex CLI', + bin: 'codex', + versionArgs: ['--version'], + buildArgs: (prompt) => ['exec', prompt], + streamFormat: 'plain', + }, + { + id: 'gemini', + name: 'Gemini CLI', + bin: 'gemini', + versionArgs: ['--version'], + buildArgs: (prompt) => ['-p', prompt], + streamFormat: 'plain', + }, + { + id: 'opencode', + name: 'OpenCode', + bin: 'opencode', + versionArgs: ['--version'], + buildArgs: (prompt) => ['run', prompt], + streamFormat: 'plain', + }, + { + id: 'cursor-agent', + name: 'Cursor Agent', + bin: 'cursor-agent', + versionArgs: ['--version'], + buildArgs: (prompt) => ['-p', prompt], + streamFormat: 'plain', + }, + { + id: 'qwen', + name: 'Qwen Code', + bin: 'qwen', + versionArgs: ['--version'], + buildArgs: (prompt) => ['-p', prompt], + streamFormat: 'plain', + }, +]; + +function resolveOnPath(bin) { + const exts = + process.platform === 'win32' + ? (process.env.PATHEXT || '.EXE;.CMD;.BAT').split(';') + : ['']; + const dirs = (process.env.PATH || '').split(delimiter); + for (const dir of dirs) { + for (const ext of exts) { + const full = path.join(dir, bin + ext); + if (full && existsSync(full)) return full; + } + } + return null; +} + +async function probe(def) { + const resolved = resolveOnPath(def.bin); + if (!resolved) return { ...stripFns(def), available: false }; + let version = null; + try { + const { stdout } = await execFileP(resolved, def.versionArgs, { timeout: 3000 }); + version = stdout.trim().split('\n')[0]; + } catch { + // binary exists but --version failed; still mark available + } + return { ...stripFns(def), available: true, path: resolved, version }; +} + +function stripFns(def) { + const { buildArgs, ...rest } = def; + return rest; +} + +export async function detectAgents() { + return Promise.all(AGENT_DEFS.map(probe)); +} + +export function getAgentDef(id) { + return AGENT_DEFS.find((a) => a.id === id) || null; +} diff --git a/daemon/claude-stream.js b/daemon/claude-stream.js new file mode 100644 index 0000000..0a324a0 --- /dev/null +++ b/daemon/claude-stream.js @@ -0,0 +1,188 @@ +/** + * Parses Claude Code's `--output-format stream-json --verbose + * --include-partial-messages` JSONL stream into a small set of UI-friendly + * events. The Claude stream is rich (message_start, content_block_start/stop, + * deltas, tool_use, tool_result, status, result) but the UI only needs to + * know five things: + * + * - status : high-level lifecycle ("initializing", "requesting", + * "thinking") + * - text_delta : assistant text chunk (gets fed to the artifact parser) + * - thinking_delta: extended-thinking chunk (shown in a collapsed block) + * - tool_use : { id, name, input } (fires when input is complete) + * - tool_result : { tool_use_id, content, is_error } + * - usage : aggregated input/output/cache tokens + cost + * + * Callers give us `onEvent({ type, ...payload })`. We track per-content-block + * state to accumulate partial tool_use input JSON and emit a single + * `tool_use` event when that block stops. + */ + +export function createClaudeStreamHandler(onEvent) { + let buffer = ''; + + // Per-content-block scratch, keyed by `${messageId}:${blockIndex}`. + const blocks = new Map(); + // Most recent assistant message id so content_block_* events without an id + // can be attributed correctly. + let currentMessageId = null; + + function blockKey(index) { + return `${currentMessageId ?? 'anon'}:${index}`; + } + + function feed(chunk) { + buffer += chunk; + let nl; + while ((nl = buffer.indexOf('\n')) !== -1) { + const line = buffer.slice(0, nl).trim(); + buffer = buffer.slice(nl + 1); + if (!line) continue; + let obj; + try { + obj = JSON.parse(line); + } catch { + onEvent({ type: 'raw', line }); + continue; + } + handleObject(obj); + } + } + + function flush() { + const rem = buffer.trim(); + buffer = ''; + if (!rem) return; + try { + handleObject(JSON.parse(rem)); + } catch { + onEvent({ type: 'raw', line: rem }); + } + } + + function handleObject(obj) { + if (!obj || typeof obj !== 'object') return; + + if (obj.type === 'system' && obj.subtype === 'init') { + onEvent({ + type: 'status', + label: 'initializing', + model: obj.model ?? null, + sessionId: obj.session_id ?? null, + }); + return; + } + + if (obj.type === 'system' && obj.subtype === 'status') { + onEvent({ type: 'status', label: obj.status ?? 'working' }); + return; + } + + if (obj.type === 'stream_event' && obj.event) { + handleStreamEvent(obj.event); + return; + } + + // `assistant` messages are the "block finished" signal for the current + // content block. For tool_use blocks whose input finished assembling, + // emit tool_use now with the final parsed input. + if (obj.type === 'assistant' && obj.message?.content) { + currentMessageId = obj.message.id ?? currentMessageId; + for (const block of obj.message.content) { + if (block.type === 'tool_use') { + onEvent({ + type: 'tool_use', + id: block.id, + name: block.name, + input: block.input ?? null, + }); + } + } + return; + } + + // `user` messages in a stream-json transcript are usually tool_result + // wrappers from prior turns. + if (obj.type === 'user' && obj.message?.content) { + for (const block of obj.message.content) { + if (block.type === 'tool_result') { + onEvent({ + type: 'tool_result', + toolUseId: block.tool_use_id, + content: stringifyToolResult(block.content), + isError: Boolean(block.is_error), + }); + } + } + return; + } + + if (obj.type === 'result') { + onEvent({ + type: 'usage', + usage: obj.usage ?? null, + costUsd: obj.total_cost_usd ?? null, + durationMs: obj.duration_ms ?? null, + stopReason: obj.stop_reason ?? null, + }); + return; + } + } + + function handleStreamEvent(ev) { + if (ev.type === 'message_start') { + currentMessageId = ev.message?.id ?? null; + if (typeof ev.ttft_ms === 'number') { + onEvent({ type: 'status', label: 'streaming', ttftMs: ev.ttft_ms }); + } + return; + } + + if (ev.type === 'content_block_start' && ev.content_block) { + const key = blockKey(ev.index); + const block = ev.content_block; + blocks.set(key, { type: block.type, name: block.name, id: block.id, input: '' }); + if (block.type === 'thinking') { + onEvent({ type: 'thinking_start' }); + } + return; + } + + if (ev.type === 'content_block_delta' && ev.delta) { + const state = blocks.get(blockKey(ev.index)); + const delta = ev.delta; + + if (delta.type === 'text_delta' && typeof delta.text === 'string') { + onEvent({ type: 'text_delta', delta: delta.text }); + return; + } + if (delta.type === 'thinking_delta' && typeof delta.thinking === 'string') { + onEvent({ type: 'thinking_delta', delta: delta.thinking }); + return; + } + if (delta.type === 'input_json_delta' && typeof delta.partial_json === 'string') { + if (state && state.type === 'tool_use') { + state.input += delta.partial_json; + } + return; + } + } + + if (ev.type === 'content_block_stop') { + blocks.delete(blockKey(ev.index)); + return; + } + } + + return { feed, flush }; +} + +function stringifyToolResult(content) { + if (typeof content === 'string') return content; + if (Array.isArray(content)) { + return content + .map((c) => (c?.type === 'text' ? c.text : JSON.stringify(c))) + .join('\n'); + } + return JSON.stringify(content); +} diff --git a/daemon/cli.js b/daemon/cli.js new file mode 100644 index 0000000..dd352ec --- /dev/null +++ b/daemon/cli.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +import { startServer } from './server.js'; + +const args = process.argv.slice(2); +let port = Number(process.env.OCD_PORT) || 7456; +let open = true; + +for (let i = 0; i < args.length; i++) { + const a = args[i]; + if (a === '-p' || a === '--port') { + port = Number(args[++i]); + } else if (a === '--no-open') { + open = false; + } else if (a === '-h' || a === '--help') { + console.log(`Usage: ocd [--port ] [--no-open] + +Starts a local daemon that: + * scans PATH for installed code-agent CLIs (claude, codex, gemini, opencode, cursor-agent, ...) + * serves a tiny web chat UI at http://localhost: + * proxies messages (text + images) to the selected agent via child-process spawn +`); + process.exit(0); + } +} + +startServer({ port }).then(url => { + console.log(`[ocd] listening on ${url}`); + if (open) { + const opener = process.platform === 'darwin' ? 'open' + : process.platform === 'win32' ? 'start' + : 'xdg-open'; + import('node:child_process').then(({ spawn }) => { + spawn(opener, [url], { detached: true, stdio: 'ignore' }).unref(); + }); + } +}); diff --git a/daemon/db.js b/daemon/db.js new file mode 100644 index 0000000..2af1552 --- /dev/null +++ b/daemon/db.js @@ -0,0 +1,465 @@ +// SQLite-backed persistence for projects, conversations, messages, and the +// per-project set of open file tabs. The on-disk project folder under +// .ocd/projects// is still the single owner of the user's actual files +// (HTML artifacts, sketches, uploads); this database tracks the metadata +// that used to live in localStorage. + +import Database from 'better-sqlite3'; +import path from 'node:path'; +import fs from 'node:fs'; + +let dbInstance = null; + +export function openDatabase(projectRoot) { + if (dbInstance) return dbInstance; + const dir = path.join(projectRoot, '.ocd'); + fs.mkdirSync(dir, { recursive: true }); + const file = path.join(dir, 'app.sqlite'); + const db = new Database(file); + db.pragma('journal_mode = WAL'); + db.pragma('foreign_keys = ON'); + migrate(db); + dbInstance = db; + return db; +} + +function migrate(db) { + db.exec(` + CREATE TABLE IF NOT EXISTS projects ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + skill_id TEXT, + design_system_id TEXT, + pending_prompt TEXT, + metadata_json TEXT, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ); + + CREATE TABLE IF NOT EXISTS templates ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + source_project_id TEXT, + files_json TEXT NOT NULL, + created_at INTEGER NOT NULL + ); + + CREATE TABLE IF NOT EXISTS conversations ( + id TEXT PRIMARY KEY, + project_id TEXT NOT NULL, + title TEXT, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ); + + CREATE INDEX IF NOT EXISTS idx_conv_project + ON conversations(project_id, updated_at DESC); + + CREATE TABLE IF NOT EXISTS messages ( + id TEXT PRIMARY KEY, + conversation_id TEXT NOT NULL, + role TEXT NOT NULL, + content TEXT NOT NULL, + events_json TEXT, + attachments_json TEXT, + produced_files_json TEXT, + started_at INTEGER, + ended_at INTEGER, + position INTEGER NOT NULL, + created_at INTEGER NOT NULL, + FOREIGN KEY(conversation_id) REFERENCES conversations(id) ON DELETE CASCADE + ); + + CREATE INDEX IF NOT EXISTS idx_messages_conv + ON messages(conversation_id, position); + + CREATE TABLE IF NOT EXISTS tabs ( + project_id TEXT NOT NULL, + name TEXT NOT NULL, + position INTEGER NOT NULL, + is_active INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY(project_id, name), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ); + + CREATE INDEX IF NOT EXISTS idx_tabs_project + ON tabs(project_id, position); + `); + // Forward-compatible column add for databases created before metadata_json. + // SQLite has no IF NOT EXISTS for ALTER, so we check pragma_table_info. + const cols = db.prepare(`PRAGMA table_info(projects)`).all(); + if (!cols.some((c) => c.name === 'metadata_json')) { + db.exec(`ALTER TABLE projects ADD COLUMN metadata_json TEXT`); + } +} + +// ---------- projects ---------- + +const PROJECT_COLS = `id, name, skill_id AS skillId, + design_system_id AS designSystemId, + pending_prompt AS pendingPrompt, + metadata_json AS metadataJson, + created_at AS createdAt, + updated_at AS updatedAt`; + +export function listProjects(db) { + const rows = db + .prepare( + `SELECT ${PROJECT_COLS} + FROM projects + ORDER BY updated_at DESC`, + ) + .all(); + return rows.map(normalizeProject); +} + +export function getProject(db, id) { + const row = db + .prepare(`SELECT ${PROJECT_COLS} FROM projects WHERE id = ?`) + .get(id); + return row ? normalizeProject(row) : null; +} + +export function insertProject(db, p) { + db.prepare( + `INSERT INTO projects + (id, name, skill_id, design_system_id, pending_prompt, + metadata_json, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + ).run( + p.id, + p.name, + p.skillId ?? null, + p.designSystemId ?? null, + p.pendingPrompt ?? null, + p.metadata ? JSON.stringify(p.metadata) : null, + p.createdAt, + p.updatedAt, + ); + return getProject(db, p.id); +} + +export function updateProject(db, id, patch) { + const existing = getProject(db, id); + if (!existing) return null; + const merged = { + ...existing, + ...patch, + updatedAt: typeof patch.updatedAt === 'number' ? patch.updatedAt : Date.now(), + }; + db.prepare( + `UPDATE projects + SET name = ?, + skill_id = ?, + design_system_id = ?, + pending_prompt = ?, + metadata_json = ?, + updated_at = ? + WHERE id = ?`, + ).run( + merged.name, + merged.skillId ?? null, + merged.designSystemId ?? null, + merged.pendingPrompt ?? null, + merged.metadata ? JSON.stringify(merged.metadata) : null, + merged.updatedAt, + id, + ); + return getProject(db, id); +} + +export function deleteProject(db, id) { + db.prepare(`DELETE FROM projects WHERE id = ?`).run(id); +} + +function normalizeProject(row) { + let metadata; + if (row.metadataJson) { + try { + metadata = JSON.parse(row.metadataJson); + } catch { + metadata = undefined; + } + } + return { + id: row.id, + name: row.name, + skillId: row.skillId, + designSystemId: row.designSystemId, + pendingPrompt: row.pendingPrompt ?? undefined, + metadata, + createdAt: Number(row.createdAt), + updatedAt: Number(row.updatedAt), + }; +} + +// ---------- templates ---------- + +export function listTemplates(db) { + return db + .prepare( + `SELECT id, name, description, source_project_id AS sourceProjectId, + files_json AS filesJson, created_at AS createdAt + FROM templates + ORDER BY created_at DESC`, + ) + .all() + .map(normalizeTemplate); +} + +export function getTemplate(db, id) { + const row = db + .prepare( + `SELECT id, name, description, source_project_id AS sourceProjectId, + files_json AS filesJson, created_at AS createdAt + FROM templates WHERE id = ?`, + ) + .get(id); + return row ? normalizeTemplate(row) : null; +} + +export function insertTemplate(db, t) { + db.prepare( + `INSERT INTO templates (id, name, description, source_project_id, files_json, created_at) + VALUES (?, ?, ?, ?, ?, ?)`, + ).run( + t.id, + t.name, + t.description ?? null, + t.sourceProjectId ?? null, + JSON.stringify(t.files ?? []), + t.createdAt, + ); + return getTemplate(db, t.id); +} + +export function deleteTemplate(db, id) { + db.prepare(`DELETE FROM templates WHERE id = ?`).run(id); +} + +function normalizeTemplate(row) { + let files = []; + try { + files = JSON.parse(row.filesJson || '[]'); + } catch { + files = []; + } + return { + id: row.id, + name: row.name, + description: row.description ?? undefined, + sourceProjectId: row.sourceProjectId ?? undefined, + files, + createdAt: Number(row.createdAt), + }; +} + +// ---------- conversations ---------- + +export function listConversations(db, projectId) { + return db + .prepare( + `SELECT id, project_id AS projectId, title, + created_at AS createdAt, updated_at AS updatedAt + FROM conversations + WHERE project_id = ? + ORDER BY updated_at DESC`, + ) + .all(projectId) + .map((r) => ({ + id: r.id, + projectId: r.projectId, + title: r.title ?? null, + createdAt: Number(r.createdAt), + updatedAt: Number(r.updatedAt), + })); +} + +export function getConversation(db, id) { + const r = db + .prepare( + `SELECT id, project_id AS projectId, title, + created_at AS createdAt, updated_at AS updatedAt + FROM conversations WHERE id = ?`, + ) + .get(id); + if (!r) return null; + return { + id: r.id, + projectId: r.projectId, + title: r.title ?? null, + createdAt: Number(r.createdAt), + updatedAt: Number(r.updatedAt), + }; +} + +export function insertConversation(db, c) { + db.prepare( + `INSERT INTO conversations + (id, project_id, title, created_at, updated_at) + VALUES (?, ?, ?, ?, ?)`, + ).run(c.id, c.projectId, c.title ?? null, c.createdAt, c.updatedAt); + return getConversation(db, c.id); +} + +export function updateConversation(db, id, patch) { + const existing = getConversation(db, id); + if (!existing) return null; + const merged = { + ...existing, + ...patch, + updatedAt: typeof patch.updatedAt === 'number' ? patch.updatedAt : Date.now(), + }; + db.prepare( + `UPDATE conversations + SET title = ?, updated_at = ? WHERE id = ?`, + ).run(merged.title ?? null, merged.updatedAt, id); + return getConversation(db, id); +} + +export function deleteConversation(db, id) { + db.prepare(`DELETE FROM conversations WHERE id = ?`).run(id); +} + +// ---------- messages ---------- + +export function listMessages(db, conversationId) { + return db + .prepare( + `SELECT id, role, content, events_json AS eventsJson, + attachments_json AS attachmentsJson, + produced_files_json AS producedFilesJson, + started_at AS startedAt, ended_at AS endedAt, + position + FROM messages + WHERE conversation_id = ? + ORDER BY position ASC`, + ) + .all(conversationId) + .map(normalizeMessage); +} + +export function upsertMessage(db, conversationId, m) { + const existing = db + .prepare(`SELECT position FROM messages WHERE id = ?`) + .get(m.id); + const now = Date.now(); + if (existing) { + db.prepare( + `UPDATE messages + SET role = ?, content = ?, events_json = ?, attachments_json = ?, + produced_files_json = ?, started_at = ?, ended_at = ? + WHERE id = ?`, + ).run( + m.role, + m.content, + m.events ? JSON.stringify(m.events) : null, + m.attachments ? JSON.stringify(m.attachments) : null, + m.producedFiles ? JSON.stringify(m.producedFiles) : null, + m.startedAt ?? null, + m.endedAt ?? null, + m.id, + ); + } else { + const max = db + .prepare( + `SELECT COALESCE(MAX(position), -1) AS m FROM messages WHERE conversation_id = ?`, + ) + .get(conversationId); + const position = (max?.m ?? -1) + 1; + db.prepare( + `INSERT INTO messages + (id, conversation_id, role, content, events_json, + attachments_json, produced_files_json, + started_at, ended_at, position, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ).run( + m.id, + conversationId, + m.role, + m.content, + m.events ? JSON.stringify(m.events) : null, + m.attachments ? JSON.stringify(m.attachments) : null, + m.producedFiles ? JSON.stringify(m.producedFiles) : null, + m.startedAt ?? null, + m.endedAt ?? null, + position, + now, + ); + } + // Bump conversation activity so the sidebar's recency sort works. + db.prepare(`UPDATE conversations SET updated_at = ? WHERE id = ?`).run( + now, + conversationId, + ); + const row = db + .prepare( + `SELECT id, role, content, events_json AS eventsJson, + attachments_json AS attachmentsJson, + produced_files_json AS producedFilesJson, + started_at AS startedAt, ended_at AS endedAt, + position + FROM messages WHERE id = ?`, + ) + .get(m.id); + return row ? normalizeMessage(row) : null; +} + +export function deleteMessage(db, id) { + db.prepare(`DELETE FROM messages WHERE id = ?`).run(id); +} + +function normalizeMessage(row) { + return { + id: row.id, + role: row.role, + content: row.content, + events: parseJsonOrUndef(row.eventsJson), + attachments: parseJsonOrUndef(row.attachmentsJson), + producedFiles: parseJsonOrUndef(row.producedFilesJson), + startedAt: row.startedAt ?? undefined, + endedAt: row.endedAt ?? undefined, + }; +} + +function parseJsonOrUndef(s) { + if (!s) return undefined; + try { + return JSON.parse(s); + } catch { + return undefined; + } +} + +// ---------- tabs ---------- + +export function listTabs(db, projectId) { + const rows = db + .prepare( + `SELECT name, position, is_active AS isActive + FROM tabs WHERE project_id = ? ORDER BY position ASC`, + ) + .all(projectId); + const active = rows.find((r) => r.isActive) ?? null; + return { + tabs: rows.map((r) => r.name), + active: active ? active.name : null, + }; +} + +export function setTabs(db, projectId, names, activeName) { + const tx = db.transaction(() => { + db.prepare(`DELETE FROM tabs WHERE project_id = ?`).run(projectId); + const ins = db.prepare( + `INSERT INTO tabs (project_id, name, position, is_active) + VALUES (?, ?, ?, ?)`, + ); + names.forEach((name, i) => { + ins.run(projectId, name, i, name === activeName ? 1 : 0); + }); + }); + tx(); + return listTabs(db, projectId); +} diff --git a/daemon/design-system-preview.js b/daemon/design-system-preview.js new file mode 100644 index 0000000..e26676f --- /dev/null +++ b/daemon/design-system-preview.js @@ -0,0 +1,619 @@ +/** + * Build a showcase HTML page from a DESIGN.md so the user can see what each + * design system looks like *before* generating anything. We don't try to + * render a unique product mockup — we extract the palette, typography, and + * a couple of component conventions, then drop them into one fixed + * template. The full DESIGN.md is rendered below as prose for reference. + * + * Parsing is deliberately permissive: imported systems vary in section + * naming and bullet style, so we use loose regexes and fall back to sane + * defaults when a token isn't found. + */ + +export function renderDesignSystemPreview(id, raw) { + const titleMatch = /^#\s+(.+?)\s*$/m.exec(raw); + const title = cleanTitle(titleMatch?.[1] ?? id); + const subtitle = extractSubtitle(raw); + const colors = extractColors(raw); + const fonts = extractFonts(raw); + + const bg = + pickColor(colors, ['page background', 'background', 'canvas', 'paper', 'bg ', 'page bg']) + ?? pickColor(colors, ['white']) + ?? '#ffffff'; + const fg = + pickColor(colors, ['heading', 'foreground', 'ink', 'fg', 'text', 'navy', 'graphite']) + ?? '#111111'; + // Accent: brand/primary names first, then fall back to the first color + // that doesn't look like a neutral white/black/grey so we always show + // something punchy in the showcase header. + const accent = + pickColor(colors, ['primary brand', 'brand primary', 'primary', 'brand', 'accent']) + ?? firstNonNeutral(colors) + ?? '#2f6feb'; + const muted = pickColor(colors, ['muted', 'secondary', 'neutral', 'subtle', 'caption']) ?? '#777777'; + const border = pickColor(colors, ['border', 'divider', 'rule', 'stroke']) ?? '#e5e5e5'; + const surface = + pickColor(colors, ['surface', 'card', 'background-secondary', 'panel', 'elevated']) + ?? '#ffffff'; + + const display = fonts.display + ?? fonts.heading + ?? "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"; + const body = fonts.body ?? display; + const mono = fonts.mono ?? "ui-monospace, 'JetBrains Mono', monospace"; + + const renderedMarkdown = renderMarkdownLite(raw); + + return ` + + + + + ${escapeHtml(title)} — design system preview + + + +
+ Design system preview · ${escapeHtml(id)} +

${escapeHtml(title)}

+ ${subtitle ? `

${escapeHtml(subtitle)}

` : ''} + +
+

Palette

+
+ ${colors + .slice(0, 12) + .map( + (c) => `
+
+
+ ${escapeHtml(c.name)} + ${escapeHtml(c.value)} +
+
`, + ) + .join('')} +
+
+ +
+

Typography

+
+ Display +
The grid carries weight; the line carries pace.
+
+
+ Body +
Body copy reads at sixteen pixels with a 1.55 leading. Restraint and rhythm matter more than novelty — pick a stack that earns the page.
+
+
+ Mono +
/* monospace · ${escapeHtml(mono.split(',')[0]?.replace(/['"]/g, '').trim() ?? 'mono')} */
+
+
+ +
+

Components

+
+
+
Card
+

Production-quality artifact

+

Sample card showing how surfaces, borders, and accent text behave in this system.

+
+
+
Buttons
+

Three weights, one accent

+
+ + + +
+
+
+
+ +
+ ${renderedMarkdown} +
+
+ +`; +} + +function extractSubtitle(raw) { + const lines = raw.split(/\r?\n/); + const h1 = lines.findIndex((l) => /^#\s+/.test(l)); + if (h1 === -1) return ''; + const after = lines.slice(h1 + 1); + const nextHeading = after.findIndex((l) => /^#{1,6}\s+/.test(l)); + const window = (nextHeading === -1 ? after : after.slice(0, nextHeading)) + .join('\n') + .replace(/^>\s*Category:.*$/gim, '') + .replace(/^>\s*/gm, '') + .trim(); + return window.split(/\n\n/)[0]?.slice(0, 240) ?? ''; +} + +function extractColors(raw) { + const colors = []; + const seen = new Set(); + + function push(name, value) { + const cleanName = name.replace(/[*_`]+/g, '').replace(/\s+/g, ' ').trim(); + if (!cleanName || cleanName.length > 60) return; + const v = normalizeHex(value); + const key = `${cleanName.toLowerCase()}|${v}`; + if (seen.has(key)) return; + seen.add(key); + colors.push({ name: cleanName, value: v }); + } + + // Form A: "- **Background:** `#FAFAFA`" / "- Background: #FAFAFA" + const reA = /^[\s>*-]*\**\s*([A-Za-z][A-Za-z0-9 /&()+_-]{1,40}?)\s*\**\s*[::]\s*`?(#[0-9a-fA-F]{3,8})/gm; + let m; + while ((m = reA.exec(raw)) !== null) push(m[1], m[2]); + + // Form B: "**Stripe Purple** (`#533afd`)" — common in awesome-design-md. + // Token name is whatever's bolded; the hex follows in parens/backticks. + const reB = /\*\*([A-Za-z][A-Za-z0-9 /&()+_-]{1,40}?)\*\*\s*\(?\s*`?(#[0-9a-fA-F]{3,8})/g; + while ((m = reB.exec(raw)) !== null) push(m[1], m[2]); + + return colors; +} + +function extractFonts(raw) { + const out = {}; + // "- **Display / headings:** `'GT Sectra', ...`" + // We want the backticked stack OR the rest of the line. + const re = /^[\s>*-]*\**\s*([A-Za-z][A-Za-z /]{1,30}?)\s*\**\s*[::]\s*`?([^`\n]+?)`?$/gm; + let m; + while ((m = re.exec(raw)) !== null) { + const label = m[1].toLowerCase(); + const value = m[2].trim().replace(/[*_`]+$/g, '').trim(); + if (!/[a-zA-Z]/.test(value)) continue; + if (value.startsWith('#')) continue; + if (/display|heading|h1|title/.test(label) && !out.display) out.display = value; + else if (/body|text|paragraph|copy/.test(label) && !out.body) out.body = value; + else if (/mono|code/.test(label) && !out.mono) out.mono = value; + } + return out; +} + +function pickColor(colors, hints) { + for (const hint of hints) { + const needle = hint.toLowerCase(); + const found = colors.find((c) => c.name.toLowerCase().includes(needle)); + if (found) return found.value; + } + return null; +} + +function firstNonNeutral(colors) { + for (const c of colors) { + const v = c.value.replace('#', '').toLowerCase(); + if (v.length !== 6) continue; + const r = parseInt(v.slice(0, 2), 16); + const g = parseInt(v.slice(2, 4), 16); + const b = parseInt(v.slice(4, 6), 16); + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const sat = max === 0 ? 0 : (max - min) / max; + if (sat > 0.25) return c.value; + } + return null; +} + +function pickReadableForeground(hex) { + const n = normalizeHex(hex); + if (n.length !== 7) return '#ffffff'; + const r = parseInt(n.slice(1, 3), 16); + const g = parseInt(n.slice(3, 5), 16); + const b = parseInt(n.slice(5, 7), 16); + // Standard luminance check. + const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + return lum > 0.6 ? '#0a0a0a' : '#ffffff'; +} + +function normalizeHex(hex) { + let h = hex.toLowerCase(); + if (h.length === 4) { + h = '#' + h.slice(1).split('').map((c) => c + c).join(''); + } + return h; +} + +function cleanTitle(raw) { + return String(raw).replace(/^Design System (Inspired by|for)\s+/i, '').trim(); +} + +function escapeHtml(s) { + return String(s).replace(/[&<>"']/g, (c) => + c === '&' ? '&' : c === '<' ? '<' : c === '>' ? '>' : c === '"' ? '"' : ''', + ); +} + +// Tiny markdown renderer — enough for our DESIGN.md prose: H1–H4, paragraphs, +// bullet/ordered lists, blockquotes, fenced code, GFM pipe tables, horizontal +// rules, inline `code` / **bold** / *italic* / [link](url). Not a full markdown +// implementation but covers everything the DESIGN.md files actually use. +function renderMarkdownLite(src) { + const lines = src.split(/\r?\n/); + const out = []; + let inList = null; + let inBlockquote = false; + let inCode = false; + let i = 0; + + function closeList() { + if (inList) { + out.push(``); + inList = null; + } + } + function closeBlockquote() { + if (inBlockquote) { + out.push(''); + inBlockquote = false; + } + } + + while (i < lines.length) { + const raw = lines[i] ?? ''; + const line = raw.trimEnd(); + + if (line.startsWith('```')) { + closeList(); + closeBlockquote(); + if (!inCode) { + out.push('
');
+        inCode = true;
+      } else {
+        out.push('
'); + inCode = false; + } + i++; + continue; + } + if (inCode) { + out.push(escapeHtml(raw)); + i++; + continue; + } + if (!line.trim()) { + closeList(); + closeBlockquote(); + i++; + continue; + } + + // GFM pipe table — at least a header row, a separator row of dashes, + // and one body row. Look ahead from `i` so we can consume the whole + // block in one step. + if (looksLikeTableHeader(line) && i + 1 < lines.length && isTableSeparator(lines[i + 1] ?? '')) { + closeList(); + closeBlockquote(); + const headerCells = splitTableRow(line); + const aligns = parseAlignments(lines[i + 1] ?? '', headerCells.length); + const bodyRows = []; + let j = i + 2; + while (j < lines.length) { + const next = (lines[j] ?? '').trimEnd(); + if (!next.trim() || !next.includes('|')) break; + bodyRows.push(splitTableRow(next)); + j++; + } + out.push(renderTable(headerCells, bodyRows, aligns)); + i = j; + continue; + } + + // ATX headings #..#### + const h = /^(#{1,4})\s+(.+)$/.exec(line); + if (h) { + closeList(); + closeBlockquote(); + const level = h[1].length; + out.push(`${inline(h[2])}`); + i++; + continue; + } + + // Horizontal rule. + if (/^([-*_])\1{2,}\s*$/.test(line)) { + closeList(); + closeBlockquote(); + out.push('
'); + i++; + continue; + } + + const bq = /^>\s?(.*)$/.exec(line); + if (bq) { + closeList(); + if (!inBlockquote) { + out.push('
'); + inBlockquote = true; + } + out.push(`

${inline(bq[1] || '')}

`); + i++; + continue; + } + + closeBlockquote(); + const li = /^([-*])\s+(.+)$/.exec(line); + if (li) { + if (inList !== 'ul') { + closeList(); + out.push('
    '); + inList = 'ul'; + } + out.push(`
  • ${inline(li[2])}
  • `); + i++; + continue; + } + const oli = /^\d+\.\s+(.+)$/.exec(line); + if (oli) { + if (inList !== 'ol') { + closeList(); + out.push('
      '); + inList = 'ol'; + } + out.push(`
    1. ${inline(oli[1])}
    2. `); + i++; + continue; + } + closeList(); + out.push(`

      ${inline(line)}

      `); + i++; + } + closeList(); + closeBlockquote(); + if (inCode) out.push(''); + return out.join('\n'); +} + +function looksLikeTableHeader(line) { + const trimmed = line.trim(); + if (!trimmed.includes('|')) return false; + // At least one pipe between non-pipe content. + return /\|/.test(trimmed.replace(/^\||\|$/g, '')); +} + +function isTableSeparator(line) { + const trimmed = line.trim(); + if (!trimmed.includes('|')) return false; + // Each cell must be only dashes / colons / whitespace. + return splitTableRow(trimmed).every((cell) => /^:?-{1,}:?$/.test(cell.trim())); +} + +function splitTableRow(line) { + let s = line.trim(); + if (s.startsWith('|')) s = s.slice(1); + if (s.endsWith('|')) s = s.slice(0, -1); + return s.split('|').map((c) => c.trim()); +} + +function parseAlignments(separatorLine, count) { + const cells = splitTableRow(separatorLine); + const aligns = []; + for (let k = 0; k < count; k++) { + const cell = (cells[k] ?? '').trim(); + const left = cell.startsWith(':'); + const right = cell.endsWith(':'); + if (left && right) aligns.push('center'); + else if (right) aligns.push('right'); + else aligns.push(null); + } + return aligns; +} + +function renderTable(header, rows, aligns) { + const th = header + .map((cell, k) => { + const align = aligns[k]; + const attr = align ? ` align="${align}"` : ''; + return `${inline(cell)}`; + }) + .join(''); + const body = rows + .map((row) => { + const tds = row + .map((cell, k) => { + const align = aligns[k]; + const attr = align ? ` align="${align}"` : ''; + return `${inline(cell)}`; + }) + .join(''); + return `${tds}`; + }) + .join(''); + return `
      ${th}${body}
      `; +} + +function inline(s) { + // Process inline tokens. Order matters: code spans first so their content + // isn't further parsed; then bold/italic; then links; finally bare URLs. + const escaped = escapeHtml(s); + return escaped + .replace(/`([^`]+)`/g, '$1') + .replace(/\*\*([^*]+)\*\*/g, '$1') + .replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, '$1$2') + .replace(/(^|[\s(])_([^_\n]+)_(?=[\s).,;:!?]|$)/g, '$1$2') + .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '$1'); +} diff --git a/daemon/design-system-showcase.js b/daemon/design-system-showcase.js new file mode 100644 index 0000000..f72c6cb --- /dev/null +++ b/daemon/design-system-showcase.js @@ -0,0 +1,722 @@ +/** + * Build a fully-formed product webpage that demonstrates a design system in + * action — not just a list of tokens, but a real-feeling marketing / + * product page (nav, hero, social proof, feature grid, dashboard preview, + * pricing, testimonials, FAQ, CTA, footer) styled entirely from the + * tokens we extract from the system's DESIGN.md. + * + * Same parsing utilities as design-system-preview.js — kept inline rather + * than imported so the two views can evolve independently. + */ + +export function renderDesignSystemShowcase(id, raw) { + const titleMatch = /^#\s+(.+?)\s*$/m.exec(raw); + const rawTitle = titleMatch?.[1] ?? id; + const title = cleanTitle(rawTitle); + const subtitle = extractSubtitle(raw) || 'A design system rendered as a real product surface.'; + const colors = extractColors(raw); + const fonts = extractFonts(raw); + + const bg = + pickColor(colors, ['page background', 'background', 'canvas', 'paper', 'bg ', 'page bg']) + ?? '#ffffff'; + const fg = + pickColor(colors, ['heading', 'foreground', 'ink', 'fg', 'text', 'navy', 'graphite']) + ?? '#0a0a0a'; + const accent = + pickColor(colors, ['primary brand', 'brand primary', 'primary', 'brand', 'accent']) + ?? firstNonNeutral(colors) + ?? '#2f6feb'; + const accent2 = + pickColor(colors, ['secondary', 'tertiary', 'highlight', 'support']) + ?? secondNonNeutral(colors, accent) + ?? accent; + const muted = pickColor(colors, ['muted', 'subtle', 'caption', 'meta', 'neutral']) ?? '#666666'; + const border = pickColor(colors, ['border', 'divider', 'rule', 'stroke']) ?? '#e6e6e6'; + const surface = + pickColor(colors, ['surface', 'card', 'background-secondary', 'panel', 'elevated']) + ?? mixSurface(bg); + + const display = fonts.display ?? fonts.heading ?? "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"; + const body = fonts.body ?? display; + const mono = fonts.mono ?? "ui-monospace, 'JetBrains Mono', monospace"; + + const accentFg = pickReadableForeground(accent); + const accent2Fg = pickReadableForeground(accent2); + + const productName = title; + const tagline = oneLine(subtitle).slice(0, 120); + + return ` + + + + + ${escapeHtml(productName)} — showcase + + + + + +
      +
      +
      +
      ${escapeHtml(productName)} · live preview
      +

      The system that makes ${escapeHtml(productName)} feel like ${escapeHtml(productName)}.

      +

      ${escapeHtml(tagline)}

      + +
      + 4.9 · App Store rating + SOC 2 · Type II compliant + 120k+ active teams +
      +
      +
      + +
      +
      +
      Trusted by teams shipping serious work
      +
      + Northwind + Pioneer + Lattice + Atlas Co. + Voltage + Foundry +
      +
      +
      + +
      +
      +
      What it does
      +

      Every primitive a fast team needs.

      +

      A system styled entirely from the tokens of ${escapeHtml(productName)} — palette, typography, surfaces, and motion. Drop it into any product and it stays in character.

      +
      + ${featureCard('★', 'Tokens that compose', 'Color, type, spacing, and elevation defined once and reused across every surface — from a marketing hero to a row in a table.')} + ${featureCard('◐', 'Light & dark in lockstep', 'Every component ships with both modes. The accent reads as confident in either context, and contrast meets WCAG AA out of the box.')} + ${featureCard('⌘', 'Desktop-first, but mobile-honest', 'Layouts collapse from a 12-column desktop grid to a focused single column without losing density or rhythm.')} + ${featureCard('▣', 'Production-grade primitives', '40+ components — from the obvious (button, input) to the load-bearing (data table, command bar, empty states).')} + ${featureCard('↗', 'Designed for handoff', 'Every spec carries a Figma frame, a code snippet, and a "do/don’t" pair so engineers don’t have to guess.')} + ${featureCard('∞', 'Built to evolve', 'Tokens version semver-style. A palette refresh ships through one file — no component code touches.')} +
      +
      +
      + +
      +
      +
      In production
      +

      A workspace, fully styled.

      +

      This is the same component library you'd use in your app — rendered with ${escapeHtml(productName)} tokens.

      +
      +
      +
      + +
      +
      +

      Overview

      + ↑ 12.4% this week +
      +
      + ${kpi('MRR', '$184,210', '+8.2%')} + ${kpi('Active orgs', '2,914', '+121')} + ${kpi('Conversion', '4.6%', '+0.4 pp')} + ${kpi('Net retention', '113%', '+2 pp')} +
      +
      +
      + Revenue · last 12 weeks + USD · weekly +
      +
      + ${inlineLineChart()} +
      +
      +
      +
      +
      +
      Top accounts
      + View all +
      + ${listRow('Northwind Trading', 'Annual · NA', '$48,200', 'up')} + ${listRow('Pioneer Robotics', 'Quarterly · EMEA', '$31,890', 'up')} + ${listRow('Atlas Cooperative', 'Annual · APAC', '$22,400', '')} + ${listRow('Foundry Group', 'Monthly · NA', '$14,750', 'up')} +
      +
      +
      +
      Activity
      + Live +
      + ${activityRow('Renewal closed', 'Lattice · 11m ago')} + ${activityRow('Trial started', 'Voltage · 22m ago')} + ${activityRow('Plan upgraded', 'Pioneer · 1h ago')} + ${activityRow('Invoice paid', 'Atlas · 2h ago')} +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      Pricing
      +

      Built for teams of one to one thousand.

      +

      Pick the plan that matches the way your team ships. Every tier ships the full token system.

      +
      + ${priceCard('Starter', '$0', 'Free forever', ['Single user', 'All core tokens', 'Up to 3 projects', 'Community support'])} + ${priceCard('Team', '$24', 'per seat / month', ['Unlimited projects', 'Real-time co-edit', 'Brand themes', 'Priority email support'], true)} + ${priceCard('Enterprise', 'Custom', 'volume pricing', ['SSO + SCIM', 'Audit logs', 'Custom token schemas', 'Dedicated success manager'])} +
      +
      +
      + +
      +
      +
      Customers
      +

      Loved by teams who care about craft.

      +
      + ${quote('"Our marketing site, our app, and our internal dashboards finally feel like the same product. The token system is doing all the work."', 'Mira Okafor', 'Head of Design · Pioneer')} + ${quote('"We swapped our entire design language in an afternoon. Nothing broke. That’s the line, and we crossed it."', 'Caleb Renner', 'Engineering Lead · Northwind')} +
      +
      +
      + +
      +
      +
      FAQ
      +

      Questions, answered.

      +
      + ${faq('Is this a Figma library, a code library, or both?', 'Both. Tokens flow from one source of truth into Figma styles and into the codegen pipeline at the same time.')} + ${faq('Can we ship our own brand theme?', 'Yes — fork the token file, change the palette and type stack, and every component reskins automatically.')} + ${faq('What about accessibility?', 'Color contrast meets WCAG AA on every surface. Components ship with focus rings, ARIA roles, and keyboard handling.')} + ${faq('How do you handle dark mode?', 'Every token has a paired dark value. The system flips at the document level — no per-component overrides needed.')} +
      +
      +
      + +
      +
      +
      +
      +

      Ship a product that finally feels finished.

      +

      Drop the system into your app today. The first project is on us.

      +
      + +
      +
      +
      +
      + + + +`; +} + +function featureCard(icon, title, body) { + return `
      +
      ${escapeHtml(icon)}
      +

      ${escapeHtml(title)}

      +

      ${escapeHtml(body)}

      +
      `; +} + +function kpi(label, value, delta) { + return `
      +
      ${escapeHtml(label)}
      +
      ${escapeHtml(value)}
      +
      ${escapeHtml(delta)}
      +
      `; +} + +function listRow(name, meta, value, status) { + const badge = status === 'up' ? '' : '·'; + return `
      +
      +
      ${escapeHtml(name)}
      +
      ${escapeHtml(meta)}
      +
      +
      ${escapeHtml(value)}
      + ${badge} +
      `; +} + +function activityRow(name, meta) { + return `
      +
      +
      ${escapeHtml(name)}
      +
      ${escapeHtml(meta)}
      +
      +
      + +
      `; +} + +function priceCard(name, price, sub, features, featured) { + return `
      +
      ${escapeHtml(name)}
      +
      ${escapeHtml(price)} ${escapeHtml(sub)}
      +
        ${features.map((f) => `
      • ${escapeHtml(f)}
      • `).join('')}
      + Choose ${escapeHtml(name)} +
      `; +} + +function quote(text, name, role) { + return `
      +

      ${escapeHtml(text)}

      +
      +
      +
      +
      ${escapeHtml(name)}
      +
      ${escapeHtml(role)}
      +
      +
      +
      `; +} + +function faq(q, a) { + return `
      +

      ${escapeHtml(q)}

      +

      ${escapeHtml(a)}

      +
      `; +} + +function inlineLineChart() { + // Deterministic numbers so the chart looks specific (12 weekly data points). + const data = [38, 44, 41, 52, 49, 61, 58, 67, 71, 76, 82, 88]; + const max = Math.max(...data); + const min = Math.min(...data); + const w = 720; + const h = 160; + const padX = 8; + const padY = 14; + const stepX = (w - padX * 2) / (data.length - 1); + const norm = (v) => padY + (h - padY * 2) * (1 - (v - min) / (max - min)); + const points = data.map((v, i) => `${padX + i * stepX},${norm(v).toFixed(1)}`).join(' '); + const area = `${padX},${h} ${points} ${w - padX},${h}`; + return ` + + + + + + + + + ${data.map((v, i) => ``).join('')} + `; +} + +function extractSubtitle(raw) { + const lines = raw.split(/\r?\n/); + const h1 = lines.findIndex((l) => /^#\s+/.test(l)); + if (h1 === -1) return ''; + const after = lines.slice(h1 + 1); + const nextHeading = after.findIndex((l) => /^#{1,6}\s+/.test(l)); + const window = (nextHeading === -1 ? after : after.slice(0, nextHeading)) + .join('\n') + .replace(/^>\s*Category:.*$/gim, '') + .replace(/^>\s*/gm, '') + .trim(); + return window.split(/\n\n/)[0]?.slice(0, 240) ?? ''; +} + +function extractColors(raw) { + const colors = []; + const seen = new Set(); + function push(name, value) { + const cleanName = name.replace(/[*_`]+/g, '').replace(/\s+/g, ' ').trim(); + if (!cleanName || cleanName.length > 60) return; + const v = normalizeHex(value); + const key = `${cleanName.toLowerCase()}|${v}`; + if (seen.has(key)) return; + seen.add(key); + colors.push({ name: cleanName, value: v }); + } + const reA = /^[\s>*-]*\**\s*([A-Za-z][A-Za-z0-9 /&()+_-]{1,40}?)\s*\**\s*[::]\s*`?(#[0-9a-fA-F]{3,8})/gm; + let m; + while ((m = reA.exec(raw)) !== null) push(m[1], m[2]); + const reB = /\*\*([A-Za-z][A-Za-z0-9 /&()+_-]{1,40}?)\*\*\s*\(?\s*`?(#[0-9a-fA-F]{3,8})/g; + while ((m = reB.exec(raw)) !== null) push(m[1], m[2]); + return colors; +} + +function extractFonts(raw) { + const out = {}; + const re = /^[\s>*-]*\**\s*([A-Za-z][A-Za-z /]{1,30}?)\s*\**\s*[::]\s*`?([^`\n]+?)`?$/gm; + let m; + while ((m = re.exec(raw)) !== null) { + const label = m[1].toLowerCase(); + const value = m[2].trim().replace(/[*_`]+$/g, '').trim(); + if (!/[a-zA-Z]/.test(value)) continue; + if (value.startsWith('#')) continue; + if (/display|heading|h1|title/.test(label) && !out.display) out.display = value; + else if (/body|text|paragraph|copy/.test(label) && !out.body) out.body = value; + else if (/mono|code/.test(label) && !out.mono) out.mono = value; + } + return out; +} + +function pickColor(colors, hints) { + for (const hint of hints) { + const needle = hint.toLowerCase(); + const found = colors.find((c) => c.name.toLowerCase().includes(needle)); + if (found) return found.value; + } + return null; +} + +function firstNonNeutral(colors) { + for (const c of colors) { + const v = c.value.replace('#', '').toLowerCase(); + if (v.length !== 6) continue; + const r = parseInt(v.slice(0, 2), 16); + const g = parseInt(v.slice(2, 4), 16); + const b = parseInt(v.slice(4, 6), 16); + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const sat = max === 0 ? 0 : (max - min) / max; + if (sat > 0.25) return c.value; + } + return null; +} + +function secondNonNeutral(colors, exclude) { + let seen = false; + for (const c of colors) { + const v = c.value.replace('#', '').toLowerCase(); + if (v.length !== 6) continue; + const r = parseInt(v.slice(0, 2), 16); + const g = parseInt(v.slice(2, 4), 16); + const b = parseInt(v.slice(4, 6), 16); + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const sat = max === 0 ? 0 : (max - min) / max; + if (sat > 0.25) { + if (c.value === exclude || (!seen)) { seen = true; continue; } + return c.value; + } + } + return null; +} + +function pickReadableForeground(hex) { + const n = normalizeHex(hex); + if (n.length !== 7) return '#ffffff'; + const r = parseInt(n.slice(1, 3), 16); + const g = parseInt(n.slice(3, 5), 16); + const b = parseInt(n.slice(5, 7), 16); + const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + return lum > 0.6 ? '#0a0a0a' : '#ffffff'; +} + +function mixSurface(bg) { + const n = normalizeHex(bg); + if (n.length !== 7) return '#fafafa'; + const r = parseInt(n.slice(1, 3), 16); + const g = parseInt(n.slice(3, 5), 16); + const b = parseInt(n.slice(5, 7), 16); + const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + // Lift dark backgrounds; tint light backgrounds slightly cooler. + const adjust = lum < 0.4 ? 16 : -8; + const fix = (v) => Math.max(0, Math.min(255, v + adjust)).toString(16).padStart(2, '0'); + return `#${fix(r)}${fix(g)}${fix(b)}`; +} + +function normalizeHex(hex) { + let h = hex.toLowerCase(); + if (h.length === 4) { + h = '#' + h.slice(1).split('').map((c) => c + c).join(''); + } + return h; +} + +function cleanTitle(raw) { + return String(raw).replace(/^Design System (Inspired by|for)\s+/i, '').trim(); +} + +function oneLine(s) { + return String(s).replace(/\s+/g, ' ').trim(); +} + +function escapeHtml(s) { + return String(s).replace(/[&<>"']/g, (c) => + c === '&' ? '&' : c === '<' ? '<' : c === '>' ? '>' : c === '"' ? '"' : ''', + ); +} diff --git a/daemon/design-systems.js b/daemon/design-systems.js new file mode 100644 index 0000000..544c4bf --- /dev/null +++ b/daemon/design-systems.js @@ -0,0 +1,157 @@ +// Design-system registry. Scans /design-systems/* for DESIGN.md +// files. Title comes from the first H1. Category comes from a +// `> Category: ` blockquote line beneath the H1. Summary is the first +// paragraph between the H1 and the next heading (Category line stripped). + +import { readdir, readFile, stat } from 'node:fs/promises'; +import path from 'node:path'; + +export async function listDesignSystems(root) { + const out = []; + let entries = []; + try { + entries = await readdir(root, { withFileTypes: true }); + } catch { + return out; + } + for (const entry of entries) { + if (!entry.isDirectory()) continue; + const designPath = path.join(root, entry.name, 'DESIGN.md'); + try { + const stats = await stat(designPath); + if (!stats.isFile()) continue; + const raw = await readFile(designPath, 'utf8'); + const titleMatch = /^#\s+(.+?)\s*$/m.exec(raw); + const title = cleanTitle(titleMatch?.[1] ?? entry.name); + out.push({ + id: entry.name, + title, + category: extractCategory(raw) ?? 'Uncategorized', + summary: summarize(raw), + swatches: extractSwatches(raw), + body: raw, + }); + } catch { + // Skip. + } + } + return out; +} + +export async function readDesignSystem(root, id) { + const file = path.join(root, id, 'DESIGN.md'); + try { + return await readFile(file, 'utf8'); + } catch { + return null; + } +} + +function summarize(raw) { + const lines = raw.split(/\r?\n/); + const firstH1 = lines.findIndex((l) => /^#\s+/.test(l)); + if (firstH1 === -1) return ''; + const afterH1 = lines.slice(firstH1 + 1); + const nextHeading = afterH1.findIndex((l) => /^#{1,6}\s+/.test(l)); + const window = (nextHeading === -1 ? afterH1 : afterH1.slice(0, nextHeading)) + .join('\n') + // Drop the Category metadata line — it's surfaced separately. + .replace(/^>\s*Category:.*$/gim, '') + .replace(/^>\s*/gm, '') + .trim(); + return window.split(/\n\n/)[0]?.slice(0, 240) ?? ''; +} + +function extractCategory(raw) { + const m = /^>\s*Category:\s*(.+?)\s*$/im.exec(raw); + return m?.[1]; +} + +// Strip boilerplate like "Design System Inspired by Cohere" → "Cohere" so +// the picker dropdown reads cleanly. Hand-authored titles that don't match +// the pattern (e.g. "Neutral Modern") pass through unchanged. +function cleanTitle(raw) { + return raw + .replace(/^Design System (Inspired by|for)\s+/i, '') + .trim(); +} + +/** + * Pull 4 representative colors from a DESIGN.md so the picker can render + * a tiny swatch row next to each system. Order: [bg, support, fg, accent]. + * + * The shape is deliberately compact — one accent + one background + one + * fg + one supporting tone — so the row reads like a brand mark even at + * thumbnail scale. Picked greedily by token-name hints (matches the + * heuristics in design-system-preview.js so the strip and the showcase + * agree on which colors the system "is"). + * + * @param {string} raw Markdown body of DESIGN.md + * @returns {string[]} Up to 4 hex strings; [] if extraction fails. + */ +function extractSwatches(raw) { + const colors = []; + const seen = new Set(); + function push(name, value) { + const cleanName = name.replace(/[*_`]+/g, '').replace(/\s+/g, ' ').trim().toLowerCase(); + const v = normalizeHex(value); + if (!v || cleanName.length > 60) return; + const key = `${cleanName}|${v}`; + if (seen.has(key)) return; + seen.add(key); + colors.push({ name: cleanName, value: v }); + } + // Form A: "- **Background:** `#FAFAFA`" + const reA = /^[\s>*-]*\**\s*([A-Za-z][A-Za-z0-9 /&()+_-]{1,40}?)\s*\**\s*[::]\s*`?(#[0-9a-fA-F]{3,8})/gm; + let m; + while ((m = reA.exec(raw)) !== null) push(m[1], m[2]); + // Form B: "**Stripe Purple** (`#533afd`)" + const reB = /\*\*([A-Za-z][A-Za-z0-9 /&()+_-]{1,40}?)\*\*\s*\(?\s*`?(#[0-9a-fA-F]{3,8})/g; + while ((m = reB.exec(raw)) !== null) push(m[1], m[2]); + if (colors.length === 0) return []; + + function pick(hints) { + for (const h of hints) { + const found = colors.find((c) => c.name.includes(h)); + if (found) return found.value; + } + return null; + } + function isNeutral(hex) { + if (!/^#[0-9a-f]{6}$/.test(hex)) return false; + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + return Math.max(r, g, b) - Math.min(r, g, b) < 10; + } + + const bg = + pick(['page background', 'background', 'canvas', 'paper', 'surface']) + ?? '#ffffff'; + const fg = + pick(['heading', 'foreground', 'ink', 'fg', 'text', 'navy', 'graphite']) + ?? '#111111'; + const accent = + pick(['primary brand', 'brand primary', 'accent', 'brand', 'primary']) + ?? colors.find((c) => !isNeutral(c.value))?.value + ?? colors[0]?.value + ?? '#888888'; + const support = + pick(['border', 'divider', 'rule', 'muted', 'secondary', 'subtle']) + ?? colors.find( + (c) => isNeutral(c.value) && c.value !== bg && c.value !== fg, + )?.value + ?? '#cccccc'; + + return [bg, support, fg, accent]; +} + +function normalizeHex(raw) { + if (typeof raw !== 'string') return null; + const m = /^#([0-9a-fA-F]{3,8})$/.exec(raw.trim()); + if (!m) return null; + let hex = m[1]; + if (hex.length === 3) hex = hex.split('').map((c) => c + c).join(''); + if (hex.length === 4) hex = hex.split('').map((c) => c + c).join('').slice(0, 8); + return '#' + hex.toLowerCase(); +} diff --git a/daemon/frontmatter.js b/daemon/frontmatter.js new file mode 100644 index 0000000..f4aab97 --- /dev/null +++ b/daemon/frontmatter.js @@ -0,0 +1,136 @@ +// Minimal YAML front-matter parser. Handles the subset used by SKILL.md in +// our examples: scalar strings/numbers/booleans, block-literal (|) strings, +// and flat arrays ("- foo"). Keeps the daemon dep-free. If you need real +// YAML (nested objects, flow-style, anchors), swap for `yaml` or `js-yaml`. + +export function parseFrontmatter(src) { + const text = src.replace(/^/, ''); + const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(text); + if (!match) return { data: {}, body: text }; + const [, yaml, body] = match; + return { data: parseYamlSubset(yaml), body }; +} + +function parseYamlSubset(src) { + const lines = src.split(/\r?\n/); + const root = {}; + const stack = [{ indent: -1, container: root, key: null }]; + let i = 0; + + while (i < lines.length) { + const raw = lines[i]; + if (/^\s*(#.*)?$/.test(raw)) { + i++; + continue; + } + const indent = raw.match(/^\s*/)[0].length; + + while (stack.length > 1 && indent <= stack[stack.length - 1].indent) { + stack.pop(); + } + const top = stack[stack.length - 1]; + const line = raw.slice(indent); + + // Array item + if (line.startsWith('- ')) { + const value = line.slice(2).trim(); + let container = top.container; + if (!Array.isArray(container)) { + // Convert the pending key's value to an array on first `-`. + const parent = stack[stack.length - 2]; + if (parent && top.key) { + parent.container[top.key] = []; + container = parent.container[top.key]; + top.container = container; + } else { + i++; + continue; + } + } + if (value.includes(':')) { + const obj = {}; + const colonIdx = value.indexOf(':'); + const key = value.slice(0, colonIdx).trim(); + const valRaw = value.slice(colonIdx + 1).trim(); + if (valRaw) obj[key] = coerce(valRaw); + container.push(obj); + stack.push({ indent, container: obj, key: null }); + } else { + container.push(coerce(value)); + } + i++; + continue; + } + + // key: value or key: | + const kv = /^([^:]+):\s*(.*)$/.exec(line); + if (!kv) { + i++; + continue; + } + const key = kv[1].trim(); + const val = kv[2]; + + if (val === '' || val === undefined) { + top.container[key] = {}; + stack.push({ indent, container: top.container[key], key }); + i++; + continue; + } + + if (val === '|' || val === '|-' || val === '>' || val === '>-') { + const collected = []; + const childIndent = indent + 2; + i++; + while (i < lines.length) { + const next = lines[i]; + if (/^\s*$/.test(next)) { + collected.push(''); + i++; + continue; + } + const nIndent = next.match(/^\s*/)[0].length; + if (nIndent < childIndent) break; + collected.push(next.slice(childIndent)); + i++; + } + top.container[key] = collected.join('\n').trimEnd(); + continue; + } + + if (val === '[]') { + top.container[key] = []; + i++; + continue; + } + + if (val.startsWith('[') && val.endsWith(']')) { + top.container[key] = val + .slice(1, -1) + .split(',') + .map((s) => coerce(s.trim())) + .filter((v) => v !== ''); + i++; + continue; + } + + top.container[key] = coerce(val); + i++; + } + + return root; +} + +function coerce(raw) { + if (raw === undefined) return ''; + let v = raw.trim(); + if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) { + return v.slice(1, -1); + } + if (v === 'true') return true; + if (v === 'false') return false; + if (v === 'null' || v === '~') return null; + if (/^-?\d+$/.test(v)) return Number(v); + if (/^-?\d*\.\d+$/.test(v)) return Number(v); + return v; +} diff --git a/daemon/lint-artifact.js b/daemon/lint-artifact.js new file mode 100644 index 0000000..2943172 --- /dev/null +++ b/daemon/lint-artifact.js @@ -0,0 +1,367 @@ +/** + * Anti-slop linter for generated HTML artifacts. + * + * Runs grep-style checks against an artifact body and returns a list of + * structured findings. P0 findings indicate the artifact is regressing + * to AI-slop tropes (purple gradients, emoji feature icons, sans-serif + * display, invented metrics, lorem-style filler) and are surfaced back + * to the agent as a system message so it can self-correct on the next + * turn. P1/P2 findings are advisories. + * + * The linter is deliberately greppy: cheap, deterministic, and trivial + * to extend. It does NOT parse HTML — false positives are tolerable + * because each finding includes a snippet so the agent can verify. + * + * Wired into the artifact save flow (POST /api/artifacts/save) and + * exposed standalone at POST /api/artifacts/lint for the chat UI to + * surface badges next to each saved artifact. + */ + +/** + * @typedef {Object} LintFinding + * @property {'P0'|'P1'|'P2'} severity + * @property {string} id short stable id (e.g. 'purple-gradient') + * @property {string} message one-line explanation + * @property {string} fix one-line corrective suggestion (for the agent) + * @property {string} [snippet] matched text (≤ 200 chars), if any + */ + +const PURPLE_HEXES = [ + '#a855f7', '#9333ea', '#7c3aed', '#6d28d9', '#581c87', + '#8b5cf6', '#a78bfa', '#c4b5fd', '#ddd6fe', '#ede9fe', +]; + +const SLOP_EMOJI = [ + '✨', '🚀', '🎯', '⚡', '🔥', '💡', '📈', '🎨', '🛡️', '🌟', + '💪', '🎉', '👋', '🙌', '✅', '⭐', '🏆', +]; + +// Simple sentinel words for invented-metric copy. Catching every claim is +// hopeless; we look for the canonical AI-startup phrasings. +const INVENTED_METRIC_PATTERNS = [ + /\b10×\s+(faster|better|easier)\b/i, + /\b100×\s+(faster|better)\b/i, + /\b99\.\d+%\s+uptime\b/i, + /\bzero[- ]downtime\b/i, + /\b3×\s+more\s+(productive|efficient)\b/i, +]; + +const FILLER_PATTERNS = [ + /\bfeature\s+(one|two|three|1|2|3)\b/i, + /\blorem\s+ipsum\b/i, + /\bdolor\s+sit\s+amet\b/i, + /\bplaceholder\s+text\b/i, + /\bsample\s+content\b/i, +]; + +// Display-face check: an h1 / h2 / h3 element whose `font-family` lands on +// Inter / Roboto / Arial / -apple-system without an actual serif before it. +// We check the ` + + +
      + +
      Engineering
      +

      Why we rewrote our sync engine in Rust

      + +

      For two years our Go sync engine was good enough. Then video editors started joining the customer list, and the GC pauses we'd been politely ignoring turned into bug reports we couldn't ignore.

      +
      + +

      The decision wasn't sudden. We'd been watching the GC pause distribution shift for six months before we admitted what the data was telling us. P50 latency was great. P99 was a horror movie. Customers syncing 30 GB of .psd files in active editing sessions were the ones writing in.

      + +

      Rewriting an entire sync engine sounds like the kind of project a startup is told never to do. We did it anyway. Here's how it went, what surprised us, and the parts I'd do differently.

      + +

      The trigger: GC pauses we couldn't fix

      +

      Go's garbage collector is brilliant. It is also, fundamentally, a tradeoff. Our hot path allocated short-lived buffer slices on every block diff — and at our scale, on a heavy uploader, the collector ran often enough that the P99 pause crept past 50ms.

      + +

      We tried the usual fixes: pooling buffers with sync.Pool, tuning GOGC, reducing allocations in the merge path. They each helped a little. None of them got us under 20ms, and the customers we cared about needed under 5.

      + +
      "We can't fix this in Go. We can fix it in something without a GC."
      + +

      Our staff engineer Sasha said this in a meeting in October. He was right. The question wasn't whether to leave Go. It was what to leave it for, and how much we could keep.

      + +

      What we kept; what we threw out

      +

      The CLI stayed in Go. The control plane stayed in Go. The bit that does block-level diffing in a hot loop on a customer's laptop — that became Rust. The boundary became a single FFI surface with a small, opinionated protocol.

      + +
      +
      38ms → 4ms
      P99 sync latency
      +
      62%
      Memory drop
      +
      11 weeks
      From RFC to ship
      +
      + +

      The numbers above are real and from production. They are also misleading without context: the Rust port doesn't just remove the GC, it also removes a layer of abstraction we'd been carrying since the Go MVP.

      + +

      What I'd do differently

      +

      One thing: the FFI boundary. We chose cgo for symmetry — Go calling Rust feels right when you already have Go everywhere. But the binding ceremony is brittle, and we ate two production incidents from string lifetime mistakes before we wrote a wrapper layer that handled them once.

      + +

      If I were starting today, I'd reach for uniffi or generate the bindings from a schema. The lessons isn't don't use cgo; it's treat the boundary like an external API the moment you cross language families.

      + +
      Filebase is hiring engineers who like writing this kind of post. See open roles →
      +
      + + diff --git a/skills/critique/SKILL.md b/skills/critique/SKILL.md new file mode 100644 index 0000000..bbfbb96 --- /dev/null +++ b/skills/critique/SKILL.md @@ -0,0 +1,258 @@ +--- +name: critique +description: | + Run a 5-dimension expert design review on any HTML artifact in the + project — Philosophy / Visual hierarchy / Detail / Functionality / + Innovation, each scored 0–10. Outputs a single self-contained HTML + report with a radar chart, evidence-backed scores, and three lists: + Keep / Fix / Quick-wins. Use when the brief asks for a "design + review", "design critique", "5 维度评审", "design audit", or "what's + wrong with my design". +triggers: + - "critique" + - "design review" + - "design audit" + - "5 维度评审" + - "5-dim review" + - "audit my design" + - "review my deck" + - "review my landing page" + - "评审" + - "复盘" +ocd: + mode: prototype + platform: desktop + scenario: design + upstream: "https://github.com/alchaincyf/huashu-design" + preview: + type: html + entry: index.html + design_system: + requires: false + example_prompt: "Run a 5-dimension critique on the magazine-web-ppt deck I just generated — score philosophy / hierarchy / detail / function / innovation, give me Keep / Fix / Quick-wins." +--- + +# Critique Skill · 5 维度专家评审 + +Produce a single-file HTML "design review report" that scores any +artifact across 5 dimensions and proposes actionable fixes. Inspired by +the *huashu-design* expert-critique flow. + +## When to use + +- After the agent (or user) generates an artifact (deck / prototype / + landing page) and the user asks "what's wrong with this?" or + "review this" +- As a self-check loop the agent can run on its own output **before** + emitting it +- For comparing two variants of the same design + +## What you produce + +A single self-contained `` review report +including: + +1. **Header** — what artifact was reviewed, date, reviewer ("OCD · + Critique skill"), 1-line verdict +2. **Radar chart** (inline SVG, no library) showing the 5 scores +3. **Five dimension cards**, each with: + - Score 0–10 (with band: 0–4 *Broken* · 5–6 *Functional* · 7–8 *Strong* + · 9–10 *Exceptional*) + - 1-paragraph evidence (cite specific elements / files / lines) + - One Keep / Fix / Quick-win bullet +4. **Combined action lists** at the bottom: + - **Keep** — what's working, don't touch + - **Fix** — P0 / P1 issues that are visually expensive + - **Quick wins** — 5–15 minute tweaks with disproportionate impact + +## The 5 dimensions + +> Each dimension is independent — a deck can be 9/10 on Innovation but +> 4/10 on Hierarchy and the report should say so plainly. Don't average +> away interesting failures. + +### 1. Philosophy consistency · 哲学一致性 + +> Does the artifact pick a clear *direction* and stick to it through +> every micro-decision (chrome / kicker / spacing / accent)? + +**Evidence to look for:** +- Is there one declared design direction (e.g. Monocle / WIRED / + Kinfolk) or is it three styles in a trench coat? +- Does the chrome / kicker vocabulary stay in one register, or does + page 3 say "Vol.04 · Spring" and page 7 say "BUT WAIT 🔥"? +- Are accent / serif / mono used by the same rule throughout? + +**0–4** Three styles fighting each other. **5–6** One direction but +half the elements drift. **7–8** Coherent, occasional drift on edge +pages. **9–10** Every element argues for the same thesis. + +### 2. Visual hierarchy · 视觉层级 + +> Can a stranger figure out what to read first, second, third — without +> being told? + +**Evidence to look for:** +- Is the largest type clearly the most important thing on each page? +- Do mono / serif / sans roles match the information's *role* (meta / + body / display)? +- Lots of "loud" elements competing? Or a clear primary + secondary + + tertiary tier? + +**0–4** Everything shouts. **5–6** Hierarchy works on hero pages but +breaks on body. **7–8** Clear tiers, occasional collision. **9–10** Eye +moves with zero friction. + +### 3. Detail execution · 细节执行 + +> The 90/10 stuff — alignment, leading, kerning at large sizes, image +> framing, foot/chrome polish, edge-case spacing. + +**Evidence to look for:** +- Big-stat pages: does the number sit on a baseline, or float? +- Left/right column tops aligned in `grid-2-7-5`? +- `frame-img` + caption proportions consistent across pages? +- Mono labels: same letter-spacing? same uppercase rule? +- Any orphaned `
      ` causing 1-character lines? + +**0–4** Visible tape and string. **5–6** Most pages clean, 1–2 +ragged. **7–8** Polished, expert eye finds 2–3 misses. **9–10** +Magazine-grade — the kind of detail that makes printed-by-hand +typographers nod. + +### 4. Functionality · 功能性 + +> Does the artifact *work* for its intended use? Click targets, nav, +> readability at presentation distance, copy-paste-ability for code +> blocks, mobile fallback if relevant. + +**Evidence to look for:** +- Deck: keyboard / wheel / touch nav all working? Iframe scroll + fallback? +- Landing: CTA above the fold? Phone number tappable on mobile? +- Runbook: code blocks copyable, mono font, no smart quotes? +- Critical info readable from 4m away (large screen presentation)? + +**0–4** Visually fine but doesn't accomplish its job. **5–6** Core +flow works, edge cases broken. **7–8** Robust through normal use. +**9–10** Defensively engineered — handles iframe / fullscreen / paste +/ print without flinching. + +### 5. Innovation · 创新性 + +> Does this push past the median? Is there one element that makes +> people lean in? + +**Evidence to look for:** +- One *unexpected* layout / motion / typographic move that wasn't + required? +- Or 100% safe — could be any deck/landing from any agency? +- Is the innovation *earned* (matches direction) or grafted on + (random WebGL on a Kinfolk slow-living deck)? + +**0–4** Generic AI-slop median. **5–6** Competent and unmemorable. +**7–8** One memorable moment, the rest solid. **9–10** Multiple +moves you'd steal — but each one obviously serves the thesis. + +## Scoring discipline (read before you score) + +- **Always cite evidence** — "scored 4 because hero page mixes + Playfair display with Inter sans on the same line" beats "feels + inconsistent". Numbers without evidence get rejected. +- **Don't average up** — if Hierarchy is 5 because page 3 is broken, + don't bump to 7 because pages 1 and 2 are fine. The score is the + *worst sustained band*. +- **Don't grade-inflate** — a 7 means *strong*, not *acceptable*. If + every score is 7+, you're not reviewing critically. +- **Innovation is allowed to be low** — 5/10 is fine for production + deliverables. Don't punish *appropriate* conservatism. + +## Workflow + +### Step 1 — Acquire the artifact + +Three modes: + +1. **Project file** — user said "review the index.html I just made": + open it from the project folder. +2. **Pasted HTML** — user pasted code in the chat: read it from the + message. +3. **Generated by you in this turn** — you just emitted an artifact + above and want to self-critique: re-read your own ``. + +If multiple HTML files exist, ask which one (don't review all). + +### Step 2 — Read enough to score + +Skim the entire ` + + +
      + + +
      +
      +
      + 5-Dim Critique + · + 2026.04.27 + · + OCD · Critique skill +
      +

      magazine-web-ppt
      example deck

      +
      +

      + 7.4 / 10 overall. Strong philosophical + backbone and detail — the deck looks like one designer made + every slide. Innovation is conservative on purpose; functionality + loses points only because the example ships without real images. +

      +
      + + +
      +
      +
      Score Radar
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PHILOSOPHY + 8 + HIERARCHY + 7 + DETAIL + 8 + FUNCTION + 6 + INNOVATION + 5 + +
      Overall · 7.4 / 10 · band Strong
      +
      + +
      +
      +
      Philosophy consistencyPhil. cons.
      +
      +
      8/10
      +
      Strong
      +
      +
      +
      Visual hierarchyHier.
      +
      +
      7/10
      +
      Strong
      +
      +
      +
      Detail executionDetail
      +
      +
      8/10
      +
      Strong
      +
      +
      +
      FunctionalityFunc.
      +
      +
      6/10
      +
      Functional
      +
      +
      +
      InnovationInnov.
      +
      +
      5/10
      +
      Functional
      +
      +
      +
      + + +

      Dimension reportsEvidence per axis

      + +
      +
      +
      +
      Philosophy consistencyPhil. cons. · 哲学一致性
      +
      8/10
      +
      +

      + The 9-slide rhythm reads as a single direction (Monocle Editorial) + from cover to close. chrome vocabulary stays in one + register: "A Talk · 2026.04.22", "Act II · 04 / 09", + "Page 06 · 金句". The drift is the kicker on + slide 5 — "Act II" is good, but the slide title "折叠" + is a one-character display word that competes with the Act number for + eyeballs. Worth tightening. +

      +
      +
      KeepThe chrome / kicker / foot vocabulary across all 9 slides — it's the deck's identity.
      +
      FixSlide 5: bump the kicker to "Act II · 折叠" or shrink the display title to clear the hierarchy.
      +
      +
      + +
      +
      +
      Visual hierarchyHier. · 视觉层级
      +
      7/10
      +
      +

      + Hero pages (1, 5, 7, 9) are textbook — display serif dominates, + kicker and meta-row recede. Body pages + mostly hold up: stat-cards on slide 2 use .stat-label + (mono small) → .stat-nb (serif large) → .stat-note + (sans body), three tiers, no collision. The miss is slide 3's + callout — its left-rule competes visually with the + .h-xl heading because both sit at the same x-coord + and similar weight. Eye doesn't know if to read heading-first or + quote-first. +

      +
      +
      KeepStat-card 3-tier structure on slide 2 — copy this everywhere.
      +
      FixSlide 3: indent the callout by 2vw or push it below the lead so it visibly belongs to a lower tier.
      +
      +
      + +
      +
      +
      Detail executionDetail · 细节执行
      +
      8/10
      +
      +

      + Magazine-grade in places — every .foot aligns + baseline-to-baseline across all 9 slides; .meta-row + uses one mono spec throughout (.16em tracking, + uppercase). Pipeline on slide 4 keeps perfect grid even when + column count drops to 3. Two real misses: (1) Slide 3 image-slot + uses aspect-ratio:16/10 but the placeholder text + inside is centered which makes it look hollow at viewport widths + ≤ 1100px; (2) the dot-nav at the bottom overlaps the foot text + on slide 5 because the hero centered grid eats vertical space. +

      +
      +
      KeepThe mono .foot spec — it's the deck's grace note, do not change letter-spacing.
      +
      FixSlide 5 hero grid: cap inner content at min-height:78vh so the foot stays clear of the dot-nav.
      +
      +
      + +
      +
      +
      FunctionalityFunc. · 功能性
      +
      6/10
      +
      +

      + Keyboard / wheel / touch navigation works correctly inside the + host iframe (verified: ←/→/PageUp/PageDown all advance). ESC + opens the index overview, dot clicks register. Big miss is the + example ships without real images — slide 3 shows a + dashed .img-slot placeholder where a product + screenshot belongs, which is the right call for an example file + but means the user can't judge how the layout holds at full + fidelity. Second miss: iframe sandbox is + allow-scripts only in the example card, so the + WebGL background loads but the dot-nav inside the iframe takes + a click before keyboard nav captures focus. +

      +
      +
      KeepThe 5-bug-fix nav script (real scroller detection, capture-phase listeners) — proven and stable.
      +
      FixAdd a data: URI placeholder image (1×1 colored gradient) in the example so slide 3's layout reads at any width.
      +
      +
      + +
      +
      +
      InnovationInnov. · 创新性
      +
      5/10
      +
      +

      + Innovation is intentionally conservative — this is a port of + 歸藏's guizang-ppt-skill, and the value proposition is + predictability, not novelty. The dual WebGL background + (Holographic Dispersion on dark, Spiral Vortex on light) is the + one earned moment; the cross-fade on slide-theme transitions is + subtle and well-timed. But everything else (layout vocabulary, + chrome / foot pattern, theme presets) is faithfully replicated + from the upstream. There is no "lean-forward" surprise that + makes a viewer screenshot a slide. For its declared purpose + (Monocle Editorial direction), this is appropriate. For an + AI demo-day deck, it's a missed opportunity. +

      +
      +
      KeepThe dual-shader cross-fade — it's the only "magic" the deck performs and it earns its keep.
      +
      Quick winAdd one typographic moment per deck — e.g. an oversized italic em kicker that breaks the grid on the closing slide.
      +
      +
      +
      + + +

      Action listsKeep · Fix · Quick wins

      + +
      +
      +
      Keepdon't break it
      +
        +
      • The 9-page rhythm: hero dark → light → dark → light → hero light → dark → hero dark → light → hero light. It's the gold standard.
      • +
      • Dual WebGL backdrops + the 1.2s cross-fade between dark and light slides.
      • +
      • chrome / kicker / foot vocabulary — they carry the Monocle direction.
      • +
      • 3-tier stat-card on slide 2 (labelnbnote).
      • +
      +
      + +
      +
      FixP0 — visually expensive
      +
        +
      • Slide 3 callout indent — currently competes with .h-xl; push 2vw right or below the lead.
      • +
      • Slide 5 hero centered grid — cap content height at 78vh so foot doesn't overlap the dot nav.
      • +
      • Slide 3 add a data: gradient placeholder image so the layout reads at narrow widths even without real assets.
      • +
      • Slide 5 kicker / display: pick one to be primary — currently both fight.
      • +
      +
      + +
      +
      Quick wins5–15 min, high signal
      +
        +
      • Inject data-screen-label on every slide for accessibility + grep self-checks.
      • +
      • Add one oversized italic en moment on the closing slide for typographic surprise.
      • +
      • Move the #hint overlay from opacity:.4 to .55 on hero pages — currently invisible.
      • +
      • Add a print stylesheet (one slide per page) so PDF export carries the rhythm.
      • +
      +
      +
      + +
      + OCD · Critique skill · v0.1 + 5 dimensions · Phil / Hier / Det / Func / Innov + github.com/alchaincyf/huashu-design +
      +
      + + diff --git a/skills/dashboard/SKILL.md b/skills/dashboard/SKILL.md new file mode 100644 index 0000000..faabaa3 --- /dev/null +++ b/skills/dashboard/SKILL.md @@ -0,0 +1,74 @@ +--- +name: dashboard +description: | + Admin / analytics dashboard in a single HTML file. Fixed left sidebar, + top bar with user/search, main grid of KPI cards and one or two charts. + Use when the brief asks for a "dashboard", "admin", "analytics", or + "control panel" screen. +triggers: + - "dashboard" + - "admin panel" + - "analytics" + - "control panel" + - "后台" + - "管理后台" +ocd: + mode: prototype + platform: desktop + scenario: operations + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] +--- + +# Dashboard Skill + +Produce a single-screen admin / analytics dashboard. + +## Workflow + +1. **Read the active DESIGN.md** (injected above). Colors, typography, spacing, + component styling all come from it. Do not invent new tokens. +2. **Classify** what the dashboard monitors (sales, traffic, usage, incidents, + ops, etc.) from the brief. Generate specific, plausible metric names and + values — no "Metric A / Metric B" placeholders. +3. **Lay out** the required regions: + - **Left sidebar** (220–260px): brand mark at top, 6–8 nav links with + icons, active state uses the DS accent. + - **Top bar**: page title on the left, search input + user avatar / status + on the right. + - **Main**: + - Row 1: 3–4 KPI cards (label + big number + delta vs. prior period). + - Row 2: one primary chart (full width or 2/3) — render as an inline SVG + line / bar / area chart drawn from real-looking numbers. + - Row 3: one secondary chart or table (recent events, top items, etc.). +4. **Write** one self-contained HTML document: + - `` through ``, CSS in one inline ` + + + +
      +
      +

      Overview · April 2026

      +
      + + +
      +
      + +
      +
      MRR
      $48.2K
      +12.4% MoM
      +
      Active accounts
      3,184
      +204 this month
      +
      Churn (30d)
      2.1%
      +0.4 pp
      +
      P95 latency
      182 ms
      -23 ms
      +
      + +
      +
      +

      Revenue · 30 days

      +
      + + + +
      +
      +
      +

      New accounts

      + + + + + + + + +
      AccountPlanStatus
      LinearTeamactive
      CursorProactive
      NotionTeamtrial
      VercelEnterpriseactive
      +
      +
      + +
      +

      Recent events

      + + + + + + + + +
      TimeAccountEventPlan
      2:14 pmAcme CoUpgraded to TeamTeam
      1:48 pmNorthwindConnected GitHubPro
      1:32 pmGlobexCancelled subscriptionSolo
      12:51 pmInitechNew seat invitedTeam
      +
      +
      + + diff --git a/skills/dating-web/SKILL.md b/skills/dating-web/SKILL.md new file mode 100644 index 0000000..81caa28 --- /dev/null +++ b/skills/dating-web/SKILL.md @@ -0,0 +1,93 @@ +--- +name: dating-web +description: | + A consumer-feeling dating / matchmaking dashboard — left rail navigation, + ticker bar of community signals, headline KPIs, a 30-day mutual-matches + bar chart, and a match-rate trend block. Editorial typography, restrained + accent. Use when the brief asks for a "dating site", "matchmaking", + "community dashboard", "social network dashboard", or any consumer + product where the data is the story. +triggers: + - "dating app" + - "dating site" + - "matchmaking" + - "social dashboard" + - "community dashboard" + - "consumer dashboard" + - "约会应用" + - "婚恋" +ocd: + mode: prototype + platform: desktop + scenario: personal + featured: 1 + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Design ‘mutuals’ — a dating site for X posters. Daily digest dashboard with stats, mutual-matches bar chart, and a community ticker." +--- + +# Dating Web Skill + +Produce a single-screen consumer dashboard that feels like a Sunday-paper +dating column rendered as software. Editorial type, single restrained +accent, lots of negative space, *no* swipe deck or hookup tropes. + +## Workflow + +1. **Read the active DESIGN.md** (injected above). Lean into a serif display + token for the metric numerals — these screens live or die on numerals. +2. **Pick a brand voice** — wry, observational, slightly literary. Generate + real, specific copy. Examples: "the people who'd text back within a day", + "manageable. two are now friends.", "your single greatest compatibility + asset." +3. **Layout**, in order: + - **Top ticker** — single-row horizontal strip across the top in a + sans-serif eyebrow style: tagline left, "NEXT TIER AT 2,080 MUTUALS" + right, both in mono caps with letter-spacing. Thin rule below. + - **Left rail** — 220–260px sidebar. Brand wordmark in serif italic at + top. User card (avatar / handle / ratio / tier). Three groups of nav: + "TODAY" (specimen, inbox, queue, notifications), "YOU" (your stats, + mutuals & communities, blocked, settings), "ARCHIVE" (past issues, + expired matches). Active item gets accent text + accent dot. + - **Main content**: + - **KPI grid** — 3 columns × 3 rows (or 9 cells). Each cell: small + caps mono label, an oversized serif numeral (use accent or muted + green for positive, muted red for caution), one-line italic + footnote. Plausible specifics — "1,842 ↑ 41 this wk · healthy + growth.", "14% above median for your cohort.", "4 / exes in your + circle · manageable. two are now friends." + - **Bar chart panel** — "mutuals — last 30 days". Tall thin black + bars, last two days highlighted in accent. Caption above with + "↑ TRENDING UP · +3 CLOSE MUTUALS THIS MONTH · TWO VIA THE SAME + OFFSITE" in mono. + - **Trend panel** — "match rate — last 12 weeks". One line of body + copy below ("STEADY CLIMB FROM 8% → 14%. ATTRIBUTABLE TO ONE + COMMUNITY JOIN…"). Footer rule. +4. **Write** a single HTML document: + - `` through ``, CSS inline. + - Background creamy off-white, body serif, mono labels everywhere. + - Use `font-feature-settings: 'tnum'` on the metric numerals. + - SVG bar chart with ~30 bars, varied heights. + - `data-ocd-id` on ticker, sidebar, kpi grid, chart, trend. +5. **Self-check**: + - Reads as restrained, editorial, slightly funny — not horny. + - Single accent token used in 3–4 places max (one KPI, two highlight + bars, one nav active state). + - No swipe deck, no hearts, no fire emoji. + +## Output contract + +Emit between `` tags: + +``` + + +... + +``` + +One sentence before the artifact, nothing after. diff --git a/skills/dating-web/example.html b/skills/dating-web/example.html new file mode 100644 index 0000000..98a7e2b --- /dev/null +++ b/skills/dating-web/example.html @@ -0,0 +1,265 @@ + + + + + + mutuals · your dating life, measured by the company you keep + + + + + + +
      +
      + YOUR DATING LIFE, MEASURED BY THE COMPANY YOU KEEP + · + REVIEWED WEEKLY +
      +
      NEXT TIER AT 2,080 MUTUALS
      +
      + +
      + + +
      +
      +
      +
      Mutuals on file
      +
      1,842
      +

      41 this wk · healthy growth.

      +
      +
      +
      Replies in 24h
      +
      47
      +

      the people who'd text back within a day.

      +
      +
      +
      Communities
      +
      14
      +

      4 active · 7 lurking · 3 inferred.

      +
      + +
      +
      Match rate
      +
      14%
      +

      above median for your cohort.

      +
      +
      +
      2nd dates
      +
      3
      +

      of 7 first dates this year. you commit.

      +
      +
      +
      Exes in your circle
      +
      4
      +

      manageable. two are now friends.

      +
      + +
      +
      Shared blocks
      +
      214
      +

      your single greatest compatibility asset.

      +
      +
      +
      Avg response
      +
      2.1h
      +

      too fast. wait 4–6h. they notice.

      +
      +
      +
      Logged-off hrs
      +
      4
      +

      / 168 this wk. we beg.

      +
      +
      + +
      +
      +

      mutuals — last 30 days

      +
      ↑ TRENDING UP · +3 CLOSE MUTUALS THIS MONTH · TWO VIA THE SAME OFFSITE
      +
      + +
      MAR 18MAR 25APR 1APR 8APR 15TODAY
      +
      + +
      +
      +

      match rate — last 12 weeks

      +
      STEADY CLIMB FROM 8% → 14%. ATTRIBUTABLE TO ONE COMMUNITY JOIN (FOUNDERS WHO POST, WK 4).
      +
      +

      A real climb, not a vibe. One community join moved your match rate more than four months of profile edits — keep posting from that circle, ship more, tweet less.

      +
      +
      +
      + + diff --git a/skills/digital-eguide/SKILL.md b/skills/digital-eguide/SKILL.md new file mode 100644 index 0000000..f2aae3e --- /dev/null +++ b/skills/digital-eguide/SKILL.md @@ -0,0 +1,91 @@ +--- +name: digital-eguide +description: | + A two-spread digital e-guide preview — page 1 is a cover (display title, + author, "What's inside" stats, table of contents teaser); page 2 is a + spread (lesson body with pull-quote and a step list). Lifestyle / creator + brand tone. Use when the brief asks for an "e-guide", "digital guide", + "lookbook", "lead magnet", "creator guide", "playbook", "PDF guide", + or "电子指南". +triggers: + - "e-guide" + - "digital guide" + - "lead magnet" + - "lookbook" + - "creator guide" + - "playbook" + - "pdf guide" + - "ebook" + - "电子指南" + - "电子书" +ocd: + mode: prototype + platform: desktop + scenario: marketing + featured: 2 + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Design ‘The Creator's Style & Format Guide’ — cover page and one inside spread, lifestyle creator brand." +--- + +# Digital E-Guide Skill + +Produce a two-page digital guide preview side-by-side. Cover on the left, +inside spread on the right. Lifestyle creator tone, lots of negative space, +serif display headings, careful column rhythm. + +## Workflow + +1. **Read the active DESIGN.md** (injected above). Pick a serif display + token for the title (italic ligatures encouraged), a body serif for + long-form, and a mono token for stats / labels. +2. **Pick the topic + author** from the brief. Generate a real title (e.g. + "The Creator's Style & Format Guide"), a real subtitle, and a one-line + author byline. +3. **Layout** — center two pages on a tinted backdrop: + - **Page 1 — cover**: + - Eyebrow ("STYLE & FORMAT GUIDE FOR CREATORS"). + - Display title with mixed weights and one italic flourish word + ("The Creator's Style & Format guide" — `&` and `guide` italic). + - 3-cell stat row ("16 PRINCIPLES OF STYLE", "38 DOS & DON'TS", + "1 BLOCK, ZERO TEMPLATES") in mono, separated by `·`. + - "What's inside" header with a 2-column TOC (chapters + page numbers + in mono, leader dots). + - Footer: "FIND YOUR VOICE" + page 01 mono. + - Subtle decorative dot or sticker (CSS) in a corner. + - **Page 2 — spread**: + - Eyebrow with chapter number + name ("CHAPTER 02 · TONE"). + - Display sub-title ("Write like you talk — only sharper."). + - 2-column body: opening paragraph + a numbered 4-step list ("01 Pick + the rule", "02 Drop the filler"…). + - Pull-quote pinned right-side: large italic display, accent color, with + attribution. + - Bottom strip with "EXERCISE" callout (mono label + 1 sentence prompt + in italic). + - Footer: chapter title + page 18 mono. +4. **Write** a single HTML document: + - `` through ``, CSS inline. + - Pages are 600×860 paper-tone cards with 6px shadow, slight rotation + opposing each other (±0.6deg) for a magazine-on-desk feel. + - `data-ocd-id` on cover, spread, toc, pull-quote, exercise. +5. **Self-check**: + - Type hierarchy is editorial — title owns page 1, sub-title owns page 2. + - Italic accent appears once per page. + - Mono used only for labels, stats, and TOC numbers. + +## Output contract + +Emit between `` tags: + +``` + + +... + +``` + +One sentence before the artifact, nothing after. diff --git a/skills/digital-eguide/example.html b/skills/digital-eguide/example.html new file mode 100644 index 0000000..e8fe2e6 --- /dev/null +++ b/skills/digital-eguide/example.html @@ -0,0 +1,204 @@ + + + + + + The Creator's Style & Format Guide — Auny + + + + + + +
      +
      +
      STYLE & FORMAT GUIDE FOR CREATORS
      +
      2026 EDITION
      +
      + +

      The Creator's Style & Format guide

      + +
      — BY AUNY · CREATOR EDUCATOR · 18 / 04 / 2026
      + +
      +
      16
      Principles of style
      +
      38
      Do's & Don'ts
      +
      1
      Block, zero templates
      +
      + +

      What's inside.

      + +
      +
      Find your voice04
      +
      Pick a format12
      +
      Tone & tension18
      +
      Visual rhythm24
      +
      Headlines that hold32
      +
      Editing the cut40
      +
      + +
      FOR THE FIRST DRAFT
      + +
      + +
      +
      +
      CHAPTER 02 · TONE
      +
      3 — RULES, 1 — EXERCISE
      +
      + +

      Write like you talk —
      only sharper.

      +

      Your voice already exists. The work is to remove the parts that aren't you, then put what's left in the order people remember. Three small rules and one Sunday-morning exercise.

      + +
      +

      Strong writing has the cadence of speech and the precision of editing. Most beginners pick one and stop. Read your draft aloud. The sentences that catch in your throat are the ones to cut.

      +
      +
      01Pick the rule. One idea per paragraph. If two appear, split the paragraph.
      +
      02Drop the filler. "I think", "kind of", "in my opinion" — they soften, then they erase.
      +
      03End with a verb. The last beat lands harder when it asks for an action, not an adjective.
      +
      04Read aloud once. Always. The microphone is the editor.
      +
      +
      + +
      + " + Specificity is the unlock — write what only you saw. + — AUNY · CHAPTER 02 +
      + +
      + EXERCISE + Rewrite your last three captions without the words just, really, or very. Keep what survives. +
      + + +
      + + diff --git a/skills/docs-page/SKILL.md b/skills/docs-page/SKILL.md new file mode 100644 index 0000000..3b487c9 --- /dev/null +++ b/skills/docs-page/SKILL.md @@ -0,0 +1,73 @@ +--- +name: docs-page +description: | + A documentation page — left nav, scrollable article body, right-rail + table of contents. Use when the brief mentions "docs", "documentation", + "guide", "API reference", or "tutorial". +triggers: + - "docs" + - "documentation" + - "guide" + - "tutorial" + - "api reference" + - "文档" +ocd: + mode: prototype + platform: desktop + scenario: engineering + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] +--- + +# Docs Page Skill + +Produce a single, three-column documentation page in one HTML file. + +## Workflow + +1. **Read the active DESIGN.md** (injected above). Use the body type token for + prose; the mono token for code; respect line-height and max-width rules. +2. **Pick a topic** from the brief — the page should look like real docs, not + a generic wireframe. Concrete API names, command examples, plausible + parameters. +3. **Lay out** three regions: + - **Left nav** (240–280px, sticky): grouped link list, current page bolded + with a left-edge accent stripe. 3–5 groups of 4–8 links. + - **Article body** (max-width ~720px, centered in the middle column): + H1, lede paragraph, H2 sections, code blocks, callout boxes (note / + warning), inline links, lists. + - **Right TOC** (200–240px, sticky): "On this page" with the H2/H3 + anchors, current section highlighted as the user scrolls. +4. **Write** a single HTML document: + - `` through ``, all CSS inline. + - CSS Grid for the three columns; sticky positioning for the rails. + - Code blocks: monospace token, soft surface fill, copy-button affordance + (visual only — no JS needed). + - Anchor IDs on every H2/H3 so the TOC links work. + - `data-ocd-id` on the nav, article, and TOC. +5. **Prose**: write at least 350 words of believable docs. Include at least + one shell command, one code snippet (5–15 lines), one callout, one table. +6. **Self-check**: + - Body text wraps at the DS line-length sweet spot (60–75 chars). + - Code uses the DS mono token, not generic `monospace`. + - Accent is restrained — used for active nav item, links, one callout + border. Not on body text. + - Page is readable at 1280w and collapses gracefully below 900w (TOC drops + out, nav becomes a top drawer). + +## Output contract + +Emit between `` tags: + +``` + + +... + +``` + +One sentence before the artifact, nothing after. diff --git a/skills/docs-page/example.html b/skills/docs-page/example.html new file mode 100644 index 0000000..3f0ec43 --- /dev/null +++ b/skills/docs-page/example.html @@ -0,0 +1,122 @@ + + + + + + Filebase docs — Quickstart + + + +
      + ◰ Filebase docs + +
      +
      + +
      +
      Docs › Getting started › Quickstart
      +

      Quickstart

      +

      Sync your first folder in under five minutes. The CLI is the fastest path; the desktop app and the API client all wrap the same engine.

      +

      1. Install the CLI

      +

      The CLI is distributed as a single binary for macOS, Linux, and Windows.

      +
      # macOS · Homebrew
      +brew install filebase
      +
      +# Linux · curl
      +curl -fsSL https://get.filebase.dev | sh
      +

      Verify the install:

      +
      filebase --version
      +# filebase 0.6.4
      +

      2. Authenticate

      +

      Sign in with your Filebase account. The token is stored in ~/.config/filebase/credentials.

      +
      filebase auth login
      +# → opens your browser
      +# ✓ Logged in as you@example.com
      +
      +
      Note
      + On servers without a browser, use filebase auth login --device for a device-code flow. +
      +

      3. Sync a folder

      +

      Pick a local directory and link it to a remote root. Filebase watches it for changes and pushes block-level diffs in the background.

      +
      cd ~/projects
      +filebase init my-team
      +filebase sync
      +

      Excluding files

      +

      Add a .filebaseignore at the root of the synced folder. Same syntax as .gitignore:

      +
      node_modules/
      +*.log
      +build/
      +

      4. Where to go next

      +

      Read Conflict resolution to understand how Filebase merges concurrent edits, or skip to the CLI reference for the full subcommand list.

      + +
      + +
      + + diff --git a/skills/email-marketing/SKILL.md b/skills/email-marketing/SKILL.md new file mode 100644 index 0000000..58f7549 --- /dev/null +++ b/skills/email-marketing/SKILL.md @@ -0,0 +1,85 @@ +--- +name: email-marketing +description: | + A brand product-launch email — masthead with wordmark, hero image block, + headline lockup with skewed-italic accent, body copy, primary CTA, and a + specifications grid. Pure HTML email layout (centered single column, table + fallback). Use when the brief asks for an "email", "newsletter blast", + "MJML", "product launch email", or "email template". +triggers: + - "email" + - "email template" + - "newsletter" + - "email blast" + - "product launch email" + - "mjml" + - "邮件营销" + - "邮件模板" +ocd: + mode: prototype + platform: desktop + scenario: marketing + featured: 3 + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Design a launch email for a sporty running shoe brand — masthead, hero, big headline lockup, specs grid, CTA." +--- + +# Email Marketing Skill + +Produce a single HTML email — centered, single column, no chrome around the +email body. Treat it like a marketing artifact: one big idea, one CTA. + +## Workflow + +1. **Read the active DESIGN.md** (injected above). Email leans on the display + font more than any other surface — pick the loudest type token in the DS + for the headline lockup. +2. **Pick the brand + product** from the brief. Generate a real wordmark, a + real product name, and one real benefit sentence — no placeholders. +3. **Layout**, in order, all centered inside a 600–680px column on a tinted + page background (so the email body looks like an email, not the page): + - **Masthead** — wordmark on the left + 3 short nav links (SHOP, JOURNAL, + MEMBERS) on the right. Thin underline. + - **Hero block** — a 16:9 product image placeholder. Use a DS-tinted + gradient or a stylized SVG silhouette of the product (shoe, bottle, + headphones, whatever the brief implies). Add a tiny brand stamp on the + top-left and a colorway tag on the bottom-left. + - **Eyebrow** — small caps, accent color, separated by `·` characters + (e.g. "NEW · MAX-CUSHION TRAINER · EMBER FLARE"). + - **Headline lockup** — 2–3 line headline using the display font, all caps, + extra-tight tracking. Apply a slight skew (`transform: skew(-6deg)`) on + one accent word to give it a sporty parallelogram feel. + - **Body** — 2–3 sentence paragraph, left-aligned, body font. + - **Primary CTA** — solid pill or block button. One only. + - **Specs grid** — 2×2 grid of (big number + unit + label) callouts using + the display font for the numbers. + - **Footer** — wordmark, address line, unsubscribe + view-in-browser links. +4. **Write** a single HTML document: + - `` through ``, CSS inline. + - Center the column with `margin: 0 auto`. Set `body { background: }` + so the email-on-page metaphor reads. + - No external images — use inline SVG or DS-tinted gradient blocks for the + product photo. + - `data-ocd-id` on the masthead, hero, headline, CTA, specs. +5. **Self-check**: + - Email reads top to bottom in 8–10 seconds. + - One CTA. Accent appears at most twice (eyebrow + CTA, or headline word). + - Looks legible on a 480px window (column reflows, type drops one step). + +## Output contract + +Emit between `` tags: + +``` + + +... + +``` + +One sentence before the artifact, nothing after. diff --git a/skills/email-marketing/example.html b/skills/email-marketing/example.html new file mode 100644 index 0000000..7efbdb5 --- /dev/null +++ b/skills/email-marketing/example.html @@ -0,0 +1,159 @@ + + + + + + SPORT TEST — Meet the Axis Pro + + + +
      +
      +
      + + SPORT TEST + EST · 2024 +
      + +
      + +
      +
      — SPORT TEST
      + +
      — EMBER FLARE
      +
      DROP 04 · 04—2026
      +
      + +
      +
      NEW · MAX-CUSHION TRAINER · EMBER FLARE
      +

      + Meet the
      + Axis Pro.
      + A sneaker that runs. +

      +

      A plush, gel-cushioned trainer wrapped in a painterly flame-knit upper. Built for long days on the road, café runs, and everything between — softer underfoot, louder on the outside. Limited first drop in Ember Flare.

      + Shop the Axis Pro +
      + +
      +
      SPECIFICATIONS · WOMEN'S
      +
      +
      +
      7.4OZ
      +
      Weight (women's US 8)
      +
      +
      +
      34MM
      +
      Max-cushion stack at the heel
      +
      +
      +
      8MM
      +
      Heel-to-toe drop for low-impact landing
      +
      +
      +
      Gel-02
      +
      Heel & forefoot gel shock pods
      +
      +
      +
      + +
      +
      +
      SPORT TEST
      +
      118 Stillman St · Brooklyn NY 11211
      + +
      +
      © 2026 SPORT TEST
      ALL RIGHTS RESERVED
      +
      +
      + + diff --git a/skills/eng-runbook/SKILL.md b/skills/eng-runbook/SKILL.md new file mode 100644 index 0000000..d958403 --- /dev/null +++ b/skills/eng-runbook/SKILL.md @@ -0,0 +1,51 @@ +--- +name: eng-runbook +description: | + An engineering runbook — service overview, alerts table, dashboards + links, common procedures with copy-pasteable commands, on-call rotation, + and an incident-response checklist. Use when the brief mentions + "runbook", "ops doc", "on-call guide", "SRE doc", or "运维手册". +triggers: + - "runbook" + - "ops doc" + - "on-call" + - "sre doc" + - "service runbook" + - "运维手册" +ocd: + mode: prototype + platform: desktop + scenario: engineering + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Write a runbook for our auth service — alerts, dashboards, common procedures, on-call rotation." +--- + +# Engineering Runbook Skill + +Produce a single-page engineering runbook. + +## Workflow + +1. Read DESIGN.md. +2. Identify the service from the brief. +3. Layout: + - Header: service name, owner team, severity tier, version. + - Service summary paragraph + dependency list. + - Alerts table: alert name / severity / what it means / first response. + - Dashboards & links list. + - Common procedures block (3–4) with code blocks (deploy, rollback, rotate keys). + - On-call rotation table (week / primary / secondary / backup). + - Incident response checklist (5 numbered steps). +4. One inline ` + + +
      +
      +
      +
      Northwind / Identity / Auth
      +

      auth-service

      +
      Owned by @identity-platform · v4.7.2 · Last reviewed 14 Oct 2025
      +
      + Tier 0 · production-critical +
      + +
      +

      01Service summary

      +
      +
      +

      auth-service issues, validates, and revokes session tokens for every Northwind product surface — web, mobile, and the public API. It owns the password store, the TOTP/WebAuthn enrollments, and the audit-log writer for all auth events.

      +

      If auth-service is down, customers cannot log in or refresh sessions. Existing valid sessions continue to work for their TTL (15 minutes) but no new auth happens.

      +
      +
      +

      Dependencies

      +
        +
      • Postgres · auth-dbhealthy
      • +
      • Redis · session-cachehealthy
      • +
      • KMS · auth-keyringhealthy
      • +
      • SES · transactionaldegraded
      • +
      • Pager · oncall.northwindhealthy
      • +
      +
      +
      +
      + +
      +

      02Alerts you might wake up to

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      AlertSeverityWhat it meansFirst response
      auth.login_5xx_rate > 1%SEV-1Login endpoint returning errors. Customers are locked out.Check Postgres + Redis dashboards. Roll back last deploy if < 30 min old.
      auth.token_refresh_lag_p95 > 800msSEV-2Refresh path is slow. Web app starts to feel sluggish.Inspect Redis CPU + connection count. Scale read replicas if needed.
      auth.signup_failure > 10/minSEV-2New signups are failing. Often SES bounces or SMTP auth.Check SES bounce rate. Failover transactional queue to backup region.
      auth.kms_signing_errors > 0SEV-1KMS can't sign session tokens. New logins fail; existing sessions OK.Page the security team. Do not roll keys without a security engineer.
      auth.audit_writer_backlog > 5kSEV-3Audit log writer is falling behind. Compliance impact.Drain manually. Open a ticket; not a wake-up.
      +
      + +
      +

      03Common procedures

      +
      +
      +

      Deploy a new version

      Use during business hours
      +

      Deploys are blue/green. The script waits for two consecutive healthchecks before promoting traffic.

      +
      # Deploy auth-service v4.7.3 to production
      +$ nw deploy auth-service --tag v4.7.3 --env production
      +
      +# Wait for two consecutive healthchecks (~90 s), then promote.
      +$ nw deploy promote auth-service --env production
      +→ traffic shifted: 10% / 50% / 100%
      +
      +
      +

      Roll back to last known good

      Use when error rate > 1% post-deploy
      +
      # Rolls back to the previously promoted version, no rebuild.
      +$ nw deploy rollback auth-service --env production
      +→ rolled back to v4.7.2 in 38 s
      +
      +
      +

      Rotate signing keys

      Schedule with security; never solo
      +
      # 1. Generate the new signing key in KMS
      +$ nw kms create-key --alias auth-signing-$(date +%Y%m%d)
      +
      +# 2. Mark the new key as the primary; old key remains valid for 24h
      +$ nw kms set-primary auth-signing --key <arn>
      +
      +# 3. After 24h, schedule deletion of the previous key
      +$ nw kms schedule-deletion auth-signing --key <old-arn> --days 30
      +
      +
      +

      Drain audit-log backlog

      Use when audit_writer_backlog alert fires
      +
      $ nw exec auth-service -- bin/audit-drain --batch 5000
      +→ drained 4,812 entries in 12 s; backlog now 0
      +
      +
      +
      + +
      +

      04On-call rotation · this month

      + + + + + + + + +
      WeekPrimarySecondaryBackup (escalation)
      Oct 27 – Nov 02Devon ParkPriya BanerjeeSasha Lin
      Nov 03 – Nov 09Caleb RennerDevon ParkSasha Lin
      Nov 10 – Nov 16Priya BanerjeeCaleb RennerMira Reddy
      Nov 17 – Nov 23Sasha LinPriya BanerjeeMira Reddy
      +
      + +
      +

      05Incident response — first 30 minutes

      +
      +
      +
      1
      +

      Acknowledge the page within 5 min.

      Type /ack in #incidents-auth. The bot stops re-paging and tags the on-call.

      +
      +
      +
      2
      +

      Open the incident channel.

      Run /incident open auth-service "<short title>". Slack bot creates a dedicated channel and pages the secondary.

      +
      +
      +
      3
      +

      Post a status snapshot.

      Customer-impact in one line, what you know, what you're checking next. Re-post every 10 minutes.

      +
      +
      +
      4
      +

      Mitigate before you diagnose.

      If a recent deploy is suspect, roll back. If KMS is degraded, fail open is never the answer for auth — escalate to security.

      +
      +
      +
      5
      +

      Hand off or stand down.

      If you can't resolve in 30 min, hand to the secondary. When healthy, close with /incident close; postmortem is owed within 5 business days.

      +
      +
      +
      + +
      + Northwind Identity Platform · runbook v3.2 + Source: ops-docs/auth-service.md +
      +
      + + diff --git a/skills/finance-report/SKILL.md b/skills/finance-report/SKILL.md new file mode 100644 index 0000000..e60a40d --- /dev/null +++ b/skills/finance-report/SKILL.md @@ -0,0 +1,60 @@ +--- +name: finance-report +description: | + Quarterly / monthly financial report — masthead with KPIs, revenue and + burn charts, P&L summary table, top-line highlights, and an outlook + paragraph. Use when the brief mentions "financial report", "Q3 report", + "MRR review", "P&L", or "财报". +triggers: + - "financial report" + - "finance report" + - "quarterly report" + - "p&l" + - "mrr review" + - "财报" + - "财务报告" +ocd: + mode: prototype + platform: desktop + scenario: finance + featured: 10 + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Build me a Q3 financial report for an early-stage SaaS — MRR, burn, gross margin, top accounts." +--- + +# Finance Report Skill + +Produce a single-screen financial report in one self-contained HTML file. + +## Workflow + +1. **Read the active DESIGN.md.** Tables, KPI cards, and chart strokes use + palette tokens — never invent new ones. +2. **Classify** the period (monthly / quarterly / yearly) and entity + (startup, division, project) from the brief. If unspecified, assume a + quarterly SaaS report and pick believable numbers. +3. **Layout** the page in this order: + - Masthead: company / period / "Confidential — Finance" badge. + - Headline KPI strip (4 cards): Revenue, Net new MRR, Gross margin, Cash runway. + - Revenue trend chart (inline SVG line + area). + - Cost breakdown chart (inline SVG bar) with a 2–3 bullet caption. + - P&L summary table (Revenue / Gross profit / Opex / Net) with current vs prior period. + - Top accounts table with logo placeholders, plan, ARR, status badge. + - Outlook paragraph + footer with author + signature line. +4. **Write** one self-contained HTML doc (CSS in one inline ` + + +
      +
      +
      +
      Northwind Trading · Q3 FY25
      +
      Quarterly Financial Report
      +
      Prepared by Finance · Issued 14 October 2025
      +
      +
      Confidential
      +
      + +

      Q3 closed ahead of plan on revenue and gross margin, with cash runway extending to 27 months on the back of a leaner cost base. Mid-market and enterprise both expanded; SMB churn remains the watch item heading into Q4.

      + +

      Headline KPIs

      +
      +
      +
      Revenue
      +
      $8.42M
      +
      ▲ 14.6% QoQ
      +
      +
      +
      Net new MRR
      +
      $184k
      +
      ▲ 22.0% QoQ
      +
      +
      +
      Gross margin
      +
      82%
      +
      ▲ 3 pp YoY
      +
      +
      +
      Cash runway
      +
      27 mo
      +
      ▲ 4 mo QoQ
      +
      +
      + +

      Revenue & costs

      +
      +
      +

      Revenue · trailing 12 months

      +
      USD millions, monthly
      +
      + + + + + + + + + + + + +
      + Revenue + Plan +
      +
      +
      +
      +

      Operating costs

      +
      USD thousands, Q3
      +
      +
      R&D
      $1.42M
      +
      Sales & GTM
      $1.10M
      +
      G&A
      $660k
      +
      Marketing
      $510k
      +
      Infrastructure
      $330k
      +
      +
      +
      + +

      P&L summary

      + + + + + + + + + + + + + + + + + + +
      Line itemQ3 FY25Q2 FY25Δ QoQQ3 FY24Δ YoY
      Revenue$8.42M$7.34M+14.6%$5.92M+42.2%
      Cost of revenue($1.51M)($1.46M)+3.4%($1.18M)+28.0%
      Gross profit$6.91M$5.88M+17.5%$4.74M+45.8%
      Operating expenses($4.02M)($4.18M)−3.8%($3.66M)+9.8%
      Operating income$2.89M$1.70M+70.0%$1.08M+167.5%
      + +

      Top accounts

      + + + + + + + + + + + + + + + + + +
      CustomerPlanRegionARRStatus
      Pioneer RoboticsEnterpriseEMEA$612kRenewed
      Atlas CooperativeEnterpriseAPAC$486kExpanded
      Foundry GroupTeam PlusNA$320kIn renewal
      Voltage Co.EnterpriseNA$298kRenewed
      Lattice HealthTeam PlusEMEA$214kAt risk
      + +

      Outlook · Q4

      +
      +
      "We're entering Q4 with the strongest pipeline coverage of the year — 3.4× plan — and the operating leverage to convert it without expanding the cost base."
      +
      + Mira Okafor, CFO + We expect revenue of $9.1–9.4M, net new MRR of $200–220k, and gross margin holding above 80%. The two open items are SMB churn (we'll publish a recovery plan with the November update) and the EMEA infra migration, which moves to GA in mid-November. +
      +
      + +
      + Northwind Trading · Q3 FY25 · Internal use only + Page 1 of 1 +
      +
      + + diff --git a/skills/gamified-app/SKILL.md b/skills/gamified-app/SKILL.md new file mode 100644 index 0000000..489ac32 --- /dev/null +++ b/skills/gamified-app/SKILL.md @@ -0,0 +1,107 @@ +--- +name: gamified-app +description: | + A multi-frame gamified mobile-app prototype — three phone frames on a dark + showcase stage. Frame 1: cover / poster, Frame 2: today's quests with XP + ribbons and a level bar, Frame 3: quest detail. Vivid quest tiles, level + ribbon, bottom tab bar. Use when the brief asks for a "gamified app", + "habit tracker", "RPG-style life app", "level-up app", "daily quests", + "XP / streak app", or "ELI5-style explainer app". +triggers: + - "gamified app" + - "habit tracker" + - "rpg app" + - "level up app" + - "daily quests" + - "xp app" + - "streak app" + - "life management app" + - "游戏化" + - "习惯打卡" +ocd: + mode: prototype + platform: mobile + scenario: personal + featured: 4 + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Design a gamified life-management app — multi-screen mobile prototype: cover poster, today's quests with XP, and a quest detail. ‘Daily quests for becoming a better human.’" +--- + +# Gamified App Skill + +Produce a multi-screen mobile prototype on a single dark showcase page. +Three phone frames side-by-side, each one its own moment in the journey. + +## Workflow + +1. **Read the active DESIGN.md** (injected above). For gamified apps, lean + on bold display type for headlines and a brighter, broader palette than + most products — quests look like quests because the colors do. +2. **Pick the brand + value prop** from the brief. Generate real quest + names (e.g. "Body — 20-min strength: pushups & planks", "Read — Four + Thousand Weeks", "Listen — Huberman Lab · Sleep Architecture", + "Nourish — Cook a high-protein lunch", "Mind — 10-min focus + meditation", "Watch — The Bear · S3 E4"). +3. **Stage** — full-bleed dark page (near-black `#0e0d0c` or DS dark token) + with a soft top spotlight gradient. Above the phones, a small caption + row: "HI-FI PROTOTYPE · IPHONE" left, brand wordmark right, both in mono. +4. **Phones** — three 360×780 phone frames in a horizontal row (wraps to + stack on narrow viewports). Each phone: + - 12px black bezel, 44px corner radius, dynamic-island notch. + - Status bar (time / signal / battery). + - Phone-specific content (below). + - Bottom tab bar with 5 icons (Today, Library, Stats, ⊕ central CTA, + Profile). Active tab in accent. +5. **Phone 1 — cover poster (sales/value prop)**: + - Status bar. + - HI-FI PROTOTYPE · IPHONE eyebrow. + - Big display headline ("Daily quests for becoming a better human."), + accent on "becoming". + - 1–2 sentence body in muted serif/sans. + - Mono tip line ("Tap quests to open detail. Toggle [theme] in the + toolbar to switch theme & layout.") + - Subtle scrolling teaser of the next screen at the bottom edge. +6. **Phone 2 — today's quests dashboard** (the hero screen): + - Greeting "Good morning, Sam" + small XP-bell ringing. + - Level ribbon — "LV 14 · Level 14 · 1648 / 2480 XP" with a progress + bar inside a glassmorphic ribbon. + - Sub-line: "8 quests waiting · earn 430 XP today". + - 3×2 grid of quest tiles. Each tile: rounded corner, pastel accent + color, glyph chip in top-left, title, mini-meta line, "+NN XP" pill + in bottom-right. + - Bottom tab bar. +7. **Phone 3 — quest detail**: + - Back arrow + screen title ("Quest"). + - Hero block with the quest's accent color, big serif quest title + ("Body — strength"), short narrative body, "REWARD +90 XP" stamp. + - Steps checklist (3–4 micro-tasks, one done, two pending). + - Big primary CTA "Start quest" pill at the bottom in accent. +8. **Write** a single HTML document: + - `` through ``, CSS inline. + - All in CSS — no images. Use `linear-gradient` and inline SVG glyphs + for tile chips and tab icons. + - `data-ocd-id` on stage, each phone, each frame's regions. +9. **Self-check**: + - Three frames, each with a distinct purpose. Not three copies of the + same screen. + - Tile colors don't overpower — each quest tile uses a different pastel + against the same neutral surface. + - Reads as gamified and adult — playful, not childish. + +## Output contract + +Emit between `` tags: + +``` + + +... + +``` + +One sentence before the artifact, nothing after. diff --git a/skills/gamified-app/example.html b/skills/gamified-app/example.html new file mode 100644 index 0000000..c2ad03e --- /dev/null +++ b/skills/gamified-app/example.html @@ -0,0 +1,292 @@ + + + + + + Level — daily quests for becoming a better human + + + + + + +
      + HI-FI PROTOTYPE · IPHONE + level. + 3 SCREENS · LIGHT MODE +
      + +
      + + +
      +
      +
      9:41·· 5G · 100%
      +
      + HI-FI PROTOTYPE · IPHONE +

      Daily quests for becoming a better human.

      +

      Level turns the things you already know you should do — exercise, read, reflect, call a friend — into a daily quest log. Finish them, earn XP, watch your classes level up.

      +

      Tap quests to open detail. Complete the 6th quest to trigger the level-up moment. Toggle [theme] in the toolbar to switch theme & layout.

      +
      NEXT — TODAY'S QUESTS
      +
      +
      +
      + + +
      +
      +
      9:41·· 5G · 100%
      +
      +

      Good morning, Sam

      +
      ×3
      +
      +
      +
      14
      +
      LEVEL
      Level 14
      +
      1648 / 2480
      +
      +
      +
      8 quests waiting · earn 430 XP today
      + +
      +
      +
      B
      +

      Body

      +
      20-min strength: pushups & planks
      + +90 +
      +
      +
      R
      +

      Read

      +
      Four Thousand Weeks
      + +60 +
      +
      +
      L
      +

      Listen

      +
      Huberman Lab — Sleep Architecture
      + +50 +
      +
      +
      N
      +

      Nourish

      +
      Cook a high-protein lunch
      + +70 +
      +
      +
      M
      +

      Mind

      +
      10-min focus meditation
      + +40 +
      +
      +
      W
      +

      Watch

      +
      The Bear · S3 E4
      + +30 +
      +
      + +
      +
      Today
      +
      Library
      +
      +
       
      +
      Stats
      +
      Profile
      +
      +
      +
      + + +
      +
      +
      9:41·· 5G · 100%
      +
      QUEST · 03 / 08
      +
      + +90 XP + — BODY · STRENGTH +

      20 minutes that change Wednesday.

      +

      A short, repeatable strength block — pushups, planks, and one wildcard. No equipment. Sam, you've finished this 11 times this month.

      +
      +
      +

      Today's micro-tasks

      +
      Roll out the mat
      +5 XP
      +
      3 × 12 pushups
      +30 XP
      +
      3 × 45s plank
      +30 XP
      +
      Wildcard: lunges
      +25 XP
      +
      +
      Start quest
      +
      +
      Today
      +
      Library
      +
      +
       
      +
      Stats
      +
      Profile
      +
      +
      +
      + +
      + + diff --git a/skills/guizang-ppt/LICENSE b/skills/guizang-ppt/LICENSE new file mode 100644 index 0000000..a638b33 --- /dev/null +++ b/skills/guizang-ppt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 op7418 (歸藏) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/skills/guizang-ppt/README.en.md b/skills/guizang-ppt/README.en.md new file mode 100644 index 0000000..a8f467e --- /dev/null +++ b/skills/guizang-ppt/README.en.md @@ -0,0 +1,119 @@ +# Magazine Web PPT · Editorial-Style Web Slide Deck Skill + +A [Claude Code / Claude Agent Skills](https://agentskills.io/) skill that generates **single-file HTML horizontal-swipe decks** with an "**editorial magazine × electronic ink**" aesthetic — picture *Monocle* with code stitched in. + +> Distilled by [Guizang](https://x.com/op7418) from offline talks like "One-Person Company: Organizations Folded by AI" and "A New Way of Working." Every pitfall hit during those decks is logged in `checklist.md`. + +![Magazine Web PPT preview](https://github.com/user-attachments/assets/5dc316a2-401c-4e37-9123-ea081b6ae470) + +## What you get + +- 🖋 **Three-tier type system**: serif for headlines, sans-serif for body, mono for metadata +- 🌊 **WebGL fluid / dispersion backgrounds** — visible on hero pages, restrained on body pages +- 📐 **Horizontal swipe navigation**: ← → arrows / scroll wheel / touch swipe / bottom dots / ESC for index +- 🎨 **5 curated theme presets**: Ink Classic / Indigo Porcelain / Forest Ink / Kraft Paper / Dune +- 🧩 **10 page layouts**: cover, act divider, big numbers, lead image + text, image grid, pipeline, hero question, big quote, before/after, image + text mix +- 📄 **Single HTML file** — no build, no server, open directly in the browser + +## Fits / Doesn't fit + +**✅ Fits**: offline talks, industry keynotes, private salons, AI product launches, demo day, presentations with strong personal voice + +**❌ Doesn't fit**: data-heavy tables, training decks (density too low), multi-user collaborative editing (static HTML) + +## Install + +### Option 1: Paste this to an AI (recommended) + +> Install the `guizang-ppt-skill` Claude Code skill for me. Steps: +> +> 1. Make sure `~/.claude/skills/` exists (create if not) +> 2. Run `git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt` +> 3. Verify: `ls ~/.claude/skills/magazine-web-ppt/` should show `SKILL.md`, `assets/`, `references/` +> 4. Tell me when done. Later, saying things like "make me a magazine-style deck" will trigger this skill. + +Paste the block above into Claude Code / Cursor / any AI agent with shell access and it handles the install. + +### Option 2: Manual CLI + +```bash +git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt +``` + +### How to trigger it + +Once installed, Claude Code auto-detects the skill. Trigger phrases: + +- "Make me a magazine-style deck" +- "Generate a horizontal swipe deck" +- "Editorial magazine style presentation" +- "Electronic ink slides for my talk" + +## Workflow + +The skill is a structured 6-step flow; Claude walks you through each: + +1. **Clarify intent** — 6-question checklist: audience, duration, source material, images, theme, hard constraints +2. **Copy template** — `assets/template.html` → project folder, update ``, swap theme vars +3. **Fill content** — pick from 10 layout skeletons, paste, edit copy (with class-name pre-flight + theme rhythm plan) +4. **Self-check** — match against `references/checklist.md`; P0 issues must all pass +5. **Preview** — open the HTML in a browser +6. **Iterate** — use inline styles to tune font size, height, spacing + +Full spec in [`SKILL.md`](./SKILL.md). + +## Directory + +``` +magazine-web-ppt/ +├── SKILL.md ← main skill file: workflow, principles, common mistakes +├── README.md ← Chinese README +├── README.en.md ← this file +├── assets/ +│ └── template.html ← runnable seed HTML (CSS + WebGL + swipe JS pre-wired) +└── references/ + ├── components.md ← component catalog (type, color, grid, icons, callout, stat, pipeline) + ├── layouts.md ← 10 layout skeletons (paste-ready) + ├── themes.md ← 5 theme presets (pick, don't customize) + └── checklist.md ← quality checklist (P0 / P1 / P2 / P3 tiers) +``` + +## Theme presets + +Pick from `references/themes.md`. **Custom hex values are not allowed** — protecting the aesthetic matters more than freedom of choice. + +| Theme | Best for | +|------|---------| +| 🖋 Ink Classic | general default, commercial launches, when in doubt | +| 🌊 Indigo Porcelain | tech / research / AI / technical keynotes | +| 🌿 Forest Ink | nature / sustainability / culture / non-fiction | +| 🍂 Kraft Paper | nostalgic / humanist / literary / indie zines | +| 🌙 Dune | art / design / creative / gallery | + +Switching themes only requires replacing the 6 variables at the top of `template.html`'s `:root{}` block — all other CSS flows through `var(--...)`. + +## Core design principles + +1. **Restraint over flash** — WebGL backgrounds only bleed through on hero pages +2. **Structure over decoration** — information hierarchy via type size + typeface + grid whitespace, not shadows or floating cards +3. **Images are first-class citizens** — crop only from the bottom; top and sides stay intact +4. **Rhythm lives on hero pages** — hero / non-hero alternation keeps the eye from fatiguing +5. **Terms stay consistent** — Skills is Skills; no mix-and-match translations + +## Visual references + +- [*Monocle*](https://monocle.com) magazine layouts +- YC Garry Tan — "Thin Harness, Fat Skills" +- Guizang's offline talk deck series + +## Contributing + +Bugs, layout issues, new layout requests — Issues and PRs welcome. Prioritize: + +- Add new classes to `template.html` first; don't let `layouts.md` reference undefined classes +- Log pitfalls into `checklist.md` at the matching P0 / P1 / P2 / P3 tier +- New theme colors go into `themes.md` with a recommended use case + +## License + +MIT © 2026 [op7418](https://github.com/op7418) diff --git a/skills/guizang-ppt/README.md b/skills/guizang-ppt/README.md new file mode 100644 index 0000000..5880489 --- /dev/null +++ b/skills/guizang-ppt/README.md @@ -0,0 +1,120 @@ +# Magazine Web PPT · 电子杂志风网页 PPT Skill + +> 🌏 **English version: [README.en.md](./README.en.md)** + +一个 [Claude Code / Claude Agent Skills](https://agentskills.io/) 技能,用于生成**单文件 HTML 横向翻页 PPT**,视觉基调是"**电子杂志 × 电子墨水**"——像 *Monocle* 贴上了代码的样子。 + +> 由 [歸藏](https://x.com/op7418) 在"一人公司:被 AI 折叠的组织"、"一种新的工作方式"等线下分享中沉淀而成,踩过的每一个坑都写进了 `checklist.md`。 + +![Magazine Web PPT 效果展示](https://github.com/user-attachments/assets/5dc316a2-401c-4e37-9123-ea081b6ae470) + +## 效果 + +- 🖋 **衬线大标题 + 非衬线正文 + 等宽元数据**的三级字体分工 +- 🌊 **WebGL 流体/色散背景**,hero 页可见,正文页克制 +- 📐 **横向左右翻页**:键盘 ← → / 滚轮 / 触屏滑动 / 底部圆点 / ESC 索引 +- 🎨 **5 套主题色预设**:墨水经典 / 靛蓝瓷 / 森林墨 / 牛皮纸 / 沙丘 +- 🧩 **10 种页面布局**:开场封面、章节幕封、数据大字报、左文右图、图片网格、Pipeline、悬念问题、大引用、Before/After 对比、图文混排 +- 📄 **单文件 HTML**:不需要构建、不需要服务器,浏览器直接打开 + +## 适合 / 不适合 + +**✅ 合适**:线下分享 / 行业内部讲话 / 私享会 / AI 产品发布 / demo day / 带强烈个人风格的演讲 + +**❌ 不合适**:大段表格数据 / 培训课件(信息密度不够)/ 需要多人协作编辑(静态 HTML) + +## 安装 + +### 方式一:把下面这段话直接发给 AI(推荐) + +> 帮我安装 `guizang-ppt-skill` 这个 Claude Code skill。请按下面步骤做: +> +> 1. 确保 `~/.claude/skills/` 目录存在(不存在就创建) +> 2. 执行 `git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt` +> 3. 验证:`ls ~/.claude/skills/magazine-web-ppt/` 应该看到 `SKILL.md`、`assets/`、`references/` 三项 +> 4. 告诉我安装好了,之后我说"做一份杂志风 PPT"之类的话就会触发这个 skill + +把这段话复制粘贴给 Claude Code / Cursor / 任何有 shell 权限的 AI Agent,它会自动完成安装。 + +### 方式二:手动命令行 + +```bash +git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt +``` + +### 触发方式 + +装好后,Claude Code 会在对话里自动发现并调用这个 skill。触发关键词: + +- "帮我做一份杂志风 PPT" +- "生成一个 horizontal swipe deck" +- "editorial magazine style presentation" +- "electronic ink 风格演讲 slides" + +## 使用流程 + +Skill 本身是结构化的 6 步工作流,Claude 会逐步引导: + +1. **需求澄清** — 6 问清单:受众、时长、素材、图片、主题色、硬约束 +2. **拷贝模板** — `assets/template.html` → 项目目录,改 `<title>`,换主题色 +3. **填充内容** — 从 10 种 layout 骨架里挑、粘、改文案(先做类名预检 + 主题节奏规划) +4. **自检** — 对照 `references/checklist.md`,P0 级问题必须全过 +5. **预览** — 浏览器直接打开 +6. **迭代** — inline style 改字号/高度/间距 + +详细说明见 [`SKILL.md`](./SKILL.md)。 + +## 目录结构 + +``` +magazine-web-ppt/ +├── SKILL.md ← Skill 主文件:工作流、原则、常见错误 +├── README.md ← 本文件 +├── assets/ +│ └── template.html ← 完整可运行的种子 HTML(CSS + WebGL + 翻页 JS 全配好) +└── references/ + ├── components.md ← 组件手册(字体、色、网格、图标、callout、stat、pipeline) + ├── layouts.md ← 10 种页面布局骨架(可直接粘贴) + ├── themes.md ← 5 套主题色预设(只能选不能自定义) + └── checklist.md ← 质量检查清单(P0 / P1 / P2 / P3 分级) +``` + +## 主题色预设 + +从 `references/themes.md` 里选一套——**不允许自定义 hex 值**,保护美学比给自由更重要。 + +| 主题 | 适合场景 | +|------|---------| +| 🖋 墨水经典 | 通用默认、商业发布、不知道选啥 | +| 🌊 靛蓝瓷 | 科技 / 研究 / AI / 技术发布会 | +| 🌿 森林墨 | 自然 / 可持续 / 文化 / 非虚构 | +| 🍂 牛皮纸 | 怀旧 / 人文 / 文学 / 独立杂志 | +| 🌙 沙丘 | 艺术 / 设计 / 创意 / 画廊 | + +切换主题只需替换 `template.html` 开头 `:root{}` 里的 6 行变量,其他 CSS 全走 `var(--...)`。 + +## 核心设计原则 + +1. **克制优于炫技** — WebGL 背景只在 hero 页透出 +2. **结构优于装饰** — 信息靠字号 + 字体对比 + 网格留白,不用阴影和浮动卡片 +3. **图片是第一公民** — 只裁底部,顶部和左右完整 +4. **节奏靠 hero 页** — hero / non-hero 交替,才不累眼睛 +5. **术语统一** — Skills 就是 Skills,不中英混译 + +## 视觉参考 + +- [*Monocle*](https://monocle.com) 杂志的版式 +- YC Garry Tan "Thin Harness, Fat Skills" +- 歸藏线下分享 PPT 系列 + +## 贡献 + +Bug、排版问题、新布局需求——欢迎开 Issue 或 PR。改动请优先: + +- 在 `template.html` 里补类,不要让 layouts.md 使用未定义的类 +- 把踩过的坑写到 `checklist.md` 对应的 P0 / P1 / P2 / P3 级别 +- 新主题色进 `themes.md` 并给出适合的场景 + +## License + +MIT © 2026 [op7418](https://github.com/op7418) diff --git a/skills/guizang-ppt/SKILL.md b/skills/guizang-ppt/SKILL.md new file mode 100644 index 0000000..535d775 --- /dev/null +++ b/skills/guizang-ppt/SKILL.md @@ -0,0 +1,314 @@ +--- +name: magazine-web-ppt +description: 生成"电子杂志 × 电子墨水"风格的横向翻页网页 PPT(单 HTML 文件),含 WebGL 流体背景、衬线标题 + 非衬线正文、章节幕封、数据大字报、图片网格等模板。当用户需要制作分享 / 演讲 / 发布会风格的网页 PPT,或提到"杂志风 PPT"、"horizontal swipe deck"、"editorial magazine"、"e-ink presentation"时使用。 +triggers: + - "ppt" + - "deck" + - "slides" + - "presentation" + - "magazine" + - "杂志" + - "杂志风 PPT" + - "horizontal swipe" + - "horizontal swipe deck" + - "editorial magazine" + - "e-ink presentation" + - "网页 PPT" + - "发布会" + - "分享 PPT" +ocd: + mode: deck + scenario: marketing + featured: 9 + default_for: deck + upstream: "https://github.com/op7418/guizang-ppt-skill" + preview: + type: html + entry: index.html + design_system: + requires: false + example_prompt: "帮我做一份杂志风的 PPT —— 关于'一人公司 · 被 AI 折叠的组织',25 分钟分享会,目标受众是设计师 + 创业者。先推荐一个方向(Monocle / WIRED / Kinfolk / Domus / Lab)让我选。" +--- + +# Magazine Web Ppt + +## 这个 Skill 做什么 + +生成一份**单文件 HTML**的横向翻页 PPT,视觉基调是: + +- **电子杂志 + 电子墨水**混血风格 +- **WebGL 流体 / 等高线 / 色散背景**(hero 页可见) +- **衬线标题(Noto Serif SC + Playfair Display)+ 非衬线正文(Noto Sans SC + Inter)+ 等宽元数据(IBM Plex Mono)** +- **Lucide 线性图标**(不用 emoji) +- **横向左右翻页**(键盘 ← →、滚轮、触屏滑动、底部圆点、ESC 索引) +- **主题平滑插值**:翻到 hero 页时颜色和 shader 柔顺过渡 + +这个 skill 的美学不是"商务 PPT",也不是"消费互联网 UI"——它像 *Monocle* 杂志贴上了代码后的样子。 + +## 何时使用 + +**合适的场景**: +- 线下分享 / 行业内部讲话 / 私享会 +- AI 新产品发布 / demo day +- 带有强烈个人风格的演讲 +- 需要"一次做完,不用翻页工具"的网页版 slides + +**不合适的场景**: +- 大段表格数据、图表叠加(用常规 PPT) +- 培训课件(信息密度不够) +- 需要多人协作编辑(这是静态 HTML) + +## 工作流 + +### Step 0 · 选方向(Direction · 必做的第一步) + +**在问 6 个澄清问题之前,先让用户在 5 个 magazine 方向里挑一个**。每个方向都把"主题色 / 推荐 layout / chrome 风格 / 推荐 slide 数"打包好,挑了方向就回答掉一半澄清问题。 + +打开 `references/styles.md`,**整段拷过来**给用户看 5 个方向的 1-line summary,然后让他选: + +``` +1. Monocle Editorial · 国际杂志风 ✦ 默认 +2. WIRED Tech · 数据 + 工程 +3. Kinfolk Slow · 慢生活 / 人文 +4. Domus Architectural · 建筑 / 空间感 +5. Lab / Reference · 学术 + 工艺手册 +``` + +如果用户说"不知道,你推荐"——**默认推 Monocle Editorial**,因为它失败概率最低。如果用户提到"AI / benchmark / 技术发布"——推 WIRED;"读书 / 私享 / 朋友圈"——推 Kinfolk;"设计 / 建筑 / portfolio"——推 Domus;"研究 / 学术 / 方法论"——推 Lab。 + +挑完方向后,在项目目录下创建或更新 `项目记录.md`,第一行写清方向 + 主题色 + 受众 + 时长(模板见 `styles.md` 末尾)。**全程不要换方向**——半路换 = 前面全废。 + +### Step 1 · 需求澄清(**动手前必做**) + +**如果用户已经给了完整的大纲 + 图片**,可以跳过直接进 Step 2。 + +**如果用户只给了主题或一个模糊想法**,用这 6 个问题逐个对齐后再动手。不要基于猜测就开始写 slide——一旦结构定错,后期翻修代价很高: + +#### 6 问澄清清单 + +> 第 5 题已在 Step 0 选方向时一并回答(方向→主题色)。下面的 5 题里,第 5 题留白即可。 + +| # | 问题 | 为什么要问 | +|---|------|-----------| +| 1 | **受众是谁?分享场景?**(行业内部 / 商业发布 / demo day / 私享会) | 决定语言风格和深度 | +| 2 | **分享时长?** | 15 分钟 ≈ 10 页,30 分钟 ≈ 20 页,45 分钟 ≈ 25-30 页(每个方向的推荐范围见 `styles.md`) | +| 3 | **有没有原始素材?**(文档 / 数据 / 旧 PPT / 文章链接) | 有素材就基于素材,没有就帮他搭 | +| 4 | **有没有图片?放在哪?** | 详见下方"图片约定" | +| 5 | ~~**想要哪套主题色?**~~ | ✓ 已在 Step 0 由方向决定 | +| 6 | **有没有硬约束?**(必须包含 XX 数据 / 不能出现 YY) | 避免返工 | + +#### 大纲协助(如果用户没有大纲) + +用"叙事弧"模板搭骨架,再填内容: + +``` +钩子(Hook) → 1 页 : 抛一个反差 / 问题 / 硬数据让人停下来 +定调(Context) → 1-2 页 : 说明背景 / 你是谁 / 为什么讲这个 +主体(Core) → 3-5 页 : 核心内容,用 Layout 4/5/6/9/10 穿插 +转折(Shift) → 1 页 : 打破预期 / 提出新观点 +收束(Takeaway) → 1-2 页 : 金句 / 悬念问题 / 行动建议 +``` + +叙事弧 + 页数规划 + 主题节奏表(见 `layouts.md`),**三张表对齐后**再进 Step 2。 + +大纲建议保存为 `项目记录.md` 或 `大纲-v1.md`,便于后续迭代。 + +#### 图片约定(告知用户) + +在动手前向用户说清: + +- **文件夹位置**:`项目/XXX/ppt/images/` 下(和 `index.html` 同级) +- **命名规范**:`{页号}-{语义}.{ext}`,例如 `01-cover.jpg` / `03-figma.jpg` / `05-dashboard.png` + - 页号补零便于排序 + - 语义用英文,短、具体、和内容对应 +- **规格建议**: + - 单张 ≥ 1600px 宽(避免大屏模糊) + - JPG 用于照片/截图,PNG 用于透明 UI/图表 + - 总大小控制在 10MB 内(影响翻页流畅度) +- **如何替换**:保持**同名覆盖**最稳(HTML 里不用改路径);如果文件名变了,记得全局搜 `images/旧名` 改成新名 +- **没图怎么办**:和用户对齐,可以先用占位色块生成结构,等图片后期补;但要告知 layout 4/5/10 等图文混排页没图就没法验证视觉效果 + +### Step 2 · 拷贝模板 + +从 `assets/template.html` 拷贝一份到目标位置(通常是 `项目/XXX/ppt/index.html`),同时在同级建一个 `images/` 文件夹准备接图片。 + +```bash +mkdir -p "项目/XXX/ppt/images" +cp "<SKILL_ROOT>/assets/template.html" "项目/XXX/ppt/index.html" +``` + +`template.html` 是一个**完整可运行**的文件——CSS、WebGL shader、翻页 JS、字体/图标 CDN 全已预设好,只有 `<main id="deck">` 里面是 3 个示例 slide(封面、章节幕封、空白填充页)。 + +#### 2.1 · 必改占位符(**容易漏**) + +拷贝后立刻改掉以下占位符,否则浏览器 Tab 会显示"[必填] 替换为 PPT 标题"这种尴尬文字: + +| 位置 | 原始 | 需改为 | +|------|------|--------| +| `<title>` | `[必填] 替换为 PPT 标题 · Deck Title` | 实际 deck 标题(如 `一种新的工作方式 · Luke Wroblewski`) | + +每次拷贝完 template.html 第一件事:grep 一下"[必填]" 确认全部替换完。 + +#### 2.2 · 选定主题色(5 套预设 · 不允许自定义) + +本 skill **只允许从 5 套精心调配的预设里选一套**,不接受用户自定义 hex 值——颜色搭配错了画面瞬间变丑,保护美学比给自由更重要。 + +| # | 主题 | 适合 | +|---|------|------| +| 1 | 🖋 墨水经典 | 通用 / 商业发布 / 不知道选啥的默认 | +| 2 | 🌊 靛蓝瓷 | 科技 / 研究 / 数据 / 技术发布会 | +| 3 | 🌿 森林墨 | 自然 / 可持续 / 文化 / 非虚构 | +| 4 | 🍂 牛皮纸 | 怀旧 / 人文 / 文学 / 独立杂志 | +| 5 | 🌙 沙丘 | 艺术 / 设计 / 创意 / 画廊 | + +**操作**: +1. 基于内容主题推荐一套,或直接问用户选哪一套 +2. 打开 `references/themes.md`,找到对应主题的 `:root` 块 +3. **整体替换** `assets/template.html`(已拷贝版本)开头 `:root{` 块里标有"主题色"注释的那几行(`--ink` / `--ink-rgb` / `--paper` / `--paper-rgb` / `--paper-tint` / `--ink-tint`) +4. 其他 CSS 都走 `var(--...)`,无需任何其他改动 + +**硬规则**: +- 一份 deck 只用一套主题,不要中途换色 +- 不要接受用户给的任意 hex 值——委婉拒绝并展示 5 套让选 +- 不要混搭(例如 ink 取墨水经典、paper 取沙丘)——会彻底违和 + +### Step 3 · 填充内容 + +#### 3.0 · 预检:类名必须在 template.html 里有定义(**最重要**) + +**这是所有生成问题的源头**。layouts.md 的骨架使用了很多类名(`h-hero` / `h-xl` / `stat-card` / `pipeline` / `grid-2-7-5` 等),如果 `assets/template.html` 的 `<style>` 里没有对应定义,浏览器会 fallback 到默认样式——大标题变成非衬线、数据卡片挤成一团、pipeline 糊成一行、图片堆到页面底部。 + +**在写任何 slide 代码之前:** + +1. **先 Read `assets/template.html`**(至少读到 `<style>` 块末尾) +2. **对照 layouts.md 的 Pre-flight 列表**,确认你要用的每个类都在 `<style>` 里存在 +3. 如果某个类缺失:**在 template.html 的 `<style>` 里补上**,不要在每个 slide 里 inline 重写 +4. **template.html 是唯一的类名来源**——不要发明新类名,如需自定义用 `style="..."` inline + +常见容易遗漏的类(必须预先确认存在): +`h-hero` / `h-xl` / `h-sub` / `h-md` / `lead` / `kicker` / `meta-row` / `stat-card` / `stat-label` / `stat-nb` / `stat-unit` / `stat-note` / `pipeline-section` / `pipeline-label` / `pipeline` / `step` / `step-nb` / `step-title` / `step-desc` / `grid-2-7-5` / `grid-2-6-6` / `grid-2-8-4` / `grid-3-3` / `grid-6` / `grid-3` / `grid-4` / `frame` / `frame-img` / `img-cap` / `callout` / `callout-src` / `chrome` / `foot` + +#### 3.0.5 · 规划主题节奏(**和类预检同等重要**) + +**在挑布局之前**,必须先列出每一页的主题 class(`hero dark` / `hero light` / `light` / `dark`)并写到文档或草稿里对齐。详细规则看 `references/layouts.md` 开头的"主题节奏规划"一节。 + +**强制规则**: + +- 每页 section 必须带 `light` / `dark` / `hero light` / `hero dark` 之一,不要只写 `hero` +- 连续 3 页以上同主题 = 视觉疲劳,不允许 +- 8 页以上必须有 ≥1 个 `hero dark` + ≥1 个 `hero light` +- 整个 deck 不能只有 `light` 正文页,必须有 `dark` 正文页制造呼吸 +- 每 3-4 页插入 1 个 hero 页(封面/幕封/问题/大引用) + +**生成后自检**:`grep 'class="slide' index.html` 列出所有主题,人工确认节奏合理再交付。 + +#### 3.1 · 挑布局 + +**不要从零写 slide**。打开 `references/layouts.md`,里面有 10 种现成布局骨架,每种都是完整可粘贴的 `<section>` 代码块: + +| Layout | 用途 | +|---|---| +| 1. 开场封面 | 第 1 页 | +| 2. 章节幕封 | 每幕开场 | +| 3. 数据大字报 | 抛硬数据 | +| 4. 左文右图(Quote + Image) | 身份反差 / 故事 | +| 5. 图片网格 | 多图对比 / 截图实证 | +| 6. 两列流水线(Pipeline) | 工作流程 | +| 7. 悬念收束 / 问题页 | 幕末 / 收尾 | +| 8. 大引用页(Big Quote) | 衬线金句 / takeaway | +| 9. 并列对比(Before / After) | 旧模式 vs 新模式 | +| 10. 图文混排(Lead Image + Side Text) | 信息密集的图文页 | + +选对应 layout,粘过去,改文案和图片路径即可。**务必先完成 3.0 预检**。 + +#### 3.2 · 图片比例规范 + +永远用**标准比例**,不要用原图奇葩比例(如 `2592/1798`): + +| 场景 | 推荐比例 | +|------|---------| +| 左文右图 主图 | 16:10 或 4:3 + `max-height:56vh` | +| 图片网格(多图对比) | **固定 `height:26vh`**,不用 aspect-ratio | +| 左小图 + 右文字 | 1:1 或 3:2 | +| 全屏主视觉 | 16:9 + `max-height:64vh` | +| 图文混排小插图 | 3:2 或 3:4 | + +**图片绝不使用 `align-self:end`**——会滑到 cell 底被浏览器工具栏遮挡。用 grid 容器 + `align-items:start`(template 已预设)让图片贴顶即可;左列若想贴底,用 flex column + `justify-content:space-between`。 + +组件细节(字体、颜色、网格、图标、callout、stat-card 等)在 `references/components.md`。 + +### Step 4 · 对照检查清单自检 + +生成完一定要打开 `references/checklist.md`,逐项对照。里面总结了**真实迭代过程中踩过的所有坑**,P0 级别的问题(emoji、图片撑破、标题换行、字体分工)必须全部通过。 + +特别要注意的几条: + +1. **大标题必须是衬线字体**——如果显示成非衬线,99% 是 Step 3.0 预检没做,`h-hero` 类在 template.html 里缺失 +2. **图片网格里只用 `height:Nvh`,不用 `aspect-ratio`**(会撑破) +3. **图片不能堆到页面底部**——不要用 `align-self:end`,用 grid + `align-items:start`(见 Step 3.2) +4. **图片只能用标准比例**(16:10 / 4:3 / 3:2 / 1:1 / 16:9),不要复制原图的奇葩比例 +5. **中文大标题 ≤ 5 字且 `nowrap`**(避免 1 字 1 行) +6. **用 Lucide,不用 emoji** +7. **标题用衬线,正文用非衬线,元数据用等宽** + +### Step 5 · 本地预览 + +直接在浏览器打开 `index.html` 就行。macOS 下: + +```bash +open "项目/XXX/ppt/index.html" +``` + +不需要本地服务器。图片走相对路径 `images/xxx.png`。 + +### Step 6 · 迭代 + +根据用户反馈修改——模板的 CSS 已经高度参数化,90% 的调整都是改 inline style(字号 `font-size:Xvw` / 高度 `height:Yvh` / 间距 `gap:Zvh`)。 + +--- + +## 资源文件导览 + +``` +magazine-web-ppt/ +├── SKILL.md ← 你正在读 +├── assets/ +│ ├── template.html ← 完整的可运行模板(种子文件) +│ └── example-slides.html ← 9 页样例 deck(用于 Examples 预览) +└── references/ + ├── styles.md ← 5 个 magazine 方向(Monocle / WIRED / Kinfolk / Domus / Lab) + ├── components.md ← 组件手册(字体、色、网格、图标、callout、stat、pipeline...) + ├── layouts.md ← 10 种页面布局骨架(可直接粘贴) + ├── themes.md ← 5 套主题色预设(只能选不能自定义) + └── checklist.md ← 质量检查清单(P0/P1/P2/P3 分级) +``` + +**加载顺序建议**: +1. 先读完 `SKILL.md`(这个文件)了解整体 +2. **Step 0 选方向时,读 `styles.md`**——5 个方向各自打包好了主题色 + 推荐 layout + chrome 风格 +3. Step 1 需求澄清完成后,如果方向需要确认,再读 `themes.md` 看色板细节 +4. **动手前 Read `assets/template.html` 的 `<style>` 块**——这是类名的唯一来源,缺类会导致整页样式崩 +5. 读 `layouts.md` 挑布局(顶部有 Pre-flight 类名清单和主题节奏规划) +6. 细节调整时读 `components.md` 查组件 +7. 生成后读 `checklist.md` 自检(顶部 P0-0 规则强制预检) + +## 核心设计原则(哲学) + +> 这些原则是"一人公司"分享 PPT 的 5 轮迭代总结出来的。违反其中任何一条,视觉感都会垮。 + +1. **克制优于炫技** — WebGL 背景只在 hero 页透出,普通页几乎看不见 +2. **结构优于装饰** — 不用阴影、不用浮动卡片、不用 padding box,一切信息靠**大字号 + 字体对比 + 网格留白** +3. **内容层级由字号和字体共同定义** — 最大衬线 = 主标题,中衬线 = 副标,大非衬线 = lead,小非衬线 = body,等宽 = 元数据 +4. **图片是第一公民** — 图片只裁底部,保证顶部和左右完整;网格用 `height:Nvh` 固定,不要用 `aspect-ratio` 撑 +5. **节奏靠 hero 页** — hero 和 non-hero 交替,才不累眼睛 +6. **术语统一** — Skills 就是 Skills,不要中英混合翻译 + +## 参考作品 + +本 skill 的视觉基调参考了: + +- 歸藏 "一人公司:被 AI 折叠的组织" 分享(2026-04-22,27 页) +- *Monocle* 杂志的版式 +- YC 总裁 Garry Tan "Thin Harness, Fat Skills" 那篇博客的 demo + +可以把它们当做风格锚点。 diff --git a/skills/guizang-ppt/assets/example-slides.html b/skills/guizang-ppt/assets/example-slides.html new file mode 100644 index 0000000..29cfc85 --- /dev/null +++ b/skills/guizang-ppt/assets/example-slides.html @@ -0,0 +1,318 @@ +<!-- + Example slides for the magazine-web-ppt skill. + + Topic: "一人公司 · The Quiet Hardware" — a fictional but realistic 64-day + case study, mirroring the rhythm and content arc of the original 歸藏 + guizang-ppt-skill demo. Used to power the Examples preview without + requiring real product imagery on disk — image slots stand in for what a + real deck would show. + + Theme rhythm: hero dark → light → dark → light → hero light → dark → + hero dark → light → hero light. Hits all 8 layout categories. +--> + +<!-- Layout 1 · Hero Cover ============================================ --> +<section class="slide hero dark"> + <div class="chrome"> + <div>A Talk · 2026.04.22</div> + <div>Vol.01</div> + </div> + <div class="frame" style="display:grid; gap:4vh; align-content:center; min-height:80vh"> + <div class="kicker">私享会 · 创作者 Demo Day</div> + <h1 class="h-hero">一人公司</h1> + <h2 class="h-sub">被 AI 折叠的组织</h2> + <p class="lead" style="max-width:60vw"> + 一个独立创作者 —— 在 64 天里完成 11 万行代码、覆盖 9 个平台、跨过 5 个时区,<br> + 生活节奏几乎没有被打扰。 + </p> + <div class="meta-row"> + <span>歸藏 Guizang</span><span>·</span><span>独立创作者</span><span>·</span><span>CodePilot 作者</span> + </div> + </div> + <div class="foot"> + <div>一场关于 AI · 组织 · 个体的分享</div> + <div>— 2026 —</div> + </div> +</section> + +<!-- Layout 2 · Big Numbers Grid ======================================= --> +<section class="slide light"> + <div class="chrome"> + <div>过去 64 天 · 开发篇</div> + <div>Act I / Dev · 02 / 09</div> + </div> + <div class="frame" style="padding-top:6vh"> + <div class="kicker">一个人,做了什么。</div> + <h2 class="h-xl">过去 64 天</h2> + <p class="lead" style="margin-bottom:5vh">从 0 到开源 CodePilot。</p> + + <div class="grid-6" style="margin-top:6vh"> + <div class="stat-card"> + <div class="stat-label">Duration</div> + <div class="stat-nb">64 <span class="stat-unit">天</span></div> + <div class="stat-note">从立项到现在</div> + </div> + <div class="stat-card"> + <div class="stat-label">Lines of Code</div> + <div class="stat-nb">110K+</div> + <div class="stat-note">一行一行写到 11 万+</div> + </div> + <div class="stat-card"> + <div class="stat-label">GitHub Stars</div> + <div class="stat-nb">5,166</div> + <div class="stat-note">单仓库 · 60 天破 5K</div> + </div> + <div class="stat-card"> + <div class="stat-label">Downloads</div> + <div class="stat-nb">41K+</div> + <div class="stat-note">装进了几万台电脑里</div> + </div> + <div class="stat-card"> + <div class="stat-label">AI Providers</div> + <div class="stat-nb">19</div> + <div class="stat-note">跨平台模型接入</div> + </div> + <div class="stat-card"> + <div class="stat-label">Commits</div> + <div class="stat-nb">608+</div> + <div class="stat-note">没有协作者</div> + </div> + </div> + </div> + <div class="foot"> + <div>项目 · CodePilot | github.com/codepilot</div> + <div>Act I · Dev Numbers</div> + </div> +</section> + +<!-- Layout 4 · Quote + Image ========================================== --> +<section class="slide dark"> + <div class="chrome"> + <div>身份反差 · The Twist</div> + <div>03 / 09</div> + </div> + <div class="frame grid-2-7-5" style="padding-top:6vh"> + <div style="display:flex; flex-direction:column; justify-content:space-between; gap:3vh"> + <div> + <div class="kicker">BUT</div> + <h2 class="h-xl" style="white-space:nowrap; font-size:7.2vw"> + 我不是程序员。 + </h2> + <p class="lead" style="margin-top:3vh"> + 大学毕业之后再没写过一行生产代码。过去十年做的是 UI 设计 / AI 特效 / 自媒体内容。 + </p> + </div> + <div class="callout"> + “这东西在三年前,<br> + 需要一个十人团队做一年。” + <div class="callout-src">— 一个观察者的判断</div> + </div> + </div> + <figure class="img-slot r-3x2" style="aspect-ratio:16/10; max-height:56vh"> + <span class="plus">+</span> + <span class="label">Product Screenshot · CodePilot</span> + </figure> + </div> + <div class="foot"> + <div>Page 03 · 我不是程序员</div> + <div>— · —</div> + </div> +</section> + +<!-- Layout 6 · Pipeline =============================================== --> +<section class="slide light"> + <div class="chrome"> + <div>我的工作流 · Workflow</div> + <div>Act II · 04 / 09</div> + </div> + <div class="frame"> + <div class="kicker">Pipeline · 流水线</div> + <h2 class="h-xl">两条流水线</h2> + + <div class="pipeline-section"> + <div class="pipeline-label">文本侧 · Text Pipeline</div> + <div class="pipeline"> + <div class="step"> + <div class="step-nb">01</div> + <div class="step-title">Draft</div> + <div class="step-desc">AI 帮我起草初稿</div> + </div> + <div class="step"> + <div class="step-nb">02</div> + <div class="step-title">Polish</div> + <div class="step-desc">AI 润色去 AI 味</div> + </div> + <div class="step"> + <div class="step-nb">03</div> + <div class="step-title">Morph</div> + <div class="step-desc">AI 变形成推特 / 小红书</div> + </div> + <div class="step"> + <div class="step-nb">04</div> + <div class="step-title">Illustrate</div> + <div class="step-desc">AI 生成信息图</div> + </div> + <div class="step"> + <div class="step-nb">05</div> + <div class="step-title">Distribute</div> + <div class="step-desc">一键分发 9 平台</div> + </div> + </div> + </div> + + <div class="pipeline-section"> + <div class="pipeline-label">视觉 · 视频侧 · Video Pipeline</div> + <div class="pipeline" data-cols="3"> + <div class="step"> + <div class="step-nb">06</div> + <div class="step-title">Cut</div> + <div class="step-desc">AI 剪辑 + 自动配字幕</div> + </div> + <div class="step"> + <div class="step-nb">07</div> + <div class="step-title">Wrap</div> + <div class="step-desc">AI 包装 + 配 BGM</div> + </div> + <div class="step"> + <div class="step-nb">08</div> + <div class="step-title">Cover</div> + <div class="step-desc">AI 生成封面图</div> + </div> + </div> + </div> + </div> + <div class="foot"> + <div>Page 04 · 我的内容工厂</div> + <div>Workflow</div> + </div> +</section> + +<!-- Layout 2 · Act Divider ============================================ --> +<section class="slide hero light"> + <div class="chrome"> + <div>第二幕 · 折叠</div> + <div>Act II · 05 / 09</div> + </div> + <div class="frame" style="display:grid; gap:6vh; align-content:center; min-height:80vh"> + <div class="kicker">Act II</div> + <h1 class="h-hero" style="font-size:8.5vw">折叠</h1> + <p class="lead" style="max-width:55vw"> + 从 “一个人做内容” 到 “一个人是组织”。<br> + AI 不是工具,是岗位的折叠器。 + </p> + </div> + <div class="foot"> + <div>第二幕引子</div> + <div>— · —</div> + </div> +</section> + +<!-- Layout 8 · Big Quote ============================================== --> +<section class="slide dark"> + <div class="chrome"> + <div>The Takeaway · 核心金句</div> + <div>06 / 09</div> + </div> + <div class="frame" style="display:grid; gap:5vh; align-content:center; min-height:80vh"> + <div class="kicker">Quote · 金句</div> + <blockquote style="font-family:var(--serif-zh); font-weight:700; font-size:5.6vw; line-height:1.2; letter-spacing:-.01em; max-width:78vw"> + “没有交接,<br>所有人都在构建。” + </blockquote> + <p class="lead" style="max-width:55vw; opacity:.65"> + Without the handoff, everyone builds.<br> + And that makes all the difference. + </p> + <div class="meta-row"> + <span>— Luke Wroblewski</span><span>·</span><span>2026.04.16</span> + </div> + </div> + <div class="foot"> + <div>Page 06 · 金句</div> + <div>— · —</div> + </div> +</section> + +<!-- Layout 7 · Hero Question ========================================== --> +<section class="slide hero dark"> + <div class="chrome"> + <div>留给你的问题</div> + <div>07 / 09</div> + </div> + <div class="frame" style="display:grid; gap:8vh; align-content:center; min-height:80vh"> + <div class="kicker">The Question</div> + <h1 class="h-hero" style="font-size:7vw; line-height:1.15"> + 你的公司里,<br> + 哪些岗位本来就<br> + 不该由人来做? + </h1> + <p class="lead" style="max-width:50vw"> + 这不是技术问题,是架构问题。 + </p> + </div> + <div class="foot"> + <div>Page 07 · The Question</div> + <div>— · —</div> + </div> +</section> + +<!-- Layout 9 · Before / After ========================================= --> +<section class="slide light"> + <div class="chrome"> + <div>旧 vs 新 · The Shift</div> + <div>08 / 09</div> + </div> + <div class="frame" style="padding-top:5vh"> + <div class="kicker">Before / After · 范式转变</div> + <h2 class="h-xl" style="margin-bottom:4vh">从交接到共建</h2> + + <div class="grid-2-6-6" style="gap:5vw 4vh"> + <div style="padding:3vh 2vw; border-left:3px solid currentColor; opacity:.55"> + <div class="kicker" style="opacity:.9">Before · 旧模式</div> + <h3 class="h-md" style="margin-top:2vh">设计 → 开发 → 交接</h3> + <ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55"> + <li>设计师在 Figma 做稿,反复对齐像素</li> + <li>开发盯着设计稿手动翻译</li> + <li>反复 PR 沟通,文档遗失在 Slack</li> + <li>非技术成员无法触碰代码</li> + </ul> + </div> + <div style="padding:3vh 2vw; border-left:3px solid currentColor"> + <div class="kicker" style="opacity:.9">After · 新模式</div> + <h3 class="h-md" style="margin-top:2vh">同工具 · 并行 · 共建</h3> + <ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55"> + <li>三个角色同时在同一份 Intent 上工作</li> + <li>agents.md / SKILL.md 是共享上下文</li> + <li>代理处理对齐、冲突、动效</li> + <li>任何人都能安全贡献代码</li> + </ul> + </div> + </div> + </div> + <div class="foot"> + <div>Page 08 · 范式转变</div> + <div>Before / After</div> + </div> +</section> + +<!-- Layout 2 · Hero Close ============================================= --> +<section class="slide hero light"> + <div class="chrome"> + <div>End · 致谢</div> + <div>09 / 09</div> + </div> + <div class="frame" style="display:grid; gap:5vh; align-content:center; min-height:80vh"> + <div class="kicker">Thanks for watching</div> + <h1 class="h-hero" style="font-size:9vw">谢谢。</h1> + <p class="lead" style="max-width:55vw"> + Slides are a single HTML file —<br> + open in any browser, no build, no server. + </p> + <div class="meta-row"> + <span>github.com/op7418/guizang-ppt-skill</span><span>·</span><span>MIT License</span> + </div> + </div> + <div class="foot"> + <div>Made with magazine-web-ppt skill</div> + <div>— Fin —</div> + </div> +</section> diff --git a/skills/guizang-ppt/assets/template.html b/skills/guizang-ppt/assets/template.html new file mode 100644 index 0000000..dbd3bb1 --- /dev/null +++ b/skills/guizang-ppt/assets/template.html @@ -0,0 +1,643 @@ +<!DOCTYPE html> +<html lang="zh-CN"> +<head> +<meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>[必填] 替换为 PPT 标题 · Deck Title + + + + + + + + + +
      ← → 翻页 · ESC 索引
      + +
      + + + + + +
      + + + + + + + + diff --git a/skills/guizang-ppt/references/checklist.md b/skills/guizang-ppt/references/checklist.md new file mode 100644 index 0000000..45946ca --- /dev/null +++ b/skills/guizang-ppt/references/checklist.md @@ -0,0 +1,265 @@ +# 质量检查清单(Checklist) + +这个清单来自"一人公司"分享 PPT 的真实迭代过程。每一条都是踩过坑之后总结的,按重要性排序。 + +生成 PPT 前,先通读一遍;生成后,逐项自检。 + +--- + +## 🔴 P0 · 一定不能犯的错 + +### 0. 生成前必须通过的类名校验(最重要) + +**现象**:直接把 layouts.md 的骨架粘到新 HTML,结果样式全部丢失——大标题变成非衬线、数据大字报字体小得像正文、pipeline 多页糊成一坨、图片堆到浏览器底部。 + +**根因**:如果 `template.html` 的 ` + + +
      +
      +
      +
      Onboarding plan · 30/60/90
      +

      Welcome, Maya. Let's make your first 90 days feel deliberate.

      +
      +
      RoleProduct Designer · Growth squad
      +
      Start dateMon, 4 November 2025
      +
      ManagerAlvaro Méndez
      +
      Onboarding buddySasha Lin
      +
      +
      +
      M
      +
      + +
      +

      Day 1 · Monday

      +

      A grounded day. Coffee with the team, a working laptop, and one shipped commit on the docs site by 5pm.

      +
      +
      +
      09:00
      Kickoff with AlvaroWelcome, week-one walkthrough, expectations chat. Office Room 3 (or Zoom).
      +
      10:00
      IT setup with DevonLaptop, badge, SSO, Slack, Figma, Linear, GitHub. Bring two photo IDs.
      +
      11:30
      Coffee with Sasha (buddy)The unwritten rules, who-to-ask map, where the good lunch spots are.
      +
      12:30
      Team lunch · Northwind cafeteriaWhole Growth squad joins. No agenda.
      +
      14:00
      Read & exploreHandbook, last quarter's design crit recordings, Figma library.
      +
      16:00
      Ship "I exist" PRAdd yourself to the team page on the docs site. Counts as your first commit.
      +
      17:00
      End-of-day check-in with Alvaro15 min. What was confusing, what wasn't. Repeat tomorrow if useful.
      +
      +
      +
      + +
      +

      First week timeline

      +

      Two activities per day. Anything else is bonus.

      +
      +
      +
      Mon
      Nov 4
      +
      Kickoff + setupAlvaro · 09:00
      +
      Ship team-page PRSasha can review
      +
      +
      +
      Tue
      Nov 5
      +
      Design system tourYuko · 10:00
      +
      Shadow user research call11:00 with Sam
      +
      +
      +
      Wed
      Nov 6
      +
      Squad weekly09:30
      +
      Pick a starter ticketFrom the "good first issues" lane
      +
      +
      +
      Thu
      Nov 7
      +
      Design crit attendance14:00. Just listen.
      +
      1:1 with skip-levelAvi · 16:00
      +
      +
      +
      Fri
      Nov 8
      +
      End-of-week retro15-min note to Alvaro
      +
      Optional: All-hands demo17:00 · drinks after
      +
      +
      +
      + +
      +

      30 · 60 · 90 day milestones

      +

      Three outcomes per checkpoint. We'll review each at the matching 1:1 with Alvaro.

      +
      +
      + Day 30 +

      Find your footing

      +
        +
      • Shipped one small, end-to-end design change to production.
      • +
      • Mapped every recurring meeting and why it exists.
      • +
      • Met with each cross-functional partner (eng, PM, research, marketing).
      • +
      +
      +
      + Day 60 +

      Own a feature

      +
        +
      • Driving design on the new onboarding redesign — own the spec.
      • +
      • Ran your first design crit as the presenter.
      • +
      • Drafted one process improvement and posted it for the team.
      • +
      +
      +
      + Day 90 +

      Move the team forward

      +
        +
      • Shipped a feature you led from research → launch.
      • +
      • Mentored someone — even informally.
      • +
      • Shared one hot take in all-hands and lived to tell.
      • +
      +
      +
      +
      + +
      +

      Things to bookmark

      +

      Open these, save them in your browser, then forget about this page.

      +
      +
      +

      Resources

      +
      📘
      Northwind Handbook
      handbook.nw
      +
      💬
      #growth-squad
      Slack
      +
      🎨
      Design Library v3.4
      Figma
      +
      📊
      Growth dashboard
      grafana.nw
      +
      💸
      Payroll & benefits
      Rippling
      +
      📅
      Onboarding calendar
      cal.nw/onboard
      +
      +
      +

      You're set when…

      +
      Laptop, SSO, and badge work end-to-end.Includes Slack, Figma, Linear, GitHub, 1Password.
      +
      You've met everyone on the squad.Coffee, walk, or 15-min Zoom — your call.
      +
      You've shipped your first PR.Even tiny ones count. Sasha will help.
      +
      You can find any meeting on the calendar.And know which ones you can decline.
      +
      You feel comfortable asking dumb questions.This is the most important one. We mean it.
      +
      +
      +
      + +
      + Northwind People Ops · Onboarding plan template v3.1 + Updated October 2025 +
      +
      + + diff --git a/skills/invoice/SKILL.md b/skills/invoice/SKILL.md new file mode 100644 index 0000000..6a05a80 --- /dev/null +++ b/skills/invoice/SKILL.md @@ -0,0 +1,48 @@ +--- +name: invoice +description: | + A printable invoice page — sender + recipient block, line items table, + tax breakdown, totals, and payment instructions. Use when the brief + mentions "invoice", "bill", "billing statement", or "发票". +triggers: + - "invoice" + - "bill" + - "billing statement" + - "发票" + - "账单" +ocd: + mode: prototype + platform: desktop + scenario: finance + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Create an invoice from a freelance design studio billing a client for a brand identity project — three line items, 10% retainer, 9% sales tax." +--- + +# Invoice Skill + +Produce a single-page printable invoice. + +## Workflow + +1. Read DESIGN.md. +2. Layout: + - Top band: studio brand on the left, "INVOICE" + number + date + due date on the right. + - Two columns: From (sender) / Bill to (recipient) with addresses. + - Project ref + payment-terms strip. + - Line items table: description / qty / unit / amount. + - Right-aligned totals block: subtotal, retainer, tax, total due. + - Payment instructions (bank, wire, ACH). + - Thank-you note + signature line. +3. Print stylesheet @media print to remove backgrounds. + +## Output contract + +``` + +... +``` diff --git a/skills/invoice/example.html b/skills/invoice/example.html new file mode 100644 index 0000000..9e6748b --- /dev/null +++ b/skills/invoice/example.html @@ -0,0 +1,214 @@ + + + + + +Invoice · Sable Studio · INV-2025-0142 + + + +
      +
      +
      +
      +
      S
      +
      +
      Sable Studio
      +
      Brand & product design · est. 2018
      +
      +
      +
      +
      +
      Invoice
      +
      INV-2025-0142
      +
      Issued 14 October 2025 · Due 13 November 2025
      +
      +
      + +
      +
      +

      From

      +
      Sable Studio LLC
      +
      + 221 Cooper Street, 4F
      + Brooklyn, NY 11211 · USA
      + EIN 87-1234567
      + billing@sable.studio +
      +
      +
      +

      Bill to

      +
      Northwind Trading Co.
      +
      + Attn: Mira Okafor, CFO
      + 500 Howard Street, Floor 9
      + San Francisco, CA 94103 · USA
      + AP: ap@northwind.com +
      +
      +
      + +
      +
      Project
      Northwind brand identity refresh
      +
      PO Number
      NW-PO-2025-3387
      +
      Terms
      Net 30 · USD
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      DescriptionQtyRateAmount
      Discovery & strategyStakeholder interviews, competitive audit, brand audit, written strategy doc.1$8,500.00$8,500.00
      Identity system designWordmark, monogram, palette, typography, motion principles, two production rounds.1$22,000.00$22,000.00
      Brand guidelines & handoffBrand book PDF, Figma library, asset pack, two team handoff sessions.1$6,500.00$6,500.00
      Senior design hours · overageAdditional rounds requested between 12 Sep and 28 Sep beyond the original SOW.14$220.00$3,080.00
      + +
      +
      +
      Payment terms
      + Payment is due within 30 days of issue. Late payments incur a 1.5% monthly service charge per the master services agreement signed 14 February 2025. The 10% retainer paid 12 August 2025 has been applied below. +
      +
      +
      Subtotal$40,080.00
      +
      Retainer applied (10%)−$4,008.00
      +
      Sales tax · NY (9%)$3,246.48
      +
      Net before tax$36,072.00
      +
      Total due$39,318.48
      +
      +
      + +
      +
      +

      Wire / ACH (USD)

      +
      BankMercury Bank
      +
      Routing (ACH)084-001-122
      +
      Routing (Wire)026-073-150
      +
      Account9847-2210-3318
      +
      MemoINV-2025-0142
      +
      +
      +

      Online payment

      +
      Pay linksable.studio/p/inv-0142
      +
      Stripe / card / ACHYes
      +
      Wise / SEPA / FXOn request
      +
      ReceiptAuto-emailed
      +
      +
      + +
      +

      Thank you, Northwind. It's been a privilege to work on this rebrand.

      +
      +
      Lila Vega
      +
      Lila Vega · Founder, Sable Studio
      +
      +
      +
      + + diff --git a/skills/kanban-board/SKILL.md b/skills/kanban-board/SKILL.md new file mode 100644 index 0000000..3d9417a --- /dev/null +++ b/skills/kanban-board/SKILL.md @@ -0,0 +1,48 @@ +--- +name: kanban-board +description: | + Kanban / task board with columns (To do / In progress / In review / Done), + draggable-looking cards, assignee avatars, swimlanes, and a top filter + bar. Use when the brief mentions "kanban", "task board", "sprint board", + "trello", "看板". +triggers: + - "kanban" + - "task board" + - "sprint board" + - "trello" + - "jira board" + - "看板" +ocd: + mode: prototype + platform: desktop + scenario: operations + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Make me a kanban board for a 5-person growth squad mid-sprint — backlog, doing, review, done." +--- + +# Kanban Board Skill + +Produce a single-screen kanban board. + +## Workflow + +1. Read the active DESIGN.md. +2. Identify squad name, sprint number, columns, and member roster from the brief. +3. Layout: + - Top bar: project crumb, sprint chip, filter row (members, labels, status), search. + - 4 columns: Backlog, In progress, In review, Done. Each column has a count chip and an "+ add" affordance. + - 3–6 cards per column. Each card: tag chip, title, assignee avatar, point estimate, progress (if applicable). + - Sidebar (collapsible feel): "Sprint pulse" with progress bar, top assignees, blocked-tickets callout. +4. One inline ` + + +
      +
      +
      + Northwind / Growth squad + Sprint 38 · Day 6 of 10 +
      + + + +
      + +
      + Members +
      + MR + CA + PB + DP + +1 +
      + Labels +
      + Active sprint + Bug + Feature + Research +
      +
      + Group · Status + Sort · Priority +
      + +
      +
      +
      Backlog
      5
      +
      + Feature +
      Add empty-state illustration to onboarding step 2
      +
      DP3 pts
      NW-241
      +
      +
      + Design +
      Refresh notification settings page tokens
      +
      MR2 pts
      NW-237
      +
      +
      + Research +
      Interview 5 new Enterprise admins about 2FA enforcement
      +
      PB5 pts
      NW-225
      +
      +
      + Chore +
      Migrate legacy auth logs to new schema
      +
      CA3 pts
      NW-219
      +
      +
      + Bug +
      CSV export drops emoji from project names
      +
      +1 pt
      NW-244
      +
      +
      + New card
      +
      + +
      +
      In progress
      4
      +
      + Feature +
      TOTP enrollment UI in member settings
      +
      +
      DP5 pts
      NW-201
      +
      +
      + Feature +
      Recovery codes — generate, download, regenerate
      +
      +
      PB3 pts
      NW-202
      +
      +
      + Bug +
      Fix focus-trap regression in command bar
      +
      +
      CA2 pts
      NW-238
      +
      +
      + Design +
      2FA challenge step — visual + microcopy
      +
      +
      MR3 pts
      NW-205
      +
      +
      + +
      +
      In review
      3
      +
      + Feature +
      Audit-log entries for 2FA setup events
      +
      PB2 pts
      NW-198
      +
      +
      + Design +
      Settings nav restructure (left rail)
      +
      MR3 pts
      NW-189
      +
      +
      + Bug +
      Workspace switcher resets scroll on close
      +
      CA1 pt
      NW-233
      +
      +
      + +
      +
      Done
      6
      +
      + Feature +
      Workspace 2FA enforcement policy (admin)
      +
      DP5 pts
      NW-181
      +
      +
      + Chore +
      Bump auth library to 4.2.0
      +
      CA1 pt
      NW-176
      +
      +
      + Research +
      2FA usability sessions (n=8)
      +
      PB3 pts
      NW-172
      +
      +
      + Design +
      Settings tokens audit
      +
      MR2 pts
      NW-168
      +
      +
      +
      +
      + + +
      + + diff --git a/skills/magazine-poster/SKILL.md b/skills/magazine-poster/SKILL.md new file mode 100644 index 0000000..8fa41d1 --- /dev/null +++ b/skills/magazine-poster/SKILL.md @@ -0,0 +1,88 @@ +--- +name: magazine-poster +description: | + An editorial-style poster — newsprint paper, dateline, oversized serif + headline with a struck-through word and italic accent, a 2-column body + block, and 6 numbered sections with annotated pull-quote captions. + Reads like a Sunday-paper full-page essay or a thoughtful launch poster. + Use when the brief asks for "magazine poster", "editorial poster", + "newsprint", "essay layout", or "manifesto". +triggers: + - "magazine poster" + - "editorial poster" + - "newsprint" + - "newspaper layout" + - "essay" + - "manifesto" + - "long-form poster" + - "杂志海报" + - "报纸版式" +ocd: + mode: prototype + platform: desktop + scenario: marketing + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Design an editorial magazine-style poster — ‘You don't need a designer to ship your first draft anymore.’ Newsprint paper, six numbered sections." +--- + +# Magazine Poster Skill + +Produce a single-page editorial poster — looks like a tear-out from a +Sunday paper. Long-form, deliberate, type-driven. + +## Workflow + +1. **Read the active DESIGN.md** (injected above). Pick the heaviest serif + token in the DS for the headline, the body serif for the columns, and + a typewriter / mono token for the section eyebrows and annotations. +2. **Pick the topic** from the brief. Write a real, opinionated headline — + one with a struck-through word ("a designer", "the template hunt") and + an italic accent on a key noun ("first draft", "mood", "specifics"). +3. **Layout**, in order: + - **Top rule** — thin black hairline + a dateline ("01 · A · YOUR LAB" + left, "DD · MMM · YYYY" right). Light typewriter font. + - **Top eyebrow** — a single mono tag like "POSTED TODAY". + - **Headline** — 2–3 lines, oversized serif. One word struck through + with `text-decoration: line-through; text-decoration-thickness: 2px`. + One word italic, in accent color. + - **Deck** — a 1–2 sentence subhead in italic serif at ~60% size of + the headline, with a dash separator and a `— what works` callout + fragment in accent. + - **Accent rule** — short horizontal accent-colored bar (~80px). + - **Body grid** — six numbered cells in a 2×3 (or 3×2) grid. Each cell: + - eyebrow (`01 · SHIP FAST`) in mono, accent color. + - bold serif sub-headline. + - 2–3 sentence body in body serif. + - one annotated callout — a quoted "use this prompt" line on a tinted + background block, set in mono. + - **Footer band** — rule above, three cells: handle / role / date, with a + small "PRO TIP" plate on the left containing one closing line. +4. **Write** a single HTML document: + - `` through ``, CSS inline. + - Background uses a creamy paper tint (`#f3eee2` or DS canvas) plus a + subtle paper noise (`radial-gradient` dots at low opacity). + - 2-column body grid via CSS Grid; min-width 1100px page. + - `data-ocd-id` on header, headline, deck, each cell, footer. +5. **Self-check**: + - Type hierarchy is unmistakable — headline owns the page. + - Strikethrough + italic accent both appear, exactly once each. + - Body reads like real opinion, not lorem ipsum. + - Looks intentional at 1280–1440px wide. + +## Output contract + +Emit between `` tags: + +``` + + +... + +``` + +One sentence before the artifact, nothing after. diff --git a/skills/magazine-poster/example.html b/skills/magazine-poster/example.html new file mode 100644 index 0000000..a560c5c --- /dev/null +++ b/skills/magazine-poster/example.html @@ -0,0 +1,207 @@ + + + + + + You don't need a designer to ship your first draft anymore — AI Enthusiast + + + +
      +
      + 01 · AI ENTHUSIAST + 17 · APR · 2026 +
      +
      — POSTED TODAY
      + +

      + You don't need a designer
      + to ship your first draft
      + anymore. +

      + +

      + Six honest ways I'm using AI to move faster from idea → artifact this week — what works, what I'd still hand to a human, and the exact prompts that got me there. +

      +
      + +
      +
      +
      01 · SHIP FAST
      +

      Clickable prototype in 90 seconds

      +

      Describe the flow in plain English. Get a real, tappable prototype — not static screens. Export to HTML and share the link.

      +
      Onboarding flow for a fintech app — 5 screens, dark mode, rounded cards, haptic-style transitions.
      +
      +
      +
      02 · PITCH
      +

      Investor deck from a napkin idea

      +

      Skip the template hunt. Draft the deck, refine it section-by-section, then export straight to PPTX or PDF — notes included.

      +
      10-slide seed pitch for a RAG tool for lawyers. Keep it minimal, data-first, one chart per slide.
      +
      +
      +
      03 · BRAND LOCK
      +

      Your design system, auto-applied

      +

      Point the model at your tokens, components, or a codebase. Every new asset respects your type, color, and spacing scale.

      +
      Use our /design-system tokens. Build a pricing page variant. Match the radius + shadow of the marketing site.
      +
      +
      +
      04 · MARKETING
      +

      Landing pages & launch collateral

      +

      One-pagers, email headers, feature comparison grids — editable, on-brand, and ready to hand off in minutes, not days.

      +
      One-pager for a Series A launch. Headline, three proof points, CTA. Editorial feel, no stock photos.
      +
      +
      +
      05 · HANDOFF
      +

      Design → engineering bundle

      +

      Finished the mock? Ship the whole handoff to your dev environment. Specs, tokens, components — no translation layer.

      +
      Export this mock to code. Wire the auth screen to Supabase. Add a loading state and empty state.
      +
      +
      +
      06 · EXPLORE
      +

      Ten directions in ten minutes

      +

      Generate N visual directions side-by-side. Use sliders to dial tone: playful, brutalist, editorial, corporate — same copy.

      +
      Show six hero section variants. Same copy, different aesthetics. Label each with a mood word.
      +
      +
      + + +
      + + diff --git a/skills/meeting-notes/SKILL.md b/skills/meeting-notes/SKILL.md new file mode 100644 index 0000000..f12b5bb --- /dev/null +++ b/skills/meeting-notes/SKILL.md @@ -0,0 +1,47 @@ +--- +name: meeting-notes +description: | + Meeting notes page — title bar with attendees, agenda checklist, decisions + block, action items table with owners + dates, and a "next meeting" footer. + Use when the brief mentions "meeting notes", "minutes", "1:1 notes", + "all-hands recap", or "会议纪要". +triggers: + - "meeting notes" + - "minutes" + - "1:1 notes" + - "all-hands recap" + - "会议纪要" +ocd: + mode: prototype + platform: desktop + scenario: operations + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] + example_prompt: "Write up notes from a 60-minute Growth squad weekly — agenda, decisions, action items with owners, next meeting." +--- + +# Meeting Notes Skill + +Produce a single-screen meeting notes page. + +## Workflow + +1. Read DESIGN.md. +2. Layout: + - Header: meeting title, date, time, location/Zoom, attendees row. + - Agenda checklist (4–6 items). + - Decisions panel — bulleted list with strong styling. + - Action items table with owner, due date, status. + - "Open questions" + "next meeting" footer. +3. Subdued colour palette, clear hierarchy. + +## Output contract + +``` + +... +``` diff --git a/skills/meeting-notes/example.html b/skills/meeting-notes/example.html new file mode 100644 index 0000000..1d63e54 --- /dev/null +++ b/skills/meeting-notes/example.html @@ -0,0 +1,234 @@ + + + + + +Growth squad weekly · 14 Oct notes + + + +
      +
      +
      Northwind / Growth squad / Weeklies
      +

      Growth squad weekly · W42

      +
      + DateTuesday, 14 October 2025 + Time10:00 – 11:00 PT + WhereZoom · meet.northwind/growth-weekly + Notes byDevon Park +
      +
      + Present +
      + DP + MR + PB + CA + SL +
      + Apologies — Alvaro M. (PTO) +
      +
      + +
      +

      Agenda

      +
      +
      +
      +
      Sprint 38 mid-sprint checkWalk the board column by column. Reset what's stuck.
      +
      10:00 · 15m
      +
      +
      +
      +
      2FA workstream — M2 riskBrand microcopy review is the open dependency.
      +
      10:15 · 10m
      +
      +
      +
      +
      Onboarding metrics reviewActivation up 9 pp WoW; debrief the empty-state work.
      +
      10:25 · 10m
      +
      +
      +
      +
      Pioneer security review prepSales loop-in for Thursday's call.
      +
      10:35 · 10m
      +
      +
      +
      +
      Q4 roadmap sneak peekDevon shares the proposed shape; we vote on top-3 themes.
      +
      10:45 · 12m
      +
      +
      +
      +
      Open thread — anything elsePushed to async — see #growth-squad.
      +
      10:57 · 3m
      +
      +
      +
      + +
      +

      Decisions

      +
      +

      What we agreed to, on the record

      +
        +
      • M2 (2FA challenge step) stays at Nov 18 unless brand review slips past Wednesday EOD; Devon owns the escalation.
      • +
      • Empty-state experiment rolls to 100% on Thursday after one more 24h hold; no follow-up control needed.
      • +
      • Q4 themes: (1) Enterprise-readiness (auth + audit), (2) Onboarding 2.0, (3) Mobile-first settings. Sasha to write up the one-pagers.
      • +
      • Weekly format: starting next week, demos move to Friday async-video; Tuesday is decisions + board only.
      • +
      +
      +
      + +
      +

      Action items

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      ActionOwnerDueStatus
      Escalate brand microcopy review to Sasha + Brand leadDPDevonWed Oct 15In progress
      Roll empty-state to 100% (with monitoring window)MRMiraThu Oct 16To do
      Pair with Sales on Pioneer call prepPBPriyaThu Oct 16To do
      Draft Q4 theme one-pagers (3)SLSashaMon Oct 20To do
      Audit-writer backlog dashboardCACalebTue Oct 21Blocked · awaiting Grafana ACL
      Switch weekly format to demos-on-FridayDPDevonMon Oct 20Done
      +
      + +
      +

      Open questions & next meeting

      +
      +
      +

      Open questions

      +

      Do we want a customer in the Q4 mobile-first kickoff (Pioneer would say yes), or do we keep the first session internal?

      +

      Should the Friday demo video be capped at 5 min, or open-ended?

      +
      +
      +

      Next meeting

      +
      DateTuesday, 21 October 2025
      +
      Time10:00 – 11:00 PT · Zoom
      +
      Pre-readSasha's Q4 one-pagers (Mon EOD)
      +
      Notes byMira Reddy (rotation)
      +
      +
      +
      + +
      + Northwind Growth squad · Notes v1 + Filed in #growth-squad · 15 Oct 2025 +
      +
      + + diff --git a/skills/mobile-app/SKILL.md b/skills/mobile-app/SKILL.md new file mode 100644 index 0000000..24b3e52 --- /dev/null +++ b/skills/mobile-app/SKILL.md @@ -0,0 +1,100 @@ +--- +name: mobile-app +description: | + A mobile-app screen rendered inside a pixel-accurate iPhone 15 Pro frame + on the page. Built by copying the seed `assets/template.html` and pasting + one screen archetype from `references/layouts.md`. Use when the brief asks + for "mobile app", "iOS app", "Android app", "phone screen", or "app UI". +triggers: + - "mobile app" + - "ios app" + - "android app" + - "phone screen" + - "app ui" + - "app mockup" + - "移动端" + - "手机 app" +ocd: + mode: prototype + platform: mobile + scenario: design + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] +--- + +# Mobile App Skill + +Produce a single mobile-app screen mockup, framed inside a real-feeling iPhone 15 Pro device. + +## Resource map + +``` +mobile-app/ +├── SKILL.md ← you're reading this +├── assets/ +│ └── template.html ← seed: device frame + screen primitives (READ FIRST) +└── references/ + ├── layouts.md ← 6 screen archetypes (Feed / Detail / Onboarding / Profile / Checkout / Focus) + └── checklist.md ← P0/P1/P2 self-review (anti-fake-device) +``` + +## Workflow + +### Step 0 — Pre-flight + +1. **Read `assets/template.html`** end-to-end through the ` + + +
      +
      [REPLACE] App · [REPLACE] Screen name
      + +
      + + + + + + +
      + +
      + 9:41 + + + + + + + + + + + + + + + + + + + +
      + + +
      +
      +
      +

      Tuesday · April 22

      +

      [REPLACE] Hi there.

      +
      + +
      + +
      +
      +

      PASTE A LAYOUT FROM

      +

      references/layouts.md

      +

      into <main class="content">

      +
      +
      +
      + + + + +
      +
      +
      +
      + + diff --git a/skills/mobile-app/example.html b/skills/mobile-app/example.html new file mode 100644 index 0000000..4752c23 --- /dev/null +++ b/skills/mobile-app/example.html @@ -0,0 +1,92 @@ + + + + + + Tomato — focus screen + + + +
      +
      +
      9:41·· 5G · 100%
      +
      +

      Tuesday · April 22

      +

      Two pomodoros to lunch.

      +
      +
      +

      Focus session

      +
      15:42
      +
      +
      + + +
      +
      +
      +

      Today

      +
      +
      3
      Sessions
      +
      75m
      Focused
      +
      2
      Tasks done
      +
      +
      +
      +

      Up next

      +
      Review Q2 OKRs
      25m · completed
      +
      Draft sync-engine post
      2 sessions estimated
      +
      1:1 prep with Mira
      1 session
      +
      + +
      +
      + + diff --git a/skills/mobile-app/references/checklist.md b/skills/mobile-app/references/checklist.md new file mode 100644 index 0000000..1589b69 --- /dev/null +++ b/skills/mobile-app/references/checklist.md @@ -0,0 +1,46 @@ +# Mobile app checklist + +Run this before emitting ``. P0 must pass. + +## P0 — must pass + +- [ ] **Frame looks like a phone, not a generic card.** Dynamic Island visible, status bar SVG icons present (signal/wifi/battery), home indicator at bottom. The seed already does this — verify you didn't accidentally delete the island/rails/indicator markup. +- [ ] **Status bar shows real glyphs**, not text like `· · · 5G · 100%`. Use the SVG icons from the seed. +- [ ] **Home indicator is the last visible thing.** Anything below it (e.g. extra padding, accidental `
      `) breaks the illusion. +- [ ] **Content scrolls, frame doesn't.** `
      ` has `overflow-y: auto`; the surrounding `.device` does not. The page background never moves. +- [ ] **Tap targets ≥ 44px tall.** The seed's `.btn-primary` (48px), `.tab` (~50px), `.icon-btn` (36px ≥ touch with padding), `.list-row` (≥48px with padding) all pass. Don't ship a button under 44px. +- [ ] **Body text ≥ 14px.** `--fs-body: 15px` already enforces this on most copy. List-row sub text uses 13px max — that's the floor. +- [ ] **One accent, used at most twice on the screen.** Typically: one active tab + one CTA, OR one accent card + one tab. Never three. +- [ ] **No external image URLs.** Use the `.ph-img` placeholder class. External CDN images break the OCD preview iframe and look fake when they 404. +- [ ] **Tab bar matches the screen kind.** Onboarding / detail / checkout: drop the `