@dsh-workbench/core 0.3.4 → 0.3.5

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 (44) hide show
  1. package/client.js +1243 -78
  2. package/dist/client/client.body.js +1243 -78
  3. package/dist/client/index.js +1243 -78
  4. package/dist/host/index.js +623 -841
  5. package/dist/types/app/client.d.ts +201 -60
  6. package/dist/types/modules/panels/model/diy-context.d.ts +4 -5
  7. package/dist/types/modules/panels/model/diy-observers.d.ts +2 -2
  8. package/dist/types/modules/panels/model/diy-page.d.ts +1 -1
  9. package/dist/types/modules/panels/unit.d.ts +3 -4
  10. package/dist/types/shared/api/context-chips.d.ts +20 -0
  11. package/dist/types/shared/api/host/diy-components.d.ts +17 -6
  12. package/dist/types/shared/api/host/diy-workshop.d.ts +0 -3
  13. package/dist/types/shared/api/host/preset-scope.d.ts +16 -0
  14. package/dist/types/shared/api/host/tool-scope.d.ts +6 -2
  15. package/dist/types/shared/api/host/workshop-root.d.ts +7 -0
  16. package/dist/types/shared/components/BlockerState.d.ts +12 -0
  17. package/dist/types/shared/components/PanelFoot.d.ts +9 -0
  18. package/dist/types/shared/components/Tip.d.ts +8 -0
  19. package/dist/types/shared/hooks/bundle-queries.d.ts +12 -0
  20. package/dist/types/shared/hooks/fact-transports.d.ts +4 -0
  21. package/dist/types/shared/hooks/panel-state-report.d.ts +12 -0
  22. package/dist/types/shared/hooks/use-api-resource.d.ts +22 -0
  23. package/dist/types/shared/hooks/use-blocker-actions.d.ts +8 -0
  24. package/dist/types/shared/hooks/use-capabilities.d.ts +55 -0
  25. package/dist/types/shared/hooks/use-capability-gate.d.ts +16 -0
  26. package/dist/types/shared/hooks/use-card-summary.d.ts +7 -0
  27. package/dist/types/shared/hooks/use-diy-debug.d.ts +13 -0
  28. package/dist/types/shared/hooks/use-diy-record.d.ts +13 -0
  29. package/dist/types/shared/hooks/use-facts.d.ts +10 -0
  30. package/dist/types/shared/hooks/use-host-write.d.ts +12 -0
  31. package/dist/types/shared/hooks/use-long-task.d.ts +8 -0
  32. package/dist/types/shared/hooks/use-unit-board.d.ts +17 -0
  33. package/dist/types/shared/hooks/use-visible-busy.d.ts +17 -0
  34. package/package.json +2 -2
  35. package/skills/workbench-module-diy/DATASOURCE.md +2 -2
  36. package/skills/workbench-module-diy/FAMILY.md +5 -4
  37. package/skills/workbench-module-diy/KIT.md +105 -100
  38. package/skills/workbench-module-diy/PANEL.md +19 -11
  39. package/skills/workbench-module-diy/REVIEW.md +4 -4
  40. package/skills/workbench-module-diy/SKILL.md +5 -4
  41. package/skills/workbench-module-diy/TOOLING.md +2 -2
  42. package/skills/workbench-module-diy/TOOLS.md +1 -1
  43. package/skills/workbench-module-diy/WIRING.md +2 -1
  44. package/skills/workbench-module-diy/HOOKS.md +0 -95
@@ -16,9 +16,9 @@
16
16
  | 落点、分层文件形状、六个脚本([BUILD.md](BUILD.md) §1–§2) | `dsh-wb-module init --template diy`(骨架带分层目录 + 一行 `arch.config.mjs` + 两行 `eslint.config.mjs`(`moduleLint({ rootDir: import.meta.dirname })`)+ 一条真读盘的数据源)+ `scripts/smoke.mjs` |
17
17
  | 语法卫生红了、要自动修([REVIEW.md](REVIEW.md) 第十三条) | `dsh-wb-module lint [--fix]`(**`check` 永不动手改文件**;`--fix` 才写回) |
18
18
  | 装、上板、不重装([WIRING.md](WIRING.md) §3) | `workbench_diy_install` / `workbench_diy_show` / `workbench_diy_status`(工坊那一跳自己上板) |
