@dsh-workbench/core 0.3.10 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -105
  3. package/bin/dsh-workbench +132 -31
  4. package/bin/dsh-workbench.cmd +5 -0
  5. package/bin/opencli-isolated +3 -2
  6. package/bin/opencli-isolated.mjs +44 -0
  7. package/bin/opencli-node-mode.cjs +1 -0
  8. package/bin/seed-workshop-windows.mjs +307 -0
  9. package/client.js +8192 -4904
  10. package/cordis.patch.yml +62 -37
  11. package/dist/client/client.body.js +8192 -4904
  12. package/dist/client/index.js +7186 -3898
  13. package/dist/host/index.js +7277 -6956
  14. package/dist/types/app/client.d.ts +3 -1
  15. package/dist/types/app/host.d.ts +2 -1
  16. package/dist/types/app/layouts/approval-detail.d.ts +3 -0
  17. package/dist/types/app/layouts/env-gate/boot-gate.d.ts +1 -12
  18. package/dist/types/app/layouts/env-gate/gate-text.d.ts +2 -1
  19. package/dist/types/app/layouts/market/DiscoverPanel.d.ts +8 -0
  20. package/dist/types/app/layouts/market/add-module-text.d.ts +1 -1
  21. package/dist/types/app/layouts/market/discover-categories.d.ts +8 -0
  22. package/dist/types/app/layouts/market/discover-confirm.d.ts +14 -0
  23. package/dist/types/app/layouts/market/discover-detail.d.ts +12 -0
  24. package/dist/types/app/layouts/market/discover-dialogs.d.ts +7 -0
  25. package/dist/types/app/layouts/market/discover-doc.d.ts +18 -0
  26. package/dist/types/app/layouts/market/discover-init-dialog.d.ts +10 -0
  27. package/dist/types/app/layouts/market/discover-rows.d.ts +24 -0
  28. package/dist/types/app/layouts/market/discover-sources-dialog.d.ts +16 -0
  29. package/dist/types/app/layouts/market/discover-states.d.ts +22 -0
  30. package/dist/types/app/layouts/market/discover-toolbar.d.ts +23 -0
  31. package/dist/types/app/layouts/market/discover-versions.d.ts +9 -0
  32. package/dist/types/app/layouts/market/discover-view.d.ts +38 -0
  33. package/dist/types/app/layouts/market/market-header.d.ts +13 -0
  34. package/dist/types/app/layouts/market/panel.d.ts +1 -1
  35. package/dist/types/app/layouts/market/use-add-module.d.ts +1 -1
  36. package/dist/types/app/layouts/market/use-discover.d.ts +72 -0
  37. package/dist/types/app/layouts/market/use-module-versions.d.ts +6 -2
  38. package/dist/types/app/layouts/rightbar/MarketView.d.ts +6 -3
  39. package/dist/types/app/layouts/rightbar/Rightbar.d.ts +1 -1
  40. package/dist/types/app/layouts/rightbar/board-shape.d.ts +1 -14
  41. package/dist/types/app/layouts/rightbar/market-act.d.ts +6 -3
  42. package/dist/types/app/layouts/rightbar/market-busy.d.ts +0 -22
  43. package/dist/types/app/layouts/rightbar/market-change.d.ts +1 -1
  44. package/dist/types/app/layouts/rightbar/market-hint.d.ts +1 -7
  45. package/dist/types/app/layouts/rightbar/market-plugin-rows.d.ts +0 -7
  46. package/dist/types/app/layouts/rightbar/market-row-icon.d.ts +1 -1
  47. package/dist/types/app/layouts/rightbar/market-row-types.d.ts +2 -2
  48. package/dist/types/app/layouts/rightbar/market-rows.d.ts +1 -1
  49. package/dist/types/app/layouts/rightbar/market-section-icon.d.ts +1 -1
  50. package/dist/types/app/layouts/rightbar/market-settle-hold.d.ts +2 -0
  51. package/dist/types/app/layouts/rightbar/market-settle.d.ts +17 -0
  52. package/dist/types/app/layouts/rightbar/market-shared-rows.d.ts +1 -1
  53. package/dist/types/app/layouts/rightbar/market-update-verdict.d.ts +31 -0
  54. package/dist/types/app/layouts/rightbar/market-view-text.d.ts +8 -1
  55. package/dist/types/app/layouts/rightbar/market-workbench-row.d.ts +1 -1
  56. package/dist/types/app/layouts/rightbar/store.d.ts +2 -0
  57. package/dist/types/app/layouts/sidebar/LongTaskDot.d.ts +6 -0
  58. package/dist/types/app/layouts/sidebar/LongTaskHover.d.ts +6 -0
  59. package/dist/types/app/layouts/sidebar/edition-boot.d.ts +6 -0
  60. package/dist/types/app/layouts/sidebar/edition.d.ts +5 -0
  61. package/dist/types/app/layouts/sidebar/use-attention.d.ts +0 -4
  62. package/dist/types/app/layouts/sidebar/workshop-preset.d.ts +2 -0
  63. package/dist/types/app/layouts/workbench-settings/AdapterSetItem.d.ts +5 -0
  64. package/dist/types/app/layouts/workbench-settings/ConfigSyncItem.d.ts +5 -0
  65. package/dist/types/app/layouts/workbench-settings/ConfigSyncRows.d.ts +11 -0
  66. package/dist/types/app/layouts/workbench-settings/NewWorkbenchItem.d.ts +5 -0
  67. package/dist/types/app/layouts/workbench-settings/SettingsItem.d.ts +8 -0
  68. package/dist/types/app/layouts/workbench-settings/SidebarEditionItem.d.ts +5 -0
  69. package/dist/types/app/layouts/workbench-settings/WorkbenchSettingsSection.d.ts +5 -0
  70. package/dist/types/app/units/manifests.d.ts +1 -1
  71. package/dist/types/{app/units → kernel/client}/registry.d.ts +1 -1
  72. package/dist/types/kernel/contract/bundle-install.d.ts +2 -0
  73. package/dist/types/kernel/contract/config-sync.d.ts +2 -0
  74. package/dist/types/kernel/contract/diy.d.ts +1 -0
  75. package/dist/types/kernel/contract/dsh-plugins.d.ts +2 -0
  76. package/dist/types/kernel/contract/index.d.ts +4 -0
  77. package/dist/types/kernel/contract/long-tasks.d.ts +2 -0
  78. package/dist/types/kernel/contract/market-index.d.ts +2 -0
  79. package/dist/types/kernel/contract/platform.d.ts +1 -0
  80. package/dist/types/kernel/contract/preflight-wake.d.ts +2 -0
  81. package/dist/types/kernel/contract/profile-create.d.ts +2 -0
  82. package/dist/types/kernel/contract/sidebar-edition.d.ts +2 -0
  83. package/dist/types/kernel/contract/unit-manifests.d.ts +2 -0
  84. package/dist/types/{shared/api → kernel}/host/mount-units.d.ts +2 -1
  85. package/dist/types/modules/panels/client.d.ts +1 -1
  86. package/dist/types/modules/panels/model/approval.d.ts +4 -8
  87. package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -1
  88. package/dist/types/modules/panels/model/diy-changelog.d.ts +2 -0
  89. package/dist/types/modules/panels/model/diy-context.d.ts +20 -18
  90. package/dist/types/modules/panels/model/diy-page.d.ts +4 -2
  91. package/dist/types/modules/panels/model/diy-record-open.d.ts +0 -15
  92. package/dist/types/modules/panels/model/diy-record-text.d.ts +0 -2
  93. package/dist/types/modules/panels/model/diy-record.d.ts +1 -31
  94. package/dist/types/modules/panels/model/diy-report.d.ts +6 -7
  95. package/dist/types/modules/panels/model/diy-tools.d.ts +1 -0
  96. package/dist/types/modules/panels/model/long-task-tools.d.ts +0 -11
  97. package/dist/types/modules/panels/model/market-dependency-update.d.ts +15 -0
  98. package/dist/types/modules/panels/model/market-dependency.d.ts +15 -10
  99. package/dist/types/modules/panels/model/market-discover-routes.d.ts +3 -0
  100. package/dist/types/modules/panels/model/market-discover.d.ts +33 -0
  101. package/dist/types/modules/panels/model/market-dispatch.d.ts +4 -3
  102. package/dist/types/modules/panels/model/market-env.d.ts +2 -3
  103. package/dist/types/modules/panels/model/market-registry.d.ts +3 -3
  104. package/dist/types/modules/panels/model/market-restart.d.ts +1 -1
  105. package/dist/types/modules/panels/model/market-route.d.ts +5 -3
  106. package/dist/types/modules/panels/model/market-sources-act.d.ts +9 -0
  107. package/dist/types/modules/panels/model/market-types.d.ts +7 -13
  108. package/dist/types/modules/panels/model/market-update-bundle.d.ts +19 -0
  109. package/dist/types/modules/panels/model/market-verify.d.ts +15 -0
  110. package/dist/types/modules/panels/model/market-workbench-update.d.ts +3 -2
  111. package/dist/types/modules/panels/model/market.d.ts +6 -5
  112. package/dist/types/modules/panels/model/plugin-actions.d.ts +2 -1
  113. package/dist/types/modules/panels/model/plugin-section.d.ts +3 -1
  114. package/dist/types/modules/panels/model/publish-preview-routes.d.ts +2 -0
  115. package/dist/types/modules/panels/unit.d.ts +8 -5
  116. package/dist/types/modules/sessions/model/attention.d.ts +0 -11
  117. package/dist/types/modules/sessions/model/session-node.d.ts +0 -10
  118. package/dist/types/shared/api/diy.d.ts +6 -4
  119. package/dist/types/shared/api/host/approval-call.d.ts +0 -9
  120. package/dist/types/shared/api/host/approval-gate.d.ts +26 -0
  121. package/dist/types/shared/api/host/bundle-routes.d.ts +2 -1
  122. package/dist/types/shared/api/host/diy-artifacts.d.ts +3 -0
  123. package/dist/types/shared/api/host/diy-changelog.d.ts +13 -0
  124. package/dist/types/shared/api/host/diy-components.d.ts +2 -3
  125. package/dist/types/shared/api/host/diy-modules.d.ts +25 -0
  126. package/dist/types/shared/api/host/diy-publish-preview.d.ts +4 -0
  127. package/dist/types/shared/api/host/diy-recon-fs.d.ts +2 -2
  128. package/dist/types/shared/api/host/diy-tokens.d.ts +8 -0
  129. package/dist/types/shared/api/host/host-routes.d.ts +10 -0
  130. package/dist/types/shared/api/host/long-task-notice.d.ts +0 -20
  131. package/dist/types/shared/api/host/long-tasks.d.ts +1 -8
  132. package/dist/types/shared/api/host/market-doc.d.ts +21 -0
  133. package/dist/types/shared/api/host/market-index-fetch.d.ts +37 -0
  134. package/dist/types/shared/api/host/market-source-init.d.ts +22 -0
  135. package/dist/types/shared/api/host/market-sources-store.d.ts +15 -0
  136. package/dist/types/shared/api/host/permission-mode.d.ts +4 -0
  137. package/dist/types/shared/api/host/post-channels.d.ts +1 -1
  138. package/dist/types/shared/api/host/sidebar-edition.d.ts +14 -0
  139. package/dist/types/shared/api/host/unit-roster-facts.d.ts +1 -1
  140. package/dist/types/shared/api/host/workspace-root.d.ts +6 -0
  141. package/dist/types/shared/api/platform/client.d.ts +10 -0
  142. package/dist/types/shared/api/unit-board.d.ts +4 -0
  143. package/dist/types/shared/api/write-outcome.d.ts +3 -0
  144. package/dist/types/shared/components/IconModules.d.ts +2 -0
  145. package/dist/types/shared/components/PanelFoot.d.ts +0 -9
  146. package/dist/types/shared/components/blocker-action.d.ts +3 -0
  147. package/dist/types/shared/dev-surface/index.d.ts +5 -0
  148. package/dist/types/shared/dev-surface/types.d.ts +79 -0
  149. package/dist/types/shared/hooks/bundle-queries.d.ts +1 -13
  150. package/dist/types/shared/hooks/fact-transport.d.ts +4 -0
  151. package/dist/types/shared/hooks/fact-transports.d.ts +0 -4
  152. package/dist/types/shared/hooks/market-queries.d.ts +11 -0
  153. package/dist/types/shared/hooks/panel-state-report.d.ts +0 -12
  154. package/dist/types/shared/hooks/sidebar-edition.d.ts +2 -0
  155. package/dist/types/shared/hooks/use-blocker-actions.d.ts +0 -8
  156. package/dist/types/shared/hooks/use-capabilities.d.ts +1 -55
  157. package/dist/types/shared/hooks/use-capability-gate.d.ts +1 -2
  158. package/dist/types/shared/hooks/use-diy-debug.d.ts +0 -13
  159. package/dist/types/shared/hooks/use-diy-record.d.ts +0 -13
  160. package/dist/types/shared/hooks/use-host-write.d.ts +1 -1
  161. package/dist/types/shared/hooks/use-shell-long-tasks.d.ts +1 -7
  162. package/dist/types/shared/hooks/use-unit-board.d.ts +1 -1
  163. package/icon.svg +17 -28
  164. package/package.json +33 -13
  165. package/preset/diy.patch.yml +3 -3
  166. package/profile/isolation.patch.yml +18 -7
  167. package/profile/sandbox.patch.yml +2 -2
  168. package/scripts/clone-profile.mjs +268 -0
  169. package/scripts/create-workbench.mjs +166 -0
  170. package/scripts/seed-profile.sh +22 -10
  171. package/scripts/seed-workshop.sh +17 -19
  172. package/skills/workbench-module-diy/BUILD.md +57 -133
  173. package/skills/workbench-module-diy/COPY.md +89 -0
  174. package/skills/workbench-module-diy/DATASOURCE.md +173 -116
  175. package/skills/workbench-module-diy/DESIGN.md +76 -109
  176. package/skills/workbench-module-diy/FAMILY.md +48 -52
  177. package/skills/workbench-module-diy/KIT.md +78 -133
  178. package/skills/workbench-module-diy/LINKAGE.md +66 -105
  179. package/skills/workbench-module-diy/PANEL.md +81 -171
  180. package/skills/workbench-module-diy/REVIEW.md +67 -81
  181. package/skills/workbench-module-diy/SKILL.md +66 -96
  182. package/skills/workbench-module-diy/STEPS.md +66 -69
  183. package/skills/workbench-module-diy/TOOLS.md +134 -103
  184. package/skills/workbench-module-diy/WIRING.md +64 -84
  185. package/skills/workbench-module-diy/WRITES.md +115 -142
  186. package/dist/types/app/layouts/config-sync/ConfigSyncSection.d.ts +0 -5
  187. package/dist/types/shared/api/bundle-install.d.ts +0 -123
  188. package/dist/types/shared/api/config-sync.d.ts +0 -35
  189. package/dist/types/shared/api/dsh-plugins.d.ts +0 -11
  190. package/dist/types/shared/api/host/bundle-manager.d.ts +0 -11
  191. package/dist/types/shared/api/host/config-sync.d.ts +0 -46
  192. package/dist/types/shared/api/host/diy-preset-health.d.ts +0 -35
  193. package/dist/types/shared/api/host/diy-workshop.d.ts +0 -23
  194. package/dist/types/shared/api/host/dsh-plugins.d.ts +0 -5
  195. package/dist/types/shared/api/host/installed-module.d.ts +0 -54
  196. package/dist/types/shared/api/host/installed-module.test.d.ts +0 -1
  197. package/dist/types/shared/api/host/opencli-command.d.ts +0 -10
  198. package/dist/types/shared/api/host/opencli-command.test.d.ts +0 -1
  199. package/dist/types/shared/api/host/opencli.test.d.ts +0 -1
  200. package/dist/types/shared/api/host/package-content.d.ts +0 -6
  201. package/dist/types/shared/api/host/package-content.test.d.ts +0 -1
  202. package/dist/types/shared/api/host/package-kind.d.ts +0 -21
  203. package/dist/types/shared/api/host/package-kind.test.d.ts +0 -1
  204. package/dist/types/shared/api/host/package-route.d.ts +0 -23
  205. package/dist/types/shared/api/host/package-route.test.d.ts +0 -1
  206. package/dist/types/shared/api/host/plugin-catalog.d.ts +0 -65
  207. package/dist/types/shared/api/host/plugin-login-hint.d.ts +0 -2
  208. package/dist/types/shared/api/host/plugin-tools.d.ts +0 -14
  209. package/dist/types/shared/api/host/plugin-units.d.ts +0 -48
  210. package/dist/types/shared/api/host/preflight-route.d.ts +0 -15
  211. package/dist/types/shared/api/host/preflight.d.ts +0 -75
  212. package/dist/types/shared/api/host/probe-cache.d.ts +0 -6
  213. package/dist/types/shared/api/host/probe-libs.d.ts +0 -11
  214. package/dist/types/shared/api/host/probe-units.d.ts +0 -17
  215. package/dist/types/shared/api/host/probe-verdict.d.ts +0 -7
  216. package/dist/types/shared/api/host/profile-registries.d.ts +0 -21
  217. package/dist/types/shared/api/host/registry-declaration.d.ts +0 -23
  218. package/dist/types/shared/api/host/release.d.ts +0 -24
  219. package/dist/types/shared/api/host/runtime-seed.d.ts +0 -38
  220. package/dist/types/shared/api/host/self-update.d.ts +0 -45
  221. package/dist/types/shared/api/host/version-range.d.ts +0 -2
  222. package/dist/types/shared/api/host/workshop-root.d.ts +0 -16
  223. package/dist/types/shared/api/long-tasks.d.ts +0 -19
  224. package/dist/types/shared/api/unit-manifests.d.ts +0 -6
  225. package/docs/HANDOFF.md +0 -333
  226. package/docs/OPERATIONS.md +0 -229
  227. package/skills/workbench-module-diy/TOOLING.md +0 -55
  228. /package/dist/types/{app/layouts/rightbar/registry.d.ts → kernel/client/board-registry.d.ts} +0 -0
  229. /package/dist/types/{app/layouts/rightbar/registry.test.d.ts → kernel/contract/contracts.test.d.ts} +0 -0
  230. /package/dist/types/{shared/api/host/bundle-manager.test.d.ts → kernel/contract/index.test.d.ts} +0 -0
  231. /package/dist/types/{shared/api → kernel}/host/unit-readiness.d.ts +0 -0
  232. /package/dist/types/{shared/api → kernel}/host/unit-registry.d.ts +0 -0
  233. /package/dist/types/shared/{api/host/diy-workshop.test.d.ts → components/blocker-action.test.d.ts} +0 -0
  234. /package/dist/types/shared/{api/host/dsh-plugins.test.d.ts → dev-surface/install.d.ts} +0 -0
