@dsh-workbench/shared 0.3.4 → 0.3.6

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 (193) hide show
  1. package/dist/api/blocker-table.d.ts +13 -0
  2. package/dist/api/blocker-table.js +13 -0
  3. package/dist/api/blocker.d.ts +6 -0
  4. package/dist/api/context-ref.d.ts +31 -0
  5. package/dist/api/context-ref.js +28 -0
  6. package/dist/api/dsh-client.d.ts +99 -0
  7. package/dist/api/dsh.d.ts +55 -3
  8. package/dist/api/facts.d.ts +42 -0
  9. package/dist/api/facts.js +33 -0
  10. package/dist/api/host-write.d.ts +18 -0
  11. package/dist/api/host-write.js +12 -0
  12. package/dist/api/long-task.d.ts +66 -1
  13. package/dist/api/long-task.js +59 -0
  14. package/dist/api/unit-board.d.ts +25 -0
  15. package/dist/api/unit-board.js +16 -0
  16. package/dist/api/unit-declaration.d.ts +36 -0
  17. package/dist/api/unit-declaration.js +24 -0
  18. package/dist/api/unit.d.ts +32 -0
  19. package/dist/api/unit.js +5 -0
  20. package/dist/api/workbench-client.d.ts +25 -0
  21. package/dist/api/workbench-client.js +13 -0
  22. package/dist/api/workbench-contract.d.ts +12 -0
  23. package/dist/components/Activity.d.ts +33 -0
  24. package/dist/components/Activity.js +24 -0
  25. package/dist/components/ApprovalDetail.d.ts +12 -0
  26. package/dist/components/ApprovalDetail.js +9 -0
  27. package/dist/components/ApprovalPlan.d.ts +14 -0
  28. package/dist/components/ApprovalPlan.js +8 -0
  29. package/dist/components/Card.d.ts +13 -0
  30. package/dist/components/Card.js +10 -0
  31. package/dist/components/CardAnchor.d.ts +14 -0
  32. package/dist/components/CardAnchor.js +11 -0
  33. package/dist/components/CardBoundary.d.ts +20 -0
  34. package/dist/components/CardBoundary.js +14 -0
  35. package/dist/components/CardHead.d.ts +13 -0
  36. package/dist/components/CardHead.js +10 -0
  37. package/dist/components/ChoiceGroup.d.ts +24 -0
  38. package/dist/components/ChoiceGroup.js +12 -0
  39. package/dist/components/Field.d.ts +10 -0
  40. package/dist/components/Field.js +7 -0
  41. package/dist/components/Form.css +6 -0
  42. package/dist/components/Form.d.ts +20 -0
  43. package/dist/components/Form.js +58 -0
  44. package/dist/components/FormActions.d.ts +14 -0
  45. package/dist/components/FormActions.js +5 -0
  46. package/dist/components/FormNodes.d.ts +103 -0
  47. package/dist/components/FormNodes.js +153 -0
  48. package/dist/components/FormRows.css +31 -0
  49. package/dist/components/FormRows.d.ts +20 -0
  50. package/dist/components/FormRows.js +14 -0
  51. package/dist/components/FormTypes.d.ts +59 -0
  52. package/dist/components/FormTypes.js +1 -0
  53. package/dist/components/Link.d.ts +12 -0
  54. package/dist/components/Link.js +9 -0
  55. package/dist/components/LongTaskRow.css +52 -0
  56. package/dist/components/LongTaskRow.d.ts +35 -0
  57. package/dist/components/LongTaskRow.js +45 -4
  58. package/dist/components/Note.d.ts +11 -0
  59. package/dist/components/Note.js +8 -0
  60. package/dist/components/ResourceStates.d.ts +25 -0
  61. package/dist/components/ResourceStates.js +22 -0
  62. package/dist/components/Rows.d.ts +17 -0
  63. package/dist/components/Rows.js +11 -0
  64. package/dist/components/SaveBar.d.ts +12 -0
  65. package/dist/components/SaveBar.js +9 -0
  66. package/dist/components/SelectField.d.ts +26 -0
  67. package/dist/components/SelectField.js +22 -0
  68. package/dist/components/SelectFieldNodes.d.ts +24 -0
  69. package/dist/components/SelectFieldNodes.js +24 -0
  70. package/dist/components/SettingsSection.d.ts +11 -0
  71. package/dist/components/SettingsSection.js +8 -0
  72. package/dist/components/Spinner.d.ts +9 -0
  73. package/dist/components/Spinner.js +6 -0
  74. package/dist/components/Stack.d.ts +8 -0
  75. package/dist/components/Stack.js +5 -0
  76. package/dist/components/StateBlock.d.ts +13 -0
  77. package/dist/components/StateBlock.js +10 -0
  78. package/dist/components/TextField.d.ts +8 -0
  79. package/dist/components/TextField.js +5 -0
  80. package/dist/components/board-module.d.ts +30 -0
  81. package/dist/components/icons.d.ts +38 -0
  82. package/dist/components/icons.js +35 -0
  83. package/dist/config/constants.d.ts +42 -0
  84. package/dist/config/constants.js +42 -0
  85. package/dist/config/storage.d.ts +4 -0
  86. package/dist/config/storage.js +4 -0
  87. package/dist/host/long-task.d.ts +7 -0
  88. package/dist/host/long-task.js +31 -5
  89. package/dist/host/tool-approval.d.ts +1 -0
  90. package/dist/host/unit-face.d.ts +6 -1
  91. package/dist/i18n/en.d.ts +1 -0
  92. package/dist/i18n/en.js +10 -8
  93. package/dist/i18n/index.d.ts +13 -9
  94. package/dist/i18n/index.js +1 -0
  95. package/dist/i18n/merge.d.ts +5 -0
  96. package/dist/i18n/merge.js +2 -0
  97. package/dist/i18n/zh.d.ts +11 -8
  98. package/dist/i18n/zh.js +10 -8
  99. package/dist/index.d.ts +6 -2
  100. package/dist/index.js +6 -2
  101. package/dist/lib/blocker-action.d.ts +22 -0
  102. package/dist/lib/blocker-action.js +13 -0
  103. package/dist/lib/board-fact.d.ts +27 -0
  104. package/dist/lib/board-fact.js +18 -0
  105. package/dist/lib/busy.d.ts +10 -0
  106. package/dist/lib/busy.js +7 -0
  107. package/dist/lib/diy-debug.d.ts +42 -2
  108. package/dist/lib/diy-debug.js +19 -1
  109. package/dist/lib/diy-record.d.ts +41 -0
  110. package/dist/lib/diy-record.js +17 -0
  111. package/dist/lib/form-problem.d.ts +10 -0
  112. package/dist/lib/form-problem.js +10 -0
  113. package/dist/lib/panel-state.d.ts +60 -0
  114. package/dist/lib/panel-state.js +30 -0
  115. package/dist/lib/react.d.ts +39 -0
  116. package/dist/lib/react.js +36 -0
  117. package/dist/lib/text.d.ts +22 -0
  118. package/dist/lib/text.js +22 -0
  119. package/dist/lib/ui.d.ts +50 -0
  120. package/dist/lib/ui.js +41 -0
  121. package/package.json +1 -1
  122. package/src/api/__tests__/long-task.test.ts +41 -1
  123. package/src/api/blocker-table.ts +13 -0
  124. package/src/api/blocker.ts +6 -0
  125. package/src/api/context-ref.ts +31 -0
  126. package/src/api/dsh-client.ts +99 -0
  127. package/src/api/dsh.ts +56 -1
  128. package/src/api/facts.ts +42 -0
  129. package/src/api/host-write.ts +18 -0
  130. package/src/api/long-task.ts +79 -1
  131. package/src/api/unit-board.ts +25 -0
  132. package/src/api/unit-declaration.ts +36 -0
  133. package/src/api/unit.ts +32 -0
  134. package/src/api/workbench-client.ts +25 -0
  135. package/src/api/workbench-contract.ts +12 -0
  136. package/src/components/Activity.tsx +33 -0
  137. package/src/components/ApprovalDetail.tsx +12 -0
  138. package/src/components/ApprovalPlan.tsx +14 -0
  139. package/src/components/Card.tsx +13 -0
  140. package/src/components/CardAnchor.tsx +14 -0
  141. package/src/components/CardBoundary.tsx +20 -0
  142. package/src/components/CardHead.tsx +13 -0
  143. package/src/components/ChoiceGroup.tsx +24 -0
  144. package/src/components/Field.tsx +10 -0
  145. package/src/components/Form.css +6 -0
  146. package/src/components/Form.test.tsx +475 -0
  147. package/src/components/Form.tsx +78 -0
  148. package/src/components/FormActions.tsx +14 -0
  149. package/src/components/FormNodes.tsx +193 -0
  150. package/src/components/FormRows.css +31 -0
  151. package/src/components/FormRows.test.tsx +71 -1
  152. package/src/components/FormRows.tsx +20 -0
  153. package/src/components/FormTypes.ts +65 -0
  154. package/src/components/FormWiring.test.tsx +88 -0
  155. package/src/components/Link.tsx +12 -0
  156. package/src/components/LongTaskRow.css +52 -0
  157. package/src/components/LongTaskRow.tsx +76 -2
  158. package/src/components/Note.tsx +11 -0
  159. package/src/components/ResourceStates.tsx +25 -0
  160. package/src/components/Rows.tsx +17 -0
  161. package/src/components/SaveBar.tsx +12 -0
  162. package/src/components/SelectField.tsx +26 -0
  163. package/src/components/SelectFieldNodes.tsx +24 -0
  164. package/src/components/SettingsSection.tsx +11 -0
  165. package/src/components/Spinner.tsx +9 -0
  166. package/src/components/Stack.tsx +8 -0
  167. package/src/components/StateBlock.tsx +13 -0
  168. package/src/components/TextField.tsx +8 -0
  169. package/src/components/__tests__/long-task-row.test.ts +115 -0
  170. package/src/components/board-module.ts +30 -0
  171. package/src/components/icons.tsx +38 -0
  172. package/src/config/constants.ts +43 -0
  173. package/src/config/storage.ts +4 -0
  174. package/src/host/__tests__/long-task.test.ts +264 -0
  175. package/src/host/long-task.ts +43 -7
  176. package/src/host/tool-approval.ts +1 -0
  177. package/src/host/unit-face.ts +7 -1
  178. package/src/i18n/en.ts +11 -8
  179. package/src/i18n/index.ts +3 -0
  180. package/src/i18n/merge.ts +5 -0
  181. package/src/i18n/zh.ts +11 -8
  182. package/src/index.ts +6 -2
  183. package/src/lib/blocker-action.ts +22 -0
  184. package/src/lib/board-fact.ts +27 -0
  185. package/src/lib/busy.ts +10 -0
  186. package/src/lib/diy-debug.test.ts +2 -1
  187. package/src/lib/diy-debug.ts +44 -3
  188. package/src/lib/diy-record.ts +41 -0
  189. package/src/lib/form-problem.ts +10 -0
  190. package/src/lib/panel-state.ts +60 -0
  191. package/src/lib/react.ts +39 -0
  192. package/src/lib/text.ts +22 -0
  193. package/src/lib/ui.ts +50 -0
