Align app directories and isolate e2e tests (#102)

* chore: align app directories

* test: consolidate external suites under e2e
This commit is contained in:
PerishFire 2026-04-30 09:47:03 +08:00 committed by GitHub
parent b712fd21de
commit cfebff9653
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
116 changed files with 361 additions and 418 deletions

1
.nvmrc
View file

@ -1 +0,0 @@
22

View file

@ -14,8 +14,8 @@ This guide tells you exactly where to look for each type of contribution and wha
|---|---|---|---|
| Make OD render a new kind of artifact (an invoice, an iOS Settings screen, a one-pager…) | a **Skill** | [`skills/<your-skill>/`](skills/) | one folder, ~2 files |
| Make OD speak a new brand's visual language | a **Design System** | [`design-systems/<brand>/DESIGN.md`](design-systems/) | one Markdown file |
| Hook up a new coding-agent CLI | an **Agent adapter** | [`daemon/agents.js`](daemon/agents.js) | ~10 lines in one array |
| Add a feature, fix a bug, lift a UX pattern from [`open-codesign`][ocod] | code | `src/`, `daemon/` | normal PR |
| Hook up a new coding-agent CLI | an **Agent adapter** | [`apps/daemon/agents.js`](apps/daemon/agents.js) | ~10 lines in one array |
| Add a feature, fix a bug, lift a UX pattern from [`open-codesign`][ocod] | code | `apps/web/src/`, `apps/daemon/` | normal PR |
| Improve docs, port a section to 中文, fix typos | docs | `README.md`, `README.zh-CN.md`, `docs/`, `QUICKSTART.md` | one PR |
If you're not sure which bucket your idea is in, [open a discussion / issue first](https://github.com/nexu-io/open-design/issues/new) and we'll point you at the right surface.
@ -169,7 +169,7 @@ The 69 product systems we ship are imported from [`VoltAgent/awesome-design-md`]
## Adding a new coding-agent CLI
Hooking up a new agent (e.g. some new shop's `foo-coder` CLI) is one entry in [`daemon/agents.js`](daemon/agents.js):
Hooking up a new agent (e.g. some new shop's `foo-coder` CLI) is one entry in [`apps/daemon/agents.js`](apps/daemon/agents.js):
```javascript
{
@ -182,7 +182,7 @@ Hooking up a new agent (e.g. some new shop's `foo-coder` CLI) is one entry in [`
}
```
That's it — daemon will detect it on `PATH`, the picker shows it, the chat path works. If the CLI emits **typed events** (like Claude Code's `--output-format stream-json`), wire a parser in [`daemon/claude-stream.js`](daemon/claude-stream.js) and set `streamFormat: 'claude-stream-json'`.
That's it — daemon will detect it on `PATH`, the picker shows it, the chat path works. If the CLI emits **typed events** (like Claude Code's `--output-format stream-json`), wire a parser in [`apps/daemon/claude-stream.js`](apps/daemon/claude-stream.js) and set `streamFormat: 'claude-stream-json'`.
Bar for merging:
@ -202,7 +202,7 @@ We're not pedantic about formatting (Prettier on save is fine), but two rules ar
Beyond that:
- **Don't narrate.** No `// import the module`, no `// loop through items`. If the code reads obviously, the comment is noise. Save comments for non-obvious intent or constraints the code can't express.
- **TypeScript** for `src/`. The daemon (`daemon/`) is plain ESM JavaScript with JSDoc when types matter — keep it that way.
- **TypeScript** for `apps/web/src/`. The daemon (`apps/daemon/`) is plain ESM JavaScript with JSDoc when types matter — keep it that way.
- **No new top-level dependencies** without a paragraph in the PR description on what we get vs. what bytes we ship. The dep list in [`package.json`](package.json) is small on purpose.
- **Run `pnpm typecheck`** before pushing. CI runs it; failing it earns a "please fix" comment.

View file

@ -14,8 +14,8 @@
|---|---|---|---|
| 让 OD 渲染一种新的 artifact一份发票、一个 iOS 设置页、一张 one-pager…… | 一个 **Skill** | [`skills/<your-skill>/`](skills/) | 一个文件夹,约 2 个文件 |
| 让 OD 说一种新品牌的视觉语言 | 一套 **Design System** | [`design-systems/<brand>/DESIGN.md`](design-systems/) | 一个 Markdown 文件 |
| 接入一个新的 coding-agent CLI | 一个 **Agent adapter** | [`daemon/agents.js`](daemon/agents.js) | 一个数组里 ~10 行 |
| 加功能、修 bug、从 [`open-codesign`][ocod] 移植一个 UX 模式 | 代码 | `src/`、`daemon/` | 普通 PR |
| 接入一个新的 coding-agent CLI | 一个 **Agent adapter** | [`apps/daemon/agents.js`](apps/daemon/agents.js) | 一个数组里 ~10 行 |
| 加功能、修 bug、从 [`open-codesign`][ocod] 移植一个 UX 模式 | 代码 | `apps/web/src/`、`apps/daemon/` | 普通 PR |
| 改文档、补中文翻译、修错别字 | 文档 | `README.md`、`README.zh-CN.md`、`docs/`、`QUICKSTART.md` | 一个 PR |
不确定自己想做的属于哪一桶?[先开 issue / discussion](https://github.com/nexu-io/open-design/issues/new),我们告诉你该改哪个面。
@ -168,7 +168,7 @@ design-systems/your-brand/
## 接入一个新的 coding-agent CLI
接入一个新 agent比如某个新 shop 的 `foo-coder` CLI就是在 [`daemon/agents.js`](daemon/agents.js) 里加一项:
接入一个新 agent比如某个新 shop 的 `foo-coder` CLI就是在 [`apps/daemon/agents.js`](apps/daemon/agents.js) 里加一项:
```javascript
{
@ -181,7 +181,7 @@ design-systems/your-brand/
}
```
完事 —— daemon 会在 `PATH` 上检测到它、picker 显示出来、对话路径就通了。如果这个 CLI 吐 **类型化事件**(像 Claude Code 的 `--output-format stream-json`),在 [`daemon/claude-stream.js`](daemon/claude-stream.js) 里写一个 parser并把 `streamFormat` 设成 `'claude-stream-json'`
完事 —— daemon 会在 `PATH` 上检测到它、picker 显示出来、对话路径就通了。如果这个 CLI 吐 **类型化事件**(像 Claude Code 的 `--output-format stream-json`),在 [`apps/daemon/claude-stream.js`](apps/daemon/claude-stream.js) 里写一个 parser并把 `streamFormat` 设成 `'claude-stream-json'`
合并硬线:
@ -201,7 +201,7 @@ design-systems/your-brand/
除此之外:
- **不要写废话注释。** 不要 `// 引入这个模块`、不要 `// 遍历元素`。如果代码本身一眼能读,注释就是噪音。注释只用来说明非显而易见的意图、或者代码本身表达不出来的约束。
- **`src/` 用 TypeScript。** Daemon (`daemon/`) 是纯 ESM JavaScript类型重要的地方用 JSDoc —— 保持这样。
- **`apps/web/src/` 用 TypeScript。** Daemon (`apps/daemon/`) 是纯 ESM JavaScript类型重要的地方用 JSDoc —— 保持这样。
- **不要随便加顶层依赖。** PR 描述里至少要有一段,说明引入它能换到什么、又新增了多少 bundle 字节。[`package.json`](package.json) 的依赖少是有意为之。
- **推之前跑 `pnpm typecheck`。** CI 会跑;挂了会换来一句「请修一下」。

View file

@ -114,7 +114,7 @@ open-design/
## Troubleshooting
- **"no agents found on PATH"** — install one of: `claude`, `codex`, `gemini`, `opencode`, `cursor-agent`, `qwen`, `copilot`. 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.
- **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 `apps/daemon/agents.js` `buildArgs` if you need to tweak.
- **artifact never renders** — the model produced text without wrapping in `<artifact>`. 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
@ -122,6 +122,6 @@ open-design/
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` now matches the shipped stack: Next.js 16 App Router in front, local daemon behind it, and `next.config.ts` rewrites in dev to keep the browser talking to the same `/api` surface.
- `docs/skills-protocol.md` describes the full `od:` frontmatter (typed inputs, sliders, capability gating). This MVP reads `name` / `description` / `triggers` / `od.mode` / `od.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/skills-protocol.md` describes the full `od:` frontmatter (typed inputs, sliders, capability gating). This MVP reads `name` / `description` / `triggers` / `od.mode` / `od.design_system.requires` only — extend `apps/daemon/skills.js` to add the rest.
- `docs/agent-adapters.md` foresees richer dispatch (capability detection, streaming tool-calls). Our `apps/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`.

View file

@ -30,7 +30,7 @@ That's not "AI tries to design something". That's an AI that has been trained, b
OD 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).
- [**`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 [`apps/web/src/prompts/discovery.ts`](apps/web/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.
@ -214,7 +214,7 @@ DISCOVERY directives (turn-1 form, turn-2 brand branch, TodoWrite, 5-dim critiq
+ (deck kind, no skill seed) 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.
Every layer is composable. Every layer is a file you can edit. Read [`apps/web/src/prompts/system.ts`](apps/web/src/prompts/system.ts) and [`apps/web/src/prompts/discovery.ts`](apps/web/src/prompts/discovery.ts) to see the actual contract.
## Architecture
@ -453,11 +453,11 @@ When the user has no brand spec, the agent emits a second form with five curated
| 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).
Full spec → [`apps/web/src/prompts/directions.ts`](apps/web/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 OD'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:
The whole machinery below is the [`huashu-design`](https://github.com/alchaincyf/huashu-design) playbook, ported into OD's prompt-stack and made enforceable per-skill via the side-file pre-flight. Read [`apps/web/src/prompts/discovery.ts`](apps/web/src/prompts/discovery.ts) for the live wording:
- **Question form first.** Turn 1 is `<question-form>` 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.**
@ -511,7 +511,7 @@ Auto-detected from `PATH` on daemon boot. No config required.
| [GitHub Copilot CLI](https://github.com/features/copilot/cli) | `copilot` | `--output-format json` (typed events) | `copilot -p <prompt> --allow-all-tools --output-format json` |
| 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).
Adding a new CLI is one entry in [`apps/daemon/agents.js`](apps/daemon/agents.js). Streaming format is one of `claude-stream-json` (typed events) or `plain` (raw text).
## References & lineage
@ -520,7 +520,7 @@ Every external project this repo borrows from. Each link goes to the source so y
| Project | Role here |
|---|---|
| [`Claude Design`][cd] | The closed-source product this repo is the open-source alternative to. |
| [**`alchaincyf/huashu-design`**](https://github.com/alchaincyf/huashu-design) | The design-philosophy core. Junior-Designer workflow, the 5-step brand-asset protocol, anti-AI-slop checklist, 5-dimensional self-critique, and the "5 schools × 20 design philosophies" library behind our direction picker — all distilled into [`src/prompts/discovery.ts`](src/prompts/discovery.ts) and [`src/prompts/directions.ts`](src/prompts/directions.ts). |
| [**`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 [`apps/web/src/prompts/discovery.ts`](apps/web/src/prompts/discovery.ts) and [`apps/web/src/prompts/directions.ts`](apps/web/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. |
@ -562,7 +562,7 @@ Issues, PRs, new skills, and new design systems are all welcome. The highest-lev
- **Add a skill** — drop a folder into [`skills/`](skills/) following the [`SKILL.md`][skill] convention.
- **Add a design system** — drop a `DESIGN.md` into [`design-systems/<brand>/`](design-systems/) using the 9-section schema.
- **Wire up a new coding-agent CLI** — one entry in [`daemon/agents.js`](daemon/agents.js).
- **Wire up a new coding-agent CLI** — one entry in [`apps/daemon/agents.js`](apps/daemon/agents.js).
Full walkthrough, bar-for-merging, code style, and what we don't accept → [`CONTRIBUTING.md`](CONTRIBUTING.md) ([简体中文](CONTRIBUTING.zh-CN.md)).

View file

@ -30,7 +30,7 @@ Anthropic 的 [Claude Design][cd]2026-04-17 发布,基于 Opus 4.7)让大
OD 站在四个开源项目的肩膀上:
- [**`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)。
- [**`alchaincyf/huashu-design`**(花叔的画术)](https://github.com/alchaincyf/huashu-design) —— 设计哲学的指南针。Junior-Designer 工作流、5 步品牌资产协议、anti-AI-slop checklist、五维自评审、以及方向选择器背后的「5 流派 × 20 种设计哲学」思路 —— 全部蒸馏进 [`apps/web/src/prompts/discovery.ts`](apps/web/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 的世界观。
@ -214,7 +214,7 @@ DISCOVERY 指令 turn-1 表单、turn-2 品牌分支、TodoWrite、
+ deck kind 且无 skill 种子时) DECK_FRAMEWORK_DIRECTIVE nav / counter / scroll / print
```
每一层都可组合。每一层都是一个你能改的文件。看 [`src/prompts/system.ts`](src/prompts/system.ts) 和 [`src/prompts/discovery.ts`](src/prompts/discovery.ts) 就知道真实契约长什么样。
每一层都可组合。每一层都是一个你能改的文件。看 [`apps/web/src/prompts/system.ts`](apps/web/src/prompts/system.ts) 和 [`apps/web/src/prompts/discovery.ts`](apps/web/src/prompts/discovery.ts) 就知道真实契约长什么样。
## 技术架构
@ -453,11 +453,11 @@ open-design/
| Brutalist | 粗粝、巨字、无阴影、刺眼强调 | Bloomberg Businessweek · Achtung |
| Soft warm | 大方、低对比、桃色中性 | Notion 营销页 · Apple Health |
完整 spec → [`src/prompts/directions.ts`](src/prompts/directions.ts)。
完整 spec → [`apps/web/src/prompts/directions.ts`](apps/web/src/prompts/directions.ts)。
## 反 AI Slop 机制
下面整套机制都是 [`huashu-design`](https://github.com/alchaincyf/huashu-design) 的 playbook被移植进 OD 的提示词栈,并通过 skill 副文件 pre-flight 让每个 skill 都能落地执行。看 [`src/prompts/discovery.ts`](src/prompts/discovery.ts) 是真实文案:
下面整套机制都是 [`huashu-design`](https://github.com/alchaincyf/huashu-design) 的 playbook被移植进 OD 的提示词栈,并通过 skill 副文件 pre-flight 让每个 skill 都能落地执行。看 [`apps/web/src/prompts/discovery.ts`](apps/web/src/prompts/discovery.ts) 是真实文案:
- **先表单。** Turn 1 必须是 `<question-form>`**不准** thinking、不准 tools、不准旁白。用户用 radio 速度选默认。
- **品牌资产协议。** 用户贴截图或 URL 时agent 走 5 步流程(定位 · 下载 · grep hex · 写 `brand-spec.md` · 复述)才能开始写 CSS。**绝不从记忆里猜品牌色**。
@ -511,7 +511,7 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。
| [GitHub Copilot CLI](https://github.com/features/copilot/cli) | `copilot` | `--output-format json`(类型化事件) | `copilot -p <prompt> --allow-all-tools --output-format json` |
| Anthropic API · BYOK | n/a | SSE 直连 | 没装任何 CLI 时的浏览器兜底 |
加一个新 CLI = 在 [`daemon/agents.js`](daemon/agents.js) 里加一项。流式格式从 `claude-stream-json`(类型化事件)和 `plain`(原始文本)两种里选一个。
加一个新 CLI = 在 [`apps/daemon/agents.js`](apps/daemon/agents.js) 里加一项。流式格式从 `claude-stream-json`(类型化事件)和 `plain`(原始文本)两种里选一个。
## 引用与师承
@ -520,7 +520,7 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。
| 项目 | 在这里的角色 |
|---|---|
| [`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)。 |
| [**`alchaincyf/huashu-design`**(花叔的画术)](https://github.com/alchaincyf/huashu-design) | 设计哲学的核心。Junior-Designer 工作流、5 步品牌资产协议、anti-AI-slop checklist、五维自评审、以及方向选择器背后的「5 流派 × 20 种设计哲学」库 —— 全部蒸馏进 [`apps/web/src/prompts/discovery.ts`](apps/web/src/prompts/discovery.ts) 与 [`apps/web/src/prompts/directions.ts`](apps/web/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。 |
@ -562,7 +562,7 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。
- **加一个 skill** —— 往 [`skills/`](skills/) 丢一个文件夹,遵循 [`SKILL.md`][skill] 规范。
- **加一套 design system** —— 往 [`design-systems/<brand>/`](design-systems/) 丢一份 `DESIGN.md`,用 9 段式 schema。
- **接入一个新的 coding-agent CLI** —— 在 [`daemon/agents.js`](daemon/agents.js) 里加一项。
- **接入一个新的 coding-agent CLI** —— 在 [`apps/daemon/agents.js`](apps/daemon/agents.js) 里加一项。
完整流程、合并硬线、代码风格、我们不接收的 PR 类型 → [`CONTRIBUTING.zh-CN.md`](CONTRIBUTING.zh-CN.md)[English](CONTRIBUTING.md))。

27
apps/daemon/package.json Normal file
View file

@ -0,0 +1,27 @@
{
"name": "@open-design/daemon",
"version": "0.1.0",
"private": true,
"type": "module",
"bin": {
"od": "./cli.js"
},
"scripts": {
"daemon": "node cli.js --no-open",
"dev": "node cli.js --no-open",
"start": "node cli.js",
"test": "vitest run -c vitest.config.ts"
},
"dependencies": {
"better-sqlite3": "^11.10.0",
"express": "^4.19.2",
"jszip": "^3.10.1",
"multer": "^1.4.5-lts.1"
},
"devDependencies": {
"vitest": "^2.1.8"
},
"engines": {
"node": "~24"
}
}

View file

@ -59,16 +59,16 @@ import {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PROJECT_ROOT = path.resolve(__dirname, '..');
const PROJECT_ROOT = path.resolve(__dirname, '../..');
// Built web app lives in `out/` — that's where Next.js writes the static
// export configured in next.config.ts. The folder name used to be `dist/`
// when this project shipped with Vite; the daemon serves whatever the
// frontend toolchain emits, no further config needed.
const STATIC_DIR = path.join(PROJECT_ROOT, 'out');
const STATIC_DIR = path.join(PROJECT_ROOT, 'apps', 'web', 'out');
const SKILLS_DIR = path.join(PROJECT_ROOT, 'skills');
const DESIGN_SYSTEMS_DIR = path.join(PROJECT_ROOT, 'design-systems');
const RUNTIME_DATA_DIR = process.env.OD_DATA_DIR
? path.resolve(process.env.OD_DATA_DIR)
? path.resolve(PROJECT_ROOT, process.env.OD_DATA_DIR)
: path.join(PROJECT_ROOT, '.od');
const ARTIFACTS_DIR = path.join(RUNTIME_DATA_DIR, 'artifacts');
const PROJECTS_DIR = path.join(RUNTIME_DATA_DIR, 'projects');
@ -721,7 +721,7 @@ export async function startServer({ port = 7456, returnServer = false } = {}) {
// Project files. Each project owns a flat folder under .od/projects/<id>/
// containing every file the user has uploaded, pasted, sketched, or that
// the agent has generated. Names are sanitized; paths are confined to the
// project's own folder (see daemon/projects.js).
// project's own folder (see apps/daemon/projects.js).
app.get('/api/projects/:id/files', async (req, res) => {
try {
const files = await listFiles(PROJECTS_DIR, req.params.id);

View file

@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
include: ['**/*.test.{ts,tsx,js,mjs,cjs}'],
},
});

View file

@ -7,7 +7,7 @@ import { ClientApp } from './client-app';
//
// For `output: 'export'` we return a single empty `slug` so Next.js emits
// one shell HTML at out/index.html; the daemon's SPA fallback (see
// daemon/server.js) serves it for any unknown non-API path so deep links
// apps/daemon/server.js) serves it for any unknown non-API path so deep links
// still hydrate to the right view. In dev we leave `dynamicParams` at its
// default (true) so `next dev` happily renders /projects/<id> directly.
export function generateStaticParams() {

View file

@ -1,6 +1,6 @@
import type { NextConfig } from 'next';
// Daemon port the local Express server binds to (see daemon/cli.js). The
// Daemon port the local Express server binds to (see apps/daemon/cli.js). The
// dev-all launcher overrides OD_PORT after probing for a free port; we read
// the same env so /api, /artifacts, and /frames always reach the right
// daemon instance during `next dev`.
@ -17,6 +17,7 @@ const DAEMON_ORIGIN = `http://127.0.0.1:${DAEMON_PORT}`;
const isProd = process.env.NODE_ENV !== 'development';
const nextConfig: NextConfig = {
allowedDevOrigins: ['127.0.0.1'],
reactStrictMode: true,
// Keep the bundle output predictable so the daemon's STATIC_DIR can point
// at it without any glob trickery.
@ -32,7 +33,7 @@ const nextConfig: NextConfig = {
}
: {
async rewrites() {
// In dev we run the daemon on a sibling port; mirror the old Vite
// In dev we run the daemon on a sibling port; proxy the app API
// proxy so the SPA can hit /api, /artifacts, and /frames without
// CORS gymnastics. SSE on /api/chat works through this rewrite
// because Next.js's dev server streams responses unbuffered.

29
apps/web/package.json Normal file
View file

@ -0,0 +1,29 @@
{
"name": "@open-design/web",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"typecheck": "tsc -b --noEmit",
"test": "vitest run -c vitest.config.ts"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.32.1",
"next": "^16.2.4",
"openai": "^6.35.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/node": "^20.17.10",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"typescript": "^5.6.3",
"vitest": "^2.1.8"
},
"engines": {
"node": "~24"
}
}

View file

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 183 KiB

View file

Before

Width:  |  Height:  |  Size: 656 B

After

Width:  |  Height:  |  Size: 656 B

View file

@ -82,6 +82,13 @@ export const zhTW: Dict = {
'settings.noAgentSelected': '尚未選擇代理',
'settings.language': '介面語言',
'settings.languageHint': '切換介面語言,設定僅儲存在當前瀏覽器。',
'settings.modelPicker': '模型',
'settings.reasoningPicker': '推理強度',
'settings.modelPickerHint':
'當 CLI 提供 `models` 命令時會自動拉取。選擇「預設」則沿用 CLI 自身的設定;選擇「自訂」可手動輸入任何 CLI 支援的模型 id。',
'settings.modelCustom': '自訂(在下方填寫)…',
'settings.modelCustomLabel': '自訂模型 id',
'settings.modelCustomPlaceholder': '例如 anthropic/claude-sonnet-4-6',
'entry.tabDesigns': '我的設計',
'entry.tabExamples': '範例',
@ -117,6 +124,9 @@ export const zhTW: Dict = {
'newproj.create': '建立',
'newproj.createFromTemplate': '基於範本建立',
'newproj.createDisabledTitle': '請先在任意專案內透過「分享」選單將其儲存為範本。',
'newproj.importClaudeZip': '匯入 Claude Design ZIP',
'newproj.importClaudeZipTitle': '匯入 Claude Design 匯出的 .zip 檔案',
'newproj.importingClaudeZip': '正在匯入…',
'newproj.privacyFooter': '預設情況下只有你能看到自己的專案。',
'newproj.designSystem': '設計系統',
'newproj.dsNoneFreeform': '不指定 — 自由發揮',
@ -208,6 +218,10 @@ export const zhTW: Dict = {
'avatar.metaOffline': '未執行',
'avatar.metaSelected': '已選',
'avatar.noAgentSelected': '尚未選擇代理',
'avatar.modelSection': '模型',
'avatar.modelLabel': '模型',
'avatar.reasoningLabel': '推理',
'avatar.customSuffix': '(自訂)',
'project.backToProjects': '返回專案列表',
'project.metaFreeform': '自由設計',
@ -315,6 +329,10 @@ export const zhTW: Dict = {
'designFiles.kindSketch': '草圖',
'designFiles.kindText': '文字',
'designFiles.kindCode': '腳本',
'designFiles.kindPdf': 'PDF',
'designFiles.kindDocument': '文件',
'designFiles.kindPresentation': '簡報',
'designFiles.kindSpreadsheet': '試算表',
'designFiles.kindBinary': '二進位',
'pasteDialog.title': '貼上文字',
'pasteDialog.hint': '將儲存到專案資料夾中,名稱隨你定。',
@ -338,6 +356,11 @@ export const zhTW: Dict = {
'fileViewer.share': '分享',
'fileViewer.binaryMeta': '二進位 · {size}',
'fileViewer.binaryNote': '二進位檔案({size} 位元組)。請下載或在本機開啟檢視。',
'fileViewer.pdfMeta': 'PDF · {size}',
'fileViewer.documentMeta': '文件',
'fileViewer.presentationMeta': '簡報',
'fileViewer.spreadsheetMeta': '試算表',
'fileViewer.previewUnavailable': '無法產生預覽,請下載或開啟檔案檢視。',
'fileViewer.download': '下載',
'fileViewer.open': '開啟',
'fileViewer.imageMeta': '圖片 · {size}',
@ -434,6 +457,10 @@ export const zhTW: Dict = {
'assistant.producedFiles': '本輪產出的檔案',
'assistant.openFile': '開啟',
'assistant.downloadFile': '下載',
'assistant.unfinishedLabel': '已停止,仍有未完成任務',
'assistant.unfinishedSummary': '剩餘 {n} 個任務',
'assistant.unfinishedMore': '還有 {n} 個',
'assistant.continueRemaining': '繼續剩餘任務',
'assistant.thinking': '思考中',
'assistant.systemReminder': '系統提示',
'assistant.waitingFirstOutput': '等待首批輸出中',

View file

@ -175,7 +175,7 @@ function parseFrame(frame: string): ParsedFrame | null {
}
}
// Translate a raw `agent` SSE payload (what daemon/claude-stream.js emits)
// Translate a raw `agent` SSE payload (what apps/daemon/claude-stream.js emits)
// into the UI's AgentEvent union. Keep this liberal — unknown types just
// return null so the UI ignores them instead of rendering garbage.
function translateAgentEvent(data: Record<string, unknown>): AgentEvent | null {

View file

@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
include: ['src/**/*.test.{ts,tsx,js,mjs,cjs}'],
},
});

View file

@ -176,7 +176,7 @@ The adapter declares which strategy to use via `capabilities().nativeSkillLoadin
### 5.7 GitHub Copilot CLI
- Invocation: `copilot -p "<prompt>" --allow-all-tools --output-format json --add-dir <skills> --add-dir <design-systems>`. `--allow-all-tools` is mandatory in non-interactive mode — without it the CLI blocks waiting for human approval on every tool call. Unlike Codex (where `exec` is a dedicated headless subcommand with auto-approve baked in) or Claude Code (which inherits its permission policy from `~/.claude/settings.json`), Copilot's `-p` mode always prompts unless this flag is passed explicitly. `--add-dir` (repeatable) widens the path-level sandbox so Copilot can read skill seeds and design-system specs that live outside the project cwd.
- Streaming: `--output-format json` emits JSONL with the same expressive shape as Claude Code's stream-json (`assistant.reasoning_delta`, `assistant.message_delta`, `tool.execution_start/complete`, `result`). `daemon/copilot-stream.js` maps these onto the same UI events as `claude-stream.js`.
- Streaming: `--output-format json` emits JSONL with the same expressive shape as Claude Code's stream-json (`assistant.reasoning_delta`, `assistant.message_delta`, `tool.execution_start/complete`, `result`). `apps/daemon/copilot-stream.js` maps these onto the same UI events as `claude-stream.js`.
- Skill loading: prompt injection only. Github Copilot's tool catalog includes a `skill` tool — native format worth reverse-engineering later.
- Surgical edits: dedicated `edit` tool.
- Detection assumes Copilot is already authenticated, via one of: `copilot login` (subcommand, OAuth device flow), the interactive `/login` slash command inside `copilot` with no args.

View file

@ -30,7 +30,7 @@
- `title`:人可读的用例名称
- `kind`:项目类型,比如 `prototype`、`deck`、`workspace`
- `flow`Playwright 里对应的自动化流程分支
- `automated`:当前是否会被 `npm run test:ui` 执行
- `automated`:当前是否会被 `pnpm run test:ui` 执行
- `description`:覆盖目标和场景说明
- `create`:创建项目时要用到的输入
- `prompt`:主输入内容
@ -91,7 +91,13 @@
## 运行方式
```bash
npm run test:ui
pnpm run test:ui
```
也可以直接在独立测试包内运行:
```bash
pnpm --filter @open-design/e2e test:ui
```
运行完成后会自动生成:
@ -111,5 +117,5 @@ npm run test:ui
如果要带界面调试:
```bash
npm run test:ui:headed
pnpm run test:ui:headed
```

Some files were not shown because too many files have changed in this diff Show more