AGENTS.md · diff

git:20261020.e2c517a to git:20261020.47dc2da

82 added, 111 removed. Audit B to B.

# AGENTS.md
给在本仓库里干活的代理(和人)看的说明。
## 常用命令
| 命令 | 作用 |
|---|---|
| `pnpm install` | 安装依赖 |
- | `pnpm run build` | 构建 `lib/`(改了前端或主机代码后,测试实例要重启才看得到) |
- | `pnpm run typecheck` | 类型检查 |
- | `pnpm run lint` | 代码检查(biome) |
- | `pnpm test` | 单元测试(vitest) |
- | `pnpm run hero` | 重做 README 封面图(中英两张,见下文) |
+ | `pnpm run build` | 构建 `lib/`(改了前端或主机代码,测试实例要重启才看得到) |
+ | `pnpm run typecheck` / `lint` / `pnpm test` | 类型检查 / biome / vitest |
+ | `pnpm run hero` | 重做 README 封面图 |
+ | `pnpm run timeline` | 重做 README 版本线 |
+ | `pnpm run release-card [版本] [zh\|en]` | 出版本更新卡片 |
+ 出图的几条命令都要一个带 DevTools 端口的 Chrome:`node tests/lib/cdp-chrome-launch.mjs 9222`;除 `hero` 外都不用起 dsh。
+
## 发布与推送
```sh
- # 1. 先构建再验证:打包守卫要读 lib/,没构建过它整块跳过(那就等于没查)
+ # 1. 先构建再验证:打包守卫读 lib/,没构建就整块跳过
pnpm run build && pnpm run typecheck && pnpm run lint && pnpm test
-
- # 2. CHANGELOG.md:把 Unreleased 那节落成 ## [<ver>] - <日期>,随本版一起提交;不更新不得发版
-
- # 3. 版本自增(会同时提交并打 tag;只想改版本号就加 --no-git-tag-version)
- pnpm version minor # 按下面的判据选 patch / minor / major
-
- # 4. 提交 + 打标签(上一步已经做了就跳过)
- git commit -am "release: workflow-lite v<ver>"
- git tag workflow-lite-v<ver>
-
- # 5. 推 git + 发 npm
- git push && git push --tags
- npm publish # publishConfig 已声明 access: public
-
- # 6. GitHub Release(手动,没有 workflow;notes 用 CHANGELOG 本节内容)
+ # 2. CHANGELOG:Unreleased 落成 ## [<ver>] - <日期>,随本版提交;不更新不发版
+ # 3. 版本自增(同时提交并打 tag workflow-lite-v<ver>)
+ pnpm version minor
+ # 4. 推送 + 发 npm
+ git push && git push --tags && npm publish
+ # 5. GitHub Release(手动;notes 用 CHANGELOG 本节)
gh release create workflow-lite-v<ver> --title "v<ver>" --notes-file <notes 文件>
```
- 发版相关的几条:
-
- - **版本号按严格 SemVer 判,不看改动大小**:新增功能(Added)→ `minor`;修 bug、只改措辞与内部实现(Fixed / 纯重构)→ `patch`;不兼容变更(删或改工具 action 与参数、数据格式、RPC 目标,或让旧版读不了新写的文件)→ `major`。判据落在**公共 API**上:工具接口、工作流 JSON / state.yaml 的数据格式、RPC 目标、peer 区间。**只改客户端呈现(排版、动效、配色、文案)不算公共 API 变更**,按它实际做了什么归类——新增了能力就是 minor,不是 patch。
- - 不兼容变更要**在 CHANGELOG 里明说**:哪一侧不兼容(新版读旧版 / 旧版读新版)、会丢什么、别的部分是否不受影响。
- - `.npmrc` 把 `@xiaoso` 作用域指到 npmjs 官方源;镜像站发布不了。
- - `publishConfig.access: public`:scoped 包不加这条,`npm publish` 默认按 restricted 发。
- - 插件版本与 dsh 版本**不锁步**:peer 是一条 `>=0.2.0-0 <0.2.1-0 || >=0.2.1-0 <0.3.0-0` 的区间(`engines.dsh` 与 peer 同串),插件自己按 SemVer 走;只有换支持版本线时才动它。
- - peer 对不上运行版本时宿主**静默跳过**该 bundle(只记进 `skippedBundles`):症状是工具突然消失、补丁层不生效,而不是启动失败。排查看 `<profile>/cordis.yml` 里有没有自己的行。
- - `THIRD_PARTY_NOTICES.md` 由 `scripts/notices.mjs` 生成,别手改。改过依赖跑一次 `pnpm run build`,它变了就一起提交;`pnpm run notices:check` 与 `tests/package.spec.ts` 盯着它有没有过期。
+ - **版本号按严格 SemVer,看公共 API 不看改动大小**:公共 API 是工具接口、工作流 JSON / state.yaml 格式、RPC 目标、peer 区间。新增能力 → minor;修 bug、改措辞、重构 → patch;删改上述接口或让旧版读不了新文件 → major。只改界面呈现按实际做了什么归类,加了能力就是 minor。
+ - 不兼容变更在 CHANGELOG 里明说:哪一侧不兼容、会丢什么、哪些不受影响。
+ - `.npmrc` 把 `@xiaoso` 指到 npmjs 官方源(镜像站发不了);`publishConfig.access: public` 不能删,否则 scoped 包按 restricted 发。
+ - 插件版本与 dsh 不锁步:peer(与 `engines.dsh` 同串)是 `>=0.2.0-0 <0.2.1-0 || >=0.2.1-0 <0.3.0-0`,只在换支持版本线时改。peer 对不上时宿主**静默跳过**(症状是工具消失、补丁不生效),查 `<profile>/cordis.yml` 有没有自己的行。
+ - `THIRD_PARTY_NOTICES.md` 由 `scripts/notices.mjs` 生成,别手改;改过依赖 build 一次,变了就一起提交(`notices:check` 和 `tests/package.spec.ts` 会查)。
## CHANGELOG 写法
- CHANGELOG 每节会原样贴进 GitHub Release,写给用户看,不是开发记录。
-
- - **分类**:小节沿用 Added / Changed / Fixed,需要时加 Other;Release 里对应写成「新增功能 / 体验优化 / 问题修复 / 其他变更」。Other 放不兼容变更、需要迁移的事、依赖升级和面向开发者的接口变化。
- - **一条一句**:一个改动写成一句完整的话,句末带句号;要补一层意思就用分号接一个分句,不再往下分子条目,也不加粗标题。一般一两行就写完,写不完的说明是在讲实现。
- - **写用户能感知的结果**:现在能做什么、看到什么、哪个问题没了。不写组件名、CSS、RPC、源码路径、动效的实现手法;界面上的名字、命令、设置项、环境变量这类用户会碰到的照写。
- - **开头的词定基调**:新增功能用「新增…」「支持…」「…可以…」;体验优化用「优化…」「改善…」「…更…」;问题修复写成「修复〈什么情况下〉〈出了什么问题〉的问题」,或者直接说现在的正确表现。不写「以前……现在……」的前后对比。
- - **限制与实验性写进同一句**:实验性的标「实验性」,已知限制用一个分句交代(如「目前只支持……」),不另起一段免责说明。
- - **不兼容变更**:同一句里写清谁受影响、要做什么(如「用旧字段的工作流包需要重新导出」);按上文要求明说哪一侧不兼容、会丢什么。
- - **合并与取舍**:同一处界面的几项小改合成一条;纯重构、测试、只改了内部措辞的不写;这一版里新加、又在这一版里修掉的问题不写进「问题修复」,那只是这一版功能本来的样子。
- - 一节开头可以留一两句概述这一版的重点,和一句「升级要不要做事」。
-
- 例子(同一条改动):
+ 每节原样贴进 GitHub Release,写给用户看。
- - 差:工作流中心「运行实例」「工作流」页的右栏、编辑页的「版本」对话框,内容一多是整栏连标题一起滚。现在标题、存版本那一栏钉在上面,只有实例表、版本表在自己的框里滚
- - 好:实例、版本较多时只有列表在框内滚动,标题和保存栏保持不动。
+ - 小节用 Added / Changed / Fixed,需要时加 Other(不兼容、迁移、依赖、开发者接口);Release 里对应「新增功能 / 体验优化 / 问题修复 / 其他变更」。
+ - 一条一句,句末带句号,最多用分号接一个分句;不分子条目、不加粗标题。写不完说明在讲实现。
+ - 写用户能感知的结果,不写组件名、CSS、RPC、源码路径;界面上的名字、命令、设置项照写。
+ - 新增用「新增 / 支持」,优化用「优化 / 改善」,修复写「修复〈什么情况下〉〈出了什么问题〉的问题」;不写「以前……现在……」。
+ - 实验性、已知限制写进同一句;不兼容变更在同一句里写清谁受影响、要做什么。
+ - 同一处的小改合成一条;重构、测试不写;这一版新加又在这一版修掉的问题不写进「问题修复」。
+ - 一节开头可以留一两句概述,和一句「升级要不要做事」。
+ 例:差「右栏、版本对话框内容一多是整栏连标题一起滚。现在标题钉在上面,只有表在框里滚」;好「实例、版本较多时只有列表在框内滚动,标题和保存栏保持不动。」
## DSH 版本
- - 兼容范围写成一条三段版本号的区间(预览版也要能装),放在每个 `@deepseek-ai/dsh-*` 的 `peerDependencies` 里;DSH 检查 peer 时带 `includePrerelease`,所以不能用 `^`。
- - 编译用的 `devDependencies`(`@deepseek-ai/dsh-*`、`@deepseek-ai/cordis`)写死精确版本,配合 `pnpm-lock.yaml` 防依赖漂移。
- - README 两版各带一个同区间的 DSH 徽标和一句适配说明。
- - 换支持的版本线时四处一起改:peer 区间、dev 精确版本、两版 README 的徽标与说明;`tests/package.spec.ts` 检查它们一致。
+ - 兼容区间写成三段版本号的区间(预览版也要能装),放在每个 `@deepseek-ai/dsh-*` 的 `peerDependencies`;DSH 查 peer 带 `includePrerelease`,不能用 `^`。
+ - `devDependencies` 的 `@deepseek-ai/dsh-*`、`@deepseek-ai/cordis` 写死精确版本,配合 lockfile。
+ - 换版本线时四处一起改:peer 区间、dev 版本、两版 README 的 DSH 徽标与适配说明;`tests/package.spec.ts` 查一致。
## 编译口径(DSH / 通用)
- 同一张图编译成两种口径:插件里执行用 **DSH 口径**,导出成 skill 用**通用口径**(`PlanOptions.skill` 给了就是通用口径)。
+ 同一张图编译成两种口径:插件里执行用 DSH 口径,导出成 skill 用通用口径(给了 `PlanOptions.skill` 就是通用)。
- - 跟执行环境有关的话——执行方式、复用执行者、设定目标、怎么加载 skill、运行状态——只写在 `src/shared/dialect.ts`,每句两套:DSH 口径可以点名工具与插件动作(`subagent`、`spawn_teammate`、`create_goal`、`workflow_lite` 的 `state` / `resume`……);通用口径不点名任何工具,只说要做成什么。`compile.ts` 只写和环境无关的部分(图、流程、资源、交接),不要在里面写工具名。
- - 运行状态:DSH 口径由插件建实例、模型经 `state` 动作改;通用口径由模型自己建、自己改工作区里的 `.workflow-lite/runs/{instance}/state.yaml`,计划里按图给出初始内容。
- - 加一条工作流设置或改一句措辞时两套一起写。`tests/shared/dialect.spec.ts` 把所有设置打开、用通用口径编译,扫计划和每份任务描述里有没有 `DSH_ONLY_TERMS`;新出现的工具名、插件说法要补进这张表。
+ - 跟执行环境有关的话(执行方式、复用执行者、设定目标、加载 skill、运行状态)只写在 `src/shared/dialect.ts`,每句两套:DSH 口径可点名工具和插件动作,通用口径不点名任何工具。`compile.ts` 里不写工具名。
+ - 运行状态:DSH 口径由插件建实例、模型经 `state` 改;通用口径由模型自己建、改 `.workflow-lite/runs/{instance}/state.yaml`,计划里给出初始内容。
+ - 加设置或改措辞两套一起写;`tests/shared/dialect.spec.ts` 用通用口径扫 `DSH_ONLY_TERMS`,新的工具名、插件说法要补进表。
## 配色主题
- - 亮色、暗色各四套(晴白、瑞士、雾蓝、林间 / 石板、墨黑、深海、暖夜),色值全在 `src/client/model/themes.ts`;默认晴白 + 石板,瑞士排第二。
- - 「瑞士」除了颜色还带一套「形」:亮色档选了它、此刻又是亮色时,视图根写 `data-look="swiss"`(`themeLook()`),`shell.module.css` 在这个开关下把 `--wl-round`(所有圆角的乘数,各 CSS 里写 `calc(Npx * var(--wl-round, 1))`;圆点的 50% 不乘)归零、去掉阴影、换 Helvetica 系字体;分组小标题的大写加粗写在各模块里的 `:global([data-look="swiss"]) .x`。新写圆角用 `calc(Npx * var(--wl-round, 1))`,别写死 px,否则瑞士下会漏圆角。瑞士下的其余做法同样写在各模块末尾的 `:global([data-look="swiss"]) .x` 里:成组列表只留上下线、输入框只留底线、分段反白、分组标题压黑线;淡底(`--wl-*-soft`)在 `shell.module.css` 的瑞士块里调淡。新加成组的盒子或卡片时顺手补一条。
- - 视图根节点把选中的两套写成 `--wl-l-*`、`--wl-d-*` 两组内联变量,`shell.module.css` 用 `light-dark()` 跟着宿主的 `color-scheme` 拼成 `--wl-*`;CSS 里不再写死色值。
- - 选择记在浏览器(`workflow-lite.theme`),在工作流中心「设置」页的「外观」里换,点了就生效,不走插件设置的保存。
- - 一套主题是一整套颜色:线色(光带、图例、线上牌子跟着它)、强调色、步骤图标色、状态色一起换;非默认的几套照着成熟配色方案配(雾蓝 Nord、林间 Everforest、墨黑 Geist、深海 Tokyo Night、暖夜 Gruvbox),主干线用强调色。
- - 改色或加主题要过 `tests/client/themes.spec.ts`:文字、语义色、线色、步骤色在面板上过 4.5:1,线色在画布上过 3:1,七种线两两 OKLab ΔE ≥ 8(默认两套 ≥ 13)。
+ - 亮暗各四套(晴白、瑞士、雾蓝、林间 / 石板、墨黑、深海、暖夜),色值全在 `src/client/model/themes.ts`;默认晴白 + 石板。非默认几套照成熟方案配(Nord、Everforest、Geist、Tokyo Night、Gruvbox),一套换全套:线色、强调色、步骤图标色、状态色。
+ - 视图根写 `--wl-l-*`、`--wl-d-*` 两组内联变量,`shell.module.css` 用 `light-dark()` 拼成 `--wl-*`;CSS 里不写死色值。
+ - 「瑞士」还带一套「形」:亮色下视图根写 `data-look="swiss"`,`--wl-round` 归零、去阴影、换 Helvetica 系字体。所以新写圆角一律 `calc(Npx * var(--wl-round, 1))`(圆点 50% 不乘);新加成组的盒子或卡片,在模块末尾补一条 `:global([data-look="swiss"]) .x`(列表只留上下线、输入框只留底线、分段反白、分组标题压黑线)。
+ - 选择存浏览器(`workflow-lite.theme`),在工作流中心「设置 → 外观」里换,即点即生效。
+ - 改色要过 `tests/client/themes.spec.ts`:文字和语义色在面板上 4.5:1,线色在画布上 3:1,七种线两两 OKLab ΔE ≥ 8(默认两套 ≥ 13)。
## 浏览器验收(CDP)
- `tests/cdp-*.mjs` 在真浏览器里走一遍功能,结论以磁盘 / RPC 为准。需要:
-
- 1. 一个装了本插件的 DSH 网页实例(默认 `http://127.0.0.1:3190`),从它打印的 URL 里取 `?token=`;
- 2. 一个带 DevTools 端口的 Chrome:`node tests/lib/cdp-chrome-launch.mjs 9222`。
+ `tests/cdp-*.mjs` 在真浏览器里走一遍,结论以磁盘 / RPC 为准。要一个装了本插件的 dsh 网页实例(默认 3190,取它打印的 `?token=`)和 DevTools Chrome:
```bash
DSH_WEB_TOKEN=<token> DSH_BASE=http://127.0.0.1:3190 node --experimental-strip-types tests/cdp-ui.mjs
```
- `DSH_BASE` 是基址(`tests/lib/web-session.mjs` 每次现读环境变量,不固化成模块常量——截图管线是 import 之后才起实例、才知道端口);`DSH_AUTH_COOKIE` 可以跳过 token 换 cookie 那一步。
+ `DSH_BASE` 每次现读(截图管线 import 之后才知道端口),`DSH_AUTH_COOKIE` 可跳过换 cookie。
| 脚本 | 覆盖 |
|---|---|
| `cdp-ui.mjs` | 画布编辑、资源、工作流切换 / 改名 / 删除 |
| `cdp-runs.mjs` | 实例与运行状态 |
| `cdp-inputs.mjs` | 执行前提问 |
| `cdp-versions.mjs` | 版本、工作流中心「工作流」页 |
- | `cdp-config.mjs` | 工作流中心「设置」页(含外观:配色主题) |
- | `cdp-pack.mjs` | 工作流中心「工作流」页的导出、导入(另存为新名字、并入已有的)、导出成 Skill(预览整个包、下载 zip、装到本机;装的那份用一次性名字,验完删掉) |
+ | `cdp-config.mjs` | 「设置」页(含配色主题) |
+ | `cdp-pack.mjs` | 导出、导入、导出成 Skill(装到本机用一次性名字,验完删掉) |
## 插件图标与词标
- - 两张 448 见方的图分工不同,**别互相替换**(`tests/package.spec.ts` 钉着这个分工):
- - `assets/icon.webp` = **插件图标**,`package.json` 顶层 `icon` 指它。**先裁到主体再缩**:脸占画面四成、头加头发占八成,标志物(黑猫发卡)完整,下缘留一点衣领和花束当锚点;裁框 950 见方、左上角 `(115, 0)`。整幅缩进去脸只剩三成,缩略图上认不出人——取景参照隔壁 tool-plus 的头像。
- - `assets/icon-full.webp` = **README 头图**,两版开头的 `<img width="140">` 指它。整幅不裁,保留完整构图。
- - 两张都:LANCZOS 缩到 448、圆角半径 104、8 倍超采样(`rounded_rectangle` 画在 448×8 上再缩)、WebP 无损(`lossless=True, quality=100, method=6`),都在 `files` 里。
- - 宿主只收**清单目录下的相对路径**的图标,格式 SVG / PNG / JPEG / WebP,上限 256 KiB;越界界面静默回退默认图。`tests/package.spec.ts` 的「插件图标」盯着:路径合法且在包内、不超过 256 KiB、本体与扩展名对得上、`files` 收得下。
- - `assets/icon-source.png`(最初的源图)、`assets/icon-art.png`(重绘成赛璐璐平涂,1254 见方)只留在仓库里做参照,不随包发布。
- - `assets/wordmark.png`:「dsh workflow lite」艺术字,Jua 400 Regular 逐词纯色,1147×130——蓝 `#3e7ce4`(项目色)、绿 `#60ba7e`(人物眼睛)、金 `#cd9e70`(金发压深)。README 里按 `width="360"` 摆,图标 140。
- - 词标重做:Pillow 把三个词按 4 倍尺寸画到同一基线上,整幅 LANCZOS 缩到 1/4,裁到墨迹外留 6% 边距;字体不进仓库,用时从 Google Fonts 取 `ofl/jua/Jua-Regular.ttf`。
- - README 两版开头都是「图标 + 词标」居中的一段 HTML,没有 H1;改文案两版同步。
+ - 两张 448 见方的图**别互相替换**(`tests/package.spec.ts` 钉着):
+ - `assets/icon.webp` 是插件图标(`package.json` 的 `icon`)。先裁到主体再缩:裁框 950 见方、左上角 `(115, 0)`,脸占四成、黑猫发卡完整;整幅缩进去缩略图上认不出人。
+ - `assets/icon-full.webp` 是 README 头图,整幅不裁。
+ - 两张都:LANCZOS 缩到 448、圆角 104(8 倍超采样画)、WebP 无损(`lossless=True, quality=100, method=6`)。
+ - 宿主只收清单目录下的相对路径,SVG / PNG / JPEG / WebP,≤ 256 KiB,越界静默回退默认图。
+ - `icon-source.png`、`icon-art.png` 只留仓库做参照,不发布。
+ - `assets/wordmark.png`:Jua 字体逐词纯色(蓝 `#3e7ce4`、绿 `#60ba7e`、金 `#cd9e70`),1147×130;重做时 Pillow 按 4 倍画、缩到 1/4、墨迹外留 6% 边距,字体用时从 Google Fonts 取。
+ - README 两版开头是「图标 140 + 词标 360」居中,没有 H1;改文案两版同步。
## README 版本线
- README 里、封面图下面的「版本线」是一张图(`assets/versions.{zh,en}.png`),挑重要变化,一条一站,不逐版本列。
+ `assets/versions.{zh,en}.png`,挑重要变化一条一站,内容在 `tests/readme/versions.json`,排版在 `versions.html`。
- - 内容在 `tests/readme/versions.json`:每站写落在哪个版本(要显示成别的就写 `label`,改过几个版本的写 `to`)、标题、两行短话(中英各一份),标题旁要挂「实验性」这类小签写 `tag`(英文要短,`Beta`,长了会折到下一行);还没发的写 `"next"`,还没定版本的规划写 `"planned"`(排在最后,显示成 `later`,不核对 CHANGELOG)。排版在 `tests/readme/versions.html`。
- - 出图:`pnpm run timeline`(要一个带 DevTools 端口的 Chrome:`node tests/lib/cdp-chrome-launch.mjs 9222`,不用起 dsh)。脚本对照 `CHANGELOG.md`,json 里的版本不存在、或有新的小版本没写进 json 就报错。
- - 发版时:把 `"next"` 改成这个版本号(有新的重要变化就加一站),重跑,新图一起提交。
- - 6 站时一栏约 150px:标题 ≤ 7 个汉字 / 15 个英文字符(带小签的再短些),短话每行 ≤ 9 个汉字 / 22 个英文字符;站数 6 站左右,多了就把早期几个版本合成一站(写 `to`);出图后看一眼两张图。
+ - 每站写 `version`(可加 `label`、跨版本写 `to`)、标题、两行短话、可选 `tag`(英文用 `Beta`);未发写 `"next"`,未定版本的规划写 `"planned"`。
+ - 脚本对照 CHANGELOG:json 里的版本不存在、或有新小版本没写进 json 都报错。
+ - 发版时把 `"next"` 改成版本号、重跑、一起提交。
+ - 6 站左右(多了合并早期几站);标题 ≤ 7 个汉字 / 15 个英文字符,短话每行 ≤ 9 个汉字 / 22 个英文字符。
- ## README 封面图
+ ## 版本更新卡片
- README 有中英两版:`README.md`(中文,默认)与 `README.en.md`(英文),开头互相链接。各用一张封面图:`assets/hero.zh.png`、`assets/hero.en.png`(2 倍像素),由同一页 HTML 按语言排版后截图得到,图里的界面都是真实截图(英文版用英文界面和英文示例)。
+ 发群聊用的一张图:头像 + 词标、插件是什么、本版标题 + 4 条两列亮点、带版本号的安装命令。560 宽(出图 1120),整张深色直角不留透明边(聊天软件会把透明处铺成白底)。
- | 文件 | 作用 |
- |---|---|
- | `tests/support/shots-env.mjs` | 封面管线的**自备环境**:起一个假模型(`tests/support/mock-llm.mjs`)、生成截图专用 profile `workflow-lite-shots`、在**随机端口**起实例、换好 cookie,再备出那条固定标题的夹具会话;`startShotsEnv()` 一把起完,拆的时候等实例真的关掉 |
- | `tests/readme-shots.mjs` | 自己把上面那套环境起起来,按语言切换浏览器语言,建示例工作流和一个实例,按封面里的大小拍素材到 `tests/runs/hero/<lang>/`(`canvas.png` 编辑页、`run-graph.png` 点亮的图、`run-position.png` / `run-timeline.png` 右栏两小块、`run-dashboard.png` 查看框里打开的 HTML 看板、`plan.txt` 编译出的计划),拍完自动清理并拆掉实例;`node … tests/readme-shots.mjs en` 只拍一种 |
- | `tests/readme/hero.html` | 封面排版:标题、三段流程(设计流程 → 生成计划提示词 → 跟踪执行进度)、软工作流说明、功能标签;`?lang=zh` / `?lang=en` 切换,文案都在页尾的 `TEXT` 里 |
- | `tests/readme-hero.mjs` | 在 Chrome 里另开一个标签页按 1040 宽渲染 `hero.html`,截成 `assets/hero.<lang>.png`;`node tests/readme-hero.mjs en` 只出一种 |
+ - 内容在 `tests/readme/release.json`(`intro` 写一次,`versions` 按版本写 `headline` + `items`),字数上限见其 `_说明`;排版在 `release.html`。图标名取 `src/client/ui/Icon.tsx`,色调 blue / green / gold / teal。
+ - 出到 `tests/runs/release/v<版本>.<lang>.png`,不进库;版本号要在 CHANGELOG 里,日期从那里读。
+ - 发版时从 CHANGELOG 挑 4 条用户最能感知的,各写一个标题加一行短话,句末不加句号;出图后看一眼,超长会被省略号截掉。
- 重做步骤:
+ ## README 封面图
- ```bash
- # 界面有变化:重拍素材并出图。前置是一台装了 dsh 的机器 + 一个带 DevTools 端口的 Chrome
- # (`node tests/lib/cdp-chrome-launch.mjs 9222`),不用先起 dsh web、不用给 token
- pnpm run hero
+ `assets/hero.{zh,en}.png`(2 倍像素),同一页 HTML 按语言排版后截图,图里的界面都是真实截图。
- # 只改了 hero.html 的文案或排版:直接出图(素材已在 tests/runs/hero/)
- pnpm run hero:render
- ```
+ | 文件 | 作用 |
+ |---|---|
+ | `tests/support/shots-env.mjs` | 自备环境:假模型、截图专用 profile、随机端口实例、cookie、固定标题的夹具会话;`startShotsEnv()` 一把起完 |
+ | `tests/readme-shots.mjs` | 起环境、建示例工作流和实例,按封面里的大小拍素材到 `tests/runs/hero/<lang>/`,拍完拆掉;加 `en` 只拍一种 |
+ | `tests/readme/hero.html` | 封面排版,`?lang=zh` / `?lang=en`,文案在页尾 `TEXT` |
+ | `tests/readme-hero.mjs` | 按 1040 宽渲染并截图;`pnpm run hero:render` 只出图不重拍 |
- 封面管线的前提:
+ 管线的坑:
- - **自己一个家**:整跑用 `tests/runs/shots-home` 当 `DSH_HOME`,profile、会话库、工作区库、投影缓存全在它底下,跑前清空、跑完删掉。这几样在 DSH 里按 `DSH_HOME` 算、**不按 profile 隔离**——共用用户自己的家的话,那条"封面夹具"会永久留在他侧栏里。
- - **不依赖真实环境数据**:封面拍的是脚本自己准备的那份数据(假模型 + 生成 profile + 随机端口 + 夹具会话);不发真实模型请求,也不受"当时 3190 上跑着什么"影响。
- - **夹具是"认领"出来的**:会话按固定 id 复用,而会话跟着它的 cwd 走(换个目录再建会被 host 回 `session/conflict`,host 又没有删会话的接口)。所以 `prepareShotsFixture` 找到就用它自己的 cwd 当工作区,并把 `adoptedWorkspace` 交出来——**收尾时不许删认领来的目录**。
- - **进会话靠搜索,不靠分组展开**:工作区分组的展开状态记在浏览器里(`dsh.workspace.view.v5` 的 `groupExpansion`),按 origin 存——换个端口就是新分组状态,于是所有分组都是折叠的,会话行压根不在 DOM 里。`enterSession` 因此走会话搜索框。
- - **关实例要等它真的关掉**:随手 `spawn('taskkill')` 就走人,调用方一退出 taskkill 没机会跑,实例留在后台占着会话写句柄,下一跑撞 `session/writer-held`。所以 `stop()` 等 `close`,等不到**就抛**(不许报成功)。
- - **"没发真实模型请求"有判据**:夹具发 prompt 的那一轮,必须等到**假模型真的收到请求**才算数。别拿 `blank` 变 false 当完成信号——它只说明消息记下了,模型调用还在后面,拿它当信号会抢跑报假红。
+ - **自己一个家**:`tests/runs/shots-home` 当 `DSH_HOME`,跑前清空、跑完删。会话库这些按 `DSH_HOME` 算、不按 profile 隔离,共用用户的家,夹具会永久留在他侧栏里。
+ - **夹具是认领来的**:会话按固定 id 复用、跟着原 cwd 走,host 又不能删会话;找到就用它的 cwd 当工作区(`adoptedWorkspace`),**收尾不许删这个目录**。
+ - **进会话靠搜索**:分组展开状态按 origin 存,换端口全折叠,会话行不在 DOM 里,`enterSession` 走搜索框。
+ - **关实例要等真的关掉**:`stop()` 等 `close`,等不到就抛;不等的话实例留在后台,下一跑撞 `session/writer-held`。
+ - **等假模型真的收到请求**才算夹具发完,别拿 `blank` 变 false 当信号。
- 改封面时注意:
+ 改封面时:
- - 计划节选是从 `tests/runs/hero/zh/plan.txt` 里摘的原文(计划只有中文,英文版是对应的译文,图里标了 translated),编译器的措辞变了要同步改;
- - 两种语言的文案改一处就要改另一处;
- - 文案用平实的陈述句,不用「不是……而是……」这类对比句式,也不用过于口语的说法;
- - 清晰:素材拍的时候就按封面里的大小换算像素比,`hero.html` 一律按「原图宽 ÷ 2」摆,一个像素对一个像素,不在封面里缩放(缩放就糊);右栏这类窄面板单独裁出来,别整块界面缩小塞进封面;
- - 字要大:封面 1040 宽,GitHub 上大约显示成 880 宽;示例工作流只留四步,图越窄字越大。改页宽、页边要同步改 `readme-shots.mjs` 的 `GRAPH_WIDTH`;
- - 层次:工作流的图是主角;右栏小块和看板按图相对原大的比例拍(不小于 `MIN_SCALE` 0.85),字号和图上的字接近,别比图还抢眼;
- - 不留空:第三段下半截左列两小块叠起来,右边看板的高度按左列算好再拍,两边齐平;
- - 出图后打开 `assets/hero.zh.png`、`assets/hero.en.png` 看一眼,确认没有错位、截断,也没有拍进本机路径或私人工作区名。
+ - 计划节选摘自 `tests/runs/hero/zh/plan.txt`(英文版是译文),编译器措辞变了要同步;中英文案一起改,用平实陈述句,不用「不是……而是……」。
+ - 素材按封面里的大小拍,`hero.html` 按「原图宽 ÷ 2」摆,不在封面里缩放;窄面板单独裁。
+ - 封面 1040 宽(GitHub 约显示 880),示例工作流只留四步;改页宽要同步 `readme-shots.mjs` 的 `GRAPH_WIDTH`。
+ - 工作流的图是主角,右栏小块和看板按比例拍(不小于 `MIN_SCALE` 0.85);第三段两列齐平不留空。
+ - 出图后两张都看一眼:没有错位、截断,没拍进本机路径或私人工作区名。