@morlay/dsh-client-ui-primitives 0.0.3 → 0.0.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 (34) hide show
  1. package/README.md +18 -266
  2. package/dist/client.cjs +11 -612
  3. package/dist/client.d.cts +33 -658
  4. package/dist/client.d.mts +33 -658
  5. package/dist/index.mjs +1 -1
  6. package/dist/{reference-Dxy9uQYz.mjs → reference-Cjcn21uW.mjs} +0 -1
  7. package/locale/en.json +1 -1
  8. package/locale/zh.json +1 -1
  9. package/package.json +18 -18
  10. package/src/client/index.ts +7 -9
  11. package/src/client/schema-form/Editor.tsx +25 -38
  12. package/src/client/schema-form/SchemaForm.tsx +6 -12
  13. package/src/client/schema-form/controller.ts +65 -123
  14. package/src/client/schema-form/draft.ts +55 -138
  15. package/src/client/schema-form/fields.tsx +5 -14
  16. package/src/client/schema-form/hints.ts +31 -131
  17. package/src/client/schema-form/index.ts +15 -20
  18. package/src/client/schema-form/labels.ts +7 -9
  19. package/src/client/schema-form/lines.ts +36 -60
  20. package/src/client/schema-form/locales.ts +6 -8
  21. package/src/client/schema-form/rows.ts +22 -41
  22. package/src/client/schema-form/schema-node.ts +87 -150
  23. package/src/client/schema-form/slot-contract.ts +55 -76
  24. package/src/client/schema-form/styles.ts +39 -51
  25. package/src/client/schema-form/testing/fake-describe.ts +3 -9
  26. package/src/client/schema-form/testing/fake-scope.ts +8 -10
  27. package/src/client/schema-form/value.tsx +14 -26
  28. package/src/client/settings-form/SettingsForm.tsx +13 -17
  29. package/src/client/settings-form/fields.tsx +25 -35
  30. package/src/client/settings-form/form-model.ts +63 -145
  31. package/src/client/styling/styled.tsx +3 -5
  32. package/src/client/theme.generated.ts +18 -7
  33. package/src/reference-markdown.tsx +1 -1
  34. package/src/reference.ts +1 -1
@@ -1,127 +1,69 @@
1
- /**
2
- * 通用表单的提示面:业务知道、而 schema 表达不了的结构信息。
3
- *
4
- * 两件事:**dict 字段的候选键**(例如「会话模式」那一行,键是运行期清单里的模式 id,schema 里写不出来),以及
5
- * **字段的候选值**(选服务商、选模型这类——候选来自部署里的另一份配置,还经常互相依赖)。业务在客户端注册一个
6
- * 同步读数,表单一侧在投影时用它补出「未配置」行或把字段画成选择器。
7
- *
8
- * 读数必须是**同步**的:投影发生在渲染帧里。异步来源(HTTP / remote)由业务自己取好再注册;注册本身就当作一次
9
- * 变更通知(所以"取到清单后再注册"是推荐用法)。
10
- */
1
+ // 通用表单的提示面:注册同步读数,投影时补出 dict 的候选键、字段的候选值与文案。
2
+ //
3
+ // 读数必须同步(投影发生在渲染帧里):异步来源由业务取好再注册,注册本身即一次变更通知。
11
4
 
12
5
  import { Service } from "@deepseek-ai/cordis";
13
6
  import type { Context } from "@deepseek-ai/cordis";
14
7
  import { DYNAMIC_SEGMENT } from "./schema-node.ts";
15
8
 
16
- /** 一个 dict 字段的候选键读数。 */
9
+ // 一个 dict 字段的候选键读数。
17
10
  export type SuggestedKeysReader = () => readonly string[];
18
11
 
19
- /** 一个字段的文案(业务知道的说法;schema 上没有 title 这种位)。 */
12
+ // 一个字段的文案(业务知道的说法;schema 上没有 title 这种位)。
20
13
  export interface FieldText {
21
- /** 短名字(行式视图里当注释的抬头)。 */
14
+ // 短名字(行式视图里当注释的抬头)。
22
15
  label?: string | undefined;
23
- /** 一句话说明。 */
16
+ // 一句话说明。
24
17
  hint?: string | undefined;
25
18
  }
26
19
 
27
- /** 一个字段的文案读数。 */
20
+ // 一个字段的文案读数。
28
21
  export type FieldTextReader = () => FieldText;
29
22
 
30
- /** 一个可选项:要写进去的值,以及页面上显示的名字。 */
23
+ // 一个可选项:要写进去的值,以及页面上显示的名字。
31
24
  export interface SelectOption {
32
- /** 选中时写进配置的值。 */
25
+ // 选中时写进配置的值。
33
26
  value: unknown;
34
- /** 显示名;省略就用值本身。 */
27
+ // 显示名;省略就用值本身。
35
28
  label?: string | undefined;
36
29
  }
