@xmanrui/dsh-im 4.28.0 → 4.29.0

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 (33) hide show
  1. package/README.en.md +9 -7
  2. package/README.md +9 -7
  3. package/lib/client.js +370 -115
  4. package/lib/index.js +298 -298
  5. package/package.json +5 -1
  6. package/plugin-src/client/channels/feishu/api.js +3 -0
  7. package/plugin-src/client/channels/feishu/voice-editor.js +115 -0
  8. package/plugin-src/client/channels/feishu/voice-settings.js +71 -0
  9. package/plugin-src/client/delivery-settings.js +8 -0
  10. package/plugin-src/client/i18n.js +16 -0
  11. package/plugin-src/client/styles.js +11 -0
  12. package/plugin-src/host/channels/dingtalk/production.mjs +1 -1
  13. package/plugin-src/host/channels/feishu/production.mjs +12 -1
  14. package/plugin-src/host/channels/feishu/rpc.mjs +21 -0
  15. package/plugin-src/host/channels/qq/production.mjs +1 -1
  16. package/plugin-src/host/channels/shared/production.mjs +1 -1
  17. package/plugin-src/host/channels/slack/production.mjs +1 -1
  18. package/plugin-src/host/channels/wecom/production.mjs +1 -1
  19. package/plugin-src/host/channels/wecom-app/production.mjs +1 -1
  20. package/plugin-src/host/channels/weixin/production.mjs +22 -5
  21. package/plugin-src/host/channels/whatsapp/production.mjs +1 -1
  22. package/src/channels/feishu/bridge.mjs +165 -12
  23. package/src/channels/feishu/feishu-cards.mjs +4 -2
  24. package/src/channels/feishu/feishu-runtime.mjs +16 -0
  25. package/src/channels/feishu/multi-bot-controller.mjs +22 -0
  26. package/src/channels/feishu/plugin-config-store.mjs +2 -0
  27. package/src/channels/feishu/voice-config.mjs +34 -0
  28. package/src/channels/feishu/voice.mjs +134 -0
  29. package/src/channels/shared/bot-workspace-store.mjs +30 -10
  30. package/src/channels/shared/default-workspace.mjs +6 -3
  31. package/src/channels/shared/harness-approval.mjs +24 -17
  32. package/src/channels/shared/harness-session-binding.mjs +4 -0
  33. package/src/channels/weixin/weixin-api.mjs +3 -2
package/lib/client.js CHANGED
@@ -590,12 +590,12 @@ async function callManagementRpc(connection, channel7, method, payload, signal)
590
590
  }
591
591
 
592
592
  // plugin-src/client/index.js
593
- var React34 = __toESM(require("react"), 1);
593
+ var React36 = __toESM(require("react"), 1);
594
594
 
595
595
  // package.json
