mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
Align app directories and isolate e2e tests (#102)
* chore: align app directories * test: consolidate external suites under e2e
This commit is contained in:
parent
b712fd21de
commit
cfebff9653
116 changed files with 361 additions and 418 deletions
1
.nvmrc
1
.nvmrc
|
|
@ -1 +0,0 @@
|
|||
22
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 会跑;挂了会换来一句「请修一下」。
|
||||
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -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)).
|
||||
|
||||
|
|
|
|||
|
|
@ -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
27
apps/daemon/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
8
apps/daemon/vitest.config.ts
Normal file
8
apps/daemon/vitest.config.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'node',
|
||||
include: ['**/*.test.{ts,tsx,js,mjs,cjs}'],
|
||||
},
|
||||
});
|
||||
|
|
@ -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() {
|
||||
0
next-env.d.ts → apps/web/next-env.d.ts
vendored
0
next-env.d.ts → apps/web/next-env.d.ts
vendored
|
|
@ -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
29
apps/web/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 656 B After Width: | Height: | Size: 656 B |
|
|
@ -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': '等待首批輸出中',
|
||||
|
|
@ -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 {
|
||||
8
apps/web/vitest.config.ts
Normal file
8
apps/web/vitest.config.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'node',
|
||||
include: ['src/**/*.test.{ts,tsx,js,mjs,cjs}'],
|
||||
},
|
||||
});
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue