@xmanrui/dsh-im 4.9.0 → 4.10.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 (47) hide show
  1. package/PROACTIVE_DELIVERY.en.md +8 -0
  2. package/PROACTIVE_DELIVERY.md +8 -0
  3. package/lib/client.js +679 -73
  4. package/lib/index.js +276 -275
  5. package/package.json +21 -6
  6. package/plugin-src/client/channels/dingtalk/api.js +23 -6
  7. package/plugin-src/client/channels/dingtalk/index.js +33 -6
  8. package/plugin-src/client/channels/dingtalk/styles.js +2 -0
  9. package/plugin-src/client/channels/feishu/api.js +2 -0
  10. package/plugin-src/client/channels/feishu/index.js +54 -0
  11. package/plugin-src/client/global-settings.js +341 -0
  12. package/plugin-src/client/i18n.js +33 -0
  13. package/plugin-src/client/index.js +47 -6
  14. package/plugin-src/client/styles.js +49 -0
  15. package/plugin-src/host/channels/dingtalk/production.mjs +12 -0
  16. package/plugin-src/host/channels/dingtalk/rpc.mjs +24 -0
  17. package/plugin-src/host/channels/feishu/production.mjs +14 -0
  18. package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
  19. package/plugin-src/host/channels/qq/production.mjs +12 -0
  20. package/plugin-src/host/channels/shared/production.mjs +12 -0
  21. package/plugin-src/host/channels/slack/production.mjs +12 -0
  22. package/plugin-src/host/channels/wecom/production.mjs +12 -0
  23. package/plugin-src/host/channels/weixin/production.mjs +12 -0
  24. package/plugin-src/host/channels/whatsapp/production.mjs +12 -0
  25. package/plugin-src/host/delivery-adapter.mjs +1 -0
  26. package/plugin-src/host/delivery-service.mjs +15 -0
  27. package/plugin-src/host/harness-session-coordinator.mjs +26 -5
  28. package/plugin-src/host/inbound-ttl-rpc.mjs +83 -0
  29. package/plugin-src/host/inbound-ttl-runtime.mjs +85 -0
  30. package/plugin-src/host/inbound-ttl-service.mjs +179 -0
  31. package/plugin-src/host/index.mjs +8 -0
  32. package/plugin-src/host/modern-harness-api.mjs +75 -10
  33. package/src/channels/dingtalk/connection-error.mjs +299 -0
  34. package/src/channels/dingtalk/dingtalk-controller.mjs +79 -34
  35. package/src/channels/dingtalk/dingtalk-runtime.mjs +9 -2
  36. package/src/channels/feishu/bridge.mjs +164 -4
  37. package/src/channels/feishu/feishu-channel.mjs +45 -10
  38. package/src/channels/feishu/feishu-runtime.mjs +9 -0
  39. package/src/channels/feishu/message-utils.mjs +13 -0
  40. package/src/channels/feishu/multi-bot-controller.mjs +16 -0
  41. package/src/channels/feishu/plugin-config-store.mjs +1 -0
  42. package/src/channels/feishu/state-store.mjs +25 -0
  43. package/src/channels/shared/bot-workspace-store.mjs +6 -0
  44. package/src/channels/shared/i18n-en/dingtalk.mjs +23 -0
  45. package/src/channels/shared/inbound-file.mjs +117 -2
  46. package/src/channels/shared/inbound-ttl-store.mjs +110 -0
  47. package/src/channels/shared/inbound-ttl.mjs +22 -0
package/lib/client.js CHANGED
@@ -566,12 +566,12 @@ __export(index_exports, {
566
566
  name: () => name
567
567
  });
568
568
  module.exports = __toCommonJS(index_exports);
569
- var React23 = __toESM(require("react"), 1);
569
+ var React24 = __toESM(require("react"), 1);
570
570
 
571
571
  // package.json