596
596
  var package_default = {
597
597
  name: "@xmanrui/dsh-im",
598
- version: "4.28.0",
598
+ version: "4.29.0",
599
599
  description: "\u628A\u5341\u4E8C\u79CD IM \u6E20\u9053\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect twelve IM channels and a public AI Office to a local DeepSeek Harness.",
600
600
  keywords: [
601
601
  "deepseek-harness",
@@ -680,6 +680,10 @@ var package_default = {
680
680
  {
681
681
  name: "Zhe (Phil) Yang",
682
682
  url: "https://github.com/yangzhe1991"
683
+ },
684
+ {
685
+ name: "Biertolovisk",
686
+ url: "https://github.com/rosalynthompson32-a11y"
683
687
  }
684
688
  ],
685
689
  license: "MIT",
@@ -1486,6 +1490,22 @@ var EN = Object.freeze({
1486
1490
  "\u63A8\u8350\u957F\u4EFB\u52A1\u4F7F\u7528\uFF1A\u8FC7\u7A0B\u4E0E\u6700\u7EC8\u7B54\u6848\u90FD\u5728\u540C\u4E00\u5F20\u5361\u7247\u91CC\u5B9E\u65F6\u66F4\u65B0\uFF0C\u4E0D\u5237\u5C4F": "Recommended for long tasks: the process and the final answer update live in one card without flooding the chat",
1487
1491
  "\u6BCF\u4E00\u6B65\u90FD\u5355\u72EC\u53D1\u4E00\u6761\u6D88\u606F\uFF08\u542B\u5DE5\u5177\u8C03\u7528\u548C\u8FC7\u7A0B\u8BF4\u660E\uFF09\uFF1B\u6CE8\u610F\u957F\u4EFB\u52A1\u4F1A\u8FDE\u7EED\u53D1\u9001\u8F83\u591A\u6D88\u606F": "Every step is sent as its own message (including tool calls and notes); long tasks may send many messages in a row",
1488
1492
  "\u5206\u6B65\u76F4\u63A8\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not save the step push setting. Try again.",
1493
+ "\u8BED\u97F3\u4EA4\u4E92": "Voice",
1494
+ "\u6B63\u5728\u8BFB\u53D6\u8BED\u97F3\u8BBE\u7F6E\u2026": "Loading voice settings\u2026",
1495
+ "\u98DE\u4E66\u8BED\u97F3\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Feishu voice settings are unavailable.",
1496
+ "\u672A\u627E\u5230\u5F53\u524D\u98DE\u4E66\u673A\u5668\u4EBA\uFF0C\u8BF7\u8FD4\u56DE\u5217\u8868\u540E\u91CD\u8BD5\u3002": "This Feishu bot was not found. Return to the bot list and try again.",
1497
+ "\u67E5\u770B\u8BED\u97F3\u4EA4\u4E92\u8BF4\u660E": "View voice help",
1498
+ "\u8BED\u97F3\u4EA4\u4E92\u5F00\u5173": "Voice on/off",
1499
+ "\u5F00\u542F(\u8BED\u97F3\u63D0\u95EE\u4E0E\u8BED\u97F3\u56DE\u590D)": "On (voice questions and voice replies)",
1500
+ "\u6536\u5230\u8BED\u97F3\u6D88\u606F\u65F6\u8F6C\u5199\u4E3A\u6587\u5B57\u5E76\u6309\u6587\u5B57\u6D41\u6C34\u7EBF\u5904\u7406,\u7B54\u6848\u540C\u65F6\u4EE5\u8BED\u97F3\u56DE\u590D;\u8F6C\u5199\u5931\u8D25\u65F6\u964D\u7EA7\u4E3A\u4EC5\u652F\u6301\u6587\u5B57/\u56FE\u7247/\u6587\u4EF6\u7684\u63D0\u793A\u3002\u51ED\u636E\u503C\u5728\u63D2\u4EF6\u51ED\u636E\u5E93\u91CC\u7BA1\u7406,\u8FD9\u91CC\u53EA\u5F15\u7528\u73AF\u5883\u53D8\u91CF\u540D\u3002": "Voice messages are transcribed to text and processed by the normal text pipeline; the answer is also sent as a voice reply. If transcription fails, the bot falls back to the text/image/file-only notice. The credential value lives in the plugin credential store; only the environment variable name is referenced here.",
1501
+ "\u51ED\u636E\u5F15\u7528(\u73AF\u5883\u53D8\u91CF\u540D)": "Credential reference (environment variable name)",
1502
+ "\u8F6C\u5199\u6A21\u578B(ASR)": "Transcription model (ASR)",
1503
+ "\u5408\u6210\u6A21\u578B(TTS)": "Synthesis model (TTS)",
1504
+ "\u56DE\u590D\u97F3\u8272": "Reply voice",
1505
+ '\u9700\u8981 DashScope \u5BC6\u94A5(\u51ED\u636E\u5E93\u4E2D\u4EE5"\u51ED\u636E\u5F15\u7528"\u4E3A\u540D\u7684\u73AF\u5883\u53D8\u91CF)\u4E0E\u53EF\u7528 ffmpeg;\u97F3\u8272\u652F\u6301 Cherry/Serena/Bella/Chelsie/Momo/Katerina \u7B49 qwen3-tts-flash \u5B98\u65B9\u97F3\u8272\u3002': "Requires a DashScope API key (the credential-store environment variable named by the credential reference above) and a working ffmpeg. Supported voices include the official qwen3-tts-flash voices Cherry/Serena/Bella/Chelsie/Momo/Katerina.",
1506
+ "\u4FDD\u5B58\u8BED\u97F3\u8BBE\u7F6E": "Save voice settings",
1507
+ "\u9ED8\u8BA4\u5173\u95ED;\u5F00\u542F\u540E\u673A\u5668\u4EBA\u53EF\u542C\u61C2\u8BED\u97F3\u6D88\u606F\u5E76\u4EE5\u8BED\u97F3\u56DE\u590D\u3002": "Off by default. When enabled, the bot understands voice messages and replies by voice.",
1508
+ "\u8BED\u97F3\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not save the voice settings. Try again.",
1489
1509
  "zcode\u8FC7\u7A0B\u5361\u6A21\u5F0F": "ZCode process-card mode",
1490
1510
  "\u8FC7\u7A0B\u8BF4\u660E\u3001\u5DE5\u5177\u6458\u8981\u4E0E\u6700\u7EC8\u7B54\u6848\u90FD\u5728\u540C\u4E00\u5F20\u5361\u7247\u4E2D\u539F\u5730\u5237\u65B0": "Process notes, the tool summary, and the final answer refresh in place inside one card",
1491
1511
  "\u601D\u8003\u4E0E\u5DE5\u5177\u6458\u8981\u6298\u53E0\u5C55\u793A\uFF0C\u6700\u7EC8\u7B54\u6848\u5728\u540C\u4E00\u5F20\u5361\u7247\u4E2D\u539F\u5730\u5237\u65B0": "Thinking and the tool summary stay folded; the final answer refreshes in place inside one card",
@@ -7198,6 +7218,29 @@ function normalizeFeishuStepPushMode(value) {
7198
7218
  return FEISHU_STEP_PUSH_MODES.POST;
7199
7219
  }
7200
7220
 
7221
+ // src/channels/feishu/voice-config.mjs
7222
+ var DEFAULT_ASR_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
7223
+ var DEFAULT_ASR_MODEL = "qwen3-asr-flash";
7224
+ var DEFAULT_TTS_MODEL = "qwen3-tts-flash";
7225
+ var DEFAULT_TTS_VOICE = "Momo";
7226
+ function cleanString(value) {
7227
+ return typeof value === "string" && value.trim() ? value.trim() : null;
7228
+ }
7229
+ function normalizeFeishuVoiceConfig(value) {
7230
+ if (value == null || typeof value !== "object" || value.enabled !== true) return null;
7231
+ const secretRef = cleanString(value.secretRef);
7232
+ if (!secretRef || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(secretRef)) return null;
7233
+ return Object.freeze({
7234
+ enabled: true,
7235
+ secretRef,
7236
+ asrModel: cleanString(value.asrModel) ?? DEFAULT_ASR_MODEL,
7237
+ ttsModel: cleanString(value.ttsModel) ?? DEFAULT_TTS_MODEL,
7238
+ ttsVoice: cleanString(value.ttsVoice) ?? DEFAULT_TTS_VOICE,
7239
+ asrBaseUrl: cleanString(value.asrBaseUrl) ?? DEFAULT_ASR_BASE_URL,
7240
+ ffmpeg: cleanString(value.ffmpeg)
7241
+ });
7242
+ }
7243
+
7201
7244
  // plugin-src/client/channels/feishu/api.js
7202
7245
  var FEISHU_RPC_CHANNEL = "/feishu";
7203
7246
  var FEISHU_ENDPOINTS = Object.freeze({
@@ -7221,6 +7264,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
7221
7264
  setGroupTopicReply: "bot.group-topic-reply.set",
7222
7265
  setStepPush: "bot.step-push.set",
7223
7266
  setStepPushMode: "bot.step-push-mode.set",
7267
+ setVoice: "bot.voice.set",
7224
7268
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
7225
7269
  testConnection: "connection.test",
7226
7270
  disconnect: "connection.disconnect"
@@ -7377,6 +7421,7 @@ function normalizeBotConnection(value, fallbackBotId) {
7377
7421
  groupTopicReply: value.groupTopicReply === true,
7378
7422
  stepPush: value.stepPush === true,
7379
7423
  stepPushMode: normalizeFeishuStepPushMode(value.stepPushMode),
7424
+ voice: normalizeFeishuVoiceConfig(value.voice),
7380
7425
  groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
7381
7426
  bot: normalizeBot2(value.bot),
7382
7427
  health: normalizeHealth(value.health, connected),
@@ -16075,7 +16120,7 @@ function installInterfaceLanguageMirror(ctx, {
16075
16120
  }
16076
16121
 
16077
16122
  // plugin-src/client/delivery-settings.js
16078
- var React30 = __toESM(require("react"), 1);
16123
+ var React32 = __toESM(require("react"), 1);
16079
16124
 
16080
16125
  // plugin-src/client/access-policy-settings.js
16081
16126
  var React28 = __toESM(require("react"), 1);
@@ -17078,6 +17123,200 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
17078
17123
  );
17079
17124
  }
17080
17125
 
17126
+ // plugin-src/client/channels/feishu/voice-settings.js
17127
+ var React31 = __toESM(require("react"), 1);
17128
+
17129
+ // plugin-src/client/channels/feishu/voice-editor.js
17130
+ var React30 = __toESM(require("react"), 1);
17131
+ function VoiceEditor({ value = null, disabled = false, onSave }) {
17132
+ const [enabled, setEnabled] = React30.useState(value != null);
17133
+ const [secretRef, setSecretRef] = React30.useState(value?.secretRef ?? "DASHSCOPE_API_KEY");
17134
+ const [asrModel, setAsrModel] = React30.useState(value?.asrModel ?? "qwen3-asr-flash");
17135
+ const [ttsModel, setTtsModel] = React30.useState(value?.ttsModel ?? "qwen3-tts-flash");
17136
+ const [ttsVoice, setTtsVoice] = React30.useState(value?.ttsVoice ?? "Momo");
17137
+ const [saving, setSaving] = React30.useState(false);
17138
+ const [error, setError] = React30.useState(null);
17139
+ const titleId = React30.useId();
17140
+ const helpId = `${titleId}-help`;
17141
+ React30.useEffect(() => {
17142
+ setEnabled(value != null);
17143
+ if (value) {
17144
+ setSecretRef(value.secretRef ?? "DASHSCOPE_API_KEY");
17145
+ setAsrModel(value.asrModel ?? "qwen3-asr-flash");
17146
+ setTtsModel(value.ttsModel ?? "qwen3-tts-flash");
17147
+ setTtsVoice(value.ttsVoice ?? "Momo");
17148
+ }
17149
+ }, [value]);
17150
+ const persist = async (payload) => {
17151
+ if (saving || disabled) return;
17152
+ setSaving(true);
17153
+ setError(null);
17154
+ try {
17155
+ await onSave?.(payload);
17156
+ } catch (cause) {
17157
+ setError(cause?.message ?? "\u8BED\u97F3\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
17158
+ } finally {
17159
+ setSaving(false);
17160
+ }
17161
+ };
17162
+ const save = () => persist(enabled ? { enabled: true, secretRef, asrModel, ttsModel, ttsVoice } : null);
17163
+ const toggle = (next) => {
17164
+ setEnabled(next);
17165
+ if (!next) void persist(null);
17166
+ };
17167
+ const input = (label, state, setState, placeholder) => h2(
17168
+ "label",
17169
+ { className: "dim-feishuVoiceField" },
17170
+ h2("span", { className: "dim-feishuVoiceLabel" }, label),
17171
+ h2("input", {
17172
+ type: "text",
17173
+ className: "dim-feishuVoiceInput",
17174
+ value: state,
17175
+ placeholder,
17176
+ disabled: disabled || saving,
17177
+ onChange: (event) => setState(event.target.value)
17178
+ })
17179
+ );
17180
+ return h2(
17181
+ "section",
17182
+ {
17183
+ className: "dim-feishuGroupControl",
17184
+ "aria-labelledby": titleId
17185
+ },
17186
+ h2(
17187
+ "div",
17188
+ { className: "dim-feishuGroupControlHeader" },
17189
+ h2(
17190
+ "div",
17191
+ { className: "dim-presetTitle" },
17192
+ h2("h3", { id: titleId }, "\u8BED\u97F3\u4EA4\u4E92"),
17193
+ h2(
17194
+ "span",
17195
+ { className: "dim-presetHelp" },
17196
+ h2("button", {
17197
+ type: "button",
17198
+ className: "dim-presetHelpButton",
17199
+ "aria-label": "\u67E5\u770B\u8BED\u97F3\u4EA4\u4E92\u8BF4\u660E",
17200
+ "aria-describedby": helpId
17201
+ }, h2("span", { "aria-hidden": "true" }, "?")),
17202
+ h2("span", {
17203
+ id: helpId,
17204
+ className: "dim-presetTooltip",
17205
+ role: "tooltip"
17206
+ }, "\u6536\u5230\u8BED\u97F3\u6D88\u606F\u65F6\u8F6C\u5199\u4E3A\u6587\u5B57\u5E76\u6309\u6587\u5B57\u6D41\u6C34\u7EBF\u5904\u7406,\u7B54\u6848\u540C\u65F6\u4EE5\u8BED\u97F3\u56DE\u590D;\u8F6C\u5199\u5931\u8D25\u65F6\u964D\u7EA7\u4E3A\u4EC5\u652F\u6301\u6587\u5B57/\u56FE\u7247/\u6587\u4EF6\u7684\u63D0\u793A\u3002\u51ED\u636E\u503C\u5728\u63D2\u4EF6\u51ED\u636E\u5E93\u91CC\u7BA1\u7406,\u8FD9\u91CC\u53EA\u5F15\u7528\u73AF\u5883\u53D8\u91CF\u540D\u3002")
17207
+ )
17208
+ ),
17209
+ saving ? h2("span", { className: "dim-feishuGroupControlStatus", role: "status" }, "\u4FDD\u5B58\u4E2D\u2026") : null
17210
+ ),
17211
+ h2(
17212
+ "select",
17213
+ {
17214
+ className: "dim-feishuGroupSelect",
17215
+ value: enabled ? "on" : "off",
17216
+ disabled: disabled || saving,
17217
+ "aria-label": "\u8BED\u97F3\u4EA4\u4E92\u5F00\u5173",
17218
+ onChange: (event) => toggle(event.target.value === "on")
17219
+ },
17220
+ h2("option", { value: "off" }, "\u5173\u95ED"),
17221
+ h2("option", { value: "on" }, "\u5F00\u542F(\u8BED\u97F3\u63D0\u95EE\u4E0E\u8BED\u97F3\u56DE\u590D)")
17222
+ ),
17223
+ enabled ? h2(
17224
+ "div",
17225
+ { className: "dim-feishuVoiceForm" },
17226
+ input("\u51ED\u636E\u5F15\u7528(\u73AF\u5883\u53D8\u91CF\u540D)", secretRef, setSecretRef, "DASHSCOPE_API_KEY"),
17227
+ input("\u8F6C\u5199\u6A21\u578B(ASR)", asrModel, setAsrModel, "qwen3-asr-flash"),
17228
+ input("\u5408\u6210\u6A21\u578B(TTS)", ttsModel, setTtsModel, "qwen3-tts-flash"),
17229
+ input("\u56DE\u590D\u97F3\u8272", ttsVoice, setTtsVoice, "Momo"),
17230
+ h2(
17231
+ "p",
17232
+ { className: "dim-feishuGroupHelp" },
17233
+ '\u9700\u8981 DashScope \u5BC6\u94A5(\u51ED\u636E\u5E93\u4E2D\u4EE5"\u51ED\u636E\u5F15\u7528"\u4E3A\u540D\u7684\u73AF\u5883\u53D8\u91CF)\u4E0E\u53EF\u7528 ffmpeg;\u97F3\u8272\u652F\u6301 Cherry/Serena/Bella/Chelsie/Momo/Katerina \u7B49 qwen3-tts-flash \u5B98\u65B9\u97F3\u8272\u3002'
17234
+ ),
17235
+ h2(
17236
+ "div",
17237
+ { className: "dim-feishuVoiceActions" },
17238
+ h2("button", {
17239
+ type: "button",
17240
+ className: "dim-feishuVoiceSave",
17241
+ onClick: () => void save(),
17242
+ disabled: disabled || saving || !secretRef.trim()
17243
+ }, "\u4FDD\u5B58\u8BED\u97F3\u8BBE\u7F6E")
17244
+ )
17245
+ ) : h2("p", { className: "dim-feishuGroupHelp" }, "\u9ED8\u8BA4\u5173\u95ED;\u5F00\u542F\u540E\u673A\u5668\u4EBA\u53EF\u542C\u61C2\u8BED\u97F3\u6D88\u606F\u5E76\u4EE5\u8BED\u97F3\u56DE\u590D\u3002"),
17246
+ error ? h2("p", {
17247
+ className: "dim-feishuGroupError",
17248
+ role: "alert"
17249
+ }, error) : null
17250
+ );
17251
+ }
17252
+
17253
+ // plugin-src/client/channels/feishu/voice-settings.js
17254
+ function FeishuVoiceSettingsPage({ account, rpcCall }) {
17255
+ const [voice, setVoice] = React31.useState(null);
17256
+ const [phase, setPhase] = React31.useState("loading");
17257
+ const [error, setError] = React31.useState(null);
17258
+ const mounted = React31.useRef(true);
17259
+ const invoke = React31.useCallback(async (endpoint, payload, signal) => {
17260
+ if (typeof rpcCall !== "function") throw new Error("\u98DE\u4E66\u8BED\u97F3\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
17261
+ return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
17262
+ }, [rpcCall]);
17263
+ const applySnapshot = React31.useCallback((value) => {
17264
+ const bot = normalizeBotsSnapshot(value).bots.find((entry) => entry.botId === account.botId);
17265
+ if (!bot) throw new Error("\u672A\u627E\u5230\u5F53\u524D\u98DE\u4E66\u673A\u5668\u4EBA\uFF0C\u8BF7\u8FD4\u56DE\u5217\u8868\u540E\u91CD\u8BD5\u3002");
17266
+ if (mounted.current) setVoice(bot.voice);
17267
+ }, [account.botId]);
17268
+ const loadSettings = React31.useCallback(async (signal) => {
17269
+ setPhase("loading");
17270
+ setError(null);
17271
+ try {
17272
+ const value = await invoke(FEISHU_ENDPOINTS.status, {}, signal);
17273
+ if (signal?.aborted || !mounted.current) return;
17274
+ applySnapshot(value);
17275
+ setPhase("ready");
17276
+ } catch (cause) {
17277
+ if (signal?.aborted || !mounted.current) return;
17278
+ setError(cause?.message ?? "\u98DE\u4E66\u8BED\u97F3\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
17279
+ setPhase("error");
17280
+ }
17281
+ }, [applySnapshot, invoke]);
17282
+ React31.useEffect(() => {
17283
+ mounted.current = true;
17284
+ const controller = new AbortController();
17285
+ void loadSettings(controller.signal);
17286
+ return () => {
17287
+ mounted.current = false;
17288
+ controller.abort();
17289
+ };
17290
+ }, [loadSettings]);
17291
+ const saveVoice = async (settings) => {
17292
+ const value = await invoke(FEISHU_ENDPOINTS.setVoice, {
17293
+ botId: account.botId,
17294
+ voice: settings === null ? null : { ...voice, ...settings }
17295
+ });
17296
+ if (mounted.current) applySnapshot(value);
17297
+ };
17298
+ if (phase === "loading") {
17299
+ return h2(
17300
+ "div",
17301
+ { className: "dim-deliveryState", role: "status", "aria-busy": "true" },
17302
+ "\u6B63\u5728\u8BFB\u53D6\u8BED\u97F3\u8BBE\u7F6E\u2026"
17303
+ );
17304
+ }
17305
+ if (phase === "error") {
17306
+ return h2(
17307
+ "div",
17308
+ { className: "dim-deliveryState", role: "alert" },
17309
+ h2("p", null, error),
17310
+ h2("button", {
17311
+ type: "button",
17312
+ className: "dim-deliveryButton",
17313
+ onClick: () => void loadSettings()
17314
+ }, "\u91CD\u65B0\u8BFB\u53D6")
17315
+ );
17316
+ }
17317
+ return h2(VoiceEditor, { value: voice, onSave: saveVoice });
17318
+ }
17319
+
17081
17320
  // plugin-src/client/delivery-settings.js
17082
17321
  var DELIVERY_RPC_CHANNEL = "/dsh-im-delivery";
17083
17322
  var DELIVERY_DOCS_URL = Object.freeze({
@@ -17099,7 +17338,8 @@ var BOT_SETTINGS_TABS = Object.freeze([
17099
17338
  ]);
17100
17339
  var FEISHU_BOT_SETTINGS_TABS = Object.freeze([
17101
17340
  ...BOT_SETTINGS_TABS,
17102
- Object.freeze({ id: "group", label: "\u7FA4\u804A" })
17341
+ Object.freeze({ id: "group", label: "\u7FA4\u804A" }),
17342
+ Object.freeze({ id: "voice", label: "\u8BED\u97F3\u4EA4\u4E92" })
17103
17343
  ]);
17104
17344
  function botSettingsTabsForChannel(channel7) {
17105
17345
  return channel7 === "feishu" ? FEISHU_BOT_SETTINGS_TABS : BOT_SETTINGS_TABS;
@@ -17311,13 +17551,13 @@ function TargetForm({
17311
17551
  }) {
17312
17552
  const editing = mode === "edit";
17313
17553
  const initialKind = initialValue?.kind && definition.fields[initialValue.kind] ? initialValue.kind : definition.kinds[0].value;
17314
- const [targetId, setTargetId] = React30.useState(initialValue?.targetId ?? "");
17315
- const [name2, setName] = React30.useState(initialValue?.name ?? "");
17316
- const [kind, setKind] = React30.useState(initialKind);
17317
- const [route, setRoute] = React30.useState(initialValue?.route ?? {});
17318
- const [error, setError] = React30.useState(null);
17319
- const [testing, setTesting] = React30.useState(false);
17320
- const [testState, setTestState] = React30.useState(null);
17554
+ const [targetId, setTargetId] = React32.useState(initialValue?.targetId ?? "");
17555
+ const [name2, setName] = React32.useState(initialValue?.name ?? "");
17556
+ const [kind, setKind] = React32.useState(initialKind);
17557
+ const [route, setRoute] = React32.useState(initialValue?.route ?? {});
17558
+ const [error, setError] = React32.useState(null);
17559
+ const [testing, setTesting] = React32.useState(false);
17560
+ const [testState, setTestState] = React32.useState(null);
17321
17561
  const currentTarget = () => {
17322
17562
  const normalizedRoute = Object.fromEntries(fieldsFor(definition, kind).map((field2) => {
17323
17563
  const raw = String(route[field2.key] ?? "").trim();
@@ -17534,7 +17774,7 @@ function TargetSuggestionPicker({
17534
17774
  suggestions.map((suggestion, index) => {
17535
17775
  const identity = routeIdentity(definition, suggestion);
17536
17776
  const added = configured.has(identity);
17537
- return React30.createElement("option", {
17777
+ return React32.createElement("option", {
17538
17778
  key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
17539
17779
  value: String(index),
17540
17780
  disabled: added
@@ -17551,11 +17791,11 @@ function TargetSuggestionPicker({
17551
17791
  );
17552
17792
  }
17553
17793
  function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
17554
- const [action, setAction] = React30.useState(null);
17555
- const [testState, setTestState] = React30.useState(null);
17556
- const [syncFeedback, setSyncFeedback] = React30.useState(null);
17557
- const [copyState, setCopyState] = React30.useState(null);
17558
- const [confirmDelete, setConfirmDelete] = React30.useState(false);
17794
+ const [action, setAction] = React32.useState(null);
17795
+ const [testState, setTestState] = React32.useState(null);
17796
+ const [syncFeedback, setSyncFeedback] = React32.useState(null);
17797
+ const [copyState, setCopyState] = React32.useState(null);
17798
+ const [confirmDelete, setConfirmDelete] = React32.useState(false);
17559
17799
  const sessionSync = target.sessionSync ?? { enabled: false, state: "unavailable" };
17560
17800
  const syncDescription = sessionSync.state === "active" ? "\u81EA\u52A8\u8DDF\u968F\u8BE5\u79C1\u804A\u7684\u5F53\u524D\u4F1A\u8BDD" : sessionSync.state === "waiting" ? "\u7B49\u5F85\u8BE5\u79C1\u804A\u5EFA\u7ACB\u65B0\u4F1A\u8BDD" : sessionSync.state === "unavailable" ? "\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807" : "\u5173\u95ED\u65F6\u4E0D\u53D1\u9001 DSH \u4F1A\u8BDD\u6D88\u606F";
17561
17801
  const testTarget = async () => {
@@ -17618,7 +17858,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
17618
17858
  h2(
17619
17859
  "div",
17620
17860
  { className: "dim-targetTitle" },
17621
- React30.createElement("strong", null, target.name || target.targetId),
17861
+ React32.createElement("strong", null, target.name || target.targetId),
17622
17862
  h2("span", null, kindLabel(definition, target.kind))
17623
17863
  ),
17624
17864
  h2("code", null, `targetId: ${target.targetId}`)
@@ -17704,26 +17944,26 @@ function DeliveryTargetSettingsPage({
17704
17944
  onBack
17705
17945
  }) {
17706
17946
  const definition = CHANNEL_DEFINITIONS[channel7];
17707
- const [activeTabId, setActiveTabId] = React30.useState(BOT_SETTINGS_TABS[0].id);
17708
- const [phase, setPhase] = React30.useState("loading");
17709
- const [targets, setTargets] = React30.useState([]);
17710
- const [suggestionPhase, setSuggestionPhase] = React30.useState("idle");
17711
- const [suggestions, setSuggestions] = React30.useState([]);
17712
- const [suggestionError, setSuggestionError] = React30.useState(null);
17713
- const [error, setError] = React30.useState(null);
17714
- const [editor, setEditor] = React30.useState(null);
17715
- const [saving, setSaving] = React30.useState(false);
17716
- const [botCopyState, setBotCopyState] = React30.useState(null);
17717
- const [accessPolicy, setAccessPolicy] = React30.useState(account.accessPolicy);
17718
- const mounted = React30.useRef(true);
17719
- React30.useEffect(() => {
17947
+ const [activeTabId, setActiveTabId] = React32.useState(BOT_SETTINGS_TABS[0].id);
17948
+ const [phase, setPhase] = React32.useState("loading");
17949
+ const [targets, setTargets] = React32.useState([]);
17950
+ const [suggestionPhase, setSuggestionPhase] = React32.useState("idle");
17951
+ const [suggestions, setSuggestions] = React32.useState([]);
17952
+ const [suggestionError, setSuggestionError] = React32.useState(null);
17953
+ const [error, setError] = React32.useState(null);
17954
+ const [editor, setEditor] = React32.useState(null);
17955
+ const [saving, setSaving] = React32.useState(false);
17956
+ const [botCopyState, setBotCopyState] = React32.useState(null);
17957
+ const [accessPolicy, setAccessPolicy] = React32.useState(account.accessPolicy);
17958
+ const mounted = React32.useRef(true);
17959
+ React32.useEffect(() => {
17720
17960
  setAccessPolicy(account.accessPolicy);
17721
17961
  }, [account.botId, account.accessPolicy]);
17722
- const invoke = React30.useCallback(async (endpoint, payload = {}, signal) => {
17962
+ const invoke = React32.useCallback(async (endpoint, payload = {}, signal) => {
17723
17963
  if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
17724
17964
  return unwrapRpcResult15(await rpcCall(endpoint, payload, signal));
17725
17965
  }, [rpcCall]);
17726
- const loadTargets = React30.useCallback(async ({ signal, silent = false } = {}) => {
17966
+ const loadTargets = React32.useCallback(async ({ signal, silent = false } = {}) => {
17727
17967
  if (!silent) setPhase("loading");
17728
17968
  setError(null);
17729
17969
  try {
@@ -17737,7 +17977,7 @@ function DeliveryTargetSettingsPage({
17737
17977
  setPhase("error");
17738
17978
  }
17739
17979
  }, [account.botId, invoke]);
17740
- const loadSuggestions = React30.useCallback(async () => {
17980
+ const loadSuggestions = React32.useCallback(async () => {
17741
17981
  setSuggestionPhase("loading");
17742
17982
  setSuggestionError(null);
17743
17983
  try {
@@ -17751,7 +17991,7 @@ function DeliveryTargetSettingsPage({
17751
17991
  setSuggestionPhase("error");
17752
17992
  }
17753
17993
  }, [account.botId, definition, invoke]);
17754
- React30.useEffect(() => {
17994
+ React32.useEffect(() => {
17755
17995
  mounted.current = true;
17756
17996
  const controller = new AbortController();
17757
17997
  void loadTargets({ signal: controller.signal });
@@ -17872,8 +18112,12 @@ function DeliveryTargetSettingsPage({
17872
18112
  }) : activeTab.id === "group" && channel7 === "feishu" ? h2(FeishuGroupSettingsPage, {
17873
18113
  account,
17874
18114
  rpcCall: accessRpcCall
18115
+ }) : activeTab.id === "voice" && channel7 === "feishu" ? h2(FeishuVoiceSettingsPage, {
18116
+ key: account.botId,
18117
+ account,
18118
+ rpcCall: accessRpcCall
17875
18119
  }) : h2(
17876
- React30.Fragment,
18120
+ React32.Fragment,
17877
18121
  null,
17878
18122
  h2(
17879
18123
  "section",
@@ -17984,7 +18228,7 @@ function DeliveryTargetSettingsPage({
17984
18228
  }
17985
18229
 
17986
18230
  // plugin-src/client/global-settings.js
17987
- var React31 = __toESM(require("react"), 1);
18231
+ var React33 = __toESM(require("react"), 1);
17988
18232
 
17989
18233
  // src/channels/shared/inbound-ttl.mjs
17990
18234
  var DEFAULT_INBOUND_TTL_HOURS = 168;
@@ -18069,14 +18313,14 @@ function GlobalButton({ children, kind = "secondary", className = "", ...props }
18069
18313
  }, children);
18070
18314
  }
18071
18315
  function ImageInputSettings({ rpcCall }) {
18072
- const [values, setValues] = React31.useState(DEFAULT_IMAGE_INPUT_SETTINGS);
18073
- const [phase, setPhase] = React31.useState("loading");
18074
- const [error, setError] = React31.useState(null);
18075
- const [saved, setSaved] = React31.useState(false);
18076
- const mounted = React31.useRef(false);
18077
- const saving = React31.useRef(false);
18078
- const id6 = React31.useId();
18079
- const load = React31.useCallback(async (signal) => {
18316
+ const [values, setValues] = React33.useState(DEFAULT_IMAGE_INPUT_SETTINGS);
18317
+ const [phase, setPhase] = React33.useState("loading");
18318
+ const [error, setError] = React33.useState(null);
18319
+ const [saved, setSaved] = React33.useState(false);
18320
+ const mounted = React33.useRef(false);
18321
+ const saving = React33.useRef(false);
18322
+ const id6 = React33.useId();
18323
+ const load = React33.useCallback(async (signal) => {
18080
18324
  setPhase("loading");
18081
18325
  setError(null);
18082
18326
  try {
@@ -18092,7 +18336,7 @@ function ImageInputSettings({ rpcCall }) {
18092
18336
  setPhase("error");
18093
18337
  }
18094
18338
  }, [rpcCall]);
18095
- React31.useEffect(() => {
18339
+ React33.useEffect(() => {
18096
18340
  mounted.current = true;
18097
18341
  const controller = new AbortController();
18098
18342
  void load(controller.signal);
@@ -18199,29 +18443,29 @@ function ImageInputSettings({ rpcCall }) {
18199
18443
  );
18200
18444
  }
18201
18445
  function GlobalSettingsPanel({ rpcCall }) {
18202
- const [phase, setPhase] = React31.useState("loading");
18203
- const [loadError, setLoadError] = React31.useState(null);
18204
- const [ttlInput, setTtlInput] = React31.useState("");
18205
- const [savedTtl, setSavedTtl] = React31.useState(null);
18206
- const [ttlError, setTtlError] = React31.useState(false);
18207
- const [saveError, setSaveError] = React31.useState(null);
18208
- const [saveSucceeded, setSaveSucceeded] = React31.useState(false);
18209
- const [isSaving, setIsSaving] = React31.useState(false);
18210
- const [sweepConfirming, setSweepConfirming] = React31.useState(false);
18211
- const [sweeping, setSweeping] = React31.useState(false);
18212
- const ttlErrorId = React31.useId();
18213
- const ttlHintsId = React31.useId();
18214
- const sweepTriggerId = React31.useId();
18215
- const sweepConfirmId = React31.useId();
18216
- const sweepConfirmTextId = React31.useId();
18217
- const sweepConfirmButtonId = React31.useId();
18218
- const mounted = React31.useRef(true);
18219
- const saving = React31.useRef(false);
18220
- const invoke = React31.useCallback(async (endpoint, payload = {}, signal) => {
18446
+ const [phase, setPhase] = React33.useState("loading");
18447
+ const [loadError, setLoadError] = React33.useState(null);
18448
+ const [ttlInput, setTtlInput] = React33.useState("");
18449
+ const [savedTtl, setSavedTtl] = React33.useState(null);
18450
+ const [ttlError, setTtlError] = React33.useState(false);
18451
+ const [saveError, setSaveError] = React33.useState(null);
18452
+ const [saveSucceeded, setSaveSucceeded] = React33.useState(false);
18453
+ const [isSaving, setIsSaving] = React33.useState(false);
18454
+ const [sweepConfirming, setSweepConfirming] = React33.useState(false);
18455
+ const [sweeping, setSweeping] = React33.useState(false);
18456
+ const ttlErrorId = React33.useId();
18457
+ const ttlHintsId = React33.useId();
18458
+ const sweepTriggerId = React33.useId();
18459
+ const sweepConfirmId = React33.useId();
18460
+ const sweepConfirmTextId = React33.useId();
18461
+ const sweepConfirmButtonId = React33.useId();
18462
+ const mounted = React33.useRef(true);
18463
+ const saving = React33.useRef(false);
18464
+ const invoke = React33.useCallback(async (endpoint, payload = {}, signal) => {
18221
18465
  if (typeof rpcCall !== "function") throw new Error("\u901A\u7528\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
18222
18466
  return unwrapRpcResult16(await rpcCall(endpoint, payload, signal));
18223
18467
  }, [rpcCall]);
18224
- const loadSettings = React31.useCallback(async ({ signal } = {}) => {
18468
+ const loadSettings = React33.useCallback(async ({ signal } = {}) => {
18225
18469
  setPhase("loading");
18226
18470
  setLoadError(null);
18227
18471
  try {
@@ -18245,7 +18489,7 @@ function GlobalSettingsPanel({ rpcCall }) {
18245
18489
  setPhase("error");
18246
18490
  }
18247
18491
  }, [invoke]);
18248
- React31.useEffect(() => {
18492
+ React33.useEffect(() => {
18249
18493
  mounted.current = true;
18250
18494
  const controller = new AbortController();
18251
18495
  void loadSettings({ signal: controller.signal });
@@ -18254,7 +18498,7 @@ function GlobalSettingsPanel({ rpcCall }) {
18254
18498
  controller.abort();
18255
18499
  };
18256
18500
  }, [loadSettings]);
18257
- React31.useEffect(() => {
18501
+ React33.useEffect(() => {
18258
18502
  if (!sweepConfirming) return;
18259
18503
  globalThis.document?.getElementById(sweepConfirmButtonId)?.focus();
18260
18504
  }, [sweepConfirmButtonId, sweepConfirming]);
@@ -19041,6 +19285,17 @@ var CSS15 = String.raw`
19041
19285
  .dim-feishuGroupSelect:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary, #3370ff); box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 15%, transparent); }
19042
19286
  .dim-feishuGroupSelect:disabled { cursor: not-allowed; opacity: .55; }
19043
19287
  .dim-feishuGroupHelp { margin: -2px 0 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; line-height: 19px; }
19288
+ .dim-feishuVoiceForm { min-width: 0; display: grid; gap: 10px; }
19289
+ .dim-feishuVoiceField { min-width: 0; display: grid; gap: 5px; }
19290
+ .dim-feishuVoiceLabel { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
19291
+ .dim-feishuVoiceInput { min-width: 0; width: 100%; height: 36px; padding: 0 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; line-height: 36px; transition: border-color .15s ease, box-shadow .15s ease; }
19292
+ .dim-feishuVoiceInput:hover:not(:disabled) { border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 42%, var(--dsw-alias-border-l2, #dfe1e5)); }
19293
+ .dim-feishuVoiceInput:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary, #3370ff); box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 15%, transparent); }
19294
+ .dim-feishuVoiceInput:disabled { cursor: not-allowed; opacity: .55; }
19295
+ .dim-feishuVoiceActions { display: flex; justify-content: flex-start; }
19296
+ .dim-feishuVoiceSave { height: 34px; padding: 0 15px; border: 1px solid var(--dsw-alias-state-business-primary, #3370ff); border-radius: 8px; color: var(--dsw-alias-state-business-primary, #3370ff); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; line-height: 32px; cursor: pointer; transition: border-color .15s ease, color .15s ease, background .15s ease; }
19297
+ .dim-feishuVoiceSave:hover:not(:disabled) { color: #0f5fce; border-color: #0f5fce; background: color-mix(in srgb, #3370ff 6%, var(--dsw-alias-bg-layer-1, #fff)); }
19298
+ .dim-feishuVoiceSave:disabled { cursor: not-allowed; opacity: .55; }
19044
19299
  .dim-feishuGroupPermissionAction { display: flex; justify-content: flex-start; }
19045
19300
  .dim-feishuGroupPermissionAction .dim-deliveryButton { color: var(--dsw-alias-state-business-primary, #3370ff); border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 30%, var(--dsw-alias-border-l2, #dfe1e5)); }
19046
19301
  .dim-feishuGroupError, .dim-feishuGroupRefreshError { margin: 0; padding: 9px 11px; border-radius: 8px; font-size: 12px; line-height: 18px; }
@@ -19555,7 +19810,7 @@ function installSessionChannelLogos(document2 = globalThis.document) {
19555
19810
  }
19556
19811
 
19557
19812
  // plugin-src/client/update-panel.js
19558
- var React32 = __toESM(require("react"), 1);
19813
+ var React34 = __toESM(require("react"), 1);
19559
19814
  var import_react_dom4 = require("react-dom");
19560
19815
  var import_valid = __toESM(require_valid(), 1);
19561
19816
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -19648,11 +19903,11 @@ function manualUpdateCommand(snapshot) {
19648
19903
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
19649
19904
  }
19650
19905
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
19651
- const [copyState, setCopyState] = React32.useState("idle");
19652
- const commandRef = React32.useRef(null);
19653
- const mounted = React32.useRef(false);
19654
- const copying = React32.useRef(false);
19655
- React32.useEffect(() => {
19906
+ const [copyState, setCopyState] = React34.useState("idle");
19907
+ const commandRef = React34.useRef(null);
19908
+ const mounted = React34.useRef(false);
19909
+ const copying = React34.useRef(false);
19910
+ React34.useEffect(() => {
19656
19911
  mounted.current = true;
19657
19912
  return () => {
19658
19913
  mounted.current = false;
@@ -19684,7 +19939,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
19684
19939
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
19685
19940
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
19686
19941
  command ? h2(
19687
- React32.Fragment,
19942
+ React34.Fragment,
19688
19943
  null,
19689
19944
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
19690
19945
  h2(
@@ -19720,7 +19975,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
19720
19975
  "aria-hidden": "true",
19721
19976
  focusable: "false"
19722
19977
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
19723
- React32.Fragment,
19978
+ React34.Fragment,
19724
19979
  null,
19725
19980
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
19726
19981
  h2("path", { d: "M5 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V5" })
@@ -19745,10 +20000,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
19745
20000
  );
19746
20001
  }
19747
20002
  function UpdateDialog({ children, onClose }) {
19748
- const dialogRef = React32.useRef(null);
19749
- const titleId = React32.useId();
19750
- const descriptionId = React32.useId();
19751
- React32.useEffect(() => {
20003
+ const dialogRef = React34.useRef(null);
20004
+ const titleId = React34.useId();
20005
+ const descriptionId = React34.useId();
20006
+ React34.useEffect(() => {
19752
20007
  const previous = globalThis.document?.activeElement;
19753
20008
  dialogRef.current?.focus?.();
19754
20009
  return () => {
@@ -19806,20 +20061,20 @@ function UpdateDialog({ children, onClose }) {
19806
20061
  return typeof document !== "undefined" && document.body ? (0, import_react_dom4.createPortal)(content, document.body) : content;
19807
20062
  }
19808
20063
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
19809
- const tooltipId = React32.useId();
19810
- const [snapshot, setSnapshot] = React32.useState(null);
19811
- const [action, setAction] = React32.useState("status");
19812
- const [error, setError] = React32.useState(null);
19813
- const [open, setOpen] = React32.useState(false);
19814
- const [uncertainInstall, setUncertainInstall] = React32.useState(false);
19815
- const mounted = React32.useRef(false);
19816
- const busy = React32.useRef(false);
19817
- const readController = React32.useRef(null);
19818
- const pollReadController = React32.useRef(null);
19819
- const installRequest = React32.useRef(null);
19820
- const onStatusRef = React32.useRef(onStatus);
20064
+ const tooltipId = React34.useId();
20065
+ const [snapshot, setSnapshot] = React34.useState(null);
20066
+ const [action, setAction] = React34.useState("status");
20067
+ const [error, setError] = React34.useState(null);
20068
+ const [open, setOpen] = React34.useState(false);
20069
+ const [uncertainInstall, setUncertainInstall] = React34.useState(false);
20070
+ const mounted = React34.useRef(false);
20071
+ const busy = React34.useRef(false);
20072
+ const readController = React34.useRef(null);
20073
+ const pollReadController = React34.useRef(null);
20074
+ const installRequest = React34.useRef(null);
20075
+ const onStatusRef = React34.useRef(onStatus);
19821
20076
  onStatusRef.current = onStatus;
19822
- const accept = React32.useCallback((next) => {
20077
+ const accept = React34.useCallback((next) => {
19823
20078
  setSnapshot(next);
19824
20079
  onStatusRef.current?.(next);
19825
20080
  }, []);
@@ -19828,7 +20083,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
19828
20083
  setUncertainInstall(false);
19829
20084
  accept(next);
19830
20085
  };
19831
- const invoke = React32.useCallback(async (endpoint, payload = {}, signal) => {
20086
+ const invoke = React34.useCallback(async (endpoint, payload = {}, signal) => {
19832
20087
  if (typeof rpcCall !== "function") {
19833
20088
  const unavailable = new Error("\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002");
19834
20089
  unavailable.code = "update-unavailable";
@@ -19836,7 +20091,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
19836
20091
  }
19837
20092
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
19838
20093
  }, [rpcCall]);
19839
- React32.useEffect(() => {
20094
+ React34.useEffect(() => {
19840
20095
  mounted.current = true;
19841
20096
  busy.current = true;
19842
20097
  const controller = new AbortController();
@@ -19859,7 +20114,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
19859
20114
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
19860
20115
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
19861
20116
  const shouldPoll = activeJob || uncertainInstall;
19862
- React32.useEffect(() => {
20117
+ React34.useEffect(() => {
19863
20118
  if (!shouldPoll) return void 0;
19864
20119
  let controller;
19865
20120
  const scheduler = createPollScheduler({
@@ -19960,7 +20215,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
19960
20215
  const buttonLabel = action === "checking" ? "\u68C0\u67E5\u4E2D\u2026" : action === "starting" || activeJob ? "\u6B63\u5728\u66F4\u65B0\u2026" : restartRequired ? "\u5F85\u624B\u52A8\u91CD\u542F" : snapshot?.canInstall ? "\u66F4\u65B0\u81F3" : "\u68C0\u67E5\u66F4\u65B0";
19961
20216
  const buttonDescription = localizeText(buttonLabel) + (buttonLabel === "\u66F4\u65B0\u81F3" ? ` v${snapshot.latestVersion}` : "");
19962
20217
  return h2(
19963
- React32.Fragment,
20218
+ React34.Fragment,
19964
20219
  null,
19965
20220
  h2(
19966
20221
  "span",
@@ -20010,25 +20265,25 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20010
20265
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
20011
20266
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
20012
20267
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
20013
- React32.Fragment,
20268
+ React34.Fragment,
20014
20269
  null,
20015
20270
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
20016
20271
  h2("dd", null, `v${snapshot.installedVersion}`)
20017
20272
  ) : null,
20018
20273
  targetVersion ? h2(
20019
- React32.Fragment,
20274
+ React34.Fragment,
20020
20275
  null,
20021
20276
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
20022
20277
  h2("dd", null, `v${targetVersion}`)
20023
20278
  ) : null,
20024
20279
  latestVersion ? h2(
20025
- React32.Fragment,
20280
+ React34.Fragment,
20026
20281
  null,
20027
20282
  h2("dt", null, "\u6700\u65B0\u7248\u672C"),
20028
20283
  h2("dd", null, `v${latestVersion}`)
20029
20284
  ) : null,
20030
20285
  historicalTargetVersion ? h2(
20031
- React32.Fragment,
20286
+ React34.Fragment,
20032
20287
  null,
20033
20288
  h2("dt", null, "\u4E0A\u6B21\u66F4\u65B0\u76EE\u6807"),
20034
20289
  h2("dd", null, `v${historicalTargetVersion}`)
@@ -20110,8 +20365,8 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20110
20365
  }
20111
20366
 
20112
20367
  // plugin-src/client/panel-error-boundary.js
20113
- var React33 = __toESM(require("react"), 1);
20114
- var IMPanelErrorBoundary = class extends React33.Component {
20368
+ var React35 = __toESM(require("react"), 1);
20369
+ var IMPanelErrorBoundary = class extends React35.Component {
20115
20370
  state = { failed: false };
20116
20371
  static getDerivedStateFromError() {
20117
20372
  return { failed: true };
@@ -20304,28 +20559,28 @@ function IMSettingsTab({
20304
20559
  browserLocation = globalThis.location,
20305
20560
  navigateToRecoveryUrl = replacePageLocation
20306
20561
  }) {
20307
- const [selected, setSelected] = React34.useState(() => preferredSectionId === GLOBAL_SETTINGS_TAB_ID || CHANNELS.some((channel7) => channel7.id === preferredSectionId) ? preferredSectionId : "weixin");
20308
- const [loopbackRecovery, setLoopbackRecovery] = React34.useState(null);
20309
- const [runningVersion, setRunningVersion] = React34.useState(IM_PLUGIN_VERSION);
20310
- const [deliverySettings, setDeliverySettings] = React34.useState(null);
20562
+ const [selected, setSelected] = React36.useState(() => preferredSectionId === GLOBAL_SETTINGS_TAB_ID || CHANNELS.some((channel7) => channel7.id === preferredSectionId) ? preferredSectionId : "weixin");
20563
+ const [loopbackRecovery, setLoopbackRecovery] = React36.useState(null);
20564
+ const [runningVersion, setRunningVersion] = React36.useState(IM_PLUGIN_VERSION);
20565
+ const [deliverySettings, setDeliverySettings] = React36.useState(null);
20311
20566
  const emailEnabled = useEmailChannelEnabled(emailRpcCall);
20312
- const visibleChannels = React34.useMemo(
20567
+ const visibleChannels = React36.useMemo(
20313
20568
  () => CHANNELS.filter((channel7) => channel7.id !== "email" || emailEnabled),
20314
20569
  [emailEnabled]
20315
20570
  );
20316
- const githubTooltipId = React34.useId();
20317
- const generalSettingsTooltipId = React34.useId();
20571
+ const githubTooltipId = React36.useId();
20572
+ const generalSettingsTooltipId = React36.useId();
20318
20573
  const globalSettingsSelected = selected === GLOBAL_SETTINGS_TAB_ID;
20319
20574
  const active = visibleChannels.find((channel7) => channel7.id === selected) ?? visibleChannels[0];
20320
20575
  const activeTabId = globalSettingsSelected ? "dim-general-settings-trigger" : `dim-tab-${active.id}`;
20321
20576
  const activePanelId = globalSettingsSelected ? `dim-panel-${GLOBAL_SETTINGS_TAB_ID}` : `dim-panel-${active.id}`;
20322
- const reportLoopbackRecovery = React34.useCallback((recovery) => {
20577
+ const reportLoopbackRecovery = React36.useCallback((recovery) => {
20323
20578
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
20324
20579
  }, []);
20325
- const reportUpdateStatus = React34.useCallback((snapshot) => {
20580
+ const reportUpdateStatus = React36.useCallback((snapshot) => {
20326
20581
  setRunningVersion(snapshot.runningVersion);
20327
20582
  }, []);
20328
- const rpcCalls = React34.useMemo(() => createLoopbackAwareRpcCalls({
20583
+ const rpcCalls = React36.useMemo(() => createLoopbackAwareRpcCalls({
20329
20584
  dingtalkRpcCall,
20330
20585
  discordRpcCall,
20331
20586
  feishuRpcCall,
@@ -20367,7 +20622,7 @@ function IMSettingsTab({
20367
20622
  weixinRpcCall,
20368
20623
  whatsappRpcCall
20369
20624
  ]);
20370
- const botSettingsContext = React34.useMemo(() => Object.freeze({
20625
+ const botSettingsContext = React36.useMemo(() => Object.freeze({
20371
20626
  openBotSettings: setDeliverySettings
20372
20627
  }), []);
20373
20628
  return h2(
@@ -20591,7 +20846,7 @@ function apply(ctx) {
20591
20846
  };
20592
20847
  const localeSnapshot = () => ctx.locale.getLocale?.()?.active ?? "";
20593
20848
  function IMPanel({ preferredSectionId }) {
20594
- React34.useSyncExternalStore(subscribeLocale, localeSnapshot, localeSnapshot);
20849
+ React36.useSyncExternalStore(subscribeLocale, localeSnapshot, localeSnapshot);
20595
20850
  return h2(
20596
20851
  IMPanelErrorBoundary,
20597
20852
  null,