@@ -1,17 +1,40 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import './Activity.css';
3
+ /**
4
+ * `ActivityBlock` 的契约:块头与块内容。
5
+ */
3
6
  export interface ActivityBlockProps {
4
7
  readonly head?: ReactNode | undefined;
5
8
  readonly children?: ReactNode | undefined;
6
9
  }
10
+ /**
11
+ * "它做过什么"那一块:上面一行块头、下面一列活动行,卡里放时间线就用它。
12
+ * @param props.head - 块头(如"最近 10 次");不给不画那一行。
13
+ * @param props.children - 块内容,通常是一串 `ActivityLine`。
14
+ * @returns 一个 div。
15
+ */
7
16
  export declare function ActivityBlock({ head, children }: ActivityBlockProps): JSX.Element;
17
+ /**
18
+ * `ActivityLine` 的契约:状态点、正文、时间与行尾动作。
19
+ */
8
20
  export interface ActivityLineProps {
9
21
  readonly dot?: ReactNode | undefined;
10
22
  readonly text: ReactNode;
11
23
  readonly at?: ReactNode | undefined;
12
24
  readonly actions?: ReactNode | undefined;
13
25
  }
26
+ /**
27
+ * 一条活动:状态点 + 正文 + 时间,行尾可挂动作;时间一律用它,别自己拼。
28
+ * @param props.dot - 行首那枚状态点;不给就只留正文。
29
+ * @param props.text - 这一条干了什么。
30
+ * @param props.at - 时间那一格(相对时间文本);不给不画。
31
+ * @param props.actions - 行尾动作,通常是一两颗 `ActivityAction`;不给不画。
32
+ * @returns 一行。
33
+ */
14
34
  export declare function ActivityLine({ dot, text, at, actions }: ActivityLineProps): JSX.Element;