37
30
 
38
- /** 一个字段的候选值读数。 */
31
+ // 一个字段的候选值读数。
39
32
  export interface SelectSpec {
40
- /**
41
- * 候选依赖哪些**兄弟字段**(相对该字段所在对象的路径)。
42
- *
43
- * 依赖只声明"什么时候要重算":读侧在投影时按当前读数(草稿优先)求一次候选,所以声明了依赖的字段会随着
44
- * 兄弟字段的编辑立刻换候选——「选完服务商才列它有哪些模型」就是这么来的。
45
- */
33
+ // 候选依赖哪些兄弟字段(相对该字段所在对象的路径):声明只表示"何时重算",编辑兄弟字段即换候选。
46
34
  dependsOn?: readonly (readonly string[])[] | undefined;
47
- /**
48
- * 求候选。
49
- * @param read - 按依赖路径(相对该字段所在对象)读当前值:草稿优先。
50
- * @returns 可选项,顺序即页面上的顺序。
51
- */
35
+ // 按依赖路径读当前值(草稿优先),返回可选项(顺序即页面顺序)。
52
36
  options: (read: (path: readonly string[]) => unknown) => readonly SelectOption[];
53
37
  }
54
38
 
55
- /** 一个命名空间里各字段的候选键、文案与候选值。 */
39
+ // 一个命名空间里各字段的候选键、文案与候选值。
56
40
  export interface SchemaFormHintsFace {
57
- /**
58
- * 注册一个 dict 字段的候选键读数(重复注册同一处会覆盖,并通知一次)。
59
- * @param ns - settings 命名空间(行 id)。
60
- * @param path - 该 dict 字段的具体路径(顶层字段就是 `['models']`)。
61
- * @param read - 同步读数:当前候选键(顺序即页面上的顺序)。
62
- * @returns 注销函数。
63
- */
41
+ // 注册一个 dict 字段的候选键读数(同处重复注册会覆盖并通知一次);返回注销函数。
64
42
  suggestKeys(ns: string, path: readonly string[], read: SuggestedKeysReader): () => void;
65
- /**
66
- * 读某处当前声明的候选键。
67
- * @param ns - settings 命名空间。
68
- * @param path - 该字段的路径。
69
- * @returns 候选键;没注册过就是空数组。
70
- */
43
+ // 读某处当前声明的候选键;没注册过就是空数组。
71
44
  keysFor(ns: string, path: readonly string[]): readonly string[];
72
45
 
73
- /**
74
- * 注册一个字段的文案(短名 + 一句话说明)。行式视图把它画成注释行;schema 的 `description` 是兜底。
75
- * @param ns - settings 命名空间。
76
- * @param path - 该字段的路径。
77
- * @param read - 同步读数。
78
- * @returns 注销函数。
79
- */
46
+ // 注册一个字段的文案(短名 + 一句话说明):行式视图画成注释行,schema 的 `description` 是兜底;返回注销函数。
80
47
  describe(ns: string, path: readonly string[], read: FieldTextReader): () => void;
81
48
 
82
- /**
83
- * 读某处当前注册的文案。
84
- * @param ns - settings 命名空间。
85
- * @param path - 该字段的路径。
86
- * @returns 文案;没注册过就是空对象。
87
- */
49
+ // 读某处当前注册的文案;没注册过就是空对象。
88
50
  textFor(ns: string, path: readonly string[]): FieldText;
89
51
 
90
- /**
91
- * 注册一个字段的候选值读数(页面据此把字段画成选择器,并随依赖字段变化重算)。
92
- * @param ns - settings 命名空间。
93
- * @param path - 该字段的路径。
94
- * @param spec - 候选取法与它依赖的兄弟字段。
95
- * @returns 注销函数。
96
- */
52
+ // 注册一个字段的候选值读数(页面据此画成选择器,随依赖字段变化重算);返回注销函数。
97
53
  select(ns: string, path: readonly string[], spec: SelectSpec): () => void;
98
54
 
99
- /**
100
- * 注册一个**具名候选源**:schema 上用 `role('select', { source })` 认领它,字段因此不必在客户端按 ns/path
101
- * 登记——同一个源可以被多行、多个字段复用(「服务商」「模型」就是这种)。
102
- * @param name - 源的名字(schema 的 `role` extra 里写的那一个)。
103
- * @param spec - 候选取法与它依赖的兄弟字段。
104
- * @returns 注销函数。
105
- */
55
+ // 注册一个具名候选源:schema 上用 `role('select', { source })` 认领,字段不必按 ns/path 登记,一个源可被多行复用。
56
+ // 返回注销函数。
106
57
  source(name: string, spec: SelectSpec): () => void;
107
58
 
108
- /**
109
- * 读一个具名候选源。
110
- * @param name - 源的名字。
111
- * @returns 该源的读数;没注册过就是 `undefined`。
112
- */
59
+ // 读一个具名候选源;没注册过就是 `undefined`。
113
60
  sourceFor(name: string): SelectSpec | undefined;
114
61
 
115
- /**
116
- * 读某处当前声明的候选值读数。
117
- * @param ns - settings 命名空间。
118
- * @param path - 该字段的路径。
119
- * @returns 候选读数;没注册过就是 `undefined`。
120
- */
62
+ // 读某处当前声明的候选值读数;没注册过就是 `undefined`。
121
63
  selectFor(ns: string, path: readonly string[]): SelectSpec | undefined;
122
64
  }