@@ -1,56 +1,52 @@
1
- # 工坊会话里那一族:33 把(agent 手上真实有什么)
1
+ # DIY 工具族:怎么选、怎么串
2
2
 
3
- [TOOLS.md](TOOLS.md) 讲**你写的模块**的能力怎么写;这一份讲**工坊会话里 agent 手上真实有什么**。
4
- 浏览器那一半照 opencli 的子命令枚举,参数只有 `args`(原样转发);为什么这么定见仓内 ADR-0062(这份指引不链仓内路径)。
3
+ 只写工具定义里没有的东西:什么时候用哪把、先后顺序、参数说明之外的坑。
4
+ 逐把的参数与返回以调用时拿到的工具定义为准;模块自身的能力见 [TOOLS.md](TOOLS.md)。
5
5
 
6
- ## 1 前置(别的都排它后面)
6
+ ## 1 选哪把
7
7
 
8
- - 只在**工坊会话**里出现,且**那块板画在屏上**;看 / 动 / 网络 / 日志那一族还要**先立调试约定** `workbench_diy_debug_start`(请用户点头一次):**没有约定一律当场拒、零命令**;不看屏 `_debug_pause`、收工 `_debug_stop`(顺手收网络与日志钩子)。
9
- - 会话钉死 `workbench`、每条带 `--window background`;`_list`/`_status`/`_panel`/`_components`/`_recon`/`_spec`/`_install`/`_show` 不碰他那一屏,不用开;`_find`/`_eval` 这些带 `read=` 时也**不碰**(走只读线,见 §2 末)。**写那一族(录制交接,见 §5)不走这条路**:它自己开一条 `workbench-rec` 会话,`_record_prepare` 要点头一次,`_record_read` / `_record_detail` / `_record_stop` 不问。
10
-
11
- ## 2 14 把浏览器工具(工具名 → argv;前缀都是 `workbench_diy_`)
12
-
13
- `_state`→`state` · `_html`→`get html` · `_find`→`find` · `_click`→`click` · `_hover`→`hover` · `_type`→`type` · `_fill`→`fill` · `_keys`→`keys` · `_scroll`→`scroll` · `_wait`→`wait` · `_eval`→`eval` · `_screenshot`→`screenshot` · `_network`→`network` · `_dialog`→`dialog`
14
-
15
- 干什么:名字直说;不直观的几个 —— `_state` URL + 标题 + 可点元素(ref 来源)、`_html` HTML(`--as json` 树)、`_find` 回 `{matches_n, entries[]}`、`_network` 收发包(`--follow`;body 拿不到)、`_screenshot` 写文件(`clip` 只裁一块)。
16
-
17
- **参数**(`scope` / `in` 默认都是 `page`):
18
-
19
- - 公共:`unit?`(哪张卡;可省 —— 只剩一个模块就用它。**要卡作用域就得给**:写 `scope: 'card'` / `in: 'card'` 或用 `$card` 却不给 ⇒ **当场拒、零命令**)· `args?: string[]`(原样接在 `opencli browser workbench <verb…>` 后)· `timeoutMs?`(默认 30000)· `read?: string`(只读线档,见本节末)。
20
- - `scope: 'card'` 只给吃 CSS 的**七把**:`_find`(`--css`)· `_html`(`--selector`,没给它我们补)· `_click`/`_hover`/`_type`/`_fill`(**位置参数 0**)· `_wait`(**只在 `wait selector`**,位置参数 1)—— `card` = 给那个目标挂 `#wb-card-<unit> .wb-card `。
21
- - **只读线档**:任何一把带上 `read: <站点地址>` 就跑在**另一条会话** `workbench-read` 上 —— **不要调试约定、不要板在屏上、不认卡作用域**(`unit` / `scope: 'card'` / `in: 'card'` / `$card` 逐个当场拒、零命令),也**不要求会话工作区里有模块包**(它一行都没碰工作区);结果与屏那一档**同一组字段**(`ok`/`exitCode`/`timedOut`/`truncated`/`stdout`/`stderr` 原样),另带 `session` 与 `opened`。`opened` = 我替你开过一次站点:跑之前先 `get url` 比域名,不在那个站点上就 `open <read>` 一次再跑。`_eval` 上沿用读线约定:脚本回 `{ ok: false, code: "not-logged-in" }` 时输出后补一句「去 <read> 登一次」(只补说明,结果字段一个不改)。**这一档取代了老的 `_probe`**(2026-10-20;来龙去脉在本仓 `docs/adr/0062` §6 与 `.scratch/diy-read-line/spec.md` —— 那两份不随包出货,只给改外壳的人):老 `_probe` 那层结构化 `entries` 没有替代 —— `_find` 的 stdout 自己解析。
22
- - `in: 'card'` 只 `_eval`(注入卡根 `root`,**脚本自己 `return`**);`clip`(CSS px)只 `_screenshot`(拍完本地裁);`$card` **任何 `args` 里**都替换成 `#wb-card-<unit> .wb-card`。
23
-
24
- **拿回**:`{ ok, exitCode, timedOut, truncated, stdout, stderr }` + `workshop`/`unit`/`verb`/`argv`(`read=` 那一档改成 `session`/`opened`:只读线不碰工作区,所以没有 `workshop`/`unit`);`_screenshot` 另给 `file`/`cropped`/`cropReason`。`ok` 只管进程,事实全在 `stdout`(报错也是一份 `{"error":{…}}`,`ok` 可能仍 `true`);不抠 JSON / 不翻中文 / 不做 ok 归一,**要参数先问 `--help`**。**"原样"有折损**:`args` 里空串 / 纯空白 / 非字符串丢掉、留下的词前后 trim、`stdout`/`stderr` 也 trim ⇒ **`_fill` 清空不了输入框**、**`_type` 敲不了空格**,没有别的口子就如实说做不到。
25
-
26
- ## 3 老工具 → 新命令
27
-
28
- | 老 | 新 |
29
- |---|---|
30
- | `_look` | `state` + `get html` + `eval` + `screenshot`(卡图靠 `clip`,上游没有 selector) |
31
- | `_reload` / `_viewport` | `eval 'location.reload()'` / `screenshot --width/--height` |
32
- | `_select` | **没了**(上游只认原生 `<select>`);下拉两拍:卡内点触发器 → `scope: 'page'` + `--role menuitem` 按名字点 |
33
- | `_net_*` | `network`(`--follow` 臂上、收工 `_debug_stop` 顺手收;**取 body 走 `_eval` 的 `fetch`**) |
34
- | `_click` `_hover` `_type` `_fill` `_keys` `_scroll` / `_logs_*` | 同名,参数改成原样转发(`console` 是空桩,控制台只能我们做) |
35
-
36
- ## 4 如实边界
37
-
38
- - `screenshot` **没有上游 selector**:卡图先 `eval` 量盒子再填 `clip`;不给路径我们补到工坊同级的 `shots/<unit|page>-<时间戳>.png`(否则回 base64);要裁的块不在视口里就退回整屏图,原因在 `cropReason`。
39
- - `state` **没有 `scope`**(卡内定位用 `find --css '$card …'`);**没有右键 / contextmenu**;ref 只在**一次快照**里有效,别跨调用存;`_dialog` 的 `accept|dismiss` 不校验;`_network --detail` **取不到 body**(缓存落在会话可写根之外)—— 走 `_eval` 里的 `fetch`。
40
- - 录制交接那条**会话捕获**(`_record_prepare` 起的:先短暂 `network --follow` 只做"臂上",然后每 3 秒一次**非跟随** `browser <session> network --all --raw`,**落盘前先按 URL 去掉静态资源与 `data:`**(含 node 就过滤、没有就原样落),一轮一行追加到工坊同级的 `shots/rec-<unit>.jsonl`)**带 body**:非跟随那条会把 live 缓冲排空,连 `request.body`(入参)与 `body`(出参)一起打出来;跟随那条只打形状(只有方法 / URL / 状态 / ct / size / 时间),所以只拿它臂上、装完就杀。它盖**整场**、在浏览器那一层,比页面钩子全(钩子只认那一页自己的 `fetch`/`XHR` —— 应用提前把 `window.fetch` 存走、或在 iframe / Worker / WebSocket 里发,它就看不见)。**不许改成"用 opencli 默认过滤"**:那层是**按响应 ct 白名单**判的(只留 json/xml/text-plain/javascript),"按下写就立刻按结束"那条**回包还没到、ct 为空**的写会被当场丢掉 —— 实测 127 条里它只留 33 条、那条写没了;按 URL 去静态则是 127 → 30 条、那条写还在。
41
- - 另外 19 把:台账 `_list`/`_status`/`_install`/`_show`、组件与 kit 清单 `_components`(不填给清单与类型面根,`name=Button` 回它的类型文件与声明)、宿主运行态 `_panel`/`_status`、数据源 `_recon`、`_spec`(问一条 opencli 命令的形状,**插件也适用**)、`_logs_*`、约定本身 `_debug_*`、录制交接 `_record_*`(§5)。
42
-
43
- ## 5 录制交接(四把):抓那一次"在外站页面上点出来的写",清单 → 挑一条 → 看原文
44
-
45
- | 工具 | 干什么 |
8
+ | 要做什么 | 用 |
46
9
  |---|---|