19
- | 想知道 dsh 原语 / 这份外壳的 kit 里有什么、某一件怎么用([KIT.md](KIT.md) §8) | `workbench_diy_components`(不填给清单 + 类型面根;`name=Button` 回它的类型文件与那份声明——JSDoc 里写着默认值与语义;工程域用 `dsh-wb-module check --only primitives --file <名字>`) |
19
+ | 想知道 dsh 原语 / 这份外壳的 kit 里有什么、某一件怎么用([KIT.md](KIT.md) §8) | `workbench_diy_components`(不填给清单 + 类型面根;`name=Button` 回它的类型文件与那份声明——JSDoc 里写着默认值与语义;`bucket=icons` 把那一桶列全——`all=true` 的全表在真工坊超 8192、中间会被修剪器裁掉,按桶取才装得下;工程域用 `dsh-wb-module check --only primitives --file <名字>`) |
20
20
  | 它到底起得来没(装配抛的错 / 声明被拒的原文) | `workbench_diy_status`("起得来"那一行,见 [REVIEW.md](REVIEW.md) 四步取证的第二步) |
21
- | 看 / 动**用户那一屏**之前要先取得约定(ADR-0055 §7「调试是一次约定」) | `workbench_diy_debug_start`(`unit=…`,**要用户点头一次**)—— 没有它,浏览器那 14 把 / `logs_*` **一律当场拒**(拒的时候一条命令都不发);不看屏了 `_pause`、收工 `_stop`(**收工顺手把网络捕获与日志钩子也收了**)。`_panel` / `_status` / `_probe` 不碰他那一屏,不用开。**全表与参数见 [FAMILY.md](FAMILY.md)** |
21
+ | 看 / 动**用户那一屏**之前要先取得约定(ADR-0055 §7「调试是一次约定」) | `workbench_diy_debug_start`(`unit=…`,**要用户点头一次**)—— 没有它,浏览器那 14 把 / `logs_*` **一律当场拒**(拒的时候一条命令都不发);不看屏了 `_pause`、收工 `_stop`(**收工顺手把网络捕获与日志钩子也收了**)。`_panel` / `_status` 不碰他那一屏,不用开(`_find` / `_eval` 的 `read=` 档也不碰 —— 那是只读线)。**全表与参数见 [FAMILY.md](FAMILY.md)** |
22
22
  | 抓那一次**在外站页面上点出来的写**(他的写只发生一次,也不在我们那条会话里) | `_record_prepare`(`unit` / `url` / `doing`,**要用户点头一次**)→ 请他按「开始录制」→ 做**一次**那动作 → 按「结束录制」→ `_record_read` 取那一段 → `_record_stop` 收工(**先 read 再 stop**)。它不走调试约定,只碰自己的 `workbench-rec` 会话;三颗键、边界与实话见 [FAMILY.md](FAMILY.md) §5 · [WRITES.md](WRITES.md) 步骤 3 |
23
23
  | 看 / 动那一屏的浏览器动作怎么发(枚举 opencli,argv 与结果原样转发) | 14 把:`workbench_diy_state` / `_html` / `_find` / `_click` / `_hover` / `_type` / `_fill` / `_keys` / `_scroll` / `_wait` / `_eval` / `_screenshot` / `_network` / `_dialog` —— 公共参数只有 `unit?` / `args?` / `timeoutMs?`(另有 `scope` / `in` / `clip` 只长在该长的那几把上;`$card` 在任何 `args` 里都替换)。**不知道参数就问 `--help`**(也照样转发);返回体是 opencli 的原样,事实在 `stdout` 里 |
24
24
  | 这一页的控制台 / 网络怎么取证([REVIEW.md](REVIEW.md) 四步取证) | `workbench_diy_logs_watch` / `_read` / `_stop`(钩子有上限:每页 200 条、单条 400 字)—— **opencli 的 `console` 是空桩**(恒回空数组、还 exit 0),控制台只能我们自己做;网络是 `workbench_diy_network` 一把(`--follow` 臂上、`--failed` / `--since` 这些原样转发;收工 `_debug_stop` 顺手收),但**`--detail` 取不到 body** —— 缓存落在会话可写根之外,只能列请求 key;要 body 走 `_eval` 里的 `fetch`。网络那一头工作台走的是**隔离 opencli**(自己的 `HOME`),它的缓存与你自己 shell 里 `~/.opencli` 那份不是同一个 —— 核验要用工具,别看错文件 |
@@ -89,7 +89,7 @@
89
89
 
90
90
  跑几十秒的事(构建、发布、批量删)做成**长任务**。宿主半侧用 `mount` deps 里的 `longTasks` 建注册表并挂回返回值:`longTasks.create({ key, run, step, outcome, view })` —— `run({ payload, onStep })` 里用 `onStep({ kind: 'start', text })` 报步骤、`return` 结果;`step`/`outcome`/`view` 各映射一段给界面;返回里带上 `longTasks: runs`。
91
91
 
