# Contributing to HTML Anything Thanks for thinking about contributing. HTML Anything is small on purpose — most of the value lives in **files** (skill folders, prompt fragments, agent adapters) rather than framework code. The highest-leverage contributions are usually one folder, one Markdown file, or a ten-line adapter. This guide tells you exactly where to look for each type of contribution and what bar a PR has to clear before we merge.

English · 简体中文

--- ## Three things you can ship in one afternoon | If you want to… | You're really adding | Where it lives | Ship size | |---|---|---|---| | Make HTML Anything render a new kind of artifact (an invoice, a job posting, an iOS Settings screen…) | a **Skill** | [`src/lib/templates/skills//`](src/lib/templates/skills/) | one folder, ~3 files | | Hook up a new coding-agent CLI | an **Agent adapter** | [`src/lib/agents/argv.ts`](src/lib/agents/argv.ts) + [`src/lib/agents/detect.ts`](src/lib/agents/detect.ts) | ~10 lines in one array | | Add a new export target (WeChat Channels, Douyin captions, Notion, …) | an **Export adapter** | [`src/components/drafts-menu.tsx`](src/components/drafts-menu.tsx) + helper under `src/lib/export/` | one component + one helper | | Add a feature, fix a bug, refactor the streaming parser | code | `src/app/`, `src/lib/`, `src/components/` | normal PR | | Improve docs, port a section into another language, fix typos | docs | `README.md`, `README.zh-CN.md`, this file | one PR | If you're not sure which bucket your idea is in, [open an issue first](https://github.com/nexu-io/html-anything/issues/new) and we'll point you at the right surface. --- ## Local setup ```bash git clone https://github.com/nexu-io/html-anything.git cd html-anything pnpm install pnpm dev # next dev — http://localhost:3000 pnpm build # next build, when verifying a release-shaped bundle ``` Node `~20` and `pnpm` are required. macOS, Linux, and WSL2 are the primary paths. Native Windows should work but isn't a primary target — file an issue if it doesn't. Before you push, make sure you have **at least one coding-agent CLI logged in** (`claude login`, `cursor login`, `gemini auth`, etc.) so you can actually run an end-to-end generation. PRs that touch the streaming or agent layer are expected to include a screenshot or log snippet showing it worked. > **A note on this project.** It is a normal Next.js 16 App Router app — no daemon, no Electron shell, no extra processes. Everything happens in `next dev`: server routes spawn the local CLI, stream stdout back as SSE, and the browser appends into an iframe `srcdoc`. If you find yourself wanting to introduce a separate long-running process, please open a discussion first. --- ## Adding a new Skill A skill is a folder under [`src/lib/templates/skills/`](src/lib/templates/skills/) with a `SKILL.md` at the root, following Claude Code's [`SKILL.md` convention][skill] plus a small extended frontmatter that the picker reads. **No registration step.** Drop the folder in, restart `pnpm dev`, the picker shows it. ### Skill folder layout ```text src/lib/templates/skills/your-skill/ ├── SKILL.md # required — prompt body + frontmatter ├── example.html # required — what the agent should produce, hand-authored ├── assets/ # optional — fonts, images, reusable CSS, layout fragments └── references/ # optional — design-system snippets, references the agent should Read ``` ### `SKILL.md` shape ```markdown --- name: your-skill description: One sentence shown in the picker preview. mode: prototype # prototype | deck | frame | social | office | doc | mockup | vfx scenario: marketing # design | marketing | engineering | product | finance | hr | sale | personal surface: desktop # desktop | mobile | A4 | 1080x1920 | 1600x900 | 1920x1080 | … preview: type: iframe # iframe | image | deck thumbnail: docs/screenshots/skills/your-skill.png # optional, used in the README showcase grid design_system: requires: optional # required | optional | none featured: false # set true if this should appear in "Showcase examples" example_prompt: | Three-sentence example prompt that demonstrates the kind of input this skill is for. --- # Your Skill ## Hard constraints - 8 px baseline grid · all spacing / line-height / font-size as multiples of 8. - CJK-first font stack: `"Noto Sans SC", "PingFang SC", "Source Han Sans"`. Latin: `"Inter", "Manrope"`. - Color contrast ≥ 4.5. Real `:focus` state on every interactive element. - Must use the user's real data. No `lorem ipsum`. No invented metrics. No purple gradients. ## Layout ## What "good" looks like - - ## What "bad" looks like - - ``` ### Bar for merging a new skill 1. **Real `example.html` ships in the folder.** Hand-author it once — the agent has a target to copy. PRs without one get bounced. 2. **The example renders in the browser** (`pnpm dev` → pick the skill → ⌘+Enter → screenshot). Attach the screenshot to the PR. 3. **Hard constraints exist and are specific.** Vague directives ("use modern typography") are not constraints. Real ones look like "Inter 96 / 64 / 40 / 24 / 16 px, 8 px grid, max two weights per slide". 4. **No `lorem ipsum`** anywhere in the example. If the example uses placeholder data, it must be plausibly-real placeholder data. 5. **Slug uses ASCII lowercase with dashes** — `deck-swiss-international`, `social-x-post-card`. Mirror the 75 existing folders. 6. **If you vendored work from another repo**, the original `LICENSE` and authorship attribution have to ship inside your skill folder. Example: `src/lib/templates/skills/deck-guizang-editorial/LICENSE` preserves the original op7418 license verbatim. ### Picker grouping The picker organizes skills along two axes. Pick values that already exist where possible — only introduce a new value if your skill genuinely doesn't fit: - **`mode`** — `prototype` · `deck` · `frame` · `social` · `office` · `doc` · `mockup` · `vfx`. - **`scenario`** — `design` · `marketing` · `engineering` · `product` · `finance` · `hr` · `sale` · `personal`. --- ## Adding a new coding-agent CLI Hooking up a new agent (e.g. some new shop's `foo-coder` CLI) is one entry in [`src/lib/agents/argv.ts`](src/lib/agents/argv.ts): ```ts { id: 'foo', name: 'Foo Coder', bin: 'foo', detect: { args: ['--version'] }, build: (prompt: string) => ({ args: ['exec', '-p', prompt], stdin: null, // or 'prompt' if the CLI reads from stdin }), stream: 'plain', // 'plain' | 'json-event' | 'claude-stream-json' } ``` That's it. `/api/agents` will detect it on `PATH`, the top-bar picker shows it, the chat path works through the same SSE pipeline. If the CLI emits **typed events** (like Claude Code's `--output-format stream-json`), add a parser in [`src/lib/agents/invoke.ts`](src/lib/agents/invoke.ts) and set `stream: 'claude-stream-json'`. ### Bar for merging an agent adapter 1. **A real session works end-to-end.** Run `pnpm dev`, pick your agent, generate any skill's `example_prompt`, and paste the SSE log into the PR description showing it streamed an artifact through. 2. **`PATH` detection works on macOS, Linux, and WSL.** The scanner already includes `~/.local/bin` · `~/.bun/bin` · `/opt/homebrew/bin` · `~/.npm-global/bin`; if your CLI lives somewhere else, add the dir to the scan list. 3. **The README's "Supported coding agents" table gets one row** in both `README.md` and `README.zh-CN.md`. 4. **Stream parser is reusable.** If the CLI emits the same JSON-line shape as another adapter, share the parser — don't fork. --- ## Adding a new export target Export targets live in two places: a helper under `src/lib/export/` that produces the bytes (string for `.html`, Blob for `.png`, `ClipboardItem` for paste), and a menu entry in [`src/components/drafts-menu.tsx`](src/components/drafts-menu.tsx) that wires it into the UI. ### Bar for merging an export target 1. **Round-trip test in the target platform.** Paste / upload the output into the platform itself (WeChat editor, X composer, Zhihu editor) and screenshot the result in the PR. 2. **No platform credentials in code.** If the target needs an API key, the user supplies it in settings; we don't ship one. 3. **Output is self-contained.** `.html` exports inline their CSS via `juice`; PNG exports use `modern-screenshot` at 2×. --- ## Code style We're not pedantic about formatting (Prettier on save is fine), but two rules are non-negotiable because they show up in the prompt stack and the user-facing API: 1. **Single quotes in TS/TSX.** Strings are single-quoted unless escaping makes them ugly. The codebase is already consistent — please match. 2. **Comments in English.** Even if the PR is translating something into 中文, code comments stay in English so we can keep one set of greppable references. Beyond that: - **Don't narrate.** No `// import the module`, no `// loop through items`. If the code reads obviously, the comment is noise. Save comments for non-obvious intent or constraints the code can't express. - **TypeScript for `src/`.** No new top-level `.js` files unless there's a compelling reason. - **No new top-level dependencies** without a paragraph in the PR description on what we get vs. what bytes we ship. The dep list in [`package.json`](package.json) is small on purpose. - **Run `pnpm build`** before pushing structural changes. Type errors block merge. --- ## Commits & pull requests - **One concern per PR.** Adding a skill + refactoring the SSE parser + bumping a dep is three PRs. - **Title is imperative + scope.** `add deck-product-launch skill`, `fix SSE backpressure when CLI hangs`, `docs: clarify skill frontmatter`. - **Body explains the why.** "What does this do" is usually obvious from the diff; "why does this need to exist" rarely is. - **Reference an issue** if there is one. If there isn't and the PR is non-trivial, open one first so we can agree the change is wanted before you spend the time. - **No squash-during-review.** Push fixups; we'll squash on merge. - **No force-push to a shared branch** unless the reviewer asked. We don't enforce a CLA. Apache-2.0 covers us; your contribution is licensed under the same. --- ## Reporting bugs Open an issue with: - What you ran (the exact `pnpm dev` invocation, or which UI button you clicked). - Which agent CLI was selected (Claude Code? Cursor Agent? …). - The skill that triggered it. - The relevant **server log tail** — most "the artifact never rendered" reports get diagnosed in 30 seconds when we can see `spawn ENOENT` or the CLI's actual error. - A screenshot if it's UI. For prompt-stack bugs ("the agent emitted a purple gradient hero, the constraint in `SKILL.md` was supposed to forbid that"), include the **full assistant message** so we can see whether the violation was the model or the prompt. --- ## Asking questions - Architecture question, design question, "is this a bug or a misuse" → [GitHub Discussions](https://github.com/nexu-io/html-anything/discussions) (preferred — searchable for the next person). - "How do I write a skill that does X" → open a discussion. We'll answer it and turn the answer into an entry in this guide if the pattern is missing. --- ## What we don't accept To keep the project focused, please don't open PRs that: - **Vendor a model runtime.** HTML Anything's whole bet is "your existing CLI is enough". We don't ship `pi-ai`, OpenAI keys, model loaders, or hosted inference proxies. - **Rewrite the frontend away from the current stack without prior discussion.** Next.js 16 App Router + React 19 + Tailwind v4 + TypeScript is the line. No Astro, Solid, Svelte, or other framework rewrites unless maintainers explicitly want that migration. - **Replace the SSE streaming model with WebSocket / long-polling.** SSE + iframe `srcdoc` append is the simplest thing that ships a live render; we keep it. - **Add telemetry / analytics / phone-home.** HTML Anything is local-first. The only outbound calls are to the agent CLI on your laptop, plus whatever the generated HTML itself references (Tailwind CDN, Google Fonts). - **Bundle a binary** without a license file and authorship attribution next to it. - **Add a skill whose `example.html` is empty / placeholder / clearly model-generated without review.** A skill is judged by what its example renders; an empty example is rejected immediately. If you're not sure whether your idea fits, open a discussion before writing the code. --- ## Localization maintenance The repo ships two languages at parity: English (`README.md`, `CONTRIBUTING.md`) and 简体中文 (`README.zh-CN.md`, `CONTRIBUTING.zh-CN.md`). When adding or renaming a skill, agent, or export target: - Update **both** READMEs' tables. - Update **both** CONTRIBUTING docs if your change introduces a new contribution surface or a new bar a PR has to clear. - **Skill prompt bodies stay in their source language.** Don't translate `SKILL.md` — it's part of the prompt stack the agent reads, and keeping one source language avoids multiplying prompt QA across locales. - Daemon error messages, file names, and agent-generated artifact text are known limitations unless a PR explicitly scopes them. --- ## License By contributing, you agree your contribution is licensed under the [Apache-2.0 License](LICENSE) of this repository. Vendored work retains its original license and authorship attribution — see each `src/lib/templates/skills//` folder's own `LICENSE` / `README.md` for what it inherits from upstream. The most prominent example is [`src/lib/templates/skills/deck-guizang-editorial/`](src/lib/templates/skills/deck-guizang-editorial/), which retains the original license and authorship attribution to [op7418](https://github.com/op7418). [skill]: https://docs.anthropic.com/en/docs/claude-code/skills