47
- | `_record_prepare` | 开一扇窗**顶到最前**(**要用户点头一次**)、装浮条、臂上捕获。参数 `unit` / `url` / `doing`,可选 `expect:{method,urlContains}`;**不给 `url` 当场拒**,**跨域也当场拒**(那个模块的 `design.json` 说过读源时,只许同域录;还没说过就不拦)。返回带 `wrote: false` |
48
- | `_record_read` | 取那一段的**清单**:`rows`(每行 `index` / `key` / `at` / `method` / `url` / `status` / `ct` / `size` / `source` / `hasRequest` / `hasResponse` / `likelyWrite` / `marked`)· `hookCaught`(钩子这一段**原始**条数)· `capturedLines` / `capturedInSegment` / `capturedOutside` · `dropped` · `noBody` · `nav` · `captureRound`(会话捕获**最近一轮**成没成:`ok` / `fail` / 空 = 还没回)。**清单一条都不删、也不回体**:页面钩子(`[t0,t1]` 段)与会话捕获(整场按段切)合并,**同名只配掉一条钩子行**(缺的 key / 入参 / 出参由捕获那条补上);捕获里两条同 method+url 是**两次真调用,都留着**。**`expect` 不再是过滤器**:它只给命中的行标 ⭐,⭐ 排在前面(不然只列最后十几条时它可能不在正文里);不设 expect 时 ⭐ = 非 GET 或带 request body。**正文每一行把它要的两样直接摆出来**:`· [0] ⭐ POST 站点/路径(捕获 · 入参 有 · 出参 40 B · at 1792583917392)` —— 开头那串就是 `key` 原文(opencli 给的,可能带 `#2`),`at` 是毫秒时间戳;**没有 `key` 的钩子行**写成 `method url(钩子 · … · 无 key)`,那种行只在还开着时按 `index` 取得到。要哪一条的原文就带它的 `key`(或 `index`)去 `_record_detail`。没按开始 / 还在录都如实说,不假装"没有请求"。可选 `waitMs`(默认 0 = 立刻回,帽 15 分钟):等不到就回 `timedOut` 并挂一条 notice 叫醒你 —— **一场就是一个周期(开始 → 结束)**,结束之后要再录就**重新 `_record_prepare` 一场**(同场没有"再按一次开始"这回事)。**他按「结束录制」不用他再跟你说一声**:录制期间宿主每 3 秒自己看一眼那一页(浮条在目标页里、推不到宿主;宿主能叫醒你的通道只有 `deliverDebugNotice`),一看到 `finished` 就推状态并发一条 notice 叫你来 `read` |
49
- | `_record_detail` | 取**某一条**的原文:入参(`request.body`)与出参(`response body`),外加请求头(可能含凭据,别落进 `design.json`)。参数 `key`(清单里那一条,原样给、含 `#2` 这种后缀)或 `index`(清单里的序号,只在那一场还开着时可用);**同一个 key 跨轮会重名**(opencli 的 key 计数器每批重置),所以再带上清单里那行的 `at` 才钉得住是哪一次(不给就取最早那条,并在正文里说重名几次)。可选 `unit` —— 那一场**收工之后**要给 unit,我去读 `shots/rec-<unit>.jsonl.last`(`_record_stop` 会把原文留成 `.last`,下一场 prepare 开录时清掉它)。取不到时如实说"两边都没有",并给兜底:DevTools 复制 curl。可选 `maxBody`(默认 60000 字符,0 = 不限) |
50
- | `_record_stop` | 停捕获、收掉那个标签、清状态(**收工前先读一次捕获文件**,与 read 同一套切段 / 合并;**原文另存成 `.last`**,所以收工之后还能 `_record_detail`;没挪成就不说"原文留在…")。回一段能直接落 `design.json` 的原文(`probeCommand`)—— **只认第一条带 body 的**;一条都没有就回空串,并如实说"只有方法 / URL,body 拿不到 —— 补到 body 再落"(没 body 的写命令落下去是一条**会打错的**命令)。**先 read(或先 detail 完)再 stop** |
51
-
52
- 左栏底部那一格给他的键(等出手 `[取消]` / 录制中 `[停]`)只**给 agent 发一条消息**,不直接改页面那一半(`[停]` 有意不动状态);**录完那一档没有键** —— agent 自己来取、自己收工。
53
- 一场过期(TTL 15 分钟)或台账被换走,**不会把采集循环留在地上**:看门那一拍看到台账没了就停捕获 + 收标签,
54
- 循环本身也被宿主 pid 兜着(宿主重起后孤儿循环自己退);`prepare` 开新一场前也先对同一落点收一次。
55
- 细则、三颗键与如实边界(`fetch`/XHR 之外抓不到、50 条 / 16 KB 上限、一次一场 15 分钟)
56
- 都在 [WRITES.md](WRITES.md) 步骤 3;纪律同那一份的「三条写纪律」。
10
+ | 认路、装没装、起不起得来 | `_status`(`unit` 可省);它的「下一步」就是下一件事 |
11
+ | 卡片空白 / 崩了 / 读不到 | `_panel` |
12
+ | 找 opencli 命令、看命令形状 | `_recon` → `_spec`(`--help -f json` 原样) |
13
+ | kit 有什么 / 令牌叫什么 | `_components`(`bucket` / `name` 取声明;`scope: tokens` 现读 `--dsw-*` / `--wb-*`) |
14
+ | 装一次 / 只上板 | `_install` / `_show` |
15
+ | 发布前记这一版改了什么 | `_changelog`(`note` 必填;文件不在就先建) |
16
+ | 发布 | `_publish`,只在用户说「帮我发布」之后 |
17
+ | 看 / 动当前页 | `_state` · `_html` · `_find` · `_screenshot` · `_click` · `_hover` · `_type` · `_fill` · `_keys` · `_scroll` · `_wait` · `_network` · `_dialog` |
18
+ | 控制台 | `_logs_watch` → 操作 → `_logs_read` → `_logs_stop` |
19
+ | 抓「用户点了之后发了什么」 | `_record_prepare` → 用户操作 → `_record_read` → `_record_detail` → `_record_stop` |
20
+
21
+ ## 2 前置
22
+
23
+ - 只在 DIY 模式的会话出现;面板得画在屏上。
24
+ - 看 / 动 / 网络 / 日志族先 `_debug_start`(审批一次),否则整族拒、零命令;收工 `_debug_stop`。
25
+ `_pause` 只暂停看屏。不碰屏的:`_status` / `_panel` / `_components` / `_recon` / `_spec` /
26
+ `_install` / `_show` / `_changelog` / `_publish`;`_find` / `_eval` 带 `read=` 时也不碰。
27
+ - 录制独立:`_record_prepare` 审批一次,其余不问;同时只有一场,要重来先 `_record_stop`。
28
+
29
+ ## 3 参数之外的坑
30
+
31
+ - 浏览器工具都能带 `args` 原样转发;空串 / 纯空白 / 非字符串会被丢弃,词前后 trim ——
32
+ `_fill` 清不空、`_type` 打不了纯空格。
33
+ - `ok` 只表示进程退出。事实在 `stdout`;错误体是 `{"error":{…}}`,`ok` 仍可能是 true。
34
+ 不解析、不翻译;参数不明先 `args: ['<verb>', '--help']`。
35
+ - 卡作用域:`unit` 指卡;`scope: 'card'` / `in: 'card'` / `$card` 需要它,
36
+ `$card` 在 args 里换成 `#wb-card-<unit> .wb-card`。
37
+ - 只读线:带 `read: <地址>` 时跑在 `workbench-read` 会话,不碰屏、不认卡作用域、能代开站点;
38
+ `_eval` 返回 `not-logged-in` 时让用户去登录。
39
+ - `_screenshot` 不给路径时写 artifacts 目录;拿不到选择器就先 `_eval` 量盒再 `clip`。
40
+ - `state` 没有 `scope`;`_select` 不存在(下拉两拍);ref 只在一次快照内有效;
41
+ `_dialog` 不校验 `accept|dismiss`;`_network --detail` 没有 body,要 body 用 `_eval` 的 fetch。
42
+ - 录制按 URL 去静态资源后落盘;`_record_read` 不回体,取体用 `_record_detail`;
43
+ `_record_stop` 先读文件再另存 `.last`,所以先 read / detail 再 stop。取不到时给 DevTools 复制 curl。
44
+ 场次 15 分钟过期;过期或台账变更会停捕获、收标签。
45
+
46
+ ## 4 发布
47
+
48
+ 只在用户明确说「帮我发布」后调。它一次做完预检 → 全门禁 → 按出口发布 → 发后核包,审批一次。
49
+ 发布前先 `_changelog` 把这一版写进 `CHANGELOG.md`:没有这一版的记录,`_publish` 会拦下。
50
+ 审批屏会把这一版的更新日志按 markdown 展示给用户。
51
+ 需要用户接管时返回 `needsUser: { step, text, command? }` 而不抛异常:把 `text` 说给用户、给命令,
52
+ 等「好了」再跑;不向用户要 token,不展示 `package.json`。
@@ -1,138 +1,106 @@
1
- # kit 速查:要做什么 → 用哪一件 → 最小写法
1
+ # kit:选型、组合、最小写法
2
2
 