123
65
 
124
- /** 提示面的完整服务(业务用注册侧,表单一侧用读侧)。 */
66
+ // 提示面的完整服务(业务用注册侧,表单一侧用读侧)。
125
67
  export class SchemaFormHints extends Service implements SchemaFormHintsFace {
126
68
  readonly #readers = new Map<string, SuggestedKeysReader>();
127
69
  readonly #texts = new Map<string, FieldTextReader>();
@@ -129,27 +71,18 @@ export class SchemaFormHints extends Service implements SchemaFormHintsFace {
129
71
  readonly #sources = new Map<string, SelectSpec>();
130
72
  readonly #listeners = new Set<() => void>();
131
73
 
132
- /** @param ctx - 提供本服务的插件上下文。 */
133
74
  constructor(ctx: Context) {
134
75
  super(ctx, "schemaFormHints");
135
76
  }
136
77
 
137
- /** @param ns - settings 命名空间。 @param path - 字段路径。 @returns 该处的键。 */
138
78
  keysFor(ns: string, path: readonly string[]): readonly string[] {
139
79
  return lookup(this.#readers, ns, path)?.() ?? [];
140
80
  }
141
81
 
142
- /** @param ns - settings 命名空间。 @param path - 字段路径。 @returns 该处注册的文案。 */
143
82
  textFor(ns: string, path: readonly string[]): FieldText {
144
83
  return lookup(this.#texts, ns, path)?.() ?? {};
145
84
  }
146
85
 
147
- /**
148
- * @param ns - settings 命名空间。
149
- * @param path - 字段路径。
150
- * @param spec - 候选取法与依赖。
151
- * @returns 注销函数。
152
- */
153
86
  select(ns: string, path: readonly string[], spec: SelectSpec): () => void {
154
87
  const key = locationKey(ns, path);
155
88
  this.#selects.set(key, spec);
@@ -161,16 +94,10 @@ export class SchemaFormHints extends Service implements SchemaFormHintsFace {
161
94
  };
162
95
  }
163
96
 
164
- /** @param ns - settings 命名空间。 @param path - 字段路径。 @returns 该处注册的候选读数。 */
165
97
  selectFor(ns: string, path: readonly string[]): SelectSpec | undefined {
166
98
  return lookup(this.#selects, ns, path);
167
99
  }
168
100
 
169
- /**
170
- * @param name - 源的名字。
171
- * @param spec - 候选取法与依赖。
172
- * @returns 注销函数。
173
- */
174
101
  source(name: string, spec: SelectSpec): () => void {
175
102
  this.#sources.set(name, spec);
176
103
  this.#publish();
@@ -181,17 +108,10 @@ export class SchemaFormHints extends Service implements SchemaFormHintsFace {
181
108
  };
182
109
  }
183
110
 
184
- /** @param name - 源的名字。 @returns 该源注册的候选读数。 */
185
111
  sourceFor(name: string): SelectSpec | undefined {
186
112
  return this.#sources.get(name);
187
113
  }
188
114
 
189
- /**
190
- * @param ns - settings 命名空间。
191
- * @param path - 字段路径。
192
- * @param read - 同步读数。
193
- * @returns 注销函数。
194
- */
195
115
  describe(ns: string, path: readonly string[], read: FieldTextReader): () => void {
196
116
  const key = locationKey(ns, path);
197
117
  this.#texts.set(key, read);
@@ -203,12 +123,6 @@ export class SchemaFormHints extends Service implements SchemaFormHintsFace {
203
123
  };
204
124
  }
205
125
 
206
- /**
207
- * @param ns - settings 命名空间。
208
- * @param path - 字段路径。
209
- * @param read - 同步读数。
210
- * @returns 注销函数。
211
- */
212
126
  suggestKeys(ns: string, path: readonly string[], read: SuggestedKeysReader): () => void {
213
127
  const key = locationKey(ns, path);
214
128
  this.#readers.set(key, read);
@@ -220,11 +134,7 @@ export class SchemaFormHints extends Service implements SchemaFormHintsFace {
220
134
  };
221
135
  }
222
136
 
223
- /**
224
- * 观察候选键的变化(注册与注销)。
225
- * @param listener - 变更回调。
226
- * @returns 取消订阅。
227
- */
137
+ // 观察候选键的变化(注册与注销);返回取消订阅。
228
138
  subscribe(listener: () => void): () => void {
229
139
  this.#listeners.add(listener);
230
140
  return () => {
@@ -232,9 +142,7 @@ export class SchemaFormHints extends Service implements SchemaFormHintsFace {
232
142
  };
233
143
  }
234
144
 
235
- /**
236
- * 重新通知一次:读数背后的数据变了(业务自己知道),页面据此重算候选行。
237
- */
145
+ // 重新通知一次:读数背后的数据变了(业务自己知道),页面据此重算候选行。
238
146
  refresh(): void {
239
147
  this.#publish();
240
148
  }
@@ -244,20 +152,12 @@ export class SchemaFormHints extends Service implements SchemaFormHintsFace {
244
152
  }
245
153
  }
246
154
 
247
- /** 一处字段的定位键(命名空间 + 路径)。 */
155
+ // 一处字段的定位键(命名空间 + 路径)。
248
156
  function locationKey(ns: string, path: readonly string[]): string {
249
157
  return `${ns}\u0000${path.join("\u0000")}`;
250
158
  }
251
159
 
252
- /**
253
- * 按具体路径找一处注册:先精确匹配,再试**模板变体**(动态键 / 数组下标的段写成 `*`)。
254
- *
255
- * 业务因此可以注册一次 `models.*.provider` 就覆盖每个模式,不必知道运行期有哪些键。
256
- * @param map - 某类注册表。
257
- * @param ns - settings 命名空间。
258
- * @param path - 具体路径。
259
- * @returns 命中的注册,或 `undefined`。
260
- */
160
+ // 按具体路径找一处注册:先精确匹配,再试模板变体(动态键 / 数组下标的段写成 `*`,一条 `models.*.provider` 覆盖整个模式)。
261
161
  function lookup<T>(map: Map<string, T>, ns: string, path: readonly string[]): T | undefined {
262
162
  const exact = map.get(locationKey(ns, path));
263
163
  if (exact !== undefined) return exact;
@@ -268,7 +168,7 @@ function lookup<T>(map: Map<string, T>, ns: string, path: readonly string[]): T
268
168
  return undefined;
269
169
  }
270
170
 
271
- /** 具体路径的模板变体(首段是顶层字段名,保持原样):通配得多的先试。 */
171
+ // 具体路径的模板变体(首段是顶层字段名,保持原样):通配得多的先试。
272
172
  function* templates(path: readonly string[]): Generator<readonly string[]> {
273
173
  const wildcardable = Math.max(0, path.length - 1);
274
174
  for (let bits = (1 << wildcardable) - 1; bits > 0; bits--) {
@@ -280,7 +180,7 @@ function* templates(path: readonly string[]): Generator<readonly string[]> {
280
180
 
281
181
  declare module "@deepseek-ai/cordis" {
282
182
  interface Context {
283
- /** 通用表单的提示面(dict 的候选键)。 */
183
+ // 通用表单的提示面(dict 的候选键)。
284
184
  schemaFormHints: SchemaFormHints;
285
185
  }
286
186
  }
@@ -1,16 +1,14 @@
1
- /**
2
- * client 半:为每个可由 schema 生成配置页的行注册本行的配置入口,并给出字段级自定义输入的槽契约。
3
- *
4
- * | 面 | 呈现 |
5
- * | ------------------------------------ | ---------------------------------------------------------------- |
6
- * | `plugins.row.config`(key 见 `rows.ts`) | `view: 'page'` 渲染该行 schema 生成的表单;`summary` 不画东西 |
7
- * | Factory `settings.schema-form.form` | 行注册项经它渲染同一份表单体,字段槽因此只声明一次 |
8
- * | `settings.schema-form.field` | chain 槽:业务方按 `role` 或 `ns + path` 认领字段的控件与文案 |
9
- *
10
- * 数据面是 host 的 settings 投影与共享配置表单(`ctx.configForms` / `ctx.settingsSchema`);bundle 与行的对应关系
11
- * 来自 `remote.pluginManager.listBundles()`。手写卡片用默认 priority 0 注册,按 slots 的 cell winner 规则自然遮住
12
- * 这里的自动项,因此两种页面可以并存、迁移不必一次做完。
13
- */
1
+ // client 半:为每个可由 schema 生成配置页的行注册本行的配置入口,并给出字段级自定义输入的槽契约。
2
+ //
3
+ // | 面 | 呈现 |
4
+ // | ------------------------------------ | ---------------------------------------------------------------- |
5
+ // | `plugins.row.config`(key 见 `rows.ts`) | `view: 'page'` 渲染该行 schema 生成的表单;`summary` 不画东西 |
6
+ // | Factory `settings.schema-form.form` | 行注册项经它渲染同一份表单体,字段槽因此只声明一次 |
7
+ // | `settings.schema-form.field` | chain 槽:业务方按 `role` 或 `ns + path` 认领字段的控件与文案 |
8
+ //
9
+ // 数据面是 host 的 settings 投影与共享配置表单(`ctx.configForms` / `ctx.settingsSchema`);bundle 与行的对应关系
10
+ // 来自 `remote.pluginManager.listBundles()`。手写卡片用默认 priority 0 注册,按 slots 的 cell winner 规则自然遮住
11
+ // 这里的自动项,因此两种页面可以并存。
14
12
 
15
13
  import type { Context } from "@deepseek-ai/cordis";
16
14
  import type {} from "@deepseek-ai/dsh-api-remotes/client";
@@ -29,10 +27,10 @@ import { RowRegistration } from "./rows.ts";
29
27
  import { SchemaForm } from "./SchemaForm.tsx";
30
28
  import type { SchemaFormProps } from "./slot-contract.ts";
31
29
 
32
- /** 本包字典与槽位文案的命名空间。 */
30
+ // 本包字典与槽位文案的命名空间。
33
31
  export const NS = "settings.schema-form";
34
32
 
35
- /** 需要的服务:槽位、字典、共享配置表单、schema 服务与插件管理(bundle 行清单)。 */
33
+ // 需要的服务:槽位、字典、共享配置表单、schema 服务与插件管理(bundle 行清单)。
36
34
  export const inject = [
37
35
  "slots",
38
36
  "locale",
@@ -72,13 +70,10 @@ export type {
72
70
  } from "./hints.ts";
73
71
  export type { SuggestedKeys } from "./schema-node.ts";
74
72
 
75
- /** 行配置入口的注册项:`page` 画表单,`summary` 不画(行的描述来自包自己的 locale)。 */
73
+ // 行配置入口的注册项:`page` 画表单,`summary` 不画(行的描述来自包自己的 locale)。
76
74
  type RowConfigEntryProps = PropsRuntime<"plugins.row.config"> & PropsRenderFactories;
77
75
 
78
- /**
79
- * 装上本包:字典、Factory(字段槽的声明者)与按行自动注册。
80
- * @param ctx - 浏览器插件上下文。
81
- */
76
+ // 装上本包:字典、Factory(字段槽的声明者)与按行自动注册。
82
77
  export function apply(ctx: Context): void {
83
78
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "ui-schema-form: dictionaries");
84
79
  // 提示面(dict 的候选键):同一次 apply 里提供,业务插件注册读数,本包的行投影读它。
@@ -1,16 +1,14 @@
1
- /**
2
- * 字段文案:标签兜底与说明解析。
3
- *
4
- * 标签没有 schema 声明可用(schemastery 的 meta 没有 title/label),所以先是字段名兜底(驼峰/下划线拆词),
5
- * 业务方经字段槽给 label/hint 时以槽为准;说明取 `description`(可本地化)与 `comment`。
6
- */
1
+ // 字段文案:标签兜底与说明解析。
2
+ //
3
+ // 标签没有 schema 声明可用(schemastery 的 meta 没有 title/label),所以先是字段名兜底(驼峰/下划线拆词),
4
+ // 业务方经字段槽给 label/hint 时以槽为准;说明取 `description`(可本地化)与 `comment`。
7
5
 
8
6
  import type { FieldNode } from "./schema-node.ts";
9
7
 
10
- /** 把 schema 里的本地化文本解析成当前语言;由 locale 服务提供。 */
8
+ // 把 schema 里的本地化文本解析成当前语言;由 locale 服务提供。
11
9
  export type ResolveText = (text: string | Readonly<Record<string, string>>) => string;
12
10
 
13
- /** 字段名兜底的可读标签:拆词并首字母大写,`0`/`1` 这类索引保持原样。 */
11
+ // 字段名兜底的可读标签:拆词并首字母大写,`0`/`1` 这类索引保持原样。
14
12
  export function humanizeKey(key: string): string {
15
13
  if (key.length === 0) return "";
16
14
  if (/^\d+$/.test(key)) return key;
@@ -21,7 +19,7 @@ export function humanizeKey(key: string): string {
21
19
  return words.charAt(0).toUpperCase() + words.slice(1);
22
20
  }
23
21
 
24
- /** 字段的说明(`description` 本地化后 + `comment`),没有就是 undefined。 */
22
+ // 字段的说明(`description` 本地化后 + `comment`),没有就是 undefined。
25
23
  export function fieldHint(node: FieldNode, resolveText: ResolveText): string | undefined {
26
24
  const parts: string[] = [];
27
25
  if (node.meta.description !== undefined) parts.push(resolveText(node.meta.description));
@@ -1,9 +1,7 @@
1
- /**
2
- * 行模型:把字段树(`walkFields` 的结果)排成**编辑器的行**——结构行(`{` / `[` / `}` / `]`)、字段行、注释行、
3
- * 以及容器尾部的添加行。
4
- *
5
- * 渲染层因此只按行画,不必自己递归:行序 = 视觉顺序,行号连续。折叠在这里生效(折叠的容器只留开启行)。
6
- */
1
+ // 行模型:把字段树(`walkFields` 的结果)排成**编辑器的行**——结构行(`{` / `[` / `}` / `]`)、字段行、注释行、
2
+ // 以及容器尾部的添加行。
3
+ //
4
+ // 渲染层因此只按行画,不必自己递归:行序 = 视觉顺序,行号连续。折叠在这里生效(折叠的容器只留开启行)。
7
5
 
8
6
  import { fieldKey, type AddableProperty, type SchemaFormState } from "./controller.ts";
9
7
  import type { DraftFieldState } from "./draft.ts";
@@ -20,29 +18,29 @@ import {
20
18
  type WalkedField,
21
19
  } from "./schema-node.ts";
22
20
 
23
- /** 一个容器的闭合行后面能加什么。 */
21
+ // 一个容器的闭合行后面能加什么。
24
22
  export type AddLineSpec =
25
- /** 数组:追加一个空项。 */
23
+ // 数组:追加一个空项。
26
24
  | { kind: "item" }
27
- /** 字典:敲键名,或从候选键里选(业务注册的读数)。 */
25
+ // 字典:敲键名,或从候选键里选(业务注册的读数)。
28
26
  | { kind: "key"; options: readonly AddableProperty[] }
29
- /** 对象:从「schema 声明了、值里还没有」的字段里选(没有候选项时不给输入框)。 */
27
+ // 对象:从「schema 声明了、值里还没有」的字段里选(没有候选项时不给输入框)。
30
28
  | { kind: "prop"; options: readonly AddableProperty[] };
31
29
 
32
- /** 一行上的变体切换控件。 */
30
+ // 一行上的变体切换控件。
33
31
  export interface VariantControl {
34
- /** 可以切到哪几支;顺序即声明顺序。 */
32
+ // 可以切到哪几支;顺序即声明顺序。
35
33
  choices: readonly VariantChoice[];
36
- /** 当前落在第几支。 */
34
+ // 当前落在第几支。
37
35
  selected: number;
38
36
  }
39
37
 
40
- /** 容器类型:结构行用 `{` 还是 `[`。 */
38
+ // 容器类型:结构行用 `{` 还是 `[`。
41
39
  export type ContainerShape = "object" | "array";
42
40
 
43
- /** 一行。 */
41
+ // 一行。
44
42
  export type EditorLine =
45
- /** 容器的开启行:`key: {` 或 `[`。 */
43
+ // 容器的开启行:`key: {` 或 `[`。
46
44
  | {
47
45
  kind: "open";
48
46
  path: readonly string[];
@@ -51,21 +49,21 @@ export type EditorLine =
51
49
  depth: number;
52
50
  collapsed: boolean;
53
51
  member: WalkedField["member"];
54
- /** 这一层是 union 时,可以在哪几支之间切(写哪个值)。 */
52
+ // 这一层是 union 时,可以在哪几支之间切(写哪个值)。
55
53
  variants: VariantControl | undefined;
56
54
  }
57
- /** 容器的关闭行:`}` / `]`(添加行挂在它后面同一行)。 */
55
+ // 容器的关闭行:`}` / `]`(添加行挂在它后面同一行)。
58
56
  | {
59
57
  kind: "close";
60
58
  path: readonly string[];
61
59
  node: FieldNode;
62
60
  shape: ContainerShape;
63
61
  depth: number;
64
- /** 这一层能加什么;`undefined` 表示加不了(对象的声明字段都配齐了)。 */
62
+ // 这一层能加什么;`undefined` 表示加不了(对象的声明字段都配齐了)。
65
63
  add: AddLineSpec | undefined;
66
64
  member: WalkedField["member"];
67
65
  }
68
- /** 字段行:`key: value`。 */
66
+ // 字段行:`key: value`。
69
67
  | {
70
68
  kind: "field";
71
69
  path: readonly string[];
@@ -73,33 +71,24 @@ export type EditorLine =
73
71
  depth: number;
74
72
  field: DraftFieldState;
75
73
  member: WalkedField["member"];
76
- /** 这个字段是 union 时,可以在哪几支之间切。 */
74
+ // 这个字段是 union 时,可以在哪几支之间切。
77
75
  variants: VariantControl | undefined;
78
- /**
79
- * 变体触发**代替这一行的值**:判别式 union 的标签行就是这样——`type: "sqlite" ▾` 里的值就是那个触发,
80
- * 不再另画一份值。
81
- */
76
+ // 变体触发**代替这一行的值**:判别式 union 的标签行就是这样——`type: "sqlite" ▾` 里的值就是那个触发,
77
+ // 不再另画一份值。
82
78
  variantsStandIn: boolean;
83
79
  }
84
- /** 注释行:schema 的说明与业务给的文案,画在字段行上方;这一行有问题时它装的是那条消息。 */
80
+ // 注释行:schema 的说明与业务给的文案,画在字段行上方;这一行有问题时它装的是那条消息。
85
81
  | {
86
82
  kind: "comment";
87
83
  path: readonly string[];
88
84
  depth: number;
89
85
  text: string;
90
- /** 这一行有校验消息(草稿解析失败或整段校验落下来):画成错误,而不是普通说明。 */
86
+ // 这一行有校验消息(草稿解析失败或整段校验落下来):画成错误,而不是普通说明。
91
87
  invalid: boolean;
92
88
  };
93
89
 
94
- /**
95
- * 容器的形状:对象类用 `{}`,序列类用 `[]`。
96
- *
97
- * union 没有自己的形状——它跟着**当前选中那一支**走(`access` 是数组就在页面上画 `[`),这也是
98
- * `access: string | string[]` 这类字段以前被画成对象容器的原因。
99
- * @param node - 字段节点。
100
- * @param value - 这一层的值(选支判据)。
101
- * @returns 形状;叶子节点是 `undefined`。
102
- */
90
+ // 容器的形状:对象类用 `{}`,序列类用 `[]`;union 没有自己的形状——它跟着**当前选中那一支**走
91
+ // (`access: string | string[]` 选到数组就画 `[`)。
103
92
  export function containerShape(node: FieldNode, value?: unknown): ContainerShape | undefined {
104
93
  switch (variantOf(node, value).type) {
105
94
  case "object":
@@ -114,13 +103,13 @@ export function containerShape(node: FieldNode, value?: unknown): ContainerShape
114
103
  }
115
104
  }
116
105
 
117
- /** 折叠状态与可见性:记「用户切换过的路径」,开合 = 切换过就反转 schema 的默认值。 */
106
+ // 折叠状态与可见性:记「用户切换过的路径」,开合 = 切换过就反转 schema 的默认值。
118
107
  export interface FoldState {
119
108
  collapsed(path: readonly string[]): boolean;
120
109
  toggle(path: readonly string[]): void;
121
110
  }
122
111
 
123
- /** 渲染顺序里可见的字段:折叠的容器把整棵子树滤掉,只留它自己的开启行。 */
112
+ // 渲染顺序里可见的字段:折叠的容器把整棵子树滤掉,只留它自己的开启行。
124
113
  export function visibleFields(state: SchemaFormState, fold: FoldState): readonly WalkedField[] {
125
114
  const containers = new Set(
126
115
  state.walked
@@ -136,12 +125,12 @@ export function visibleFields(state: SchemaFormState, fold: FoldState): readonly
136
125
  });
137
126
  }
138
127
 
139
- /** 这一层的当前值:与字段行显示的是同一份读数(草稿优先)。 */
128
+ // 这一层的当前值:与字段行显示的是同一份读数(草稿优先)。
140
129
  function valueAt(state: SchemaFormState, path: readonly string[]): unknown {
141
130
  return state.fields.get(fieldKey(path))?.value;
142
131
  }
143
132
 
144
- /** 这一层能加什么:数组追加空项,字典敲键名(可来自候选),对象从还没配的声明字段里选。 */
133
+ // 这一层能加什么:数组追加空项,字典敲键名(可来自候选),对象从还没配的声明字段里选。
145
134
  function addOf(
146
135
  node: FieldNode,
147
136
  shape: ContainerShape,
@@ -152,7 +141,7 @@ function addOf(
152
141
  return options.length === 0 ? undefined : { kind: "prop", options };
153
142
  }
154
143
 
155
- /** 一个节点上的变体切换控件:不是 union、或只有一支、或成员全是常量(字面量集合是选择器)时没有。 */
144
+ // 一个节点上的变体切换控件:不是 union、或只有一支、或成员全是常量(字面量集合是选择器)时没有。
156
145
  function variantsOf(node: FieldNode, value: unknown): VariantControl | undefined {
157
146
  const union = unionOf(node);
158
147
  if (union === undefined || union.choices !== undefined || union.variants.length < 2)
@@ -163,14 +152,7 @@ function variantsOf(node: FieldNode, value: unknown): VariantControl | undefined
163
152
  };
164
153
  }
165
154
 
166
- /**
167
- * 排成行序列。
168
- * @param state - 页面读数。
169
- * @param fold - 折叠状态。
170
- * @param resolveText - `description` 的本地化。
171
- * @param t - 本包字典。
172
- * @returns 行序列(行号即下标)。
173
- */
155
+ // 排成行序列(行号即下标)。
174
156
  export function editorLines(
175
157
  state: SchemaFormState,
176
158
  fold: FoldState,
@@ -271,10 +253,8 @@ export function editorLines(
271
253
  return moveVariantControlToTag(lines);
272
254
  }
273
255
 
274
- /**
275
- * 判别式 union 的切换控件挪到**标签字段那一行**上:`session-rdb` 的页面上用户改的就是 `type`,把它放在容器
276
- * 行上等于让人在两处看同一件事。没有标签字段(`access` 这种按值形状选的)就留在 union 自己那一行。
277
- */
256
+ // 判别式 union 的切换控件挪到**标签字段那一行**上:`session-rdb` 的页面上用户改的就是 `type`,把它放在容器
257
+ // 行上等于让人在两处看同一件事。没有标签字段(`access` 这种按值形状选的)就留在 union 自己那一行。
278
258
  function moveVariantControlToTag(lines: EditorLine[]): EditorLine[] {
279
259
  for (const line of lines) {
280
260
  if (line.kind === "comment" || line.kind === "close") continue;
@@ -297,12 +277,8 @@ function moveVariantControlToTag(lines: EditorLine[]): EditorLine[] {
297
277
  return lines;
298
278
  }
299
279
 
300
- /**
301
- * 一行的注释:业务给的文案(字段槽的 label/hint)优先,其次 schema 的说明。
302
- *
303
- * 这一行有问题时**注释位让给那条消息**(参考实现也是把错误画在行尾那一段的位置上)——说明换成"哪里不对",
304
- * 错误因此不必挤在值后面。
305
- */
280
+ // 一行的注释:业务经提示面给的文案优先,schema 的 `description` / `comment` 兜底;这一行有问题时
281
+ // **注释位让给那条消息**(错误因此不必挤在值后面)。
306
282
  function commentFor(
307
283
  state: SchemaFormState,
308
284
  item: WalkedField,
@@ -327,7 +303,7 @@ function commentFor(
327
303
  return parts.length === 0 ? undefined : { text: parts.join(" · "), invalid: false };
328
304
  }
329
305
 
330
- /** `path` 是不是 `prefix` 的后代(含自身)。 */
306
+ // `path` 是不是 `prefix` 的后代(含自身)。
331
307
  function isPrefix(prefix: readonly string[], path: readonly string[]): boolean {
332
308
  if (prefix.length > path.length) return false;
333
309
  return prefix.every((segment, index) => path[index] === segment);
@@ -1,10 +1,8 @@
1
- /**
2
- * 本包字典(命名空间 `settings.schema-form`):表单壳、字段控件与只读呈现的文案。
3
- *
4
- * 字段自身的人话标签不在这里(那是业务方经字段槽或 schema `description` 给的),这里只放**机制**文案。
5
- */
1
+ // 本包字典(命名空间 `settings.schema-form`):表单壳、字段控件与只读呈现的文案。
2
+ //
3
+ // 字段自身的人话标签不在这里(那是业务方经字段槽或 schema `description` 给的),这里只放**机制**文案。
6
4
 
7
- /** 中文文案(部署语言偏好是 zh)。 */
5
+ // 中文文案(部署语言偏好是 zh)。
8
6
  export const zh = {
9
7
  unavailable: "该插件当前未加载,暂时无法配置。",
10
8
  readOnly: "本部署的设置为只读。",
@@ -64,7 +62,7 @@ export const zh = {
64
62
  missingItem: "每一项都需要一个 {key}",
65
63
  } as const;
66
64
 
67
- /** 英文文案。 */
65
+ // 英文文案。
68
66
  export const en = {
69
67
  unavailable: "This plugin is not loaded, so it cannot be configured right now.",
70
68
  readOnly: "This deployment stores settings read-only.",
@@ -124,5 +122,5 @@ export const en = {
124
122
  missingItem: "Every item needs a {key}",
125
123
  } as const;
126
124
 
127
- /** 本命名空间的字典键。 */
125
+ // 本命名空间的字典键。
128
126
  export type SchemaFormLocaleKey = keyof typeof zh;