@xmanrui/dsh-im 1.0.2 → 1.2.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 (52) hide show
  1. package/README.en.md +23 -3
  2. package/README.md +23 -3
  3. package/assets/logo-dsh-im-chinese-readme-3x2.png +0 -0
  4. package/assets/logo_cn.png +0 -0
  5. package/lib/client.js +815 -560
  6. package/lib/index.js +163 -163
  7. package/package.json +1 -1
  8. package/plugin-src/client/agent-preset.js +15 -6
  9. package/plugin-src/client/channel-card-meta.js +48 -0
  10. package/plugin-src/client/channels/dingtalk/index.js +25 -19
  11. package/plugin-src/client/channels/dingtalk/styles.js +0 -6
  12. package/plugin-src/client/channels/feishu/index.js +41 -35
  13. package/plugin-src/client/channels/feishu/styles.js +0 -5
  14. package/plugin-src/client/channels/qq/index.js +24 -16
  15. package/plugin-src/client/channels/shared/token-channel.js +32 -24
  16. package/plugin-src/client/channels/wecom/index.js +24 -16
  17. package/plugin-src/client/channels/weixin/index.js +29 -23
  18. package/plugin-src/client/channels/weixin/styles.js +0 -5
  19. package/plugin-src/client/channels/whatsapp/api.js +11 -0
  20. package/plugin-src/client/channels/whatsapp/index.js +152 -23
  21. package/plugin-src/client/channels/whatsapp/styles.js +25 -0
  22. package/plugin-src/client/i18n.js +20 -0
  23. package/plugin-src/client/styles.js +23 -8
  24. package/plugin-src/host/channels/whatsapp/rpc.mjs +19 -1
  25. package/plugin-src/host/index.mjs +14 -1
  26. package/src/channels/dingtalk/dingtalk-api.mjs +215 -2
  27. package/src/channels/dingtalk/dingtalk-bridge.mjs +155 -4
  28. package/src/channels/discord/discord-api.mjs +134 -6
  29. package/src/channels/discord/discord-runtime.mjs +15 -4
  30. package/src/channels/feishu/bridge.mjs +223 -15
  31. package/src/channels/feishu/feishu-channel.mjs +227 -1
  32. package/src/channels/feishu/plugin-controller.mjs +1 -0
  33. package/src/channels/qq/qq-bridge.mjs +217 -10
  34. package/src/channels/shared/editable-message-stream.mjs +18 -1
  35. package/src/channels/shared/harness-client.mjs +99 -7
  36. package/src/channels/shared/semantic/artifact.mjs +748 -0
  37. package/src/channels/shared/semantic/delivery.mjs +153 -0
  38. package/src/channels/shared/text-harness-bridge.mjs +149 -3
  39. package/src/channels/shared/workspace-session.mjs +15 -1
  40. package/src/channels/slack/manifest.mjs +1 -0
  41. package/src/channels/slack/slack-api.mjs +167 -4
  42. package/src/channels/slack/slack-runtime.mjs +21 -5
  43. package/src/channels/telegram/telegram-api.mjs +111 -5
  44. package/src/channels/telegram/telegram-runtime.mjs +18 -4
  45. package/src/channels/wecom/wecom-bridge.mjs +260 -12
  46. package/src/channels/weixin/weixin-api.mjs +268 -2
  47. package/src/channels/weixin/weixin-bridge.mjs +134 -3
  48. package/src/channels/weixin/weixin-controller.mjs +5 -1
  49. package/src/channels/weixin/weixin-runtime.mjs +5 -1
  50. package/src/channels/whatsapp/config-store.mjs +43 -0
  51. package/src/channels/whatsapp/whatsapp-controller.mjs +22 -1
  52. package/src/channels/whatsapp/whatsapp-runtime.mjs +149 -5
package/lib/client.js CHANGED
@@ -40,7 +40,7 @@ __export(index_exports, {
40
40
  name: () => name
41
41
  });
42
42
  module.exports = __toCommonJS(index_exports);
43
- var React19 = __toESM(require("react"), 1);
43
+ var React20 = __toESM(require("react"), 1);
44
44
 
45
45
  // plugin-src/client/channel-logos.js
46
46
  var React = __toESM(require("react"), 1);