3
- `check --only kit` 列**名字**(这台机器上真有的,别照抄别的模块),**逐件用法在 `.d.ts` 的 JSDoc 里**
4
- (`workbench_diy_components name=<名字>` 一回就是它)。这一份给的是**跨件**的:怎么拼、纪律、排布。
3
+ `check --only kit` 列**当前安装的外壳真实有的**成员;逐件用法在 `.d.ts` 的 JSDoc(`workbench_diy_components name=<名>`)。
4
+ 本文件只给跨件规则与最小示例。全部从 `kit.components` / `kit.hooks` / `kit.lib` / `kit.api`
5
+ (`@dsh-workbench/core/client`)取;示例中 `h = createElement`(从 `react` 引)。
5
6
 
6
- **能拿 kit 的就别自己画。** 理由只有一条:主题、语言、悬停、禁用、焦点环、空态、被裁的处理
7
- 都只维护一份。自己画的不会跟着换主题,也不会跟着我们改口径 —— 这就是"同一个东西长得不一样"的来源。
8
-
9
- > 全部从 `const { … } = kit.components`(`@dsh-workbench/core/client`)拿。
10
- > 例子里的 `h` = `createElement`(`import { createElement as h } from 'react'`)。
7
+ 优先用 kit:kit 随外壳换主题、跟语言走。
11
8
 
