@mhfire/dsh-im-bridge 0.3.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -7,7 +7,7 @@ window.__ModuleLoader__.load({
7
7
  let react = require("react");
8
8
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
- let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
10
+ let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
11
11
  //#region \0dsh-css:C:\Users\user\Desktop\dsh-im-bridge\plugin\src\client\PluginCard.module.css.mjs
12
12
  const css$1 = ".yj1zEa_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.yj1zEa_card:hover{border-color:var(--dsw-alias-label-dimmed)}.yj1zEa_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.yj1zEa_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.yj1zEa_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.yj1zEa_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.yj1zEa_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.yj1zEa_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.yj1zEa_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.yj1zEa_chevronOpen{transform:rotate(180deg)}.yj1zEa_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.yj1zEa_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.yj1zEa_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.yj1zEa_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.yj1zEa_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.yj1zEa_discard,.yj1zEa_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.yj1zEa_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.yj1zEa_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.yj1zEa_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.yj1zEa_discard:disabled,.yj1zEa_save:disabled{opacity:.4;cursor:default}.yj1zEa_discard:focus-visible,.yj1zEa_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
13
13
  const tagId$1 = "@mhfire/dsh-im-bridge/PluginCard.module.css";
@@ -115,7 +115,7 @@ window.__ModuleLoader__.load({
115
115
  }
116
116
  //#endregion
117
117
  //#region \0dsh-css:C:\Users\user\Desktop\dsh-im-bridge\plugin\src\client\fields.module.css.mjs
118
- const css = ".OMD7cq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.OMD7cq_field+.OMD7cq_field{border-top:1px solid var(--dsw-alias-border-l2)}.OMD7cq_head{align-items:center;gap:8px;display:flex}.OMD7cq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.OMD7cq_badges{align-items:center;gap:8px;display:inline-flex}.OMD7cq_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.OMD7cq_badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}.OMD7cq_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.OMD7cq_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.OMD7cq_reset:disabled{cursor:default}.OMD7cq_input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.OMD7cq_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.OMD7cq_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.OMD7cq_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.OMD7cq_inputInvalid:focus-visible{outline:none}.OMD7cq_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.OMD7cq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
118
+ const css = ".OMD7cq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.OMD7cq_field+.OMD7cq_field{border-top:1px solid var(--dsw-alias-border-l2)}.OMD7cq_head{align-items:center;gap:8px;display:flex}.OMD7cq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.OMD7cq_badges{align-items:center;gap:8px;display:inline-flex}.OMD7cq_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.OMD7cq_badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}.OMD7cq_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.OMD7cq_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.OMD7cq_reset:disabled{cursor:default}.OMD7cq_input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.OMD7cq_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.OMD7cq_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.OMD7cq_input::placeholder{color:var(--dsw-alias-label-tertiary)}.OMD7cq_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.OMD7cq_inputInvalid:focus-visible{outline:none}.OMD7cq_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.OMD7cq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.OMD7cq_install{appearance:none;font:inherit;cursor:pointer;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border:1px solid #0000;border-radius:8px;align-self:flex-start;padding:5px 14px;font-size:13px;line-height:1.5}.OMD7cq_install:hover:not(:disabled){opacity:.92}.OMD7cq_install:disabled{opacity:.4;cursor:default}.OMD7cq_install:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
119
119
  const tagId = "@mhfire/dsh-im-bridge/fields.module.css";
120
120
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
121
121
  const tag = document.createElement("style");
@@ -133,6 +133,7 @@ window.__ModuleLoader__.load({
133
133
  "hint": "OMD7cq_hint",
134
134
  "input": "OMD7cq_input",
135
135
  "inputInvalid": "OMD7cq_inputInvalid",
136
+ "install": "OMD7cq_install",
136
137
  "invalid": "OMD7cq_invalid",
137
138
  "label": "OMD7cq_label",
138
139
  "reset": "OMD7cq_reset"
@@ -189,7 +190,8 @@ window.__ModuleLoader__.load({
189
190
  }
190
191
  /**
191
192
  * Write-only credential control. The literal never rides a response, so the
192
- * control starts blank and reports only whether one is configured.
193
+ * control starts blank and reports only whether one is configured. A configured
194
+ * empty draft shows a dots placeholder so the box does not look unset.
193
195
  * @param props - the field's copy, staged text, and configured state.
194
196
  * @returns the labelled control.
195
197
  */
@@ -217,6 +219,7 @@ window.__ModuleLoader__.load({
217
219
  type: "password",
218
220
  autoComplete: "off",
219
221
  value: props.text,
222
+ placeholder: props.configured && props.text === "" ? "••••••••" : void 0,
220
223
  disabled: props.disabled,
221
224
  onChange: (event) => {
222
225
  props.onEdit(event.target.value);
@@ -278,6 +281,16 @@ window.__ModuleLoader__.load({
278
281
  props.edit("secret", text);
279
282
  }
280
283
  }),
284
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillsInstall, {
285
+ t,
286
+ disabled,
287
+ available: state.skillsInstallAvailable,
288
+ status: state.skillsInstallStatus,
289
+ dest: state.skillsDest,
290
+ count: state.skillsCount,
291
+ error: state.skillsError,
292
+ onInstall: props.installSkills
293
+ }),
281
294
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ValueField, {
282
295
  id: "im-bridge-allowFrom",
283
296
  label: t("allowFrom"),
@@ -373,6 +386,44 @@ window.__ModuleLoader__.load({
373
386
  ]
374
387
  });
375
388
  }
389
+ function skillsStatusText(t, status, dest, count, error, available) {
390
+ if (!available) return t("skillsUnavailable");
391
+ if (status === "ok") return t("skillsInstalled").replace("{count}", String(count)).replace("{dest}", dest);
392
+ if (status === "error") return error === "" ? t("skillsFailed") : `${t("skillsFailed")} ${error}`;
393
+ return t("skillsHint");
394
+ }
395
+ /**
396
+ * Host-side install of official wecomcli-* (browser never chooses the path).
397
+ * @param props - copy, status, and the install action.
398
+ * @returns the labelled control.
399
+ */
400
+ function SkillsInstall(props) {
401
+ const installing = props.status === "installing";
402
+ const status = skillsStatusText(props.t, props.status, props.dest, props.count, props.error, props.available);
403
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
404
+ className: fields_module_css_default.field,
405
+ children: [
406
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
407
+ className: fields_module_css_default.head,
408
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
409
+ className: fields_module_css_default.label,
410
+ children: props.t("skillsTitle")
411
+ })
412
+ }),
413
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
414
+ type: "button",
415
+ className: fields_module_css_default.install,
416
+ disabled: props.disabled || !props.available || installing,
417
+ onClick: props.onInstall,
418
+ children: props.t(installing ? "skillsInstalling" : "skillsInstall")
419
+ }),
420
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
421
+ className: props.status === "error" || !props.available ? fields_module_css_default.invalid : fields_module_css_default.hint,
422
+ children: status
423
+ })
424
+ ]
425
+ });
426
+ }
376
427
  //#endregion
377
428
  //#region src/client/card-form.ts
378
429
  /**
@@ -446,7 +497,7 @@ window.__ModuleLoader__.load({
446
497
  }
447
498
  /** Publish a projection rebuilt whenever the scope or a draft changes. */
