@dsh-workbench/core 0.3.10 → 0.3.11
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.
- package/README.md +19 -7
- package/bin/dsh-workbench +2 -2
- package/client.js +1648 -973
- package/cordis.patch.yml +60 -35
- package/dist/client/client.body.js +1648 -973
- package/dist/client/index.js +1333 -658
- package/dist/host/index.js +2896 -2334
- package/dist/types/app/client.d.ts +2 -1
- package/dist/types/app/layouts/env-gate/boot-gate.d.ts +1 -1
- package/dist/types/app/layouts/market/use-module-versions.d.ts +13 -2
- package/dist/types/app/layouts/rightbar/MarketView.d.ts +6 -3
- package/dist/types/app/layouts/rightbar/market-act.d.ts +6 -3
- package/dist/types/app/layouts/rightbar/market-settle-hold.d.ts +2 -0
- package/dist/types/app/layouts/rightbar/market-settle.d.ts +20 -0
- package/dist/types/app/layouts/rightbar/market-update-verdict.d.ts +41 -0
- package/dist/types/app/layouts/rightbar/market-view-text.d.ts +4 -1
- package/dist/types/app/layouts/rightbar/store.d.ts +14 -0
- package/dist/types/app/layouts/sidebar/LongTaskDot.d.ts +12 -0
- package/dist/types/app/layouts/sidebar/LongTaskHover.d.ts +12 -0
- package/dist/types/app/layouts/sidebar/edition-boot.d.ts +24 -0
- package/dist/types/app/layouts/sidebar/edition.d.ts +22 -0
- package/dist/types/app/layouts/workbench-settings/ConfigSyncItem.d.ts +5 -0
- package/dist/types/app/layouts/workbench-settings/SettingsItem.d.ts +17 -0
- package/dist/types/app/layouts/workbench-settings/SidebarEditionItem.d.ts +11 -0
- package/dist/types/app/layouts/workbench-settings/WorkbenchSettingsSection.d.ts +11 -0
- package/dist/types/modules/panels/model/approval.d.ts +12 -4
- package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -1
- package/dist/types/modules/panels/model/diy-context.d.ts +31 -14
- package/dist/types/modules/panels/model/diy-page.d.ts +3 -2
- package/dist/types/modules/panels/model/diy-record.d.ts +3 -3
- package/dist/types/modules/panels/model/diy-report.d.ts +6 -7
- package/dist/types/modules/panels/model/market-dependency-update.d.ts +21 -0
- package/dist/types/modules/panels/model/market-dispatch.d.ts +1 -0
- package/dist/types/modules/panels/model/market-types.d.ts +1 -0
- package/dist/types/modules/panels/model/market-update-bundle.d.ts +25 -0
- package/dist/types/modules/panels/model/market-verify.d.ts +31 -0
- package/dist/types/modules/panels/unit.d.ts +3 -4
- package/dist/types/shared/api/bundle-install.d.ts +13 -0
- package/dist/types/shared/api/host/approval-gate.d.ts +39 -0
- package/dist/types/shared/api/host/bundle-routes.d.ts +2 -1
- package/dist/types/shared/api/host/diy-artifacts.d.ts +14 -0
- package/dist/types/shared/api/host/diy-modules.d.ts +41 -0
- package/dist/types/shared/api/host/host-routes.d.ts +13 -0
- package/dist/types/shared/api/host/permission-mode.d.ts +9 -0
- package/dist/types/shared/api/host/runtime-seed.d.ts +2 -0
- package/dist/types/shared/api/host/self-update.d.ts +9 -2
- package/dist/types/shared/api/host/sidebar-edition.d.ts +46 -0
- package/dist/types/shared/api/host/workspace-root.d.ts +13 -0
- package/dist/types/shared/api/sidebar-edition.d.ts +14 -0
- package/dist/types/shared/api/unit-board.d.ts +20 -0
- package/dist/types/shared/api/write-outcome.d.ts +15 -0
- package/dist/types/shared/components/IconModules.d.ts +8 -0
- package/dist/types/shared/components/blocker-action.d.ts +16 -0
- package/dist/types/shared/hooks/sidebar-edition.d.ts +7 -0
- package/dist/types/shared/hooks/use-capabilities.d.ts +5 -0
- package/dist/types/shared/hooks/use-capability-gate.d.ts +1 -2
- package/dist/types/shared/hooks/use-host-write.d.ts +1 -1
- package/dist/types/shared/hooks/use-unit-board.d.ts +1 -1
- package/docs/HANDOFF.md +3 -3
- package/docs/OPERATIONS.md +16 -5
- package/package.json +3 -3
- package/preset/diy.patch.yml +2 -2
- package/profile/isolation.patch.yml +11 -0
- package/skills/workbench-module-diy/BUILD.md +62 -131
- package/skills/workbench-module-diy/COPY.md +89 -0
- package/skills/workbench-module-diy/DATASOURCE.md +84 -112
- package/skills/workbench-module-diy/DESIGN.md +75 -109
- package/skills/workbench-module-diy/FAMILY.md +70 -54
- package/skills/workbench-module-diy/KIT.md +77 -133
- package/skills/workbench-module-diy/LINKAGE.md +66 -105
- package/skills/workbench-module-diy/PANEL.md +79 -171
- package/skills/workbench-module-diy/REVIEW.md +67 -81
- package/skills/workbench-module-diy/SKILL.md +57 -96
- package/skills/workbench-module-diy/STEPS.md +66 -69
- package/skills/workbench-module-diy/TOOLING.md +34 -51
- package/skills/workbench-module-diy/TOOLS.md +110 -103
- package/skills/workbench-module-diy/WIRING.md +51 -83
- package/skills/workbench-module-diy/WRITES.md +89 -142
- package/dist/types/app/layouts/config-sync/ConfigSyncSection.d.ts +0 -5
- package/dist/types/shared/api/host/diy-workshop.d.ts +0 -23
- package/dist/types/shared/api/host/workshop-root.d.ts +0 -16
- /package/dist/types/shared/{api/host/diy-workshop.test.d.ts → components/blocker-action.test.d.ts} +0 -0
|
@@ -1,138 +1,106 @@
|
|
|
1
|
-
# kit
|
|
1
|
+
# kit:选型、组合、最小写法
|
|
2
2
|
|
|
3
|
-
`check --only kit`
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
`
|
|
23
|
-
|
|
24
|
-
**`CardAnchor`** —— 外壳自己盖的锚点(`#wb-card-<unit>`)。**别自己写**,也别删。
|
|
18
|
+
- `CardHead`:自拼卡头时用;`platformHref` 给一个即出 ↗。
|
|
19
|
+
- `CardAnchor`:外壳锚点 `#wb-card-<unit>`;不写不删。
|
|
20
|
+
- `CardBoundary`:卡片错误边界;不要自己 try/catch 渲染。
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
## 2 状态
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
五态:待机 / 在读 / 就绪 / 就绪但空 / 读不到。
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
| 什么时候 | 用 |
|
|
26
|
+
| 场景 | 用 |
|
|
33
27
|
|---|---|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
| 就绪但空 | `EmptyState
|
|
37
|
-
|
|
|
38
|
-
| 一句说明 /
|
|
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
|
-
|
|
41
|
+
读不到:`detail` 放原始错误码与原文;不显示 0。
|
|
51
42
|
|
|
52
|
-
## 3
|
|
43
|
+
## 3 行与记录
|
|
53
44
|
|
|
54
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
71
|
-
第一处错误怎么带进视口、操作区怎么排全归它。**一张完整的表单就用它**(口径见 §5,逐件看 `.d.ts`)。
|
|
58
|
+
`Form` 管理整张表单:字段、校验时机、错误位置、首错聚焦、操作区。
|
|
72
59
|
|
|
73
60
|
```tsx
|
|
74
61
|
h(Form<{ branch: string }>, {
|
|
75
|
-
fields: [
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
],
|
|
81
|
-
submit: { label: t('myunit.save'), busyLabel: t('myunit.saving') },
|
|
82
|
-
onSubmit: save,
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
|
|
104
|
-
|
|
76
|
+
控件:`Field`(未进 `FormRow` 时包标签与说明)· `TextField`(`invalid`)· `ChoiceGroup`(`multi`)·
|
|
77
|
+
`SelectField`(常规下拉;`variant: 'ghost'` 无边框;非原生 select)· `SaveBar`(取消 / 保存)。
|
|
78
|
+
不手写 `<button>` / `<input>` / `<select>`;`<textarea>` 例外。
|
|
105
79
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
**要不要问归调用点**(`danger` 只标"破坏性")。口径见 ADR-0073。
|
|
80
|
+
`FormActions`:操作区。`primary` 靠右、`danger` 在其左、每区一个 `primary`、窄屏折行;`busy` 带 `Spinner`。
|
|
81
|
+
不写 `gap` / `margin` / 顺序,不借 `.wb-formActions` 槽类(F3 红)。`danger` 仅标破坏性;二次确认归调用点。
|
|
109
82
|
|
|
110
|
-
|
|
83
|
+
`ConfirmBar`:确认条。键值走 `Rows`,错误走 `Note tone="problem"`,动作走 `FormActions`;不重拼这四槽。
|
|
84
|
+
是否询问归调用点。
|
|
111
85
|
|
|
112
|
-
## 4′
|
|
86
|
+
## 4′ hooks
|
|
113
87
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
| 场合 | 用它 |
|
|
88
|
+
| 场景 | 用 |
|
|
117
89
|
|---|---|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
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
|
-
|
|
97
|
+
取数链见 [LINKAGE.md](LINKAGE.md) §8;配置见 [PANEL.md](PANEL.md) §5。
|
|
126
98
|
|
|
127
|
-
## 5
|
|
99
|
+
## 5 表单三层:面 → 行 → 控件
|
|
128
100
|
|
|
129
|
-
|
|
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,40 @@ h(FormRows, null,
|
|
|
140
108
|
h(SelectField, { value, options, onChange: setMode })))
|
|
141
109
|
```
|
|
142
110
|
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
167
|
-
**`Form` / `ConfirmBar` 是新成员**:要用得等外壳**发一版含它的版本**,再把 peer 下限抬到**那一版**
|
|
168
|
-
(版本随发版填,别照抄别的件);`FormRow(s)` / `FormActions` 的下限是另一件事。
|
|
121
|
+
时点:`Form` / `ConfirmBar` 需外壳发版含它们后,把 peer 下限抬到该版。
|
|
169
122
|
|
|
170
|
-
## 6
|
|
123
|
+
## 6 其他
|
|
171
124
|
|
|
172
|
-
-
|
|
173
|
-
|
|
174
|
-
-
|
|
175
|
-
|
|
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.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
3. **自己写 CSS 只写布局**(flex / grid / gap / 对齐)。要一个新的"长得像控件"的东西时,
|
|
188
|
-
先问一句:kit 里是不是已经有了?
|
|
132
|
+
1. 调用点只给数据与语义;外观类名只允许布局槽(通栏、外边距、网格位置)。
|
|
133
|
+
2. 字号与颜色走 kit 与令牌(`--dsw-*` / `--wb-*`)。重点数字用 `--dsw-font-xl-24` + `tabular-nums`;
|
|
134
|
+
刻度外字面值在模块 `README.md` 说明([PANEL.md](PANEL.md) §6)。
|
|
135
|
+
3. 自写 CSS 只写布局(flex / grid / gap / 对齐)。
|
|
189
136
|
|
|
190
|
-
## 8 kit
|
|
137
|
+
## 8 需要 kit 没有的标准控件
|
|
191
138
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
该写进 `package.json` 的那两行;骨架默认已备好。
|
|
195
|
-
**要看某一件**:`--file <名字>`(`--files | grep <名字>`)。
|
|
139
|
+
查:`workbench_diy_components`(DIY 会话)或 `check --only primitives`(`--all` / `--json` / `--file <名>`)。
|
|
140
|
+
写入:
|
|
196
141
|
|
|
197
142
|
```jsonc
|
|
198
143
|
"dsh": { "client": { "external": [ "@dsh-workbench/core/client", "@deepseek-ai/dsh-client-ui-primitives" ] } },
|
|
199
|
-
"devDependencies": { "@deepseek-ai/dsh-client-ui-primitives": "
|
|
144
|
+
"devDependencies": { "@deepseek-ai/dsh-client-ui-primitives": "<与外壳一致的精确版>" }
|
|
200
145
|
```
|
|
201
146
|
|
|
202
|
-
|
|
203
|
-
宿主侧的契约都见 [PANEL.md](PANEL.md) §9。
|
|
147
|
+
改后 `pnpm install`。边界见 [PANEL.md](PANEL.md) §9。
|
|
@@ -1,142 +1,103 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 联动:谁在动、何时变、哪一格变
|
|
2
2
|
|
|
3
|
-
步骤 3
|
|
3
|
+
步骤 3 的参考。实现见 §8。
|
|
4
4
|
|
|
5
|
-
## 1
|
|
5
|
+
## 1 触发规则
|
|
6
6
|
|
|
7
|
-
|
|
|
7
|
+
| 触发 | 面板 |
|
|
8
8
|
|---|---|
|
|
9
|
-
| agent
|
|
10
|
-
| agent
|
|
11
|
-
| agent
|
|
12
|
-
| agent
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
9
|
+
| agent 读面板这一格 | 该格按最窄切片重读;值与对话一致 |
|
|
10
|
+
| agent 读面板外的东西(明细 / 日志 / 子集) | 不变 |
|
|
11
|
+
| agent 只预览 | 不变 |
|
|
12
|
+
| agent 真写 | 相关格变新值 |
|
|
13
|
+
| 用户自按动作 | 该格立即变,原地显示进行中 / 完成 |
|
|
14
|
+
| 用户改偏好 | 保存后按新偏好显示 |
|
|
15
|
+
| 其他窗口 / 会话改动 | 跟随变,无需刷新 |
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
一次说清变哪一级:整板 / 一张卡 / 一行。"刷新一下"不合格。只动该动的格,粒度落到"某投影 + 最窄参数"
|
|
18
|
+
(`cmdb/builds?env=dev`);否则会整卡重读。
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
## 2 进行中
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
- 显示可读的步骤名,不用内部步骤号。
|
|
23
|
+
- 要登录:给登录入口;登录后续跑,不重来。
|
|
24
|
+
- 失败:卡在哪步、一句原因、原始输出(折叠)、一条可照做的路。
|
|
25
|
+
- 不画进度条、不画流程控制台。
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
中途停止可续:用户说"接着做"即从上次继续。
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
- 要**登录**就给他一条去登录的路,登录完这次继续,不用他重来;
|
|
28
|
-
- **失败**给他:卡在哪一步、一句能读的原因、原始输出(折叠起来),以及一条能照做的路;
|
|
29
|
-
- **不画进度条、不画流程控制台**:状态 + 链接 + 原始输出就够。
|
|
29
|
+
## 3 "交给 agent"的内容
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
- 哪一类 + 哪个对象 + 多少(按用户点的那一行,不是全量)。
|
|
32
|
+
- 一句"用哪个能力取明细"(agent 打不开面板的 ↗)。
|
|
33
|
+
- 不塞明细本身。
|
|
34
|
+
- 保持用户点的分类;认不出就报错,不退回全量。
|
|
32
35
|
|
|
33
|
-
##
|
|
36
|
+
## 4 收起模块
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
- 加一句**该用哪个能力往下拿明细**(agent 打不开面板上那个 ↗,明细只能它自己再问一次);
|
|
37
|
-
- **明细本身不要塞进去**(一页几十行按会话付费,而 agent 拿到那句指引自己会去要);
|
|
38
|
-
- 他点的那一类要跟着走(点"圈复杂度"就只给圈复杂度,不能悄悄退回整份);
|
|
39
|
-
- 认不出的那一类要**当场说错**,不许默默退回整份(他要的是这一块,给全部比报错更糟)。
|
|
38
|
+
收起 = 该项目内 agent 也失去该能力。收起时说明;收起后能力清单更新,对话显示"能力有更新";其他项目不受影响。
|
|
40
39
|
|
|
41
|
-
##
|
|
40
|
+
## 5 不陈旧
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
覆盖三种:切项目 → 立刻显示新项目;其他窗口 / 会话改动 → 跟随;程序刚更新 → 不因"我比你还新"而不收更新。
|
|
43
|
+
允许秒级延迟,但要说明;不得停在旧值等手动刷新。
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
- 收起之后 agent 手上的能力清单会变,用户会在对话里看到一句"能力有更新";
|
|
47
|
-
- 别的项目不受影响。
|
|
45
|
+
## 6 联动表(步骤 3 产出)
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
三种情况都要有答案:
|
|
52
|
-
|
|
53
|
-
1. **他切了项目** —— 看到的立刻是新项目的数据;
|
|
54
|
-
2. **别的窗口 / 另一个会话也改了** —— 他这里会跟着变;
|
|
55
|
-
3. **程序刚更新过(开发时最常见)** —— 不会因为"我比你还新"这种判断而永远收不到更新。
|
|
56
|
-
|
|
57
|
-
允许有延迟(几秒),但延迟要说清;**不允许停在旧值上、只有手动点刷新才动**。
|
|
58
|
-
|
|
59
|
-
## 6 联动场景表(步骤 3 的产出)
|
|
60
|
-
|
|
61
|
-
| 谁在动 | 面板什么时候变 | 哪一格变 | 用户看到什么 |
|
|
47
|
+
| 谁在动 | 何时变 | 哪一格 | 看到什么 |
|
|
62
48
|
|---|---|---|---|
|
|
63
|
-
| agent
|
|
64
|
-
| agent
|
|
65
|
-
| agent 预览 | 不变 | — |
|
|
66
|
-
| agent
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
| agent
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- 那一格**最窄的切片**是什么(哪个环境的哪一列 / 哪一档筛选 / 就是整张卡)?
|
|
85
|
-
- 手里那份**是不是就是**那一格的值?
|
|
86
|
-
|
|
87
|
-
第三种答案决定面板怎么动:
|
|
88
|
-
|
|
89
|
-
| 它读的是 | 面板 |
|
|
49
|
+
| agent 读面板这一格 | 读完 | 该格(最窄切片) | 加载态(约半秒)→ 与对话同值 |
|
|
50
|
+
| agent 读面板外 | 不变 | — | 对话内结果 |
|
|
51
|
+
| agent 预览 | 不变 | — | 对话内"将要做什么" |
|
|
52
|
+
| agent 真写 | 完成 | 相关格 | 新值 |
|
|
53
|
+
| 用户自按 | 立即 + 完成再更新 | 该格 | 确认条 → 进行中 → 结果 |
|
|
54
|
+
| 用户改偏好 | 保存后 | 整卡 | 回看板、数据已换 |
|
|
55
|
+
| agent 改面板 / 装卸能力 | 自动 | 增删或顺序,或相关几处 | 界面变化 + "能力有更新" |
|
|
56
|
+
| 长任务运行 | 自动推进度 | 卡的进度行 | 到哪步 / 要登录 / 失败原因 |
|
|
57
|
+
| 其他窗口 / 程序更新 | 自动 | 同"真写" / 整板 | 同"真写" / 不回旧值 |
|
|
58
|
+
| 切项目 / 点刷新 | 立即 | 整板 / 该份 | 新项目 / 转圈后新值 |
|
|
59
|
+
| 模块在项目内被收起 | 自动 | 少一张卡 | "能力有更新" |
|
|
60
|
+
|
|
61
|
+
## 7 读工具对应哪一格
|
|
62
|
+
|
|
63
|
+
每把读工具回答三句,写进能力清单:
|
|
64
|
+
|
|
65
|
+
1. 读的数据是面板哪一格?
|
|
66
|
+
2. 该格最窄切片是什么(哪个环境哪一列 / 哪档筛选 / 整卡)?
|
|
67
|
+
3. 手里这份是否就是该格的值?
|
|
68
|
+
|
|
69
|
+
| 读的范围 | 面板 |
|
|
90
70
|
|---|---|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
95
|
-
三句都写进能力清单(用户看的是"哪一格会跟着动")。
|
|
96
|
-
|
|
97
|
-
→ 实现:本文 §1 与 §7。
|
|
71
|
+
| 该格范围内 | 该格重读;手里就是该格值时可直接交出(不再重打平台) |
|
|
72
|
+
| 面板外(明细 / 日志 / 单条) | 不变 |
|
|
73
|
+
| 该格的子集(只读 TODO、更小 limit) | 不变;不得当该格交出(错值)—— 要一致就读全量档 |
|
|
98
74
|
|
|
99
|
-
## 8
|
|
75
|
+
## 8 宿主侧实现
|
|
100
76
|
|
|
101
|
-
|
|
77
|
+
四个可选字段。不写 `factsOf` 的模块,面板格不会自己新。
|
|
102
78
|
|
|
103
79
|
```ts
|
|
104
80
|
factsOf: (call) => (call.source === REBUILD_TOOL ? [factOfRoute(HEALTH_ROUTE)] : undefined),
|
|
105
81
|
```
|
|
106
82
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
* **读能力也要认领**(§1 与 §7 的口径):agent 只是读了一次,那一格也该重读。
|
|
112
|
-
* 长任务动完也要认领:`longTasks.create({ …, facts: () => [factOfRoute(HEALTH_ROUTE)] })`。
|
|
83
|
+
- `call.source` = 能力名,`call.args` = 本次参数。返回本次涉及的事实:`[]` = 涉及但无具体事实;
|
|
84
|
+
`undefined` = 与面板无关。
|
|
85
|
+
- 事实名用 `factOfRoute('<unit>/<路由>')`(`kit.api.factOfRoute`),不要手拼。读能力也认领;长任务写
|
|
86
|
+
`longTasks.create({ …, facts: () => [factOfRoute(HEALTH_ROUTE)] })`。
|
|
113
87
|
|
|
114
88
|
```tsx
|
|
115
89
|
const rev = useFactRevision(factOfRoute(HEALTH_ROUTE));
|
|
116
90
|
const resource = useApiResource(ROUTE, query, rev, {});
|
|
117
91
|
```
|
|
118
92
|
|
|
119
|
-
|
|
120
|
-
`options.fact ?? factOfRoute(route)`),所以多数情况你什么都不用接;用户自己按的那个动作做完,
|
|
121
|
-
用 `useFactInvalidate()()` 把这条事实标脏。
|
|
93
|
+
`useApiResource` 默认订阅 `factOfRoute(route)`;用户自按动作完成后用 `useFactInvalidate()()` 标脏。
|
|
122
94
|
|
|
123
95
|
```ts
|
|
124
96
|
context: { kinds: ['module'], render: async (ref) => `模块 ${ref.parts[0]}:…` },
|
|
125
97
|
warmReads: () => [['<unit>', '<target>']],
|
|
126
98
|
```
|
|
127
99
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
preflight 里拼成 `opencli <argv…> --site-session persistent -f json` 先跑一遍(把站点登录态与首屏焐热)。
|
|
133
|
-
例:`[['cmdb', 'enterprises']]`(一条 opencli 命令的 argv,还带测试)。
|
|
134
|
-
**本机读盘、browser bridge、直接打接口的读一律不要写这里** —— 写了外壳会去跑一条不存在的命令,
|
|
135
|
-
preflight 只会给个 warn;这些读没有"冷启动"可省,焐不热任何东西。
|
|
136
|
-
* `values`(`deps.values`,`ValueHandoff`):把一次写的结果交给别的单元 —— `values.hand({fact, key}, 值)`,
|
|
137
|
-
另一边 `values.take({fact, key})` 取;`key` 自己起(例:一次发布的结果给联动那一格用)。
|
|
138
|
-
|
|
139
|
-
## 9 范例
|
|
140
|
-
|
|
141
|
-
要一个"同形状、能跑"的样子:[SKILL.md](SKILL.md) 的「找不到一件事的时候」—— 模板
|
|
142
|
-
(`template-diy/` / `template/`)与装机模块的源码都可以直接读。
|
|
100
|
+
- `context`:把 agent 上下文里的引用渲染成人话;`kinds` 列出识别的类型;形状不对就 `throw`。
|
|
101
|
+
- `warmReads`:只给走 opencli 适配器的读。条目是该 opencli 命令的 argv,启动 preflight 先跑一遍。
|
|
102
|
+
本地读盘、browser bridge、直连接口的读不要写。
|
|
103
|
+
- `values`:跨单元传值。`values.hand({fact, key}, 值)` / `values.take({fact, key})`。
|