12
9
  ## 1 卡片外壳
13
10
 
14
- **`Card`** —— 一张卡的壳:标题 + 刷新钮 + 卡片体。面板上每一格都是它。
11
+ - `Card`:标题 + 刷新 + 内容。面板每格都用它。
15
12
 
16
13
  ```tsx
17
14
  h(Card, { t: props.t, title: t('myunit.cardTitle'),
18
15
  refreshing: resource.phase === 'loading', onRefresh: props.onRefresh }, body)
19
16
  ```
20
17
 
21
- **`CardHead`** —— 你自己拼卡头时用它(`Card` 内部就是它)。prop 见 `.d.ts`;
22
- `platformHref` 只给这一颗就够,那颗 ↗ 立刻画出来。
23
-
24
- **`CardAnchor`** —— 外壳自己盖的锚点(`#wb-card-<unit>`)。**别自己写**,也别删。
18
+ - `CardHead`:自拼卡头时用;`platformHref` 给一个即出 ↗。
19
+ - `CardAnchor`:外壳锚点 `#wb-card-<unit>`;不写不删。
20
+ - `CardBoundary`:卡片错误边界;不要自己 try/catch 渲染。
25
21
 
26
- **`CardBoundary`** —— 卡片级错误边界。一张卡炸了不许把整块面板带走;**别自己 try/catch 包渲染**。
22
+ ## 2 状态
27
23
 
