@morlay/dsh-client-ui-primitives 0.0.4 → 0.0.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 (36) hide show
  1. package/README.md +18 -266
  2. package/dist/client.cjs +1 -611
  3. package/dist/client.d.cts +37 -684
  4. package/dist/client.d.mts +37 -684
  5. package/dist/index.d.cts +2 -2
  6. package/dist/index.d.mts +2 -2
  7. package/dist/index.mjs +1 -1
  8. package/dist/{reference-Dxy9uQYz.mjs → reference-Cjcn21uW.mjs} +0 -1
  9. package/locale/en.json +1 -1
  10. package/locale/zh.json +1 -1
  11. package/package.json +2 -2
  12. package/src/client/index.ts +7 -9
  13. package/src/client/schema-form/Editor.tsx +25 -38
  14. package/src/client/schema-form/SchemaForm.tsx +6 -12
  15. package/src/client/schema-form/controller.ts +65 -123
  16. package/src/client/schema-form/draft.ts +55 -138
  17. package/src/client/schema-form/fields.tsx +5 -14
  18. package/src/client/schema-form/hints.ts +31 -131
  19. package/src/client/schema-form/index.ts +15 -20
  20. package/src/client/schema-form/labels.ts +7 -9
  21. package/src/client/schema-form/lines.ts +36 -60
  22. package/src/client/schema-form/locales.ts +6 -8
  23. package/src/client/schema-form/rows.ts +22 -41
  24. package/src/client/schema-form/schema-node.ts +87 -150
  25. package/src/client/schema-form/slot-contract.ts +55 -76
  26. package/src/client/schema-form/styles.ts +39 -51
  27. package/src/client/schema-form/testing/fake-describe.ts +3 -9
  28. package/src/client/schema-form/testing/fake-scope.ts +8 -10
  29. package/src/client/schema-form/value.tsx +14 -26
  30. package/src/client/settings-form/SettingsForm.tsx +13 -17
  31. package/src/client/settings-form/fields.tsx +25 -35
  32. package/src/client/settings-form/form-model.ts +63 -145
  33. package/src/client/styling/styled.tsx +3 -5
  34. package/src/client/theme.generated.ts +6 -7
  35. package/src/reference-markdown.tsx +1 -1
  36. package/src/reference.ts +1 -1