92
- 浏览器半侧用 kit 现成的:`kit.hooks.useLongTask({ unit, workspace })` → `feed.runs`(每条 `status`/`step`/`steps`/`why`),画的时候用 `<LongTaskRow …/>` 或 `longTaskDot`。状态四种:`running` / `needs-login`(带 `loginUrl`)/ `ok` / `failed`;**契约里没有"停止"**,用户要中途停得自己在 `run` 里看信号。判据(`check --only contracts`):注册表五个方法要全、`create` 的 `key`/`run`/`view` 不能缺、产物里必须有 `useLongTask`/`LongTaskRow`/`longTaskDot` 之一。
92
+ 浏览器半侧用 kit 现成的 `useLongTask`(→ `feed.runs`),画用 `LongTaskRow` 或 `longTaskDot`(每条的字段与四种状态见 `.d.ts`);**契约里没有"停止"**,用户要中途停得自己在 `run` 里看信号。判据(`check --only contracts`):注册表五个方法要全、`create` 的 `key`/`run`/`view` 不能缺、产物里必须有 `useLongTask`/`LongTaskRow`/`longTaskDot` 之一。
93
93
 
94
94
  ## 10 用户自己按的那个入口 / 范例
95
95
 
@@ -44,7 +44,8 @@ import 运行值会被**打进你的包**(第二份实例),而且浏览器
44
44
  另外两条不是"被拦",是纪律:**样式走 `*.module.css`,类名别带 `wb-` 前缀**(`wb-*` 是外壳的**全局**
45
45
  类名:模块定义它、或借契约里没有的那一个,`check --only style` 都判红;模块自己的类名由构建**按文件路径
46
46
  哈希**,天然不会跟谁撞。要借外壳契约里的类,写 `:global(.wb-…)`,且那个类必须在契约里);
47
- **`src/**` 里零注释** —— 要说的写进 `README.md` 或 spec。
47
+ **`src/**` 里只许 JSDoc、且贴着导出**(用法归声明;`//`、实现里的解释与 `.css` 注释都不许)——
48
+ 为什么写进 `README.md` 或 spec。
48
49
 
49
50
  ## 3 装上、上板
50
51
 