28
- ## 2 状态:这是下限最集中的地方
24
+ 五态:待机 / 在读 / 就绪 / 就绪但空 / 读不到。
29
25
 
30
- 先想清五态(`design.json` 的状态矩阵就是这五格):**待机 / 在读 / 就绪 / 就绪但空 / 读不到** —— 每一态都要有东西画。
31
-
32
- | 什么时候 | 用 |
26
+ | 场景 | 用 |
33
27
  |---|---|
34
- | 有内容了 | `StateBlock`(标题 + `hint` 一行 + 正文) |
35
- | 要他先选一个 / 正在读 / 读不到 | `CenteredState`(居中一屏) |
36
- | 就绪但空 | `EmptyState`(它只是 `CenteredState` 的简写) |
37
- | 在读、且你已经有大概形状 | `SkeletonRows`(骨架) |
38
- | 一句说明 / 一句报错 | `Note`(`tone: 'problem'` 是报错) |
39
-
40
- prop 与默认值看 `.d.ts`。
28
+ | 有内容 | `StateBlock`(标题 + `hint` + 正文) |
29
+ | 需先选 / 在读 / 读不到 | `CenteredState` |
30
+ | 就绪但空 | `EmptyState` |
31
+ | 在读且有形状 | `SkeletonRows` |
32
+ | 一句说明 / 报错 | `Note`(`tone: 'problem'`) |
41
33
 
42
34
  ```tsx
43
35
  h(CenteredState, { icon: null, title: t('myunit.loading'), hint: t('myunit.loadingHint'), compact: true })
44
36
  h(CenteredState, { icon: null, title: t('myunit.errorTitle'), detail: `${code}: ${message}`, hint: t('myunit.errorHint'), compact: true })
45
37
  h(EmptyState, { text: t('myunit.empty'), icon: h(IconPanelScreen, { size: 20 }), compact: true })
46
38
  h(StateBlock, { title: rows.title, hint: `3 条 · ${when}`, resolving: t('myunit.rows') }, table)
47
- h(Note, { children: t('myunit.hint') })
48
39
  ```
49
40
 
50
- **读不到就说读不到**:`detail` 里放原始错误码与那句话,**绝不显示 0**(0 是"真的是 0")。
41
+ 读不到:`detail` 放原始错误码与原文;不显示 0。
51
42
 
52
- ## 3 一行一行
43
+ ## 3 行与记录
53
44
 
54
- **`Rows` + `Row`** —— 键值行(左边名字、右边值)。**别自己写 `ul`/`li` 或 div 行**:
55
- 分隔线、行距、对齐、两端溢出都在这两件里。
45
+ - `Rows` + `Row`:键值行;不手写 `ul` / `li` / div 行。
56
46
 
57
47
  ```tsx
58
48
  h(Rows, null,
59
49
  ...items.map((one) => h(Row, { key: one.name, label: one.name, value: `${size} · ${when}` })))
60
50
  ```
61
51
 
62
- **`ActivityBlock` / `ActivityLine` / `ActivityAction`** —— "它做过什么"那一块(prop 见 `.d.ts`)。时间一律用它。
63
-
64
- **`LongTaskRow`** —— 长任务那一行(五态,prop 见 `.d.ts`);别自己画进度。传 `stop` 要宿主认 `signal`、peer 抬到带它那版([TOOLS.md](TOOLS.md) §9)。
65
-
66
- **`ApprovalDetail` / `ApprovalPlan`** —— 审批屏那两格(写操作才要点头);口径与那张 ASCII 见 [DESIGN.md](DESIGN.md) §3 末节。
52
+ - `ActivityBlock` / `ActivityLine` / `ActivityAction`:活动记录;时间用它。
53
+ - `LongTaskRow`:长任务行(五态);不画进度条。传 `stop` 需宿主响应 signal 且 peer 下限到位([TOOLS.md](TOOLS.md) §9)。
54
+ - `ApprovalDetail` / `ApprovalPlan`:审批屏两格;形状见 [DESIGN.md](DESIGN.md) §3 末节。
67
55
 
68
56
  ## 4 表单
69
57
 
70
- **`Form`** —— **整张表单**(面那一层):`fields` 配置进,行怎么排、错误摆哪、什么时候校验、提交怎么被挡住、
71
- 第一处错误怎么带进视口、操作区怎么排全归它。**一张完整的表单就用它**(口径见 §5,逐件看 `.d.ts`)。
58
+ `Form` 管理整张表单:字段、校验时机、错误位置、首错聚焦、操作区。
72
59
 
73
60
  ```tsx
74
61
  h(Form<{ branch: string }>, {
75
- fields: [
76
- { key: 'branch', label: t('myunit.branch'), value: draft.branch, onChange: onBranch, // 值与改写同一处
77
- validate: (v) => (v === '' ? t('myunit.branchRequired') : undefined),
78
- control: (b) => h(TextField, { id: b.id, name: b.name, invalid: b.invalid,
79
- disabled: b.disabled, value: b.value, onChange: (e) => b.onChange(e.currentTarget.value) }) },
80
- ],
81
- submit: { label: t('myunit.save'), busyLabel: t('myunit.saving') }, // cancel / extra 见 `.d.ts`
82
- onSubmit: save, // 有错就不调它;values 就是这个类型
62
+ fields: [{
63
+ key: 'branch', label: t('myunit.branch'), value: draft.branch, onChange: onBranch, // 值与改写同格
64
+ validate: (v) => (v === '' ? t('myunit.branchRequired') : undefined),
65
+ control: (b) => h(TextField, { id: b.id, name: b.name, invalid: b.invalid,
66
+ disabled: b.disabled, value: b.value, onChange: (e) => b.onChange(e.currentTarget.value) }),
67
+ }],
68
+ submit: { label: t('myunit.save'), busyLabel: t('myunit.saving') },
69
+ onSubmit: save,
83
70
  })
84
71
  ```
85
72
 
