@xmanrui/dsh-im 4.9.1 → 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 (39) hide show
  1. package/PROACTIVE_DELIVERY.en.md +8 -0
  2. package/PROACTIVE_DELIVERY.md +8 -0
  3. package/lib/client.js +610 -56
  4. package/lib/index.js +273 -272
  5. package/package.json +12 -1
  6. package/plugin-src/client/channels/feishu/api.js +2 -0
  7. package/plugin-src/client/channels/feishu/index.js +54 -0
  8. package/plugin-src/client/global-settings.js +341 -0
  9. package/plugin-src/client/i18n.js +29 -0
  10. package/plugin-src/client/index.js +47 -6
  11. package/plugin-src/client/styles.js +49 -0
  12. package/plugin-src/host/channels/dingtalk/production.mjs +12 -0
  13. package/plugin-src/host/channels/feishu/production.mjs +14 -0
  14. package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
  15. package/plugin-src/host/channels/qq/production.mjs +12 -0
  16. package/plugin-src/host/channels/shared/production.mjs +12 -0
  17. package/plugin-src/host/channels/slack/production.mjs +12 -0
  18. package/plugin-src/host/channels/wecom/production.mjs +12 -0
  19. package/plugin-src/host/channels/weixin/production.mjs +12 -0
  20. package/plugin-src/host/channels/whatsapp/production.mjs +12 -0
  21. package/plugin-src/host/delivery-adapter.mjs +1 -0
  22. package/plugin-src/host/delivery-service.mjs +15 -0
  23. package/plugin-src/host/harness-session-coordinator.mjs +26 -5
  24. package/plugin-src/host/inbound-ttl-rpc.mjs +83 -0
  25. package/plugin-src/host/inbound-ttl-runtime.mjs +85 -0
  26. package/plugin-src/host/inbound-ttl-service.mjs +179 -0
  27. package/plugin-src/host/index.mjs +8 -0
  28. package/plugin-src/host/modern-harness-api.mjs +58 -2
  29. package/src/channels/feishu/bridge.mjs +164 -4
  30. package/src/channels/feishu/feishu-channel.mjs +45 -10
  31. package/src/channels/feishu/feishu-runtime.mjs +9 -0
  32. package/src/channels/feishu/message-utils.mjs +13 -0
  33. package/src/channels/feishu/multi-bot-controller.mjs +16 -0
  34. package/src/channels/feishu/plugin-config-store.mjs +1 -0
  35. package/src/channels/feishu/state-store.mjs +25 -0
  36. package/src/channels/shared/bot-workspace-store.mjs +6 -0
  37. package/src/channels/shared/inbound-file.mjs +117 -2
  38. package/src/channels/shared/inbound-ttl-store.mjs +110 -0
  39. 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.1",
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",
@@ -658,6 +658,17 @@ var package_default = {
658
658
  "@deepseek-ai/dsh-client-locale"
659
659
  ],
660
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
+ ]
661
672
  }
662
673
  },