@@ -297,6 +297,7 @@ var EN = Object.freeze({
297
297
  "\u79BB\u7EBF": "Offline",
298
298
  "\u5DF2\u65AD\u5F00": "Disconnected",
299
299
  "\u6D88\u606F\u901A\u9053": "Message channel",
300
+ "\u67E5\u770B\u6D88\u606F\u901A\u9053\u8BF4\u660E": "View message channel details",
300
301
  "\u6700\u8FD1\u68C0\u67E5": "Last checked",
301
302
  "\u5F53\u524D\u5DE5\u4F5C\u533A": "Current workspace",
302
303
  "\u9009\u62E9\u76EE\u5F55": "Choose folder",
@@ -326,6 +327,7 @@ var EN = Object.freeze({
326
327
  "\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002": "The workspace path must point to a directory.",
327
328
  "\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002": "The bot could not be found.",
328
329
  "Agent Preset": "Agent Preset",
330
+ "\u67E5\u770B Agent Preset \u8BF4\u660E": "View Agent Preset help",
329
331
  "\u8DDF\u968F Host \u9ED8\u8BA4": "Follow the Host default",
330
332
  "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09": " (unavailable)",
331
333
  "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002": "This affects only new sessions. If the current chat already has a session, send /new, then send a regular message to apply it.",
@@ -548,6 +550,24 @@ var EN = Object.freeze({
548
550
  "\u6B63\u5728\u5EFA\u7ACB\u5B89\u5168\u7684\u5173\u8054\u8BBE\u5907\u4F1A\u8BDD\u3002": "Creating a secure linked-device session.",
549
551
  "\u5173\u8054\u8BBE\u5907\u6B63\u5728\u63A5\u5165 DeepSeek Harness\u3002": "Linking the device to DeepSeek Harness.",
550
552
  "WhatsApp Web \u5173\u8054\u8BBE\u5907\u8FD0\u884C\u6B63\u5E38": "WhatsApp linked device is healthy",
553
+ "\u67E5\u770B WhatsApp \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E": "View WhatsApp access mode details",
554
+ "WhatsApp \u8BBF\u95EE\u6A21\u5F0F": "WhatsApp access mode",
555
+ "\u4EC5\u81EA\u5DF1\u6A21\u5F0F": "Only me",
556
+ "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F": "Selected contacts",
557
+ "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F": "Open responses",
558
+ "\u4EC5\u81EA\u5DF1\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09": "Only me (default)",
559
+ "\u5DF2\u751F\u6548\uFF1A": "Active: ",
560
+ "\u53EA\u54CD\u5E94\u5DF2\u7ED1\u5B9A WhatsApp \u8D26\u53F7\u7684\u81EA\u804A\u6D88\u606F\u3002": "Only respond to self-chat messages from the linked WhatsApp account.",
561
+ "\u54CD\u5E94\u81EA\u804A\u548C\u767D\u540D\u5355\u8054\u7CFB\u4EBA\u7684\u79C1\u804A\uFF0C\u5FFD\u7565\u7FA4\u804A\u3002": "Respond to self-chat and allowlisted direct messages; ignore group messages.",
562
+ "\u54CD\u5E94\u6240\u6709\u79C1\u804A\uFF0C\u4EE5\u53CA\u7FA4\u804A\u4E2D\u7684\u63D0\u53CA\u6216\u56DE\u590D\u3002": "Respond to all direct messages and to group mentions or replies.",
563
+ "\u5141\u8BB8\u79C1\u804A\u7684 WhatsApp \u7535\u8BDD\u53F7\u7801": "WhatsApp phone numbers allowed to send direct messages",
564
+ "\u6BCF\u884C\u4E00\u4E2A\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\u7684\u53F7\u7801": "One number with country or region code per line",
565
+ "\u53EF\u4EE5\u5305\u542B\u5F00\u5934\u7684 +\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u81EA\u52A8\u79FB\u9664\u3002": "A leading + is allowed and removed when saved.",
566
+ "\u4EC5\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F\u4F7F\u7528\u767D\u540D\u5355\uFF0C\u5207\u6362\u6A21\u5F0F\u65F6\u4F1A\u4FDD\u7559\u3002": "Only Selected contacts uses the allowlist; it is retained when modes change.",
567
+ "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u5C06\u53EA\u63A5\u53D7\u81EA\u804A\u6D88\u606F\u3002": "The allowlist is empty; only self-chat messages will be accepted after saving.",
568
+ "\u7535\u8BDD\u53F7\u7801\u5FC5\u987B\u5305\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002": "Each phone number must include a country or region code on its own line.",
569
+ "WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "WhatsApp access settings are currently unavailable.",
570
+ "WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002": "Could not save WhatsApp access settings.",
551
571
  "Bot API \u957F\u8F6E\u8BE2": "Bot API long polling",
552
572
  " Gateway \u957F\u8FDE\u63A5": " Gateway persistent connection",
553
573
  "Gateway \u957F\u8FDE\u63A5": "Gateway persistent connection",
@@ -904,6 +924,7 @@ function normalizeAgentPresetCatalog(value) {
904
924
  }
905
925
  function AgentPresetEditor({ agentPreset = "", disabled = false, onSave }) {
906
926
  const catalog = React3.useContext(AgentPresetCatalogContext) ?? EMPTY_AGENT_PRESET_CATALOG;
927
+ const helpId = React3.useId();
907
928
  const current = normalizeAgentPresetId(agentPreset);
908
929
  const [saving, setSaving] = React3.useState(false);
909
930
  const [error, setError] = React3.useState(null);
@@ -936,7 +957,26 @@ function AgentPresetEditor({ agentPreset = "", disabled = false, onSave }) {
936
957
  h2(
937
958
  "div",
938
959
  { className: "dim-presetHeader" },
939
- h2("span", null, "Agent Preset"),
960
+ h2(
961
+ "span",
962
+ { className: "dim-presetTitle" },
963
+ h2("span", null, "Agent Preset"),
964
+ h2(
965
+ "span",
966
+ { className: "dim-presetHelp" },
967
+ h2("button", {
968
+ type: "button",
969
+ className: "dim-presetHelpButton",
970
+ "aria-label": "\u67E5\u770B Agent Preset \u8BF4\u660E",
971
+ "aria-describedby": helpId
972
+ }, h2("span", { "aria-hidden": "true" }, "?")),
973
+ h2("span", {
974
+ id: helpId,
975
+ className: "dim-presetTooltip",
976
+ role: "tooltip"
977
+ }, "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002")
978
+ )
979
+ ),
940
980
  saving ? h2("span", { className: "dim-presetStatus" }, "\u4FDD\u5B58\u4E2D\u2026") : null
941
981
  ),
942
982
  React3.createElement(
@@ -957,11 +997,6 @@ function AgentPresetEditor({ agentPreset = "", disabled = false, onSave }) {
957
997
  item.unavailable ? [item.id, "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09"] : item.label && item.label !== item.id ? `${item.label}\uFF08${item.id}\uFF09` : item.id
958
998
  ))
959
999
  ),
960
- h2(
961
- "small",
962
- { className: "dim-presetHelp" },
963
- "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002"
964
- ),
965
1000
  error || currentUnavailable ? h2(
966
1001
  "p",
967
1002
  { className: "dim-presetError", role: error ? "alert" : "status" },
@@ -1171,7 +1206,7 @@ function formatRemaining(milliseconds) {
1171
1206
  }
1172
1207
 
1173
1208
  // plugin-src/client/channels/dingtalk/index.js
1174
- var React8 = __toESM(require("react"), 1);
1209
+ var React9 = __toESM(require("react"), 1);
1175
1210
 
1176
1211
  // plugin-src/client/credential-binding.js
1177
1212
  var React4 = __toESM(require("react"), 1);
@@ -1667,6 +1702,73 @@ function useWorkspaceSnapshotFence() {
1667
1702
  }), []);
1668
1703
  }
1669
1704
 
1705
+ // plugin-src/client/channel-card-meta.js
1706
+ var React8 = __toESM(require("react"), 1);
1707
+ function ChannelListHeading({ className = "", id: id5, title, connectionLabel }) {
1708
+ const helpId = React8.useId();
1709
+ return h2(
1710
+ "div",
1711
+ { className: `${className} dim-listHeading`.trim() },
1712
+ h2(
1713
+ "div",
1714
+ { className: "dim-listTitle" },
1715
+ h2("h3", id5 ? { id: id5 } : null, title),
1716
+ h2(
1717
+ "span",
1718
+ { className: "dim-channelHelp" },
1719
+ h2("button", {
1720
+ type: "button",
1721
+ className: "dim-channelHelpButton",
1722
+ "aria-label": "\u67E5\u770B\u6D88\u606F\u901A\u9053\u8BF4\u660E",
1723
+ "aria-describedby": helpId
1724
+ }, h2("span", { "aria-hidden": "true" }, "?")),
1725
+ h2(
1726
+ "span",
1727
+ {
1728
+ id: helpId,
1729
+ className: "dim-channelTooltip",
1730
+ role: "tooltip"
1731
+ },
1732
+ h2("span", null, "\u6D88\u606F\u901A\u9053"),
1733
+ h2("strong", null, connectionLabel)
1734
+ )
1735
+ )
1736
+ )
1737
+ );
1738
+ }
1739
+ function BotStatusMeta({
1740
+ className = "",
1741
+ dotClassName = "",
1742
+ tone,
1743
+ stateLabel: stateLabel2,
1744
+ lastCheckedAt,
1745
+ formatCheckedTime: formatCheckedTime2,
1746
+ healthState
1747
+ }) {
1748
+ return h2(
1749
+ "div",
1750
+ { className: "dim-botHealthGroup" },
1751
+ h2(
1752
+ "div",
1753
+ {
1754
+ className: `${className} dim-botHealth`.trim(),
1755
+ ...healthState ? { "data-health": healthState } : {}
1756
+ },
1757
+ h2("span", {
1758
+ className: `${dotClassName} dim-healthDot`.trim(),
1759
+ "data-tone": tone
1760
+ }),
1761
+ h2("span", null, stateLabel2)
1762
+ ),
1763
+ h2(
1764
+ "div",
1765
+ { className: "dim-lastChecked" },
1766
+ h2("span", null, "\u6700\u8FD1\u68C0\u67E5"),
1767
+ h2("span", null, formatCheckedTime2(lastCheckedAt))
1768
+ )
1769
+ );
1770
+ }
1771
+
1670
1772
  // plugin-src/client/channels/dingtalk/styles.js
1671
1773
  var DINGTALK_STYLE_ID = "xmanrui-dsh-dingtalk-settings";
1672
1774
  var CSS = String.raw`
@@ -1755,10 +1857,6 @@ var CSS = String.raw`
1755
1857
  .ddt-accountIdentity h3 { overflow: hidden; font-size: 15px; text-overflow: ellipsis; white-space: nowrap; }
1756
1858
  .ddt-accountIdentity p { margin-top: 4px; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; }
1757
1859
  .ddt-health { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; }
1758
- .ddt-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 20px 0; }
1759
- .ddt-metric { min-width: 0; padding: 12px; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
1760
- .ddt-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; }
1761
- .ddt-metric dd { overflow: hidden; margin: 5px 0 0; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
1762
1860
  .ddt-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
1763
1861
  .ddt-accountFooter .ddt-actions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; }
1764
1862
  .ddt-accountFooter .ddt-button { flex: none; white-space: nowrap; }
@@ -1779,8 +1877,6 @@ var CSS = String.raw`
1779
1877
  .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; }
1780
1878
  .ddt-qrColumn { width: 100%; min-width: 0; }
1781
1879
  .ddt-qrCopy { width: 100%; }
1782
- .ddt-metrics { gap: 8px; }
1783
- .ddt-metric { padding: 10px; }
1784
1880
  }
1785
1881
  @media (max-width: 720px) {
1786
1882
  .ddt-heading, .ddt-accountTop { flex-direction: column; align-items: stretch; }
@@ -1825,7 +1921,7 @@ function DingtalkIcon({ size = 28 }) {
1825
1921
  d: "M37.05 22.783c-6.758-5.216-14.378-12.128-22.73-19.538-.655-.585-1.242-.354-1.536.42-1.88 4.973-.058 9.386 2.889 11.932s7.368 4.912 10.058 6.155c.105.049.013.203-.093.163-4.953-2.182-8.397-3.765-13.07-7.368-.497-.388-1.01-.242-1.07.521-.384 4.748 2.657 8.483 6.058 9.745 2.1.781 4.398 1.212 6.53 1.474.109.015.084.178-.027.178-2.747.01-6.058-.654-8.935-1.751-.606-.233-.818.25-.722.633.491 2.008 2.974 5.076 6.926 5.73a12 12 0 0 0 2.228.115c.164 0 .208.089.154.217q-2.685 4.6-2.803 4.797c-.091.152-.036.275.156.275h3.543c.164 0 .264.106.18.246l-4.958 8.196c-.191.328.035.565.395.301s15.212-11.133 15.636-11.448c.195-.142.148-.327-.124-.327h-3.18c-.206 0-.252-.14-.111-.28.14-.141 3.602-3.594 4.837-4.888 1.283-1.35 1.938-3.825-.231-5.498"
1826
1922
  }));
1827
1923
  }
1828
- var Button = React8.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
1924
+ var Button = React9.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
1829
1925
  return h2("button", {
1830
1926
  ...props,
1831
1927
  ref,
@@ -1921,13 +2017,13 @@ function EmptyView({ busy, onStart }) {
1921
2017
  );
1922
2018
  }
1923
2019
  function QrPanel({ provision, now, busy, onRefresh, onCancel }) {
1924
- const [imageFailed, setImageFailed] = React8.useState(false);
2020
+ const [imageFailed, setImageFailed] = React9.useState(false);
1925
2021
  const source = safeQrSource(provision.qrCodeDataUrl);
1926
2022
  const remaining = Math.max(0, provision.expiresAt - now);
1927
2023
  const expired = remaining === 0 || provision.status === "expired";
1928
2024
  const duration = Math.max(1, provision.durationMs ?? 10 * 6e4);
1929
2025
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
1930
- React8.useEffect(() => setImageFailed(false), [source]);
2026
+ React9.useEffect(() => setImageFailed(false), [source]);
1931
2027
  return h2(
1932
2028
  "div",
1933
2029
  { className: "ddt-card dim-surfaceCard" },
@@ -2044,8 +2140,8 @@ function checkedTime(value) {
2044
2140
  }
2045
2141
  }
2046
2142
  function RemoveConfirmation({ account, busy, onConfirm, onCancel }) {
2047
- const cancelRef = React8.useRef(null);
2048
- React8.useEffect(() => cancelRef.current?.focus(), []);
2143
+ const cancelRef = React9.useRef(null);
2144
+ React9.useEffect(() => cancelRef.current?.focus(), []);
2049
2145
  return h2(
2050
2146
  "div",
2051
2147
  {
@@ -2106,28 +2202,14 @@ function AccountCard({
2106
2202
  h2("p", { title: account.bot.clientIdMasked }, account.bot.clientIdMasked)
2107
2203
  )
2108
2204
  ),
2109
- h2(
2110
- "div",
2111
- { className: "ddt-health dim-botHealth" },
2112
- h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
2113
- h2("span", null, stateLabel2)
2114
- )
2115
- ),
2116
- h2(
2117
- "dl",
2118
- { className: "ddt-metrics dim-botMetrics" },
2119
- h2(
2120
- "div",
2121
- { className: "ddt-metric dim-botMetric" },
2122
- h2("dt", null, "\u6D88\u606F\u901A\u9053"),
2123
- h2("dd", null, account.connected ? "Stream \u957F\u8FDE\u63A5" : "\u79BB\u7EBF")
2124
- ),
2125
- h2(
2126
- "div",
2127
- { className: "ddt-metric dim-botMetric" },
2128
- h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"),
2129
- h2("dd", null, checkedTime(account.health.lastCheckedAt))
2130
- )
2205
+ h2(BotStatusMeta, {
2206
+ className: "ddt-health",
2207
+ dotClassName: "ddt-dot",
2208
+ tone,
2209
+ stateLabel: stateLabel2,
2210
+ lastCheckedAt: account.health.lastCheckedAt,
2211
+ formatCheckedTime: checkedTime
2212
+ })
2131
2213
  ),
2132
2214
  h2(WorkspaceEditor, {
2133
2215
  workspace: account.workspace,
@@ -2142,24 +2224,28 @@ function AccountCard({
2142
2224
  h2(
2143
2225
  "div",
2144
2226
  { className: "ddt-accountFooter dim-cardFooter" },
2145
- summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
2146
- feedback ? h2("div", {
2147
- className: "ddt-summary dim-cardSummary",
2148
- role: "status"
2149
- }, feedback) : null,
2150
2227
  h2(
2151
2228
  "div",
2152
- { className: "ddt-actions dim-cardActions" },
2229
+ { className: "dim-cardFooterLayout" },
2153
2230
  h2(
2154
- Button,
2155
- { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) },
2156
- busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"
2231
+ "div",
2232
+ { className: "ddt-actions dim-cardActions" },
2233
+ h2(
2234
+ Button,
2235
+ { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) },
2236
+ busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"
2237
+ ),
2238
+ h2(
2239
+ Button,
2240
+ { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) },
2241
+ "\u79FB\u9664\u63A5\u5165"
2242
+ )
2157
2243
  ),
2158
- h2(
2159
- Button,
2160
- { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) },
2161
- "\u79FB\u9664\u63A5\u5165"
2162
- )
2244
+ summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
2245
+ feedback ? h2("div", {
2246
+ className: "ddt-summary dim-cardFeedback",
2247
+ role: "status"
2248
+ }, feedback) : null
2163
2249
  )
2164
2250
  )
2165
2251
  ),
@@ -2175,11 +2261,11 @@ function AccountList(props) {
2175
2261
  return h2(
2176
2262
  "section",
2177
2263
  { className: "dim-listSection" },
2178
- h2(
2179
- "div",
2180
- { className: "ddt-listHeading dim-listHeading" },
2181
- h2("h3", null, "\u5DF2\u63A5\u5165\u7684\u9489\u9489\u673A\u5668\u4EBA")
2182
- ),
2264
+ h2(ChannelListHeading, {
2265
+ className: "ddt-listHeading",
2266
+ title: "\u5DF2\u63A5\u5165\u7684\u9489\u9489\u673A\u5668\u4EBA",
2267
+ connectionLabel: "Stream \u957F\u8FDE\u63A5"
2268
+ }),
2183
2269
  h2("ul", { className: "ddt-list dim-botList" }, props.bots.map((account) => h2(
2184
2270
  "li",
2185
2271
  { key: account.botId },
@@ -2200,7 +2286,7 @@ function AccountList(props) {
2200
2286
  }
2201
2287
  var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 });
2202
2288
  function DingtalkSettingsTab({ rpcCall }) {
2203
- const [model, setModel] = React8.useState({
2289
+ const [model, setModel] = React9.useState({
2204
2290
  phase: "loading",
2205
2291
  bots: [],
2206
2292
  totals: EMPTY_TOTALS,
@@ -2208,22 +2294,22 @@ function DingtalkSettingsTab({ rpcCall }) {
2208
2294
  error: null,
2209
2295
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
2210
2296
  });
2211
- const [provision, setProvision] = React8.useState(null);
2212
- const [busy, setBusy] = React8.useState(false);
2213
- const [busyByBot, setBusyByBot] = React8.useState({});
2214
- const [feedbackByBot, setFeedbackByBot] = React8.useState({});
2215
- const [removeTarget, setRemoveTarget] = React8.useState(null);
2216
- const [credentialOpen, setCredentialOpen] = React8.useState(false);
2217
- const [credentialError, setCredentialError] = React8.useState(null);
2218
- const [notice, setNotice] = React8.useState("");
2219
- const [now, setNow] = React8.useState(() => Date.now());
2220
- const addButtonRef = React8.useRef(null);
2221
- const mountedRef = React8.useRef(true);
2222
- const statusRequestRef = React8.useRef(0);
2297
+ const [provision, setProvision] = React9.useState(null);
2298
+ const [busy, setBusy] = React9.useState(false);
2299
+ const [busyByBot, setBusyByBot] = React9.useState({});
2300
+ const [feedbackByBot, setFeedbackByBot] = React9.useState({});
2301
+ const [removeTarget, setRemoveTarget] = React9.useState(null);
2302
+ const [credentialOpen, setCredentialOpen] = React9.useState(false);
2303
+ const [credentialError, setCredentialError] = React9.useState(null);
2304
+ const [notice, setNotice] = React9.useState("");
2305
+ const [now, setNow] = React9.useState(() => Date.now());
2306
+ const addButtonRef = React9.useRef(null);
2307
+ const mountedRef = React9.useRef(true);
2308
+ const statusRequestRef = React9.useRef(0);
2223
2309
  const workspaceFence = useWorkspaceSnapshotFence();
2224
- const noticeFrameRef = React8.useRef(null);
2225
- const focusFrameRef = React8.useRef(null);
2226
- React8.useEffect(() => {
2310
+ const noticeFrameRef = React9.useRef(null);
2311
+ const focusFrameRef = React9.useRef(null);
2312
+ React9.useEffect(() => {
2227
2313
  mountedRef.current = true;
2228
2314
  return () => {
2229
2315
  mountedRef.current = false;
@@ -2238,8 +2324,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2238
2324
  }
2239
2325
  };
2240
2326
  }, []);
2241
- React8.useEffect(() => installDingtalkStyles(), []);
2242
- const announce = React8.useCallback((message) => {
2327
+ React9.useEffect(() => installDingtalkStyles(), []);
2328
+ const announce = React9.useCallback((message) => {
2243
2329
  if (!mountedRef.current) return;
2244
2330
  if (noticeFrameRef.current !== null) {
2245
2331
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -2253,7 +2339,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2253
2339
  });
2254
2340
  }
2255
2341
  }, []);
2256
- const discardStaleFeedback = React8.useCallback((snapshot) => {
2342
+ const discardStaleFeedback = React9.useCallback((snapshot) => {
2257
2343
  const botsById = new Map(snapshot.bots.map((bot) => [bot.botId, bot]));
2258
2344
  setFeedbackByBot((current) => {
2259
2345
  let changed = false;
@@ -2268,7 +2354,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2268
2354
  return changed ? next : current;
2269
2355
  });
2270
2356
  }, []);
2271
- const focusAddButton = React8.useCallback(() => {
2357
+ const focusAddButton = React9.useCallback(() => {
2272
2358
  if (!mountedRef.current) return;
2273
2359
  if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current);
2274
2360
  focusFrameRef.current = window.requestAnimationFrame(() => {
@@ -2276,11 +2362,11 @@ function DingtalkSettingsTab({ rpcCall }) {
2276
2362
  if (mountedRef.current) addButtonRef.current?.focus();
2277
2363
  });
2278
2364
  }, []);
2279
- const invoke = React8.useCallback(async (endpoint, payload = {}, signal) => {
2365
+ const invoke = React9.useCallback(async (endpoint, payload = {}, signal) => {
2280
2366
  if (typeof rpcCall !== "function") throw new TypeError("\u9489\u9489\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
2281
2367
  return unwrapRpcResult(await rpcCall(endpoint, payload, signal));
2282
2368
  }, [rpcCall]);
2283
- const loadStatus = React8.useCallback(async ({
2369
+ const loadStatus = React9.useCallback(async ({
2284
2370
  signal,
2285
2371
  silent = false,
2286
2372
  restoreProvisioning = false
@@ -2324,12 +2410,12 @@ function DingtalkSettingsTab({ rpcCall }) {
2324
2410
  return void 0;
2325
2411
  }
2326
2412
  }, [discardStaleFeedback, invoke, workspaceFence]);
2327
- React8.useEffect(() => {
2413
+ React9.useEffect(() => {
2328
2414
  const controller = new AbortController();
2329
2415
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
2330
2416
  return () => controller.abort();
2331
2417
  }, [loadStatus]);
2332
- React8.useEffect(() => {
2418
+ React9.useEffect(() => {
2333
2419
  if (model.phase !== "ready") return void 0;
2334
2420
  const controller = new AbortController();
2335
2421
  let running = false;
@@ -2348,14 +2434,14 @@ function DingtalkSettingsTab({ rpcCall }) {
2348
2434
  window.clearInterval(timer);
2349
2435
  };
2350
2436
  }, [loadStatus, model.phase]);
2351
- React8.useEffect(() => {
2437
+ React9.useEffect(() => {
2352
2438
  if (!provision || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
2353
2439
  const timer = window.setInterval(() => {
2354
2440
  if (mountedRef.current) setNow(Date.now());
2355
2441
  }, 1e3);
2356
2442
  return () => window.clearInterval(timer);
2357
2443
  }, [provision?.attemptId, provision?.status]);
2358
- const startProvisioning = React8.useCallback(async ({ replace = false } = {}) => {
2444
+ const startProvisioning = React9.useCallback(async ({ replace = false } = {}) => {
2359
2445
  if (!mountedRef.current) return;
2360
2446
  setCredentialOpen(false);
2361
2447
  setCredentialError(null);
@@ -2393,7 +2479,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2393
2479
  if (mountedRef.current) setBusy(false);
2394
2480
  }
2395
2481
  }, [announce, invoke, provision?.attemptId]);
2396
- const bindCredentials = React8.useCallback(async ({ identity, secret }) => {
2482
+ const bindCredentials = React9.useCallback(async ({ identity, secret }) => {
2397
2483
  if (!mountedRef.current) return;
2398
2484
  const snapshotVersion = workspaceFence.beginMutation();
2399
2485
  setBusy(true);
@@ -2425,7 +2511,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2425
2511
  if (mountedRef.current) setBusy(false);
2426
2512
  }
2427
2513
  }, [announce, discardStaleFeedback, invoke, loadStatus, workspaceFence]);
2428
- const cancelProvisioning = React8.useCallback(async () => {
2514
+ const cancelProvisioning = React9.useCallback(async () => {
2429
2515
  if (!mountedRef.current) return;
2430
2516
  setBusy(true);
2431
2517
  try {
@@ -2443,7 +2529,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2443
2529
  if (mountedRef.current) setBusy(false);
2444
2530
  }
2445
2531
  }, [announce, focusAddButton, invoke, provision?.attemptId, provision?.status]);
2446
- React8.useEffect(() => {
2532
+ React9.useEffect(() => {
2447
2533
  const attemptId = provision?.attemptId;
2448
2534
  if (!attemptId || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
2449
2535
  const controller = new AbortController();
@@ -2502,7 +2588,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2502
2588
  timer = null;
2503
2589
  };
2504
2590
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
2505
- const setBotBusy = React8.useCallback((botId, operation) => {
2591
+ const setBotBusy = React9.useCallback((botId, operation) => {
2506
2592
  if (!mountedRef.current) return;
2507
2593
  setBusyByBot((current) => {
2508
2594
  const next = { ...current };
@@ -2511,7 +2597,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2511
2597
  return next;
2512
2598
  });
2513
2599
  }, []);
2514
- const runBotAction = React8.useCallback(async ({ account, operation, endpoint, payload, success }) => {
2600
+ const runBotAction = React9.useCallback(async ({ account, operation, endpoint, payload, success }) => {
2515
2601
  if (!mountedRef.current) return void 0;
2516
2602
  const snapshotVersion = workspaceFence.beginMutation();
2517
2603
  setBotBusy(account.botId, operation);
@@ -2565,7 +2651,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2565
2651
  if (mountedRef.current) setBotBusy(account.botId, null);
2566
2652
  }
2567
2653
  }, [announce, discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2568
- const reconnect = React8.useCallback((account) => runBotAction({
2654
+ const reconnect = React9.useCallback((account) => runBotAction({
2569
2655
  account,
2570
2656
  operation: "reconnect",
2571
2657
  endpoint: DINGTALK_ENDPOINTS.reconnectBot,
@@ -2576,7 +2662,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2576
2662
  return connectionTestFeedback(snapshot.testMessage) ?? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
2577
2663
  }
2578
2664
  }), [runBotAction]);
2579
- const saveWorkspace = React8.useCallback(async (account, workspace) => {
2665
+ const saveWorkspace = React9.useCallback(async (account, workspace) => {
2580
2666
  const workspaceVersion = workspaceFence.beginMutation();
2581
2667
  setBotBusy(account.botId, "workspace");
2582
2668
  try {
@@ -2601,7 +2687,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2601
2687
  if (mountedRef.current) setBotBusy(account.botId, null);
2602
2688
  }
2603
2689
  }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2604
- const saveAgentPreset = React8.useCallback(async (account, agentPreset) => {
2690
+ const saveAgentPreset = React9.useCallback(async (account, agentPreset) => {
2605
2691
  const snapshotVersion = workspaceFence.beginMutation();
2606
2692
  setBotBusy(account.botId, "preset");
2607
2693
  try {
@@ -2626,7 +2712,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2626
2712
  if (mountedRef.current) setBotBusy(account.botId, null);
2627
2713
  }
2628
2714
  }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2629
- const remove = React8.useCallback(async (account) => {
2715
+ const remove = React9.useCallback(async (account) => {
2630
2716
  const snapshot = await runBotAction({
2631
2717
  account,
2632
2718
  operation: "delete",
@@ -2710,7 +2796,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2710
2796
  h2(Button, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
2711
2797
  )
2712
2798
  ) : h2(
2713
- React8.Fragment,
2799
+ React9.Fragment,
2714
2800
  null,
2715
2801
  credentialView,
2716
2802
  provisionView,
@@ -2829,8 +2915,8 @@ var normalizeSnapshot2 = api.normalizeSnapshot;
2829
2915
  var presentError2 = api.presentError;
2830
2916
 
2831
2917
  // plugin-src/client/channels/shared/token-channel.js
2832
- var React9 = __toESM(require("react"), 1);
2833
- var Button3 = React9.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
2918
+ var React10 = __toESM(require("react"), 1);
2919
+ var Button3 = React10.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
2834
2920
  return h2("button", {
2835
2921
  ...props,
2836
2922
  ref,
@@ -2912,28 +2998,14 @@ function createTokenChannelSettings(definition) {
2912
2998
  h2("p", null, identity)
2913
2999
  )
2914
3000
  ),
2915
- h2(
2916
- "div",
2917
- { className: "ddt-health dim-botHealth" },
2918
- h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
2919
- h2("span", null, stateLabel2)
2920
- )
2921
- ),
2922
- h2(
2923
- "dl",
2924
- { className: "ddt-metrics dim-botMetrics" },
2925
- h2(
2926
- "div",
2927
- { className: "ddt-metric dim-botMetric" },
2928
- h2("dt", null, "\u6D88\u606F\u901A\u9053"),
2929
- h2("dd", null, account.connected ? connectionLabel : "\u79BB\u7EBF")
2930
- ),
2931
- h2(
2932
- "div",
2933
- { className: "ddt-metric dim-botMetric" },
2934
- h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"),
2935
- h2("dd", null, checkedTime2(account.health.lastCheckedAt))
2936
- )
3001
+ h2(BotStatusMeta, {
3002
+ className: "ddt-health",
3003
+ dotClassName: "ddt-dot",
3004
+ tone,
3005
+ stateLabel: stateLabel2,
3006
+ lastCheckedAt: account.health.lastCheckedAt,
3007
+ formatCheckedTime: checkedTime2
3008
+ })
2937
3009
  ),
2938
3010
  h2(WorkspaceEditor, {
2939
3011
  workspace: account.workspace,
@@ -2953,22 +3025,29 @@ function createTokenChannelSettings(definition) {
2953
3025
  h2(
2954
3026
  "div",
2955
3027
  { className: "ddt-accountFooter dim-cardFooter" },
2956
- summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
2957
- testNotice ? h2("div", { className: "ddt-summary dim-cardSummary", role: "status" }, testNotice) : null,
2958
3028
  h2(
2959
3029
  "div",
2960
- { className: "ddt-actions dim-cardActions" },
2961
- h2(Button3, {
2962
- className: "dim-cardAction",
2963
- onClick: onReconnect,
2964
- disabled: Boolean(busy)
2965
- }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
2966
- h2(Button3, {
2967
- className: "dim-cardAction",
2968
- kind: "danger",
2969
- onClick: onRequestRemove,
2970
- disabled: Boolean(busy)
2971
- }, "\u79FB\u9664\u63A5\u5165")
3030
+ { className: "dim-cardFooterLayout" },
3031
+ h2(
3032
+ "div",
3033
+ { className: "ddt-actions dim-cardActions" },
3034
+ h2(Button3, {
3035
+ className: "dim-cardAction",
3036
+ onClick: onReconnect,
3037
+ disabled: Boolean(busy)
3038
+ }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
3039
+ h2(Button3, {
3040
+ className: "dim-cardAction",
3041
+ kind: "danger",
3042
+ onClick: onRequestRemove,
3043
+ disabled: Boolean(busy)
3044
+ }, "\u79FB\u9664\u63A5\u5165")
3045
+ ),
3046
+ summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
3047
+ testNotice ? h2("div", {
3048
+ className: "ddt-summary dim-cardFeedback",
3049
+ role: "status"
3050
+ }, testNotice) : null
2972
3051
  )
2973
3052
  )
2974
3053
  ),
@@ -2991,22 +3070,22 @@ function createTokenChannelSettings(definition) {
2991
3070
  );
2992
3071
  }
2993
3072
  function SettingsTab({ rpcCall }) {
2994
- const [model, setModel] = React9.useState({
3073
+ const [model, setModel] = React10.useState({
2995
3074
  phase: "loading",
2996
3075
  bots: [],
2997
3076
  totals: { configured: 0, connected: 0 },
2998
3077
  error: null,
2999
3078
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
3000
3079
  });
3001
- const [credentialOpen, setCredentialOpen] = React9.useState(false);
3002
- const [credentialError, setCredentialError] = React9.useState(null);
3003
- const [busy, setBusy] = React9.useState(false);
3004
- const [busyByBot, setBusyByBot] = React9.useState({});
3005
- const [testNoticeByBot, setTestNoticeByBot] = React9.useState({});
3006
- const [removeTarget, setRemoveTarget] = React9.useState(null);
3007
- const mounted = React9.useRef(true);
3080
+ const [credentialOpen, setCredentialOpen] = React10.useState(false);
3081
+ const [credentialError, setCredentialError] = React10.useState(null);
3082
+ const [busy, setBusy] = React10.useState(false);
3083
+ const [busyByBot, setBusyByBot] = React10.useState({});
3084
+ const [testNoticeByBot, setTestNoticeByBot] = React10.useState({});
3085
+ const [removeTarget, setRemoveTarget] = React10.useState(null);
3086
+ const mounted = React10.useRef(true);
3008
3087
  const workspaceFence = useWorkspaceSnapshotFence();
3009
- React9.useEffect(() => {
3088
+ React10.useEffect(() => {
3010
3089
  const disposeDingtalk = installDingtalkStyles();
3011
3090
  const disposeChannel = installStyles();
3012
3091
  mounted.current = true;
@@ -3016,11 +3095,11 @@ function createTokenChannelSettings(definition) {
3016
3095
  disposeDingtalk();
3017
3096
  };
3018
3097
  }, []);
3019
- const invoke = React9.useCallback(async (endpoint, payload = {}, signal) => {
3098
+ const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => {
3020
3099
  if (typeof rpcCall !== "function") throw new TypeError(`${channel4} \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5`);
3021
3100
  return api4.unwrapRpcResult(await rpcCall(endpoint, payload, signal));
3022
3101
  }, [rpcCall]);
3023
- const loadStatus = React9.useCallback(async ({ signal, silent = false } = {}) => {
3102
+ const loadStatus = React10.useCallback(async ({ signal, silent = false } = {}) => {
3024
3103
  const workspaceVersion = workspaceFence.beginStatus();
3025
3104
  if (workspaceVersion === null) return;
3026
3105
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -3044,12 +3123,12 @@ function createTokenChannelSettings(definition) {
3044
3123
  }
3045
3124
  }
3046
3125
  }, [invoke, workspaceFence]);
3047
- React9.useEffect(() => {
3126
+ React10.useEffect(() => {
3048
3127
  const controller = new AbortController();
3049
3128
  void loadStatus({ signal: controller.signal });
3050
3129
  return () => controller.abort();
3051
3130
  }, [loadStatus]);
3052
- React9.useEffect(() => {
3131
+ React10.useEffect(() => {
3053
3132
  if (model.phase !== "ready") return void 0;
3054
3133
  const controller = new AbortController();
3055
3134
  const timer = window.setInterval(
@@ -3061,7 +3140,7 @@ function createTokenChannelSettings(definition) {
3061
3140
  window.clearInterval(timer);
3062
3141
  };
3063
3142
  }, [loadStatus, model.phase]);
3064
- const bindCredentials = React9.useCallback(async (values) => {
3143
+ const bindCredentials = React10.useCallback(async (values) => {
3065
3144
  const snapshotVersion = workspaceFence.beginMutation();
3066
3145
  setBusy(true);
3067
3146
  setCredentialError(null);
@@ -3089,7 +3168,7 @@ function createTokenChannelSettings(definition) {
3089
3168
  if (mounted.current) setBusy(false);
3090
3169
  }
3091
3170
  }, [invoke, loadStatus, workspaceFence]);
3092
- const botAction = React9.useCallback(async (account, operation, endpoint, payload) => {
3171
+ const botAction = React10.useCallback(async (account, operation, endpoint, payload) => {
3093
3172
  const snapshotVersion = workspaceFence.beginMutation();
3094
3173
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
3095
3174
  try {
@@ -3131,11 +3210,11 @@ function createTokenChannelSettings(definition) {
3131
3210
  const botList = model.bots.length > 0 ? h2(
3132
3211
  "section",
3133
3212
  { className: "dim-listSection" },
3134
- h2(
3135
- "div",
3136
- { className: "ddt-listHeading dim-listHeading" },
3137
- h2("h3", null, `\u5DF2\u63A5\u5165\u7684 ${channel4} \u673A\u5668\u4EBA`)
3138
- ),
3213
+ h2(ChannelListHeading, {
3214
+ className: "ddt-listHeading",
3215
+ title: `\u5DF2\u63A5\u5165\u7684 ${channel4} \u673A\u5668\u4EBA`,
3216
+ connectionLabel
3217
+ }),
3139
3218
  h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard5, {
3140
3219
  account,
3141
3220
  busy: busyByBot[account.botId],
@@ -3226,7 +3305,7 @@ function createTokenChannelSettings(definition) {
3226
3305
  h2(Button3, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
3227
3306
  )
3228
3307
  ) : h2(
3229
- React9.Fragment,
3308
+ React10.Fragment,
3230
3309
  null,
3231
3310
  credentialOpen ? CredentialPanel ? h2(CredentialPanel, {
3232
3311
  busy,
@@ -3327,7 +3406,7 @@ var DiscordSettingsTab = channel.SettingsTab;
3327
3406
  var DiscordAccountCard = channel.AccountCard;
3328
3407
 
3329
3408
  // plugin-src/client/channels/feishu/index.js
3330
- var React11 = __toESM(require("react"), 1);
3409
+ var React12 = __toESM(require("react"), 1);
3331
3410
 
3332
3411
  // plugin-src/client/channels/feishu/api.js
3333
3412
  var FEISHU_RPC_CHANNEL = "/feishu";
@@ -3585,7 +3664,7 @@ function formatRemaining2(milliseconds) {
3585
3664
  }
3586
3665
 
3587
3666
  // plugin-src/client/lifecycle.js
3588
- var React10 = __toESM(require("react"), 1);
3667
+ var React11 = __toESM(require("react"), 1);
3589
3668
  function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) {
3590
3669
  let disposed = false;
3591
3670
  let timer;
@@ -3647,8 +3726,8 @@ function createAnimationFrameScheduler({ requestFrame, cancelFrame }) {
3647
3726
  };
3648
3727
  }
3649
3728
  function useAnimationFrameScheduler() {
3650
- const schedulerRef = React10.useRef(null);
3651
- React10.useEffect(() => {
3729
+ const schedulerRef = React11.useRef(null);
3730
+ React11.useEffect(() => {
3652
3731
  const scheduler = createAnimationFrameScheduler({
3653
3732
  requestFrame: (callback) => window.requestAnimationFrame(callback),
3654
3733
  cancelFrame: (frame) => window.cancelAnimationFrame(frame)
@@ -3659,7 +3738,7 @@ function useAnimationFrameScheduler() {
3659
3738
  if (schedulerRef.current === scheduler) schedulerRef.current = null;
3660
3739
  };
3661
3740
  }, []);
3662
- return React10.useCallback(
3741
+ return React11.useCallback(
3663
3742
  (callback, key) => schedulerRef.current?.schedule(callback, key) ?? false,
3664
3743
  []
3665
3744
  );
@@ -4069,10 +4148,6 @@ var CSS3 = String.raw`
4069
4148
  .bxf-healthPill[data-health="degraded"], .bxf-healthPill[data-health="checking"], .bxf-healthPill[data-health="connecting"] { color: var(--bxf-warning); background: color-mix(in srgb, var(--bxf-warning) 10%, transparent); }
4070
4149
  .bxf-healthPill[data-health="offline"], .bxf-healthPill[data-health="error"] { color: var(--bxf-error); background: color-mix(in srgb, var(--bxf-error) 10%, transparent); }
4071
4150
 
4072
- .bxf-statusGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 22px; }
4073
- .bxf-metric { min-width: 0; border: 1px solid var(--dsw-alias-border-l2, #dee0e3); border-radius: 9px; padding: 12px 13px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
4074
- .bxf-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
4075
- .bxf-metric dd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 550; margin: 3px 0 0; }
4076
4151
 
4077
4152
  .bxf-responseMode { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; column-gap: 10px; row-gap: 5px; margin-top: 6px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
4078
4153
  .bxf-responseModeHeader { display: contents; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
@@ -4184,7 +4259,6 @@ var CSS3 = String.raw`
4184
4259
  .bxf-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; }
4185
4260
  .bxf-botProvision .bxf-qrLayout { grid-template-columns: minmax(0, 1fr); }
4186
4261
  .bxf-qrCopy { width: 100%; }
4187
- .bxf-statusGrid { grid-template-columns: minmax(0, 1fr); }
4188
4262
  .bxf-connectedTop { align-items: flex-start; flex-direction: column; }
4189
4263
  .bxf-inlineError { grid-template-columns: minmax(0, 1fr); padding: 20px; }
4190
4264
  .bxf-statusNotice { align-items: flex-start; flex-wrap: wrap; }
@@ -4286,7 +4360,7 @@ function QrIcon({ size = 58 }) {
4286
4360
  fill: "currentColor"
4287
4361
  }));
4288
4362
  }
4289
- var Button5 = React11.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
4363
+ var Button5 = React12.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
4290
4364
  return h2("button", {
4291
4365
  ...props,
4292
4366
  ref,
@@ -4403,7 +4477,7 @@ function safeQrSource2(value) {
4403
4477
  return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
4404
4478
  }
4405
4479
  function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4406
- const [imageFailed, setImageFailed] = React11.useState(false);
4480
+ const [imageFailed, setImageFailed] = React12.useState(false);
4407
4481
  const qrSource = safeQrSource2(provision.qrCodeDataUrl);
4408
4482
  const href = safeVerificationHref(provision.verificationUrl);
4409
4483
  const remaining = Math.max(0, provision.expiresAt - now);
@@ -4412,7 +4486,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4412
4486
  const repairing = isCallbackRepair(provision);
4413
4487
  const grantingGroupMessages = isGroupMessagePermission(provision);
4414
4488
  const botName = provision.botName ?? "\u6B64\u673A\u5668\u4EBA";
4415
- React11.useEffect(() => setImageFailed(false), [qrSource]);
4489
+ React12.useEffect(() => setImageFailed(false), [qrSource]);
4416
4490
  return h2(
4417
4491
  "div",
4418
4492
  { className: "bxf-card bxf-provisionCard dim-surfaceCard" },
@@ -4575,11 +4649,11 @@ function connectionTestNotice2(value) {
4575
4649
  return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
4576
4650
  }
4577
4651
  function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
4578
- const cancelRef = React11.useRef(null);
4652
+ const cancelRef = React12.useRef(null);
4579
4653
  const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-");
4580
4654
  const titleId = `bxf-remove-title-${idPart}`;
4581
4655
  const descriptionId = `bxf-remove-description-${idPart}`;
4582
- React11.useEffect(() => cancelRef.current?.focus(), []);
4656
+ React12.useEffect(() => cancelRef.current?.focus(), []);
4583
4657
  return h2(
4584
4658
  "div",
4585
4659
  {
@@ -4621,9 +4695,9 @@ function GroupResponseModeEditor({
4621
4695
  onAuthorize
4622
4696
  }) {
4623
4697
  const current = normalizeGroupResponseMode(value);
4624
- const [saving, setSaving] = React11.useState(false);
4625
- const [authorizing, setAuthorizing] = React11.useState(false);
4626
- const [error, setError] = React11.useState(null);
4698
+ const [saving, setSaving] = React12.useState(false);
4699
+ const [authorizing, setAuthorizing] = React12.useState(false);
4700
+ const [error, setError] = React12.useState(null);
4627
4701
  const change = async (event) => {
4628
4702
  const next = normalizeGroupResponseMode(event.target.value);
4629
4703
  if (next === current || saving || disabled) return;
@@ -4757,28 +4831,15 @@ function BotCard({
4757
4831
  h2("p", { title: bot.appIdMasked }, bot.appIdMasked ?? "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58")
4758
4832
  )
4759
4833
  ),
4760
- h2(
4761
- "div",
4762
- { className: "bxf-healthPill dim-botHealth", "data-health": stateForDisplay },
4763
- h2("span", { className: "bxf-dot dim-healthDot", "data-tone": tone }),
4764
- h2("span", null, HEALTH_LABELS[stateForDisplay] ?? "\u72B6\u6001\u672A\u77E5")
4765
- )
4766
- ),
4767
- h2(
4768
- "dl",
4769
- { className: "bxf-statusGrid dim-botMetrics" },
4770
- h2(
4771
- "div",
4772
- { className: "bxf-metric dim-botMetric" },
4773
- h2("dt", null, "\u6D88\u606F\u901A\u9053"),
4774
- h2("dd", null, connected ? "\u957F\u8FDE\u63A5" : stateForDisplay === "connecting" ? "\u8FDE\u63A5\u4E2D" : "\u5DF2\u65AD\u5F00")
4775
- ),
4776
- h2(
4777
- "div",
4778
- { className: "bxf-metric dim-botMetric" },
4779
- h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"),
4780
- h2("dd", null, formatCheckedTime(health.lastCheckedAt))
4781
- )
4834
+ h2(BotStatusMeta, {
4835
+ className: "bxf-healthPill",
4836
+ dotClassName: "bxf-dot",
4837
+ tone,
4838
+ stateLabel: HEALTH_LABELS[stateForDisplay] ?? "\u72B6\u6001\u672A\u77E5",
4839
+ lastCheckedAt: health.lastCheckedAt,
4840
+ formatCheckedTime,
4841
+ healthState: stateForDisplay
4842
+ })
4782
4843
  ),
4783
4844
  h2(WorkspaceEditor, {
4784
4845
  workspace: connection.workspace,
@@ -4808,40 +4869,44 @@ function BotCard({
4808
4869
  h2(
4809
4870
  "div",
4810
4871
  { className: "bxf-connectedFooter dim-cardFooter" },
4811
- summary ? h2(
4812
- "div",
4813
- { className: "bxf-healthSummary dim-cardSummary", "data-error": actionError || connection.error ? "true" : void 0 },
4814
- summary
4815
- ) : null,
4816
- testNotice ? h2("div", {
4817
- className: "bxf-healthSummary dim-cardSummary",
4818
- role: "status"
4819
- }, testNotice) : null,
4820
4872
  h2(
4821
4873
  "div",
4822
- { className: "bxf-actions bxf-botActions dim-cardActions" },
4823
- h2(Button5, {
4824
- className: "dim-cardAction",
4825
- onClick: onReconnect,
4826
- disabled: Boolean(busy),
4827
- "aria-busy": busy === "reconnect" ? "true" : void 0,
4828
- "aria-label": `${connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"}${bot.name}`
4829
- }, busy === "reconnect" ? connected ? "\u68C0\u67E5\u4E2D\u2026" : "\u6B63\u5728\u8FDE\u63A5\u2026" : connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
4830
- h2(Button5, {
4831
- className: "bxf-repairButton dim-cardAction",
4832
- onClick: onRepairCallback,
4833
- disabled: Boolean(busy) || repairDisabled,
4834
- "aria-busy": busy === "callback-repair" ? "true" : void 0,
4835
- "aria-label": `\u4FEE\u590D${bot.name}\u7684\u5361\u7247\u6309\u94AE`
4836
- }, busy === "callback-repair" ? "\u7B49\u5F85\u626B\u7801\u2026" : "\u4FEE\u590D\u5361\u7247\u6309\u94AE"),
4837
- h2(Button5, {
4838
- className: "dim-cardAction",
4839
- kind: "danger",
4840
- onClick: onRequestRemove,
4841
- disabled: Boolean(busy),
4842
- ref: removeButtonRef,
4843
- "aria-label": `\u4ECE DeepSeek Harness \u79FB\u9664${bot.name}`
4844
- }, "\u79FB\u9664\u63A5\u5165")
4874
+ { className: "dim-cardFooterLayout" },
4875
+ h2(
4876
+ "div",
4877
+ { className: "bxf-actions bxf-botActions dim-cardActions" },
4878
+ h2(Button5, {
4879
+ className: "dim-cardAction",
4880
+ onClick: onReconnect,
4881
+ disabled: Boolean(busy),
4882
+ "aria-busy": busy === "reconnect" ? "true" : void 0,
4883
+ "aria-label": `${connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"}${bot.name}`
4884
+ }, busy === "reconnect" ? connected ? "\u68C0\u67E5\u4E2D\u2026" : "\u6B63\u5728\u8FDE\u63A5\u2026" : connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
4885
+ h2(Button5, {
4886
+ className: "bxf-repairButton dim-cardAction",
4887
+ onClick: onRepairCallback,
4888
+ disabled: Boolean(busy) || repairDisabled,
4889
+ "aria-busy": busy === "callback-repair" ? "true" : void 0,
4890
+ "aria-label": `\u4FEE\u590D${bot.name}\u7684\u5361\u7247\u6309\u94AE`
4891
+ }, busy === "callback-repair" ? "\u7B49\u5F85\u626B\u7801\u2026" : "\u4FEE\u590D\u5361\u7247\u6309\u94AE"),
4892
+ h2(Button5, {
4893
+ className: "dim-cardAction",
4894
+ kind: "danger",
4895
+ onClick: onRequestRemove,
4896
+ disabled: Boolean(busy),
4897
+ ref: removeButtonRef,
4898
+ "aria-label": `\u4ECE DeepSeek Harness \u79FB\u9664${bot.name}`
4899
+ }, "\u79FB\u9664\u63A5\u5165")
4900
+ ),
4901
+ summary ? h2(
4902
+ "div",
4903
+ { className: "bxf-healthSummary dim-cardSummary", "data-error": actionError || connection.error ? "true" : void 0 },
4904
+ summary
4905
+ ) : null,
4906
+ testNotice ? h2("div", {
4907
+ className: "bxf-healthSummary dim-cardFeedback",
4908
+ role: "status"
4909
+ }, testNotice) : null
4845
4910
  )
4846
4911
  )
4847
4912
  ),
@@ -4857,11 +4922,12 @@ function BotList(props) {
4857
4922
  return h2(
4858
4923
  "section",
4859
4924
  { className: "bxf-listSection dim-listSection", "aria-labelledby": "bxf-bot-list-title" },
4860
- h2(
4861
- "div",
4862
- { className: "bxf-listHeading dim-listHeading" },
4863
- h2("h3", { id: "bxf-bot-list-title" }, "\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA")
4864
- ),
4925
+ h2(ChannelListHeading, {
4926
+ className: "bxf-listHeading",
4927
+ id: "bxf-bot-list-title",
4928
+ title: "\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA",
4929
+ connectionLabel: "\u957F\u8FDE\u63A5"
4930
+ }),
4865
4931
  h2(
4866
4932
  "ul",
4867
4933
  { className: "bxf-botList dim-botList", role: "list" },
@@ -4945,7 +5011,7 @@ function mergeFeishuSnapshotState(current, snapshot, { restoreProvisioning = fal
4945
5011
  };
4946
5012
  }
4947
5013
  function FeishuSettingsTab({ rpcCall }) {
4948
- const [model, setModel] = React11.useState({
5014
+ const [model, setModel] = React12.useState({
4949
5015
  phase: "loading",
4950
5016
  revision: 0,
4951
5017
  bots: [],
@@ -4955,41 +5021,41 @@ function FeishuSettingsTab({ rpcCall }) {
4955
5021
  statusError: null,
4956
5022
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
4957
5023
  });
4958
- const [pageBusy, setPageBusy] = React11.useState(false);
4959
- const [provisionBusy, setProvisionBusy] = React11.useState(false);
4960
- const [credentialOpen, setCredentialOpen] = React11.useState(false);
4961
- const [credentialBusy, setCredentialBusy] = React11.useState(false);
4962
- const [credentialError, setCredentialError] = React11.useState(null);
4963
- const [busyByBot, setBusyByBot] = React11.useState({});
4964
- const [errorsByBot, setErrorsByBot] = React11.useState({});
4965
- const [testNoticesByBot, setTestNoticesByBot] = React11.useState({});
4966
- const [removeTargetId, setRemoveTargetId] = React11.useState(null);
4967
- const [announcement, setAnnouncement] = React11.useState("");
4968
- const [now, setNow] = React11.useState(() => Date.now());
4969
- const [focusBotId, setFocusBotId] = React11.useState(null);
4970
- const cardRefs = React11.useRef(/* @__PURE__ */ new Map());
4971
- const removeButtonRefs = React11.useRef(/* @__PURE__ */ new Map());
4972
- const targetedProvisionRef = React11.useRef(null);
4973
- const addButtonRef = React11.useRef(null);
4974
- const mountedRef = React11.useRef(true);
5024
+ const [pageBusy, setPageBusy] = React12.useState(false);
5025
+ const [provisionBusy, setProvisionBusy] = React12.useState(false);
5026
+ const [credentialOpen, setCredentialOpen] = React12.useState(false);
5027
+ const [credentialBusy, setCredentialBusy] = React12.useState(false);
5028
+ const [credentialError, setCredentialError] = React12.useState(null);
5029
+ const [busyByBot, setBusyByBot] = React12.useState({});
5030
+ const [errorsByBot, setErrorsByBot] = React12.useState({});
5031
+ const [testNoticesByBot, setTestNoticesByBot] = React12.useState({});
5032
+ const [removeTargetId, setRemoveTargetId] = React12.useState(null);
5033
+ const [announcement, setAnnouncement] = React12.useState("");
5034
+ const [now, setNow] = React12.useState(() => Date.now());
5035
+ const [focusBotId, setFocusBotId] = React12.useState(null);
5036
+ const cardRefs = React12.useRef(/* @__PURE__ */ new Map());
5037
+ const removeButtonRefs = React12.useRef(/* @__PURE__ */ new Map());
5038
+ const targetedProvisionRef = React12.useRef(null);
5039
+ const addButtonRef = React12.useRef(null);
5040
+ const mountedRef = React12.useRef(true);
4975
5041
  const workspaceFence = useWorkspaceSnapshotFence();
4976
5042
  const scheduleAnimationFrame = useAnimationFrameScheduler();
4977
- React11.useEffect(() => {
5043
+ React12.useEffect(() => {
4978
5044
  mountedRef.current = true;
4979
5045
  return () => {
4980
5046
  mountedRef.current = false;
4981
5047
  };
4982
5048
  }, []);
4983
- const announce = React11.useCallback((message) => {
5049
+ const announce = React12.useCallback((message) => {
4984
5050
  setAnnouncement("");
4985
5051
  scheduleAnimationFrame(() => {
4986
5052
  if (message) setAnnouncement(message);
4987
5053
  }, "announcement");
4988
5054
  }, [scheduleAnimationFrame]);
4989
- const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => {
5055
+ const invoke = React12.useCallback(async (endpoint, payload = {}, signal) => {
4990
5056
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
4991
5057
  }, [rpcCall]);
4992
- const mergeSnapshot = React11.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
5058
+ const mergeSnapshot = React12.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
4993
5059
  const now2 = Date.now();
4994
5060
  setModel((current) => mergeFeishuSnapshotState(
4995
5061
  current,
@@ -4997,7 +5063,7 @@ function FeishuSettingsTab({ rpcCall }) {
4997
5063
  { restoreProvisioning, now: now2 }
4998
5064
  ));
4999
5065
  }, []);
5000
- const loadStatus = React11.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
5066
+ const loadStatus = React12.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
5001
5067
  const workspaceVersion = workspaceFence.beginStatus();
5002
5068
  if (workspaceVersion === null || !mountedRef.current) return void 0;
5003
5069
  if (!silent) setPageBusy(true);
@@ -5015,12 +5081,12 @@ function FeishuSettingsTab({ rpcCall }) {
5015
5081
  if (!silent && !signal?.aborted && mountedRef.current) setPageBusy(false);
5016
5082
  }
5017
5083
  }, [invoke, mergeSnapshot, workspaceFence]);
5018
- React11.useEffect(() => {
5084
+ React12.useEffect(() => {
5019
5085
  const controller = new AbortController();
5020
5086
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
5021
5087
  return () => controller.abort();
5022
5088
  }, [loadStatus]);
5023
- React11.useEffect(() => {
5089
+ React12.useEffect(() => {
5024
5090
  if (model.phase !== "ready") return void 0;
5025
5091
  const controller = new AbortController();
5026
5092
  let inFlight = false;
@@ -5039,7 +5105,7 @@ function FeishuSettingsTab({ rpcCall }) {
5039
5105
  window.clearInterval(timer);
5040
5106
  };
5041
5107
  }, [loadStatus, model.phase]);
5042
- React11.useEffect(() => {
5108
+ React12.useEffect(() => {
5043
5109
  if (!focusBotId) return;
5044
5110
  const node = cardRefs.current.get(focusBotId);
5045
5111
  if (!node) return;
@@ -5048,7 +5114,7 @@ function FeishuSettingsTab({ rpcCall }) {
5048
5114
  setFocusBotId(null);
5049
5115
  }, [focusBotId, model.bots]);
5050
5116
  const targetedProvisionFocusKey = isTargetedAppUpdate2(model.provisioning) ? `${model.provisioning.botId}:${model.provisioning.attemptId ?? "preparing"}:${model.provisioning.phase}` : null;
5051
- React11.useEffect(() => {
5117
+ React12.useEffect(() => {
5052
5118
  if (!targetedProvisionFocusKey) return;
5053
5119
  scheduleAnimationFrame(() => {
5054
5120
  const node = targetedProvisionRef.current;
@@ -5057,7 +5123,7 @@ function FeishuSettingsTab({ rpcCall }) {
5057
5123
  node.focus?.({ preventScroll: true });
5058
5124
  }, "targeted-provision-focus");
5059
5125
  }, [scheduleAnimationFrame, targetedProvisionFocusKey]);
5060
- const startProvisioning = React11.useCallback(async ({
5126
+ const startProvisioning = React12.useCallback(async ({
5061
5127
  replace = false,
5062
5128
  operation = FEISHU_REGISTRATION_OPERATIONS.PROVISION,
5063
5129
  bot
@@ -5133,7 +5199,7 @@ function FeishuSettingsTab({ rpcCall }) {
5133
5199
  model.provisioning?.botId,
5134
5200
  model.provisioning?.botName
5135
5201
  ]);
5136
- const bindCredentials = React11.useCallback(async ({ identity, secret }) => {
5202
+ const bindCredentials = React12.useCallback(async ({ identity, secret }) => {
5137
5203
  const snapshotVersion = workspaceFence.beginMutation();
5138
5204
  setCredentialBusy(true);
5139
5205
  setCredentialError(null);
@@ -5155,7 +5221,7 @@ function FeishuSettingsTab({ rpcCall }) {
5155
5221
  setCredentialBusy(false);
5156
5222
  }
5157
5223
  }, [announce, invoke, loadStatus, mergeSnapshot, workspaceFence]);
5158
- const cancelProvisioning = React11.useCallback(async () => {
5224
+ const cancelProvisioning = React12.useCallback(async () => {
5159
5225
  const activeProvision = model.provisioning;
5160
5226
  const attemptId = activeProvision?.attemptId;
5161
5227
  const repairing = isCallbackRepair(activeProvision);
@@ -5220,7 +5286,7 @@ function FeishuSettingsTab({ rpcCall }) {
5220
5286
  const countdownPhase = model.provisioning?.phase;
5221
5287
  const countdownExpiresAt = model.provisioning?.expiresAt;
5222
5288
  const countdownExpired = model.provisioning?.expired;
5223
- React11.useEffect(() => {
5289
+ React12.useEffect(() => {
5224
5290
  if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0;
5225
5291
  const tick = () => {
5226
5292
  const timestamp7 = Date.now();
@@ -5233,7 +5299,7 @@ function FeishuSettingsTab({ rpcCall }) {
5233
5299
  const timer = window.setInterval(tick, 1e3);
5234
5300
  return () => window.clearInterval(timer);
5235
5301
  }, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]);
5236
- React11.useEffect(() => {
5302
+ React12.useEffect(() => {
5237
5303
  const provision2 = model.provisioning;
5238
5304
  if (!provision2 || !["qr", "connecting"].includes(provision2.phase) || !provision2.attemptId || provision2.expired) return void 0;
5239
5305
  const controller = new AbortController();
@@ -5301,7 +5367,7 @@ function FeishuSettingsTab({ rpcCall }) {
5301
5367
  window.clearTimeout(timer);
5302
5368
  };
5303
5369
  }, [announce, invoke, loadStatus, model.provisioning]);
5304
- const setBotBusy = React11.useCallback((botId, value) => {
5370
+ const setBotBusy = React12.useCallback((botId, value) => {
5305
5371
  setBusyByBot((current) => {
5306
5372
  const next = { ...current };
5307
5373
  if (value) next[botId] = value;
@@ -5309,7 +5375,7 @@ function FeishuSettingsTab({ rpcCall }) {
5309
5375
  return next;
5310
5376
  });
5311
5377
  }, []);
5312
- const setBotError = React11.useCallback((botId, error) => {
5378
+ const setBotError = React12.useCallback((botId, error) => {
5313
5379
  setErrorsByBot((current) => {
5314
5380
  const next = { ...current };
5315
5381
  if (error) next[botId] = presentError3(error);
@@ -5317,7 +5383,7 @@ function FeishuSettingsTab({ rpcCall }) {
5317
5383
  return next;
5318
5384
  });
5319
5385
  }, []);
5320
- const repairCallback = React11.useCallback((connection) => {
5386
+ const repairCallback = React12.useCallback((connection) => {
5321
5387
  if (model.provisioning) return;
5322
5388
  setRemoveTargetId(null);
5323
5389
  setBotError(connection.botId, null);
@@ -5331,7 +5397,7 @@ function FeishuSettingsTab({ rpcCall }) {
5331
5397
  bot: connection
5332
5398
  });
5333
5399
  }, [model.provisioning, setBotError, startProvisioning]);
5334
- const reconnectOneBot = React11.useCallback(async (connection) => {
5400
+ const reconnectOneBot = React12.useCallback(async (connection) => {
5335
5401
  const { botId, bot } = connection;
5336
5402
  const snapshotVersion = workspaceFence.beginMutation();
5337
5403
  setBotBusy(botId, "reconnect");
@@ -5371,7 +5437,7 @@ function FeishuSettingsTab({ rpcCall }) {
5371
5437
  setBotBusy(botId, null);
5372
5438
  }
5373
5439
  }, [announce, invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
5374
- const saveWorkspace = React11.useCallback(async (connection, workspace) => {
5440
+ const saveWorkspace = React12.useCallback(async (connection, workspace) => {
5375
5441
  const { botId } = connection;
5376
5442
  const workspaceVersion = workspaceFence.beginMutation();
5377
5443
  setBotBusy(botId, "workspace");
@@ -5390,7 +5456,7 @@ function FeishuSettingsTab({ rpcCall }) {
5390
5456
  if (mountedRef.current) setBotBusy(botId, null);
5391
5457
  }
5392
5458
  }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
5393
- const saveAgentPreset = React11.useCallback(async (connection, agentPreset) => {
5459
+ const saveAgentPreset = React12.useCallback(async (connection, agentPreset) => {
5394
5460
  const { botId } = connection;
5395
5461
  const snapshotVersion = workspaceFence.beginMutation();
5396
5462
  setBotBusy(botId, "preset");
@@ -5409,7 +5475,7 @@ function FeishuSettingsTab({ rpcCall }) {
5409
5475
  if (mountedRef.current) setBotBusy(botId, null);
5410
5476
  }
5411
5477
  }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
5412
- const authorizeGroupMessages = React11.useCallback(async (connection) => {
5478
+ const authorizeGroupMessages = React12.useCallback(async (connection) => {
5413
5479
  const { botId } = connection;
5414
5480
  if (model.provisioning) {
5415
5481
  throw new Error("\u8BF7\u5148\u5B8C\u6210\u5F53\u524D\u98DE\u4E66\u6388\u6743\u64CD\u4F5C\uFF0C\u518D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\u3002");
@@ -5426,7 +5492,7 @@ function FeishuSettingsTab({ rpcCall }) {
5426
5492
  bot: connection
5427
5493
  });
5428
5494
  }, [model.provisioning, setBotError, startProvisioning]);
5429
- const saveGroupResponseMode = React11.useCallback(async (connection, groupResponseMode) => {
5495
+ const saveGroupResponseMode = React12.useCallback(async (connection, groupResponseMode) => {
5430
5496
  const { botId } = connection;
5431
5497
  if (groupResponseMode === "all" && connection.groupMessagePermissionGranted !== true) {
5432
5498
  await authorizeGroupMessages(connection);
@@ -5457,15 +5523,15 @@ function FeishuSettingsTab({ rpcCall }) {
5457
5523
  setBotError,
5458
5524
  workspaceFence
5459
5525
  ]);
5460
- const requestRemove = React11.useCallback((connection) => {
5526
+ const requestRemove = React12.useCallback((connection) => {
5461
5527
  setRemoveTargetId(connection.botId);
5462
5528
  }, []);
5463
- const cancelRemove = React11.useCallback(() => {
5529
+ const cancelRemove = React12.useCallback(() => {
5464
5530
  const botId = removeTargetId;
5465
5531
  setRemoveTargetId(null);
5466
5532
  scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus");
5467
5533
  }, [removeTargetId, scheduleAnimationFrame]);
5468
- const confirmRemove = React11.useCallback(async (connection) => {
5534
+ const confirmRemove = React12.useCallback(async (connection) => {
5469
5535
  const { botId, bot } = connection;
5470
5536
  const snapshotVersion = workspaceFence.beginMutation();
5471
5537
  setBotBusy(botId, "delete");
@@ -5551,11 +5617,11 @@ function FeishuSettingsTab({ rpcCall }) {
5551
5617
  setCredentialError(null);
5552
5618
  }
5553
5619
  }) : null;
5554
- const setCardRef = React11.useCallback((botId, node) => {
5620
+ const setCardRef = React12.useCallback((botId, node) => {
5555
5621
  if (node) cardRefs.current.set(botId, node);
5556
5622
  else cardRefs.current.delete(botId);
5557
5623
  }, []);
5558
- const setRemoveButtonRef = React11.useCallback((botId, node) => {
5624
+ const setRemoveButtonRef = React12.useCallback((botId, node) => {
5559
5625
  if (node) removeButtonRefs.current.set(botId, node);
5560
5626
  else removeButtonRefs.current.delete(botId);
5561
5627
  }, []);
@@ -5594,7 +5660,7 @@ function FeishuSettingsTab({ rpcCall }) {
5594
5660
  onRetry: () => void loadStatus(),
5595
5661
  busy: pageBusy
5596
5662
  }) : h2(
5597
- React11.Fragment,
5663
+ React12.Fragment,
5598
5664
  null,
5599
5665
  credentialContent,
5600
5666
  targetedProvisioning ? null : provisionContent,
@@ -5756,7 +5822,7 @@ function formatRemaining3(milliseconds) {
5756
5822
  }
5757
5823
 
5758
5824
  // plugin-src/client/channels/qq/index.js
5759
- var React12 = __toESM(require("react"), 1);
5825
+ var React13 = __toESM(require("react"), 1);
5760
5826
 
5761
5827
  // plugin-src/client/channels/qq/styles.js
5762
5828
  var QQ_STYLE_ID = "xmanrui-dsh-im-qq-settings";
@@ -5781,7 +5847,7 @@ function installQqStyles() {
5781
5847
 
5782
5848
  // plugin-src/client/channels/qq/index.js
5783
5849
  var ACTIVE_STATES = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
5784
- var Button7 = React12.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
5850
+ var Button7 = React13.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
5785
5851
  return h2("button", {
5786
5852
  ...props,
5787
5853
  ref,
@@ -6023,18 +6089,14 @@ function AccountCard2({
6023
6089
  h2("p", null, account.bot.appIdMasked)
6024
6090
  )
6025
6091
  ),
6026
- h2(
6027
- "div",
6028
- { className: "ddt-health dim-botHealth" },
6029
- h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
6030
- h2("span", null, stateLabel2)
6031
- )
6032
- ),
6033
- h2(
6034
- "dl",
6035
- { className: "ddt-metrics dim-botMetrics" },
6036
- h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6D88\u606F\u901A\u9053"), h2("dd", null, account.connected ? "WebSocket \u957F\u8FDE\u63A5" : "\u79BB\u7EBF")),
6037
- h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h2("dd", null, checkedTime3(account.health.lastCheckedAt)))
6092
+ h2(BotStatusMeta, {
6093
+ className: "ddt-health",
6094
+ dotClassName: "ddt-dot",
6095
+ tone,
6096
+ stateLabel: stateLabel2,
6097
+ lastCheckedAt: account.health.lastCheckedAt,
6098
+ formatCheckedTime: checkedTime3
6099
+ })
6038
6100
  ),
6039
6101
  h2(WorkspaceEditor, {
6040
6102
  workspace: account.workspace,
@@ -6049,17 +6111,21 @@ function AccountCard2({
6049
6111
  h2(
6050
6112
  "div",
6051
6113
  { className: "ddt-accountFooter dim-cardFooter" },
6052
- summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
6053
- feedback ? h2("div", {
6054
- className: "ddt-summary dim-cardSummary",
6055
- role: "status",
6056
- "aria-live": "polite"
6057
- }, feedback) : null,
6058
6114
  h2(
6059
6115
  "div",
6060
- { className: "ddt-actions dim-cardActions" },
6061
- h2(Button7, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
6062
- h2(Button7, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
6116
+ { className: "dim-cardFooterLayout" },
6117
+ h2(
6118
+ "div",
6119
+ { className: "ddt-actions dim-cardActions" },
6120
+ h2(Button7, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
6121
+ h2(Button7, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
6122
+ ),
6123
+ summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
6124
+ feedback ? h2("div", {
6125
+ className: "ddt-summary dim-cardFeedback",
6126
+ role: "status",
6127
+ "aria-live": "polite"
6128
+ }, feedback) : null
6063
6129
  )
6064
6130
  )
6065
6131
  ),
@@ -6072,25 +6138,25 @@ function AccountCard2({
6072
6138
  );
6073
6139
  }
6074
6140
  function QqSettingsTab({ rpcCall }) {
6075
- const [model, setModel] = React12.useState({
6141
+ const [model, setModel] = React13.useState({
6076
6142
  phase: "loading",
6077
6143
  bots: [],
6078
6144
  totals: { configured: 0, connected: 0 },
6079
6145
  error: null,
6080
6146
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
6081
6147
  });
6082
- const [provision, setProvision] = React12.useState(null);
6083
- const [busy, setBusy] = React12.useState(false);
6084
- const [busyByBot, setBusyByBot] = React12.useState({});
6085
- const [feedbackByBot, setFeedbackByBot] = React12.useState({});
6086
- const [removeTarget, setRemoveTarget] = React12.useState(null);
6087
- const [credentialOpen, setCredentialOpen] = React12.useState(false);
6088
- const [credentialError, setCredentialError] = React12.useState(null);
6089
- const [now, setNow] = React12.useState(Date.now());
6090
- const mounted = React12.useRef(true);
6148
+ const [provision, setProvision] = React13.useState(null);
6149
+ const [busy, setBusy] = React13.useState(false);
6150
+ const [busyByBot, setBusyByBot] = React13.useState({});
6151
+ const [feedbackByBot, setFeedbackByBot] = React13.useState({});
6152
+ const [removeTarget, setRemoveTarget] = React13.useState(null);
6153
+ const [credentialOpen, setCredentialOpen] = React13.useState(false);
6154
+ const [credentialError, setCredentialError] = React13.useState(null);
6155
+ const [now, setNow] = React13.useState(Date.now());
6156
+ const mounted = React13.useRef(true);
6091
6157
  const workspaceFence = useWorkspaceSnapshotFence();
6092
- const addButtonRef = React12.useRef(null);
6093
- React12.useEffect(() => {
6158
+ const addButtonRef = React13.useRef(null);
6159
+ React13.useEffect(() => {
6094
6160
  const disposeDingtalk = installDingtalkStyles();
6095
6161
  const disposeQq = installQqStyles();
6096
6162
  mounted.current = true;
@@ -6100,11 +6166,11 @@ function QqSettingsTab({ rpcCall }) {
6100
6166
  disposeDingtalk();
6101
6167
  };
6102
6168
  }, []);
6103
- const invoke = React12.useCallback(async (endpoint, payload = {}, signal) => {
6169
+ const invoke = React13.useCallback(async (endpoint, payload = {}, signal) => {
6104
6170
  if (typeof rpcCall !== "function") throw new TypeError("QQ \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
6105
6171
  return unwrapRpcResult4(await rpcCall(endpoint, payload, signal));
6106
6172
  }, [rpcCall]);
6107
- const loadStatus = React12.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
6173
+ const loadStatus = React13.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
6108
6174
  const workspaceVersion = workspaceFence.beginStatus();
6109
6175
  if (workspaceVersion === null) return void 0;
6110
6176
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -6130,12 +6196,12 @@ function QqSettingsTab({ rpcCall }) {
6130
6196
  return void 0;
6131
6197
  }
6132
6198
  }, [invoke, workspaceFence]);
6133
- React12.useEffect(() => {
6199
+ React13.useEffect(() => {
6134
6200
  const controller = new AbortController();
6135
6201
  void loadStatus({ signal: controller.signal, restore: true });
6136
6202
  return () => controller.abort();
6137
6203
  }, [loadStatus]);
6138
- React12.useEffect(() => {
6204
+ React13.useEffect(() => {
6139
6205
  if (model.phase !== "ready") return void 0;
6140
6206
  const controller = new AbortController();
6141
6207
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -6144,12 +6210,12 @@ function QqSettingsTab({ rpcCall }) {
6144
6210
  window.clearInterval(timer);
6145
6211
  };
6146
6212
  }, [loadStatus, model.phase]);
6147
- React12.useEffect(() => {
6213
+ React13.useEffect(() => {
6148
6214
  if (!provision || !ACTIVE_STATES.has(provision.status)) return void 0;
6149
6215
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
6150
6216
  return () => window.clearInterval(timer);
6151
6217
  }, [provision?.attemptId, provision?.status]);
6152
- const startProvisioning = React12.useCallback(async (replace = false) => {
6218
+ const startProvisioning = React13.useCallback(async (replace = false) => {
6153
6219
  setCredentialOpen(false);
6154
6220
  setCredentialError(null);
6155
6221
  setBusy(true);
@@ -6167,7 +6233,7 @@ function QqSettingsTab({ rpcCall }) {
6167
6233
  if (mounted.current) setBusy(false);
6168
6234
  }
6169
6235
  }, [invoke, provision?.attemptId]);
6170
- const bindCredentials = React12.useCallback(async ({ identity, secret }) => {
6236
+ const bindCredentials = React13.useCallback(async ({ identity, secret }) => {
6171
6237
  const snapshotVersion = workspaceFence.beginMutation();
6172
6238
  setBusy(true);
6173
6239
  setCredentialError(null);
@@ -6195,7 +6261,7 @@ function QqSettingsTab({ rpcCall }) {
6195
6261
  if (mounted.current) setBusy(false);
6196
6262
  }
6197
6263
  }, [invoke, loadStatus, workspaceFence]);
6198
- const closeProvision = React12.useCallback(async () => {
6264
+ const closeProvision = React13.useCallback(async () => {
6199
6265
  setBusy(true);
6200
6266
  try {
6201
6267
  if (provision?.attemptId && ACTIVE_STATES.has(provision.status)) {
@@ -6206,7 +6272,7 @@ function QqSettingsTab({ rpcCall }) {
6206
6272
  if (mounted.current) setBusy(false);
6207
6273
  }
6208
6274
  }, [invoke, provision?.attemptId, provision?.status]);
6209
- React12.useEffect(() => {
6275
+ React13.useEffect(() => {
6210
6276
  const attemptId = provision?.attemptId;
6211
6277
  if (!attemptId || !ACTIVE_STATES.has(provision.status)) return void 0;
6212
6278
  const controller = new AbortController();
@@ -6236,7 +6302,7 @@ function QqSettingsTab({ rpcCall }) {
6236
6302
  window.clearTimeout(timer);
6237
6303
  };
6238
6304
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
6239
- const botAction = React12.useCallback(async (account, operation, endpoint, payload) => {
6305
+ const botAction = React13.useCallback(async (account, operation, endpoint, payload) => {
6240
6306
  const snapshotVersion = workspaceFence.beginMutation();
6241
6307
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
6242
6308
  try {
@@ -6261,7 +6327,7 @@ function QqSettingsTab({ rpcCall }) {
6261
6327
  });
6262
6328
  }
6263
6329
  }, [invoke, loadStatus, workspaceFence]);
6264
- const reconnect = React12.useCallback(async (account) => {
6330
+ const reconnect = React13.useCallback(async (account) => {
6265
6331
  setFeedbackByBot((current) => {
6266
6332
  const next = { ...current };
6267
6333
  delete next[account.botId];
@@ -6307,11 +6373,11 @@ function QqSettingsTab({ rpcCall }) {
6307
6373
  const botList = model.bots.length > 0 ? h2(
6308
6374
  "section",
6309
6375
  { className: "dim-listSection" },
6310
- h2(
6311
- "div",
6312
- { className: "ddt-listHeading dim-listHeading" },
6313
- h2("h3", null, "\u5DF2\u7ED1\u5B9A\u7684 QQ \u673A\u5668\u4EBA")
6314
- ),
6376
+ h2(ChannelListHeading, {
6377
+ className: "ddt-listHeading",
6378
+ title: "\u5DF2\u7ED1\u5B9A\u7684 QQ \u673A\u5668\u4EBA",
6379
+ connectionLabel: "WebSocket \u957F\u8FDE\u63A5"
6380
+ }),
6315
6381
  h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard2, {
6316
6382
  account,
6317
6383
  busy: busyByBot[account.botId],
@@ -6370,7 +6436,7 @@ function QqSettingsTab({ rpcCall }) {
6370
6436
  addButtonRef
6371
6437
  }),
6372
6438
  model.phase === "loading" ? h2(LoadingView3) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 QQ \u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button7, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
6373
- React12.Fragment,
6439
+ React13.Fragment,
6374
6440
  null,
6375
6441
  credentialView,
6376
6442
  provisionView,
@@ -6459,7 +6525,7 @@ function normalizeOfficeStatus(value) {
6459
6525
  }
6460
6526
 
6461
6527
  // plugin-src/client/channels/office/index.js
6462
- var React13 = __toESM(require("react"), 1);
6528
+ var React14 = __toESM(require("react"), 1);
6463
6529
  function Button9({ children, kind = "secondary", ...props }) {
6464
6530
  return h2("button", { ...props, type: "button", className: "ddt-button", "data-kind": kind }, children);
6465
6531
  }
@@ -6488,12 +6554,12 @@ function stateLabel(model) {
6488
6554
  return "\u5DF2\u914D\u7F6E";
6489
6555
  }
6490
6556
  function OfficeSettingsTab({ rpcCall, initialStatus }) {
6491
- const [model, setModel] = React13.useState(normalizeOfficeStatus(initialStatus));
6492
- const [phase, setPhase] = React13.useState(initialStatus === void 0 ? "loading" : "ready");
6493
- const [busy, setBusy] = React13.useState("");
6494
- const [error, setError] = React13.useState("");
6495
- const [notice, setNotice] = React13.useState("");
6496
- const [form, setForm] = React13.useState({
6557
+ const [model, setModel] = React14.useState(normalizeOfficeStatus(initialStatus));
6558
+ const [phase, setPhase] = React14.useState(initialStatus === void 0 ? "loading" : "ready");
6559
+ const [busy, setBusy] = React14.useState("");
6560
+ const [error, setError] = React14.useState("");
6561
+ const [notice, setNotice] = React14.useState("");
6562
+ const [form, setForm] = React14.useState({
6497
6563
  baseUrl: "",
6498
6564
  deviceId: "local-harness",
6499
6565
  deviceToken: "",
@@ -6502,11 +6568,11 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
6502
6568
  workspaces: "",
6503
6569
  instructionPresets: ""
6504
6570
  });
6505
- const invoke = React13.useCallback(async (endpoint, payload = {}) => {
6571
+ const invoke = React14.useCallback(async (endpoint, payload = {}) => {
6506
6572
  if (typeof rpcCall !== "function") throw new Error("AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
6507
6573
  return unwrapOfficeRpc(await rpcCall(endpoint, payload));
6508
6574
  }, [rpcCall]);
6509
- const adopt = React13.useCallback((value) => {
6575
+ const adopt = React14.useCallback((value) => {
6510
6576
  const next = normalizeOfficeStatus(value?.snapshot ?? value);
6511
6577
  setModel(next);
6512
6578
  if (next.config) setForm((current) => ({
@@ -6521,7 +6587,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
6521
6587
  }));
6522
6588
  return next;
6523
6589
  }, []);
6524
- const load = React13.useCallback(async () => {
6590
+ const load = React14.useCallback(async () => {
6525
6591
  try {
6526
6592
  adopt(await invoke(OFFICE_RPC_ENDPOINTS.status));
6527
6593
  setPhase("ready");
@@ -6531,7 +6597,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
6531
6597
  setError(caught.message);
6532
6598
  }
6533
6599
  }, [adopt, invoke]);
6534
- React13.useEffect(() => {
6600
+ React14.useEffect(() => {
6535
6601
  void load();
6536
6602
  }, [load]);
6537
6603
  const run = async (name2, operation) => {
@@ -6548,7 +6614,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
6548
6614
  setBusy("");
6549
6615
  }
6550
6616
  };
6551
- const hooks = React13.useMemo(() => {
6617
+ const hooks = React14.useMemo(() => {
6552
6618
  try {
6553
6619
  return officeHookUrls(form.baseUrl);
6554
6620
  } catch {
@@ -6733,7 +6799,7 @@ var normalizeSnapshot4 = api2.normalizeSnapshot;
6733
6799
  var presentError5 = api2.presentError;
6734
6800
 
6735
6801
  // plugin-src/client/channels/slack/index.js
6736
- var React14 = __toESM(require("react"), 1);
6802
+ var React15 = __toESM(require("react"), 1);
6737
6803
 
6738
6804
  // src/channels/slack/manifest.mjs
6739
6805
  var SLACK_APP_MANIFEST_YAML = `_metadata:
@@ -6756,6 +6822,7 @@ oauth_config:
6756
6822
  - app_mentions:read
6757
6823
  - chat:write
6758
6824
  - files:read
6825
+ - files:write
6759
6826
  - im:history
6760
6827
  settings:
6761
6828
  event_subscriptions:
@@ -6806,10 +6873,10 @@ function installSlackStyles() {
6806
6873
 
6807
6874
  // plugin-src/client/channels/slack/index.js
6808
6875
  function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) {
6809
- const [botToken, setBotToken] = React14.useState("");
6810
- const [appToken, setAppToken] = React14.useState("");
6811
- const [copied, setCopied] = React14.useState(false);
6812
- const headingId = React14.useId();
6876
+ const [botToken, setBotToken] = React15.useState("");
6877
+ const [appToken, setAppToken] = React15.useState("");
6878
+ const [copied, setCopied] = React15.useState(false);
6879
+ const headingId = React15.useId();
6813
6880
  const copyManifest = async () => {
6814
6881
  try {
6815
6882
  await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML);
@@ -6965,7 +7032,7 @@ var normalizeSnapshot5 = api3.normalizeSnapshot;
6965
7032
  var presentError6 = api3.presentError;
6966
7033
 
6967
7034
  // plugin-src/client/channels/telegram/index.js
6968
- var React15 = __toESM(require("react"), 1);
7035
+ var React16 = __toESM(require("react"), 1);
6969
7036
 
6970
7037
  // plugin-src/client/channels/telegram/styles.js
6971
7038
  var TELEGRAM_STYLE_ID = "xmanrui-dsh-im-telegram-settings";
@@ -7031,11 +7098,11 @@ function allowedUsersFromText(value) {
7031
7098
  function TelegramAccessSettings({ account, busy = false, onSave }) {
7032
7099
  const policy = policyFor(account);
7033
7100
  const sourceUsers = policy.allowedUsers.join("\n");
7034
- const accessHelpId = React15.useId();
7035
- const [accessMode, setAccessMode] = React15.useState(policy.accessMode);
7036
- const [allowedUsers, setAllowedUsers] = React15.useState(sourceUsers);
7037
- const [error, setError] = React15.useState(null);
7038
- React15.useEffect(() => {
7101
+ const accessHelpId = React16.useId();
7102
+ const [accessMode, setAccessMode] = React16.useState(policy.accessMode);
7103
+ const [allowedUsers, setAllowedUsers] = React16.useState(sourceUsers);
7104
+ const [error, setError] = React16.useState(null);
7105
+ React16.useEffect(() => {
7039
7106
  setAccessMode(policy.accessMode);
7040
7107
  setAllowedUsers(sourceUsers);
7041
7108
  setError(null);
@@ -7295,7 +7362,7 @@ function formatRemaining4(milliseconds) {
7295
7362
  }
7296
7363
 
7297
7364
  // plugin-src/client/channels/wecom/index.js
7298
- var React16 = __toESM(require("react"), 1);
7365
+ var React17 = __toESM(require("react"), 1);
7299
7366
 
7300
7367
  // plugin-src/client/channels/wecom/styles.js
7301
7368
  var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
@@ -7320,7 +7387,7 @@ function installWecomStyles() {
7320
7387
 
7321
7388
  // plugin-src/client/channels/wecom/index.js
7322
7389
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
7323
- var Button10 = React16.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
7390
+ var Button10 = React17.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
7324
7391
  return h2("button", {
7325
7392
  ...props,
7326
7393
  ref,
@@ -7562,18 +7629,14 @@ function AccountCard3({
7562
7629
  h2("p", null, account.bot.appIdMasked)
7563
7630
  )
7564
7631
  ),
7565
- h2(
7566
- "div",
7567
- { className: "ddt-health dim-botHealth" },
7568
- h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
7569
- h2("span", null, stateLabel2)
7570
- )
7571
- ),
7572
- h2(
7573
- "dl",
7574
- { className: "ddt-metrics dim-botMetrics" },
7575
- h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6D88\u606F\u901A\u9053"), h2("dd", null, account.connected ? "WebSocket \u957F\u8FDE\u63A5" : "\u79BB\u7EBF")),
7576
- h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h2("dd", null, checkedTime4(account.health.lastCheckedAt)))
7632
+ h2(BotStatusMeta, {
7633
+ className: "ddt-health",
7634
+ dotClassName: "ddt-dot",
7635
+ tone,
7636
+ stateLabel: stateLabel2,
7637
+ lastCheckedAt: account.health.lastCheckedAt,
7638
+ formatCheckedTime: checkedTime4
7639
+ })
7577
7640
  ),
7578
7641
  h2(WorkspaceEditor, {
7579
7642
  workspace: account.workspace,
@@ -7588,17 +7651,21 @@ function AccountCard3({
7588
7651
  h2(
7589
7652
  "div",
7590
7653
  { className: "ddt-accountFooter dim-cardFooter" },
7591
- summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
7592
- feedback ? h2("div", {
7593
- className: "ddt-summary dim-cardSummary",
7594
- role: "status",
7595
- "aria-live": "polite"
7596
- }, feedback) : null,
7597
7654
  h2(
7598
7655
  "div",
7599
- { className: "ddt-actions dim-cardActions" },
7600
- h2(Button10, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
7601
- h2(Button10, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
7656
+ { className: "dim-cardFooterLayout" },
7657
+ h2(
7658
+ "div",
7659
+ { className: "ddt-actions dim-cardActions" },
7660
+ h2(Button10, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
7661
+ h2(Button10, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
7662
+ ),
7663
+ summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
7664
+ feedback ? h2("div", {
7665
+ className: "ddt-summary dim-cardFeedback",
7666
+ role: "status",
7667
+ "aria-live": "polite"
7668
+ }, feedback) : null
7602
7669
  )
7603
7670
  )
7604
7671
  ),
@@ -7611,27 +7678,27 @@ function AccountCard3({
7611
7678
  );
7612
7679
  }
7613
7680
  function WecomSettingsTab({ rpcCall }) {
7614
- const [model, setModel] = React16.useState({
7681
+ const [model, setModel] = React17.useState({
7615
7682
  phase: "loading",
7616
7683
  bots: [],
7617
7684
  totals: { configured: 0, connected: 0 },
7618
7685
  error: null,
7619
7686
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
7620
7687
  });
7621
- const [provision, setProvision] = React16.useState(null);
7622
- const [busy, setBusy] = React16.useState(false);
7623
- const [busyByBot, setBusyByBot] = React16.useState({});
7624
- const [feedbackByBot, setFeedbackByBot] = React16.useState({});
7625
- const [removeTarget, setRemoveTarget] = React16.useState(null);
7626
- const [credentialOpen, setCredentialOpen] = React16.useState(false);
7627
- const [credentialError, setCredentialError] = React16.useState(null);
7628
- const [notice, setNotice] = React16.useState("");
7629
- const [now, setNow] = React16.useState(Date.now());
7630
- const mounted = React16.useRef(true);
7688
+ const [provision, setProvision] = React17.useState(null);
7689
+ const [busy, setBusy] = React17.useState(false);
7690
+ const [busyByBot, setBusyByBot] = React17.useState({});
7691
+ const [feedbackByBot, setFeedbackByBot] = React17.useState({});
7692
+ const [removeTarget, setRemoveTarget] = React17.useState(null);
7693
+ const [credentialOpen, setCredentialOpen] = React17.useState(false);
7694
+ const [credentialError, setCredentialError] = React17.useState(null);
7695
+ const [notice, setNotice] = React17.useState("");
7696
+ const [now, setNow] = React17.useState(Date.now());
7697
+ const mounted = React17.useRef(true);
7631
7698
  const workspaceFence = useWorkspaceSnapshotFence();
7632
- const addButtonRef = React16.useRef(null);
7633
- const noticeFrameRef = React16.useRef(null);
7634
- const announce = React16.useCallback((message) => {
7699
+ const addButtonRef = React17.useRef(null);
7700
+ const noticeFrameRef = React17.useRef(null);
7701
+ const announce = React17.useCallback((message) => {
7635
7702
  if (!mounted.current) return;
7636
7703
  if (noticeFrameRef.current !== null) {
7637
7704
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -7645,7 +7712,7 @@ function WecomSettingsTab({ rpcCall }) {
7645
7712
  });
7646
7713
  }
7647
7714
  }, []);
7648
- React16.useEffect(() => {
7715
+ React17.useEffect(() => {
7649
7716
  const disposeDingtalk = installDingtalkStyles();
7650
7717
  const disposeWecom = installWecomStyles();
7651
7718
  mounted.current = true;
@@ -7659,11 +7726,11 @@ function WecomSettingsTab({ rpcCall }) {
7659
7726
  disposeDingtalk();
7660
7727
  };
7661
7728
  }, []);
7662
- const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
7729
+ const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
7663
7730
  if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
7664
7731
  return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
7665
7732
  }, [rpcCall]);
7666
- const loadStatus = React16.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
7733
+ const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
7667
7734
  const workspaceVersion = workspaceFence.beginStatus();
7668
7735
  if (workspaceVersion === null) return void 0;
7669
7736
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -7689,12 +7756,12 @@ function WecomSettingsTab({ rpcCall }) {
7689
7756
  return void 0;
7690
7757
  }
7691
7758
  }, [invoke, workspaceFence]);
7692
- React16.useEffect(() => {
7759
+ React17.useEffect(() => {
7693
7760
  const controller = new AbortController();
7694
7761
  void loadStatus({ signal: controller.signal, restore: true });
7695
7762
  return () => controller.abort();
7696
7763
  }, [loadStatus]);
7697
- React16.useEffect(() => {
7764
+ React17.useEffect(() => {
7698
7765
  if (model.phase !== "ready") return void 0;
7699
7766
  const controller = new AbortController();
7700
7767
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -7703,12 +7770,12 @@ function WecomSettingsTab({ rpcCall }) {
7703
7770
  window.clearInterval(timer);
7704
7771
  };
7705
7772
  }, [loadStatus, model.phase]);
7706
- React16.useEffect(() => {
7773
+ React17.useEffect(() => {
7707
7774
  if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
7708
7775
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
7709
7776
  return () => window.clearInterval(timer);
7710
7777
  }, [provision?.attemptId, provision?.status]);
7711
- const startProvisioning = React16.useCallback(async (replace = false) => {
7778
+ const startProvisioning = React17.useCallback(async (replace = false) => {
7712
7779
  setCredentialOpen(false);
7713
7780
  setCredentialError(null);
7714
7781
  setBusy(true);
@@ -7726,7 +7793,7 @@ function WecomSettingsTab({ rpcCall }) {
7726
7793
  if (mounted.current) setBusy(false);
7727
7794
  }
7728
7795
  }, [invoke, provision?.attemptId]);
7729
- const bindCredentials = React16.useCallback(async ({ identity, secret }) => {
7796
+ const bindCredentials = React17.useCallback(async ({ identity, secret }) => {
7730
7797
  const snapshotVersion = workspaceFence.beginMutation();
7731
7798
  setBusy(true);
7732
7799
  setCredentialError(null);
@@ -7754,7 +7821,7 @@ function WecomSettingsTab({ rpcCall }) {
7754
7821
  if (mounted.current) setBusy(false);
7755
7822
  }
7756
7823
  }, [invoke, loadStatus, workspaceFence]);
7757
- const closeProvision = React16.useCallback(async () => {
7824
+ const closeProvision = React17.useCallback(async () => {
7758
7825
  setBusy(true);
7759
7826
  try {
7760
7827
  if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@@ -7765,7 +7832,7 @@ function WecomSettingsTab({ rpcCall }) {
7765
7832
  if (mounted.current) setBusy(false);
7766
7833
  }
7767
7834
  }, [invoke, provision?.attemptId, provision?.status]);
7768
- React16.useEffect(() => {
7835
+ React17.useEffect(() => {
7769
7836
  const attemptId = provision?.attemptId;
7770
7837
  if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
7771
7838
  const controller = new AbortController();
@@ -7795,7 +7862,7 @@ function WecomSettingsTab({ rpcCall }) {
7795
7862
  window.clearTimeout(timer);
7796
7863
  };
7797
7864
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
7798
- const botAction = React16.useCallback(async (account, operation, endpoint, payload) => {
7865
+ const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
7799
7866
  const snapshotVersion = workspaceFence.beginMutation();
7800
7867
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
7801
7868
  try {
@@ -7820,7 +7887,7 @@ function WecomSettingsTab({ rpcCall }) {
7820
7887
  });
7821
7888
  }
7822
7889
  }, [invoke, loadStatus, workspaceFence]);
7823
- const reconnect = React16.useCallback(async (account) => {
7890
+ const reconnect = React17.useCallback(async (account) => {
7824
7891
  setFeedbackByBot((current) => {
7825
7892
  const next = { ...current };
7826
7893
  delete next[account.botId];
@@ -7877,11 +7944,11 @@ function WecomSettingsTab({ rpcCall }) {
7877
7944
  const botList = model.bots.length > 0 ? h2(
7878
7945
  "section",
7879
7946
  { className: "dim-listSection" },
7880
- h2(
7881
- "div",
7882
- { className: "ddt-listHeading dim-listHeading" },
7883
- h2("h3", null, "\u5DF2\u7ED1\u5B9A\u7684\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA")
7884
- ),
7947
+ h2(ChannelListHeading, {
7948
+ className: "ddt-listHeading",
7949
+ title: "\u5DF2\u7ED1\u5B9A\u7684\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA",
7950
+ connectionLabel: "WebSocket \u957F\u8FDE\u63A5"
7951
+ }),
7885
7952
  h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard3, {
7886
7953
  account,
7887
7954
  busy: busyByBot[account.botId],
@@ -7941,7 +8008,7 @@ function WecomSettingsTab({ rpcCall }) {
7941
8008
  }),
7942
8009
  h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
7943
8010
  model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button10, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
7944
- React16.Fragment,
8011
+ React17.Fragment,
7945
8012
  null,
7946
8013
  credentialView,
7947
8014
  provisionView,
@@ -7952,7 +8019,7 @@ function WecomSettingsTab({ rpcCall }) {
7952
8019
  }
7953
8020
 
7954
8021
  // plugin-src/client/channels/weixin/index.js
7955
- var React17 = __toESM(require("react"), 1);
8022
+ var React18 = __toESM(require("react"), 1);
7956
8023
 
7957
8024
  // plugin-src/client/channels/weixin/api.js
7958
8025
  var WEIXIN_RPC_CHANNEL = "/weixin";
@@ -8193,10 +8260,6 @@ var CSS9 = String.raw`
8193
8260
  .dxw-accountIdentity h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; }
8194
8261
  .dxw-accountIdentity p { color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; margin-top: 4px; }
8195
8262
  .dxw-health { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; }
8196
- .dxw-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 20px 0; }
8197
- .dxw-metric { padding: 12px 14px; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
8198
- .dxw-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; }
8199
- .dxw-metric dd { overflow: hidden; margin: 5px 0 0; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
8200
8263
  .dxw-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
8201
8264
  .dxw-accountFooter .dxw-actions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; }
8202
8265
  .dxw-accountFooter .dxw-button { flex: none; white-space: nowrap; }
@@ -8215,7 +8278,6 @@ var CSS9 = String.raw`
8215
8278
  .dxw-logo { display: none; }
8216
8279
  .dxw-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; }
8217
8280
  .dxw-qrCopy { width: 100%; }
8218
- .dxw-metrics { grid-template-columns: minmax(0, 1fr); }
8219
8281
  .dxw-cardBody { padding: 20px; }
8220
8282
  }
8221
8283
  @media (prefers-reduced-motion: reduce) {
@@ -8237,7 +8299,7 @@ function installWeixinStyles() {
8237
8299
  }
8238
8300
 
8239
8301
  // plugin-src/client/channels/weixin/index.js
8240
- var Button12 = React17.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
8302
+ var Button12 = React18.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
8241
8303
  return h2("button", {
8242
8304
  ...props,
8243
8305
  ref,
@@ -8310,14 +8372,14 @@ function EmptyView5({ onStart, busy }) {
8310
8372
  );
8311
8373
  }
8312
8374
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
8313
- const [imageFailed, setImageFailed] = React17.useState(false);
8375
+ const [imageFailed, setImageFailed] = React18.useState(false);
8314
8376
  const source = safeQrSource5(provision.qrCodeDataUrl);
8315
8377
  const href = safeVerificationUrl(provision.verificationUrl);
8316
8378
  const remaining = Math.max(0, provision.expiresAt - now);
8317
8379
  const expired = remaining === 0 || provision.status === "expired";
8318
8380
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
8319
8381
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
8320
- React17.useEffect(() => setImageFailed(false), [source]);
8382
+ React18.useEffect(() => setImageFailed(false), [source]);
8321
8383
  return h2(
8322
8384
  "div",
8323
8385
  { className: "dxw-card dim-surfaceCard" },
@@ -8384,9 +8446,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
8384
8446
  );
8385
8447
  }
8386
8448
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
8387
- const [code, setCode] = React17.useState("");
8449
+ const [code, setCode] = React18.useState("");
8388
8450
  const valid = /^\d{4,8}$/.test(code);
8389
- React17.useEffect(() => setCode(""), [provision.attemptId]);
8451
+ React18.useEffect(() => setCode(""), [provision.attemptId]);
8390
8452
  return h2(
8391
8453
  "div",
8392
8454
  { className: "dxw-card dim-surfaceCard" },
@@ -8507,28 +8569,14 @@ function AccountCard4({
8507
8569
  h2("div", { className: "dxw-avatar dim-botAvatar", "aria-hidden": "true" }, h2(WeixinLogoGlyph, { size: 27 })),
8508
8570
  h2("div", { className: "dim-botName" }, h2("h3", null, account.bot.name), h2("p", null, account.bot.accountIdMasked))
8509
8571
  ),
8510
- h2(
8511
- "div",
8512
- { className: "dxw-health dim-botHealth" },
8513
- h2("span", { className: "dxw-dot dim-healthDot", "data-tone": tone }),
8514
- h2("span", null, account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA")
8515
- )
8516
- ),
8517
- h2(
8518
- "dl",
8519
- { className: "dxw-metrics dim-botMetrics" },
8520
- h2(
8521
- "div",
8522
- { className: "dxw-metric dim-botMetric" },
8523
- h2("dt", null, "\u6D88\u606F\u901A\u9053"),
8524
- h2("dd", null, account.connected ? "iLink \u957F\u8F6E\u8BE2" : "\u79BB\u7EBF")
8525
- ),
8526
- h2(
8527
- "div",
8528
- { className: "dxw-metric dim-botMetric" },
8529
- h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"),
8530
- h2("dd", null, checkedTime5(account.health.lastCheckedAt))
8531
- )
8572
+ h2(BotStatusMeta, {
8573
+ className: "dxw-health",
8574
+ dotClassName: "dxw-dot",
8575
+ tone,
8576
+ stateLabel: account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA",
8577
+ lastCheckedAt: account.health.lastCheckedAt,
8578
+ formatCheckedTime: checkedTime5
8579
+ })
8532
8580
  ),
8533
8581
  h2(WorkspaceEditor, {
8534
8582
  workspace: account.workspace,
@@ -8543,25 +8591,29 @@ function AccountCard4({
8543
8591
  h2(
8544
8592
  "div",
8545
8593
  { className: "dxw-accountFooter dim-cardFooter" },
8546
- summary ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary) : null,
8547
- account.lastMessageError ? h2("div", {
8548
- className: "dxw-summary dim-cardSummary",
8549
- role: "status"
8550
- }, `\u6700\u8FD1\u4E00\u6761\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF1A${account.lastMessageError.message}`) : null,
8551
- feedback ? h2("div", {
8552
- className: "dxw-summary dim-cardSummary",
8553
- role: "status",
8554
- "aria-live": "polite"
8555
- }, feedback) : null,
8556
8594
  h2(
8557
8595
  "div",
8558
- { className: "dxw-actions dim-cardActions" },
8596
+ { className: "dim-cardFooterLayout" },
8559
8597
  h2(
8560
- Button12,
8561
- { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) },
8562
- busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"
8598
+ "div",
8599
+ { className: "dxw-actions dim-cardActions" },
8600
+ h2(
8601
+ Button12,
8602
+ { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) },
8603
+ busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"
8604
+ ),
8605
+ h2(Button12, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
8563
8606
  ),
8564
- h2(Button12, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
8607
+ summary ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary) : null,
8608
+ account.lastMessageError ? h2("div", {
8609
+ className: "dxw-summary dim-cardSummary",
8610
+ role: "status"
8611
+ }, `\u6700\u8FD1\u4E00\u6761\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF1A${account.lastMessageError.message}`) : null,
8612
+ feedback ? h2("div", {
8613
+ className: "dxw-summary dim-cardFeedback",
8614
+ role: "status",
8615
+ "aria-live": "polite"
8616
+ }, feedback) : null
8565
8617
  )
8566
8618
  )
8567
8619
  ),
@@ -8587,7 +8639,11 @@ function AccountList2(props) {
8587
8639
  return h2(
8588
8640
  "section",
8589
8641
  { className: "dim-listSection" },
8590
- h2("div", { className: "dxw-listHeading dim-listHeading" }, h2("h3", null, "\u5DF2\u63A5\u5165\u7684\u5FAE\u4FE1\u8D26\u53F7")),
8642
+ h2(ChannelListHeading, {
8643
+ className: "dxw-listHeading",
8644
+ title: "\u5DF2\u63A5\u5165\u7684\u5FAE\u4FE1\u8D26\u53F7",
8645
+ connectionLabel: "iLink \u957F\u8F6E\u8BE2"
8646
+ }),
8591
8647
  h2("ul", { className: "dxw-list dim-botList" }, props.bots.map((account) => h2(
8592
8648
  "li",
8593
8649
  { key: account.botId },
@@ -8617,7 +8673,7 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
8617
8673
  };
8618
8674
  }
8619
8675
  function WeixinSettingsTab({ rpcCall }) {
8620
- const [model, setModel] = React17.useState({
8676
+ const [model, setModel] = React18.useState({
8621
8677
  phase: "loading",
8622
8678
  bots: [],
8623
8679
  totals: EMPTY_TOTALS3,
@@ -8625,33 +8681,33 @@ function WeixinSettingsTab({ rpcCall }) {
8625
8681
  error: null,
8626
8682
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
8627
8683
  });
8628
- const [provision, setProvision] = React17.useState(null);
8629
- const [busy, setBusy] = React17.useState(false);
8630
- const [busyByBot, setBusyByBot] = React17.useState({});
8631
- const [feedbackByBot, setFeedbackByBot] = React17.useState({});
8632
- const [removeTarget, setRemoveTarget] = React17.useState(null);
8633
- const [notice, setNotice] = React17.useState("");
8634
- const [now, setNow] = React17.useState(() => Date.now());
8635
- const addButtonRef = React17.useRef(null);
8636
- const mountedRef = React17.useRef(true);
8684
+ const [provision, setProvision] = React18.useState(null);
8685
+ const [busy, setBusy] = React18.useState(false);
8686
+ const [busyByBot, setBusyByBot] = React18.useState({});
8687
+ const [feedbackByBot, setFeedbackByBot] = React18.useState({});
8688
+ const [removeTarget, setRemoveTarget] = React18.useState(null);
8689
+ const [notice, setNotice] = React18.useState("");
8690
+ const [now, setNow] = React18.useState(() => Date.now());
8691
+ const addButtonRef = React18.useRef(null);
8692
+ const mountedRef = React18.useRef(true);
8637
8693
  const workspaceFence = useWorkspaceSnapshotFence();
8638
8694
  const scheduleAnimationFrame = useAnimationFrameScheduler();
8639
- React17.useEffect(() => {
8695
+ React18.useEffect(() => {
8640
8696
  mountedRef.current = true;
8641
8697
  return () => {
8642
8698
  mountedRef.current = false;
8643
8699
  };
8644
8700
  }, []);
8645
- const announce = React17.useCallback((value) => {
8701
+ const announce = React18.useCallback((value) => {
8646
8702
  setNotice("");
8647
8703
  scheduleAnimationFrame(() => {
8648
8704
  if (value) setNotice(value);
8649
8705
  }, "announcement");
8650
8706
  }, [scheduleAnimationFrame]);
8651
- const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
8707
+ const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
8652
8708
  return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
8653
8709
  }, [rpcCall]);
8654
- const loadStatus = React17.useCallback(async ({
8710
+ const loadStatus = React18.useCallback(async ({
8655
8711
  signal,
8656
8712
  silent = false,
8657
8713
  restoreProvisioning = false
@@ -8688,12 +8744,12 @@ function WeixinSettingsTab({ rpcCall }) {
8688
8744
  return void 0;
8689
8745
  }
8690
8746
  }, [invoke, workspaceFence]);
8691
- React17.useEffect(() => {
8747
+ React18.useEffect(() => {
8692
8748
  const controller = new AbortController();
8693
8749
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
8694
8750
  return () => controller.abort();
8695
8751
  }, [loadStatus]);
8696
- React17.useEffect(() => {
8752
+ React18.useEffect(() => {
8697
8753
  if (model.phase !== "ready") return void 0;
8698
8754
  const controller = new AbortController();
8699
8755
  let running = false;
@@ -8712,12 +8768,12 @@ function WeixinSettingsTab({ rpcCall }) {
8712
8768
  window.clearInterval(timer);
8713
8769
  };
8714
8770
  }, [loadStatus, model.phase]);
8715
- React17.useEffect(() => {
8771
+ React18.useEffect(() => {
8716
8772
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
8717
8773
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
8718
8774
  return () => window.clearInterval(timer);
8719
8775
  }, [provision?.attemptId, provision?.status]);
8720
- const startProvisioning = React17.useCallback(async ({ replace = false } = {}) => {
8776
+ const startProvisioning = React18.useCallback(async ({ replace = false } = {}) => {
8721
8777
  setBusy(true);
8722
8778
  try {
8723
8779
  if (replace && provision?.attemptId) {
@@ -8738,7 +8794,7 @@ function WeixinSettingsTab({ rpcCall }) {
8738
8794
  setBusy(false);
8739
8795
  }
8740
8796
  }, [announce, invoke, provision?.attemptId]);
8741
- const cancelProvisioning = React17.useCallback(async () => {
8797
+ const cancelProvisioning = React18.useCallback(async () => {
8742
8798
  setBusy(true);
8743
8799
  try {
8744
8800
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -8753,7 +8809,7 @@ function WeixinSettingsTab({ rpcCall }) {
8753
8809
  setBusy(false);
8754
8810
  }
8755
8811
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
8756
- const submitVerification = React17.useCallback(async (verifyCode) => {
8812
+ const submitVerification = React18.useCallback(async (verifyCode) => {
8757
8813
  if (!provision?.attemptId) return;
8758
8814
  setBusy(true);
8759
8815
  try {
@@ -8769,7 +8825,7 @@ function WeixinSettingsTab({ rpcCall }) {
8769
8825
  setBusy(false);
8770
8826
  }
8771
8827
  }, [announce, invoke, provision?.attemptId]);
8772
- React17.useEffect(() => {
8828
+ React18.useEffect(() => {
8773
8829
  const attemptId = provision?.attemptId;
8774
8830
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
8775
8831
  const controller = new AbortController();
@@ -8817,7 +8873,7 @@ function WeixinSettingsTab({ rpcCall }) {
8817
8873
  controller.abort();
8818
8874
  };
8819
8875
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
8820
- const setBotBusy = React17.useCallback((botId, value) => {
8876
+ const setBotBusy = React18.useCallback((botId, value) => {
8821
8877
  setBusyByBot((current) => {
8822
8878
  const next = { ...current };
8823
8879
  if (value) next[botId] = value;
@@ -8825,7 +8881,7 @@ function WeixinSettingsTab({ rpcCall }) {
8825
8881
  return next;
8826
8882
  });
8827
8883
  }, []);
8828
- const reconnect = React17.useCallback(async (account) => {
8884
+ const reconnect = React18.useCallback(async (account) => {
8829
8885
  const snapshotVersion = workspaceFence.beginMutation();
8830
8886
  setBotBusy(account.botId, "reconnect");
8831
8887
  setFeedbackByBot((current) => {
@@ -8876,7 +8932,7 @@ function WeixinSettingsTab({ rpcCall }) {
8876
8932
  setBotBusy(account.botId, null);
8877
8933
  }
8878
8934
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
8879
- const saveWorkspace = React17.useCallback(async (account, workspace) => {
8935
+ const saveWorkspace = React18.useCallback(async (account, workspace) => {
8880
8936
  const workspaceVersion = workspaceFence.beginMutation();
8881
8937
  setBotBusy(account.botId, "workspace");
8882
8938
  try {
@@ -8900,7 +8956,7 @@ function WeixinSettingsTab({ rpcCall }) {
8900
8956
  if (mountedRef.current) setBotBusy(account.botId, null);
8901
8957
  }
8902
8958
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
8903
- const saveAgentPreset = React17.useCallback(async (account, agentPreset) => {
8959
+ const saveAgentPreset = React18.useCallback(async (account, agentPreset) => {
8904
8960
  const snapshotVersion = workspaceFence.beginMutation();
8905
8961
  setBotBusy(account.botId, "preset");
8906
8962
  try {
@@ -8924,7 +8980,7 @@ function WeixinSettingsTab({ rpcCall }) {
8924
8980
  if (mountedRef.current) setBotBusy(account.botId, null);
8925
8981
  }
8926
8982
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
8927
- const remove = React17.useCallback(async (account) => {
8983
+ const remove = React18.useCallback(async (account) => {
8928
8984
  const snapshotVersion = workspaceFence.beginMutation();
8929
8985
  setBotBusy(account.botId, "delete");
8930
8986
  try {
@@ -9008,7 +9064,7 @@ function WeixinSettingsTab({ rpcCall }) {
9008
9064
  h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
9009
9065
  )
9010
9066
  ) : h2(
9011
- React17.Fragment,
9067
+ React18.Fragment,
9012
9068
  null,
9013
9069
  provisionView,
9014
9070
  model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@@ -9037,6 +9093,7 @@ var WHATSAPP_ENDPOINTS = Object.freeze({
9037
9093
  cancelProvisioning: "provision.cancel",
9038
9094
  reconnectBot: "bot.reconnect",
9039
9095
  deleteBot: "bot.delete",
9096
+ setAccessPolicy: "bot.access-policy.set",
9040
9097
  setWorkspace: "bot.workspace.set",
9041
9098
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT
9042
9099
  });
@@ -9104,6 +9161,12 @@ function normalizeBot6(value) {
9104
9161
  state: connected ? "connected" : state,
9105
9162
  workspace: text4(value.workspace, "", 4096),
9106
9163
  agentPreset: normalizeAgentPresetId(value.agentPreset),
9164
+ accessPolicy: {
9165
+ accessMode: ["self-only", "private-allowlist", "open"].includes(
9166
+ value.accessPolicy?.accessMode
9167
+ ) ? value.accessPolicy.accessMode : "self-only",
9168
+ allowedNumbers: Array.isArray(value.accessPolicy?.allowedNumbers) ? [...new Set(value.accessPolicy.allowedNumbers.filter((entry) => typeof entry === "string" && /^[1-9]\d{4,14}$/.test(entry)))] : []
9169
+ },
9107
9170
  bot: {
9108
9171
  name: text4(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
9109
9172
  idMasked: text4(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
@@ -9144,7 +9207,7 @@ function formatRemaining6(milliseconds) {
9144
9207
  }
9145
9208
 
9146
9209
  // plugin-src/client/channels/whatsapp/index.js
9147
- var React18 = __toESM(require("react"), 1);
9210
+ var React19 = __toESM(require("react"), 1);
9148
9211
 
9149
9212
  // plugin-src/client/channels/whatsapp/styles.js
9150
9213
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@@ -9152,6 +9215,31 @@ var CSS10 = String.raw`
9152
9215
  .dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
9153
9216
  .dwa-avatar { color: #fff; background: #25d366; }
9154
9217
  .dwa-avatar svg { display: block; }
9218
+ .dwa-access { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
9219
+ .dwa-accessHeading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
9220
+ .dwa-accessHeading > strong { font-size: 13px; }
9221
+ .dwa-accessStatus { min-width: 0; display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px; }
9222
+ .dwa-accessBadge { flex: none; padding: 3px 8px; border-radius: 999px; color: #08785f; background: #eafbf0; font-size: 11px; font-weight: 700; }
9223
+ .dwa-accessBadge[data-mode="private-allowlist"] { color: #0f6f8f; background: #eaf7fd; }
9224
+ .dwa-accessBadge[data-mode="open"] { color: #a15c00; background: #fff3d6; }
9225
+ .dwa-accessHelp { position: relative; display: inline-flex; flex: none; }
9226
+ .dwa-accessHelpButton { width: 20px; height: 20px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #25d366 34%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #128c7e; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 1; font-weight: 750; cursor: help; }
9227
+ .dwa-accessTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: 270px; max-width: min(290px, calc(100vw - 48px)); display: grid; gap: 8px; padding: 10px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-primary, #1f2329); 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; }
9228
+ .dwa-accessTooltipItem { display: grid; gap: 2px; }
9229
+ .dwa-accessTooltipItem + .dwa-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
9230
+ .dwa-accessTooltipItem strong { font-size: 12px; line-height: 17px; }
9231
+ .dwa-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
9232
+ .dwa-accessHelp:hover .dwa-accessTooltip, .dwa-accessHelp:focus-within .dwa-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
9233
+ .dwa-accessField { display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
9234
+ .dwa-accessField select, .dwa-accessField textarea { width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
9235
+ .dwa-accessField select { height: 34px; padding: 0 9px; }
9236
+ .dwa-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
9237
+ .dwa-accessField textarea:disabled { color: var(--dsw-alias-label-tertiary, #8f959e); background: var(--dsw-alias-bg-module-platform, #f2f3f5); cursor: not-allowed; resize: none; opacity: 1; }
9238
+ .dwa-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
9239
+ .dwa-accessWarning, .dwa-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
9240
+ .dwa-accessWarning { color: #a15c00; }
9241
+ .dwa-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
9242
+ .dwa-accessActions { display: flex; justify-content: flex-end; }
9155
9243
  `;
9156
9244
  function installWhatsappStyles() {
9157
9245
  if (typeof document === "undefined") return () => {
@@ -9169,7 +9257,157 @@ function installWhatsappStyles() {
9169
9257
 
9170
9258
  // plugin-src/client/channels/whatsapp/index.js
9171
9259
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
9172
- var Button14 = React18.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
9260
+ function accessPolicyFor(account) {
9261
+ const accessMode = ["self-only", "private-allowlist", "open"].includes(
9262
+ account?.accessPolicy?.accessMode
9263
+ ) ? account.accessPolicy.accessMode : "self-only";
9264
+ return {
9265
+ accessMode,
9266
+ allowedNumbers: Array.isArray(account?.accessPolicy?.allowedNumbers) ? account.accessPolicy.allowedNumbers : []
9267
+ };
9268
+ }
9269
+ function allowedNumbersFromText(value) {
9270
+ const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
9271
+ const normalized = entries.map((entry) => entry.replace(/^\+/, ""));
9272
+ if (normalized.some((entry) => !/^[1-9]\d{4,14}$/.test(entry))) {
9273
+ throw new TypeError("\u7535\u8BDD\u53F7\u7801\u5FC5\u987B\u5305\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002");
9274
+ }
9275
+ return [...new Set(normalized)];
9276
+ }
9277
+ function WhatsappAccessSettings({ account, busy = false, onSave }) {
9278
+ const policy = accessPolicyFor(account);
9279
+ const sourceNumbers = policy.allowedNumbers.join("\n");
9280
+ const helpId = React19.useId();
9281
+ const [accessMode, setAccessMode] = React19.useState(policy.accessMode);
9282
+ const [allowedNumbers, setAllowedNumbers] = React19.useState(sourceNumbers);
9283
+ const [error, setError] = React19.useState(null);
9284
+ React19.useEffect(() => {
9285
+ setAccessMode(policy.accessMode);
9286
+ setAllowedNumbers(sourceNumbers);
9287
+ setError(null);
9288
+ }, [policy.accessMode, sourceNumbers]);
9289
+ const save = async (event) => {
9290
+ event.preventDefault();
9291
+ setError(null);
9292
+ try {
9293
+ const normalized = allowedNumbersFromText(allowedNumbers);
9294
+ if (typeof onSave !== "function") throw new Error("WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
9295
+ await onSave({ accessMode, allowedNumbers: normalized });
9296
+ } catch (caught) {
9297
+ setError(caught?.message ?? "WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
9298
+ }
9299
+ };
9300
+ const allowlistEnabled = accessMode === "private-allowlist";
9301
+ const labels = {
9302
+ "self-only": "\u4EC5\u81EA\u5DF1\u6A21\u5F0F",
9303
+ "private-allowlist": "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F",
9304
+ open: "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F"
9305
+ };
9306
+ return h2(
9307
+ "form",
9308
+ { className: "dwa-access", onSubmit: save },
9309
+ h2(
9310
+ "div",
9311
+ { className: "dwa-accessHeading" },
9312
+ h2("strong", null, "\u8BBF\u95EE\u8BBE\u7F6E"),
9313
+ h2(
9314
+ "span",
9315
+ { className: "dwa-accessStatus" },
9316
+ h2(
9317
+ "span",
9318
+ { className: "dwa-accessBadge", "data-mode": policy.accessMode },
9319
+ ["\u5DF2\u751F\u6548\uFF1A", labels[policy.accessMode]]
9320
+ ),
9321
+ h2(
9322
+ "span",
9323
+ { className: "dwa-accessHelp" },
9324
+ h2("button", {
9325
+ type: "button",
9326
+ className: "dwa-accessHelpButton",
9327
+ "aria-label": "\u67E5\u770B WhatsApp \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E",
9328
+ "aria-describedby": helpId
9329
+ }, h2("span", { "aria-hidden": "true" }, "?")),
9330
+ h2(
9331
+ "span",
9332
+ { id: helpId, className: "dwa-accessTooltip", role: "tooltip" },
9333
+ h2(
9334
+ "span",
9335
+ { className: "dwa-accessTooltipItem" },
9336
+ h2("strong", null, "\u4EC5\u81EA\u5DF1\u6A21\u5F0F"),
9337
+ h2("span", null, "\u53EA\u54CD\u5E94\u5DF2\u7ED1\u5B9A WhatsApp \u8D26\u53F7\u7684\u81EA\u804A\u6D88\u606F\u3002")
9338
+ ),
9339
+ h2(
9340
+ "span",
9341
+ { className: "dwa-accessTooltipItem" },
9342
+ h2("strong", null, "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F"),
9343
+ h2("span", null, "\u54CD\u5E94\u81EA\u804A\u548C\u767D\u540D\u5355\u8054\u7CFB\u4EBA\u7684\u79C1\u804A\uFF0C\u5FFD\u7565\u7FA4\u804A\u3002")
9344
+ ),
9345
+ h2(
9346
+ "span",
9347
+ { className: "dwa-accessTooltipItem" },
9348
+ h2("strong", null, "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F"),
9349
+ h2("span", null, "\u54CD\u5E94\u6240\u6709\u79C1\u804A\uFF0C\u4EE5\u53CA\u7FA4\u804A\u4E2D\u7684\u63D0\u53CA\u6216\u56DE\u590D\u3002")
9350
+ )
9351
+ )
9352
+ )
9353
+ )
9354
+ ),
9355
+ h2(
9356
+ "label",
9357
+ { className: "dwa-accessField" },
9358
+ h2("span", null, "\u6A21\u5F0F"),
9359
+ h2(
9360
+ "select",
9361
+ {
9362
+ value: accessMode,
9363
+ disabled: busy,
9364
+ "aria-label": "WhatsApp \u8BBF\u95EE\u6A21\u5F0F",
9365
+ onChange: (event) => {
9366
+ setAccessMode(event.target.value);
9367
+ setError(null);
9368
+ }
9369
+ },
9370
+ h2("option", { value: "self-only" }, "\u4EC5\u81EA\u5DF1\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09"),
9371
+ h2("option", { value: "private-allowlist" }, "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F"),
9372
+ h2("option", { value: "open" }, "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F")
9373
+ )
9374
+ ),
9375
+ allowlistEnabled ? h2(
9376
+ "label",
9377
+ { className: "dwa-accessField" },
9378
+ h2("span", null, "\u5141\u8BB8\u79C1\u804A\u7684 WhatsApp \u7535\u8BDD\u53F7\u7801"),
9379
+ h2("textarea", {
9380
+ value: allowedNumbers,
9381
+ disabled: busy,
9382
+ rows: 3,
9383
+ placeholder: "\u6BCF\u884C\u4E00\u4E2A\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\u7684\u53F7\u7801",
9384
+ "aria-label": "\u5141\u8BB8\u79C1\u804A\u7684 WhatsApp \u7535\u8BDD\u53F7\u7801",
9385
+ onChange: (event) => {
9386
+ setAllowedNumbers(event.target.value);
9387
+ setError(null);
9388
+ }
9389
+ }),
9390
+ h2("small", null, "\u53EF\u4EE5\u5305\u542B\u5F00\u5934\u7684 +\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u81EA\u52A8\u79FB\u9664\u3002")
9391
+ ) : null,
9392
+ allowlistEnabled && allowedNumbers.trim() === "" ? h2(
9393
+ "p",
9394
+ { className: "dwa-accessWarning", role: "status" },
9395
+ "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u5C06\u53EA\u63A5\u53D7\u81EA\u804A\u6D88\u606F\u3002"
9396
+ ) : null,
9397
+ error ? h2("p", { className: "dwa-accessError", role: "alert" }, error) : null,
9398
+ h2(
9399
+ "div",
9400
+ { className: "dwa-accessActions" },
9401
+ h2("button", {
9402
+ type: "submit",
9403
+ className: "ddt-button",
9404
+ "data-kind": "secondary",
9405
+ disabled: busy
9406
+ }, busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
9407
+ )
9408
+ );
9409
+ }
9410
+ var Button14 = React19.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
9173
9411
  return h2("button", {
9174
9412
  ...props,
9175
9413
  ref,
@@ -9392,6 +9630,7 @@ function WhatsappAccountCard({
9392
9630
  onReconnect,
9393
9631
  onWorkspaceSave,
9394
9632
  onAgentPresetSave,
9633
+ onAccessPolicySave,
9395
9634
  onRequestRemove,
9396
9635
  onConfirmRemove,
9397
9636
  onCancelRemove
@@ -9423,28 +9662,14 @@ function WhatsappAccountCard({
9423
9662
  h2("p", null, account.bot.idMasked)
9424
9663
  )
9425
9664
  ),
9426
- h2(
9427
- "div",
9428
- { className: "ddt-health dim-botHealth" },
9429
- h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
9430
- h2("span", null, stateLabel2)
9431
- )
9432
- ),
9433
- h2(
9434
- "dl",
9435
- { className: "ddt-metrics dim-botMetrics" },
9436
- h2(
9437
- "div",
9438
- { className: "ddt-metric dim-botMetric" },
9439
- h2("dt", null, "\u6D88\u606F\u901A\u9053"),
9440
- h2("dd", null, account.connected ? "WhatsApp Web" : "\u79BB\u7EBF")
9441
- ),
9442
- h2(
9443
- "div",
9444
- { className: "ddt-metric dim-botMetric" },
9445
- h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"),
9446
- h2("dd", null, checkedTime6(account.health.lastCheckedAt))
9447
- )
9665
+ h2(BotStatusMeta, {
9666
+ className: "ddt-health",
9667
+ dotClassName: "ddt-dot",
9668
+ tone,
9669
+ stateLabel: stateLabel2,
9670
+ lastCheckedAt: account.health.lastCheckedAt,
9671
+ formatCheckedTime: checkedTime6
9672
+ })
9448
9673
  ),
9449
9674
  h2(WorkspaceEditor, {
9450
9675
  workspace: account.workspace,
@@ -9456,28 +9681,37 @@ function WhatsappAccountCard({
9456
9681
  disabled: Boolean(busy),
9457
9682
  onSave: onAgentPresetSave
9458
9683
  }),
9684
+ h2(WhatsappAccessSettings, {
9685
+ account,
9686
+ busy: Boolean(busy),
9687
+ onSave: onAccessPolicySave
9688
+ }),
9459
9689
  h2(
9460
9690
  "div",
9461
9691
  { className: "ddt-accountFooter dim-cardFooter" },
9462
- summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
9463
- testNotice ? h2("div", {
9464
- className: "ddt-summary dim-cardSummary",
9465
- role: "status"
9466
- }, testNotice) : null,
9467
9692
  h2(
9468
9693
  "div",
9469
- { className: "ddt-actions dim-cardActions" },
9470
- h2(Button14, {
9471
- className: "dim-cardAction",
9472
- onClick: onReconnect,
9473
- disabled: Boolean(busy)
9474
- }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
9475
- h2(Button14, {
9476
- className: "dim-cardAction",
9477
- kind: "danger",
9478
- onClick: onRequestRemove,
9479
- disabled: Boolean(busy)
9480
- }, "\u79FB\u9664\u63A5\u5165")
9694
+ { className: "dim-cardFooterLayout" },
9695
+ h2(
9696
+ "div",
9697
+ { className: "ddt-actions dim-cardActions" },
9698
+ h2(Button14, {
9699
+ className: "dim-cardAction",
9700
+ onClick: onReconnect,
9701
+ disabled: Boolean(busy)
9702
+ }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
9703
+ h2(Button14, {
9704
+ className: "dim-cardAction",
9705
+ kind: "danger",
9706
+ onClick: onRequestRemove,
9707
+ disabled: Boolean(busy)
9708
+ }, "\u79FB\u9664\u63A5\u5165")
9709
+ ),
9710
+ summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
9711
+ testNotice ? h2("div", {
9712
+ className: "ddt-summary dim-cardFeedback",
9713
+ role: "status"
9714
+ }, testNotice) : null
9481
9715
  )
9482
9716
  )
9483
9717
  ),
@@ -9490,23 +9724,23 @@ function WhatsappAccountCard({
9490
9724
  );
9491
9725
  }
9492
9726
  function WhatsappSettingsTab({ rpcCall }) {
9493
- const [model, setModel] = React18.useState({
9727
+ const [model, setModel] = React19.useState({
9494
9728
  phase: "loading",
9495
9729
  bots: [],
9496
9730
  totals: { configured: 0, connected: 0 },
9497
9731
  error: null,
9498
9732
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
9499
9733
  });
9500
- const [provision, setProvision] = React18.useState(null);
9501
- const [busy, setBusy] = React18.useState(false);
9502
- const [busyByBot, setBusyByBot] = React18.useState({});
9503
- const [testNoticeByBot, setTestNoticeByBot] = React18.useState({});
9504
- const [removeTarget, setRemoveTarget] = React18.useState(null);
9505
- const [now, setNow] = React18.useState(Date.now());
9506
- const mounted = React18.useRef(true);
9734
+ const [provision, setProvision] = React19.useState(null);
9735
+ const [busy, setBusy] = React19.useState(false);
9736
+ const [busyByBot, setBusyByBot] = React19.useState({});
9737
+ const [testNoticeByBot, setTestNoticeByBot] = React19.useState({});
9738
+ const [removeTarget, setRemoveTarget] = React19.useState(null);
9739
+ const [now, setNow] = React19.useState(Date.now());
9740
+ const mounted = React19.useRef(true);
9507
9741
  const workspaceFence = useWorkspaceSnapshotFence();
9508
- const addButtonRef = React18.useRef(null);
9509
- React18.useEffect(() => {
9742
+ const addButtonRef = React19.useRef(null);
9743
+ React19.useEffect(() => {
9510
9744
  const disposeDingtalk = installDingtalkStyles();
9511
9745
  const disposeWhatsapp = installWhatsappStyles();
9512
9746
  mounted.current = true;
@@ -9516,11 +9750,11 @@ function WhatsappSettingsTab({ rpcCall }) {
9516
9750
  disposeDingtalk();
9517
9751
  };
9518
9752
  }, []);
9519
- const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
9753
+ const invoke = React19.useCallback(async (endpoint, payload = {}, signal) => {
9520
9754
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
9521
9755
  return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
9522
9756
  }, [rpcCall]);
9523
- const loadStatus = React18.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9757
+ const loadStatus = React19.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9524
9758
  const workspaceVersion = workspaceFence.beginStatus();
9525
9759
  if (workspaceVersion === null) return void 0;
9526
9760
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -9550,12 +9784,12 @@ function WhatsappSettingsTab({ rpcCall }) {
9550
9784
  return void 0;
9551
9785
  }
9552
9786
  }, [invoke, workspaceFence]);
9553
- React18.useEffect(() => {
9787
+ React19.useEffect(() => {
9554
9788
  const controller = new AbortController();
9555
9789
  void loadStatus({ signal: controller.signal, restore: true });
9556
9790
  return () => controller.abort();
9557
9791
  }, [loadStatus]);
9558
- React18.useEffect(() => {
9792
+ React19.useEffect(() => {
9559
9793
  if (model.phase !== "ready") return void 0;
9560
9794
  const controller = new AbortController();
9561
9795
  const timer = window.setInterval(
@@ -9567,12 +9801,12 @@ function WhatsappSettingsTab({ rpcCall }) {
9567
9801
  window.clearInterval(timer);
9568
9802
  };
9569
9803
  }, [loadStatus, model.phase]);
9570
- React18.useEffect(() => {
9804
+ React19.useEffect(() => {
9571
9805
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
9572
9806
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
9573
9807
  return () => window.clearInterval(timer);
9574
9808
  }, [provision?.attemptId, provision?.status]);
9575
- const startProvisioning = React18.useCallback(async (replace = false) => {
9809
+ const startProvisioning = React19.useCallback(async (replace = false) => {
9576
9810
  setBusy(true);
9577
9811
  try {
9578
9812
  if (replace && provision?.attemptId) {
@@ -9590,7 +9824,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9590
9824
  if (mounted.current) setBusy(false);
9591
9825
  }
9592
9826
  }, [invoke, provision?.attemptId]);
9593
- const closeProvision = React18.useCallback(async () => {
9827
+ const closeProvision = React19.useCallback(async () => {
9594
9828
  setBusy(true);
9595
9829
  try {
9596
9830
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -9601,7 +9835,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9601
9835
  if (mounted.current) setBusy(false);
9602
9836
  }
9603
9837
  }, [invoke, provision?.attemptId, provision?.status]);
9604
- React18.useEffect(() => {
9838
+ React19.useEffect(() => {
9605
9839
  const attemptId = provision?.attemptId;
9606
9840
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
9607
9841
  const controller = new AbortController();
@@ -9642,7 +9876,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9642
9876
  if (timer) window.clearTimeout(timer);
9643
9877
  };
9644
9878
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
9645
- const botAction = React18.useCallback(async (account, operation, endpoint, payload) => {
9879
+ const botAction = React19.useCallback(async (account, operation, endpoint, payload) => {
9646
9880
  const snapshotVersion = workspaceFence.beginMutation();
9647
9881
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
9648
9882
  if (operation === "reconnect") {
@@ -9691,11 +9925,11 @@ function WhatsappSettingsTab({ rpcCall }) {
9691
9925
  const botList = model.bots.length > 0 ? h2(
9692
9926
  "section",
9693
9927
  { className: "dim-listSection" },
9694
- h2(
9695
- "div",
9696
- { className: "ddt-listHeading dim-listHeading" },
9697
- h2("h3", null, "\u5DF2\u63A5\u5165\u7684 WhatsApp \u673A\u5668\u4EBA")
9698
- ),
9928
+ h2(ChannelListHeading, {
9929
+ className: "ddt-listHeading",
9930
+ title: "\u5DF2\u63A5\u5165\u7684 WhatsApp \u673A\u5668\u4EBA",
9931
+ connectionLabel: "WhatsApp Web"
9932
+ }),
9699
9933
  h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(WhatsappAccountCard, {
9700
9934
  account,
9701
9935
  busy: busyByBot[account.botId],
@@ -9719,6 +9953,12 @@ function WhatsappSettingsTab({ rpcCall }) {
9719
9953
  WHATSAPP_ENDPOINTS.setAgentPreset,
9720
9954
  { botId: account.botId, agentPreset }
9721
9955
  ),
9956
+ onAccessPolicySave: (accessPolicy) => botAction(
9957
+ account,
9958
+ "access",
9959
+ WHATSAPP_ENDPOINTS.setAccessPolicy,
9960
+ { botId: account.botId, ...accessPolicy }
9961
+ ),
9722
9962
  onRequestRemove: () => setRemoveTarget(account.botId),
9723
9963
  onCancelRemove: () => setRemoveTarget(null),
9724
9964
  onConfirmRemove: async () => {
@@ -9755,7 +9995,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9755
9995
  h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
9756
9996
  )
9757
9997
  ) : h2(
9758
- React18.Fragment,
9998
+ React19.Fragment,
9759
9999
  null,
9760
10000
  provision?.status === "pending" ? h2(QrPanel5, {
9761
10001
  provision,
@@ -9863,6 +10103,14 @@ var CSS11 = String.raw`
9863
10103
  .dim-panel .dim-listSection { display: flex; flex-direction: column; gap: 0; }
9864
10104
  .dim-panel .dim-listHeading { min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 6px; padding: 0; }
9865
10105
  .dim-panel .dim-listHeading h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: normal; font-weight: 650; }
10106
+ .dim-panel .dim-listTitle { min-width: 0; display: inline-flex; align-items: center; gap: 6px; }
10107
+ .dim-panel .dim-channelHelp { position: relative; display: inline-flex; flex: none; }
10108
+ .dim-panel .dim-channelHelpButton { width: 17px; height: 17px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #1677ff 28%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #1677ff; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 11px; line-height: 1; font-weight: 700; cursor: help; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
10109
+ .dim-panel .dim-channelHelpButton:hover { border-color: #1677ff; color: #0f5fce; background: color-mix(in srgb, #1677ff 8%, var(--dsw-alias-bg-layer-1, #fff)); }
10110
+ .dim-panel .dim-channelHelpButton:focus-visible { outline: none; border-color: #1677ff; box-shadow: 0 0 0 3px color-mix(in srgb, #1677ff 16%, transparent); }
10111
+ .dim-panel .dim-channelTooltip { position: absolute; top: calc(100% + 7px); left: 0; z-index: 30; width: max-content; max-width: min(280px, calc(100vw - 48px)); display: flex; align-items: baseline; gap: 5px; padding: 8px 10px; 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-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); font-size: 11px; line-height: 16px; font-weight: 400; white-space: normal; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
10112
+ .dim-panel .dim-channelTooltip strong { color: var(--dsw-alias-label-primary, #1f2329); font-weight: 600; white-space: nowrap; }
10113
+ .dim-panel .dim-channelHelp:hover .dim-channelTooltip, .dim-panel .dim-channelHelp:focus-within .dim-channelTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
9866
10114
  .dim-panel .dim-botList { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
9867
10115
  .dim-panel .dim-loadingView { padding: 38px; color: var(--dsw-alias-label-secondary, #646a73); text-align: center; }
9868
10116
  .dim-panel .dim-loadingView h3 { margin: 0 0 7px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 17px; line-height: normal; font-weight: 650; }
@@ -9919,13 +10167,18 @@ var CSS11 = String.raw`
9919
10167
  .dim-panel .dim-workspaceEdit:disabled { cursor: not-allowed; opacity: .55; }
9920
10168
  .dim-panel .dim-workspacePath { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; display: block; overflow-x: auto; overflow-y: hidden; color: var(--dsw-alias-label-primary, #1f2329); font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
9921
10169
  .dim-panel .dim-preset { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; column-gap: 10px; row-gap: 4px; margin-top: 6px; padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
9922
- .dim-panel .dim-presetHeader { display: contents; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
9923
- .dim-panel .dim-presetHeader > span { grid-column: 1; grid-row: 1; white-space: nowrap; }
10170
+ .dim-panel .dim-presetHeader { position: relative; min-width: 0; grid-column: 1 / -1; grid-row: 1; display: flex; align-items: center; justify-content: space-between; gap: 10px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
10171
+ .dim-panel .dim-presetTitle { min-width: 0; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
10172
+ .dim-panel .dim-presetHelp { display: inline-flex; align-items: center; flex: none; }
10173
+ .dim-panel .dim-presetHelpButton { width: 17px; height: 17px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #1677ff 28%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #1677ff; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 11px; line-height: 1; font-weight: 700; cursor: help; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
10174
+ .dim-panel .dim-presetHelpButton:hover { border-color: #1677ff; color: #0f5fce; background: color-mix(in srgb, #1677ff 8%, var(--dsw-alias-bg-layer-1, #fff)); }
10175
+ .dim-panel .dim-presetHelpButton:focus-visible { outline: none; border-color: #1677ff; box-shadow: 0 0 0 3px color-mix(in srgb, #1677ff 16%, transparent); }
10176
+ .dim-panel .dim-presetTooltip { position: absolute; top: calc(100% + 7px); left: 0; z-index: 30; width: min(320px, 100%); padding: 9px 10px; 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-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); font-size: 11px; line-height: 16px; font-weight: 400; overflow-wrap: anywhere; white-space: normal; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
10177
+ .dim-panel .dim-presetHelp:hover .dim-presetTooltip, .dim-panel .dim-presetHelp:focus-within .dim-presetTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
9924
10178
  .dim-panel .dim-presetStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
9925
10179
  .dim-panel .dim-presetSelect { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; height: 30px; padding: 0 8px; 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-1, #fff); font: inherit; font-size: 12px; }
9926
10180
  .dim-panel .dim-presetSelect:disabled { cursor: not-allowed; opacity: .55; }
9927
- .dim-panel .dim-presetHelp { grid-column: 1 / -1; grid-row: 3; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 1.4; }
9928
- .dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 4; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
10181
+ .dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 3; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
9929
10182
  .dim-directoryPickerBackdrop { --dim-blue: var(--dsw-alias-state-business-primary, #3370ff); --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent); position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; background: rgb(15 17 21 / 42%); backdrop-filter: blur(3px); }
9930
10183
  .dim-directoryPickerBackdrop, .dim-directoryPickerBackdrop *, .dim-directoryPickerBackdrop *::before, .dim-directoryPickerBackdrop *::after { box-sizing: border-box; }
9931
10184
  .dim-directoryPicker { width: min(720px, 100%); height: min(620px, calc(100vh - 48px)); min-height: 420px; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 18px; outline: none; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 24px 72px rgb(15 17 21 / 24%); }
@@ -9968,6 +10221,9 @@ var CSS11 = String.raw`
9968
10221
  .dim-directoryPickerActions button:hover:not(:disabled) { filter: brightness(.97); }
9969
10222
  .dim-directoryPickerActions button:disabled { cursor: not-allowed; opacity: .52; }
9970
10223
  .dim-panel .dim-cardSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; }
10224
+ .dim-panel .dim-cardFooterLayout { min-width: 0; width: 100%; display: flex; flex-direction: column; align-items: stretch; gap: 9px; }
10225
+ .dim-panel .dim-cardFooterLayout > .dim-cardActions { align-self: flex-end; }
10226
+ .dim-panel .dim-cardFeedback { width: 100%; padding: 8px 10px; border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f7f8fa); font: inherit; font-size: 12px; font-weight: 400; line-height: 18px; overflow-wrap: anywhere; white-space: normal; }
9971
10227
  .dim-panel .dim-cardActions { flex: none; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; margin: 0 0 0 auto; }
9972
10228
  .dim-panel .dim-cardActions .dim-cardAction { flex: none; min-height: 32px; padding: 0 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; }
9973
10229
  .dim-panel .dim-cardActions .dim-cardAction:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
@@ -9982,15 +10238,13 @@ var CSS11 = String.raw`
9982
10238
  .dim-panel .dim-botName { min-width: 0; }
9983
10239
  .dim-panel .dim-botName h3 { overflow: hidden; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; font-weight: 650; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
9984
10240
  .dim-panel .dim-botName p { overflow: hidden; margin: 4px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
10241
+ .dim-panel .dim-botHealthGroup { flex: none; display: grid; justify-items: end; gap: 5px; }
9985
10242
  .dim-panel .dim-botCard .dim-botHealth { flex: none; min-height: 0; display: inline-flex; align-items: center; gap: 7px; padding: 0; border: 0; border-radius: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; }
10243
+ .dim-panel .dim-lastChecked { display: inline-flex; align-items: baseline; gap: 4px; color: var(--dsw-alias-label-tertiary, #8f959e); font: inherit; font-size: 11px; font-weight: 400; line-height: normal; white-space: nowrap; }
9986
10244
  .dim-panel .dim-botCard .dim-healthDot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: #aeb3bb; box-shadow: none; }
9987
10245
  .dim-panel .dim-botCard .dim-healthDot[data-tone="success"] { background: var(--dsw-alias-state-success-primary, #20a162); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-success-primary, #20a162) 14%, transparent); }
9988
10246
  .dim-panel .dim-botCard .dim-healthDot[data-tone="warning"] { background: var(--dsw-alias-state-warn-primary, #d97706); }
9989
10247
  .dim-panel .dim-botCard .dim-healthDot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #d54941); }
9990
- .dim-panel .dim-botMetrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 6px 0; }
9991
- .dim-panel .dim-botMetric { min-width: 0; padding: 6px 8px; border: 0; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
9992
- .dim-panel .dim-botMetric dt { margin: 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; font-weight: 400; line-height: normal; }
9993
- .dim-panel .dim-botMetric dd { overflow: hidden; margin: 3px 0 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 400; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
9994
10248
  .dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
9995
10249
  .dim-panel .ddt-headingCopy { display: none; }
9996
10250
  .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
@@ -10021,6 +10275,7 @@ var CSS11 = String.raw`
10021
10275
  }
10022
10276
  @media (max-width: 720px) {
10023
10277
  .dim-panel .dim-botCardTop { flex-direction: column; align-items: stretch; }
10278
+ .dim-panel .dim-botHealthGroup { justify-items: start; }
10024
10279
  }
10025
10280
  @media (max-width: 560px) {
10026
10281
  .dim-title { flex-direction: column; gap: 10px; }
@@ -10156,8 +10411,8 @@ function IMSettingsTab({
10156
10411
  officeRpcCall,
10157
10412
  workspaceDirectoryPicker
10158
10413
  }) {
10159
- const [selected, setSelected] = React19.useState("weixin");
10160
- const githubTooltipId = React19.useId();
10414
+ const [selected, setSelected] = React20.useState("weixin");
10415
+ const githubTooltipId = React20.useId();
10161
10416
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
10162
10417
  return h2(
10163
10418
  WorkspaceDirectoryPickerContext.Provider,