86
- **`Form<Values>` 把每一格的键与值类型钉住**(不写类型参数 = 默认档、不钉,每格要自己收窄);
87
- **值与它的改写写在同一格**(`value` + `onChange`,`control` 从绑定取 `binding.value` / `binding.onChange`
88
- —— `Form` 不持有值);**会 `visible: false` 的键在 `Values` 里写可选**(`branch?: string` ⇒ 值是
89
- `string | undefined`)。`validate` 是同步纯函数、第二参是整张值表(跨字段读得到别的格);**控件永远由
90
- `control` 给**(没有"给个 `options` 自己选控件"的 DSL)。
91
-
92
- **控件那几件** —— `Field`(没进 `FormRow` 时兜住标签 + 说明)· `TextField`(`invalid` 标红)·
93
- `ChoiceGroup`(`multi` 区分多选)· `SelectField`(默认常规下拉框,`variant: 'ghost'` 是无边框那档,
94
- **不是原生 `<select>`**)· `SaveBar`(设置面底下那条"取消 / 保存"):都是只给数据与语义 prop 的哑组件,
95
- **逐件看 `.d.ts`**。
96
-
97
- **`FormActions`** —— **操作区**(一组按钮,形状见 `.d.ts`;**整张表单里由 `Form` 组**)。
98
- **顺序 / 间距 / 换行都归它** —— `primary` 永远在右、`danger` 紧挨它左侧、一个区只许一颗 `primary`、
99
- 窄了自动折行且 `primary` 落在右下角;`busy` 自带 `Spinner`。**别自己写 `gap` / `margin` / 顺序,
100
- 也别借 `.wb-formActions` 槽类手摆**(F3 红)。`danger` 只标"破坏性"(**永远不当填充的 `primary`**);
101
- **二次确认那半归调用点**。
73
+ 规则:`Form<Values>` 钉键与值类型;值与 `onChange` 同格;`visible: false` 的键在 `Values` 中可选;
74
+ `validate` 同步纯函数、第二参为整张值表;控件只经 `control` 提供。
102
75
 
103
- **别手写 `<button>` / `<input>` / `<select>`**(dsh 有 `Button`,kit 有上面这几件;手写的判据会红)。
104
- `<textarea>` 例外(dsh 与 kit 都没有多行输入)。
76
+ 控件:`Field`(未进 `FormRow` 时包标签与说明)· `TextField`(`invalid`)· `ChoiceGroup`(`multi`)·
77
+ `SelectField`(常规下拉;`variant: 'ghost'` 无边框;非原生 select)· `SaveBar`(取消 / 保存)。
78
+ 不手写 `<button>` / `<input>` / `<select>`;`<textarea>` 例外。
105
79
 
106
- **`ConfirmBar`** —— 「先问一句再动手」那一条(标题 / 说明 / 键值 / 失败那句 / 动作区一次收口):
107
- 键值走 `Rows` / `Row`、错走 `Note tone="problem"`、动作走 `FormActions` —— **别自己拼这四个槽**。
108
- **要不要问归调用点**(`danger` 只标"破坏性")。口径见 ADR-0073。
80
+ `FormActions`:操作区。`primary` 靠右、`danger` 在其左、每区一个 `primary`、窄屏折行;`busy` 带 `Spinner`。
81
+ 不写 `gap` / `margin` / 顺序,不借 `.wb-formActions` 槽类(F3 红)。`danger` 仅标破坏性;二次确认归调用点。
109
82
 
110
- 怎么排(标签位置、错误态、尺寸)见 §5。
83
+ `ConfirmBar`:确认条。键值走 `Rows`,错误走 `Note tone="problem"`,动作走 `FormActions`;不重拼这四槽。
84
+ 是否询问归调用点。
111
85
 
112
- ## 4′ hook 怎么搭(逐件看 `.d.ts`)
86
+ ## 4′ hooks
113
87
 
114
- kit 的 hook 也分四个场合,**别自己 `useState` + `setTimeout` 造一套**(面板要同一套口径):
115
-
116
- | 场合 | 用它 |
88
+ | 场景 | 用 |
117
89
  |---|---|
118
- | 卡片里的一次读(自带同 key 合流 / 事实订阅 / 上报面板) | `useApiResource`(非 hook 的本体是 `requestResource`,**用了就一定要 `release`**) |
119
- | 写完让面板重取 / 听事实变化 | `useFactInvalidate` · `useFactRevision` · `subscribeFactRevision`(非 hook,记得退订) |
120
- | "至少转一会儿"的忙态 | `useVisibleBusy`(投影那两件 `busyView` / `heldPhase`) |
121
- | 一格**卡片**起步(gate + 忙 + 探针 + 上报 + blocker 一次叠好) | `useUnitBoard`(`read(gated)` 里自己调 `useApiResource`) |
122
- | 只要一格的能力门 / 整张能力表 | `useCapabilityGate` / `useCapabilityMap` |
123
- | 写(**别在组件里 `fetch`**) / 轮询长任务 | `useHostWrite` / `useLongTask`(画用 `LongTaskRow`;判「还在跑」`longTaskIsLive`、写完让这一格立刻重读 `refresh()`) |
90
+ | 卡片一次读(同 key 合流 / 事实订阅 / 上报) | `useApiResource`(非 hook 为 `requestResource`,用后必须 `release`) |
91
+ | 写后重取 / 听事实变化 | `useFactInvalidate` · `useFactRevision` · `subscribeFactRevision`(非 hook 要退订) |
92
+ | 最短忙态 | `useVisibleBusy`(`busyView` / `heldPhase`) |
93
+ | 整卡起步(gate + 忙 + 探针 + 上报 + blocker) | `useUnitBoard`(`read(gated)` 内调 `useApiResource`) |
94
+ | 单点 / 整表能力门 | `useCapabilityGate` / `useCapabilityMap` |
95
+ | 写(组件内不 fetch)/ 长任务轮询 | `useHostWrite` / `useLongTask`(`LongTaskRow`;`longTaskIsLive`;写后 `refresh()`) |
124
96
 
125
- 取数那条链的图见 [LINKAGE.md](LINKAGE.md) §8,写通道与配置见 [PANEL.md](PANEL.md) §5。
97
+ 取数链见 [LINKAGE.md](LINKAGE.md) §8;配置见 [PANEL.md](PANEL.md) §5。
126
98
 
127
- ## 5 表单这一层:`Form`(面)→ 行 → 控件
99
+ ## 5 表单三层:面 → 行 → 控件
128
100
 
129
- 表单三层:**面**(`Form`,写法见 §4:值 / 校验 / 操作区全归它)→ **行**(`FormRows` + `FormRow`)→
130
- **控件**(`TextField` / `SelectField` / `ChoiceGroup`)。**一张表单一个面**。**别再自己摆操作区** ——
131
- 它自己组 `[...extra, ...cancel, submit(primary)]`(提交钮永远在右;「一会在左一会在右」就是自己摆的结果);
132
- 其余口径(时机 / 第一处错误 / `busy` / `disabled`)看它的 JSDoc 与 ADR-0068 / ADR-0074。
101
+ 一张表单一个 `Form`;不要另摆操作区(`Form` 组 `[...extra, ...cancel, submit(primary)]`)。
133
102
 
134
- `FormRows` + `FormRow` + `FormActions` 留给**不是整张表单**的地方:单行(一格字段嵌在别处)、
135
- 非表单的一组动作(工具栏、卡头那几颗钮)—— 那里才自己拼这三件:
103
+ `FormRows` + `FormRow` + `FormActions` 仅用于非整表场景:单行嵌入、工具栏、卡头按钮。
136
104
 
137
105
  ```tsx
138
106
  h(FormRows, null,
@@ -140,64 +108,41 @@ h(FormRows, null,
140
108
  h(SelectField, { value, options, onChange: setMode })))
141
109
  ```
142
110
 