448
499
  bind(project) {
449
- const store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(project());
500
+ const store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(project());
450
501
  this.listeners.add(() => {
451
502
  store.set(project());
452
503
  });
@@ -577,6 +628,10 @@ window.__ModuleLoader__.load({
577
628
  this.failed = false;
578
629
  this.publish();
579
630
  }
631
+ /** Rebuild bound projections for card-owned state outside the settings draft. */
632
+ notify() {
633
+ this.publish();
634
+ }
580
635
  spec(field) {
581
636
  const spec = this.specs.get(field);
582
637
  if (spec === void 0) throw new Error(`im-bridge card has no field ${field}`);
@@ -610,15 +665,22 @@ window.__ModuleLoader__.load({
610
665
  var WecomCardController = class {
611
666
  scope;
612
667
  describe;
668
+ rpc;
613
669
  form;
614
670
  store;
671
+ skillsInstallStatus = "idle";
672
+ skillsDest = "";
673
+ skillsCount = 0;
674
+ skillsError = "";
615
675
  /**
616
676
  * @param scope - bound settings scope for the `im-bridge` namespace.
617
677
  * @param describe - Host describe face; secret literals never ride it.
678
+ * @param rpc - optional Connection RPC for the skills install button.
618
679
  */
619
- constructor(scope, describe) {
680
+ constructor(scope, describe, rpc) {
620
681
  this.scope = scope;
621
682
  this.describe = describe;
683
+ this.rpc = rpc;
622
684
  this.form = new CardForm(scope, [
623
685
  csvField("allowFrom"),
624
686
  numberField("agentTimeoutSec"),
@@ -649,7 +711,12 @@ window.__ModuleLoader__.load({
649
711
  deniedMessage: this.form.field("deniedMessage"),
650
712
  welcomeMessage: this.form.field("welcomeMessage"),
651
713
  provider: this.form.field("provider"),
652
- model: this.form.field("model")
714
+ model: this.form.field("model"),
715
+ skillsInstallAvailable: this.rpc !== void 0,
716
+ skillsInstallStatus: this.skillsInstallStatus,
717
+ skillsDest: this.skillsDest,
718
+ skillsCount: this.skillsCount,
719
+ skillsError: this.skillsError
653
720
  };
654
721
  }
655
722
  /**
@@ -670,11 +737,45 @@ window.__ModuleLoader__.load({
670
737
  await this.scope.set(field, text);
671
738
  return this.secretConfigured(field);
672
739
  }
740
+ /**
741
+ * Download official wecomcli-* into the Host-resolved directory.
742
+ * The browser does not choose the path.
743
+ */
744
+ installSkills() {
745
+ this.runInstall();
746
+ }
747
+ async runInstall() {
748
+ if (this.rpc === void 0 || this.skillsInstallStatus === "installing") return;
749
+ this.skillsInstallStatus = "installing";
750
+ this.skillsError = "";
751
+ this.form.notify();
752
+ try {
753
+ const result = await this.rpc.call("/im-bridge", "wecomcli.installSkills", {});
754
+ if (!result.ok) {
755
+ this.skillsInstallStatus = "error";
756
+ this.skillsError = result.error.message;
757
+ this.form.notify();
758
+ return;
759
+ }
760
+ const value = result.value;
761
+ this.skillsInstallStatus = "ok";
762
+ this.skillsDest = typeof value.dest === "string" ? value.dest : "";
763
+ this.skillsCount = typeof value.count === "number" ? value.count : 0;
764
+ this.skillsError = "";
765
+ } catch (error) {
766
+ this.skillsInstallStatus = "error";
767
+ this.skillsError = error instanceof Error ? error.message : String(error);
768
+ }
769
+ this.form.notify();
770
+ }
673
771
  /** Face the slot registration injects. */
674
772
  inject() {
675
773
  return {
676
774
  hooks: { wecomCard: this.store },
677
- ...this.form.actions()
775
+ ...this.form.actions(),
776
+ installSkills: () => {
777
+ this.installSkills();
778
+ }
678
779
  };
679
780
  }
680
781
  };
@@ -697,7 +798,7 @@ window.__ModuleLoader__.load({
697
798
  invalidNumber: "Enter a finite number.",
698
799
  botId: "Bot ID",
699
800
  secret: "Secret",
700
- secretHint: "Leave blank to keep the stored value. Save, then restart the process to open the WebSocket.",
801
+ secretHint: "The box stays empty on purpose (the stored value never rides the wire). A Configured badge means it is saved; type a new value to replace it. Save, then restart the process to open the WebSocket.",
701
802
  secretConfigured: "Configured",
702
803
  secretUnset: "Not configured",
703
804
  allowFrom: "Allowed sender userids",
@@ -713,7 +814,14 @@ window.__ModuleLoader__.load({
713
814
  provider: "WeCom-only provider",
714
815
  providerHint: "Empty follows the GUI default model. Both provider and model must be set to override.",
715
816
  model: "WeCom-only model",
716
- modelHint: "Takes effect only together with provider."
817
+ modelHint: "Takes effect only together with provider.",
818
+ skillsTitle: "WeCom office skills",
819
+ skillsHint: "Installs wecomcli-* into the plugin directory ($DSH_HOME/wecom-cli-skills). Do not use npx skills add -g; that CLI has no --dir. An empty Bot ID / Secret box is normal.",
820
+ skillsInstall: "Install official skills",
821
+ skillsInstalling: "Installing…",
822
+ skillsInstalled: "Installed {count} skills into {dest}",
823
+ skillsFailed: "Install failed.",
824
+ skillsUnavailable: "Install needs the Web host Connection."
717
825
  };
718
826
  /** Chinese copy for the im-bridge card. */
719
827
  const zh = {
@@ -732,7 +840,7 @@ window.__ModuleLoader__.load({
732
840
  invalidNumber: "请输入有效数字。",
733
841
  botId: "Bot ID",
734
842
  secret: "Secret",
735
- secretHint: "留空保留已存值。保存后需重启进程才会连 WebSocket。",
843
+ secretHint: "框空是正常的(已存值不会传到浏览器)。徽章「已配置」即已保存;重新输入可覆盖。保存后需重启进程才会连 WebSocket。",
736
844
  secretConfigured: "已配置",
737
845
  secretUnset: "未配置",
738
846
  allowFrom: "允许的发送者 userid",
@@ -748,7 +856,14 @@ window.__ModuleLoader__.load({
748
856
  provider: "企微专用 provider",
749
857
  providerHint: "空 = 跟随 GUI 默认模型。须与 model 同时填写才覆盖。",
750
858
  model: "企微专用 model",
751
- modelHint: "仅在同时填写 provider 时生效。"
859
+ modelHint: "仅在同时填写 provider 时生效。",
860
+ skillsTitle: "企微办公 skills",
861
+ skillsHint: "装到程序目录 $DSH_HOME/wecom-cli-skills。不要用 npx skills add -g;CLI 没有 --dir。Bot ID / Secret 框空是正常的。",
862
+ skillsInstall: "安装官方 skills",
863
+ skillsInstalling: "正在安装…",
864
+ skillsInstalled: "已装 {count} 个到 {dest}",
865
+ skillsFailed: "安装失败。",
866
+ skillsUnavailable: "安装需要 Web Host 的 Connection。"
752
867
  };
753
868
  //#endregion
754
869
  //#region src/client/index.ts
@@ -767,7 +882,8 @@ window.__ModuleLoader__.load({
767
882
  * @param ctx - browser plugin context.
768
883
  */
769
884
  function apply(ctx) {
770
- const card = new WecomCardController(ctx.settingsScope.bind({ namespace: NS }), ctx.settingsScope.describe());
885
+ const connection = ctx.get("connection");
886
+ const card = new WecomCardController(ctx.settingsScope.bind({ namespace: NS }), ctx.settingsScope.describe(), connection?.rpc);
771
887
  ctx.effect(() => ctx.locale.register(NS, {
772
888
  zh,
773
889
  en
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useState","css","IconChevronDownOutline14","css","createSnapshotStore","NS"],"sources":["../src/client/PluginCard.tsx","../src/client/fields.tsx","../src/client/WecomCard.tsx","../src/client/card-form.ts","../src/client/card-controller.ts","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/** Expandable plugin card chrome matching the Host Plugins section. */\n\nimport { useState, type ReactElement, type ReactNode } from 'react'\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { ImBridgeLocaleKey } from './locales.ts'\nimport type { CardShell } from './card-form.ts'\nimport css from './PluginCard.module.css'\n\n/** Card chrome shared by this plugin's settings card. */\nexport interface PluginCardProps {\n /** Locale reader for this card's copy. */\n t: (key: ImBridgeLocaleKey) => string\n /** Locale key of the plugin's name. */\n titleKey: ImBridgeLocaleKey\n /** Locale key of the line describing what these settings govern. */\n descriptionKey: ImBridgeLocaleKey\n /** The card's form state. */\n state: CardShell\n /** Write every staged edit. */\n onSave: () => void\n /** Drop every staged edit. */\n onDiscard: () => void\n /** The plugin's controls. */\n children: ReactNode\n}\n\n/**\n * Render one plugin card.\n * @param props - locale copy, form state, and controls.\n * @returns the card, or nothing when the namespace is unavailable.\n */\nexport function PluginCard(props: PluginCardProps): ReactElement | null {\n const [open, setOpen] = useState(false)\n const { state } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const blocked = !state.dirty || state.invalid || state.saving\n return (\n <li className={open ? `${css.card} ${css.cardOpen}` : css.card}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${props.t(open ? 'collapse' : 'expand')}: ${title}`}\n onClick={() => { setOpen(current => !current) }}\n >\n <span className={css.headText}>\n <span className={css.name}>{title}</span>\n <span className={css.description}>{props.t(props.descriptionKey)}</span>\n </span>\n {state.dirty ? <span className={css.pending}>{props.t('unsaved')}</span> : null}\n <IconChevronDownOutline14 className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron} />\n </button>\n {open\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{props.t('readOnly')}</p> : null}\n {props.children}\n <div className={css.footer}>\n {state.failed ? <p className={css.failed} role=\"status\">{props.t('saveFailed')}</p> : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.onSave}\n >\n {props.t(state.saving ? 'saving' : 'save')}\n </button>\n </div>\n </div>\n )\n : null}\n </li>\n )\n}\n","/** Staged value and write-only secret controls. */\n\nimport type { ReactElement } from 'react'\nimport css from './fields.module.css'\n\n/** Shared props for a labelled field control. */\nexport interface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation under the control. */\n hint: string\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control. */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/**\n * A staged value field.\n * @param props - the field's copy, staged text, and edit actions.\n * @returns the labelled control.\n */\nexport function ValueField(props: FieldProps & {\n /** Hints a numeric keypad without narrowing accepted drafts. */\n numeric?: boolean\n}): ReactElement {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type=\"text\"\n {...props.numeric === true ? { inputMode: 'numeric' as const } : {}}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\n/**\n * Write-only credential control. The literal never rides a response, so the\n * control starts blank and reports only whether one is configured.\n * @param props - the field's copy, staged text, and configured state.\n * @returns the labelled control.\n */\nexport function SecretField(props: Pick<FieldProps, 'id' | 'label' | 'hint' | 'text' | 'disabled' | 'onEdit'> & {\n /** Whether the Host reports a configured value for this secret slot. */\n configured: boolean\n /** Copy describing the configured state. */\n stateLabel: string\n}): ReactElement {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n <span className={css.badges}>\n <span className={props.configured ? css.badge : css.badgeMuted}>{props.stateLabel}</span>\n </span>\n </div>\n <input\n id={props.id}\n className={css.input}\n type=\"password\"\n autoComplete=\"off\"\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n","/** WeCom settings card: owned chrome, staged fields, save/discard. */\n\nimport type { ReactElement } from 'react'\nimport type { ImBridgeLocaleKey } from './locales.ts'\nimport type { WecomCardFace, WecomCardState } from './card-controller.ts'\nimport { PluginCard } from './PluginCard.tsx'\nimport { SecretField, ValueField } from './fields.tsx'\n\n/** Props the slot renderer binds for this card. */\nexport interface WecomCardProps {\n /** Translate a dictionary key of the `im-bridge` namespace. */\n t: (key: ImBridgeLocaleKey) => string\n /** Card snapshot bound from inject.hooks.wecomCard. */\n useWecomCard: <S>(select: (state: WecomCardState) => S) => S\n /** Stage draft text for one field. */\n edit: WecomCardFace['edit']\n /** Stage a clear back to the composition layer. */\n resetField: WecomCardFace['resetField']\n /** Write every staged edit. */\n save: WecomCardFace['save']\n /** Drop every staged edit. */\n discard: WecomCardFace['discard']\n}\n\n/**\n * Render the WeCom settings card.\n * @param props - locale copy, snapshot hook, and form actions.\n * @returns the card, or nothing when the namespace is unavailable.\n */\nexport function WecomCard(props: WecomCardProps): ReactElement | null {\n const { t } = props\n const state = props.useWecomCard(snapshot => snapshot)\n const disabled = !state.writable\n const field = {\n overriddenLabel: t('overridden'),\n resetLabel: t('reset'),\n invalidLabel: t('invalidNumber'),\n disabled,\n }\n return (\n <PluginCard\n t={t}\n titleKey=\"title\"\n descriptionKey=\"description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n >\n <SecretField\n id=\"im-bridge-botId\"\n label={t('botId')}\n hint={t('secretHint')}\n disabled={disabled}\n text={state.botId.text}\n configured={state.botIdConfigured}\n stateLabel={state.botIdConfigured ? t('secretConfigured') : t('secretUnset')}\n onEdit={text => { props.edit('botId', text) }}\n />\n <SecretField\n id=\"im-bridge-secret\"\n label={t('secret')}\n hint={t('secretHint')}\n disabled={disabled}\n text={state.secret.text}\n configured={state.secretConfigured}\n stateLabel={state.secretConfigured ? t('secretConfigured') : t('secretUnset')}\n onEdit={text => { props.edit('secret', text) }}\n />\n <ValueField\n id=\"im-bridge-allowFrom\"\n label={t('allowFrom')}\n hint={t('allowFromHint')}\n {...field}\n {...state.allowFrom}\n onEdit={text => { props.edit('allowFrom', text) }}\n onReset={() => { props.resetField('allowFrom') }}\n />\n <ValueField\n id=\"im-bridge-agentTimeoutSec\"\n label={t('agentTimeoutSec')}\n hint={t('agentTimeoutSecHint')}\n numeric\n {...field}\n {...state.agentTimeoutSec}\n onEdit={text => { props.edit('agentTimeoutSec', text) }}\n onReset={() => { props.resetField('agentTimeoutSec') }}\n />\n <ValueField\n id=\"im-bridge-startHint\"\n label={t('startHint')}\n hint={t('startHintHint')}\n {...field}\n {...state.startHint}\n onEdit={text => { props.edit('startHint', text) }}\n onReset={() => { props.resetField('startHint') }}\n />\n <ValueField\n id=\"im-bridge-deniedMessage\"\n label={t('deniedMessage')}\n hint={t('deniedMessageHint')}\n {...field}\n {...state.deniedMessage}\n onEdit={text => { props.edit('deniedMessage', text) }}\n onReset={() => { props.resetField('deniedMessage') }}\n />\n <ValueField\n id=\"im-bridge-welcomeMessage\"\n label={t('welcomeMessage')}\n hint={t('welcomeMessageHint')}\n {...field}\n {...state.welcomeMessage}\n onEdit={text => { props.edit('welcomeMessage', text) }}\n onReset={() => { props.resetField('welcomeMessage') }}\n />\n <ValueField\n id=\"im-bridge-provider\"\n label={t('provider')}\n hint={t('providerHint')}\n {...field}\n {...state.provider}\n onEdit={text => { props.edit('provider', text) }}\n onReset={() => { props.resetField('provider') }}\n />\n <ValueField\n id=\"im-bridge-model\"\n label={t('model')}\n hint={t('modelHint')}\n {...field}\n {...state.model}\n onEdit={text => { props.edit('model', text) }}\n onReset={() => { props.resetField('model') }}\n />\n </PluginCard>\n )\n}\n","/**\n * Staged settings form owned by this plugin.\n * Mirrors the Host Plugins section model without importing its chrome.\n */\n\nimport {\n createSnapshotStore,\n type SettingsScope,\n type SettingsScopeSnapshot,\n type SnapshotStore,\n} from '@deepseek-ai/dsh-client-runtime/client'\n\n/** Write one staged field performs on save. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** Convert one section field between stored value and draft text. */\nexport interface CardFieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /** Render a stored value as draft text. */\n format: (value: unknown) => string\n /** The write this draft stages, or undefined when the text is invalid. */\n parse: (text: string) => FieldWrite | undefined\n}\n\n/**\n * A control whose literal never rides a response. The draft starts blank;\n * a blank draft writes nothing so a save cannot clear a stored secret.\n */\nexport interface CardSecretSpec {\n /** Field name addressing this control inside the card's form. */\n field: string\n /** Write the staged text; resolves to whether the Host accepted it. */\n write: (text: string) => Promise<boolean>\n}\n\n/** One field as a control renders it. */\nexport interface CardFieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts. */\n invalid: boolean\n}\n\n/** Card-level form state. */\nexport interface CardShell {\n /** False while the namespace is not served; the card renders nothing. */\n available: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land. */\n failed: boolean\n}\n\n/** Write actions a card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\ninterface StagedEdit {\n text: string\n clear: boolean\n}\n\ninterface PlannedWrite {\n field: string\n run: (() => Promise<boolean>) | undefined\n}\n\n/** Whole-number field. Empty draft clears. */\nexport function numberField(field: string): CardFieldSpec {\n return {\n field,\n format: value => typeof value === 'number' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n return Number.isFinite(parsed) ? { kind: 'set', value: parsed } : undefined\n },\n }\n}\n\n/** Free-text field. Empty draft clears. */\nexport function textField(field: string): CardFieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/** Comma-separated string list. Empty draft stores []. */\nexport function csvField(field: string): CardFieldSpec {\n return {\n field,\n format: value => Array.isArray(value) ? value.join(',') : '',\n parse: (text) => {\n const items = text.split(',').map(item => item.trim()).filter(Boolean)\n return { kind: 'set', value: items }\n },\n }\n}\n\n/** Stages edits over one settings namespace and writes them on save. */\nexport class CardForm<T> {\n private readonly specs: Map<string, CardFieldSpec>\n private readonly secretSpecs: Map<string, CardSecretSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n private saving = false\n private failed = false\n\n /**\n * @param scope - bound settings scope for this card's namespace.\n * @param specs - section fields this card edits.\n * @param secrets - write-only controls; a blank draft is a no-op.\n */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: CardFieldSpec[],\n secrets: CardSecretSpec[] = [],\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n this.secretSpecs = new Map(secrets.map(spec => [spec.field, spec]))\n scope.subscribe(() => { this.publish() })\n }\n\n /** Publish a projection rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Card-level state: what the Host serves and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** One control's staged text, override badge, and validity. */\n field(field: string): CardFieldState {\n const staged = this.staged.get(field)\n if (this.secretSpecs.has(field)) {\n return { text: staged?.text ?? '', overridden: false, invalid: false }\n }\n const spec = this.spec(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n }\n }\n\n /** Edit, reset, save, and discard actions bound to this form. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.spec(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.publish()\n },\n }\n }\n\n /** Write every staged edit, then re-seed from what the Host accepted. */\n async save(): Promise<void> {\n const plan = this.plan()\n const writes = plan.flatMap(item => item.run === undefined ? [] : [item.run])\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const write of writes) {\n landed = await write() && landed\n }\n if (landed) this.staged.clear()\n this.saving = false\n this.failed = !landed\n this.publish()\n }\n\n private plan(): PlannedWrite[] {\n const plan: PlannedWrite[] = []\n for (const [field, staged] of this.staged) {\n const secret = this.secretSpecs.get(field)\n if (secret !== undefined) {\n const value = staged.text.trim()\n if (value !== '') plan.push({ field, run: () => secret.write(value) })\n continue\n }\n const spec = this.spec(field)\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, run: () => this.store(field, write.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n return !this.stored(field)\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value)\n return this.userLayer()?.[field] === value\n || (Array.isArray(value) && JSON.stringify(this.userLayer()?.[field]) === JSON.stringify(value))\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.publish()\n }\n\n private spec(field: string): CardFieldSpec {\n const spec = this.specs.get(field)\n if (spec === undefined) throw new Error(`im-bridge card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener()\n }\n}\n","/** Staged form over the `im-bridge` settings namespace. */\n\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport {\n CardForm,\n csvField,\n numberField,\n textField,\n type CardActions,\n type CardFieldState,\n type CardShell,\n} from './card-form.ts'\n\n/** One redacted secret slot from `settings.describe`. */\ninterface SecretSlotView {\n path: readonly string[]\n set: boolean\n}\n\n/**\n * Cross-namespace describe face. Typed locally so this out-of-tree package\n * does not value- or type-import `@deepseek-ai/dsh-client-ui-settings`.\n */\ninterface SettingsDescribeFace {\n getSnapshot(): {\n view?: {\n namespaces: ReadonlyArray<{\n ns: string\n secrets: readonly SecretSlotView[]\n }>\n }\n }\n}\n\n/** Settings namespace paired with this card. */\nconst NS = 'im-bridge'\n\n/** User-editable slice of the im-bridge settings namespace. */\nexport interface WecomCardSettings {\n allowFrom?: string[]\n agentTimeoutSec?: number\n startHint?: string\n deniedMessage?: string\n welcomeMessage?: string\n provider?: string\n model?: string\n}\n\n/** Snapshot the WeCom card renders. */\nexport interface WecomCardState extends CardShell {\n botId: CardFieldState\n secret: CardFieldState\n botIdConfigured: boolean\n secretConfigured: boolean\n allowFrom: CardFieldState\n agentTimeoutSec: CardFieldState\n startHint: CardFieldState\n deniedMessage: CardFieldState\n welcomeMessage: CardFieldState\n provider: CardFieldState\n model: CardFieldState\n}\n\n/** Face the card's slot registration injects. */\nexport interface WecomCardFace extends CardActions {\n hooks: {\n /** Card snapshot bound by the renderer as useWecomCard. */\n wecomCard: SnapshotStore<WecomCardState>\n }\n}\n\n/** Bridges the `im-bridge` scope onto the staged card form. */\nexport class WecomCardController {\n private readonly form: CardForm<WecomCardSettings>\n private readonly store: SnapshotStore<WecomCardState>\n\n /**\n * @param scope - bound settings scope for the `im-bridge` namespace.\n * @param describe - Host describe face; secret literals never ride it.\n */\n constructor(\n private readonly scope: SettingsScope<WecomCardSettings>,\n private readonly describe: SettingsDescribeFace,\n ) {\n this.form = new CardForm(\n scope,\n [\n csvField('allowFrom'),\n numberField('agentTimeoutSec'),\n textField('startHint'),\n textField('deniedMessage'),\n textField('welcomeMessage'),\n textField('provider'),\n textField('model'),\n ],\n [\n { field: 'botId', write: text => this.writeSecret('botId', text) },\n { field: 'secret', write: text => this.writeSecret('secret', text) },\n ],\n )\n this.store = this.form.bind(() => this.projection())\n }\n\n private projection(): WecomCardState {\n return {\n ...this.form.shell(),\n botId: this.form.field('botId'),\n secret: this.form.field('secret'),\n botIdConfigured: this.secretConfigured('botId'),\n secretConfigured: this.secretConfigured('secret'),\n allowFrom: this.form.field('allowFrom'),\n agentTimeoutSec: this.form.field('agentTimeoutSec'),\n startHint: this.form.field('startHint'),\n deniedMessage: this.form.field('deniedMessage'),\n welcomeMessage: this.form.field('welcomeMessage'),\n provider: this.form.field('provider'),\n model: this.form.field('model'),\n }\n }\n\n /**\n * Whether the Host reports a configured value for one secret slot.\n * @param field - `botId` or `secret`.\n * @returns true when describe lists that slot as set.\n */\n private secretConfigured(field: string): boolean {\n const row = this.describe.getSnapshot().view?.namespaces.find(candidate => candidate.ns === NS)\n return row?.secrets.some(slot => slot.path[0] === field && slot.set) === true\n }\n\n /**\n * Write one credential into the user layer, then read configured state back.\n * @param field - `botId` or `secret`.\n * @param text - the staged literal.\n * @returns whether describe reports the slot set afterwards.\n */\n private async writeSecret(field: string, text: string): Promise<boolean> {\n await this.scope.set(field, text)\n return this.secretConfigured(field)\n }\n\n /** Face the slot registration injects. */\n inject(): WecomCardFace {\n return { hooks: { wecomCard: this.store }, ...this.form.actions() }\n }\n}\n","/** Dictionary keys owned by the im-bridge settings card. */\nexport type ImBridgeLocaleKey =\n | 'title'\n | 'description'\n | 'unsaved'\n | 'readOnly'\n | 'saveFailed'\n | 'save'\n | 'saving'\n | 'discard'\n | 'expand'\n | 'collapse'\n | 'overridden'\n | 'reset'\n | 'invalidNumber'\n | 'botId'\n | 'secret'\n | 'secretHint'\n | 'secretConfigured'\n | 'secretUnset'\n | 'allowFrom'\n | 'allowFromHint'\n | 'agentTimeoutSec'\n | 'agentTimeoutSecHint'\n | 'startHint'\n | 'startHintHint'\n | 'deniedMessage'\n | 'deniedMessageHint'\n | 'welcomeMessage'\n | 'welcomeMessageHint'\n | 'provider'\n | 'providerHint'\n | 'model'\n | 'modelHint'\n\n/** English copy for the im-bridge card. */\nexport const en: Record<ImBridgeLocaleKey, string> = {\n title: 'WeCom Bridge',\n description: 'Credentials, allow-list, timeouts, and WeCom-only model overrides.',\n unsaved: 'Unsaved',\n readOnly: 'This document is read-only.',\n saveFailed: 'Save did not land. Correct the fields and try again.',\n save: 'Save',\n saving: 'Saving…',\n discard: 'Discard',\n expand: 'Show settings',\n collapse: 'Hide settings',\n overridden: 'Overridden',\n reset: 'Reset',\n invalidNumber: 'Enter a finite number.',\n botId: 'Bot ID',\n secret: 'Secret',\n secretHint: 'Leave blank to keep the stored value. Save, then restart the process to open the WebSocket.',\n secretConfigured: 'Configured',\n secretUnset: 'Not configured',\n allowFrom: 'Allowed sender userids',\n allowFromHint: 'Comma-separated. Empty allows everyone.',\n agentTimeoutSec: 'Task timeout (seconds)',\n agentTimeoutSecHint: 'Progress bar and remaining-time estimate.',\n startHint: 'Placeholder while thinking',\n startHintHint: 'First stream line after a message arrives.',\n deniedMessage: 'Denied-sender reply',\n deniedMessageHint: 'Sent when the userid is outside the allow-list.',\n welcomeMessage: 'Welcome message',\n welcomeMessageHint: 'Sent when a user opens the WeCom chat.',\n provider: 'WeCom-only provider',\n providerHint: 'Empty follows the GUI default model. Both provider and model must be set to override.',\n model: 'WeCom-only model',\n modelHint: 'Takes effect only together with provider.',\n}\n\n/** Chinese copy for the im-bridge card. */\nexport const zh: Record<ImBridgeLocaleKey, string> = {\n title: '企业微信桥接',\n description: '凭证、白名单、超时和企微专用模型覆盖。',\n unsaved: '未保存',\n readOnly: '当前文档不可写。',\n saveFailed: '保存未生效,请修正后重试。',\n save: '保存',\n saving: '保存中…',\n discard: '放弃',\n expand: '展开设置',\n collapse: '收起设置',\n overridden: '已覆盖',\n reset: '重置',\n invalidNumber: '请输入有效数字。',\n botId: 'Bot ID',\n secret: 'Secret',\n secretHint: '留空保留已存值。保存后需重启进程才会连 WebSocket。',\n secretConfigured: '已配置',\n secretUnset: '未配置',\n allowFrom: '允许的发送者 userid',\n allowFromHint: '逗号分隔;空 = 允许所有人。',\n agentTimeoutSec: '单任务超时(秒)',\n agentTimeoutSecHint: '动画进度条和剩余估算的基准。',\n startHint: '开始处理时的占位提示',\n startHintHint: '收到消息后推送的第一条流式文案。',\n deniedMessage: '非白名单拒绝文案',\n deniedMessageHint: '发送者不在白名单时回复。',\n welcomeMessage: '进入会话欢迎语',\n welcomeMessageHint: '用户打开企微会话时发送。',\n provider: '企微专用 provider',\n providerHint: '空 = 跟随 GUI 默认模型。须与 model 同时填写才覆盖。',\n model: '企微专用 model',\n modelHint: '仅在同时填写 provider 时生效。',\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'im-bridge': ImBridgeLocaleKey\n }\n}\n","/**\n * Browser half: Settings card keyed by the `im-bridge` namespace.\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: ctx.settingsScope.describe and the keyed slot declaration.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { WecomCard } from './WecomCard.tsx'\nimport { WecomCardController } from './card-controller.ts'\nimport { en, zh } from './locales.ts'\n\n/** Settings namespace shared with the Host half. */\nconst NS = 'im-bridge'\n\n/** Required browser services. */\nexport const inject = ['slots', 'locale', 'connection', 'remote', 'settingsScope']\n\n/**\n * Register locale copy and the Plugins-tab card.\n * @param ctx - browser plugin context.\n */\nexport function apply(ctx: ClientContext): void {\n const card = new WecomCardController(\n ctx.settingsScope.bind({ namespace: NS }),\n ctx.settingsScope.describe(),\n )\n\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'im-bridge: locale dicts')\n\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n key: NS,\n locale: NS,\n inject: () => card.inject(),\n }, WecomCard))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["useState","css","IconChevronDownOutline14","css","css","createSnapshotStore","NS"],"sources":["../src/client/PluginCard.tsx","../src/client/fields.tsx","../src/client/WecomCard.tsx","../src/client/card-form.ts","../src/client/card-controller.ts","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/** Expandable plugin card chrome matching the Host Plugins section. */\n\nimport { useState, type ReactElement, type ReactNode } from 'react'\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { ImBridgeLocaleKey } from './locales.ts'\nimport type { CardShell } from './card-form.ts'\nimport css from './PluginCard.module.css'\n\n/** Card chrome shared by this plugin's settings card. */\nexport interface PluginCardProps {\n /** Locale reader for this card's copy. */\n t: (key: ImBridgeLocaleKey) => string\n /** Locale key of the plugin's name. */\n titleKey: ImBridgeLocaleKey\n /** Locale key of the line describing what these settings govern. */\n descriptionKey: ImBridgeLocaleKey\n /** The card's form state. */\n state: CardShell\n /** Write every staged edit. */\n onSave: () => void\n /** Drop every staged edit. */\n onDiscard: () => void\n /** The plugin's controls. */\n children: ReactNode\n}\n\n/**\n * Render one plugin card.\n * @param props - locale copy, form state, and controls.\n * @returns the card, or nothing when the namespace is unavailable.\n */\nexport function PluginCard(props: PluginCardProps): ReactElement | null {\n const [open, setOpen] = useState(false)\n const { state } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const blocked = !state.dirty || state.invalid || state.saving\n return (\n <li className={open ? `${css.card} ${css.cardOpen}` : css.card}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${props.t(open ? 'collapse' : 'expand')}: ${title}`}\n onClick={() => { setOpen(current => !current) }}\n >\n <span className={css.headText}>\n <span className={css.name}>{title}</span>\n <span className={css.description}>{props.t(props.descriptionKey)}</span>\n </span>\n {state.dirty ? <span className={css.pending}>{props.t('unsaved')}</span> : null}\n <IconChevronDownOutline14 className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron} />\n </button>\n {open\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{props.t('readOnly')}</p> : null}\n {props.children}\n <div className={css.footer}>\n {state.failed ? <p className={css.failed} role=\"status\">{props.t('saveFailed')}</p> : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.onSave}\n >\n {props.t(state.saving ? 'saving' : 'save')}\n </button>\n </div>\n </div>\n )\n : null}\n </li>\n )\n}\n","/** Staged value and write-only secret controls. */\n\nimport type { ReactElement } from 'react'\nimport css from './fields.module.css'\n\n/** Shared props for a labelled field control. */\nexport interface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation under the control. */\n hint: string\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control. */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/**\n * A staged value field.\n * @param props - the field's copy, staged text, and edit actions.\n * @returns the labelled control.\n */\nexport function ValueField(props: FieldProps & {\n /** Hints a numeric keypad without narrowing accepted drafts. */\n numeric?: boolean\n}): ReactElement {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type=\"text\"\n {...props.numeric === true ? { inputMode: 'numeric' as const } : {}}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\n/**\n * Write-only credential control. The literal never rides a response, so the\n * control starts blank and reports only whether one is configured. A configured\n * empty draft shows a dots placeholder so the box does not look unset.\n * @param props - the field's copy, staged text, and configured state.\n * @returns the labelled control.\n */\nexport function SecretField(props: Pick<FieldProps, 'id' | 'label' | 'hint' | 'text' | 'disabled' | 'onEdit'> & {\n /** Whether the Host reports a configured value for this secret slot. */\n configured: boolean\n /** Copy describing the configured state. */\n stateLabel: string\n}): ReactElement {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n <span className={css.badges}>\n <span className={props.configured ? css.badge : css.badgeMuted}>{props.stateLabel}</span>\n </span>\n </div>\n <input\n id={props.id}\n className={css.input}\n type=\"password\"\n autoComplete=\"off\"\n value={props.text}\n placeholder={props.configured && props.text === '' ? '••••••••' : undefined}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n","/** WeCom settings card: owned chrome, staged fields, save/discard. */\n\nimport type { ReactElement } from 'react'\nimport type { ImBridgeLocaleKey } from './locales.ts'\nimport type { WecomCardFace, WecomCardState } from './card-controller.ts'\nimport { PluginCard } from './PluginCard.tsx'\nimport { SecretField, ValueField } from './fields.tsx'\nimport css from './fields.module.css'\n\n/** Props the slot renderer binds for this card. */\nexport interface WecomCardProps {\n /** Translate a dictionary key of the `im-bridge` namespace. */\n t: (key: ImBridgeLocaleKey) => string\n /** Card snapshot bound from inject.hooks.wecomCard. */\n useWecomCard: <S>(select: (state: WecomCardState) => S) => S\n /** Stage draft text for one field. */\n edit: WecomCardFace['edit']\n /** Stage a clear back to the composition layer. */\n resetField: WecomCardFace['resetField']\n /** Write every staged edit. */\n save: WecomCardFace['save']\n /** Drop every staged edit. */\n discard: WecomCardFace['discard']\n /** Download official wecomcli-* into the Host-resolved directory. */\n installSkills: WecomCardFace['installSkills']\n}\n\n/**\n * Render the WeCom settings card.\n * @param props - locale copy, snapshot hook, and form actions.\n * @returns the card, or nothing when the namespace is unavailable.\n */\nexport function WecomCard(props: WecomCardProps): ReactElement | null {\n const { t } = props\n const state = props.useWecomCard(snapshot => snapshot)\n const disabled = !state.writable\n const field = {\n overriddenLabel: t('overridden'),\n resetLabel: t('reset'),\n invalidLabel: t('invalidNumber'),\n disabled,\n }\n return (\n <PluginCard\n t={t}\n titleKey=\"title\"\n descriptionKey=\"description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n >\n <SecretField\n id=\"im-bridge-botId\"\n label={t('botId')}\n hint={t('secretHint')}\n disabled={disabled}\n text={state.botId.text}\n configured={state.botIdConfigured}\n stateLabel={state.botIdConfigured ? t('secretConfigured') : t('secretUnset')}\n onEdit={text => { props.edit('botId', text) }}\n />\n <SecretField\n id=\"im-bridge-secret\"\n label={t('secret')}\n hint={t('secretHint')}\n disabled={disabled}\n text={state.secret.text}\n configured={state.secretConfigured}\n stateLabel={state.secretConfigured ? t('secretConfigured') : t('secretUnset')}\n onEdit={text => { props.edit('secret', text) }}\n />\n <SkillsInstall\n t={t}\n disabled={disabled}\n available={state.skillsInstallAvailable}\n status={state.skillsInstallStatus}\n dest={state.skillsDest}\n count={state.skillsCount}\n error={state.skillsError}\n onInstall={props.installSkills}\n />\n <ValueField\n id=\"im-bridge-allowFrom\"\n label={t('allowFrom')}\n hint={t('allowFromHint')}\n {...field}\n {...state.allowFrom}\n onEdit={text => { props.edit('allowFrom', text) }}\n onReset={() => { props.resetField('allowFrom') }}\n />\n <ValueField\n id=\"im-bridge-agentTimeoutSec\"\n label={t('agentTimeoutSec')}\n hint={t('agentTimeoutSecHint')}\n numeric\n {...field}\n {...state.agentTimeoutSec}\n onEdit={text => { props.edit('agentTimeoutSec', text) }}\n onReset={() => { props.resetField('agentTimeoutSec') }}\n />\n <ValueField\n id=\"im-bridge-startHint\"\n label={t('startHint')}\n hint={t('startHintHint')}\n {...field}\n {...state.startHint}\n onEdit={text => { props.edit('startHint', text) }}\n onReset={() => { props.resetField('startHint') }}\n />\n <ValueField\n id=\"im-bridge-deniedMessage\"\n label={t('deniedMessage')}\n hint={t('deniedMessageHint')}\n {...field}\n {...state.deniedMessage}\n onEdit={text => { props.edit('deniedMessage', text) }}\n onReset={() => { props.resetField('deniedMessage') }}\n />\n <ValueField\n id=\"im-bridge-welcomeMessage\"\n label={t('welcomeMessage')}\n hint={t('welcomeMessageHint')}\n {...field}\n {...state.welcomeMessage}\n onEdit={text => { props.edit('welcomeMessage', text) }}\n onReset={() => { props.resetField('welcomeMessage') }}\n />\n <ValueField\n id=\"im-bridge-provider\"\n label={t('provider')}\n hint={t('providerHint')}\n {...field}\n {...state.provider}\n onEdit={text => { props.edit('provider', text) }}\n onReset={() => { props.resetField('provider') }}\n />\n <ValueField\n id=\"im-bridge-model\"\n label={t('model')}\n hint={t('modelHint')}\n {...field}\n {...state.model}\n onEdit={text => { props.edit('model', text) }}\n onReset={() => { props.resetField('model') }}\n />\n </PluginCard>\n )\n}\n\nfunction skillsStatusText(\n t: (key: ImBridgeLocaleKey) => string,\n status: WecomCardState['skillsInstallStatus'],\n dest: string,\n count: number,\n error: string,\n available: boolean,\n): string {\n if (!available) return t('skillsUnavailable')\n if (status === 'ok') {\n return t('skillsInstalled').replace('{count}', String(count)).replace('{dest}', dest)\n }\n if (status === 'error') {\n return error === '' ? t('skillsFailed') : `${t('skillsFailed')} ${error}`\n }\n return t('skillsHint')\n}\n\n/**\n * Host-side install of official wecomcli-* (browser never chooses the path).\n * @param props - copy, status, and the install action.\n * @returns the labelled control.\n */\nfunction SkillsInstall(props: {\n t: (key: ImBridgeLocaleKey) => string\n disabled: boolean\n available: boolean\n status: WecomCardState['skillsInstallStatus']\n dest: string\n count: number\n error: string\n onInstall: () => void\n}): ReactElement {\n const installing = props.status === 'installing'\n const status = skillsStatusText(\n props.t,\n props.status,\n props.dest,\n props.count,\n props.error,\n props.available,\n )\n return (\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label}>{props.t('skillsTitle')}</span>\n </div>\n <button\n type=\"button\"\n className={css.install}\n disabled={props.disabled || !props.available || installing}\n onClick={props.onInstall}\n >\n {props.t(installing ? 'skillsInstalling' : 'skillsInstall')}\n </button>\n <p className={props.status === 'error' || !props.available ? css.invalid : css.hint}>\n {status}\n </p>\n </div>\n )\n}\n","/**\n * Staged settings form owned by this plugin.\n * Mirrors the Host Plugins section model without importing its chrome.\n */\n\nimport { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'\n\n/** Write one staged field performs on save. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** Convert one section field between stored value and draft text. */\nexport interface CardFieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /** Render a stored value as draft text. */\n format: (value: unknown) => string\n /** The write this draft stages, or undefined when the text is invalid. */\n parse: (text: string) => FieldWrite | undefined\n}\n\n/**\n * A control whose literal never rides a response. The draft starts blank;\n * a blank draft writes nothing so a save cannot clear a stored secret.\n */\nexport interface CardSecretSpec {\n /** Field name addressing this control inside the card's form. */\n field: string\n /** Write the staged text; resolves to whether the Host accepted it. */\n write: (text: string) => Promise<boolean>\n}\n\n/** One field as a control renders it. */\nexport interface CardFieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts. */\n invalid: boolean\n}\n\n/** Card-level form state. */\nexport interface CardShell {\n /** False while the namespace is not served; the card renders nothing. */\n available: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land. */\n failed: boolean\n}\n\n/** Write actions a card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\ninterface StagedEdit {\n text: string\n clear: boolean\n}\n\ninterface PlannedWrite {\n field: string\n run: (() => Promise<boolean>) | undefined\n}\n\n/** Whole-number field. Empty draft clears. */\nexport function numberField(field: string): CardFieldSpec {\n return {\n field,\n format: value => typeof value === 'number' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n return Number.isFinite(parsed) ? { kind: 'set', value: parsed } : undefined\n },\n }\n}\n\n/** Free-text field. Empty draft clears. */\nexport function textField(field: string): CardFieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/** Comma-separated string list. Empty draft stores []. */\nexport function csvField(field: string): CardFieldSpec {\n return {\n field,\n format: value => Array.isArray(value) ? value.join(',') : '',\n parse: (text) => {\n const items = text.split(',').map(item => item.trim()).filter(Boolean)\n return { kind: 'set', value: items }\n },\n }\n}\n\n/** Stages edits over one settings namespace and writes them on save. */\nexport class CardForm<T> {\n private readonly specs: Map<string, CardFieldSpec>\n private readonly secretSpecs: Map<string, CardSecretSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n private saving = false\n private failed = false\n\n /**\n * @param scope - bound settings scope for this card's namespace.\n * @param specs - section fields this card edits.\n * @param secrets - write-only controls; a blank draft is a no-op.\n */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: CardFieldSpec[],\n secrets: CardSecretSpec[] = [],\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n this.secretSpecs = new Map(secrets.map(spec => [spec.field, spec]))\n scope.subscribe(() => { this.publish() })\n }\n\n /** Publish a projection rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Card-level state: what the Host serves and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** One control's staged text, override badge, and validity. */\n field(field: string): CardFieldState {\n const staged = this.staged.get(field)\n if (this.secretSpecs.has(field)) {\n return { text: staged?.text ?? '', overridden: false, invalid: false }\n }\n const spec = this.spec(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n }\n }\n\n /** Edit, reset, save, and discard actions bound to this form. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.spec(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.publish()\n },\n }\n }\n\n /** Write every staged edit, then re-seed from what the Host accepted. */\n async save(): Promise<void> {\n const plan = this.plan()\n const writes = plan.flatMap(item => item.run === undefined ? [] : [item.run])\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const write of writes) {\n landed = await write() && landed\n }\n if (landed) this.staged.clear()\n this.saving = false\n this.failed = !landed\n this.publish()\n }\n\n private plan(): PlannedWrite[] {\n const plan: PlannedWrite[] = []\n for (const [field, staged] of this.staged) {\n const secret = this.secretSpecs.get(field)\n if (secret !== undefined) {\n const value = staged.text.trim()\n if (value !== '') plan.push({ field, run: () => secret.write(value) })\n continue\n }\n const spec = this.spec(field)\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, run: () => this.store(field, write.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n return !this.stored(field)\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value)\n return this.userLayer()?.[field] === value\n || (Array.isArray(value) && JSON.stringify(this.userLayer()?.[field]) === JSON.stringify(value))\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.publish()\n }\n\n /** Rebuild bound projections for card-owned state outside the settings draft. */\n notify(): void {\n this.publish()\n }\n\n private spec(field: string): CardFieldSpec {\n const spec = this.specs.get(field)\n if (spec === undefined) throw new Error(`im-bridge card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener()\n }\n}\n","/** Staged form over the `im-bridge` settings namespace. */\n\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport {\n CardForm,\n csvField,\n numberField,\n textField,\n type CardActions,\n type CardFieldState,\n type CardShell,\n} from './card-form.ts'\n\n/** One redacted secret slot from `settings.describe`. */\ninterface SecretSlotView {\n path: readonly string[]\n set: boolean\n}\n\n/**\n * Cross-namespace describe face. Typed locally so this out-of-tree package\n * does not value- or type-import `@deepseek-ai/dsh-client-ui-settings`.\n */\ninterface SettingsDescribeFace {\n getSnapshot(): {\n view?: {\n namespaces: ReadonlyArray<{\n ns: string\n secrets: readonly SecretSlotView[]\n }>\n }\n }\n}\n\n/** Settings namespace paired with this card. */\nconst NS = 'im-bridge'\n\n/** User-editable slice of the im-bridge settings namespace. */\nexport interface WecomCardSettings {\n allowFrom?: string[]\n agentTimeoutSec?: number\n startHint?: string\n deniedMessage?: string\n welcomeMessage?: string\n provider?: string\n model?: string\n}\n\n/** Snapshot the WeCom card renders. */\nexport interface WecomCardState extends CardShell {\n botId: CardFieldState\n secret: CardFieldState\n botIdConfigured: boolean\n secretConfigured: boolean\n allowFrom: CardFieldState\n agentTimeoutSec: CardFieldState\n startHint: CardFieldState\n deniedMessage: CardFieldState\n welcomeMessage: CardFieldState\n provider: CardFieldState\n model: CardFieldState\n /** Whether the Host Connection RPC is available. */\n skillsInstallAvailable: boolean\n /** idle / installing / ok / error. */\n skillsInstallStatus: 'idle' | 'installing' | 'ok' | 'error'\n /** Destination after a successful install. */\n skillsDest: string\n /** Loaded wecomcli-* count after a successful install. */\n skillsCount: number\n /** Host error message after a failed install. */\n skillsError: string\n}\n\n/** Face the card's slot registration injects. */\nexport interface WecomCardFace extends CardActions {\n /** Download official wecomcli-* into the Host-resolved directory. */\n installSkills: () => void\n hooks: {\n /** Card snapshot bound by the renderer as useWecomCard. */\n wecomCard: SnapshotStore<WecomCardState>\n }\n}\n\n/** Unary RPC caller used by the install button. */\nexport interface SkillsInstallRpc {\n call(\n channel: string,\n endpoint: string,\n payload: unknown,\n signal?: AbortSignal,\n ): Promise<\n | { ok: true; value: unknown }\n | { ok: false; error: { message: string } }\n >\n}\n\n/** Bridges the `im-bridge` scope onto the staged card form. */\nexport class WecomCardController {\n private readonly form: CardForm<WecomCardSettings>\n private readonly store: SnapshotStore<WecomCardState>\n private skillsInstallStatus: WecomCardState['skillsInstallStatus'] = 'idle'\n private skillsDest = ''\n private skillsCount = 0\n private skillsError = ''\n\n /**\n * @param scope - bound settings scope for the `im-bridge` namespace.\n * @param describe - Host describe face; secret literals never ride it.\n * @param rpc - optional Connection RPC for the skills install button.\n */\n constructor(\n private readonly scope: SettingsScope<WecomCardSettings>,\n private readonly describe: SettingsDescribeFace,\n private readonly rpc: SkillsInstallRpc | undefined,\n ) {\n this.form = new CardForm(\n scope,\n [\n csvField('allowFrom'),\n numberField('agentTimeoutSec'),\n textField('startHint'),\n textField('deniedMessage'),\n textField('welcomeMessage'),\n textField('provider'),\n textField('model'),\n ],\n [\n { field: 'botId', write: text => this.writeSecret('botId', text) },\n { field: 'secret', write: text => this.writeSecret('secret', text) },\n ],\n )\n this.store = this.form.bind(() => this.projection())\n }\n\n private projection(): WecomCardState {\n return {\n ...this.form.shell(),\n botId: this.form.field('botId'),\n secret: this.form.field('secret'),\n botIdConfigured: this.secretConfigured('botId'),\n secretConfigured: this.secretConfigured('secret'),\n allowFrom: this.form.field('allowFrom'),\n agentTimeoutSec: this.form.field('agentTimeoutSec'),\n startHint: this.form.field('startHint'),\n deniedMessage: this.form.field('deniedMessage'),\n welcomeMessage: this.form.field('welcomeMessage'),\n provider: this.form.field('provider'),\n model: this.form.field('model'),\n skillsInstallAvailable: this.rpc !== undefined,\n skillsInstallStatus: this.skillsInstallStatus,\n skillsDest: this.skillsDest,\n skillsCount: this.skillsCount,\n skillsError: this.skillsError,\n }\n }\n\n /**\n * Whether the Host reports a configured value for one secret slot.\n * @param field - `botId` or `secret`.\n * @returns true when describe lists that slot as set.\n */\n private secretConfigured(field: string): boolean {\n const row = this.describe.getSnapshot().view?.namespaces.find(candidate => candidate.ns === NS)\n return row?.secrets.some(slot => slot.path[0] === field && slot.set) === true\n }\n\n /**\n * Write one credential into the user layer, then read configured state back.\n * @param field - `botId` or `secret`.\n * @param text - the staged literal.\n * @returns whether describe reports the slot set afterwards.\n */\n private async writeSecret(field: string, text: string): Promise<boolean> {\n await this.scope.set(field, text)\n return this.secretConfigured(field)\n }\n\n /**\n * Download official wecomcli-* into the Host-resolved directory.\n * The browser does not choose the path.\n */\n installSkills(): void {\n void this.runInstall()\n }\n\n private async runInstall(): Promise<void> {\n if (this.rpc === undefined || this.skillsInstallStatus === 'installing') return\n this.skillsInstallStatus = 'installing'\n this.skillsError = ''\n this.form.notify()\n try {\n const result = await this.rpc.call('/im-bridge', 'wecomcli.installSkills', {})\n if (!result.ok) {\n this.skillsInstallStatus = 'error'\n this.skillsError = result.error.message\n this.form.notify()\n return\n }\n const value = result.value as { dest?: unknown; count?: unknown }\n this.skillsInstallStatus = 'ok'\n this.skillsDest = typeof value.dest === 'string' ? value.dest : ''\n this.skillsCount = typeof value.count === 'number' ? value.count : 0\n this.skillsError = ''\n } catch (error) {\n this.skillsInstallStatus = 'error'\n this.skillsError = error instanceof Error ? error.message : String(error)\n }\n this.form.notify()\n }\n\n /** Face the slot registration injects. */\n inject(): WecomCardFace {\n return {\n hooks: { wecomCard: this.store },\n ...this.form.actions(),\n installSkills: () => { this.installSkills() },\n }\n }\n}\n","/** Dictionary keys owned by the im-bridge settings card. */\nexport type ImBridgeLocaleKey =\n | 'title'\n | 'description'\n | 'unsaved'\n | 'readOnly'\n | 'saveFailed'\n | 'save'\n | 'saving'\n | 'discard'\n | 'expand'\n | 'collapse'\n | 'overridden'\n | 'reset'\n | 'invalidNumber'\n | 'botId'\n | 'secret'\n | 'secretHint'\n | 'secretConfigured'\n | 'secretUnset'\n | 'allowFrom'\n | 'allowFromHint'\n | 'agentTimeoutSec'\n | 'agentTimeoutSecHint'\n | 'startHint'\n | 'startHintHint'\n | 'deniedMessage'\n | 'deniedMessageHint'\n | 'welcomeMessage'\n | 'welcomeMessageHint'\n | 'provider'\n | 'providerHint'\n | 'model'\n | 'modelHint'\n | 'skillsTitle'\n | 'skillsHint'\n | 'skillsInstall'\n | 'skillsInstalling'\n | 'skillsInstalled'\n | 'skillsFailed'\n | 'skillsUnavailable'\n\n/** English copy for the im-bridge card. */\nexport const en: Record<ImBridgeLocaleKey, string> = {\n title: 'WeCom Bridge',\n description: 'Credentials, allow-list, timeouts, and WeCom-only model overrides.',\n unsaved: 'Unsaved',\n readOnly: 'This document is read-only.',\n saveFailed: 'Save did not land. Correct the fields and try again.',\n save: 'Save',\n saving: 'Saving…',\n discard: 'Discard',\n expand: 'Show settings',\n collapse: 'Hide settings',\n overridden: 'Overridden',\n reset: 'Reset',\n invalidNumber: 'Enter a finite number.',\n botId: 'Bot ID',\n secret: 'Secret',\n secretHint: 'The box stays empty on purpose (the stored value never rides the wire). A Configured badge means it is saved; type a new value to replace it. Save, then restart the process to open the WebSocket.',\n secretConfigured: 'Configured',\n secretUnset: 'Not configured',\n allowFrom: 'Allowed sender userids',\n allowFromHint: 'Comma-separated. Empty allows everyone.',\n agentTimeoutSec: 'Task timeout (seconds)',\n agentTimeoutSecHint: 'Progress bar and remaining-time estimate.',\n startHint: 'Placeholder while thinking',\n startHintHint: 'First stream line after a message arrives.',\n deniedMessage: 'Denied-sender reply',\n deniedMessageHint: 'Sent when the userid is outside the allow-list.',\n welcomeMessage: 'Welcome message',\n welcomeMessageHint: 'Sent when a user opens the WeCom chat.',\n provider: 'WeCom-only provider',\n providerHint: 'Empty follows the GUI default model. Both provider and model must be set to override.',\n model: 'WeCom-only model',\n modelHint: 'Takes effect only together with provider.',\n skillsTitle: 'WeCom office skills',\n skillsHint: 'Installs wecomcli-* into the plugin directory ($DSH_HOME/wecom-cli-skills). Do not use npx skills add -g; that CLI has no --dir. An empty Bot ID / Secret box is normal.',\n skillsInstall: 'Install official skills',\n skillsInstalling: 'Installing…',\n skillsInstalled: 'Installed {count} skills into {dest}',\n skillsFailed: 'Install failed.',\n skillsUnavailable: 'Install needs the Web host Connection.',\n}\n\n/** Chinese copy for the im-bridge card. */\nexport const zh: Record<ImBridgeLocaleKey, string> = {\n title: '企业微信桥接',\n description: '凭证、白名单、超时和企微专用模型覆盖。',\n unsaved: '未保存',\n readOnly: '当前文档不可写。',\n saveFailed: '保存未生效,请修正后重试。',\n save: '保存',\n saving: '保存中…',\n discard: '放弃',\n expand: '展开设置',\n collapse: '收起设置',\n overridden: '已覆盖',\n reset: '重置',\n invalidNumber: '请输入有效数字。',\n botId: 'Bot ID',\n secret: 'Secret',\n secretHint: '框空是正常的(已存值不会传到浏览器)。徽章「已配置」即已保存;重新输入可覆盖。保存后需重启进程才会连 WebSocket。',\n secretConfigured: '已配置',\n secretUnset: '未配置',\n allowFrom: '允许的发送者 userid',\n allowFromHint: '逗号分隔;空 = 允许所有人。',\n agentTimeoutSec: '单任务超时(秒)',\n agentTimeoutSecHint: '动画进度条和剩余估算的基准。',\n startHint: '开始处理时的占位提示',\n startHintHint: '收到消息后推送的第一条流式文案。',\n deniedMessage: '非白名单拒绝文案',\n deniedMessageHint: '发送者不在白名单时回复。',\n welcomeMessage: '进入会话欢迎语',\n welcomeMessageHint: '用户打开企微会话时发送。',\n provider: '企微专用 provider',\n providerHint: '空 = 跟随 GUI 默认模型。须与 model 同时填写才覆盖。',\n model: '企微专用 model',\n modelHint: '仅在同时填写 provider 时生效。',\n skillsTitle: '企微办公 skills',\n skillsHint: '装到程序目录 $DSH_HOME/wecom-cli-skills。不要用 npx skills add -g;CLI 没有 --dir。Bot ID / Secret 框空是正常的。',\n skillsInstall: '安装官方 skills',\n skillsInstalling: '正在安装…',\n skillsInstalled: '已装 {count} 个到 {dest}',\n skillsFailed: '安装失败。',\n skillsUnavailable: '安装需要 Web Host 的 Connection。',\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'im-bridge': ImBridgeLocaleKey\n }\n}\n","/**\n * Browser half: Settings card keyed by the `im-bridge` namespace.\n */\n\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\n// Type-only: ctx.slots, ctx.locale, ctx.settingsScope.describe, and the keyed\n// slot declaration. Behavior crosses packages through services and slots only.\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { WecomCard } from './WecomCard.tsx'\nimport { WecomCardController, type SkillsInstallRpc } from './card-controller.ts'\nimport { en, zh } from './locales.ts'\n\n/** Settings namespace shared with the Host half. */\nconst NS = 'im-bridge'\n\n/** Required browser services. */\nexport const inject = ['slots', 'locale', 'connection', 'remote', 'settingsScope']\n\n/**\n * Register locale copy and the Plugins-tab card.\n * @param ctx - browser plugin context.\n */\nexport function apply(ctx: ClientContext): void {\n const connection = ctx.get('connection') as { rpc?: SkillsInstallRpc } | undefined\n const card = new WecomCardController(\n ctx.settingsScope.bind({ namespace: NS }),\n ctx.settingsScope.describe(),\n connection?.rpc,\n )\n\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'im-bridge: locale dicts')\n\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n key: NS,\n locale: NS,\n inject: () => card.inject(),\n }, WecomCard))\n}\n"],"mappings":"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"}