572
572
  var package_default = {
573
573
  name: "@xmanrui/dsh-im",
574
- version: "4.9.0",
574
+ version: "4.10.0",
575
575
  description: "\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
576
576
  keywords: [
577
577
  "deepseek-harness",
@@ -593,10 +593,7 @@ var package_default = {
593
593
  "whatsapp",
594
594
  "ai-office"
595
595
  ],
596
- author: {
597
- name: "xmanrui",
598
- url: "https://github.com/xmanrui"
599
- },
596
+ author: "xmanrui (https://github.com/xmanrui)",
600
597
  contributors: [
601
598
  {
602
599
  name: "C3H3-AI",
@@ -618,7 +615,9 @@ var package_default = {
618
615
  url: "git+https://github.com/xmanrui/dsh-im.git"
619
616
  },
620
617
  homepage: "https://github.com/xmanrui/dsh-im#readme",
621
- bugs: "https://github.com/xmanrui/dsh-im/issues",
618
+ bugs: {
619
+ url: "https://github.com/xmanrui/dsh-im/issues"
620
+ },
622
621
  publishConfig: {
623
622
  access: "public"
624
623
  },
@@ -659,6 +658,17 @@ var package_default = {
659
658
  "@deepseek-ai/dsh-client-locale"
660
659
  ],
661
660
  platform: "web"
661
+ },
662
+ compatibility: {
663
+ dsh: "0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1",
664
+ dshReleases: {
665
+ "0.1.2-alpha.4": "compatible",
666
+ "0.1.2-alpha.5": "compatible",
667
+ "0.1.2-rc.1": "compatible"
668
+ },
669
+ profiles: [
670
+ "web"
671
+ ]
662
672
  }
663
673
  },
664
674
  scripts: {
@@ -687,6 +697,11 @@ var package_default = {
687
697
  "react-dom": "18.3.1",
688
698
  "react-test-renderer": "18.3.1",
689
699
  semver: "7.8.5"
700
+ },
701
+ directories: {
702
+ doc: "docs",
703
+ lib: "lib",
704
+ test: "test"
690
705
  }
691
706
  };
692
707
 
@@ -1053,6 +1068,11 @@ var EN = Object.freeze({
1053
1068
  "\u9700\u5728\u98DE\u4E66\u4E3A\u8BE5\u673A\u5668\u4EBA\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u5F00\u901A\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002": "Grant this bot the \u201CRead all messages in associated group chat\u201D Feishu scope (im:message.group_msg); once granted, it will process every visible group message.",
1054
1069
  "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not update the group response mode. Try again.",
1055
1070
  "\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not authorize group-message permission. Try again.",
1071
+ "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D": "Reply to group chats as Feishu topics",
1072
+ "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not save the topic reply setting. Try again.",
1073
+ "\u5173\u95ED\uFF08\u7FA4\u5185\u76F4\u63A5\u56DE\u590D\uFF09": "Off (reply in the group feed)",
1074
+ "\u5F00\u542F\uFF08\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF09": "On (auto-open a dedicated Feishu topic)",
1075
+ "\u5F00\u542F\u540E\uFF0C\u7FA4\u804A\u4E2D\u5411\u673A\u5668\u4EBA\u63D0\u95EE\u4F1A\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF0C\u56DE\u590D\u843D\u5728\u8BDD\u9898\u5185\uFF1B\u6BCF\u4E2A\u8BDD\u9898\u662F dsh \u4F1A\u8BDD\u5217\u8868\u91CC\u4E00\u6761\u72EC\u7ACB\u4F1A\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u4E92\u4E0D\u4E32\u3002\u79C1\u804A\u4E0D\u53D7\u5F71\u54CD\u3002": "When enabled, questions to the bot in a group auto-open a dedicated Feishu topic and replies stay inside it. Each topic is an independent conversation in dsh with its own context. Private chats are unaffected.",
1056
1076
  "\u9489\u9489\u8BBE\u7F6E": "DingTalk settings",
1057
1077
  "\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E": "WeCom settings",
1058
1078
  "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA": "Scan QR code",
@@ -1189,6 +1209,8 @@ var EN = Object.freeze({
1189
1209
  "\u68C0\u67E5\u8FDE\u63A5": "Check connection",
1190
1210
  "\u68C0\u67E5\u4E2D\u2026": "Checking\u2026",
1191
1211
  "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Connection check failed. Try again later.",
1212
+ "\u673A\u5668\u4EBA\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u8FDE\u63A5\u672A\u5C31\u7EEA": "The bot was saved, but the connection is not ready",
1213
+ "\u67E5\u770B\u5DF2\u4FDD\u5B58\u7684\u673A\u5668\u4EBA": "View saved bot",
1192
1214
  "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u5BF9\u5E94\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002": "Test message sent. Check the matching bot conversation.",
1193
1215
  "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u673A\u5668\u4EBA\u5C1A\u672A\u6536\u5230\u53EF\u7528\u4E8E\u6D4B\u8BD5\u7684\u79C1\u804A\u6D88\u606F\u3002": "Connection check completed. The bot has not received a direct message it can use for testing.",
1194
1216
  "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002": "Connection check completed, but the test message could not be sent.",
@@ -1672,7 +1694,29 @@ var EN = Object.freeze({
1672
1694
  "\u65B0\u5EFA\u76EE\u6807": "New target",
1673
1695
  "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026": "Loading delivery targets\u2026",
1674
1696
  "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807": "No delivery targets yet",
1675
- "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002": "Select New target to choose a conversation or enter the details manually."
1697
+ "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002": "Select New target to choose a conversation or enter the details manually.",
1698
+ "\u5168\u5C40\u8BBE\u7F6E": "Global settings",
1699
+ "\u901A\u7528\u8BBE\u7F6E": "General settings",
1700
+ "\u901A\u7528\u8BBE\u7F6E\u5206\u7C7B": "General settings categories",
1701
+ "\u9644\u4EF6": "Attachments",
1702
+ "IM \u8BBE\u7F6E\u5BFC\u822A": "IM settings navigation",
1703
+ "\u9644\u4EF6\u4FDD\u7559\u65F6\u957F (\u5C0F\u65F6)": "Attachment retention (hours)",
1704
+ "\u67E5\u770B\u9644\u4EF6\u4FDD\u7559\u65F6\u957F\u8BF4\u660E": "View attachment retention details",
1705
+ "\u6C38\u4E45\u4FDD\u7559\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u6E05\u7406": "Keep forever; nothing is removed automatically",
1706
+ "\u6BCF Turn \u7ED3\u675F\u540E\u7ACB\u5373\u6E05\u7406": "Cleaned up as soon as each turn ends",
1707
+ "\u5C0F\u65F6\u540E\u81EA\u52A8\u6E05\u7406": "hours until automatic cleanup",
1708
+ "\u8BF7\u8F93\u5165 -1\u30010 \u6216 1~${INBOUND_TTL_MAX_HOURS} \u4E4B\u95F4\u7684\u6574\u6570\u3002": "Enter -1, 0, or an integer from 1 to ${INBOUND_TTL_MAX_HOURS}.",
1709
+ "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not save the setting. Try again later.",
1710
+ "\u5DF2\u4FDD\u5B58": "Saved",
1711
+ "\u901A\u7528\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "General settings are unavailable.",
1712
+ "\u6B63\u5728\u8BFB\u53D6\u901A\u7528\u8BBE\u7F6E\u2026": "Loading general settings\u2026",
1713
+ "\u65E0\u6CD5\u8BFB\u53D6\u901A\u7528\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not load general settings. Try again later.",
1714
+ "\u901A\u7528\u8BBE\u7F6E\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\u3002": "General settings returned an unrecognized response.",
1715
+ "\u6E05\u7406\u8FC7\u671F\u9644\u4EF6": "Clean up expired attachments",
1716
+ "\u786E\u8BA4\u6E05\u7406\u8FC7\u671F\u9644\u4EF6": "Confirm expired attachment cleanup",
1717
+ "\u786E\u8BA4\u6E05\u7406\u5F53\u524D\u5DF2\u8FC7\u671F\u7684\u9644\u4EF6\uFF1F": "Clean up the attachments that are currently expired?",
1718
+ "\u786E\u8BA4\u6E05\u7406": "Confirm cleanup",
1719
+ "\u6B63\u5728\u6E05\u7406\u2026": "Cleaning up\u2026"
1676
1720
  });
1677
1721
  var en = EN;
1678
1722
  var zh = Object.freeze(Object.fromEntries(
@@ -1695,6 +1739,8 @@ function translateDynamic(text6) {
1695
1739
  if (match) return `${match[1]}/${match[2]} online`;
1696
1740
  match = /^已接入 (\d+) 个机器人,其中 (\d+) 个在线$/.exec(text6);
1697
1741
  if (match) return `${match[2]} of ${match[1]} bots online`;
1742
+ match = /^请输入 -1、0 或 1~(\d+) 之间的整数。$/.exec(text6);
1743
+ if (match) return `Enter -1, 0, or an integer from 1 to ${match[1]}.`;
1698
1744
  match = /^正在读取\s*(.+?)\s*机器人状态…$/.exec(text6);
1699
1745
  if (match) return `Loading ${channelName(match[1])} bot status\u2026`;
1700
1746
  match = /^无法读取\s*(.+?)\s*机器人状态$/.exec(text6);
@@ -1753,6 +1799,8 @@ function translateDynamic(text6) {
1753
1799
  if (match) return `Automatic status refresh failed: ${match[1]}`;
1754
1800
  match = /^操作失败:(.+)$/.exec(text6);
1755
1801
  if (match) return `Operation failed: ${match[1]}`;
1802
+ match = /^连接检查失败:(.+)(参考号:(.+))$/.exec(text6);
1803
+ if (match) return `Connection check failed: ${localizeText(match[1])} (reference: ${match[2]})`;
1756
1804
  match = /^连接检查失败:(.+)$/.exec(text6);
1757
1805
  if (match) return `Connection check failed: ${match[1]}`;
1758
1806
  match = /^移除失败:(.+)$/.exec(text6);
@@ -2252,11 +2300,19 @@ function sanitizeMessage(value, fallback) {
2252
2300
  if (FORBIDDEN_ERROR_FIELDS.test(message)) return fallback;
2253
2301
  return message.replace(/([=:]\s*)[^\s,;,。]+/g, "$1\u2022\u2022\u2022\u2022\u2022\u2022").slice(0, 240);
2254
2302
  }
2303
+ function safeReferenceId(value) {
2304
+ const referenceId = optionalString(value, 40);
2305
+ return referenceId && /^DT-CONN-[A-F0-9]{8}$/.test(referenceId) ? referenceId : void 0;
2306
+ }
2255
2307
  function normalizeError(value, fallbackCode, fallbackMessage) {
2256
2308
  if (!isRecord(value)) return void 0;
2309
+ const hint = sanitizeMessage(value.hint, "");
2310
+ const referenceId = safeReferenceId(value.referenceId);
2257
2311
  return {
2258
2312
  code: safeErrorCode(value.code, fallbackCode),
2259
- message: sanitizeMessage(value.message, fallbackMessage)
2313
+ message: sanitizeMessage(value.message, fallbackMessage),
2314
+ ...hint ? { hint } : {},
2315
+ ...referenceId ? { referenceId } : {}
2260
2316
  };
2261
2317
  }
2262
2318
  function normalizeTestMessage(value) {
@@ -2271,8 +2327,15 @@ function unwrapRpcResult(result) {
2271
2327
  throw new Error("\u9489\u9489\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
2272
2328
  }
2273
2329
  if (!result.ok) {
2274
- const error = new Error(sanitizeMessage(result.error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25"));
2275
- error.code = safeErrorCode(result.error?.code, "DINGTALK_RPC_ERROR");
2330
+ const visible = normalizeError(
2331
+ result.error,
2332
+ "DINGTALK_RPC_ERROR",
2333
+ "\u9489\u9489\u64CD\u4F5C\u5931\u8D25"
2334
+ ) ?? { code: "DINGTALK_RPC_ERROR", message: "\u9489\u9489\u64CD\u4F5C\u5931\u8D25" };
2335
+ const error = new Error(visible.message);
2336
+ error.code = visible.code;
2337
+ if (visible.hint) error.hint = visible.hint;
2338
+ if (visible.referenceId) error.referenceId = visible.referenceId;
2276
2339
  throw error;
2277
2340
  }
2278
2341
  return result.value;
@@ -2377,10 +2440,11 @@ function connectionTestFeedback(result) {
2377
2440
  return result ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
2378
2441
  }
2379
2442
  function presentError(error) {
2380
- return {
2381
- code: safeErrorCode(error?.code, "DINGTALK_ERROR"),
2382
- message: sanitizeMessage(error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
2383
- };
2443
+ return normalizeError(
2444
+ error,
2445
+ "DINGTALK_ERROR",
2446
+ "\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
2447
+ ) ?? { code: "DINGTALK_ERROR", message: "\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" };
2384
2448
  }
2385
2449
  function formatRemaining(milliseconds) {
2386
2450
  const seconds = Math.max(0, Math.ceil(Number(milliseconds) / 1e3) || 0);
@@ -3680,6 +3744,8 @@ var CSS = String.raw`
3680
3744
  .ddt-inlineError h3 { font-size: 17px; }
3681
3745
  .ddt-inlineError p { line-height: 1.55; }
3682
3746
  .ddt-errorCode { font: 11px ui-monospace, SFMono-Regular, monospace; opacity: .8; }
3747
+ .ddt-errorDiagnostic { display: grid; gap: 5px; color: var(--ddt-error); }
3748
+ .ddt-errorHint { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 1.55; }
3683
3749
  .ddt-listHeading { display: flex; align-items: center; justify-content: space-between; margin: 2px 0 9px; }
3684
3750
  .ddt-listHeading h3 { margin: 0; font-size: 14px; }
3685
3751
  .ddt-list { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
@@ -3936,25 +4002,45 @@ function ProgressPanel({ status, busy, onCancel }) {
3936
4002
  )
3937
4003
  );
3938
4004
  }
4005
+ function ConnectionErrorDiagnostic({ error }) {
4006
+ if (!error) return null;
4007
+ return h2(
4008
+ "div",
4009
+ { className: "ddt-errorDiagnostic" },
4010
+ error.hint ? h2("p", { className: "ddt-errorHint" }, error.hint) : null,
4011
+ h2(
4012
+ "span",
4013
+ { className: "ddt-errorCode" },
4014
+ h2("span", null, "\u9519\u8BEF\u7801"),
4015
+ `: ${error.code}`,
4016
+ error.referenceId ? h2(React10.Fragment, null, " \xB7 ", h2("span", null, "\u53C2\u8003\u53F7"), `: ${error.referenceId}`) : null
4017
+ )
4018
+ );
4019
+ }
3939
4020
  function ProvisionError({ provision, busy, onRetry, onClose }) {
3940
4021
  const error = provision.error ?? {
3941
4022
  code: "DINGTALK_PROVISION_FAILED",
3942
4023
  message: "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"
3943
4024
  };
4025
+ const connectionFailed = Boolean(error.referenceId);
3944
4026
  return h2(
3945
4027
  "div",
3946
4028
  { className: "ddt-card dim-surfaceCard" },
3947
4029
  h2(
3948
4030
  "div",
3949
4031
  { className: "ddt-inlineError dim-inlineError", role: "alert" },
3950
- h2("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"),
4032
+ h2("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : connectionFailed ? "\u673A\u5668\u4EBA\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u8FDE\u63A5\u672A\u5C31\u7EEA" : "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"),
3951
4033
  h2("p", null, error.message),
3952
- h2("span", { className: "ddt-errorCode" }, error.code),
4034
+ h2(ConnectionErrorDiagnostic, { error }),
3953
4035
  h2(
3954
4036
  "div",
3955
4037
  { className: "ddt-actions dim-viewActions" },
3956
- h2(Button, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
3957
- h2(Button, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
4038
+ connectionFailed ? h2(Button, { kind: "primary", onClick: onClose, disabled: busy }, "\u67E5\u770B\u5DF2\u4FDD\u5B58\u7684\u673A\u5668\u4EBA") : h2(
4039
+ React10.Fragment,
4040
+ null,
4041
+ h2(Button, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
4042
+ h2(Button, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
4043
+ )
3958
4044
  )
3959
4045
  )
3960
4046
  );
@@ -4091,6 +4177,7 @@ function AccountCard({
4091
4177
  )
4092
4178
  ),
4093
4179
  summary2 ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary2) : null,
4180
+ account.error ? h2(ConnectionErrorDiagnostic, { error: account.error }) : null,
4094
4181
  account.lastMessageError ? h2(LastMessageErrorSummary, {
4095
4182
  className: "ddt-summary",
4096
4183
  error: account.lastMessageError
@@ -4416,6 +4503,10 @@ function DingtalkSettingsTab({ rpcCall }) {
4416
4503
  if (!canCommit()) return;
4417
4504
  const account = result.botId ? snapshot?.bots.find((bot) => bot.botId === result.botId) : snapshot?.bots.find((bot) => bot.connected);
4418
4505
  if (!account?.connected) {
4506
+ if (account?.error) {
4507
+ setProvision((current) => current?.attemptId === attemptId ? { ...current, ...result, status: "failed", error: account.error } : current);
4508
+ return;
4509
+ }
4419
4510
  setProvision((current) => current?.attemptId === attemptId ? { ...current, ...result, status: "connecting" } : current);
4420
4511
  schedule(result.pollIntervalMs);
4421
4512
  return;
@@ -4490,7 +4581,8 @@ function DingtalkSettingsTab({ rpcCall }) {
4490
4581
  return snapshot;
4491
4582
  } catch (error) {
4492
4583
  if (!mountedRef.current) return void 0;
4493
- const failureMessage = operation === "reconnect" ? "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : `\u64CD\u4F5C\u5931\u8D25\uFF1A${presentError(error).message}`;
4584
+ const visibleError = presentError(error);
4585
+ const failureMessage = operation === "reconnect" ? visibleError.referenceId ? `\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF1A${visibleError.message}\uFF08\u53C2\u8003\u53F7\uFF1A${visibleError.referenceId}\uFF09` : "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : `\u64CD\u4F5C\u5931\u8D25\uFF1A${visibleError.message}`;
4494
4586
  if (operation === "reconnect") {
4495
4587
  setFeedbackByBot((current) => ({
4496
4588
  ...current,
@@ -4712,7 +4804,7 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
4712
4804
  function createTokenChannelApi(channel4, connectionSummary, {
4713
4805
  normalizeBotExtension = () => ({})
4714
4806
  } = {}) {
4715
- const unwrapRpcResult12 = (result) => {
4807
+ const unwrapRpcResult13 = (result) => {
4716
4808
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
4717
4809
  throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
4718
4810
  }
@@ -4769,11 +4861,11 @@ function createTokenChannelApi(channel4, connectionSummary, {
4769
4861
  agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
4770
4862
  };
4771
4863
  };
4772
- const presentError12 = (error) => ({
4864
+ const presentError13 = (error) => ({
4773
4865
  code: text2(error?.code, `${channel4.toUpperCase()}_ERROR`, 80),
4774
4866
  message: text2(error?.message, `${channel4}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
4775
4867
  });
4776
- return Object.freeze({ unwrapRpcResult: unwrapRpcResult12, normalizeSnapshot: normalizeSnapshot9, presentError: presentError12 });
4868
+ return Object.freeze({ unwrapRpcResult: unwrapRpcResult13, normalizeSnapshot: normalizeSnapshot9, presentError: presentError13 });
4777
4869
  }
4778
4870
 
4779
4871
  // plugin-src/client/channels/discord/api.js
@@ -5322,6 +5414,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
5322
5414
  setContextEnhancement: "bot.context-enhancement.set",
5323
5415
  setAccessPolicy: "bot.access-policy.set",
5324
5416
  setGroupResponseMode: "bot.group-response-mode.set",
5417
+ setGroupTopicReply: "bot.group-topic-reply.set",
5325
5418
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
5326
5419
  testConnection: "connection.test",
5327
5420
  disconnect: "connection.disconnect"
@@ -5472,6 +5565,7 @@ function normalizeBotConnection(value, fallbackBotId) {
5472
5565
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
5473
5566
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
5474
5567
  groupResponseMode: normalizeGroupResponseMode(value.groupResponseMode),
5568
+ groupTopicReply: value.groupTopicReply === true,
5475
5569
  groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
5476
5570
  bot: normalizeBot2(value.bot),
5477
5571
  health: normalizeHealth(value.health, connected),
@@ -6130,6 +6224,57 @@ function GroupResponseModeEditor({
6130
6224
  }, error) : null
6131
6225
  );
6132
6226
  }
6227
+ function GroupTopicReplyEditor({ value = false, disabled = false, onSave }) {
6228
+ const current = value === true ? "on" : "off";
6229
+ const [saving, setSaving] = React13.useState(false);
6230
+ const [error, setError] = React13.useState(null);
6231
+ const change = async (event) => {
6232
+ const next = event.target.value === "on";
6233
+ if ((next ? "on" : "off") === current || saving || disabled) return;
6234
+ setSaving(true);
6235
+ setError(null);
6236
+ try {
6237
+ await onSave?.(next);
6238
+ } catch (cause) {
6239
+ setError(cause?.message ?? "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
6240
+ } finally {
6241
+ setSaving(false);
6242
+ }
6243
+ };
6244
+ return h2(
6245
+ "div",
6246
+ { className: "bxf-responseMode dim-responseMode" },
6247
+ h2(
6248
+ "div",
6249
+ { className: "bxf-responseModeHeader dim-responseModeHeader" },
6250
+ h2("span", null, "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D"),
6251
+ saving ? h2("span", { className: "bxf-responseModeStatus dim-responseModeStatus" }, "\u4FDD\u5B58\u4E2D\u2026") : null
6252
+ ),
6253
+ h2(
6254
+ "select",
6255
+ {
6256
+ className: "bxf-responseModeSelect dim-responseModeSelect",
6257
+ value: current,
6258
+ disabled: disabled || saving,
6259
+ "aria-label": "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D",
6260
+ onChange: (event) => {
6261
+ void change(event);
6262
+ }
6263
+ },
6264
+ h2("option", { value: "off" }, "\u5173\u95ED\uFF08\u7FA4\u5185\u76F4\u63A5\u56DE\u590D\uFF09"),
6265
+ h2("option", { value: "on" }, "\u5F00\u542F\uFF08\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF09")
6266
+ ),
6267
+ h2(
6268
+ "small",
6269
+ { className: "bxf-responseModeHelp dim-responseModeHelp" },
6270
+ "\u5F00\u542F\u540E\uFF0C\u7FA4\u804A\u4E2D\u5411\u673A\u5668\u4EBA\u63D0\u95EE\u4F1A\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF0C\u56DE\u590D\u843D\u5728\u8BDD\u9898\u5185\uFF1B\u6BCF\u4E2A\u8BDD\u9898\u662F dsh \u4F1A\u8BDD\u5217\u8868\u91CC\u4E00\u6761\u72EC\u7ACB\u4F1A\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u4E92\u4E0D\u4E32\u3002\u79C1\u804A\u4E0D\u53D7\u5F71\u54CD\u3002"
6271
+ ),
6272
+ error ? h2("p", {
6273
+ className: "bxf-responseModeError dim-responseModeError",
6274
+ role: "alert"
6275
+ }, error) : null
6276
+ );
6277
+ }
6133
6278
  function BotCard({
6134
6279
  connection,
6135
6280
  busy,
@@ -6145,6 +6290,7 @@ function BotCard({
6145
6290
  onAgentPresetSave,
6146
6291
  onContextEnhancementSave,
6147
6292
  onGroupResponseModeSave,
6293
+ onGroupTopicReplySave,
6148
6294
  onGroupMessagePermissionAuthorize,
6149
6295
  onRequestRemove,
6150
6296
  onConfirmRemove,
@@ -6232,6 +6378,11 @@ function BotCard({
6232
6378
  onSave: onGroupResponseModeSave,
6233
6379
  onAuthorize: onGroupMessagePermissionAuthorize
6234
6380
  }),
6381
+ h2(GroupTopicReplyEditor, {
6382
+ value: connection.groupTopicReply,
6383
+ disabled: Boolean(busy),
6384
+ onSave: onGroupTopicReplySave
6385
+ }),
6235
6386
  provisionContent ? h2("section", {
6236
6387
  className: "bxf-botProvision dim-botProvision",
6237
6388
  "aria-label": `${bot.name}\u7684\u98DE\u4E66\u6388\u6743\u6D41\u7A0B`,
@@ -6341,6 +6492,7 @@ function BotList(props) {
6341
6492
  onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(bot, agentPreset),
6342
6493
  onContextEnhancementSave: (config) => props.onContextEnhancementSave(bot, config),
6343
6494
  onGroupResponseModeSave: (groupResponseMode) => props.onGroupResponseModeSave(bot, groupResponseMode),
6495
+ onGroupTopicReplySave: (groupTopicReply) => props.onGroupTopicReplySave(bot, groupTopicReply),
6344
6496
  onGroupMessagePermissionAuthorize: () => props.onGroupMessagePermissionAuthorize(bot),
6345
6497
  onRequestRemove: () => props.onRequestRemove(bot),
6346
6498
  onConfirmRemove: () => props.onConfirmRemove(bot),
@@ -7083,6 +7235,12 @@ function FeishuSettingsTab({ rpcCall }) {
7083
7235
  { config }
7084
7236
  ),
7085
7237
  onGroupResponseModeSave: saveGroupResponseMode,
7238
+ onGroupTopicReplySave: (connection, groupTopicReply) => saveBotSetting(
7239
+ connection,
7240
+ "group-topic-reply",
7241
+ FEISHU_ENDPOINTS.setGroupTopicReply,
7242
+ { groupTopicReply }
7243
+ ),
7086
7244
  onGroupMessagePermissionAuthorize: authorizeGroupMessages,
7087
7245
  onRequestRemove: requestRemove,
7088
7246
  onConfirmRemove: (bot) => void confirmRemove(bot),
@@ -12976,6 +13134,374 @@ function DeliveryTargetSettingsPage({
12976
13134
  );
12977
13135
  }
12978
13136
 
13137
+ // plugin-src/client/global-settings.js
13138
+ var React22 = __toESM(require("react"), 1);
13139
+
13140
+ // src/channels/shared/inbound-ttl.mjs
13141
+ var DEFAULT_INBOUND_TTL_HOURS = 168;
13142
+ var INBOUND_TTL_MAX_HOURS = 8760;
13143
+ var INTEGER_STRING = /^[+-]?\d+$/;
13144
+ function normalizeInboundTtlHours(value) {
13145
+ let candidate = value;
13146
+ if (typeof candidate === "string") {
13147
+ const trimmed = candidate.trim();
13148
+ if (!INTEGER_STRING.test(trimmed)) return null;
13149
+ candidate = Number(trimmed);
13150
+ }
13151
+ if (typeof candidate !== "number" || !Number.isInteger(candidate)) return null;
13152
+ if (candidate === -1) return -1;
13153
+ if (candidate >= 0 && candidate <= INBOUND_TTL_MAX_HOURS) return candidate;
13154
+ return null;
13155
+ }
13156
+
13157
+ // plugin-src/client/global-settings.js
13158
+ var GLOBAL_SETTINGS_RPC_CHANNEL = "/dsh-im-settings";
13159
+ var GLOBAL_SETTINGS_TAB_ID = "global-settings";
13160
+ var ATTACHMENTS_TAB_ID = "dim-general-settings-tab-attachments";
13161
+ var ATTACHMENTS_PANEL_ID = "dim-general-settings-panel-attachments";
13162
+ var GLOBAL_SETTINGS_ENDPOINTS = Object.freeze({
13163
+ getTtl: "settings.inbound-ttl.get",
13164
+ setTtl: "settings.inbound-ttl.set",
13165
+ sweep: "settings.inbound-ttl.sweep"
13166
+ });
13167
+ function presentError11(error, fallback) {
13168
+ return error?.message || fallback;
13169
+ }
13170
+ function unwrapRpcResult12(result) {
13171
+ if (result?.ok === true) return result.value;
13172
+ if (result?.ok === false) {
13173
+ const error = new Error(result.error?.message || "\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
13174
+ error.code = result.error?.code;
13175
+ throw error;
13176
+ }
13177
+ return result;
13178
+ }
13179
+ function GlobalSettingsLogoGlyph({ size } = {}) {
13180
+ return h2("svg", {
13181
+ ...size === void 0 ? {} : { width: size, height: size },
13182
+ viewBox: "0 0 24 24",
13183
+ focusable: "false",
13184
+ "aria-hidden": "true",
13185
+ "data-im-icon": "global-settings"
13186
+ }, h2("path", {
13187
+ fill: "currentColor",
13188
+ d: "M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.488.488 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96a.488.488 0 0 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58ZM12 15.6A3.61 3.61 0 0 1 8.4 12c0-1.98 1.62-3.6 3.6-3.6s3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6Z"
13189
+ }));
13190
+ }
13191
+ function GlobalButton({ children, kind = "secondary", className = "", ...props }) {
13192
+ return h2("button", {
13193
+ ...props,
13194
+ type: props.type ?? "button",
13195
+ className: `dim-deliveryButton ${className}`.trim(),
13196
+ "data-kind": kind
13197
+ }, children);
13198
+ }
13199
+ function GlobalSettingsPanel({ rpcCall }) {
13200
+ const [phase, setPhase] = React22.useState("loading");
13201
+ const [loadError, setLoadError] = React22.useState(null);
13202
+ const [ttlInput, setTtlInput] = React22.useState("");
13203
+ const [savedTtl, setSavedTtl] = React22.useState(null);
13204
+ const [ttlError, setTtlError] = React22.useState(false);
13205
+ const [saveError, setSaveError] = React22.useState(null);
13206
+ const [saveSucceeded, setSaveSucceeded] = React22.useState(false);
13207
+ const [isSaving, setIsSaving] = React22.useState(false);
13208
+ const [sweepConfirming, setSweepConfirming] = React22.useState(false);
13209
+ const [sweeping, setSweeping] = React22.useState(false);
13210
+ const ttlErrorId = React22.useId();
13211
+ const ttlHintsId = React22.useId();
13212
+ const sweepTriggerId = React22.useId();
13213
+ const sweepConfirmId = React22.useId();
13214
+ const sweepConfirmTextId = React22.useId();
13215
+ const sweepConfirmButtonId = React22.useId();
13216
+ const mounted = React22.useRef(true);
13217
+ const saving = React22.useRef(false);
13218
+ const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
13219
+ if (typeof rpcCall !== "function") throw new Error("\u901A\u7528\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
13220
+ return unwrapRpcResult12(await rpcCall(endpoint, payload, signal));
13221
+ }, [rpcCall]);
13222
+ const loadSettings = React22.useCallback(async ({ signal } = {}) => {
13223
+ setPhase("loading");
13224
+ setLoadError(null);
13225
+ try {
13226
+ const value = await invoke(GLOBAL_SETTINGS_ENDPOINTS.getTtl, {}, signal);
13227
+ if (signal?.aborted || !mounted.current) return;
13228
+ const ttlHours = normalizeInboundTtlHours(value?.ttlHours);
13229
+ if (ttlHours === null) {
13230
+ setLoadError("\u901A\u7528\u8BBE\u7F6E\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\u3002");
13231
+ setPhase("error");
13232
+ return;
13233
+ }
13234
+ setSavedTtl(ttlHours);
13235
+ setTtlInput(String(ttlHours));
13236
+ setTtlError(false);
13237
+ setSaveError(null);
13238
+ setSaveSucceeded(false);
13239
+ setPhase("ready");
13240
+ } catch (caught) {
13241
+ if (signal?.aborted || caught?.name === "AbortError" || !mounted.current) return;
13242
+ setLoadError(presentError11(caught, "\u65E0\u6CD5\u8BFB\u53D6\u901A\u7528\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
13243
+ setPhase("error");
13244
+ }
13245
+ }, [invoke]);
13246
+ React22.useEffect(() => {
13247
+ mounted.current = true;
13248
+ const controller = new AbortController();
13249
+ void loadSettings({ signal: controller.signal });
13250
+ return () => {
13251
+ mounted.current = false;
13252
+ controller.abort();
13253
+ };
13254
+ }, [loadSettings]);
13255
+ React22.useEffect(() => {
13256
+ if (!sweepConfirming) return;
13257
+ globalThis.document?.getElementById(sweepConfirmButtonId)?.focus();
13258
+ }, [sweepConfirmButtonId, sweepConfirming]);
13259
+ const commitTtl = async () => {
13260
+ if (phase === "loading" || saving.current) return;
13261
+ const proposed = normalizeInboundTtlHours(ttlInput.trim());
13262
+ if (proposed === null) {
13263
+ setSaveError(null);
13264
+ setSaveSucceeded(false);
13265
+ setTtlError(true);
13266
+ return;
13267
+ }
13268
+ setTtlError(false);
13269
+ if (proposed === savedTtl) return;
13270
+ saving.current = true;
13271
+ setIsSaving(true);
13272
+ setSaveError(null);
13273
+ setSaveSucceeded(false);
13274
+ try {
13275
+ const value = await invoke(GLOBAL_SETTINGS_ENDPOINTS.setTtl, { ttlHours: proposed });
13276
+ if (!mounted.current) return;
13277
+ const confirmed = normalizeInboundTtlHours(value?.ttlHours);
13278
+ const finalTtl = confirmed === null ? proposed : confirmed;
13279
+ setSavedTtl(finalTtl);
13280
+ setTtlInput((current) => current.trim() === String(proposed) ? String(finalTtl) : current);
13281
+ setLoadError(null);
13282
+ setSaveSucceeded(true);
13283
+ setPhase("ready");
13284
+ } catch (caught) {
13285
+ if (mounted.current) {
13286
+ setSaveError(presentError11(caught, "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
13287
+ }
13288
+ } finally {
13289
+ saving.current = false;
13290
+ if (mounted.current) setIsSaving(false);
13291
+ }
13292
+ };
13293
+ const requestSweep = () => {
13294
+ if (sweeping) return;
13295
+ setSweepConfirming(true);
13296
+ };
13297
+ const cancelSweep = ({ restoreFocus = false } = {}) => {
13298
+ setSweepConfirming(false);
13299
+ if (restoreFocus) globalThis.document?.getElementById(sweepTriggerId)?.focus();
13300
+ };
13301
+ const runSweep = async () => {
13302
+ setSweepConfirming(false);
13303
+ setSweeping(true);
13304
+ try {
13305
+ await invoke(GLOBAL_SETTINGS_ENDPOINTS.sweep, {});
13306
+ } catch {
13307
+ } finally {
13308
+ if (mounted.current) setSweeping(false);
13309
+ }
13310
+ };
13311
+ const inlineStatus = phase === "loading" ? { role: "status", message: "\u6B63\u5728\u8BFB\u53D6\u901A\u7528\u8BBE\u7F6E\u2026" } : ttlError ? { role: "alert", message: `\u8BF7\u8F93\u5165 -1\u30010 \u6216 1~${INBOUND_TTL_MAX_HOURS} \u4E4B\u95F4\u7684\u6574\u6570\u3002` } : saveError ? { role: "alert", message: saveError } : phase === "error" && loadError ? { role: "alert", message: loadError } : saveSucceeded ? { role: "status", message: "\u5DF2\u4FDD\u5B58" } : null;
13312
+ const proposedTtl = normalizeInboundTtlHours(ttlInput.trim());
13313
+ const canSave = phase !== "loading" && !isSaving && proposedTtl !== null && proposedTtl !== savedTtl;
13314
+ return h2(
13315
+ "section",
13316
+ { className: "dim-generalSettingsPage", "aria-label": "\u901A\u7528\u8BBE\u7F6E" },
13317
+ h2(
13318
+ "header",
13319
+ { className: "dim-generalSettingsHeader" },
13320
+ h2("h2", null, "\u901A\u7528\u8BBE\u7F6E")
13321
+ ),
13322
+ h2(
13323
+ "div",
13324
+ { className: "dim-generalSettingsTabsBar" },
13325
+ h2(
13326
+ "div",
13327
+ {
13328
+ className: "dim-generalSettingsTabs",
13329
+ role: "tablist",
13330
+ "aria-label": "\u901A\u7528\u8BBE\u7F6E\u5206\u7C7B"
13331
+ },
13332
+ h2("button", {
13333
+ type: "button",
13334
+ id: ATTACHMENTS_TAB_ID,
13335
+ className: "dim-generalSettingsTab",
13336
+ role: "tab",
13337
+ "aria-selected": true,
13338
+ "aria-controls": ATTACHMENTS_PANEL_ID
13339
+ }, "\u9644\u4EF6")
13340
+ )
13341
+ ),
13342
+ h2(
13343
+ "div",
13344
+ {
13345
+ id: ATTACHMENTS_PANEL_ID,
13346
+ className: "dim-generalSettingsTabPanel",
13347
+ role: "tabpanel",
13348
+ "aria-labelledby": ATTACHMENTS_TAB_ID
13349
+ },
13350
+ h2(
13351
+ "section",
13352
+ {
13353
+ className: "dim-globalSection",
13354
+ "aria-label": "\u9644\u4EF6",
13355
+ "aria-busy": phase === "loading"
13356
+ },
13357
+ h2(
13358
+ "div",
13359
+ { className: "dim-globalHead" },
13360
+ h2(
13361
+ "div",
13362
+ { className: "dim-globalHeadTitle" },
13363
+ h2("h3", { id: "dim-globalTtlTitle" }, "\u9644\u4EF6\u4FDD\u7559\u65F6\u957F (\u5C0F\u65F6)"),
13364
+ h2(
13365
+ "div",
13366
+ { className: "dim-globalTtlHelp" },
13367
+ h2("button", {
13368
+ type: "button",
13369
+ className: "dim-channelHelpButton dim-globalTtlHelpButton",
13370
+ "aria-label": "\u67E5\u770B\u9644\u4EF6\u4FDD\u7559\u65F6\u957F\u8BF4\u660E",
13371
+ "aria-describedby": ttlHintsId
13372
+ }, h2("span", { "aria-hidden": "true" }, "?")),
13373
+ h2(
13374
+ "div",
13375
+ {
13376
+ id: ttlHintsId,
13377
+ className: "dim-globalTtlTooltip",
13378
+ role: "tooltip"
13379
+ },
13380
+ h2(
13381
+ "ul",
13382
+ { className: "dim-globalTtlHints" },
13383
+ h2(
13384
+ "li",
13385
+ null,
13386
+ h2("code", null, "-1"),
13387
+ h2("span", null, "\u6C38\u4E45\u4FDD\u7559\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u6E05\u7406")
13388
+ ),
13389
+ h2(
13390
+ "li",
13391
+ null,
13392
+ h2("code", null, "0"),
13393
+ h2("span", null, "\u6BCF Turn \u7ED3\u675F\u540E\u7ACB\u5373\u6E05\u7406")
13394
+ ),
13395
+ h2(
13396
+ "li",
13397
+ null,
13398
+ h2("code", null, `1~${INBOUND_TTL_MAX_HOURS}`),
13399
+ h2("span", null, "\u5C0F\u65F6\u540E\u81EA\u52A8\u6E05\u7406")
13400
+ )
13401
+ )
13402
+ )
13403
+ )
13404
+ )
13405
+ ),
13406
+ h2(
13407
+ "form",
13408
+ {
13409
+ className: "dim-globalTtlRow",
13410
+ onSubmit: (event) => {
13411
+ event.preventDefault();
13412
+ void commitTtl();
13413
+ }
13414
+ },
13415
+ h2("input", {
13416
+ id: "dim-globalTtlInput",
13417
+ className: "dim-globalTtlInput",
13418
+ type: "text",
13419
+ value: ttlInput,
13420
+ placeholder: String(DEFAULT_INBOUND_TTL_HOURS),
13421
+ autoComplete: "off",
13422
+ disabled: phase === "loading" || isSaving,
13423
+ "aria-labelledby": "dim-globalTtlTitle",
13424
+ "aria-invalid": ttlError ? "true" : void 0,
13425
+ "aria-describedby": ttlError ? ttlErrorId : ttlHintsId,
13426
+ onBlur: () => {
13427
+ setTtlError(normalizeInboundTtlHours(ttlInput.trim()) === null);
13428
+ },
13429
+ onKeyDown: (event) => {
13430
+ if (event.key !== "Enter") return;
13431
+ event.preventDefault();
13432
+ void commitTtl();
13433
+ },
13434
+ onChange: (event) => {
13435
+ setTtlInput(event.target.value);
13436
+ setTtlError(false);
13437
+ setSaveError(null);
13438
+ setSaveSucceeded(false);
13439
+ }
13440
+ }),
13441
+ h2(GlobalButton, {
13442
+ type: "submit",
13443
+ kind: "primary",
13444
+ className: "dim-globalSaveButton",
13445
+ disabled: !canSave
13446
+ }, isSaving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"),
13447
+ h2(
13448
+ "div",
13449
+ {
13450
+ className: "dim-globalSweepAction",
13451
+ onBlur: (event) => {
13452
+ if (sweepConfirming && !event.currentTarget.contains(event.relatedTarget)) cancelSweep();
13453
+ }
13454
+ },
13455
+ h2(GlobalButton, {
13456
+ id: sweepTriggerId,
13457
+ className: "dim-globalSweepButton",
13458
+ onClick: () => sweepConfirming ? cancelSweep() : requestSweep(),
13459
+ disabled: sweeping,
13460
+ "aria-haspopup": "dialog",
13461
+ "aria-expanded": sweepConfirming,
13462
+ "aria-controls": sweepConfirmId
13463
+ }, sweeping ? "\u6B63\u5728\u6E05\u7406\u2026" : "\u6E05\u7406\u8FC7\u671F\u9644\u4EF6"),
13464
+ sweepConfirming ? h2(
13465
+ "div",
13466
+ {
13467
+ id: sweepConfirmId,
13468
+ className: "dim-globalSweepConfirm",
13469
+ role: "alertdialog",
13470
+ "aria-label": "\u786E\u8BA4\u6E05\u7406\u8FC7\u671F\u9644\u4EF6",
13471
+ "aria-describedby": sweepConfirmTextId,
13472
+ onKeyDown: (event) => {
13473
+ if (event.key !== "Escape") return;
13474
+ event.preventDefault();
13475
+ cancelSweep({ restoreFocus: true });
13476
+ }
13477
+ },
13478
+ h2("p", { id: sweepConfirmTextId }, "\u786E\u8BA4\u6E05\u7406\u5F53\u524D\u5DF2\u8FC7\u671F\u7684\u9644\u4EF6\uFF1F"),
13479
+ h2(
13480
+ "div",
13481
+ { className: "dim-globalSweepConfirmActions" },
13482
+ h2(GlobalButton, { onClick: () => cancelSweep({ restoreFocus: true }) }, "\u53D6\u6D88"),
13483
+ h2(GlobalButton, {
13484
+ id: sweepConfirmButtonId,
13485
+ kind: "danger",
13486
+ className: "dim-globalSweepConfirmButton",
13487
+ onClick: () => void runSweep()
13488
+ }, "\u786E\u8BA4\u6E05\u7406")
13489
+ )
13490
+ ) : null
13491
+ ),
13492
+ inlineStatus ? h2("p", {
13493
+ id: ttlErrorId,
13494
+ className: "dim-globalInline",
13495
+ "data-tone": inlineStatus.role === "alert" ? "error" : void 0,
13496
+ role: inlineStatus.role,
13497
+ "aria-live": inlineStatus.role === "status" ? "polite" : void 0
13498
+ }, inlineStatus.message) : null
13499
+ )
13500
+ )
13501
+ )
13502
+ );
13503
+ }
13504
+
12979
13505
  // plugin-src/client/loopback-recovery.js
12980
13506
  var TRANSPORT_FORBIDDEN = /^transport failure for \/[A-Za-z0-9._~-]+\/[A-Za-z0-9_$./~-]+: HTTP 403$/;
12981
13507
  var LOOPBACK_RECOVERY_ERROR_CODE = "loopback-recovery-required";
@@ -13094,6 +13620,15 @@ var CSS11 = String.raw`
13094
13620
  .dim-githubArrow { font-size: 13px; line-height: 1; }
13095
13621
  .dim-githubTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 20; width: max-content; max-width: min(220px, 80vw); padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
13096
13622
  .dim-githubAction:hover .dim-githubTooltip, .dim-githubAction:focus-within .dim-githubTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
13623
+ .dim-generalSettingsAction { position: relative; display: inline-flex; flex: none; }
13624
+ .dim-generalSettingsButton { width: 30px; height: 30px; display: grid; place-items: center; flex: none; padding: 0; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-layer-1, #fff); cursor: pointer; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
13625
+ .dim-generalSettingsButton svg { display: block; }
13626
+ .dim-generalSettingsButton:hover { border-color: #aeb3bb; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
13627
+ .dim-generalSettingsButton[aria-current="page"] { border-color: color-mix(in srgb, var(--dim-blue) 42%, var(--dsw-alias-border-l2, #dfe1e5)); color: var(--dim-blue); background: var(--dim-blue-soft); }
13628
+ .dim-generalSettingsButton:focus-visible { outline: 2px solid color-mix(in srgb, var(--dim-blue) 70%, white); outline-offset: 2px; }
13629
+ .dim-generalSettingsTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 20; width: max-content; padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
13630
+ .dim-generalSettingsAction:hover .dim-generalSettingsTooltip, .dim-generalSettingsButton:focus-visible + .dim-generalSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
13631
+ .dim-generalSettingsButton[aria-current="page"] + .dim-generalSettingsTooltip { opacity: 0; visibility: hidden; transform: translateY(-3px); }
13097
13632
  .dim-layout { display: grid; grid-template-columns: 174px 1px minmax(0, 1fr); gap: 24px; align-items: start; }
13098
13633
  .dim-rail { max-height: 520px; display: grid; align-content: start; gap: 8px; overflow-y: auto; padding: 0 4px 1px 1px; scrollbar-width: thin; scrollbar-color: var(--dsw-alias-border-l2, #dfe1e5) transparent; }
13099
13634
  .dim-rail::-webkit-scrollbar { width: 4px; }
@@ -13501,6 +14036,46 @@ var CSS11 = String.raw`
13501
14036
  .dim-accessFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
13502
14037
  .dim-accessFeedback[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
13503
14038
  .dim-accessActions { display: flex; justify-content: flex-end; }
14039
+ .dim-generalSettingsPage { min-width: 0; display: grid; }
14040
+ .dim-generalSettingsHeader { min-width: 0; margin: 0 0 8px; }
14041
+ .dim-generalSettingsHeader h2 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 18px; line-height: 25px; font-weight: 680; }
14042
+ .dim-generalSettingsTabsBar { min-width: 0; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
14043
+ .dim-generalSettingsTabs { min-width: 0; display: flex; align-items: flex-end; gap: 24px; overflow-x: auto; scrollbar-width: none; }
14044
+ .dim-generalSettingsTabs::-webkit-scrollbar { display: none; }
14045
+ .dim-generalSettingsTab { position: relative; min-height: 38px; flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 6px 2px 9px; border: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 13px; line-height: 20px; font-weight: 560; white-space: nowrap; cursor: pointer; transition: color .15s ease; }
14046
+ .dim-generalSettingsTab::after { content: ''; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; border-radius: 2px 2px 0 0; background: transparent; transform: scaleX(.45); transition: background .15s ease, transform .15s ease; }
14047
+ .dim-generalSettingsTab:hover:not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); }
14048
+ .dim-generalSettingsTab[aria-selected="true"] { color: var(--dim-blue); font-weight: 650; }
14049
+ .dim-generalSettingsTab[aria-selected="true"]::after { background: var(--dim-blue); transform: scaleX(1); }
14050
+ .dim-generalSettingsTab:focus-visible { outline: 2px solid var(--dim-blue); outline-offset: -2px; border-radius: 5px; }
14051
+ .dim-generalSettingsTabPanel { min-width: 0; padding-top: 14px; }
14052
+ .dim-globalSection { min-width: 0; padding: 14px 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
14053
+ .dim-globalHead { min-width: 0; display: flex; align-items: center; }
14054
+ .dim-globalHeadTitle { min-width: 0; display: inline-flex; align-items: center; gap: 6px; }
14055
+ .dim-globalHead h3 { min-width: 0; overflow: hidden; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; line-height: 22px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
14056
+ .dim-globalTtlHelp { position: relative; display: inline-flex; flex: none; }
14057
+ .dim-globalTtlTooltip { position: absolute; top: calc(100% + 7px); left: 0; z-index: 30; width: max-content; max-width: min(280px, calc(100vw - 48px)); padding: 9px 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
14058
+ .dim-globalTtlHelp:hover .dim-globalTtlTooltip, .dim-globalTtlHelpButton:focus-visible + .dim-globalTtlTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
14059
+ .dim-globalTtlHints { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
14060
+ .dim-globalTtlHints li { min-width: 0; display: flex; align-items: baseline; gap: 8px; }
14061
+ .dim-globalTtlHints code { flex: none; min-width: 44px; padding: 0 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-module-platform, #f7f8fa); font: 10px/16px ui-monospace, SFMono-Regular, Menlo, monospace; text-align: center; }
14062
+ .dim-globalTtlHints span { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
14063
+ .dim-globalTtlRow { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; margin-top: 12px; }
14064
+ .dim-globalTtlInput { width: min(160px, 100%); min-width: 110px; max-width: 160px; flex: 1 1 130px; height: 34px; 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: 13px ui-monospace, SFMono-Regular, Menlo, monospace; transition: border-color .16s ease, box-shadow .16s ease; }
14065
+ .dim-globalTtlInput:hover:not(:disabled) { border-color: var(--dsw-alias-border-l3, #bbbfc4); }
14066
+ .dim-globalTtlInput:focus { outline: none; border-color: var(--dim-blue); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dim-blue) 14%, transparent); }
14067
+ .dim-globalTtlInput[aria-invalid="true"] { border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 62%, var(--dsw-alias-border-l2, #dfe1e5)); }
14068
+ .dim-globalTtlInput:disabled { cursor: not-allowed; opacity: .55; }
14069
+ .dim-globalSaveButton { min-width: 58px; min-height: 34px; }
14070
+ .dim-globalSweepAction { position: relative; display: inline-flex; flex: none; margin-left: auto; }
14071
+ .dim-globalSweepButton { flex: none; }
14072
+ .dim-globalSweepConfirm { position: absolute; top: calc(100% + 8px); right: 0; z-index: 35; width: 218px; padding: 11px 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 12px 30px rgb(31 35 41 / 18%); }
14073
+ .dim-globalSweepConfirm p { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; }
14074
+ .dim-globalSweepConfirmActions { display: flex; justify-content: flex-end; gap: 7px; margin-top: 10px; }
14075
+ .dim-deliveryButton.dim-globalSweepConfirmButton { border-color: var(--dsw-alias-state-error-primary, #d54941); color: #fff; background: var(--dsw-alias-state-error-primary, #d54941); }
14076
+ .dim-deliveryButton.dim-globalSweepConfirmButton:hover:not(:disabled) { border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 82%, #000); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 88%, #000); }
14077
+ .dim-globalInline { flex: 1 0 100%; min-width: 0; margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; overflow-wrap: anywhere; }
14078
+ .dim-globalInline[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
13504
14079
  .dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
13505
14080
  .dim-panel .ddt-headingCopy { display: none; }
13506
14081
  .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
@@ -13585,7 +14160,7 @@ function installImStyles() {
13585
14160
  }
13586
14161
 
13587
14162
  // plugin-src/client/update-panel.js
13588
- var React22 = __toESM(require("react"), 1);
14163
+ var React23 = __toESM(require("react"), 1);
13589
14164
  var import_react_dom3 = require("react-dom");
13590
14165
  var import_valid = __toESM(require_valid(), 1);
13591
14166
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -13632,7 +14207,7 @@ function unwrapSnapshot(result) {
13632
14207
  }
13633
14208
  return value;
13634
14209
  }
13635
- function presentError11(error) {
14210
+ function presentError12(error) {
13636
14211
  const code = error?.rpcError?.code ?? error?.code ?? "";
13637
14212
  const message = error?.rpcError?.message ?? error?.message ?? "";
13638
14213
  if (code === "update-unavailable" || /(?:not[-_ ]found|unimplemented|unknown[-_ ](?:endpoint|channel)|no[-_ ]handler)/i.test(code) || /(?:not found|unknown (?:endpoint|channel)|not registered|no .*handler|HTTP 404)/i.test(message)) {
@@ -13675,11 +14250,11 @@ function manualUpdateCommand(snapshot) {
13675
14250
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
13676
14251
  }
13677
14252
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13678
- const [copyState, setCopyState] = React22.useState("idle");
13679
- const commandRef = React22.useRef(null);
13680
- const mounted = React22.useRef(false);
13681
- const copying = React22.useRef(false);
13682
- React22.useEffect(() => {
14253
+ const [copyState, setCopyState] = React23.useState("idle");
14254
+ const commandRef = React23.useRef(null);
14255
+ const mounted = React23.useRef(false);
14256
+ const copying = React23.useRef(false);
14257
+ React23.useEffect(() => {
13683
14258
  mounted.current = true;
13684
14259
  return () => {
13685
14260
  mounted.current = false;
@@ -13711,7 +14286,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13711
14286
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
13712
14287
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
13713
14288
  command ? h2(
13714
- React22.Fragment,
14289
+ React23.Fragment,
13715
14290
  null,
13716
14291
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
13717
14292
  h2(
@@ -13747,7 +14322,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13747
14322
  "aria-hidden": "true",
13748
14323
  focusable: "false"
13749
14324
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
13750
- React22.Fragment,
14325
+ React23.Fragment,
13751
14326
  null,
13752
14327
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
13753
14328
  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" })
@@ -13772,10 +14347,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13772
14347
  );
13773
14348
  }
13774
14349
  function UpdateDialog({ children, onClose }) {
13775
- const dialogRef = React22.useRef(null);
13776
- const titleId = React22.useId();
13777
- const descriptionId = React22.useId();
13778
- React22.useEffect(() => {
14350
+ const dialogRef = React23.useRef(null);
14351
+ const titleId = React23.useId();
14352
+ const descriptionId = React23.useId();
14353
+ React23.useEffect(() => {
13779
14354
  const previous = globalThis.document?.activeElement;
13780
14355
  dialogRef.current?.focus?.();
13781
14356
  return () => {
@@ -13833,19 +14408,19 @@ function UpdateDialog({ children, onClose }) {
13833
14408
  return typeof document !== "undefined" && document.body ? (0, import_react_dom3.createPortal)(content, document.body) : content;
13834
14409
  }
13835
14410
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13836
- const [snapshot, setSnapshot] = React22.useState(null);
13837
- const [action, setAction] = React22.useState("status");
13838
- const [error, setError] = React22.useState(null);
13839
- const [open, setOpen] = React22.useState(false);
13840
- const [uncertainInstall, setUncertainInstall] = React22.useState(false);
13841
- const mounted = React22.useRef(false);
13842
- const busy = React22.useRef(false);
13843
- const readController = React22.useRef(null);
13844
- const pollReadController = React22.useRef(null);
13845
- const installRequest = React22.useRef(null);
13846
- const onStatusRef = React22.useRef(onStatus);
14411
+ const [snapshot, setSnapshot] = React23.useState(null);
14412
+ const [action, setAction] = React23.useState("status");
14413
+ const [error, setError] = React23.useState(null);
14414
+ const [open, setOpen] = React23.useState(false);
14415
+ const [uncertainInstall, setUncertainInstall] = React23.useState(false);
14416
+ const mounted = React23.useRef(false);
14417
+ const busy = React23.useRef(false);
14418
+ const readController = React23.useRef(null);
14419
+ const pollReadController = React23.useRef(null);
14420
+ const installRequest = React23.useRef(null);
14421
+ const onStatusRef = React23.useRef(onStatus);
13847
14422
  onStatusRef.current = onStatus;
13848
- const accept = React22.useCallback((next) => {
14423
+ const accept = React23.useCallback((next) => {
13849
14424
  setSnapshot(next);
13850
14425
  onStatusRef.current?.(next);
13851
14426
  }, []);
@@ -13854,7 +14429,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13854
14429
  setUncertainInstall(false);
13855
14430
  accept(next);
13856
14431
  };
13857
- const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
14432
+ const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
13858
14433
  if (typeof rpcCall !== "function") {
13859
14434
  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");
13860
14435
  unavailable.code = "update-unavailable";
@@ -13862,14 +14437,14 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13862
14437
  }
13863
14438
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
13864
14439
  }, [rpcCall]);
13865
- React22.useEffect(() => {
14440
+ React23.useEffect(() => {
13866
14441
  mounted.current = true;
13867
14442
  busy.current = true;
13868
14443
  const controller = new AbortController();
13869
14444
  void invoke("update.status", {}, controller.signal).then((next) => {
13870
14445
  if (!controller.signal.aborted) accept(next);
13871
14446
  }).catch((cause) => {
13872
- if (!controller.signal.aborted) setError(presentError11(cause));
14447
+ if (!controller.signal.aborted) setError(presentError12(cause));
13873
14448
  }).finally(() => {
13874
14449
  if (!controller.signal.aborted) {
13875
14450
  busy.current = false;
@@ -13885,7 +14460,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13885
14460
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
13886
14461
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
13887
14462
  const shouldPoll = activeJob || uncertainInstall;
13888
- React22.useEffect(() => {
14463
+ React23.useEffect(() => {
13889
14464
  if (!shouldPoll) return void 0;
13890
14465
  let controller;
13891
14466
  const scheduler = createPollScheduler({
@@ -13906,7 +14481,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13906
14481
  keepPolling = ACTIVE_STATES4.has(next.job?.state);
13907
14482
  }
13908
14483
  } catch (cause) {
13909
- if (!pendingController.signal.aborted) setError(presentError11(cause));
14484
+ if (!pendingController.signal.aborted) setError(presentError12(cause));
13910
14485
  } finally {
13911
14486
  if (pollReadController.current === pendingController) pollReadController.current = null;
13912
14487
  }
@@ -13937,7 +14512,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13937
14512
  else acceptAuthoritative(next);
13938
14513
  }
13939
14514
  } catch (cause) {
13940
- if (!controller.signal.aborted && mounted.current) setError(presentError11(cause));
14515
+ if (!controller.signal.aborted && mounted.current) setError(presentError12(cause));
13941
14516
  } finally {
13942
14517
  if (!controller.signal.aborted && mounted.current) {
13943
14518
  busy.current = false;
@@ -13963,7 +14538,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13963
14538
  if (mounted.current) acceptAuthoritative(next);
13964
14539
  } catch (cause) {
13965
14540
  if (mounted.current) {
13966
- setError(presentError11(cause));
14541
+ setError(presentError12(cause));
13967
14542
  setUncertainInstall(true);
13968
14543
  }
13969
14544
  } finally {
@@ -13983,7 +14558,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13983
14558
  const manualCommand = manualUpdateCommand(snapshot);
13984
14559
  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";
13985
14560
  return h2(
13986
- React22.Fragment,
14561
+ React23.Fragment,
13987
14562
  null,
13988
14563
  h2("button", {
13989
14564
  type: "button",
@@ -14008,13 +14583,13 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14008
14583
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
14009
14584
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
14010
14585
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
14011
- React22.Fragment,
14586
+ React23.Fragment,
14012
14587
  null,
14013
14588
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
14014
14589
  h2("dd", null, `v${snapshot.installedVersion}`)
14015
14590
  ) : null,
14016
14591
  targetVersion ? h2(
14017
- React22.Fragment,
14592
+ React23.Fragment,
14018
14593
  null,
14019
14594
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
14020
14595
  h2("dd", null, `v${targetVersion}`)
@@ -14214,23 +14789,28 @@ function IMSettingsTab({
14214
14789
  officeRpcCall,
14215
14790
  updateRpcCall,
14216
14791
  deliveryRpcCall,
14792
+ globalSettingsRpcCall,
14217
14793
  workspaceDirectoryPicker,
14218
14794
  browserLocation = globalThis.location,
14219
14795
  navigateToRecoveryUrl = replacePageLocation
14220
14796
  }) {
14221
- const [selected, setSelected] = React23.useState("weixin");
14222
- const [loopbackRecovery, setLoopbackRecovery] = React23.useState(null);
14223
- const [runningVersion, setRunningVersion] = React23.useState(IM_PLUGIN_VERSION);
14224
- const [deliverySettings, setDeliverySettings] = React23.useState(null);
14225
- const githubTooltipId = React23.useId();
14797
+ const [selected, setSelected] = React24.useState("weixin");
14798
+ const [loopbackRecovery, setLoopbackRecovery] = React24.useState(null);
14799
+ const [runningVersion, setRunningVersion] = React24.useState(IM_PLUGIN_VERSION);
14800
+ const [deliverySettings, setDeliverySettings] = React24.useState(null);
14801
+ const githubTooltipId = React24.useId();
14802
+ const generalSettingsTooltipId = React24.useId();
14803
+ const globalSettingsSelected = selected === GLOBAL_SETTINGS_TAB_ID;
14226
14804
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
14227
- const reportLoopbackRecovery = React23.useCallback((recovery) => {
14805
+ const activeTabId = globalSettingsSelected ? "dim-general-settings-trigger" : `dim-tab-${active.id}`;
14806
+ const activePanelId = globalSettingsSelected ? `dim-panel-${GLOBAL_SETTINGS_TAB_ID}` : `dim-panel-${active.id}`;
14807
+ const reportLoopbackRecovery = React24.useCallback((recovery) => {
14228
14808
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
14229
14809
  }, []);
14230
- const reportUpdateStatus = React23.useCallback((snapshot) => {
14810
+ const reportUpdateStatus = React24.useCallback((snapshot) => {
14231
14811
  setRunningVersion(snapshot.runningVersion);
14232
14812
  }, []);
14233
- const rpcCalls = React23.useMemo(() => createLoopbackAwareRpcCalls({
14813
+ const rpcCalls = React24.useMemo(() => createLoopbackAwareRpcCalls({
14234
14814
  dingtalkRpcCall,
14235
14815
  discordRpcCall,
14236
14816
  feishuRpcCall,
@@ -14242,7 +14822,8 @@ function IMSettingsTab({
14242
14822
  whatsappRpcCall,
14243
14823
  officeRpcCall,
14244
14824
  updateRpcCall,
14245
- deliveryRpcCall
14825
+ deliveryRpcCall,
14826
+ globalSettingsRpcCall
14246
14827
  }, {
14247
14828
  location: browserLocation,
14248
14829
  onRecovery: reportLoopbackRecovery
@@ -14252,6 +14833,7 @@ function IMSettingsTab({
14252
14833
  discordRpcCall,
14253
14834
  deliveryRpcCall,
14254
14835
  feishuRpcCall,
14836
+ globalSettingsRpcCall,
14255
14837
  officeRpcCall,
14256
14838
  qqRpcCall,
14257
14839
  reportLoopbackRecovery,
@@ -14262,7 +14844,7 @@ function IMSettingsTab({
14262
14844
  weixinRpcCall,
14263
14845
  whatsappRpcCall
14264
14846
  ]);
14265
- const botSettingsContext = React23.useMemo(() => Object.freeze({
14847
+ const botSettingsContext = React24.useMemo(() => Object.freeze({
14266
14848
  openBotSettings: setDeliverySettings
14267
14849
  }), []);
14268
14850
  return h2(
@@ -14314,6 +14896,28 @@ function IMSettingsTab({
14314
14896
  className: "dim-githubTooltip",
14315
14897
  role: "tooltip"
14316
14898
  }, "\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub")
14899
+ ),
14900
+ h2(
14901
+ "span",
14902
+ { className: "dim-generalSettingsAction" },
14903
+ h2("button", {
14904
+ type: "button",
14905
+ id: "dim-general-settings-trigger",
14906
+ className: "dim-generalSettingsButton",
14907
+ "aria-label": "\u901A\u7528\u8BBE\u7F6E",
14908
+ "aria-describedby": generalSettingsTooltipId,
14909
+ "aria-controls": `dim-panel-${GLOBAL_SETTINGS_TAB_ID}`,
14910
+ "aria-current": globalSettingsSelected ? "page" : void 0,
14911
+ onClick: () => {
14912
+ setSelected(GLOBAL_SETTINGS_TAB_ID);
14913
+ setDeliverySettings(null);
14914
+ }
14915
+ }, h2(GlobalSettingsLogoGlyph, { size: 17 })),
14916
+ h2("span", {
14917
+ id: generalSettingsTooltipId,
14918
+ className: "dim-generalSettingsTooltip",
14919
+ role: "tooltip"
14920
+ }, "\u901A\u7528\u8BBE\u7F6E")
14317
14921
  )
14318
14922
  )
14319
14923
  ),
@@ -14322,7 +14926,7 @@ function IMSettingsTab({
14322
14926
  { className: "dim-layout" },
14323
14927
  h2(
14324
14928
  "nav",
14325
- { className: "dim-rail", role: "tablist", "aria-label": "IM \u6E20\u9053" },
14929
+ { className: "dim-rail", role: "tablist", "aria-label": "IM \u8BBE\u7F6E\u5BFC\u822A" },
14326
14930
  CHANNELS.map((channel4) => h2(
14327
14931
  "button",
14328
14932
  {
@@ -14331,7 +14935,7 @@ function IMSettingsTab({
14331
14935
  role: "tab",
14332
14936
  id: `dim-tab-${channel4.id}`,
14333
14937
  className: "dim-channel",
14334
- "aria-selected": channel4.id === active.id,
14938
+ "aria-selected": !globalSettingsSelected && channel4.id === active.id,
14335
14939
  "aria-controls": `dim-panel-${channel4.id}`,
14336
14940
  onClick: () => {
14337
14941
  setSelected(channel4.id);
@@ -14353,8 +14957,8 @@ function IMSettingsTab({
14353
14957
  {
14354
14958
  className: "dim-panel",
14355
14959
  role: "tabpanel",
14356
- id: `dim-panel-${active.id}`,
14357
- "aria-labelledby": `dim-tab-${active.id}`
14960
+ id: activePanelId,
14961
+ "aria-labelledby": activeTabId
14358
14962
  },
14359
14963
  loopbackRecovery ? h2(LoopbackRecoveryNotice, {
14360
14964
  recovery: loopbackRecovery,
@@ -14363,7 +14967,7 @@ function IMSettingsTab({
14363
14967
  h2(
14364
14968
  BotSettingsContext.Provider,
14365
14969
  { value: botSettingsContext },
14366
- deliverySettings?.channel === active.id ? h2(DeliveryTargetSettingsPage, {
14970
+ globalSettingsSelected ? h2(GlobalSettingsPanel, { rpcCall: rpcCalls.globalSettingsRpcCall }) : deliverySettings?.channel === active.id ? h2(DeliveryTargetSettingsPage, {
14367
14971
  channel: active.id,
14368
14972
  account: deliverySettings,
14369
14973
  rpcCall: rpcCalls.deliveryRpcCall,
@@ -14412,6 +15016,7 @@ function apply(ctx) {
14412
15016
  const officeRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(OFFICE_RPC_CHANNEL, endpoint, payload, signal);
14413
15017
  const updateRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(UPDATE_RPC_CHANNEL, endpoint, payload, signal);
14414
15018
  const deliveryRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(DELIVERY_RPC_CHANNEL, endpoint, payload, signal);
15019
+ const globalSettingsRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(GLOBAL_SETTINGS_RPC_CHANNEL, endpoint, payload, signal);
14415
15020
  const workspaceDirectoryPicker = Object.freeze({
14416
15021
  listDirectory: (path, signal) => callWorkspaceDirectoryApi(ctx, "listDirectory", path, signal),
14417
15022
  pickDirectory: () => callWorkspaceDirectoryApi(ctx, "pickDirectory")
@@ -14435,6 +15040,7 @@ function apply(ctx) {
14435
15040
  officeRpcCall,
14436
15041
  updateRpcCall,
14437
15042
  deliveryRpcCall,
15043
+ globalSettingsRpcCall,
14438
15044
  workspaceDirectoryPicker
14439
15045
  })
14440
15046
  }, IMSettingsTab));