143
- - **标签归 `FormRow`**(默认在左,`layout: 'stacked'` 才上下堆叠);**控件只给数据与语义 prop**,
144
- 进了 `FormRow` 就别再给控件自己的 `label` / `hint`(会多一个标签、还退回上下堆叠)。
145
- - **`hint` 与 `problem` 二选一**:有错先显示错。
146
-
147
- **错误态**:位置与长相归 `FormRow.problem` + 控件的 `invalid`;**算错的逻辑留在模块**(纯函数 + 文案,
148
- 交给 `FormField.validate`)—— **别指望外壳给校验引擎**(不做 `rules` / 异步 / 联动),
149
- **"提交过没有"与聚焦第一处错也归 `Form`**(别自己记布尔、别自己调 `revealFirstProblem`)。
150
-
151
- **选择类用哪件**:候选多 / 要搜索 / 要分组 / 名单可能读不到 ⇒ `SelectField`;候选少、要一眼看全 / 多选 ⇒
152
- `ChoiceGroup`(`loading` / `emptyText` / `variant` 两件一个意思)。**loading 那颗一律是 `Spinner`**。
153
-
154
- **尺寸口径**:字段控件默认 **28**,别自己调高;行距只有一档、标签列宽也归 `FormRow(s)`
155
- (长标签不折行、同一组共用一个标签列,别自己给标签写宽度);**控件的宽度也别自己写**
156
- (字段控件自己吃满控件槽那一格,ADR-0052 §13)。非行的子项(如分组标题)自动通栏。
111
+ - 标签归 `FormRow`(默认左置;`layout: 'stacked'` 堆叠)。进了 `FormRow` 就不再给控件 `label` / `hint`。
112
+ `hint` 与 `problem` 二选一,错误优先。
113
+ - 错误:位置与外观归 `FormRow.problem` + 控件 `invalid`;校验逻辑留在模块(纯函数 + 文案,交给 `validate`)。
114
+ "是否提交过"与首错聚焦归 `Form`。
115
+ - 选择:候选多 / 需搜索 / 需分组 / 可能读不到 → `SelectField`;候选少 / 要全览 / 多选 → `ChoiceGroup`。
116
+ loading 一律 `Spinner`。
117
+ - 尺寸:字段控件默认 28;行距与标签列宽归 `FormRow(s)`;控件宽度归控件槽。非行子项自动通栏。
157
118
 
158
- **反例(别这么写)**:
159
-
160
- ```tsx
161
- h('span', { className: styles.label }, …) // ← 手写标签行:F1 红
162
- h('div', { className: styles.form }, …) // ← 自己管行距(.form { gap }):F2 红
163
- h('form', …) // ← 裸 <form> / 手写行槽类:F4 红(表单归 Form)
164
- ```
119
+ 反例:手写标签行(F1)、自管行距(F2)、裸 `<form>` / 手写行槽类(F4)。
165
120
 
166
- **时点**:这一层随外壳发版后模块才能用(`check --only kit` 拿 peer 下限那份外壳比,不抬必红)。
167
- **`Form` / `ConfirmBar` 是新成员**:要用得等外壳**发一版含它的版本**,再把 peer 下限抬到**那一版**
168
- (版本随发版填,别照抄别的件);`FormRow(s)` / `FormActions` 的下限是另一件事。
121
+ 时点:`Form` / `ConfirmBar` 需外壳发版含它们后,把 peer 下限抬到该版。
169
122
 
170
- ## 6 别的
123
+ ## 6 其他
171
124
 
172
- - **`Link`** —— **唯一**的外链写法(`h(Link, { href, children })`):**别去掉箭头、别在文案里自带 `→`、
173
- 别手写 `<a>`**(判据红)。
174
- - **`SettingsSection` / `Stack` / `BlockerState` / `Tip`** —— 一屏设置里的一节 / 竖排间距 / 阻断屏 /
175
- Tooltip(只有图的地方**必须**有它或 `aria-label`);prop 看 `.d.ts`。
176
- - **真机上造不出来的那几态**:拿你自己那份 `plugin.test.ts` 把五态各喂一遍(`check --only cards`
177
- 拿七种外壳侧输入各喂一遍,只问"退化得干不干净")。
178
- - **图标** —— `IconPanelScreen` / `IconModuleMark` / `IconNpm` / `IconOpencli`;其余图形一律从 dsh 的
179
- `Icon*Regular` 拿,**别自己画图标**。
125
+ - `Link`:唯一外链(`h(Link, { href, children })`);不去箭头、文案不带 `→`、不手写 `<a>`。
126
+ - `SettingsSection` / `Stack` / `BlockerState` / `Tip`:设置节 / 竖排 / 阻断屏 / Tooltip(纯图必须有它或 `aria-label`)。
127
+ - 图标:`IconPanelScreen` / `IconModuleMark` / `IconNpm` / `IconOpencli`;其余从 dsh `Icon*Regular` 取,不自绘。
128
+ - 真机造不出的状态:`plugin.test.ts` 覆盖五态。
180
129
 
181
- ## 7 三条纪律
130
+ ## 7 规则
182
131
 
183
- 1. **调用点只给数据与语义**。别从外面塞外观类名 —— 只有布局槽例外(通栏、外边距、网格位置)。
184
- 2. **字号与颜色走 kit 与令牌**(`--dsw-*` / `--wb-*`),不写字面值。**重点可以大**:要突出的数字用
185
- `--dsw-font-xl-24`(24/32、600)加 `font-variant-numeric: tabular-nums`(见 [PANEL.md](PANEL.md) §6);
186
- 刻度外非要字面值时,在模块 `README.md` 里写一句为什么。
187
- 3. **自己写 CSS 只写布局**(flex / grid / gap / 对齐)。要一个新的"长得像控件"的东西时,
188
- 先问一句:kit 里是不是已经有了?
132
+ 1. 调用点只给数据与语义;外观类名只允许布局槽(通栏、外边距、网格位置)。
133
+ 2. 字号与颜色走 kit 与令牌。重点数字用 `--dsw-font-xl-24` + `tabular-nums`;
134
+ 令牌名现查不猜:`workbench_diy_components` 给 `scope: 'tokens'`(现读装着的 dsh 与外壳;`name=--dsw-…` 连浅/深值)。
135
+ 刻度外字面值在模块 `README.md` 说明([PANEL.md](PANEL.md) §6)。
136
+ 3. 自写 CSS 只写布局(flex / grid / gap / 对齐)。
189
137
 
190
- ## 8 kit 里没有、但你确定要标准控件
138
+ ## 8 需要 kit 没有的标准控件
191
139
 
192
- **先问工具**:DIY 会话里调 `workbench_diy_components`(不填给清单;`name=Button` 回它的类型与声明)。工程域用 `dsh-wb-module check --only primitives`
193
- (`--all` 全表 / `--json` 机器档)—— 分桶导出 + 与 kit 求交(`kit 里已有` 的优先用)+
194
- 该写进 `package.json` 的那两行;骨架默认已备好。
195
- **要看某一件**:`--file <名字>`(`--files | grep <名字>`)。
140
+ 查:`workbench_diy_components`(DIY 会话)或 `check --only primitives`(`--all` / `--json` / `--file <名>`)。
141
+ 写入:
196
142
 
197
143
  ```jsonc
198
144
  "dsh": { "client": { "external": [ "@dsh-workbench/core/client", "@deepseek-ai/dsh-client-ui-primitives" ] } },
199
- "devDependencies": { "@deepseek-ai/dsh-client-ui-primitives": "<外壳钉的那版,精确版>" }
145
+ "devDependencies": { "@deepseek-ai/dsh-client-ui-primitives": "<与外壳一致的精确版>" }
200
146
  ```
201
147
 
202
- 改完 `pnpm install` 一次;三条会红的边界(只在浏览器侧引 / 版本精确对齐 / 能拿 kit 的别引)与
203
- 宿主侧的契约都见 [PANEL.md](PANEL.md) §9。
148
+ 改后 `pnpm install`。边界见 [PANEL.md](PANEL.md) §9。