35
+ /**
36
+ * `ActivityAction` 的契约:一颗图标钮、一个外链,或一个失败态。
37
+ */
15
38
  export interface ActivityActionProps {
16
39
  readonly label: string;
17
40
  readonly hint?: string | undefined;
@@ -20,4 +43,14 @@ export interface ActivityActionProps {
20
43
  readonly onClick?: (() => void) | undefined;
21
44
  readonly failed?: boolean | undefined;
22
45
  }
46
+ /**
47
+ * 活动行尾那颗动作:给 `href` 就是外链,否则是图标钮并带上 tooltip。
48
+ * @param props.label - 读屏名,也是 `hint` 不给时的 tooltip 文案。
49
+ * @param props.hint - tooltip 上那句更细的说明;不给退化成 `label`。
50
+ * @param props.icon - 那颗图标。
51
+ * @param props.href - 给了就渲染成新窗口外链;与 `onClick` 同时给时以它为准。
52
+ * @param props.onClick - 图标钮的点击回调。
53
+ * @param props.failed - 上一次动作失败了:钮换成危险色(默认 false)。
54
+ * @returns 一个 `<a>` 或一颗 `Button`。
55
+ */
23
56
  export declare function ActivityAction({ label, hint, icon, href, onClick, failed }: ActivityActionProps): JSX.Element;
@@ -1,11 +1,25 @@
1
1
  import { Button, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
2
2
  import { h } from '../lib/react.js';
3
3
  import './Activity.css';
4
+ /**
5
+ * "它做过什么"那一块:上面一行块头、下面一列活动行,卡里放时间线就用它。
6
+ * @param props.head - 块头(如"最近 10 次");不给不画那一行。
7
+ * @param props.children - 块内容,通常是一串 `ActivityLine`。
8
+ * @returns 一个 div。
9
+ */
4
10
  export function ActivityBlock({ head, children }) {
5
11
  return (h("div", { className: "wb-activityBlock" },
6
12
  head === undefined ? null : h("div", { className: "wb-activityBlockHead" }, head),
7
13
  children));
8
14
  }
15
+ /**
16
+ * 一条活动:状态点 + 正文 + 时间,行尾可挂动作;时间一律用它,别自己拼。
17
+ * @param props.dot - 行首那枚状态点;不给就只留正文。
18
+ * @param props.text - 这一条干了什么。
19
+ * @param props.at - 时间那一格(相对时间文本);不给不画。
20
+ * @param props.actions - 行尾动作,通常是一两颗 `ActivityAction`;不给不画。
21
+ * @returns 一行。
22
+ */
9
23
  export function ActivityLine({ dot, text, at, actions }) {
10
24
  return (h("div", { className: "wb-activityLine" },
11
25
  dot,
@@ -13,6 +27,16 @@ export function ActivityLine({ dot, text, at, actions }) {
13
27
  at === undefined ? null : h("span", { className: "wb-activityLineAt" }, at),
14
28
  actions === undefined ? null : h("span", { className: "wb-activityLineActions" }, actions)));
15
29
  }
30
+ /**
31
+ * 活动行尾那颗动作:给 `href` 就是外链,否则是图标钮并带上 tooltip。
32
+ * @param props.label - 读屏名,也是 `hint` 不给时的 tooltip 文案。
33
+ * @param props.hint - tooltip 上那句更细的说明;不给退化成 `label`。
34
+ * @param props.icon - 那颗图标。
35
+ * @param props.href - 给了就渲染成新窗口外链;与 `onClick` 同时给时以它为准。
36
+ * @param props.onClick - 图标钮的点击回调。
37
+ * @param props.failed - 上一次动作失败了:钮换成危险色(默认 false)。
38
+ * @returns 一个 `<a>` 或一颗 `Button`。
39
+ */
16
40
  export function ActivityAction({ label, hint, icon, href, onClick, failed }) {
17
41
  const tip = hint ?? label;
18
42
  if (href !== undefined) {
@@ -19,6 +19,9 @@ interface ChatSnapshot {
19
19
  };
20
20
  }
21
21
  type UseChat = <T>(selector: (snapshot: ChatSnapshot) => T) => T | undefined;
22
+ /**
23
+ * `ApprovalDetail` 的契约:调用 id、读会话快照的钩子、已装模块与兜底渲染。
24
+ */
22
25
  export interface ApprovalDetailProps {
23
26
  readonly callId?: string;
24
27
  readonly useChat?: UseChat;
@@ -26,5 +29,14 @@ export interface ApprovalDetailProps {
26
29
  readonly t?: Translate;
27
30
  readonly renderShell?: ((input: ApprovalInput, t: Translate) => ReactNode) | undefined;
28
31
  }
32
+ /**
33
+ * 审批屏的详情格:按工具名找到认领它的模块交给它渲染,没有模块认领就退回外壳那张表或一段原始命令。
34
+ * @param props.callId - 这次工具调用的 id;不给就直接走兜底(连调用都找不到)。
35
+ * @param props.useChat - 读会话快照的钩子(dsh 注入);不给就当作读不到调用。
36
+ * @param props.units - 已装模块的面,按各自的 `approval.tools` 认领这次调用。
37
+ * @param props.t - 翻译函数;不给就用返回 key 的直通实现。
38
+ * @param props.renderShell - 没有模块认领时外壳自己的渲染(那张表);不给且这次调用没带 `command` 时整格返回 `null`。
39
+ * @returns 认领方的渲染、外壳那张表,或一段可复制的命令。
40
+ */
29
41
  export declare function ApprovalDetail(props: ApprovalDetailProps): JSX.Element | null;
30
42
  export {};
@@ -22,6 +22,15 @@ function parseArgs(raw) {
22
22
  return {};
23
23
  }
24
24
  }
25
+ /**
26
+ * 审批屏的详情格:按工具名找到认领它的模块交给它渲染,没有模块认领就退回外壳那张表或一段原始命令。
27
+ * @param props.callId - 这次工具调用的 id;不给就直接走兜底(连调用都找不到)。
28
+ * @param props.useChat - 读会话快照的钩子(dsh 注入);不给就当作读不到调用。
29
+ * @param props.units - 已装模块的面,按各自的 `approval.tools` 认领这次调用。
30
+ * @param props.t - 翻译函数;不给就用返回 key 的直通实现。
31
+ * @param props.renderShell - 没有模块认领时外壳自己的渲染(那张表);不给且这次调用没带 `command` 时整格返回 `null`。
32
+ * @returns 认领方的渲染、外壳那张表,或一段可复制的命令。
33
+ */
25
34
  export function ApprovalDetail(props) {
26
35
  const useChat = props.useChat ?? (() => undefined);
27
36
  const call = useChat((snapshot) => callOf(snapshot, props.callId));
@@ -1,13 +1,27 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import './ApprovalPlan.css';
3
+ /**
4
+ * 审批详情里的一行:左边标签、右边值。
5
+ */
3
6
  export interface ApprovalPlanRow {
4
7
  readonly label: ReactNode;
5
8
  readonly value: ReactNode;
6
9
  }
10
+ /**
11
+ * `ApprovalPlan` 的契约:详情行,外加附注与警告两格。
12
+ */
7
13
  export interface ApprovalPlanProps {
8
14
  readonly rows: readonly ApprovalPlanRow[];
9
15
  readonly hint?: ReactNode | undefined;
10
16
  readonly warn?: ReactNode | undefined;
11
17
  readonly children?: ReactNode | undefined;
12
18
  }
19
+ /**
20
+ * 审批屏那张"这次要做什么"的表:一行一项,上面可给一句附注、下面可给一句警告。
21
+ * @param props.rows - 详情行,按给的顺序渲染;空数组时只剩附注与警告。
22
+ * @param props.hint - 表上面那句说明;不给不画。
23
+ * @param props.warn - 表下面那句警告;不给不画。
24
+ * @param props.children - 插在表与 `warn` 之间的补充内容。
25
+ * @returns 一个 div。
26
+ */
13
27
  export declare function ApprovalPlan({ rows, hint, warn, children }: ApprovalPlanProps): JSX.Element;
@@ -1,5 +1,13 @@
1
1
  import { h } from '../lib/react.js';
2
2
  import './ApprovalPlan.css';
3
+ /**
4
+ * 审批屏那张"这次要做什么"的表:一行一项,上面可给一句附注、下面可给一句警告。
5
+ * @param props.rows - 详情行,按给的顺序渲染;空数组时只剩附注与警告。
6
+ * @param props.hint - 表上面那句说明;不给不画。
7
+ * @param props.warn - 表下面那句警告;不给不画。
8
+ * @param props.children - 插在表与 `warn` 之间的补充内容。
9
+ * @returns 一个 div。
10
+ */
3
11
  export function ApprovalPlan({ rows, hint, warn, children }) {
4
12
  return (h("div", { className: "wb-approvalPlan" },
5
13
  hint === undefined ? null : h("p", { className: "wb-approvalHint" }, hint),
@@ -1,6 +1,9 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import type { Translate } from '../api/dsh-client.js';
3
3
  import './Card.css';
4
+ /**
5
+ * `Card` 的契约:标题、刷新、卡片体,以及可整块替换的卡头。
6
+ */
4
7
  export interface CardProps {
5
8
  readonly t: Translate;
6
9
  readonly title: string;
@@ -10,4 +13,14 @@ export interface CardProps {
10
13
  readonly head?: ReactNode | undefined;
11
14
  readonly children?: ReactNode | undefined;
12
15
  }
16
+ /**
17
+ * 一张卡的壳:卡头(标题 + 刷新钮)+ 卡片体,面板上每一格都是它。
18
+ * @param props.t - 卡头那两句文案(刷新 / 刷新中)的翻译函数。
19
+ * @param props.refreshing - 正在刷新:刷新钮随之禁用并转圈;接 `resource.phase === 'loading'`。
20
+ * @param props.onRefresh - 点刷新钮做什么,异步由调用点自己管。
21
+ * @param props.meta - 标题右侧那一小段补充(条数、时间);不给不占位。
22
+ * @param props.head - 整块替换卡头时给(如自带齿轮的 `CardHead`);不给用默认卡头。
23
+ * @param props.children - 卡片体。
24
+ * @returns 一个 `<section>`。
25
+ */
13
26
  export declare function Card({ t, title, refreshing, onRefresh, meta, head, children }: CardProps): JSX.Element;
@@ -1,6 +1,16 @@
1
1
  import { h } from '../lib/react.js';
2
2
  import { CardHead } from './CardHead.js';
3
3
  import './Card.css';
4
+ /**
5
+ * 一张卡的壳:卡头(标题 + 刷新钮)+ 卡片体,面板上每一格都是它。
6
+ * @param props.t - 卡头那两句文案(刷新 / 刷新中)的翻译函数。
7
+ * @param props.refreshing - 正在刷新:刷新钮随之禁用并转圈;接 `resource.phase === 'loading'`。
8
+ * @param props.onRefresh - 点刷新钮做什么,异步由调用点自己管。
9
+ * @param props.meta - 标题右侧那一小段补充(条数、时间);不给不占位。
10
+ * @param props.head - 整块替换卡头时给(如自带齿轮的 `CardHead`);不给用默认卡头。
11
+ * @param props.children - 卡片体。
12
+ * @returns 一个 `<section>`。
13
+ */
4
14
  export function Card({ t, title, refreshing, onRefresh, meta, head, children }) {
5
15
  return (h("section", { className: "wb-card" },
6
16
  head === undefined ? (h(CardHead, { t: t, title: title, refreshing: refreshing, onRefresh: onRefresh, meta: meta })) : (head),
@@ -1,7 +1,21 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
+ /**
3
+ * 由 unit id 拼出卡片锚点 id,外壳的截图、滚动与页面探针都按它找卡。
4
+ * @param unit - unit id;`null` 或空串(没有 unit 的卡)时返回 undefined,不生成锚点。
5
+ * @returns `wb-card-<unit>`,或 undefined。
6
+ */
2
7
  export declare function cardIdOf(unit: string | null): string | undefined;
8
+ /**
9
+ * `CardAnchor` 的契约:unit id 与它锚住的内容。
10
+ */
3
11
  export interface CardAnchorProps {
4
12
  readonly unit: string;
5
13
  readonly children?: ReactNode | undefined;
6
14
  }
15
+ /**
16
+ * 外壳自己盖的卡片锚点:套在卡外面给出 `id="wb-card-<unit>"`;模块别自己写,也别删。
17
+ * @param props.unit - 卡的 unit id,用来算锚点 id。
18
+ * @param props.children - 被锚住的卡片本体;锚点自己 `display: contents`,不占布局。
19
+ * @returns 带 id 的占位 div。
20
+ */
7
21
  export declare function CardAnchor({ unit, children }: CardAnchorProps): JSX.Element;
@@ -1,8 +1,19 @@
1
1
  import { h } from '../lib/react.js';
2
2
  import styles from './CardAnchor.module.css';
3
+ /**
4
+ * 由 unit id 拼出卡片锚点 id,外壳的截图、滚动与页面探针都按它找卡。
5
+ * @param unit - unit id;`null` 或空串(没有 unit 的卡)时返回 undefined,不生成锚点。
6
+ * @returns `wb-card-<unit>`,或 undefined。
7
+ */
3
8
  export function cardIdOf(unit) {
4
9
  return unit === null || unit === '' ? undefined : `wb-card-${unit}`;
5
10
  }
11
+ /**
12
+ * 外壳自己盖的卡片锚点:套在卡外面给出 `id="wb-card-<unit>"`;模块别自己写,也别删。
13
+ * @param props.unit - 卡的 unit id,用来算锚点 id。
14
+ * @param props.children - 被锚住的卡片本体;锚点自己 `display: contents`,不占布局。
15
+ * @returns 带 id 的占位 div。
16
+ */
6
17
  export function CardAnchor({ unit, children }) {
7
18
  return (h("div", { id: cardIdOf(unit), className: styles.anchor }, children));
8
19
  }
@@ -1,6 +1,9 @@
1
1
  import type { Translate } from '../api/dsh-client.js';
2
2
  import type { ReactNode } from '../lib/react.js';
3
3
  import { Component } from '../lib/react.js';
4
+ /**
5
+ * `CardBoundary` 的契约:被守的 unit、重来键与失败回调。
6
+ */
4
7
  export interface CardBoundaryProps {
5
8
  readonly unit: string;
6
9
  readonly resetKey: string;
@@ -8,10 +11,27 @@ export interface CardBoundaryProps {
8
11
  readonly onFailure?: ((reason: string) => void) | undefined;
9
12
  readonly children?: ReactNode | undefined;
10
13
  }
14
+ /**
15
+ * `CardBoundary` 的状态:这次渲染炸出来的那句话,没炸是 `null`。
16
+ */
11
17
  export interface CardBoundaryState {
12
18
  readonly reason: string | null;
13
19
  }
20
+ /**
21
+ * 把抛出来的任何东西收成一句人能看的失败原因,上报与展示都用它。
22
+ * @param error - 抛出来的值:`Error` 取 `message`,字符串原样,其余走 JSON。
23
+ * @returns 一句原因文本;`Error('')` 这种没带 message 的会回空串(调用点别把它当"一定有话")。
24
+ */
14
25
  export declare function errorReasonOf(error: unknown): string;
26
+ /**
27
+ * 卡片级错误边界:一张卡渲染炸了只画一条报错 + 重试,不让整块面板跟着塌。
28
+ * @param props.unit - 出事的 unit id,写进报错文案并作为上报标识。
29
+ * @param props.resetKey - 重来键:它一变(如切了工作区)就自动清掉报错,把卡片重新渲染一遍。
30
+ * @param props.t - 报错那句与重试钮文案的翻译函数。
31
+ * @param props.onFailure - 失败时把原因报给宿主(面板状态要用);不给就只打一条 console。
32
+ * @param props.children - 正常时要渲染的卡片内容。
33
+ * @returns 正常时是 `children`,炸过且还没重来之前是一条报错。
34
+ */
15
35
  export declare class CardBoundary extends Component<CardBoundaryProps, CardBoundaryState> {
16
36
  state: CardBoundaryState;
17
37
  static getDerivedStateFromError(error: unknown): CardBoundaryState;
@@ -1,11 +1,25 @@
1
1
  import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
2
2
  import { Component, h } from '../lib/react.js';
3
3
  import { Note } from './Note.js';
4
+ /**
5
+ * 把抛出来的任何东西收成一句人能看的失败原因,上报与展示都用它。
6
+ * @param error - 抛出来的值:`Error` 取 `message`,字符串原样,其余走 JSON。
7
+ * @returns 一句原因文本;`Error('')` 这种没带 message 的会回空串(调用点别把它当"一定有话")。
8
+ */
4
9
  export function errorReasonOf(error) {
5
10
  if (error instanceof Error)
6
11
  return error.message;
7
12
  return typeof error === 'string' ? error : JSON.stringify(error) ?? String(error);
8
13
  }
14
+ /**
15
+ * 卡片级错误边界:一张卡渲染炸了只画一条报错 + 重试,不让整块面板跟着塌。
16
+ * @param props.unit - 出事的 unit id,写进报错文案并作为上报标识。
17
+ * @param props.resetKey - 重来键:它一变(如切了工作区)就自动清掉报错,把卡片重新渲染一遍。
18
+ * @param props.t - 报错那句与重试钮文案的翻译函数。
19
+ * @param props.onFailure - 失败时把原因报给宿主(面板状态要用);不给就只打一条 console。
20
+ * @param props.children - 正常时要渲染的卡片内容。
21
+ * @returns 正常时是 `children`,炸过且还没重来之前是一条报错。
22
+ */
9
23
  export class CardBoundary extends Component {
10
24
  state = { reason: null };
11
25
  static getDerivedStateFromError(error) {
@@ -1,6 +1,9 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import type { Translate } from '../api/dsh-client.js';
3
3
  import './CardHead.css';
4
+ /**
5
+ * `CardHead` 的契约:标题、补充、刷新,外加齿轮与平台跳转两格。
6
+ */
4
7
  export interface CardHeadProps {
5
8
  readonly t: Translate;
6
9
  readonly title: string;
@@ -14,4 +17,14 @@ export interface CardHeadProps {
14
17
  readonly platform?: string | undefined;
15
18
  readonly platformTitle?: string | undefined;
16
19
  }
20
+ /**
21
+ * 卡头:标题 + 补充 + 刷新钮(`Card` 内部就是它),要自己拼卡头或加齿轮 / 平台跳转时直接用。
22
+ * @param props.t - 刷新那两句文案(刷新 / 刷新中)的翻译函数。
23
+ * @param props.title - 卡标题;`props.meta` 是它右侧那一小段补充,不给不占位。
24
+ * @param props.refreshing - 正在刷新:刷新钮禁用并转圈。
25
+ * @param props.onRefresh - 点刷新钮做什么。
26
+ * @param props.gear - 齿轮钮的读屏名;`gearOpen` 控制它高亮、`onToggleGear` 是点击回调,三者一起给才画。
27
+ * @param props.platformHref - 整张卡对应系统的地址:给了就画那颗 ↗(新窗口),tooltip 用 `platformTitle`,缺了退化用 `platform`。
28
+ * @returns 一个 `<header>`。
29
+ */
17
30
  export declare function CardHead({ t, title, meta, refreshing, onRefresh, gear, gearOpen, onToggleGear, platformHref, platform, platformTitle, }: CardHeadProps): JSX.Element;
@@ -1,6 +1,16 @@
1
1
  import { Button, IconRefreshOutlineRegular, IconRightUpOutlineRegular, IconSettingsOutlineRegular, Tooltip, } from '@deepseek-ai/dsh-client-ui-primitives';
2
2
  import { h } from '../lib/react.js';
3
3
  import './CardHead.css';
4
+ /**
5
+ * 卡头:标题 + 补充 + 刷新钮(`Card` 内部就是它),要自己拼卡头或加齿轮 / 平台跳转时直接用。
6
+ * @param props.t - 刷新那两句文案(刷新 / 刷新中)的翻译函数。
7
+ * @param props.title - 卡标题;`props.meta` 是它右侧那一小段补充,不给不占位。
8
+ * @param props.refreshing - 正在刷新:刷新钮禁用并转圈。
9
+ * @param props.onRefresh - 点刷新钮做什么。
10
+ * @param props.gear - 齿轮钮的读屏名;`gearOpen` 控制它高亮、`onToggleGear` 是点击回调,三者一起给才画。
11
+ * @param props.platformHref - 整张卡对应系统的地址:给了就画那颗 ↗(新窗口),tooltip 用 `platformTitle`,缺了退化用 `platform`。
12
+ * @returns 一个 `<header>`。
13
+ */
4
14
  export function CardHead({ t, title, meta, refreshing, onRefresh, gear, gearOpen, onToggleGear, platformHref, platform, platformTitle, }) {
5
15
  const refreshLabel = refreshing ? t('rightbar.refreshing') : t('rightbar.refresh');
6
16
  const platformLabel = platformTitle ?? platform ?? t('rightbar.openPlatform');
@@ -1,5 +1,8 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import './ChoiceGroup.css';
3
+ /**
4
+ * 一个候选项:值、人看的标签,可带一行说明。
5
+ */
3
6
  export interface ChoiceGroupOption {
4
7
  readonly value: string;
5
8
  readonly label: ReactNode;
@@ -15,16 +18,37 @@ interface ChoiceGroupCommon {
15
18
  readonly loadingText?: ReactNode | undefined;
16
19
  readonly emptyText?: ReactNode | undefined;
17
20
  }
21
+ /**
22
+ * 单选那一支的契约:`value` + `onChange(next)`;`multi` 不给或给 false。
23
+ */
18
24
  export interface ChoiceGroupSingleProps extends ChoiceGroupCommon {
19
25
  readonly multi?: false | undefined;
20
26
  readonly value: string;
21
27
  readonly onChange: (next: string) => void;
22
28
  }
29
+ /**
30
+ * 多选那一支的契约:`multi: true` + `values` + `onChange(next[])`。
31
+ */
23
32
  export interface ChoiceGroupMultiProps extends ChoiceGroupCommon {
24
33
  readonly multi: true;
25
34
  readonly values: readonly string[];
26
35
  readonly onChange: (next: readonly string[]) => void;
27
36
  }
37
+ /**
38
+ * `ChoiceGroup` 的契约:单选与多选两支的联合。
39
+ */
28
40
  export type ChoiceGroupProps = ChoiceGroupSingleProps | ChoiceGroupMultiProps;
41
+ /**
42
+ * 一组单选或多选:候选少、要一眼看全时用它;候选多或要搜索就用 `SelectField`。
43
+ * @param props.multi - true 走多选那支(`values` / `onChange(string[])`);默认单选(`value` / `onChange(string)`)。
44
+ * @param props.label - 只给读屏的组名(`aria-label`)。
45
+ * @param props.name - 这一组 `input` 的共用 name。
46
+ * @param props.options - 候选项,按给的顺序排;空数组时画 `emptyText`(没给就什么都不画)。
47
+ * @param props.disabled - 禁掉整组(默认 false),不是单颗。
48
+ * @param props.variant - `boxed`(默认,带边框的选项格)或 `ghost`(无边框,跟着父容器走)。
49
+ * @param props.loading - 正在读候选:整组换成转圈,文案取 `loadingText`(不给是 `…`);默认 false。
50
+ * @param props.emptyText - 读到了但一个候选都没有时那句话;不给不画。
51
+ * @returns 一组 `radio` / `checkbox` 选项。
52
+ */
29
53
  export declare function ChoiceGroup(props: ChoiceGroupProps): JSX.Element;
30
54
  export {};
@@ -17,6 +17,18 @@ function ChoiceMenu(input) {
17
17
  }));
18
18
  return (h("div", { className: "wb-choiceMenu", "data-variant": input.variant, role: input.multi ? 'group' : 'radiogroup', "aria-label": input.label, "aria-busy": input.loading ? true : undefined }, body));
19
19
  }
20
+ /**
21
+ * 一组单选或多选:候选少、要一眼看全时用它;候选多或要搜索就用 `SelectField`。
22
+ * @param props.multi - true 走多选那支(`values` / `onChange(string[])`);默认单选(`value` / `onChange(string)`)。
23
+ * @param props.label - 只给读屏的组名(`aria-label`)。
24
+ * @param props.name - 这一组 `input` 的共用 name。
25
+ * @param props.options - 候选项,按给的顺序排;空数组时画 `emptyText`(没给就什么都不画)。
26
+ * @param props.disabled - 禁掉整组(默认 false),不是单颗。
27
+ * @param props.variant - `boxed`(默认,带边框的选项格)或 `ghost`(无边框,跟着父容器走)。
28
+ * @param props.loading - 正在读候选:整组换成转圈,文案取 `loadingText`(不给是 `…`);默认 false。
29
+ * @param props.emptyText - 读到了但一个候选都没有时那句话;不给不画。
30
+ * @returns 一组 `radio` / `checkbox` 选项。
31
+ */
20
32
  export function ChoiceGroup(props) {
21
33
  const common = {
22
34
  label: props.label,
@@ -1,8 +1,18 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import './Field.css';
3
+ /**
4
+ * `Field` 的契约:一行"标签 + 控件 + 说明"的排布。
5
+ */
3
6
  export interface FieldProps {
4
7
  readonly label: ReactNode;
5
8
  readonly hint?: ReactNode | undefined;
6
9
  readonly children?: ReactNode | undefined;
7
10
  }
11
+ /**
12
+ * 标签 + 控件的排布:没进 `FormRow` 时用它兜住一个字段的标签与说明。
13
+ * @param props.label - 左侧标签,必给。
14
+ * @param props.hint - 控件下面那行说明;不给不画。
15
+ * @param props.children - 控件本体,落在标签与说明之间。
16
+ * @returns 一个 `<label>`,点标签会聚焦里面的控件。
17
+ */
8
18
  export declare function Field({ label, hint, children }: FieldProps): JSX.Element;
@@ -1,5 +1,12 @@
1
1
  import { h } from '../lib/react.js';
2
2
  import './Field.css';
3
+ /**
4
+ * 标签 + 控件的排布:没进 `FormRow` 时用它兜住一个字段的标签与说明。
5
+ * @param props.label - 左侧标签,必给。
6
+ * @param props.hint - 控件下面那行说明;不给不画。
7
+ * @param props.children - 控件本体,落在标签与说明之间。
8
+ * @returns 一个 `<label>`,点标签会聚焦里面的控件。
9
+ */
3
10
  export function Field({ label, hint, children }) {
4
11
  return (h("label", { className: "wb-field" },
5
12
  h("span", { className: "wb-fieldLabel" }, label),
@@ -0,0 +1,6 @@
1
+ .wb-form {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--wb-len-12);
5
+ width: 100%;
6
+ }
@@ -0,0 +1,20 @@
1
+ import type { FormProps } from './FormTypes.js';
2
+ import './Form.css';
3
+ /**
4
+ * 整张表单:`fields` 进,行、错误、操作区、(被挡住的)提交都由它安排 —— 它自己一个值都不存,
5
+ * 值仍归调用点的 model(`value` 只读给校验看)。
6
+ * 还没提交过时一格错都不显示;第一次提交之后每次渲染重算,改好了当场消。
7
+ * 提交那一拍永远先算一遍:有错就不调 `onSubmit`,并把第一个有错的 `FormRow` 带进视口、聚焦它里面的控件。
8
+ * 回车与点提交钮走的是同一条路:真 `<form>` 的隐式提交在**只有一个文本框**时也会 fire,所以 `busy` / `disabled` 时回车同样不提交。
9
+ * 调用点只管数据与语义:行怎么排、错误摆哪、什么时候校验、按钮在哪,这里没有口子。
10
+ * @param props.fields - 每格的键 / 标签 / 值 / 校验 / 控件;`visible: false` 的那格当它不存在(不渲染、不校验、不进 `values`)。
11
+ * @param props.onSubmit - 校验全过的那一拍,回的是从 `fields` 现取的键值表;被挡住时不会被调。
12
+ * @param props.submit - 提交钮文案;`busyLabel` 是提交中换的那句,不给就沿用 `label`。
13
+ * @param props.cancel - 取消钮;给了就排在提交钮左边,`busy` 时一起禁用。
14
+ * @param props.extra - 其余动作(default / danger),排在最左,`busy` 时一起禁用。
15
+ * @param props.busy - 提交中(默认 false):整表控件的绑定 `disabled` 为真,所有动作一起禁用,回车也不提交。
16
+ * @param props.disabled - 只再禁提交钮(如没改动可存),各格控件与其余动作不受影响,回车也不提交。
17
+ * @param props.problem - 整表级那句错(提交失败),落在行与操作区之间;不给就不画。
18
+ * @returns 一个 `<form>`(`noValidate`:报错形状归 `FormRow`,不走浏览器的气泡)。
19
+ */
20
+ export declare function Form(props: FormProps): JSX.Element;
@@ -0,0 +1,58 @@
1
+ import { h, useEffect, useRef, useState } from '../lib/react.js';
2
+ import { FormActions } from './FormActions.js';
3
+ import { FormRows } from './FormRows.js';
4
+ import { Note } from './Note.js';
5
+ import { formActionsOf, formProblemsOf, formRowsOf, formSubmitOf, reportDuplicateKeys, visibleFieldsOf, } from './FormNodes.js';
6
+ import { revealFirstProblem } from '../lib/form-problem.js';
7
+ import './Form.css';
8
+ /**
9
+ * 整张表单:`fields` 进,行、错误、操作区、(被挡住的)提交都由它安排 —— 它自己一个值都不存,
10
+ * 值仍归调用点的 model(`value` 只读给校验看)。
11
+ * 还没提交过时一格错都不显示;第一次提交之后每次渲染重算,改好了当场消。
12
+ * 提交那一拍永远先算一遍:有错就不调 `onSubmit`,并把第一个有错的 `FormRow` 带进视口、聚焦它里面的控件。
13
+ * 回车与点提交钮走的是同一条路:真 `<form>` 的隐式提交在**只有一个文本框**时也会 fire,所以 `busy` / `disabled` 时回车同样不提交。
14
+ * 调用点只管数据与语义:行怎么排、错误摆哪、什么时候校验、按钮在哪,这里没有口子。
15
+ * @param props.fields - 每格的键 / 标签 / 值 / 校验 / 控件;`visible: false` 的那格当它不存在(不渲染、不校验、不进 `values`)。
16
+ * @param props.onSubmit - 校验全过的那一拍,回的是从 `fields` 现取的键值表;被挡住时不会被调。
17
+ * @param props.submit - 提交钮文案;`busyLabel` 是提交中换的那句,不给就沿用 `label`。
18
+ * @param props.cancel - 取消钮;给了就排在提交钮左边,`busy` 时一起禁用。
19
+ * @param props.extra - 其余动作(default / danger),排在最左,`busy` 时一起禁用。
20
+ * @param props.busy - 提交中(默认 false):整表控件的绑定 `disabled` 为真,所有动作一起禁用,回车也不提交。
21
+ * @param props.disabled - 只再禁提交钮(如没改动可存),各格控件与其余动作不受影响,回车也不提交。
22
+ * @param props.problem - 整表级那句错(提交失败),落在行与操作区之间;不给就不画。
23
+ * @returns 一个 `<form>`(`noValidate`:报错形状归 `FormRow`,不走浏览器的气泡)。
24
+ */
25
+ export function Form(props) {
26
+ const { fields, onSubmit, submit, cancel, extra, busy = false, disabled = false, problem } = props;
27
+ const [done, setDone] = useState(false);
28
+ const [reveal, setReveal] = useState(0);
29
+ const rootRef = useRef(null);
30
+ const shown = visibleFieldsOf(fields);
31
+ const problems = formProblemsOf(shown, done);
32
+ reportDuplicateKeys(shown);
33
+ useEffect(() => {
34
+ if (reveal === 0)
35
+ return;
36
+ const root = rootRef.current;
37
+ if (root !== null)
38
+ revealFirstProblem(root);
39
+ }, [reveal]);
40
+ const submitForm = () => {
41
+ if (busy || disabled)
42
+ return;
43
+ const outcome = formSubmitOf(visibleFieldsOf(fields));
44
+ setDone(true);
45
+ if (outcome.blocked) {
46
+ setReveal((count) => count + 1);
47
+ return;
48
+ }
49
+ onSubmit(outcome.values);
50
+ };
51
+ return (h("form", { className: "wb-form", ref: rootRef, noValidate: true, onSubmit: (event) => {
52
+ event.preventDefault();
53
+ submitForm();
54
+ } },
55
+ h(FormRows, null, formRowsOf({ fields: shown, problems, showProblem: done, busy })),
56
+ problem === undefined ? null : h(Note, { tone: 'problem', role: 'alert' }, problem),
57
+ h(FormActions, { actions: formActionsOf({ extra, cancel, submit, busy, disabled, onSubmitClick: submitForm }) })));
58
+ }
@@ -1,6 +1,12 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import './FormActions.css';
3
+ /**
4
+ * 一颗动作按钮的语气:`default`、`danger`(破坏性,描边危险色,永远不当填充主操作)、`primary`(填充,一个区只许一颗)。
5
+ */
3
6
  export type FormActionKind = 'default' | 'danger' | 'primary';
7
+ /**
8
+ * 操作区里的一颗动作:键、文案、语气与回调;`busy` 时自带转圈并禁用。
9
+ */
4
10
  export interface FormAction {
5
11
  readonly key: string;
6
12
  readonly label: ReactNode;
@@ -9,7 +15,15 @@ export interface FormAction {
9
15
  readonly disabled?: boolean | undefined;
10
16
  readonly busy?: boolean | undefined;
11
17
  }
18
+ /**
19
+ * `FormActions` 的契约:一组动作。
20
+ */
12
21
  export interface FormActionsProps {
13
22
  readonly actions: readonly FormAction[];
14
23
  }
24
+ /**
25
+ * 操作区:把一组动作按"primary 永远在右、danger 紧挨它左侧"排好,窄了自动折行。
26
+ * @param props.actions - 动作清单;调用点给的顺序只在同语气内部生效,空数组时整块不渲染。
27
+ * @returns 一排按钮,或 `null`(没有动作时)。
28
+ */
15
29
  export declare function FormActions({ actions }: FormActionsProps): JSX.Element | null;
@@ -26,6 +26,11 @@ function variantOf(kind) {
26
26
  return 'outline';
27
27
  return 'ghost';
28
28
  }
29
+ /**
30
+ * 操作区:把一组动作按"primary 永远在右、danger 紧挨它左侧"排好,窄了自动折行。
31
+ * @param props.actions - 动作清单;调用点给的顺序只在同语气内部生效,空数组时整块不渲染。
32
+ * @returns 一排按钮,或 `null`(没有动作时)。
33
+ */
29
34
  export function FormActions({ actions }) {
30
35
  if (actions.length === 0)
31
36
  return null;