| 1 | # Contributing to html-video |
| 2 | |
| 3 | > **English** · [中文](#中文) |
| 4 | |
| 5 | Thank you for your interest in contributing! html-video is an Apache-2.0 project by the [Open Design](https://github.com/nexu-io/open-design) team. We welcome contributions of all kinds — code, docs, templates, bug reports, and ideas. |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## Community |
| 10 | |
| 11 | - **Discord**: [Join the Open Design Discord](https://github.com/nexu-io/open-design#community) — the main hub for questions, design discussions, and real-time help. |
| 12 | - **X (Twitter)**: Follow [@nexudotio](https://x.com/nexudotio) for project updates. |
| 13 | - **GitHub Issues**: Bug reports, feature requests, and template proposals all go here. |
| 14 | |
| 15 | --- |
| 16 | |
| 17 | ## Development Setup |
| 18 | |
| 19 | ### Prerequisites |
| 20 | |
| 21 | | Requirement | Minimum Version | How to check | |
| 22 | |---|---|---| |
| 23 | | **Node.js** | 20+ | `node --version` | |
| 24 | | **pnpm** | 9+ | `pnpm --version` | |
| 25 | | **ffmpeg** | Any recent | `ffmpeg -version` | |
| 26 | | **Chromium** (or Playwright browsers) | See below | `npx playwright install chromium` | |
| 27 | |
| 28 | **Why Chromium?** The default [Hyperframes](https://github.com/heygen-com/hyperframes) engine renders videos by recording animated HTML in a headless Chromium browser. You need either: |
| 29 | |
| 30 | - A system Chromium/Chrome install (auto-detected), or |
| 31 | - Playwright's bundled Chromium: `npx playwright install chromium` |
| 32 | |
| 33 | **Why ffmpeg?** After recording each frame as WebM, ffmpeg encodes them to MP4 (libx264) and concatenates them into the final video. It's also used for the optional AI soundtrack mixing. |
| 34 | |
| 35 | ### Clone, Install, Build |
| 36 | |
| 37 | ```bash |
| 38 | git clone https://github.com/nexu-io/html-video.git |
| 39 | cd html-video |
| 40 | pnpm install |
| 41 | pnpm -r build |
| 42 | ``` |
| 43 | |
| 44 | This is a **pnpm workspace monorepo**. All packages live under `packages/` and templates under `templates/`. The `pnpm -r build` command builds every package in dependency order. |
| 45 | |
| 46 | ### Run Locally |
| 47 | |
| 48 | **Studio (browser UI):** |
| 49 | |
| 50 | ```bash |
| 51 | node packages/cli/dist/bin.js studio |
| 52 | # Opens at http://127.0.0.1:3071 |
| 53 | ``` |
| 54 | |
| 55 | **CLI tools:** |
| 56 | |
| 57 | ```bash |
| 58 | # Check what's installed and ready |
| 59 | node packages/cli/dist/bin.js doctor |
| 60 | |
| 61 | # Search templates by intent |
| 62 | node packages/cli/dist/bin.js search-templates --intent "data chart" --top 5 |
| 63 | ``` |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## Project Structure |
| 68 | |
| 69 | ``` |
| 70 | packages/ |
| 71 | ├── core/ Types, registries, orchestrator, MiniMax + ffmpeg audio |
| 72 | ├── content-graph/ Multi-frame storyboard IR (nodes + edges, topo-sort) |
| 73 | ├── runtime/ Agent runtime — detect / spawn / stream (13 agents) |
| 74 | ├── adapter-hyperframes/ Hyperframes engine adapter — Chromium + ffmpeg render |
| 75 | ├── cli/ `html-video` command + studio HTTP server + source fetching |
| 76 | └── project-studio/ Browser studio UI (chat, gallery, frames, soundtrack, export) |
| 77 | templates/ 21 curated, license-clean video templates |
| 78 | research/ RFCs (engine adapter / template metadata / agent skill / content-graph) |
| 79 | ``` |
| 80 | |
| 81 | --- |
| 82 | |
| 83 | ## How to Add a New Agent Runtime |
| 84 | |
| 85 | Adding support for a new coding agent is the most common contribution. It's a self-contained change in `packages/runtime/`. |
| 86 | |
| 87 | **Pattern (from Trae CLI PR #12):** |
| 88 | |
| 89 | ### 1. Create the agent definition file |
| 90 | |
| 91 | `packages/runtime/src/defs/<agent>.ts`: |
| 92 | |
| 93 | ```ts |
| 94 | import type { AgentDef } from '../types.js'; |
| 95 | |
| 96 | export const myAgent: AgentDef = { |
| 97 | id: 'my-agent', // kebab-case, stable |
| 98 | name: 'My Agent', // Human-readable |
| 99 | bin: 'my-agent-cli', // CLI binary name (looked up on PATH) |
| 100 | versionArgs: ['--version'], // Args to check version (for `doctor`) |
| 101 | buildArgs(prompt, ctx) { // Build spawn arguments |
| 102 | return ['--print', prompt]; |
| 103 | }, |
| 104 | streamFormat: 'plain', // 'plain' | 'claude-stream' | 'json-event-stream' | 'acp-json-rpc' |
| 105 | promptViaStdin: false, // true if sending prompt via stdin |
| 106 | installUrl: 'https://example.com/install', // Where users can get it |
| 107 | }; |
| 108 | ``` |
| 109 | |
| 110 | See `packages/runtime/src/types.ts` for the full `AgentDef` interface — it supports ACP JSON-RPC agents, HTTP-based agents (like the Anthropic API), binary fallbacks, and extra availability checks. |
| 111 | |
| 112 | ### 2. Register the agent |
| 113 | |
| 114 | In `packages/runtime/src/registry.ts`, import and add your agent to the `AGENT_DEFS` array: |
| 115 | |
| 116 | ```ts |
| 117 | import { myAgent } from './defs/my-agent.js'; |
| 118 | |
| 119 | export const AGENT_DEFS: AgentDef[] = [ |
| 120 | // ... existing agents |
| 121 | myAgent, |
| 122 | ]; |
| 123 | ``` |
| 124 | |
| 125 | Order matters: the first available agent is the default selection in the studio. |
| 126 | |
| 127 | ### 3. Test |
| 128 | |
| 129 | ```bash |
| 130 | pnpm --filter @html-video/runtime build |
| 131 | node packages/cli/dist/bin.js doctor # Your agent should appear if its binary is on PATH |
| 132 | ``` |
| 133 | |
| 134 | --- |
| 135 | |
| 136 | ## How to Add a New Template |
| 137 | |
| 138 | Templates live under `templates/<id>/` and are described by a `template.html-video.yaml` manifest. The studio scans templates at startup and the agent reads the manifest to understand what the template does and what inputs it needs. |
| 139 | |
| 140 | ### Minimum structure |
| 141 | |
| 142 | ``` |
| 143 | templates/frame-my-cool-animation/ |
| 144 | ├── template.html-video.yaml # Required — see format below |
| 145 | ├── source/index.html # Required — the animated HTML (Hyperframes engine) |
| 146 | ├── SKILL.md # Agent-readable instructions for filling in the template |
| 147 | ├── example.md # Example input |
| 148 | └── poster.svg / preview.png # Static preview image |
| 149 | ``` |
| 150 | |
| 151 | ### Provenance rules (RFC-07) |
| 152 | |
| 153 | Every template MUST follow the [RFC-07 provenance rules](research/2026-06-04-spec-07-ppt-to-template.md): |
| 154 | |
| 155 | 1. **License gate**: Only permissive open-source licenses (MIT, Apache-2.0, BSD, CC-BY, CC-BY-SA). No NC, ND, or unlicensed sources. |
| 156 | 2. **Three-layer attribution**: L1 (original design studio/designer) → L2 (skill/upstream author) → L3 (our transformation). All three must be recorded in `provenance`. |
| 157 | 3. **Naming**: Use descriptive feature names, not studio/designer names. ❌ `frame-pentagram-stat` → ✅ `frame-editorial-anchor` |
| 158 | 4. **Transformation quality**: Must add real animation timeline, use own sample data, and have identifiable redesign from the upstream source. |
| 159 | 5. **Deduplication**: Check against existing templates from the same upstream. Don't ship near-identical variants. |
| 160 | |
| 161 | ### manifest.yaml skeleton |
| 162 | |
| 163 | ```yaml |
| 164 | spec_version: 1 |
| 165 | id: frame-my-cool-animation |
| 166 | name: My Cool Animation |
| 167 | description: A short description for agents and the gallery. |
| 168 | engine: hyperframes |
| 169 | category: title-card |
| 170 | tags: [animation, reveal] |
| 171 | best_for: |
| 172 | - "Product launch teasers" |
| 173 | - "Social media shorts" |
| 174 | inputs: |
| 175 | schema: |
| 176 | type: object |
| 177 | required: [title] |
| 178 | properties: |
| 179 | title: { type: string, description: "Main headline" } |
| 180 | subtitle: { type: string, description: "Subtitle line" } |
| 181 | examples: |
| 182 | - title: "Hello World" |
| 183 | subtitle: "This is an example" |
| 184 | output: |
| 185 | formats: [mp4] |
| 186 | default_format: mp4 |
| 187 | duration: { type: variable, min_sec: 3, max_sec: 15 } |
| 188 | license: |
| 189 | spdx: Apache-2.0 |
| 190 | attribution_required: false |
| 191 | redistribution_allowed: true |
| 192 | commercial_use: true |
| 193 | provenance: |
| 194 | origin: |
| 195 | name: "Original Designer / Studio" |
| 196 | kind: design-studio |
| 197 | reference: "https://example.com" |
| 198 | via_skill: |
| 199 | name: upstream-skill-name |
| 200 | author: "Author Name" |
| 201 | url: https://github.com/author/upstream |
| 202 | license: MIT |
| 203 | source_file: path/to/source.html |
| 204 | transformation: > |
| 205 | Static design → animated Hyperframes timeline with CSS @keyframes. |
| 206 | Re-colored, original sample data. |
| 207 | ``` |
| 208 | |
| 209 | See [RFC-02](research/2026-05-26-spec-02-template-metadata.md) for the complete `template.html-video.yaml` specification. |
| 210 | |
| 211 | --- |
| 212 | |
| 213 | ## How to Add a New Engine Adapter |
| 214 | |
| 215 | The engine adapter interface ([RFC-01](research/2026-05-26-spec-01-engine-adapter.md)) lets any video rendering backend plug into html-video. The shipped adapter is `@html-video/adapter-hyperframes` — use it as the reference implementation. |
| 216 | |
| 217 | ### 1. Create a new package |
| 218 | |
| 219 | ``` |
| 220 | packages/adapter-<engine>/ |
| 221 | ├── package.json |
| 222 | ├── src/ |
| 223 | │ ├── index.ts # Export default EngineAdapter instance |
| 224 | │ ├── capabilities.ts # Static capability declaration |
| 225 | │ ├── validate.ts # Validate a template for this engine |
| 226 | │ └── render.ts # Core render implementation |
| 227 | └── tsconfig.json |
| 228 | ``` |
| 229 | |
| 230 | ### 2. Implement the EngineAdapter interface |
| 231 | |
| 232 | The core contract is defined in `packages/core/src/types.ts`: |
| 233 | |
| 234 | ```ts |
| 235 | export interface EngineAdapter { |
| 236 | id: EngineId; |
| 237 | name: string; |
| 238 | upstreamVersion: string; |
| 239 | capabilities: EngineCapabilities; |
| 240 | validate(template: Template): ValidationResult; |
| 241 | render(input: RenderInput, ctx: RenderContext): Promise<RenderOutput>; |
| 242 | preview?(template: Template, ctx: PreviewContext): Promise<PreviewHandle>; |
| 243 | listNativeTemplates?(): Promise<NativeTemplateRef[]>; |
| 244 | } |
| 245 | ``` |
| 246 | |
| 247 | Key conventions from RFC-01: |
| 248 | |
| 249 | - **Process isolation**: Each `render()` spawns an independent subprocess. Subprocess crashes must reject the promise and leave no partial output files. |
| 250 | - **Progress reporting**: 0-100% based on current frame / total frames. Stage hints: `preparing` (0-10%), `rendering` (10-95%), `muxing` (95-100%). |
| 251 | - **Cancellation**: Respect `ctx.signal.aborted` — kill subprocess, cleanup workDir temp files, reject with `AbortError`. |
| 252 | - **Package naming**: `@html-video/adapter-<name>`, peer-depend on the upstream engine. |
| 253 | |
| 254 | ### 3. Register in core |
| 255 | |
| 256 | The core dynamically loads adapters — once your package is in `packages/` and listed in `pnpm-workspace.yaml`, it will be auto-discovered at runtime. |
| 257 | |
| 258 | --- |
| 259 | |
| 260 | ## Code Style |
| 261 | |
| 262 | - **Language**: TypeScript (strict mode, `tsconfig.base.json` inheritance) |
| 263 | - **Formatting**: [Biome](https://biomejs.dev/) — 2 spaces, single quotes, trailing commas, semicolons, LF line endings. Run `pnpm format` to auto-format. |
| 264 | - **Linting**: `pnpm lint` runs Biome linter with recommended rules. |
| 265 | - **Monorepo tooling**: pnpm workspaces, `pnpm -r build` builds all packages in order. |
| 266 | - **Imports**: Use `.js` extensions in TypeScript imports (for ESM compatibility). |
| 267 | |
| 268 | ### Before submitting |
| 269 | |
| 270 | ```bash |
| 271 | pnpm typecheck # TypeScript across all packages |
| 272 | pnpm lint # Biome linter |
| 273 | pnpm format # Auto-format all files |
| 274 | pnpm test # Run all tests |
| 275 | ``` |
| 276 | |
| 277 | --- |
| 278 | |
| 279 | ## How to Submit a Pull Request |
| 280 | |
| 281 | 1. **Fork** the repository and create a branch from `main`. |
| 282 | 2. **Make your changes** — keep them focused. One PR = one logical change. |
| 283 | 3. **Test your changes** — run `pnpm typecheck && pnpm lint && pnpm test`. |
| 284 | 4. **Write a clear commit message** — follow conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`. |
| 285 | 5. **Open a PR** against `nexu-io/html-video:main`. |
| 286 | 6. **Describe** what you changed, why, and how to verify it. |
| 287 | |
| 288 | We review PRs regularly. If your PR adds a new feature, link to or include related tests. |
| 289 | |
| 290 | --- |
| 291 | |
| 292 | ## License |
| 293 | |
| 294 | By contributing, you agree that your contributions will be licensed under the [Apache-2.0 License](LICENSE) — same as the rest of the project. No contributor license agreement (CLA) is required. |
| 295 | |
| 296 | --- |
| 297 | |
| 298 | ## Questions? |
| 299 | |
| 300 | - **Real-time chat**: Join the [Open Design Discord](https://github.com/nexu-io/open-design#community) |
| 301 | - **Bugs & features**: Open an issue on [GitHub](https://github.com/nexu-io/html-video/issues) |
| 302 | - **Design decisions**: Read the RFCs in [`research/`](research/) |
| 303 | |
| 304 | --- |
| 305 | |
| 306 | ## 中文 {#中文} |
| 307 | |
| 308 | 感谢你有意为 html-video 贡献!html-video 是 [Open Design](https://github.com/nexu-io/open-design) 团队维护的 Apache-2.0 项目。我们欢迎所有形式的贡献 —— 代码、文档、模板、bug 反馈和想法。 |
| 309 | |
| 310 | ### 社区 |
| 311 | |
| 312 | - **Discord**:[加入 Open Design Discord](https://github.com/nexu-io/open-design#community) — 主要的问答、设计讨论和实时求助渠道。 |
| 313 | - **X(Twitter)**:关注 [@nexudotio](https://x.com/nexudotio) 获取项目动态。 |
| 314 | - **GitHub Issues**:提 bug、功能建议、模板提案都来这里。 |
| 315 | |
| 316 | ### 开发环境搭建 |
| 317 | |
| 318 | #### 前置依赖 |
| 319 | |
| 320 | | 依赖 | 最低版本 | 检查方式 | |
| 321 | |---|---|---| |
| 322 | | **Node.js** | 20+ | `node --version` | |
| 323 | | **pnpm** | 9+ | `pnpm --version` | |
| 324 | | **ffmpeg** | 任意较新版本 | `ffmpeg -version` | |
| 325 | | **Chromium**(或 Playwright 浏览器)| 见下文 | `npx playwright install chromium` | |
| 326 | |
| 327 | **为什么需要 Chromium?** 默认的 [Hyperframes](https://github.com/heygen-com/hyperframes) 引擎用无头 Chromium 录制带动画的 HTML 来渲染视频。你需要以下之一: |
| 328 | |
| 329 | - 系统安装的 Chromium/Chrome(自动检测),或 |
| 330 | - Playwright 内置的 Chromium:`npx playwright install chromium` |
| 331 | |
| 332 | **为什么需要 ffmpeg?** 渲出每帧 WebM 后,ffmpeg 将它们编码为 MP4(libx264)再拼接成最终视频。可选的 AI 配乐混音也要用到它。 |
| 333 | |
| 334 | #### 克隆、安装、构建 |
| 335 | |
| 336 | ```bash |
| 337 | git clone https://github.com/nexu-io/html-video.git |
| 338 | cd html-video |
| 339 | pnpm install |
| 340 | pnpm -r build |
| 341 | ``` |
| 342 | |
| 343 | 这是 **pnpm workspace 单体仓库**。所有包在 `packages/` 下,模板在 `templates/` 下。`pnpm -r build` 按依赖顺序构建所有包。 |
| 344 | |
| 345 | #### 本地运行 |
| 346 | |
| 347 | **Studio(浏览器界面):** |
| 348 | |
| 349 | ```bash |
| 350 | node packages/cli/dist/bin.js studio |
| 351 | # 在 http://127.0.0.1:3071 打开 |
| 352 | ``` |
| 353 | |
| 354 | **CLI 工具:** |
| 355 | |
| 356 | ```bash |
| 357 | # 查看已安装并可用的 agent 和引擎 |
| 358 | node packages/cli/dist/bin.js doctor |
| 359 | |
| 360 | # 按意图搜索模板 |
| 361 | node packages/cli/dist/bin.js search-templates --intent "数据图表" --top 5 |
| 362 | ``` |
| 363 | |
| 364 | ### 如何添加新的 Agent 运行时 |
| 365 | |
| 366 | 添加对新 coding agent 的支持是最常见的贡献类型。改动集中在 `packages/runtime/` 内。 |
| 367 | |
| 368 | **模式(参考 Trae CLI PR #12):** |
| 369 | |
| 370 | #### 1. 创建 agent 定义文件 |
| 371 | |
| 372 | `packages/runtime/src/defs/<agent>.ts`: |
| 373 | |
| 374 | ```ts |
| 375 | import type { AgentDef } from '../types.js'; |
| 376 | |
| 377 | export const myAgent: AgentDef = { |
| 378 | id: 'my-agent', // kebab-case,稳定不变 |
| 379 | name: 'My Agent', // 可读名称 |
| 380 | bin: 'my-agent-cli', // CLI 二进制名(在 PATH 上查找) |
| 381 | versionArgs: ['--version'], // 检查版本的参数(给 `doctor` 用) |
| 382 | buildArgs(prompt, ctx) { // 构建启动参数 |
| 383 | return ['--print', prompt]; |
| 384 | }, |
| 385 | streamFormat: 'plain', // 'plain' | 'claude-stream' | 'json-event-stream' | 'acp-json-rpc' |
| 386 | promptViaStdin: false, // 是否通过 stdin 传递 prompt |
| 387 | installUrl: 'https://example.com/install', // 安装指引链接 |
| 388 | }; |
| 389 | ``` |
| 390 | |
| 391 | 完整 `AgentDef` 接口见 `packages/runtime/src/types.ts` —— 它支持 ACP JSON-RPC agent、基于 HTTP 的 agent(如 Anthropic API)、二进制回退路径和额外的可用性检查。 |
| 392 | |
| 393 | #### 2. 注册 agent |
| 394 | |
| 395 | 在 `packages/runtime/src/registry.ts` 中 import 并将你的 agent 加入 `AGENT_DEFS` 数组: |
| 396 | |
| 397 | ```ts |
| 398 | import { myAgent } from './defs/my-agent.js'; |
| 399 | |
| 400 | export const AGENT_DEFS: AgentDef[] = [ |
| 401 | // ... 已有 agent |
| 402 | myAgent, |
| 403 | ]; |
| 404 | ``` |
| 405 | |
| 406 | 顺序很重要:第一个可用的 agent 是 studio 的默认选项。 |
| 407 | |
| 408 | #### 3. 测试 |
| 409 | |
| 410 | ```bash |
| 411 | pnpm --filter @html-video/runtime build |
| 412 | node packages/cli/dist/bin.js doctor # 如果二进制在 PATH 上,你的 agent 应该出现 |
| 413 | ``` |
| 414 | |
| 415 | ### 如何添加新模板 |
| 416 | |
| 417 | 模板放在 `templates/<id>/` 下,由 `template.html-video.yaml` 清单描述。studio 启动时扫描模板,agent 读取清单来了解模板用途和输入需求。 |
| 418 | |
| 419 | #### 最小目录结构 |
| 420 | |
| 421 | ``` |
| 422 | templates/frame-my-cool-animation/ |
| 423 | ├── template.html-video.yaml # 必选 —— 格式见下文 |
| 424 | ├── source/index.html # 必选 —— 带动画的 HTML(Hyperframes 引擎) |
| 425 | ├── SKILL.md # Agent 可读的填参说明 |
| 426 | ├── example.md # 示例输入 |
| 427 | └── poster.svg / preview.png # 静态预览图 |
| 428 | ``` |
| 429 | |
| 430 | #### 来源规范(RFC-07) |
| 431 | |
| 432 | 每个模板必须遵守 [RFC-07 来源规范](research/2026-06-04-spec-07-ppt-to-template.md): |
| 433 | |
| 434 | 1. **许可闸门**:只收明确宽松开源的许可(MIT、Apache-2.0、BSD、CC-BY、CC-BY-SA)。不收 NC、ND 或无许可的来源。 |
| 435 | 2. **三层署名**:L1(原始设计工作室/设计师)→ L2(skill/上游作者)→ L3(我们的转化)。三层都必须记在 `provenance` 里。 |
| 436 | 3. **命名**:用描述设计特征的名字,不要挪用工作室/设计师名。❌ `frame-pentagram-stat` → ✅ `frame-editorial-anchor` |
| 437 | 4. **转化质量**:必须新增真实的动效时间线、用自有示例数据、相比上游有可辨别的再设计。 |
| 438 | 5. **查重**:跟同一上游来源的已有模板比对,不提交几乎一样的变体。 |
| 439 | |
| 440 | ### 如何添加新引擎适配器 |
| 441 | |
| 442 | 引擎适配器接口([RFC-01](research/2026-05-26-spec-01-engine-adapter.md))让任何视频渲染后端都能接入 html-video。已发布的适配器是 `@html-video/adapter-hyperframes` —— 用它作为参考实现。 |
| 443 | |
| 444 | #### 1. 创建新包 |
| 445 | |
| 446 | ``` |
| 447 | packages/adapter-<engine>/ |
| 448 | ├── package.json |
| 449 | ├── src/ |
| 450 | │ ├── index.ts # 导出默认 EngineAdapter 实例 |
| 451 | │ ├── capabilities.ts # 静态能力声明 |
| 452 | │ ├── validate.ts # 校验模板能否被本引擎渲染 |
| 453 | │ └── render.ts # 核心渲染实现 |
| 454 | └── tsconfig.json |
| 455 | ``` |
| 456 | |
| 457 | #### 2. 实现 EngineAdapter 接口 |
| 458 | |
| 459 | 核心契约定义在 `packages/core/src/types.ts`,详见 [RFC-01](research/2026-05-26-spec-01-engine-adapter.md)。关键约定: |
| 460 | |
| 461 | - **进程隔离**:每次 `render()` 启动独立子进程。子进程崩溃必须 reject promise,不留下不完整的输出文件。 |
| 462 | - **进度报告**:按当前帧/总帧数算 0-100%。阶段提示:`preparing`(0-10%)、`rendering`(10-95%)、`muxing`(95-100%)。 |
| 463 | - **取消**:响应 `ctx.signal.aborted` —— 杀掉子进程,清理 workDir 临时文件,reject `AbortError`。 |
| 464 | - **包命名**:`@html-video/adapter-<name>`,peer-depend 上游引擎。 |
| 465 | |
| 466 | ### 代码风格 |
| 467 | |
| 468 | - **语言**:TypeScript(strict 模式,继承 `tsconfig.base.json`) |
| 469 | - **格式化**:[Biome](https://biomejs.dev/) —— 2 空格缩进、单引号、尾逗号、分号、LF 换行。运行 `pnpm format` 自动格式化。 |
| 470 | - **Lint**:`pnpm lint` 用 Biome linter 的推荐规则。 |
| 471 | - **单体仓库工具链**:pnpm workspace,`pnpm -r build` 按顺序构建所有包。 |
| 472 | - **Import**:TypeScript import 使用 `.js` 扩展名(ESM 兼容)。 |
| 473 | |
| 474 | #### 提交前检查 |
| 475 | |
| 476 | ```bash |
| 477 | pnpm typecheck # 全仓库 TypeScript 检查 |
| 478 | pnpm lint # Biome linter |
| 479 | pnpm format # 自动格式化所有文件 |
| 480 | pnpm test # 运行所有测试 |
| 481 | ``` |
| 482 | |
| 483 | ### 如何提交 Pull Request |
| 484 | |
| 485 | 1. **Fork** 仓库,从 `main` 创建分支。 |
| 486 | 2. **做出改动** —— 保持聚焦。一个 PR = 一个逻辑变更。 |
| 487 | 3. **测试你的改动** —— 运行 `pnpm typecheck && pnpm lint && pnpm test`。 |
| 488 | 4. **写清楚的 commit message** —— 遵循 conventional commits:`feat:`、`fix:`、`docs:`、`refactor:`、`test:`、`chore:`。 |
| 489 | 5. **发起 PR** 到 `nexu-io/html-video:main`。 |
| 490 | 6. **描述**你改了什么、为什么、怎么验证。 |
| 491 | |
| 492 | 我们会定期 review PR。如果你的 PR 加了新功能,请附带或链接相关测试。 |
| 493 | |
| 494 | ### 许可 |
| 495 | |
| 496 | 贡献即表示你同意你的贡献将按照 [Apache-2.0 许可](LICENSE) 授权 —— 和项目其他部分一致。不需要签署贡献者协议(CLA)。 |
| 497 | |
| 498 | ### 有问题? |
| 499 | |
| 500 | - **实时聊天**:加入 [Open Design Discord](https://github.com/nexu-io/open-design#community) |
| 501 | - **Bug 和功能**:在 [GitHub](https://github.com/nexu-io/html-video/issues) 提 issue |
| 502 | - **设计决策**:阅读 [`research/`](research/) 中的 RFC |
| 503 |