@@ -1,95 +0,0 @@
1
- # HOOKS:kit 里那些 hook 怎么用
2
-
3
- **为什么有这份**:外壳与共享包的源码按"零注释"规矩写,emit 出来的 `<名字>.d.ts` 里**没有说明文字** ——
4
- 只有参数名与类型。组件的 prop 一般看得出来,hook 的语义看不出来(什么时候调、取数什么时候重来、
5
- "在忙"为什么还要挂一会儿)。这份就是那些语义的出处。
6
- **原语那 5 个 hook 不在这里**(`useAnchoredPosition` / `useAnchoredMaxHeight` / `useCodeHighlighter` /
7
- `useDismissOnOutsidePointer` / `useModalLayer`):它们的 `.d.ts` 自带 JSDoc。
8
-
9
- 问清单、或要某一件的类型文件:`workbench_diy_components`(`name=useVisibleBusy`)。
10
-
11
- ## 0 两条路,别绕开
12
-
13
- - **取数**:卡片里所有读都走 `useApiResource`。它自带三件事 —— 同 key 合流、事实通道订阅("这条变了"自动重取)、
14
- 把读数上报面板(`workbench_diy_panel` 看的就是它)。
15
- - **状态**:`useVisibleBusy`(在忙 / 安定)+ `useCapabilityGate`(被能力挡住没)+ `useUnitBoard`(把两者与
16
- 卡片读数叠成一组)。**别自己 `useState` + `setTimeout` 造一套**:面板要的是同一套口径。
17
-
18
- ## 1 取数与事实
19
-
20
- ### `useApiResource(route, query, refreshToken, options?)` → `{ phase, value?, error?, at? }`
21
-
22
- 详见 [LINKAGE.md](LINKAGE.md) §8。`phase` 四态 `idle` / `loading` / `ready` / `error`;`value` 是
23
- `ApiResponse<T>`(`ok` / `data`);`options`:`enabled`(前置条件不满足就别发)、`key`(自定义身份)、
24
- `fact`(订哪条事实,默认 `factOfRoute(route)`)、`report: false`(不上报面板 —— 夹具 / 后台探针用)。
25
-
26
- ### `useFactRevision(fact)` → 版本号 | `useFactInvalidate()` → `(fact?) => void`
27
-
28
- 详见 [LINKAGE.md](LINKAGE.md) §8。前者是"这条事实的版本",喂给 `useApiResource`(默认订阅已经接好);
29
- 后者是**写完之后**把这条标脏 —— 不标,面板就停在旧值。
30
-
31
- ### `subscribeFactRevision(fact, listener)` → 退订函数
32
-
33
- 非 hook:给"不在渲染里"的地方用(宿主事件、定时器里看到的旧值)。返回值就是退订函数,**记得退订**。
34
- 渲染里要版本号用 `useFactRevision`,别自己 `useEffect` + `subscribe` 绕一圈。
35
-
36
- ### `resourceIdentity(route, query, override?)` | `readKeyOf(identity, refreshToken, factRev)`
37
-
38
- 都不是 hook:前者是"这次读的身份"(`route` + 排序后的 query;给了 `override` 就用它);后者把身份与两个
39
- 版本号拼成**合流的 key**。要自己管缓存时才用得上。
40
-
41
- ### `requestResource(route, query, key)` → `{ task, release }`
42
-
43
- 非 hook 的取数本体:**同 key 只发一次**(并发调用共享同一个 `task`),最后一个 `release()` 才 abort。
44
- `useApiResource` 内部就是它 + 事实订阅 + 上报。用它就**一定要 `release`**(放 `finally`),否则那条请求永不取消。
45
-
46
- ## 2 忙与安定
47
-
48
- ### `useVisibleBusy(active, floorMs)` → `{ busy, settling }`
49
-
50
- **最短可见时长**:`active` 变真立刻 `busy`;变假时若这次忙还没到 `floorMs`,`busy` 先挂着,这期间
51
- `settling: true`("读数在回来的路上")。`floorMs` 传共享包的 `MIN_BUSY_MS`,别自己编。
52
- 为什么:取数几毫秒就回来时不挂一会儿,用户看到的是"闪一下",比不转还难读(ADR-0022)。
53
-
54
- ### `busyView(active, visible)` | `heldPhase(phase, refreshing)`
55
-
56
- 两个纯函数(不是 hook,好测):`busyView` 是"真忙还是在收尾"的投影,`useVisibleBusy` 用的就是它;
57
- `heldPhase` 把"正在刷新"折进 `phase`(`refreshing && phase === 'ready'` ⇒ `'loading'`),
58
- 让"旧值还显示着、新的在路上"这种态有名字。
59
-
60
- ## 3 卡片那一格与能力门
61
-
62
- ### `useUnitBoard({ unit, gate, refreshToken, onRefresh, onSummary, read, t, enabled?, platformBlocker? })`
63
-
64
- **卡片起步就用它**:把"gate 挡着就不读"、忙 / 安定、探针(被能力挡住或读数报错时自动 `forceProbe`)、
65
- `useCardSummary`(把 `{ refreshing, at }` 交给调用点上报面板)、以及 blocker 的拆分
66
- (`capabilityBlocker` / `errorBlocker`)一次叠好。`read(gated)` 是你自己的读数函数(里面调 `useApiResource`);
67
- 返回 `feed`(含 `state`)、`capability`、`gated`、`refresh`、`forceProbe`、`summary` 与那几个 blocker 字段。
68
-
69
- ### `useCapabilityGate(unit, refreshToken, options?)` → `{ capability, gated, forceProbe }`
70
-
71
- 这一格的**能力**(`design.json` 里声明的那份)与"挡住了没"。`options.mode` 换门的口径(默认 `'capability'`),
72
- `options.fixture` 给夹具。`forceProbe()` 是"现在就去探一次"。
73
-
74
- ### `useCapabilityMap(refreshToken)` → `{ capabilities, checkedAt, checking, forceProbe, reload }`
75
-
76
- **全部**单元的能力表(不是一格):设置页 / 体检那一类界面用。`checking` 是探针在跑,`reload()` 重取整张表。
77
-
78
- ### `useCardSummary(onSummary, summary)`
79
-
80
- 把 `{ refreshing, at }` 在变化时交给调用点(`onSummary` 要**稳定引用**,一般 `useCallback`)——
81
- 面板那行"最近一次读是什么时候"就是这么上去的。`useUnitBoard` 已经接好;自己搭卡片才直接用。
82
-
83
- ## 4 写与长任务
84
-
85
- ### `useHostWrite({ channel, resource?, networkText })` → `{ busy, failed, detail, send, fail, clear }`
86
-
87
- 详见 [PANEL.md](PANEL.md) §5(写通道与配置持久化)。`send(body, { silent })` 回 `WriteOutcome`
88
- (`ok` / 人话 message / detail),失败自动落到 `failed` + `detail`(界面直接显示,别自己拼);`silent` 是不打扰;
89
- `fail` 是"本地就知道不行"时自己塞一条。**写只走这条**:别在组件里直接 `fetch`。
90
-
91
- ### `useLongTask({ unit, workspace, mocked?, enabled?, pollMs? })` → `{ runs }`
92
-
93
- 详见 [TOOLS.md](TOOLS.md) §9。轮询这一单元的长任务:有在跑的(`running` / `needs-login`)按
94
- `longTaskPollMs` 快档轮,空闲慢档;`workspace` 为 null 或 `mocked` 时不轮。画用 `<LongTaskRow …/>`,
95
- 点用 `longTaskDot(status)`。