663
674
  scripts: {
@@ -1057,6 +1068,11 @@ var EN = Object.freeze({
1057
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.",
1058
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.",
1059
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.",
1060
1076
  "\u9489\u9489\u8BBE\u7F6E": "DingTalk settings",
1061
1077
  "\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E": "WeCom settings",
1062
1078
  "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA": "Scan QR code",
@@ -1678,7 +1694,29 @@ var EN = Object.freeze({
1678
1694
  "\u65B0\u5EFA\u76EE\u6807": "New target",
1679
1695
  "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026": "Loading delivery targets\u2026",
1680
1696
  "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807": "No delivery targets yet",
1681
- "\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"
1682
1720
  });
1683
1721
  var en = EN;
1684
1722
  var zh = Object.freeze(Object.fromEntries(
@@ -1701,6 +1739,8 @@ function translateDynamic(text6) {
1701
1739
  if (match) return `${match[1]}/${match[2]} online`;
1702
1740
  match = /^已接入 (\d+) 个机器人,其中 (\d+) 个在线$/.exec(text6);
1703
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]}.`;
1704
1744
  match = /^正在读取\s*(.+?)\s*机器人状态…$/.exec(text6);
1705
1745
  if (match) return `Loading ${channelName(match[1])} bot status\u2026`;
1706
1746
  match = /^无法读取\s*(.+?)\s*机器人状态$/.exec(text6);
@@ -4764,7 +4804,7 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
4764
4804
  function createTokenChannelApi(channel4, connectionSummary, {
4765
4805
  normalizeBotExtension = () => ({})
4766
4806
  } = {}) {
4767
- const unwrapRpcResult12 = (result) => {
4807
+ const unwrapRpcResult13 = (result) => {
4768
4808
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
4769
4809
  throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
4770
4810
  }
@@ -4821,11 +4861,11 @@ function createTokenChannelApi(channel4, connectionSummary, {
4821
4861
  agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
4822
4862
  };
4823
4863
  };
4824
- const presentError12 = (error) => ({
4864
+ const presentError13 = (error) => ({
4825
4865
  code: text2(error?.code, `${channel4.toUpperCase()}_ERROR`, 80),
4826
4866
  message: text2(error?.message, `${channel4}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
4827
4867
  });
4828
- return Object.freeze({ unwrapRpcResult: unwrapRpcResult12, normalizeSnapshot: normalizeSnapshot9, presentError: presentError12 });
4868
+ return Object.freeze({ unwrapRpcResult: unwrapRpcResult13, normalizeSnapshot: normalizeSnapshot9, presentError: presentError13 });
4829
4869
  }
4830
4870
 
4831
4871
  // plugin-src/client/channels/discord/api.js
@@ -5374,6 +5414,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
5374
5414
  setContextEnhancement: "bot.context-enhancement.set",
5375
5415
  setAccessPolicy: "bot.access-policy.set",
5376
5416
  setGroupResponseMode: "bot.group-response-mode.set",
5417
+ setGroupTopicReply: "bot.group-topic-reply.set",
5377
5418
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
5378
5419
  testConnection: "connection.test",
5379
5420
  disconnect: "connection.disconnect"
@@ -5524,6 +5565,7 @@ function normalizeBotConnection(value, fallbackBotId) {
5524
5565
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
5525
5566
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
5526
5567
  groupResponseMode: normalizeGroupResponseMode(value.groupResponseMode),
5568
+ groupTopicReply: value.groupTopicReply === true,
5527
5569
  groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
5528
5570
  bot: normalizeBot2(value.bot),
5529
5571
  health: normalizeHealth(value.health, connected),
@@ -6182,6 +6224,57 @@ function GroupResponseModeEditor({
6182
6224
  }, error) : null
6183
6225
  );
6184
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
+ }
6185
6278
  function BotCard({
6186
6279
  connection,
6187
6280
  busy,
@@ -6197,6 +6290,7 @@ function BotCard({
6197
6290
  onAgentPresetSave,
6198
6291
  onContextEnhancementSave,
6199
6292
  onGroupResponseModeSave,
6293
+ onGroupTopicReplySave,
6200
6294
  onGroupMessagePermissionAuthorize,
6201
6295
  onRequestRemove,
6202
6296
  onConfirmRemove,
@@ -6284,6 +6378,11 @@ function BotCard({
6284
6378
  onSave: onGroupResponseModeSave,
6285
6379
  onAuthorize: onGroupMessagePermissionAuthorize
6286
6380
  }),
6381
+ h2(GroupTopicReplyEditor, {
6382
+ value: connection.groupTopicReply,
6383
+ disabled: Boolean(busy),
6384
+ onSave: onGroupTopicReplySave
6385
+ }),
6287
6386
  provisionContent ? h2("section", {
6288
6387
  className: "bxf-botProvision dim-botProvision",
6289
6388
  "aria-label": `${bot.name}\u7684\u98DE\u4E66\u6388\u6743\u6D41\u7A0B`,
@@ -6393,6 +6492,7 @@ function BotList(props) {
6393
6492
  onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(bot, agentPreset),
6394
6493
  onContextEnhancementSave: (config) => props.onContextEnhancementSave(bot, config),
6395
6494
  onGroupResponseModeSave: (groupResponseMode) => props.onGroupResponseModeSave(bot, groupResponseMode),
6495
+ onGroupTopicReplySave: (groupTopicReply) => props.onGroupTopicReplySave(bot, groupTopicReply),
6396
6496
  onGroupMessagePermissionAuthorize: () => props.onGroupMessagePermissionAuthorize(bot),
6397
6497
  onRequestRemove: () => props.onRequestRemove(bot),
6398
6498
  onConfirmRemove: () => props.onConfirmRemove(bot),
@@ -7135,6 +7235,12 @@ function FeishuSettingsTab({ rpcCall }) {
7135
7235
  { config }
7136
7236
  ),
7137
7237
  onGroupResponseModeSave: saveGroupResponseMode,
7238
+ onGroupTopicReplySave: (connection, groupTopicReply) => saveBotSetting(
7239
+ connection,
7240
+ "group-topic-reply",
7241
+ FEISHU_ENDPOINTS.setGroupTopicReply,
7242
+ { groupTopicReply }
7243
+ ),
7138
7244
  onGroupMessagePermissionAuthorize: authorizeGroupMessages,
7139
7245
  onRequestRemove: requestRemove,
7140
7246
  onConfirmRemove: (bot) => void confirmRemove(bot),
@@ -13028,6 +13134,374 @@ function DeliveryTargetSettingsPage({
13028
13134
  );
13029
13135
  }
13030
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
+
13031
13505
  // plugin-src/client/loopback-recovery.js
13032
13506
  var TRANSPORT_FORBIDDEN = /^transport failure for \/[A-Za-z0-9._~-]+\/[A-Za-z0-9_$./~-]+: HTTP 403$/;
13033
13507
  var LOOPBACK_RECOVERY_ERROR_CODE = "loopback-recovery-required";
@@ -13146,6 +13620,15 @@ var CSS11 = String.raw`
13146
13620
  .dim-githubArrow { font-size: 13px; line-height: 1; }
13147
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; }
13148
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); }
13149
13632
  .dim-layout { display: grid; grid-template-columns: 174px 1px minmax(0, 1fr); gap: 24px; align-items: start; }
13150
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; }
13151
13634
  .dim-rail::-webkit-scrollbar { width: 4px; }
@@ -13553,6 +14036,46 @@ var CSS11 = String.raw`
13553
14036
  .dim-accessFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
13554
14037
  .dim-accessFeedback[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
13555
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); }
13556
14079
  .dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
13557
14080
  .dim-panel .ddt-headingCopy { display: none; }
13558
14081
  .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
@@ -13637,7 +14160,7 @@ function installImStyles() {
13637
14160
  }
13638
14161
 
13639
14162
  // plugin-src/client/update-panel.js
13640
- var React22 = __toESM(require("react"), 1);
14163
+ var React23 = __toESM(require("react"), 1);
13641
14164
  var import_react_dom3 = require("react-dom");
13642
14165
  var import_valid = __toESM(require_valid(), 1);
13643
14166
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -13684,7 +14207,7 @@ function unwrapSnapshot(result) {
13684
14207
  }
13685
14208
  return value;
13686
14209
  }
13687
- function presentError11(error) {
14210
+ function presentError12(error) {
13688
14211
  const code = error?.rpcError?.code ?? error?.code ?? "";
13689
14212
  const message = error?.rpcError?.message ?? error?.message ?? "";
13690
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)) {
@@ -13727,11 +14250,11 @@ function manualUpdateCommand(snapshot) {
13727
14250
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
13728
14251
  }
13729
14252
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13730
- const [copyState, setCopyState] = React22.useState("idle");
13731
- const commandRef = React22.useRef(null);
13732
- const mounted = React22.useRef(false);
13733
- const copying = React22.useRef(false);
13734
- 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(() => {
13735
14258
  mounted.current = true;
13736
14259
  return () => {
13737
14260
  mounted.current = false;
@@ -13763,7 +14286,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13763
14286
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
13764
14287
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
13765
14288
  command ? h2(
13766
- React22.Fragment,
14289
+ React23.Fragment,
13767
14290
  null,
13768
14291
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
13769
14292
  h2(
@@ -13799,7 +14322,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13799
14322
  "aria-hidden": "true",
13800
14323
  focusable: "false"
13801
14324
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
13802
- React22.Fragment,
14325
+ React23.Fragment,
13803
14326
  null,
13804
14327
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
13805
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" })
@@ -13824,10 +14347,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
13824
14347
  );
13825
14348
  }
13826
14349
  function UpdateDialog({ children, onClose }) {
13827
- const dialogRef = React22.useRef(null);
13828
- const titleId = React22.useId();
13829
- const descriptionId = React22.useId();
13830
- React22.useEffect(() => {
14350
+ const dialogRef = React23.useRef(null);
14351
+ const titleId = React23.useId();
14352
+ const descriptionId = React23.useId();
14353
+ React23.useEffect(() => {
13831
14354
  const previous = globalThis.document?.activeElement;
13832
14355
  dialogRef.current?.focus?.();
13833
14356
  return () => {
@@ -13885,19 +14408,19 @@ function UpdateDialog({ children, onClose }) {
13885
14408
  return typeof document !== "undefined" && document.body ? (0, import_react_dom3.createPortal)(content, document.body) : content;
13886
14409
  }
13887
14410
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13888
- const [snapshot, setSnapshot] = React22.useState(null);
13889
- const [action, setAction] = React22.useState("status");
13890
- const [error, setError] = React22.useState(null);
13891
- const [open, setOpen] = React22.useState(false);
13892
- const [uncertainInstall, setUncertainInstall] = React22.useState(false);
13893
- const mounted = React22.useRef(false);
13894
- const busy = React22.useRef(false);
13895
- const readController = React22.useRef(null);
13896
- const pollReadController = React22.useRef(null);
13897
- const installRequest = React22.useRef(null);
13898
- 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);
13899
14422
  onStatusRef.current = onStatus;
13900
- const accept = React22.useCallback((next) => {
14423
+ const accept = React23.useCallback((next) => {
13901
14424
  setSnapshot(next);
13902
14425
  onStatusRef.current?.(next);
13903
14426
  }, []);
@@ -13906,7 +14429,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13906
14429
  setUncertainInstall(false);
13907
14430
  accept(next);
13908
14431
  };
13909
- const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
14432
+ const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
13910
14433
  if (typeof rpcCall !== "function") {
13911
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");
13912
14435
  unavailable.code = "update-unavailable";
@@ -13914,14 +14437,14 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13914
14437
  }
13915
14438
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
13916
14439
  }, [rpcCall]);
13917
- React22.useEffect(() => {
14440
+ React23.useEffect(() => {
13918
14441
  mounted.current = true;
13919
14442
  busy.current = true;
13920
14443
  const controller = new AbortController();
13921
14444
  void invoke("update.status", {}, controller.signal).then((next) => {
13922
14445
  if (!controller.signal.aborted) accept(next);
13923
14446
  }).catch((cause) => {
13924
- if (!controller.signal.aborted) setError(presentError11(cause));
14447
+ if (!controller.signal.aborted) setError(presentError12(cause));
13925
14448
  }).finally(() => {
13926
14449
  if (!controller.signal.aborted) {
13927
14450
  busy.current = false;
@@ -13937,7 +14460,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13937
14460
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
13938
14461
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
13939
14462
  const shouldPoll = activeJob || uncertainInstall;
13940
- React22.useEffect(() => {
14463
+ React23.useEffect(() => {
13941
14464
  if (!shouldPoll) return void 0;
13942
14465
  let controller;
13943
14466
  const scheduler = createPollScheduler({
@@ -13958,7 +14481,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13958
14481
  keepPolling = ACTIVE_STATES4.has(next.job?.state);
13959
14482
  }
13960
14483
  } catch (cause) {
13961
- if (!pendingController.signal.aborted) setError(presentError11(cause));
14484
+ if (!pendingController.signal.aborted) setError(presentError12(cause));
13962
14485
  } finally {
13963
14486
  if (pollReadController.current === pendingController) pollReadController.current = null;
13964
14487
  }
@@ -13989,7 +14512,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
13989
14512
  else acceptAuthoritative(next);
13990
14513
  }
13991
14514
  } catch (cause) {
13992
- if (!controller.signal.aborted && mounted.current) setError(presentError11(cause));
14515
+ if (!controller.signal.aborted && mounted.current) setError(presentError12(cause));
13993
14516
  } finally {
13994
14517
  if (!controller.signal.aborted && mounted.current) {
13995
14518
  busy.current = false;
@@ -14015,7 +14538,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14015
14538
  if (mounted.current) acceptAuthoritative(next);
14016
14539
  } catch (cause) {
14017
14540
  if (mounted.current) {
14018
- setError(presentError11(cause));
14541
+ setError(presentError12(cause));
14019
14542
  setUncertainInstall(true);
14020
14543
  }
14021
14544
  } finally {
@@ -14035,7 +14558,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14035
14558
  const manualCommand = manualUpdateCommand(snapshot);
14036
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";
14037
14560
  return h2(
14038
- React22.Fragment,
14561
+ React23.Fragment,
14039
14562
  null,
14040
14563
  h2("button", {
14041
14564
  type: "button",
@@ -14060,13 +14583,13 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14060
14583
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
14061
14584
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
14062
14585
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
14063
- React22.Fragment,
14586
+ React23.Fragment,
14064
14587
  null,
14065
14588
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
14066
14589
  h2("dd", null, `v${snapshot.installedVersion}`)
14067
14590
  ) : null,
14068
14591
  targetVersion ? h2(
14069
- React22.Fragment,
14592
+ React23.Fragment,
14070
14593
  null,
14071
14594
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
14072
14595
  h2("dd", null, `v${targetVersion}`)
@@ -14266,23 +14789,28 @@ function IMSettingsTab({
14266
14789
  officeRpcCall,
14267
14790
  updateRpcCall,
14268
14791
  deliveryRpcCall,
14792
+ globalSettingsRpcCall,
14269
14793
  workspaceDirectoryPicker,
14270
14794
  browserLocation = globalThis.location,
14271
14795
  navigateToRecoveryUrl = replacePageLocation
14272
14796
  }) {
14273
- const [selected, setSelected] = React23.useState("weixin");
14274
- const [loopbackRecovery, setLoopbackRecovery] = React23.useState(null);
14275
- const [runningVersion, setRunningVersion] = React23.useState(IM_PLUGIN_VERSION);
14276
- const [deliverySettings, setDeliverySettings] = React23.useState(null);
14277
- 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;
14278
14804
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
14279
- 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) => {
14280
14808
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
14281
14809
  }, []);
14282
- const reportUpdateStatus = React23.useCallback((snapshot) => {
14810
+ const reportUpdateStatus = React24.useCallback((snapshot) => {
14283
14811
  setRunningVersion(snapshot.runningVersion);
14284
14812
  }, []);
14285
- const rpcCalls = React23.useMemo(() => createLoopbackAwareRpcCalls({
14813
+ const rpcCalls = React24.useMemo(() => createLoopbackAwareRpcCalls({
14286
14814
  dingtalkRpcCall,
14287
14815
  discordRpcCall,
14288
14816
  feishuRpcCall,
@@ -14294,7 +14822,8 @@ function IMSettingsTab({
14294
14822
  whatsappRpcCall,
14295
14823
  officeRpcCall,
14296
14824
  updateRpcCall,
14297
- deliveryRpcCall
14825
+ deliveryRpcCall,
14826
+ globalSettingsRpcCall
14298
14827
  }, {
14299
14828
  location: browserLocation,
14300
14829
  onRecovery: reportLoopbackRecovery
@@ -14304,6 +14833,7 @@ function IMSettingsTab({
14304
14833
  discordRpcCall,
14305
14834
  deliveryRpcCall,
14306
14835
  feishuRpcCall,
14836
+ globalSettingsRpcCall,
14307
14837
  officeRpcCall,
14308
14838
  qqRpcCall,
14309
14839
  reportLoopbackRecovery,
@@ -14314,7 +14844,7 @@ function IMSettingsTab({
14314
14844
  weixinRpcCall,
14315
14845
  whatsappRpcCall
14316
14846
  ]);
14317
- const botSettingsContext = React23.useMemo(() => Object.freeze({
14847
+ const botSettingsContext = React24.useMemo(() => Object.freeze({
14318
14848
  openBotSettings: setDeliverySettings
14319
14849
  }), []);
14320
14850
  return h2(
@@ -14366,6 +14896,28 @@ function IMSettingsTab({
14366
14896
  className: "dim-githubTooltip",
14367
14897
  role: "tooltip"
14368
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")
14369
14921
  )
14370
14922
  )
14371
14923
  ),
@@ -14374,7 +14926,7 @@ function IMSettingsTab({
14374
14926
  { className: "dim-layout" },
14375
14927
  h2(
14376
14928
  "nav",
14377
- { className: "dim-rail", role: "tablist", "aria-label": "IM \u6E20\u9053" },
14929
+ { className: "dim-rail", role: "tablist", "aria-label": "IM \u8BBE\u7F6E\u5BFC\u822A" },
14378
14930
  CHANNELS.map((channel4) => h2(
14379
14931
  "button",
14380
14932
  {
@@ -14383,7 +14935,7 @@ function IMSettingsTab({
14383
14935
  role: "tab",
14384
14936
  id: `dim-tab-${channel4.id}`,
14385
14937
  className: "dim-channel",
14386
- "aria-selected": channel4.id === active.id,
14938
+ "aria-selected": !globalSettingsSelected && channel4.id === active.id,
14387
14939
  "aria-controls": `dim-panel-${channel4.id}`,
14388
14940
  onClick: () => {
14389
14941
  setSelected(channel4.id);
@@ -14405,8 +14957,8 @@ function IMSettingsTab({
14405
14957
  {
14406
14958
  className: "dim-panel",
14407
14959
  role: "tabpanel",
14408
- id: `dim-panel-${active.id}`,
14409
- "aria-labelledby": `dim-tab-${active.id}`
14960
+ id: activePanelId,
14961
+ "aria-labelledby": activeTabId
14410
14962
  },
14411
14963
  loopbackRecovery ? h2(LoopbackRecoveryNotice, {
14412
14964
  recovery: loopbackRecovery,
@@ -14415,7 +14967,7 @@ function IMSettingsTab({
14415
14967
  h2(
14416
14968
  BotSettingsContext.Provider,
14417
14969
  { value: botSettingsContext },
14418
- deliverySettings?.channel === active.id ? h2(DeliveryTargetSettingsPage, {
14970
+ globalSettingsSelected ? h2(GlobalSettingsPanel, { rpcCall: rpcCalls.globalSettingsRpcCall }) : deliverySettings?.channel === active.id ? h2(DeliveryTargetSettingsPage, {
14419
14971
  channel: active.id,
14420
14972
  account: deliverySettings,
14421
14973
  rpcCall: rpcCalls.deliveryRpcCall,
@@ -14464,6 +15016,7 @@ function apply(ctx) {
14464
15016
  const officeRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(OFFICE_RPC_CHANNEL, endpoint, payload, signal);
14465
15017
  const updateRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(UPDATE_RPC_CHANNEL, endpoint, payload, signal);
14466
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);
14467
15020
  const workspaceDirectoryPicker = Object.freeze({
14468
15021
  listDirectory: (path, signal) => callWorkspaceDirectoryApi(ctx, "listDirectory", path, signal),
14469
15022
  pickDirectory: () => callWorkspaceDirectoryApi(ctx, "pickDirectory")
@@ -14487,6 +15040,7 @@ function apply(ctx) {
14487
15040
  officeRpcCall,
14488
15041
  updateRpcCall,
14489
15042
  deliveryRpcCall,
15043
+ globalSettingsRpcCall,
14490
15044
  workspaceDirectoryPicker
14491
15045
  })
14492
15046
  }, IMSettingsTab));