package/dist/client.cjs CHANGED
@@ -21,7 +21,6 @@ window.__ModuleLoader__.load({
21
21
  let react = require("react");
22
22
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
23
23
  let react_jsx_runtime = require("react/jsx-runtime");
24
- /** 空的元数据:动态子节点(无声明 schema 的 dict 键)与空字段树用它。 */
25
24
  function emptyMeta() {
26
25
  return {
27
26
  description: void 0,
@@ -42,11 +41,9 @@ window.__ModuleLoader__.load({
42
41
  extra: void 0
43
42
  };
44
43
  }
45
- /** `badges` 只留文本:配色与文案由字典决定。 */
46
44
  function badgesOf(meta) {
47
45
  return (meta.badges ?? []).map((badge) => badge.text);
48
46
  }
49
- /** 归一化一个节点的 meta。 */
50
47
  function metaOf(schema) {
51
48
  const meta = schema.meta ?? {};
52
49
  return {
@@ -68,17 +65,14 @@ window.__ModuleLoader__.load({
68
65
  extra: meta.extra
69
66
  };
70
67
  }
71
- /** 外层 meta 与内层 meta 合并:外层的 undefined 不覆盖内层(transform 的 inner 带着自己的角色)。 */
72
68
  function mergeMeta(inner, outer) {
73
69
  const merged = { ...inner };
74
70
  for (const [key, value] of Object.entries(outer)) if (value !== void 0) merged[key] = value;
75
71
  return merged;
76
72
  }
77
- /** 节点自己的路径末段:动态段保持占位,根节点为空串。 */
78
73
  function keyOf(path) {
79
74
  return path[path.length - 1] ?? "";
80
75
  }
81
- /** 一个纯字符串叶子(dict 没有声明键 schema 时用它)。 */
82
76
  function stringLeaf(path, meta) {
83
77
  return {
84
78
  key: keyOf(path),
@@ -89,7 +83,6 @@ window.__ModuleLoader__.load({
89
83
  type: "string"
90
84
  };
91
85
  }
92
- /** 把 schema 的类型名收口成渲染层认识的那几种。 */
93
86
  function leafTypeOf(type) {
94
87
  switch (type) {
95
88
  case "string": return "string";
@@ -100,12 +93,6 @@ window.__ModuleLoader__.load({
100
93
  default: return "any";
101
94
  }
102
95
  }
103
- /**
104
- * 投影一个 schema 节点。
105
- * @param schema - rehydrate 之后的 volatile 表单 schema(或它的子节点)。
106
- * @param path - 该节点相对段根的路径;动态子节点由调用方给占位段。
107
- * @returns 渲染层要的字段树。
108
- */
109
96
  function projectNode(schema, path = []) {
110
97
  return project(schema, path, []);
111
98
  }
@@ -223,13 +210,11 @@ window.__ModuleLoader__.load({
223
210
  };
224
211
  }
225
212
  }
226
- /** 展开 `lazy` 包装:投影里 lazy 只是一层,值仍在同一路径上。 */
227
213
  function unwrapLazy(node) {
228
214
  let current = node;
229
215
  while (current?.type === "lazy") current = current.inner;
230
216
  return current;
231
217
  }
232
- /** 某一层可见的字段:object 直接给;union / intersect 把各变体的对象字段并起来。 */
233
218
  function fieldsOf(node) {
234
219
  const current = unwrapLazy(node);
235
220
  switch (current?.type) {
@@ -239,12 +224,6 @@ window.__ModuleLoader__.load({
239
224
  default: return [];
240
225
  }
241
226
  }
242
- /**
243
- * 按具体路径找回投影节点(动态段匹配真实键或索引)。
244
- * @param root - 段根的字段树。
245
- * @param path - 具体路径。
246
- * @returns 命中的节点,或 `undefined`。
247
- */
248
227
  function seekNode(root, path) {
249
228
  let current = root;
250
229
  for (const segment of path) {
@@ -270,16 +249,6 @@ window.__ModuleLoader__.load({
270
249
  }
271
250
  return unwrapLazy(current);
272
251
  }
273
- /**
274
- * 按当前值把字段树展开成渲染顺序的扁平列表(前序):容器在前、子项紧随其后。
275
- *
276
- * 出现的**只有值里有的东西,加上必填的**:对象的必填字段始终占行(值是空的也要让用户看见"必须填"),非必填的
277
- * 按值出现——值里没有就不占行,它在这一层的「添加属性」候选里,选中才加。数组按索引、字典按键展开;字面量
278
- * union 是叶子控件,对象 union 只展开与当前值匹配的那个变体,intersect 的各成员在同一层展开。
279
- * @param root - 段根的字段树。
280
- * @param value - 生效值(或草稿后的值)。
281
- * @returns 渲染顺序的字段列表。
282
- */
283
252
  function walkFields(root, value) {
284
253
  const out = [];
285
254
  visit(root, root.path, value, out, true, void 0, /* @__PURE__ */ new Set(), void 0);
@@ -345,14 +314,12 @@ window.__ModuleLoader__.load({
345
314
  default: return;
346
315
  }
347
316
  }
348
- /** 数组的项身份:显式声明优先,否则 item 上的 `id` 字段。 */
349
317
  function mergeKeyOf(schema, item) {
350
318
  const declared = declaredExtra(schema, "mergeKey");
351
319
  if (declared !== void 0) return declared;
352
320
  const current = unwrapLazy(item);
353
321
  return current?.type === "object" && current.fields.some((field) => field.key === "id") ? "id" : void 0;
354
322
  }
355
- /** union 的判别标签:显式声明优先,否则所有对象成员共有的唯一常量字段。 */
356
323
  function tagKeyOf(schema, variants) {
357
324
  const declared = declaredExtra(schema, "tag");
358
325
  if (declared !== void 0) return declared;
@@ -362,29 +329,18 @@ window.__ModuleLoader__.load({
362
329
  const candidates = (objects[0]?.fields ?? []).map((field) => unwrapLazy(field)).filter((field) => field?.type === "const").map((field) => field.key).filter((key) => objects.every((object) => unwrapLazy(object.fields.find((field) => field.key === key))?.type === "const"));
363
330
  return candidates.length === 1 ? candidates[0] : void 0;
364
331
  }
365
- /** `role(text, { … })` 里声明的字符串约定(项身份 `mergeKey`、判别标签 `tag`)。 */
366
332
  function declaredExtra(schema, name) {
367
333
  const extra = schema.meta?.extra;
368
334
  if (typeof extra !== "object" || extra === null) return void 0;
369
335
  const value = Reflect.get(extra, name);
370
336
  return typeof value === "string" && value.length > 0 ? value : void 0;
371
337
  }
372
- /** 一项的项身份值(渲染与冲突检查共用的读法)。 */
373
338
  function memberLabel(value, mergeKey) {
374
339
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
375
340
  const member = Reflect.get(value, mergeKey);
376
341
  if (typeof member === "string") return member;
377
342
  if (typeof member === "number" && Number.isFinite(member)) return String(member);
378
343
  }
379
- /**
380
- * 项身份冲突:同一层里两个成员同名,或某个成员没有名字。
381
- *
382
- * 名字是成员的「身份」,写盘仍按索引——所以这只是保存前的本地校验:重名的数组在页面上是非法草稿,
383
- * 而不是悄悄写到 host 上。
384
- * @param root - 段根的字段树。
385
- * @param value - 草稿后的段值。
386
- * @returns 第一处冲突,或 `undefined`。
387
- */
388
344
  function mergeKeyConflict(root, value) {
389
345
  return scanConflict(root, root.path, value);
390
346
  }
@@ -449,34 +405,24 @@ window.__ModuleLoader__.load({
449
405
  default: return;
450
406
  }
451
407
  }
452
- /** 一个容器值里的成员(对象键或数组索引)。 */
453
408
  function entriesOf(value) {
454
409
  if (Array.isArray(value)) return value.map((item, index) => [String(index), item]);
455
410
  if (typeof value === "object" && value !== null) return Object.entries(value);
456
411
  return [];
457
412
  }
458
- /** 读一个成员的值。 */
459
413
  function memberOf(value, key) {
460
414
  if (Array.isArray(value)) return value[Number(key)];
461
415
  if (typeof value === "object" && value !== null) return value[String(key)];
462
416
  }
463
- /** 把节点收成 union:渲染层判形状与选支都从这里进。 */
464
417
  function unionOf(node) {
465
418
  const current = unwrapLazy(node);
466
419
  return current?.type === "union" ? current : void 0;
467
420
  }
468
- /**
469
- * 当前值落在哪一支:与 {@link walkFields} 的展开同一判据(标签值优先,其次值的形状,最后第一支)。
470
- * @param node - 字段节点(不是 union 就原样返回)。
471
- * @param value - 这一层的值。
472
- * @returns 选中变体的节点。
473
- */
474
421
  function variantOf(node, value) {
475
422
  const union = unionOf(node);
476
423
  if (union === void 0) return unwrapLazy(node) ?? node;
477
424
  return pickVariant(union, value) ?? union;
478
425
  }
479
- /** 变体里那个判别标签字段的常量值(不是 `const` 就没有)。 */
480
426
  function tagValueOf(variant, tagKey) {
481
427
  const current = unwrapLazy(variant);
482
428
  if (current?.type !== "object" && current?.type !== "intersect") return {
@@ -496,7 +442,6 @@ window.__ModuleLoader__.load({
496
442
  value: void 0
497
443
  };
498
444
  }
499
- /** 一个变体在页面上的名字:能看出"这一支是什么",而不是内部类型名。 */
500
445
  function variantLabel(variant, tag) {
501
446
  if (tag.found) return JSON.stringify(tag.value);
502
447
  switch (unwrapLazy(variant)?.type) {
@@ -511,14 +456,6 @@ window.__ModuleLoader__.load({
511
456
  default: return unwrapLazy(variant)?.type ?? "any";
512
457
  }
513
458
  }
514
- /**
515
- * 变体切换的选项。
516
- *
517
- * 带判别标签的 union 写**标签值**(`session-rdb` 的 `type: sqlite` ↔ `type: postgres`);没有标签的写目标变体的
518
- * 空值(`access` 在「一段文本」与「一组文本」之间切)。
519
- * @param union - 投影出来的 union 节点。
520
- * @returns 每个变体一项,顺序即声明顺序。
521
- */
522
459
  function variantChoices(union) {
523
460
  return union.variants.map((variant) => {
524
461
  const tag = union.tagKey === void 0 ? {
@@ -531,7 +468,6 @@ window.__ModuleLoader__.load({
531
468
  };
532
469
  });
533
470
  }
534
- /** 按路径读一个值:对象键与数组下标都认(值的读法只有这一处)。 */
535
471
  function readPath(root, path) {
536
472
  return path.reduce((node, segment) => {
537
473
  if (Array.isArray(node)) return node[Number(segment)];
@@ -539,29 +475,12 @@ window.__ModuleLoader__.load({
539
475
  return node[segment];
540
476
  }, root);
541
477
  }
542
- /**
543
- * 这个"默认值"其实不是值——它不该用来当新加进来的那一项的初值。
544
- *
545
- * 两种:`null`("没有值"的判断位,例如 `defaultModel` 用 `.default(null)` 表达"没配就没有这个键"),以及
546
- * schemastery 给容器类型自动塞的空壳(`{}`)。两种都该让容器把字段摆出来。
547
- * @param node - 字段节点。
548
- * @returns 是不是空壳。
549
- */
550
478
  function isBlankDefault(node) {
551
479
  const value = node.meta.defaultValue;
552
480
  if (value === null) return true;
553
481
  if (node.type !== "object" && node.type !== "intersect") return false;
554
482
  return typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
555
483
  }
556
- /**
557
- * 一个新子节点的初值:声明的默认值优先;对象把字段摆出来(见下);字典/数组给空的容器;其余标量给 **`null`**。
558
- *
559
- * `null` 在这里是"还没有值"的判断位(`null` / `undefined` 都算),页面据此把它画成一个等着输入的位子——
560
- * 而不是 `""`、`0` 这种看着像已经填过的值。host 侧也认这个语义:非必填字段拿到 `null` 回退到默认值,
561
- * 必填字段拿到 `null` 就是"必须填"。
562
- * @param node - 子节点的字段树。
563
- * @returns 初值。
564
- */
565
484
  function emptyValueOf(node) {
566
485
  if (node.meta.hasDefault && !isBlankDefault(node)) return structuredClone(node.meta.defaultValue);
567
486
  switch (node.type) {
@@ -576,7 +495,6 @@ window.__ModuleLoader__.load({
576
495
  default: return null;
577
496
  }
578
497
  }
579
- /** 选中与当前值匹配的 union 变体:对象按判别标签的值判,叶子按值类型判。 */
580
498
  function pickVariant(node, value) {
581
499
  if (node.tagKey !== void 0 && typeof value === "object" && value !== null && !Array.isArray(value)) {
582
500
  const tagValue = Reflect.get(value, node.tagKey);
@@ -598,14 +516,6 @@ window.__ModuleLoader__.load({
598
516
  }
599
517
  //#endregion
600
518
  //#region src/client/schema-form/draft.ts
601
- /**
602
- * 把校验器抛出的错误收成一条失败。
603
- *
604
- * schemastery 的 `ValidationError` 把路径留在 `options.path` 上(消息里的 `$…` 前缀是给命令行看的),所以这里
605
- * 取路径、去前缀——行内报错据此知道挂在哪一行。
606
- * @param error - 校验器抛出的错误。
607
- * @returns 消息与路径。
608
- */
609
519
  function failureOf(error) {
610
520
  const raw = error instanceof Error ? error.message : String(error);
611
521
  const path = (error.options?.path ?? []).filter((segment) => typeof segment === "string" || typeof segment === "number").map(String);
@@ -615,9 +525,6 @@ window.__ModuleLoader__.load({
615
525
  path
616
526
  };
617
527
  }
618
- /**
619
- * 一页配置的草稿。一个命名空间一个实例:它订阅共享配置表单,投影给渲染层读。
620
- */
621
528
  var SchemaDraftModel = class {
622
529
  #scope;
623
530
  #root;
@@ -629,9 +536,6 @@ window.__ModuleLoader__.load({
629
536
  #saving = false;
630
537
  #failed = false;
631
538
  #violation;
632
- /**
633
- * @param options - 本页的共享配置表单、字段树与整段校验。
634
- */
635
539
  constructor(options) {
636
540
  this.#scope = options.scope;
637
541
  this.#root = options.root;
@@ -640,32 +544,15 @@ window.__ModuleLoader__.load({
640
544
  this.#publish();
641
545
  });
642
546
  }
643
- /**
644
- * 换一份字段树。
645
- *
646
- * 增删项要按它取节点默认值,而 schema 可能随 describe 变(行的 Config 改了)——所以控制器每次投影都同步一次,
647
- * 否则加进来的项会取不到默认值。
648
- * @param root - 段根的字段树。
649
- */
650
547
  setRoot(root) {
651
548
  this.#root = root;
652
549
  }
653
- /**
654
- * 观察草稿与读数的变化。
655
- * @param listener - 每次投影都调一次的回调。
656
- * @returns 取消订阅。
657
- */
658
550
  subscribe(listener) {
659
551
  this.#listeners.add(listener);
660
552
  return () => {
661
553
  this.#listeners.delete(listener);
662
554
  };
663
555
  }
664
- /**
665
- * 投影一份读数给渲染层。
666
- * @param project - 用模型的当前读数造渲染要的状态。
667
- * @returns 渲染层按选择器读的 store。
668
- */
669
556
  bind(project) {
670
557
  const store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(project());
671
558
  this.subscribe(() => {
@@ -673,10 +560,6 @@ window.__ModuleLoader__.load({
673
560
  });
674
561
  return store;
675
562
  }
676
- /**
677
- * 表单级状态(与上游 `SettingsForm` 的壳同形)。
678
- * @returns 可用性、可写性、脏、非法、保存中、上次保存失败。
679
- */
680
563
  shell() {
681
564
  const { writes, blocked } = this.#plan();
682
565
  return {
@@ -688,22 +571,9 @@ window.__ModuleLoader__.load({
688
571
  failed: this.#failed
689
572
  };
690
573
  }
691
- /**
692
- * 保存前的整段校验(保存被 schema 挡下时非空)。
693
- * @returns 失败消息与它的位置,或 `undefined`。
694
- */
695
574
  violation() {
696
575
  return this.#violation;
697
576
  }
698
- /**
699
- * 读一个字段的草稿状态。
700
- * @param path - 具体路径(动态段已按真实键或索引替换)。
701
- * @param preview - 本轮已经算好的读数(`preview()` 的结果)。
702
- *
703
- * **容器的值要跟着别处的草稿走**:判别式 union 的选支就是容器的值,若这里读生效层,`type` 的切换控件会显示旧
704
- * 的那一支(字段树却已经换了)。所以有 `preview` 就用它——控制器一次投影里只算一份。
705
- * @returns 显示值、文本草稿、非法消息、是否覆盖。
706
- */
707
577
  field(path, preview) {
708
578
  const snapshot = this.#scope.getSnapshot();
709
579
  const staged = this.#staged.get(pathKey(path));
@@ -749,23 +619,12 @@ window.__ModuleLoader__.load({
749
619
  };
750
620
  }
751
621
  }
752
- /**
753
- * 暂存一个合法值。
754
- * @param path - 具体路径。
755
- * @param value - 控件给出的值。
756
- */
757
622
  set(path, value) {
758
623
  this.#stage(path, {
759
624
  kind: "value",
760
625
  value
761
626
  });
762
627
  }
763
- /**
764
- * 暂存一段文本,保存时才解析。
765
- * @param path - 具体路径。
766
- * @param text - 用户输入的原文。
767
- * @param parse - 解析规则;失败时给出消息并挡住保存。
768
- */
769
628
  setText(path, text, parse) {
770
629
  this.#stage(path, {
771
630
  kind: "text",
@@ -773,17 +632,9 @@ window.__ModuleLoader__.load({
773
632
  parse
774
633
  });
775
634
  }
776
- /**
777
- * 暂存「恢复默认」。
778
- * @param path - 具体路径。
779
- */
780
635
  clear(path) {
781
636
  this.#stage(path, { kind: "clear" });
782
637
  }
783
- /**
784
- * 追加一个数组项,值取该项节点的默认值。
785
- * @param path - 数组字段的具体路径。
786
- */
787
638
  appendItem(path) {
788
639
  const node = seekNode(this.#root, path);
789
640
  if (node?.type !== "array") return;
@@ -791,21 +642,9 @@ window.__ModuleLoader__.load({
791
642
  const index = Array.isArray(current) ? current.length : 0;
792
643
  this.set([...path, String(index)], emptyValueOf(node.item));
793
644
  }
794
- /**
795
- * 移除一个数组项。
796
- * @param path - 数组字段的具体路径。
797
- * @param index - 项在数组里的位置。
798
- */
799
645
  removeItem(path, index) {
800
646
  this.#stage([...path, String(index)], { kind: "drop" });
801
647
  }
802
- /**
803
- * 给一层加一项,值取它的默认值。
804
- *
805
- * 字典认任意键;对象只认 schema 声明过的字段(写一个没声明的键会被 host 挡下,所以这里直接不写)。
806
- * @param path - 容器的具体路径。
807
- * @param key - 要加的键。
808
- */
809
648
  addKey(path, key) {
810
649
  const node = seekNode(this.#root, path);
811
650
  if (node === void 0) return;
@@ -817,25 +656,15 @@ window.__ModuleLoader__.load({
817
656
  if (field === void 0) return;
818
657
  this.set([...path, key], emptyValueOf(field));
819
658
  }
820
- /**
821
- * 删一个字典键。
822
- * @param path - 字典字段的具体路径。
823
- * @param key - 要删的键。
824
- */
825
659
  removeKey(path, key) {
826
660
  this.#stage([...path, key], { kind: "drop" });
827
661
  }
828
- /**
829
- * 撤销一个字段的草稿(回到它当前的生效值)——行内编辑取消时用。
830
- * @param path - 具体路径。
831
- */
832
662
  revert(path) {
833
663
  if (!this.#staged.delete(pathKey(path))) return;
834
664
  this.#failed = false;
835
665
  this.#violation = void 0;
836
666
  this.#publish();
837
667
  }
838
- /** 丢弃全部草稿,回到 host 的读数。 */
839
668
  discard() {
840
669
  if (this.#staged.size === 0 && !this.#failed && this.#violation === void 0) return;
841
670
  this.#staged.clear();
@@ -844,13 +673,6 @@ window.__ModuleLoader__.load({
844
673
  this.#violation = void 0;
845
674
  this.#publish();
846
675
  }
847
- /**
848
- * 把全部草稿写成一次带 revision 栅栏的提交。
849
- *
850
- * host 是「值是否被接受」的唯一权威(有些约束 schema 表达不出来),所以落盘与否读回来决定,而不是在这里
851
- * 预测;没落盘的保存保留草稿,让用户改而不是重打。
852
- * @returns 写完与读回之后的收束。
853
- */
854
676
  async save() {
855
677
  const snapshot = this.#scope.getSnapshot();
856
678
  if (this.#saving || !snapshot.writable) return;
@@ -886,12 +708,10 @@ window.__ModuleLoader__.load({
886
708
  this.#publish();
887
709
  }
888
710
  }
889
- /** 释放对共享配置表单的订阅。 */
890
711
  dispose() {
891
712
  this.#unsubscribe();
892
713
  this.#listeners.clear();
893
714
  }
894
- /** 每条草稿这一轮会写什么;被 schema 挡下的草稿没有 op,但仍然是脏的。 */
895
715
  #plan() {
896
716
  const snapshot = this.#scope.getSnapshot();
897
717
  const writes = [];
@@ -932,7 +752,6 @@ window.__ModuleLoader__.load({
932
752
  blocked
933
753
  };
934
754
  }
935
- /** 生效值套上本轮草稿后的段值:字段树按它展开(追加的数组项立刻出现),整段校验也看它。 */
936
755
  preview() {
937
756
  const ops = this.#plan().writes.flatMap((item) => item.op === void 0 ? [] : [item.op]);
938
757
  return applyOps(this.#scope.getSnapshot().value ?? {}, ops);
@@ -951,17 +770,14 @@ window.__ModuleLoader__.load({
951
770
  for (const listener of this.#listeners) listener();
952
771
  }
953
772
  };
954
- /** 深比较:值相等就不写(避免把「改回原值」也当成一次写)。 */
955
773
  function sameValue$1(left, right) {
956
774
  if (Object.is(left, right)) return true;
957
775
  if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return false;
958
776
  return JSON.stringify(left) === JSON.stringify(right);
959
777
  }
960
- /** 路径的 map 键。 */
961
778
  function pathKey(path) {
962
779
  return JSON.stringify(path);
963
780
  }
964
- /** 路径末段是否在用户层里存在(覆盖判据)。 */
965
781
  function hasPath(root, path) {
966
782
  if (path.length === 0) return root !== void 0;
967
783
  const parent = readPath(root, path.slice(0, -1));
@@ -970,7 +786,6 @@ window.__ModuleLoader__.load({
970
786
  if (typeof parent !== "object" || parent === null) return false;
971
787
  return leaf in parent;
972
788
  }
973
- /** 把一组 op 套到一个段值上(本地读数用,不写 host)。 */
974
789
  function applyOps(section, ops) {
975
790
  const next = structuredClone(section);
976
791
  for (const op of ops) {
@@ -989,26 +804,15 @@ window.__ModuleLoader__.load({
989
804
  }
990
805
  //#endregion
991
806
  //#region src/client/schema-form/controller.ts
992
- /** `role('select', { source })` 里声明的候选源名字。 */
993
807
  function declaredSource(node) {
994
808
  const extra = node?.meta.extra;
995
809
  if (typeof extra !== "object" || extra === null) return void 0;
996
810
  const name = Reflect.get(extra, "source");
997
811
  return typeof name === "string" && name.length > 0 ? name : void 0;
998
812
  }
999
- /** 字段在状态表里的键(具体路径的 JSON)。 */
1000
813
  function fieldKey(path) {
1001
814
  return JSON.stringify(path);
1002
815
  }
1003
- /**
1004
- * rehydrate 一段配置 schema,并判它能不能当页面的根。
1005
- *
1006
- * 能当根的形状有三种:对象段、**相交段**(共享判别字段 + 各分支的 `const` 标记,段值仍是对象)、以及带判别
1007
- * 标签的 `union`(成员都是对象)。字面量 `union`(整段就是一个标量)不行:那一行的写盘会变成整段替换。
1008
- * @param serialized - host 发来的 `schema.toJSON()`。
1009
- * @param rehydrate - host schema 的 rehydrate。
1010
- * @returns schema 与它的字段树,或 `undefined`(不能当页面根)。
1011
- */
1012
816
  function projectRoot(serialized, rehydrate) {
1013
817
  try {
1014
818
  const schema = rehydrate(serialized);
@@ -1026,16 +830,9 @@ window.__ModuleLoader__.load({
1026
830
  return;
1027
831
  }
1028
832
  }
1029
- /**
1030
- * 一行能否自动生成配置页:它的 schema 能 rehydrate 且根是 {@link projectRoot} 认的形状。
1031
- * @param view - host 发来的命名空间视图。
1032
- * @param rehydrate - host schema 的 rehydrate。
1033
- * @returns 是否可以渲染。
1034
- */
1035
833
  function canRender(view, rehydrate) {
1036
834
  return view !== void 0 && projectRoot(view.schema, rehydrate) !== void 0;
1037
835
  }
1038
- /** 段根不是可渲染对象时的空字段树。 */
1039
836
  const NO_FIELDS = {
1040
837
  key: "",
1041
838
  path: [],
@@ -1045,23 +842,11 @@ window.__ModuleLoader__.load({
1045
842
  type: "object",
1046
843
  fields: []
1047
844
  };
1048
- /** 值里已有的键(数组给索引)。 */
1049
845
  function keysOf(value) {
1050
846
  if (Array.isArray(value)) return value.map((_item, index) => String(index));
1051
847
  if (typeof value === "object" && value !== null) return Object.keys(value);
1052
848
  return [];
1053
849
  }
1054
- /**
1055
- * 一个容器当前能加什么。
1056
- *
1057
- * 对象层(含相交与 union 的选中变体)是**非必填**、值里又没有的字段——必填的已经占着行了;字典层是业务注册的
1058
- * 候选键(模式清单这类运行期数据)。数组层不在这里:它的「添加一项」是追加一个空项,没有键名可选。
1059
- * @param node - 容器节点。
1060
- * @param path - 该容器的具体路径。
1061
- * @param value - 该容器自己的值(草稿后的)。
1062
- * @param hints - 提示面(只用到候选键)。
1063
- * @returns 可添加项,顺序即 schema / 读数的顺序;列表为空时页面不给输入框。
1064
- */
1065
850
  function addableAt(node, path, value, hints) {
1066
851
  const current = variantOf(node, value);
1067
852
  if (current.type === "dict") {
@@ -1077,17 +862,6 @@ window.__ModuleLoader__.load({
1077
862
  description: field.meta.description ?? field.meta.comment
1078
863
  }] : []);
1079
864
  }
1080
- /**
1081
- * 一个字段的可选项。
1082
- *
1083
- * 字面量集合的 union(`z.union(["wal", "delete"])`)自己就带着候选;其余看提示面注册的读数——读的时候把依赖的
1084
- * 兄弟字段值喂进去,所以「选完服务商才列它有哪些模型」是投影的一部分,不是额外的一次订阅。
1085
- * @param node - 字段节点。
1086
- * @param path - 该字段的具体路径。
1087
- * @param value - 段值(草稿后的)。
1088
- * @param hints - 提示面。
1089
- * @returns 可选项;没有候选就是 `undefined`。
1090
- */
1091
865
  function optionsFor(node, path, value, hints) {
1092
866
  const current = unwrapLazy(node);
1093
867
  if (current?.type === "union" && current.choices !== void 0) return current.choices.map((choice) => ({ value: choice }));
@@ -1098,9 +872,6 @@ window.__ModuleLoader__.load({
1098
872
  const options = spec.options((relative) => readPath(value, [...parent, ...relative]));
1099
873
  return options.length === 0 ? void 0 : options;
1100
874
  }
1101
- /**
1102
- * 一行配置页的控制器。
1103
- */
1104
875
  var SchemaFormController = class {
1105
876
  #ns;
1106
877
  #deps;
@@ -1110,10 +881,6 @@ window.__ModuleLoader__.load({
1110
881
  #view;
1111
882
  #schema;
1112
883
  #root;
1113
- /**
1114
- * @param ns - settings 命名空间(行 id)。
1115
- * @param deps - 该行的共享表单与 schema 面。
1116
- */
1117
884
  constructor(ns, deps) {
1118
885
  this.#ns = ns;
1119
886
  this.#deps = deps;
@@ -1132,7 +899,6 @@ window.__ModuleLoader__.load({
1132
899
  deps.describe.ensure();
1133
900
  this.#sync();
1134
901
  }
1135
- /** 槽位渲染器要的动作与状态。 */
1136
902
  face() {
1137
903
  return {
1138
904
  hooks: { schemaForm: this.#store },
@@ -1168,20 +934,16 @@ window.__ModuleLoader__.load({
1168
934
  }
1169
935
  };
1170
936
  }
1171
- /** 本行的 settings 命名空间。 */
1172
937
  namespace() {
1173
938
  return this.#ns;
1174
939
  }
1175
- /** 外部提示(候选键这类)变了:重新投影一次,字段树与草稿都不动。 */
1176
940
  refresh() {
1177
941
  this.#store.set(this.#project());
1178
942
  }
1179
- /** 释放订阅。 */
1180
943
  dispose() {
1181
944
  for (const dispose of this.#disposers.splice(0)) dispose();
1182
945
  this.#model.dispose();
1183
946
  }
1184
- /** 重新读 schema(describe 变了):只换字段树,草稿与 store 不动。 */
1185
947
  #sync() {
1186
948
  const view = this.#deps.describe.getSnapshot().view?.namespaces.find((row) => row.ns === this.#ns);
1187
949
  this.#view = view;
@@ -1191,7 +953,6 @@ window.__ModuleLoader__.load({
1191
953
  this.#model.setRoot(projected?.root ?? NO_FIELDS);
1192
954
  this.#store.set(this.#project());
1193
955
  }
1194
- /** 整段校验:schema 先说话,再看数组的项身份有没有冲突。 */
1195
956
  #validate(deps, value) {
1196
957
  if (this.#schema !== void 0) {
1197
958
  const failure = deps.validate(this.#schema, value);
@@ -1208,7 +969,6 @@ window.__ModuleLoader__.load({
1208
969
  path: conflict.path
1209
970
  };
1210
971
  }
1211
- /** 渲染层读的一份完整投影。 */
1212
972
  #project() {
1213
973
  const fields = /* @__PURE__ */ new Map();
1214
974
  const root = this.#root;
@@ -1248,41 +1008,21 @@ window.__ModuleLoader__.load({
1248
1008
  };
1249
1009
  //#endregion
1250
1010
  //#region src/client/schema-form/hints.ts
1251
- /**
1252
- * 通用表单的提示面:业务知道、而 schema 表达不了的结构信息。
1253
- *
1254
- * 两件事:**dict 字段的候选键**(例如「会话模式」那一行,键是运行期清单里的模式 id,schema 里写不出来),以及
1255
- * **字段的候选值**(选服务商、选模型这类——候选来自部署里的另一份配置,还经常互相依赖)。业务在客户端注册一个
1256
- * 同步读数,表单一侧在投影时用它补出「未配置」行或把字段画成选择器。
1257
- *
1258
- * 读数必须是**同步**的:投影发生在渲染帧里。异步来源(HTTP / remote)由业务自己取好再注册;注册本身就当作一次
1259
- * 变更通知(所以"取到清单后再注册"是推荐用法)。
1260
- */
1261
- /** 提示面的完整服务(业务用注册侧,表单一侧用读侧)。 */
1262
1011
  var SchemaFormHints = class extends _deepseek_ai_cordis.Service {
1263
1012
  #readers = /* @__PURE__ */ new Map();
1264
1013
  #texts = /* @__PURE__ */ new Map();
1265
1014
  #selects = /* @__PURE__ */ new Map();
1266
1015
  #sources = /* @__PURE__ */ new Map();
1267
1016
  #listeners = /* @__PURE__ */ new Set();
1268
- /** @param ctx - 提供本服务的插件上下文。 */
1269
1017
  constructor(ctx) {
1270
1018
  super(ctx, "schemaFormHints");
1271
1019
  }
1272
- /** @param ns - settings 命名空间。 @param path - 字段路径。 @returns 该处的键。 */
1273
1020
  keysFor(ns, path) {
1274
1021
  return lookup(this.#readers, ns, path)?.() ?? [];
1275
1022
  }
1276
- /** @param ns - settings 命名空间。 @param path - 字段路径。 @returns 该处注册的文案。 */
1277
1023
  textFor(ns, path) {
1278
1024
  return lookup(this.#texts, ns, path)?.() ?? {};
1279
1025
  }
1280
- /**
1281
- * @param ns - settings 命名空间。
1282
- * @param path - 字段路径。
1283
- * @param spec - 候选取法与依赖。
1284
- * @returns 注销函数。
1285
- */
1286
1026
  select(ns, path, spec) {
1287
1027
  const key = locationKey(ns, path);
1288
1028
  this.#selects.set(key, spec);
@@ -1293,15 +1033,9 @@ window.__ModuleLoader__.load({
1293
1033
  this.#publish();
1294
1034
  };
1295
1035
  }
1296
- /** @param ns - settings 命名空间。 @param path - 字段路径。 @returns 该处注册的候选读数。 */
1297
1036
  selectFor(ns, path) {
1298
1037
  return lookup(this.#selects, ns, path);
1299
1038
  }
1300
- /**
1301
- * @param name - 源的名字。
1302
- * @param spec - 候选取法与依赖。
1303
- * @returns 注销函数。
1304
- */
1305
1039
  source(name, spec) {
1306
1040
  this.#sources.set(name, spec);
1307
1041
  this.#publish();
@@ -1311,16 +1045,9 @@ window.__ModuleLoader__.load({
1311
1045
  this.#publish();
1312
1046
  };
1313
1047
  }
1314
- /** @param name - 源的名字。 @returns 该源注册的候选读数。 */
1315
1048
  sourceFor(name) {
1316
1049
  return this.#sources.get(name);
1317
1050
  }
1318
- /**
1319
- * @param ns - settings 命名空间。
1320
- * @param path - 字段路径。
1321
- * @param read - 同步读数。
1322
- * @returns 注销函数。
1323
- */
1324
1051
  describe(ns, path, read) {
1325
1052
  const key = locationKey(ns, path);
1326
1053
  this.#texts.set(key, read);
@@ -1331,12 +1058,6 @@ window.__ModuleLoader__.load({
1331
1058
  this.#publish();
1332
1059
  };
1333
1060
  }
1334
- /**
1335
- * @param ns - settings 命名空间。
1336
- * @param path - 字段路径。
1337
- * @param read - 同步读数。
1338
- * @returns 注销函数。
1339
- */
1340
1061
  suggestKeys(ns, path, read) {
1341
1062
  const key = locationKey(ns, path);
1342
1063
  this.#readers.set(key, read);
@@ -1347,20 +1068,12 @@ window.__ModuleLoader__.load({
1347
1068
  this.#publish();
1348
1069
  };
1349
1070
  }
1350
- /**
1351
- * 观察候选键的变化(注册与注销)。
1352
- * @param listener - 变更回调。
1353
- * @returns 取消订阅。
1354
- */
1355
1071
  subscribe(listener) {
1356
1072
  this.#listeners.add(listener);
1357
1073
  return () => {
1358
1074
  this.#listeners.delete(listener);
1359
1075
  };
1360
1076
  }
1361
- /**
1362
- * 重新通知一次:读数背后的数据变了(业务自己知道),页面据此重算候选行。
1363
- */
1364
1077
  refresh() {
1365
1078
  this.#publish();
1366
1079
  }
@@ -1368,19 +1081,9 @@ window.__ModuleLoader__.load({
1368
1081
  for (const listener of this.#listeners) listener();
1369
1082
  }
1370
1083
  };
1371
- /** 一处字段的定位键(命名空间 + 路径)。 */
1372
1084
  function locationKey(ns, path) {
1373
1085
  return `${ns}\u0000${path.join("\0")}`;
1374
1086
  }
1375
- /**
1376
- * 按具体路径找一处注册:先精确匹配,再试**模板变体**(动态键 / 数组下标的段写成 `*`)。
1377
- *
1378
- * 业务因此可以注册一次 `models.*.provider` 就覆盖每个模式,不必知道运行期有哪些键。
1379
- * @param map - 某类注册表。
1380
- * @param ns - settings 命名空间。
1381
- * @param path - 具体路径。
1382
- * @returns 命中的注册,或 `undefined`。
1383
- */
1384
1087
  function lookup(map, ns, path) {
1385
1088
  const exact = map.get(locationKey(ns, path));
1386
1089
  if (exact !== void 0) return exact;
@@ -1389,19 +1092,12 @@ window.__ModuleLoader__.load({
1389
1092
  if (hit !== void 0) return hit;
1390
1093
  }
1391
1094
  }
1392
- /** 具体路径的模板变体(首段是顶层字段名,保持原样):通配得多的先试。 */
1393
1095
  function* templates(path) {
1394
1096
  const wildcardable = Math.max(0, path.length - 1);
1395
1097
  for (let bits = (1 << wildcardable) - 1; bits > 0; bits--) yield path.map((segment, index) => index > 0 && (bits & 1 << index - 1) !== 0 ? "*" : segment);
1396
1098
  }
1397
1099
  //#endregion
1398
1100
  //#region src/client/schema-form/locales.ts
1399
- /**
1400
- * 本包字典(命名空间 `settings.schema-form`):表单壳、字段控件与只读呈现的文案。
1401
- *
1402
- * 字段自身的人话标签不在这里(那是业务方经字段槽或 schema `description` 给的),这里只放**机制**文案。
1403
- */
1404
- /** 中文文案(部署语言偏好是 zh)。 */
1405
1101
  const zh = {
1406
1102
  unavailable: "该插件当前未加载,暂时无法配置。",
1407
1103
  readOnly: "本部署的设置为只读。",
@@ -1456,7 +1152,6 @@ window.__ModuleLoader__.load({
1456
1152
  duplicateItem: "重复的 {key}:{value}",
1457
1153
  missingItem: "每一项都需要一个 {key}"
1458
1154
  };
1459
- /** 英文文案。 */
1460
1155
  const en = {
1461
1156
  unavailable: "This plugin is not loaded, so it cannot be configured right now.",
1462
1157
  readOnly: "This deployment stores settings read-only.",
@@ -1513,12 +1208,6 @@ window.__ModuleLoader__.load({
1513
1208
  };
1514
1209
  //#endregion
1515
1210
  //#region src/client/schema-form/rows.ts
1516
- /**
1517
- * 配对命名空间与 bundle 行清单。
1518
- * @param namespaces - host 发来的命名空间视图。
1519
- * @param bundles - host 的 bundle 清单(含各 bundle 声明的行 id)。
1520
- * @returns 该自动注册的行,按命名空间视图的顺序。
1521
- */
1522
1211
  function rowRegistrations(namespaces, bundles) {
1523
1212
  const owners = /* @__PURE__ */ new Map();
1524
1213
  for (const bundle of bundles) for (const row of bundle.rows) owners.set(row.rowId, bundle.name);
@@ -1534,28 +1223,15 @@ window.__ModuleLoader__.load({
1534
1223
  }
1535
1224
  return plans;
1536
1225
  }
1537
- /** 自动项的兜底优先级:手写卡片(默认 0)遮住它。 */
1538
1226
  const FALLBACK_PRIORITY = 100;
1539
- /**
1540
- * 自动注册的差分器:按 describe 与 bundle 清单把「该有的行」与「已在的行」对齐。
1541
- */
1542
1227
  var RowRegistration = class {
1543
1228
  #deps;
1544
1229
  #live = /* @__PURE__ */ new Map();
1545
1230
  #pending;
1546
1231
  #disposed = false;
1547
- /**
1548
- * @param deps - 槽位、清单读面与每行的槽项工厂。
1549
- */
1550
1232
  constructor(deps) {
1551
1233
  this.#deps = deps;
1552
1234
  }
1553
- /**
1554
- * 对齐一次:新增该有的行,注销不该有的行。
1555
- *
1556
- * 并发调用折叠成一次(bundle 清单只读一趟),后到的调用等同一趟结束。
1557
- * @returns 对齐完成。
1558
- */
1559
1235
  sync() {
1560
1236
  if (this.#disposed) return Promise.resolve();
1561
1237
  this.#pending ??= this.#run().finally(() => {
@@ -1563,7 +1239,6 @@ window.__ModuleLoader__.load({
1563
1239
  });
1564
1240
  return this.#pending;
1565
1241
  }
1566
- /** 收掉自己:注销还活着的全部注册并释放它们的控制器。 */
1567
1242
  dispose() {
1568
1243
  this.#disposed = true;
1569
1244
  this.#release();
@@ -1596,7 +1271,6 @@ window.__ModuleLoader__.load({
1596
1271
  this.#live.set(ns, dispose);
1597
1272
  }
1598
1273
  }
1599
- /** 注销全部并释放控制器。 */
1600
1274
  #release() {
1601
1275
  for (const [ns, dispose] of this.#live) {
1602
1276
  dispose();
@@ -1607,7 +1281,6 @@ window.__ModuleLoader__.load({
1607
1281
  };
1608
1282
  //#endregion
1609
1283
  //#region src/client/schema-form/labels.ts
1610
- /** 字段的说明(`description` 本地化后 + `comment`),没有就是 undefined。 */
1611
1284
  function fieldHint(node, resolveText) {
1612
1285
  const parts = [];
1613
1286
  if (node.meta.description !== void 0) parts.push(resolveText(node.meta.description));
@@ -1616,21 +1289,6 @@ window.__ModuleLoader__.load({
1616
1289
  }
1617
1290
  //#endregion
1618
1291
  //#region src/client/schema-form/lines.ts
1619
- /**
1620
- * 行模型:把字段树(`walkFields` 的结果)排成**编辑器的行**——结构行(`{` / `[` / `}` / `]`)、字段行、注释行、
1621
- * 以及容器尾部的添加行。
1622
- *
1623
- * 渲染层因此只按行画,不必自己递归:行序 = 视觉顺序,行号连续。折叠在这里生效(折叠的容器只留开启行)。
1624
- */
1625
- /**
1626
- * 容器的形状:对象类用 `{}`,序列类用 `[]`。
1627
- *
1628
- * union 没有自己的形状——它跟着**当前选中那一支**走(`access` 是数组就在页面上画 `[`),这也是
1629
- * `access: string | string[]` 这类字段以前被画成对象容器的原因。
1630
- * @param node - 字段节点。
1631
- * @param value - 这一层的值(选支判据)。
1632
- * @returns 形状;叶子节点是 `undefined`。
1633
- */
1634
1292
  function containerShape(node, value) {
1635
1293
  switch (variantOf(node, value).type) {
1636
1294
  case "object":
@@ -1641,7 +1299,6 @@ window.__ModuleLoader__.load({
1641
1299
  default: return;
1642
1300
  }
1643
1301
  }
1644
- /** 渲染顺序里可见的字段:折叠的容器把整棵子树滤掉,只留它自己的开启行。 */
1645
1302
  function visibleFields(state, fold) {
1646
1303
  const containers = new Set(state.walked.filter((item) => containerShape(item.node, valueAt(state, item.path)) !== void 0).map((item) => fieldKey(item.path)));
1647
1304
  return state.walked.filter((item) => {
@@ -1652,11 +1309,9 @@ window.__ModuleLoader__.load({
1652
1309
  return true;
1653
1310
  });
1654
1311
  }
1655
- /** 这一层的当前值:与字段行显示的是同一份读数(草稿优先)。 */
1656
1312
  function valueAt(state, path) {
1657
1313
  return state.fields.get(fieldKey(path))?.value;
1658
1314
  }
1659
- /** 这一层能加什么:数组追加空项,字典敲键名(可来自候选),对象从还没配的声明字段里选。 */
1660
1315
  function addOf(node, shape, options) {
1661
1316
  if (shape === "array") return { kind: "item" };
1662
1317
  if (unwrapLazy(node)?.type === "dict") return {
@@ -1668,7 +1323,6 @@ window.__ModuleLoader__.load({
1668
1323
  options
1669
1324
  };
1670
1325
  }
1671
- /** 一个节点上的变体切换控件:不是 union、或只有一支、或成员全是常量(字面量集合是选择器)时没有。 */
1672
1326
  function variantsOf(node, value) {
1673
1327
  const union = unionOf(node);
1674
1328
  if (union === void 0 || union.choices !== void 0 || union.variants.length < 2) return void 0;
@@ -1677,14 +1331,6 @@ window.__ModuleLoader__.load({
1677
1331
  selected: union.variants.indexOf(variantOf(union, value))
1678
1332
  };
1679
1333
  }
1680
- /**
1681
- * 排成行序列。
1682
- * @param state - 页面读数。
1683
- * @param fold - 折叠状态。
1684
- * @param resolveText - `description` 的本地化。
1685
- * @param t - 本包字典。
1686
- * @returns 行序列(行号即下标)。
1687
- */
1688
1334
  function editorLines(state, fold, resolveText, t) {
1689
1335
  const lines = [];
1690
1336
  const open = [];
@@ -1773,10 +1419,6 @@ window.__ModuleLoader__.load({
1773
1419
  }
1774
1420
  return moveVariantControlToTag(lines);
1775
1421
  }
1776
- /**
1777
- * 判别式 union 的切换控件挪到**标签字段那一行**上:`session-rdb` 的页面上用户改的就是 `type`,把它放在容器
1778
- * 行上等于让人在两处看同一件事。没有标签字段(`access` 这种按值形状选的)就留在 union 自己那一行。
1779
- */
1780
1422
  function moveVariantControlToTag(lines) {
1781
1423
  for (const line of lines) {
1782
1424
  if (line.kind === "comment" || line.kind === "close") continue;
@@ -1792,12 +1434,6 @@ window.__ModuleLoader__.load({
1792
1434
  }
1793
1435
  return lines;
1794
1436
  }
1795
- /**
1796
- * 一行的注释:业务给的文案(字段槽的 label/hint)优先,其次 schema 的说明。
1797
- *
1798
- * 这一行有问题时**注释位让给那条消息**(参考实现也是把错误画在行尾那一段的位置上)——说明换成"哪里不对",
1799
- * 错误因此不必挤在值后面。
1800
- */
1801
1437
  function commentFor(state, item, resolveText, t) {
1802
1438
  const key = fieldKey(item.path);
1803
1439
  const failure = state.fields.get(key)?.invalid ?? state.invalidAt.get(key);
@@ -1817,7 +1453,6 @@ window.__ModuleLoader__.load({
1817
1453
  invalid: false
1818
1454
  };
1819
1455
  }
1820
- /** `path` 是不是 `prefix` 的后代(含自身)。 */
1821
1456
  function isPrefix(prefix, path) {
1822
1457
  if (prefix.length > path.length) return false;
1823
1458
  return prefix.every((segment, index) => path[index] === segment);
@@ -2100,10 +1735,6 @@ window.__ModuleLoader__.load({
2100
1735
  //#endregion
2101
1736
  //#region src/client/styling/styled.tsx
2102
1737
  const sxSymbol = Symbol("styled.sx");
2103
- /**
2104
- * `styled('div')({ padding: '4px' }, { '&:hover': { … } })` → 带 `data-css-*` 的组件。
2105
- * 多个样式对象按顺序深合并(后者覆盖前者),便于把变体叠在基样式上。
2106
- */
2107
1738
  function styled(Component, defaultProps = {}) {
2108
1739
  return (...styles) => {
2109
1740
  const sx = styles.reduce((merged, style) => toMerged(merged, style), {});
@@ -2120,19 +1751,11 @@ window.__ModuleLoader__.load({
2120
1751
  return Object.assign(Styled, { [sxSymbol]: sx });
2121
1752
  };
2122
1753
  }
2123
- /** 取回 styled 组件携带的样式对象(需要在其之上继续组合时用)。 */
2124
1754
  function styleOf(component) {
2125
1755
  return component[sxSymbol];
2126
1756
  }
2127
1757
  //#endregion
2128
1758
  //#region src/client/theme.generated.ts
2129
- /**
2130
- * 官方主题的 `--dsw-*` token 全集(400 个),由 packages/client/ui-primitives/scripts/gen-design-tokens.mts 生成,勿手改。
2131
- *
2132
- * 叶子是该变量的**默认值**(官方 light 主题里首次出现的定义):既是类型推导的来源,也可作 fallback。
2133
- * `"$"` 键出现在「自身也是 token 的分支」上(命名体系里有 40 个短名同时是长名前缀的 token)。
2134
- * 消费时只需树形状——`Token.vars()` 生成 `var(--dsw-…)`,不重新定义变量。
2135
- * 漂移、往返与默认值完整性由 src/__tests__/design-tokens.spec.ts 守卫。 */
2136
1759
  const designTokens = {
2137
1760
  alias: {
2138
1761
  bg: {
@@ -2808,36 +2431,18 @@ window.__ModuleLoader__.load({
2808
2431
  const dsw = Token.vars(designTokens, { prefix: "dsw" });
2809
2432
  //#endregion
2810
2433
  //#region src/client/schema-form/styles.ts
2811
- /**
2812
- * 行式编辑器的样式:等宽、行号、语法色、悬停行为——全部消费官方 `--dsw-*` token。
2813
- *
2814
- * 几何只有一套:行高、行内间距、动作按钮尺寸都由下面几个常量给,行与行之间不各自决定。
2815
- */
2816
- /** 等宽字体栈:配置值的观感与代码一致。 */
2817
2434
  const MONO = "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace";
2818
- /** 行高:行内容、行内输入、折叠箭头、按钮排都在这一档上对齐。 */
2819
2435
  const ROW = "22px";
2820
- /** 行内元素之间的间距。 */
2821
2436
  const GAP = "6px";
2822
- /** 动作按钮:方形图标键,尺寸与行高同一档的紧凑版。 */
2823
2437
  const ACTION = "20px";
2824
- /** 动作按钮之间的间距(比行内间距紧一档)。 */
2825
2438
  const ACTION_GAP = "4px";
2826
- /** 折叠列(chevron 与它的占位)宽度。 */
2827
2439
  const FOLD = "14px";
2828
- /** 行号列宽度。 */
2829
2440
  const NUMBER = "32px";
2830
- /** 行尾留白。 */
2831
2441
  const PAD = "8px";
2832
- /** 细线:theme 给的 0.5px 描边(`0 0 0 0.5px var(--dsw-elevation-stroke-color)`)。 */
2833
2442
  const HAIRLINE = dsw.elevation.stroke;
2834
- /** 描边色:theme 里给描边用的那一档(与官方 `Menu` 面板同源)。 */
2835
2443
  const LINE = dsw.elevation.stroke.color;
2836
- /** 行左侧那道「改过」的标记宽度。 */
2837
2444
  const EDGE = "2px";
2838
- /** 出错那一行的底色:theme 的红掺进透明里(半透明红),比整行实红轻、又比只有一道条明显。 */
2839
2445
  const ERROR_WASH = `color-mix(in srgb, ${String(dsw.alias.state.error.secondary)} 12%, transparent)`;
2840
- /** 编辑器的根:纵向排列的行。 */
2841
2446
  const EditorRoot = styled("div")({
2842
2447
  display: "flex",
2843
2448
  flexDirection: "column",
@@ -2847,7 +2452,6 @@ window.__ModuleLoader__.load({
2847
2452
  lineHeight: ROW,
2848
2453
  color: dsw.alias.label.primary
2849
2454
  });
2850
- /** 一行:行号 + 折进 + 行内容。悬停给一层浅底。 */
2851
2455
  const LineRow = styled("div")({
2852
2456
  display: "flex",
2853
2457
  alignItems: "flex-start",
@@ -2868,7 +2472,6 @@ window.__ModuleLoader__.load({
2868
2472
  "&[data-invalid='true'] [data-role='key']": { color: dsw.alias.state.error.primary },
2869
2473
  "&:hover [data-role='actions']": { opacity: 1 }
2870
2474
  });
2871
- /** 一行的内容区:**缩进只作用在这里**——行号列固定在左边,缩进不推它。 */
2872
2475
  const LineBody = styled("div")({
2873
2476
  display: "flex",
2874
2477
  alignItems: "center",
@@ -2877,7 +2480,6 @@ window.__ModuleLoader__.load({
2877
2480
  minWidth: 0,
2878
2481
  minHeight: ROW
2879
2482
  });
2880
- /** 行号列:右对齐、不可选、淡色。 */
2881
2483
  const LineNumber = styled("span")({
2882
2484
  flex: "none",
2883
2485
  width: NUMBER,
@@ -2887,7 +2489,6 @@ window.__ModuleLoader__.load({
2887
2489
  userSelect: "none",
2888
2490
  cursor: "pointer"
2889
2491
  });
2890
- /** 折叠 chevron(官方箭头图标,尺寸与别的行内图标同源)。 */
2891
2492
  const LineFold = styled("button")({
2892
2493
  display: "inline-flex",
2893
2494
  alignItems: "center",
@@ -2902,23 +2503,19 @@ window.__ModuleLoader__.load({
2902
2503
  cursor: "pointer",
2903
2504
  "&:hover": { color: dsw.alias.label.secondary }
2904
2505
  });
2905
- /** 叶子行的折叠占位(保持列对齐)。 */
2906
2506
  const LineFoldSpacer = styled("span")({
2907
2507
  flex: "none",
2908
2508
  width: FOLD
2909
2509
  });
2910
- /** 键名(配置键原样;数组下标淡一些)。 */
2911
2510
  const LineKey = styled("span")({
2912
2511
  flex: "none",
2913
2512
  color: dsw.alias.label.primary,
2914
2513
  "&[data-index='true']": { color: dsw.alias.label.caption }
2915
2514
  });
2916
- /** 结构符与标点(`{` `}` `[` `]` `:`)。 */
2917
2515
  const LineToken = styled("span")({
2918
2516
  flex: "none",
2919
2517
  color: dsw.alias.label.caption
2920
2518
  });
2921
- /** 值(token):字符串 / 数字 / 布尔各有语法色。 */
2922
2519
  const LineValue = styled("span")({
2923
2520
  minWidth: 0,
2924
2521
  overflow: "hidden",
@@ -2929,9 +2526,6 @@ window.__ModuleLoader__.load({
2929
2526
  "&[data-tone='boolean']": { color: dsw.alias.state.success.primary },
2930
2527
  "&[data-tone='empty']": { color: dsw.alias.label.caption }
2931
2528
  });
2932
- /**
2933
- * 值上的下拉触发:**它自己就是值的呈现**(不再是「值 + 一个下拉」两遍),点开才出菜单。
2934
- */
2935
2529
  const ValueTrigger = styled("button")({
2936
2530
  display: "inline-flex",
2937
2531
  alignItems: "center",
@@ -2947,11 +2541,6 @@ window.__ModuleLoader__.load({
2947
2541
  "&:disabled": { cursor: "default" },
2948
2542
  "&:focus-visible": { outline: "none" }
2949
2543
  });
2950
- /**
2951
- * 行内输入的外壳:官方 `Input` 压到与值同一行高,并**吃掉剩下的宽度**。
2952
- *
2953
- * 行内编辑与容器闭合行的添加输入共用它——两处的输入框因此长得一模一样。
2954
- */
2955
2544
  const CompactInput = styled("span")({
2956
2545
  display: "inline-flex",
2957
2546
  flex: "1 1 auto",
@@ -2967,10 +2556,6 @@ window.__ModuleLoader__.load({
2967
2556
  lineHeight: ROW
2968
2557
  }
2969
2558
  });
2970
- /**
2971
- * 多行值的行内输入:官方没有多行原子,所以这里用同一套观感的字段壳(同边框、同圆角、同焦点色),
2972
- * 高度跟着内容长、宽度同样撑满剩下的位置。
2973
- */
2974
2559
  const CompactTextField = styled("span")({
2975
2560
  display: "inline-flex",
2976
2561
  flex: "1 1 auto",
@@ -2998,7 +2583,6 @@ window.__ModuleLoader__.load({
2998
2583
  color: dsw.alias.label.primary
2999
2584
  }
3000
2585
  });
3001
- /** 行内注释(schema 说明与业务文案):比配置值小一档,占满这一行剩下的宽度,长了就截断(hover 看全文)。 */
3002
2586
  const LineComment = styled("span")({
3003
2587
  flex: "1 1 auto",
3004
2588
  minWidth: 0,
@@ -3008,16 +2592,12 @@ window.__ModuleLoader__.load({
3008
2592
  fontFamily: "inherit",
3009
2593
  fontSize: "12px"
3010
2594
  });
3011
- /** 行内的校验消息(与注释放同一档字号)。 */
3012
2595
  const LineInvalid = styled("span")({
3013
2596
  flex: "none",
3014
2597
  color: dsw.alias.state.error.primary,
3015
2598
  fontFamily: "inherit",
3016
2599
  fontSize: "12px"
3017
2600
  });
3018
- /**
3019
- * 悬停行时才显示这一行的动作按钮:显形规则写在行上(`[data-role='actions']`),组件只需要带这个属性。
3020
- */
3021
2601
  const HoverActions = styled(styled("span")({
3022
2602
  display: "inline-flex",
3023
2603
  alignItems: "center",
@@ -3045,12 +2625,7 @@ window.__ModuleLoader__.load({
3045
2625
  color: dsw.alias.label.dimmed
3046
2626
  }
3047
2627
  }
3048
- }))({
3049
- /**
3050
- * 编辑态的两个按钮常显,而且**要有底色与描边**:行底色上只有淡色图标是看不见的,
3051
- * 所以这里给实体按钮(theme 的描边 + 一层底),确认用成功档、取消用中性档。
3052
- */
3053
- "&[data-editing='true']": {
2628
+ }))({ "&[data-editing='true']": {
3054
2629
  opacity: 1,
3055
2630
  gap: GAP,
3056
2631
  "& > button": {
@@ -3060,7 +2635,6 @@ window.__ModuleLoader__.load({
3060
2635
  "&:first-of-type": { color: dsw.alias.state.success.primary }
3061
2636
  }
3062
2637
  } });
3063
- /** 添加输入:**跟在容器的闭合括号同一行**(菜单锚在它上面),宽度吃掉剩下的位置。 */
3064
2638
  const AddWrap = styled("span")({
3065
2639
  position: "relative",
3066
2640
  display: "inline-flex",
@@ -3071,7 +2645,6 @@ window.__ModuleLoader__.load({
3071
2645
  marginLeft: GAP,
3072
2646
  "&[data-invalid='true'] input": { borderColor: dsw.alias.state.error.primary }
3073
2647
  });
3074
- /** 一行说明(不可用 / 没有可生成项时用)。 */
3075
2648
  const Hint$1 = styled("p")({
3076
2649
  margin: 0,
3077
2650
  fontSize: "12px",
@@ -3080,42 +2653,27 @@ window.__ModuleLoader__.load({
3080
2653
  });
3081
2654
  //#endregion
3082
2655
  //#region src/client/schema-form/value.tsx
3083
- /**
3084
- * 行内值的呈现与编辑文本规则,以及字段槽注册方复用的默认值组件。
3085
- *
3086
- * 值本身仍走草稿:这个文件只决定"怎么显示"(语法色 token、下拉触发的呈现)与"怎么编辑"(一行输入框里的文本
3087
- * 形状——字符串把换行写成 `\n`,与一行的输入框相配)。
3088
- */
3089
- /** 编辑态文本:字符串就是原文(换行照旧),其余按 JSON 写。 */
3090
2656
  function valueText(value) {
3091
2657
  if (value === void 0 || value === null) return "";
3092
2658
  if (typeof value === "string") return value;
3093
2659
  if (typeof value === "number" || typeof value === "boolean") return String(value);
3094
2660
  return JSON.stringify(value);
3095
2661
  }
3096
- /** 这个值要在多行输入里编辑吗(字符串带换行)。 */
3097
2662
  function isMultiline(value) {
3098
2663
  return typeof value === "string" && /[\r\n]/.test(value);
3099
2664
  }
3100
- /** 显示态文本:字符串带引号;多行与特殊字符都按 JSON 的写法收在一行里。 */
3101
2665
  function tokenText(value) {
3102
2666
  if (value === void 0 || value === null) return "null";
3103
2667
  if (typeof value === "string") return JSON.stringify(value);
3104
2668
  if (typeof value === "number" || typeof value === "boolean") return String(value);
3105
2669
  return JSON.stringify(value);
3106
2670
  }
3107
- /** 值的语法色分组。 */
3108
2671
  function tokenTone(value) {
3109
2672
  if (value === void 0 || value === null) return "empty";
3110
2673
  if (typeof value === "number") return "number";
3111
2674
  if (typeof value === "boolean") return "boolean";
3112
2675
  return "string";
3113
2676
  }
3114
- /**
3115
- * 行内值的默认呈现(显示态 token)。
3116
- * @param props.owner - 字段上下文(值、是否 secret、只读原因)。
3117
- * @returns 一行里的值节点。
3118
- */
3119
2677
  function InlineValue({ owner }) {
3120
2678
  if (owner.options !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionSelect, {
3121
2679
  owner,
@@ -3132,11 +2690,6 @@ window.__ModuleLoader__.load({
3132
2690
  children: tokenText(owner.value)
3133
2691
  });
3134
2692
  }
3135
- /**
3136
- * 选择器:**值本身**就是下拉的触发(不再另画一个 `select` 与它并列),点开是官方菜单。
3137
- *
3138
- * 当前值不在候选里时(例如目录里已经没有那个服务商)照原样显示,选中才换成新值。
3139
- */
3140
2693
  function OptionSelect({ owner, options }) {
3141
2694
  const [open, setOpen] = (0, react.useState)(false);
3142
2695
  const selected = options.findIndex((option) => sameValue(option.value, owner.value));
@@ -3175,31 +2728,16 @@ window.__ModuleLoader__.load({
3175
2728
  })
3176
2729
  });
3177
2730
  }
3178
- /** 值是否就是这一项(菜单的高亮判据)。 */
3179
2731
  function sameValue(left, right) {
3180
2732
  if (Object.is(left, right)) return true;
3181
2733
  if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return false;
3182
2734
  return JSON.stringify(left) === JSON.stringify(right);
3183
2735
  }
3184
- /**
3185
- * 字段槽注册方复用的默认值:只改文案时拿它包一层即可(`owner` 上覆盖 `label`/`hint`)。
3186
- *
3187
- * 注意行式视图里键名来自配置键本身、注释来自 `hint`,所以 `label` 只在业务自写控件时才需要。
3188
- */
3189
2736
  function SchemaFieldDefault({ owner }) {
3190
2737
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineValue, { owner });
3191
2738
  }
3192
2739
  //#endregion
3193
2740
  //#region src/client/schema-form/fields.tsx
3194
- /**
3195
- * 各类型的文本解析规则(消息来自本包字典)。
3196
- *
3197
- * 解析不只看类型:schema 上声明的界限(`pattern` / `min` / `max` / `step`)与字面量集合都在这里说话——
3198
- * 用户敲完当场就知道对不对,而不是等保存时被整段校验退回。
3199
- * @param node - 字段节点(`meta` 是约束的来源)。
3200
- * @param t - 本包字典。
3201
- * @returns 这段文本的解析规则。
3202
- */
3203
2741
  function parseFor(node, t) {
3204
2742
  switch (node.type) {
3205
2743
  case "number": return (text) => {
@@ -3313,20 +2851,6 @@ window.__ModuleLoader__.load({
3313
2851
  }
3314
2852
  //#endregion
3315
2853
  //#region src/client/schema-form/Editor.tsx
3316
- /**
3317
- * 行式编辑器:按 `editorLines` 排出的行画 JSON 结构视图——行号、折进、`key: value`、结构行、注释行、容器闭合行
3318
- * 后面的添加入口;值在行内点开编辑(Enter 提交 / Esc 撤销)。
3319
- *
3320
- * 缩进只作用在**行内容**上(行号列固定),添加输入与闭合括号同一行;行内控件用官方的 `Input` / `Menu` / `Button`,
3321
- * 与设置页其余部分同一套视觉与键盘行为。
3322
- *
3323
- * 字段槽仍然生效:命中时该字段**值**的位置换成注册方的组件,键名、注释、行号与行为按钮仍由这里画。
3324
- */
3325
- /**
3326
- * 画一行的配置。
3327
- * @param props - 读数、动作、字典与字段槽入口。
3328
- * @returns 行式编辑器。
3329
- */
3330
2854
  function Editor(props) {
3331
2855
  const { ns, state, face, t, resolveText, renderField } = props;
3332
2856
  const [toggled, setToggled] = (0, react.useState)(() => /* @__PURE__ */ new Set());
@@ -3377,7 +2901,6 @@ window.__ModuleLoader__.load({
3377
2901
  }, `${line.kind}:${fieldKey(line.path)}:${String(index)}`))
3378
2902
  });
3379
2903
  }
3380
- /** 折叠状态:记「用户切换过的路径」,开合 = 切换过就反转 schema 的默认值。 */
3381
2904
  function useFold(state, toggled, setToggled) {
3382
2905
  const defaults = (0, react.useMemo)(() => {
3383
2906
  const collapsed = /* @__PURE__ */ new Set();
@@ -3405,7 +2928,6 @@ window.__ModuleLoader__.load({
3405
2928
  toggled
3406
2929
  ]);
3407
2930
  }
3408
- /** 一行的渲染分派。 */
3409
2931
  function Row({ line, n, ns, state, face, fold, t, resolveText, disabled, editing, selected, dismissed, onEdit, onAdded, onDismiss, onSelect, renderField }) {
3410
2932
  const key = fieldKey(line.path);
3411
2933
  const indent = { paddingLeft: `${String(line.depth * 14)}px` };
@@ -3485,7 +3007,6 @@ window.__ModuleLoader__.load({
3485
3007
  })]
3486
3008
  });
3487
3009
  }
3488
- /** 字段行:`key: value`(数组成员的键名是下标,淡一些)。 */
3489
3010
  function FieldLine({ line, ns, state, face, t, disabled, editing, dismissed, onEdit, onDismiss, renderField }) {
3490
3011
  const key = fieldKey(line.path);
3491
3012
  const member = line.member;
@@ -3642,11 +3163,9 @@ window.__ModuleLoader__.load({
3642
3163
  })
3643
3164
  ] });
3644
3165
  }
3645
- /** 复制用的文本(字符串带引号,粘到哪里都是合法片段)。 */
3646
3166
  function tokenFor(value) {
3647
3167
  return typeof value === "string" ? JSON.stringify(value) : JSON.stringify(value ?? null);
3648
3168
  }
3649
- /** 变体切换:判别式 union 挂在标签行上,按形状选的挂在字段行上。触发元素就是当前那一支。 */
3650
3169
  function VariantSelect({ control, path, face, disabled, t }) {
3651
3170
  const [open, setOpen] = (0, react.useState)(false);
3652
3171
  if (control === void 0) return null;
@@ -3685,7 +3204,6 @@ window.__ModuleLoader__.load({
3685
3204
  })
3686
3205
  });
3687
3206
  }
3688
- /** 开启行的前缀:字段键名(对象里的一层)或数组成员的下标;成员行另外带移除。 */
3689
3207
  function LinePrefix({ line, face, t, disabled }) {
3690
3208
  const member = line.member;
3691
3209
  const key = member?.key ?? line.node.key;
@@ -3710,7 +3228,6 @@ window.__ModuleLoader__.load({
3710
3228
  })
3711
3229
  ] });
3712
3230
  }
3713
- /** 成员行的移除(悬停时出现):数组项按索引、字典键按键名。 */
3714
3231
  function RemoveLineButton({ member, face, t, disabled }) {
3715
3232
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3716
3233
  type: "button",
@@ -3724,12 +3241,6 @@ window.__ModuleLoader__.load({
3724
3241
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 14 })
3725
3242
  });
3726
3243
  }
3727
- /**
3728
- * 容器闭合行后面的添加入口。
3729
- *
3730
- * 数组是「追加一个空项」;对象/字典给出**候选**——对象列的是 schema 声明了、值里还没有的字段(带说明),
3731
- * 字典列的是业务注册的候选键(还能自己敲键名)。粘一整段 `{…}` / `[…]` 则整层覆盖。
3732
- */
3733
3244
  function AddLine({ line, state, face, t, resolveText, disabled, onAdded }) {
3734
3245
  const spec = line.add;
3735
3246
  const [text, setText] = (0, react.useState)("");
@@ -3824,17 +3335,6 @@ window.__ModuleLoader__.load({
3824
3335
  }
3825
3336
  //#endregion
3826
3337
  //#region src/client/schema-form/SchemaForm.tsx
3827
- /**
3828
- * 一行的配置页:官方表单壳(保存 / 丢弃 / 只读 / 不可用)+ 行式 JSON 结构编辑器 + 字段槽。
3829
- *
3830
- * 每个可渲染的行注册项经 `renderFactorySlot('settings.schema-form.form', …)` 渲染这个 Factory,字段槽因此只需在
3831
- * Factory 的 `children` 里声明一次(slots 的 children 声明 per key 唯一,而本包给每个行都注册了一项)。
3832
- */
3833
- /**
3834
- * 渲染一行的配置页。
3835
- * @param props - 行 id、控制器面、本地化解析与框架注入的槽位渲染座位。
3836
- * @returns 表单壳里的行式编辑器,或「没有可自动生成的配置项」一行。
3837
- */
3838
3338
  function SchemaForm(props) {
3839
3339
  const { ns, face, t, resolveText, renderSlotChain } = props;
3840
3340
  const store = face.hooks.schemaForm;
@@ -3876,7 +3376,6 @@ window.__ModuleLoader__.load({
3876
3376
  })]
3877
3377
  });
3878
3378
  }
3879
- /** 文本草稿的解析规则按字段类型给:行内编辑只上报原文,值在保存那一刻成形。 */
3880
3379
  function withParse(owner, t) {
3881
3380
  return (text, parse) => {
3882
3381
  owner.onEditText(text, parse ?? parseFor(owner.node, t));
@@ -3907,9 +3406,7 @@ window.__ModuleLoader__.load({
3907
3406
  visibleFields: () => visibleFields,
3908
3407
  walkFields: () => walkFields
3909
3408
  });
3910
- /** 本包字典与槽位文案的命名空间。 */
3911
3409
  const NS = "settings.schema-form";
3912
- /** 需要的服务:槽位、字典、共享配置表单、schema 服务与插件管理(bundle 行清单)。 */
3913
3410
  const inject$1 = [
3914
3411
  "slots",
3915
3412
  "locale",
@@ -3918,10 +3415,6 @@ window.__ModuleLoader__.load({
3918
3415
  "remote",
3919
3416
  "remote.pluginManager"
3920
3417
  ];
3921
- /**
3922
- * 装上本包:字典、Factory(字段槽的声明者)与按行自动注册。
3923
- * @param ctx - 浏览器插件上下文。
3924
- */
3925
3418
  function apply$1(ctx) {
3926
3419
  ctx.effect(() => ctx.locale.register(NS, {
3927
3420
  zh,
@@ -4030,26 +3523,6 @@ window.__ModuleLoader__.load({
4030
3523
  }
4031
3524
  //#endregion
4032
3525
  //#region src/client/settings-form/form-model.ts
4033
- /**
4034
- * The staged form model behind a plugin's settings page.
4035
- *
4036
- * A card stages what the user types and writes it only when they save. Each
4037
- * settings write is a durable, revision-fenced document mutation, so a control
4038
- * that committed as it settled turned one edit into a write the user never
4039
- * asked for and could not preview; staged text makes what is on screen exactly
4040
- * what a save would store.
4041
- *
4042
- * A field shows its effective value — the user layer over the composition
4043
- * layer over the schema default — and whether the user layer carries it. That
4044
- * presence, not a value comparison, is what marks a field overridden: an
4045
- * override equal to the composition default is still an override.
4046
- */
4047
- /**
4048
- * A whole-number field. An empty draft clears the field; any other draft that
4049
- * is not a finite number blocks the save.
4050
- * @param field - field name inside the namespace section.
4051
- * @returns the field's conversion spec.
4052
- */
4053
3526
  function settingsNumberField(field) {
4054
3527
  return {
4055
3528
  field,
@@ -4065,12 +3538,6 @@ window.__ModuleLoader__.load({
4065
3538
  }
4066
3539
  };
4067
3540
  }
4068
- /**
4069
- * A free-text field. An empty draft clears the field, so emptying the control
4070
- * and saving is the same gesture as resetting it.
4071
- * @param field - field name inside the namespace section.
4072
- * @returns the field's conversion spec.
4073
- */
4074
3541
  function settingsTextField(field) {
4075
3542
  return {
4076
3543
  field,
@@ -4084,13 +3551,6 @@ window.__ModuleLoader__.load({
4084
3551
  }
4085
3552
  };
4086
3553
  }
4087
- /**
4088
- * Stages one card's edits over one settings namespace and writes them on save.
4089
- *
4090
- * The form publishes through a snapshot store because slot components read
4091
- * through a snapshot selector, while both the scope and the local drafts
4092
- * change underneath; every projection is rebuilt from the two together.
4093
- */
4094
3554
  var SettingsFormModel = class {
4095
3555
  scope;
4096
3556
  specs;
@@ -4101,11 +3561,6 @@ window.__ModuleLoader__.load({
4101
3561
  unsubscribe;
4102
3562
  saving = false;
4103
3563
  failed = false;
4104
- /**
4105
- * @param scope - the shared configuration form for this card's namespace.
4106
- * @param specs - the section fields this card edits.
4107
- * @param secrets - the card's write-only controls, written outside the section.
4108
- */
4109
3564
  constructor(scope, specs, secrets = []) {
4110
3565
  this.scope = scope;
4111
3566
  this.specs = new Map(specs.map((spec) => [spec.field, spec]));
@@ -4114,11 +3569,6 @@ window.__ModuleLoader__.load({
4114
3569
  this.publish();
4115
3570
  });
4116
3571
  }
4117
- /**
4118
- * Publish a projection of this form, rebuilt whenever the scope or a draft changes.
4119
- * @param project - build the card's state from the form's current reads.
4120
- * @returns the store the card's component reads through its bound selector.
4121
- */
4122
3572
  bind(project) {
4123
3573
  const store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(project());
4124
3574
  this.listeners.add(() => {
@@ -4126,10 +3576,6 @@ window.__ModuleLoader__.load({
4126
3576
  });
4127
3577
  return store;
4128
3578
  }
4129
- /**
4130
- * Read the card-level state: what the Host serves, and what a save would do.
4131
- * @returns the form state every card shares.
4132
- */
4133
3579
  shell() {
4134
3580
  const snapshot = this.scope.getSnapshot();
4135
3581
  const plan = this.plan();
@@ -4142,11 +3588,6 @@ window.__ModuleLoader__.load({
4142
3588
  failed: this.failed
4143
3589
  };
4144
3590
  }
4145
- /**
4146
- * Read one control's state.
4147
- * @param field - field name of a section field or of a write-only control.
4148
- * @returns the draft text, whether a save would leave an override, and whether it is invalid.
4149
- */
4150
3591
  field(field) {
4151
3592
  const staged = this.staged.get(field);
4152
3593
  if (this.secretSpecs.has(field)) return {
@@ -4167,10 +3608,6 @@ window.__ModuleLoader__.load({
4167
3608
  invalid: write === void 0
4168
3609
  };
4169
3610
  }
4170
- /**
4171
- * Build the edit, reset, save, and discard actions bound to this form.
4172
- * @returns the actions a card's slot entry injects.
4173
- */
4174
3611
  actions() {
4175
3612
  return {
4176
3613
  edit: (field, text) => {
@@ -4197,15 +3634,6 @@ window.__ModuleLoader__.load({
4197
3634
  }
4198
3635
  };
4199
3636
  }
4200
- /**
4201
- * Write every staged edit, then re-seed from what the Host accepted.
4202
- *
4203
- * The Host is the only authority on whether a value was accepted — its
4204
- * validators own the constraints no schema can express — so the outcome is
4205
- * read back from the section rather than predicted here. A save that did not
4206
- * land keeps its drafts, so the user can correct them instead of retyping.
4207
- * @returns settlement after every write and the read-back.
4208
- */
4209
3637
  async save() {
4210
3638
  const plan = this.plan();
4211
3639
  if (!plan.length || this.saving || !this.scope.getSnapshot().writable || plan.some((item) => item.run === void 0 && item.op === void 0)) return;
@@ -4232,17 +3660,10 @@ window.__ModuleLoader__.load({
4232
3660
  this.publish();
4233
3661
  }
4234
3662
  }
4235
- /** Release the form's accepted-value subscription. */
4236
3663
  dispose() {
4237
3664
  this.unsubscribe();
4238
3665
  this.listeners.clear();
4239
3666
  }
4240
- /**
4241
- * Every staged edit a save would write. An entry whose draft is not a value
4242
- * its field accepts carries no write: the form is still dirty, and the save
4243
- * refuses rather than dropping the edit.
4244
- * @returns the planned writes, in the order the fields were staged.
4245
- */
4246
3667
  plan() {
4247
3668
  const plan = [];
4248
3669
  for (const [field, staged] of this.staged) {
@@ -4469,7 +3890,6 @@ window.__ModuleLoader__.load({
4469
3890
  const Input = styled("input")(styles$1.input);
4470
3891
  const Invalid = styled("p")(styles$1.invalid);
4471
3892
  const Hint = styled("p")(styles$1.hint);
4472
- /** 上游 icons 里的 IconInfoOutlineRegular(fill-only 几何,整份图标表不搬)。 */
4473
3893
  function InfoGlyph({ size }) {
4474
3894
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
4475
3895
  width: size,
@@ -4494,13 +3914,6 @@ window.__ModuleLoader__.load({
4494
3914
  ]
4495
3915
  });
4496
3916
  }
4497
- /**
4498
- * A staged value field. `numeric` only hints the keypad: which drafts a field
4499
- * accepts is decided by its spec, so the control never silently rewrites what
4500
- * the user typed.
4501
- * @param props - the field's copy, its staged text, and the edit actions.
4502
- * @returns the labelled control.
4503
- */
4504
3917
  function SettingsValueField(props) {
4505
3918
  const [helpOpen, setHelpOpen] = (0, react.useState)(false);
4506
3919
  const helpId = `${props.id}-help`;
@@ -4554,14 +3967,6 @@ window.__ModuleLoader__.load({
4554
3967
  }) : null
4555
3968
  ] });
4556
3969
  }
4557
- /**
4558
- * A write-only credential control. The value never rides a response, so the
4559
- * control reports only whether one is configured and starts blank; a blank
4560
- * draft writes nothing, which keeps the stored key rather than clearing it.
4561
- * 控件要求浏览器不要把已存的登录密码自动填进来(上游 11c0511271)。
4562
- * @param props - the field's copy, its staged text, and the configured state.
4563
- * @returns the labelled control.
4564
- */
4565
3970
  function SettingsSecretField(props) {
4566
3971
  const Badge = props.configured ? NeutralBadge : QuietBadge;
4567
3972
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, { children: [
@@ -4635,11 +4040,6 @@ window.__ModuleLoader__.load({
4635
4040
  const Footer = styled("div")(styles.footer);
4636
4041
  const Failed = styled("p")(styles.failed);
4637
4042
  const Save = styled("button")(styles.save);
4638
- /**
4639
- * Render one plugin's settings form.
4640
- * @param props - the form's copy and state, its controls, and the save and discard actions.
4641
- * @returns the form, or the unavailable line while the namespace is not served.
4642
- */
4643
4043
  function SettingsForm(props) {
4644
4044
  const { state, labels } = props;
4645
4045
  const discard = (0, react.useRef)(props.onDiscard);
@@ -17492,7 +16892,6 @@ window.__ModuleLoader__.load({
17492
16892
  ...column === void 0 ? {} : { column: Number(column) }
17493
16893
  };
17494
16894
  }
17495
- /** 引号 mention 的起手字符:`@"path with spaces"`。 */
17496
16895
  const QUOTED_PATH = "\"";
17497
16896
  function parseQuotedReference(body) {
17498
16897
  const end = body.indexOf(QUOTED_PATH, 1);
@@ -17629,15 +17028,6 @@ window.__ModuleLoader__.load({
17629
17028
  }
17630
17029
  //#endregion
17631
17030
  //#region src/client/index.ts
17632
- /**
17633
- * 需要的**服务名**:样式与引用那半不要服务,schema 表单那半要槽位、字典与配置表单。
17634
- *
17635
- * 与包清单里的 `dsh.client.inject` 不是一回事:那份是**装配行 id**(谁先到),交给模块系统排 arrival 顺序;
17636
- * 这里读的是 cordis 服务名(谁已 provide),Loader 拿它决定这一行何时才算 active。写错成包名,这一行就永远
17637
- * pending(`web boot: ... waiting for services`)。
17638
- *
17639
- * 值直接取自 schema 表单那一面,不另抄一份清单。
17640
- */
17641
17031
  const inject = [...inject$1];
17642
17032
  function apply(ctx) {
17643
17033
  apply$1(ctx);