@xmanrui/dsh-im 4.34.0 → 4.34.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -590,12 +590,12 @@ async function callManagementRpc(connection, channel7, method, payload, signal)
590
590
  }
591
591
 
592
592
  // plugin-src/client/index.js
593
- var React38 = __toESM(require("react"), 1);
593
+ var React39 = __toESM(require("react"), 1);
594
594
 
595
595
  // package.json
596
596
  var package_default = {
597
597
  name: "@xmanrui/dsh-im",
598
- version: "4.34.0",
598
+ version: "4.34.2",
599
599
  icon: "./assets/logo-plugin-message-link-gradient.webp",
600
600
  description: "\u628A\u5FAE\u4FE1\u3001\u98DE\u4E66\u3001\u4F01\u5FAE\u3001\u9489\u9489\u3001QQ\u3001Telegram\u3001WhatsApp\u3001Slack\u3001Discord\u3001Matrix\u63A5\u5165DSH\u3002 Connect Telegram, WhatsApp, Slack, Discord, Matrix, WeChat, Feishu, WeCom, DingTalk, and QQ to DSH.",
601
601
  keywords: [
@@ -1193,6 +1193,8 @@ var EN = Object.freeze({
1193
1193
  "IM \u9762\u677F\u52A0\u8F7D\u5931\u8D25": "Could not load the IM panel",
1194
1194
  "\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E": "More bot settings",
1195
1195
  "\u66F4\u591A\u8BBE\u7F6E": "More settings",
1196
+ "\u5411\u5DE6\u6EDA\u52A8\u8BBE\u7F6E\u9875\u7B7E": "Scroll settings tabs left",
1197
+ "\u5411\u53F3\u6EDA\u52A8\u8BBE\u7F6E\u9875\u7B7E": "Scroll settings tabs right",
1196
1198
  "\u5E38\u7528\u914D\u7F6E": "Common settings",
1197
1199
  "\u673A\u5668\u4EBA\u8BBE\u7F6E": "Bot settings",
1198
1200
  "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E": "Bot settings tabs",
@@ -1213,6 +1215,11 @@ var EN = Object.freeze({
1213
1215
  "\u5C1A\u672A\u6DFB\u52A0\u7528\u6237": "No users added",
1214
1216
  "\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A": "Group chat is not supported by this channel",
1215
1217
  "\u539F\u6240\u6709\u8005\u6216\u626B\u7801\u63A5\u5165\u8005\u59CB\u7EC8\u53EF\u4EE5\u8BBF\u95EE\u5E76\u6267\u884C\u547D\u4EE4\uFF1B\u4EE5\u4E0B\u8BBE\u7F6E\u4EC5\u7EA6\u675F\u5176\u4ED6\u7528\u6237\u3002": "The original owner or QR-code operator can always access the bot and run commands; the settings below apply only to other users.",
1218
+ "\u5DF2\u7531\u63A5\u5165\u6D41\u7A0B\u786E\u8BA4\u7684\u6240\u6709\u8005\u53EF\u4EE5\u8BBF\u95EE\u5E76\u6267\u884C\u547D\u4EE4\uFF0C\u4E0D\u53D7\u672C\u9875\u9650\u5236\u3002\u672A\u8BC6\u522B\u5230\u6240\u6709\u8005\u8EAB\u4EFD\u65F6\uFF0C\u6309\u672C\u9875\u8BBF\u95EE\u8BBE\u7F6E\u6267\u884C\u3002": "Owners confirmed during connection setup can access the bot and run commands regardless of these settings. When no owner has been identified, these access settings apply.",
1219
+ "\u767D\u540D\u5355\u8BF7\u586B\u5199\u4F7F\u7528\u8005\u7684 Open ID\uFF0C\u4E0D\u662F\u673A\u5668\u4EBA\u7684 Open ID\u3002\u4EC5\u4FDD\u5B58\u901A\u914D\u7B26\u7684\u63A5\u5165\u914D\u7F6E\u4E0D\u5305\u542B\u6240\u6709\u8005\u8EAB\u4EFD\u3002": "Enter the user\u2019s Open ID in the allowlist, not the bot\u2019s Open ID. A connection configured with only a wildcard has no identified owner.",
1220
+ "\u626B\u7801\u83B7\u5F97\u7684\u662F\u79C1\u804A\u8EAB\u4EFD\uFF0C\u7FA4\u804A\u53EA\u6709\u5339\u914D\u5DF2\u786E\u8BA4\u7684\u6240\u6709\u8005\u8EAB\u4EFD\u65F6\u624D\u8C41\u514D\uFF1B\u5176\u4ED6\u6210\u5458\u6309\u7FA4\u804A\u767D\u540D\u5355\u8BBE\u7F6E\u6267\u884C\u3002": "QR setup identifies the owner for direct messages. In groups, only a matching confirmed owner identity is exempt; other members follow the group allowlist settings.",
1221
+ "\u586B\u5199\u4F7F\u7528\u8005\u7684 Open ID\uFF08ou_xxx\uFF09\uFF0C\u4E0D\u662F\u673A\u5668\u4EBA ID": "Enter the user\u2019s Open ID (ou_xxx), not the bot ID",
1222
+ "\u8FD9\u91CC\u9700\u8981\u586B\u5199\u7528\u6237\u7684 Open ID\uFF0C\u4E0D\u80FD\u586B\u5199\u673A\u5668\u4EBA\u81EA\u5DF1\u7684 Open ID\u3002": "Enter a user Open ID here, not the bot\u2019s own Open ID.",
1216
1223
  "\u6B64\u533A\u57DF\u65E0\u9700\u914D\u7F6E\uFF0C\u4FDD\u5B58\u79C1\u804A\u8BBE\u7F6E\u65F6\u4F1A\u4FDD\u7559\u73B0\u6709\u7FA4\u804A\u7B56\u7565\u3002": "No setup is needed here. Saving direct-message settings keeps the existing group policy.",
1217
1224
  "\u8BBF\u95EE\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002": "Access settings saved.",
1218
1225
  "\u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Access settings are currently unavailable.",
@@ -16173,11 +16180,12 @@ function installInterfaceLanguageMirror(ctx, {
16173
16180
  }
16174
16181
 
16175
16182
  // plugin-src/client/delivery-settings.js
16176
- var React33 = __toESM(require("react"), 1);
16183
+ var React34 = __toESM(require("react"), 1);
16177
16184
 
16178
16185
  // plugin-src/client/access-policy-settings.js
16179
16186
  var React28 = __toESM(require("react"), 1);
16180
16187
  var ACCESS_POLICY_ENDPOINT = "bot.access-policy.set";
16188
+ var CONFIRMED_OWNER_CHANNELS = /* @__PURE__ */ new Set(["weixin", "feishu", "qq", "whatsapp", "discord"]);
16181
16189
  var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
16182
16190
  weixin: Object.freeze({
16183
16191
  directUserLabel: "\u5FAE\u4FE1\u7528\u6237 ID",
@@ -16186,9 +16194,9 @@ var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
16186
16194
  }),
16187
16195
  feishu: Object.freeze({
16188
16196
  directUserLabel: "\u98DE\u4E66 Open ID",
16189
- directPlaceholder: "ou_xxx",
16197
+ directPlaceholder: "\u586B\u5199\u4F7F\u7528\u8005\u7684 Open ID\uFF08ou_xxx\uFF09\uFF0C\u4E0D\u662F\u673A\u5668\u4EBA ID",
16190
16198
  groupUserLabel: "\u7FA4\u6210\u5458 Open ID",
16191
- groupPlaceholder: "ou_xxx"
16199
+ groupPlaceholder: "\u586B\u5199\u4F7F\u7528\u8005\u7684 Open ID\uFF08ou_xxx\uFF09\uFF0C\u4E0D\u662F\u673A\u5668\u4EBA ID"
16192
16200
  }),
16193
16201
  dingtalk: Object.freeze({
16194
16202
  directUserLabel: "\u9489\u9489\u7528\u6237 ID",
@@ -16287,6 +16295,8 @@ function ScenePolicyEditor({
16287
16295
  placeholder,
16288
16296
  disabled = false,
16289
16297
  unsupported = false,
16298
+ ownerHelp,
16299
+ identityHelp,
16290
16300
  onChange
16291
16301
  }) {
16292
16302
  const ownerHelpId = React28.useId();
@@ -16332,7 +16342,7 @@ function ScenePolicyEditor({
16332
16342
  id: ownerHelpId,
16333
16343
  className: "dim-channelTooltip dim-accessHelpTooltip",
16334
16344
  role: "tooltip"
16335
- }, "\u539F\u6240\u6709\u8005\u6216\u626B\u7801\u63A5\u5165\u8005\u59CB\u7EC8\u53EF\u4EE5\u8BBF\u95EE\u5E76\u6267\u884C\u547D\u4EE4\uFF1B\u4EE5\u4E0B\u8BBE\u7F6E\u4EC5\u7EA6\u675F\u5176\u4ED6\u7528\u6237\u3002")
16345
+ }, ownerHelp, identityHelp ? h2("br") : null, identityHelp)
16336
16346
  )
16337
16347
  )
16338
16348
  ),
@@ -16481,6 +16491,8 @@ function ScenePolicyEditor({
16481
16491
  }
16482
16492
  function AccessPolicySettingsPage({ channel: channel7, account, rpcCall, onSaved }) {
16483
16493
  const definition = ACCESS_CHANNEL_DEFINITIONS[channel7];
16494
+ const ownerHelp = CONFIRMED_OWNER_CHANNELS.has(channel7) ? "\u5DF2\u7531\u63A5\u5165\u6D41\u7A0B\u786E\u8BA4\u7684\u6240\u6709\u8005\u53EF\u4EE5\u8BBF\u95EE\u5E76\u6267\u884C\u547D\u4EE4\uFF0C\u4E0D\u53D7\u672C\u9875\u9650\u5236\u3002\u672A\u8BC6\u522B\u5230\u6240\u6709\u8005\u8EAB\u4EFD\u65F6\uFF0C\u6309\u672C\u9875\u8BBF\u95EE\u8BBE\u7F6E\u6267\u884C\u3002" : "\u539F\u6240\u6709\u8005\u6216\u626B\u7801\u63A5\u5165\u8005\u59CB\u7EC8\u53EF\u4EE5\u8BBF\u95EE\u5E76\u6267\u884C\u547D\u4EE4\uFF1B\u4EE5\u4E0B\u8BBE\u7F6E\u4EC5\u7EA6\u675F\u5176\u4ED6\u7528\u6237\u3002";
16495
+ const identityHelp = channel7 === "feishu" ? "\u767D\u540D\u5355\u8BF7\u586B\u5199\u4F7F\u7528\u8005\u7684 Open ID\uFF0C\u4E0D\u662F\u673A\u5668\u4EBA\u7684 Open ID\u3002\u4EC5\u4FDD\u5B58\u901A\u914D\u7B26\u7684\u63A5\u5165\u914D\u7F6E\u4E0D\u5305\u542B\u6240\u6709\u8005\u8EAB\u4EFD\u3002" : null;
16484
16496
  const initialPolicy = normalizeAccessPolicy(account?.accessPolicy);
16485
16497
  const initialKey = JSON.stringify(initialPolicy);
16486
16498
  const [draft, setDraft] = React28.useState(() => clonePolicy(
@@ -16544,6 +16556,8 @@ function AccessPolicySettingsPage({ channel: channel7, account, rpcCall, onSaved
16544
16556
  policy: draft.direct,
16545
16557
  userLabel: definition.directUserLabel,
16546
16558
  placeholder: definition.directPlaceholder,
16559
+ ownerHelp,
16560
+ identityHelp,
16547
16561
  disabled: saving,
16548
16562
  onChange: (direct) => {
16549
16563
  setDraft((current) => ({ ...current, direct }));
@@ -16556,6 +16570,8 @@ function AccessPolicySettingsPage({ channel: channel7, account, rpcCall, onSaved
16556
16570
  policy: draft.group,
16557
16571
  userLabel: definition.groupUserLabel ?? definition.directUserLabel,
16558
16572
  placeholder: definition.groupPlaceholder ?? definition.directPlaceholder,
16573
+ ownerHelp,
16574
+ identityHelp: channel7 === "qq" ? "\u626B\u7801\u83B7\u5F97\u7684\u662F\u79C1\u804A\u8EAB\u4EFD\uFF0C\u7FA4\u804A\u53EA\u6709\u5339\u914D\u5DF2\u786E\u8BA4\u7684\u6240\u6709\u8005\u8EAB\u4EFD\u65F6\u624D\u8C41\u514D\uFF1B\u5176\u4ED6\u6210\u5458\u6309\u7FA4\u804A\u767D\u540D\u5355\u8BBE\u7F6E\u6267\u884C\u3002" : identityHelp,
16559
16575
  disabled: saving || definition.groupSupported === false,
16560
16576
  unsupported: definition.groupSupported === false,
16561
16577
  onChange: (group) => {
@@ -16582,8 +16598,124 @@ function AccessPolicySettingsPage({ channel: channel7, account, rpcCall, onSaved
16582
16598
  );
16583
16599
  }
16584
16600
 
16585
- // plugin-src/client/channels/feishu/group-settings.js
16601
+ // plugin-src/client/bot-settings-tabs.js
16586
16602
  var React29 = __toESM(require("react"), 1);
16603
+ function revealTab(strip, tab) {
16604
+ if (!strip || !tab) return;
16605
+ const viewport = strip.getBoundingClientRect();
16606
+ const bounds = tab.getBoundingClientRect();
16607
+ if (bounds.left < viewport.left) strip.scrollLeft += bounds.left - viewport.left;
16608
+ else if (bounds.right > viewport.right) strip.scrollLeft += bounds.right - viewport.right;
16609
+ }
16610
+ function BotSettingsTabs({ tabs, activeTabId, onChange }) {
16611
+ const barRef = React29.useRef(null);
16612
+ const stripRef = React29.useRef(null);
16613
+ const listRef = React29.useRef(null);
16614
+ const [scroll, setScroll] = React29.useState({ overflow: false, left: false, right: false });
16615
+ const measure = React29.useCallback(() => {
16616
+ const bar = barRef.current;
16617
+ const strip = stripRef.current;
16618
+ const list = listRef.current;
16619
+ if (!bar || !strip || !list) return;
16620
+ const next = {
16621
+ // Compare against the whole bar, so arrows disappear as soon as all tabs fit.
16622
+ overflow: list.scrollWidth > bar.clientWidth + 1,
16623
+ left: strip.scrollLeft > 1,
16624
+ right: strip.scrollWidth - strip.clientWidth - strip.scrollLeft > 1
16625
+ };
16626
+ setScroll((previous) => Object.keys(next).every((key) => next[key] === previous[key]) ? previous : next);
16627
+ }, []);
16628
+ React29.useEffect(() => {
16629
+ const strip = stripRef.current;
16630
+ if (!strip) return void 0;
16631
+ const refresh = () => {
16632
+ revealTab(strip, strip.querySelector('[aria-selected="true"]'));
16633
+ measure();
16634
+ };
16635
+ refresh();
16636
+ const view = strip.ownerDocument.defaultView;
16637
+ const observer = view.ResizeObserver ? new view.ResizeObserver(refresh) : null;
16638
+ for (const node of [barRef.current, strip, listRef.current]) observer?.observe(node);
16639
+ view.addEventListener("resize", refresh);
16640
+ return () => {
16641
+ observer?.disconnect();
16642
+ view.removeEventListener("resize", refresh);
16643
+ };
16644
+ }, [tabs, activeTabId, scroll.overflow, measure]);
16645
+ const scrollTabs = (direction) => {
16646
+ const strip = stripRef.current;
16647
+ if (!strip) return;
16648
+ strip.scrollLeft += direction * Math.max(80, strip.clientWidth * 0.75);
16649
+ measure();
16650
+ };
16651
+ const handleKeyDown = (event, index) => {
16652
+ let next;
16653
+ if (event.key === "Home") next = 0;
16654
+ else if (event.key === "End") next = tabs.length - 1;
16655
+ else if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
16656
+ next = (index + (event.key === "ArrowRight" ? 1 : -1) + tabs.length) % tabs.length;
16657
+ }
16658
+ if (next === void 0) return;
16659
+ event.preventDefault();
16660
+ onChange(tabs[next].id);
16661
+ const target = stripRef.current?.querySelectorAll('[role="tab"]')[next];
16662
+ target?.focus({ preventScroll: true });
16663
+ revealTab(stripRef.current, target);
16664
+ };
16665
+ const arrow = (direction, label, enabled) => scroll.overflow ? h2("button", {
16666
+ type: "button",
16667
+ className: "dim-botSettingsScroll",
16668
+ "aria-label": label,
16669
+ title: label,
16670
+ disabled: !enabled,
16671
+ onClick: () => scrollTabs(direction)
16672
+ }, h2(
16673
+ "svg",
16674
+ { width: 16, height: 16, viewBox: "0 0 16 16", "aria-hidden": "true" },
16675
+ h2("path", {
16676
+ d: direction < 0 ? "M10 3 5 8l5 5" : "m6 3 5 5-5 5",
16677
+ fill: "none",
16678
+ stroke: "currentColor",
16679
+ strokeWidth: 1.5,
16680
+ strokeLinecap: "round",
16681
+ strokeLinejoin: "round"
16682
+ })
16683
+ )) : null;
16684
+ return h2(
16685
+ "div",
16686
+ { ref: barRef, className: "dim-botSettingsTabsBar" },
16687
+ arrow(-1, "\u5411\u5DE6\u6EDA\u52A8\u8BBE\u7F6E\u9875\u7B7E", scroll.left),
16688
+ h2("nav", {
16689
+ ref: stripRef,
16690
+ className: "dim-botSettingsTabs",
16691
+ role: "tablist",
16692
+ "aria-label": "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E",
16693
+ onScroll: measure
16694
+ }, h2(
16695
+ "div",
16696
+ { ref: listRef, className: "dim-botSettingsTabsList", role: "presentation" },
16697
+ tabs.map((tab, index) => h2("button", {
16698
+ key: tab.id,
16699
+ id: `dim-bot-settings-${tab.id}-tab`,
16700
+ type: "button",
16701
+ role: "tab",
16702
+ className: "dim-botSettingsTab",
16703
+ "aria-selected": tab.id === activeTabId,
16704
+ "aria-controls": `dim-bot-settings-${tab.id}-panel`,
16705
+ tabIndex: tab.id === activeTabId ? 0 : -1,
16706
+ onClick: (event) => {
16707
+ onChange(tab.id);
16708
+ revealTab(stripRef.current, event?.currentTarget);
16709
+ },
16710
+ onKeyDown: (event) => handleKeyDown(event, index)
16711
+ }, tab.label))
16712
+ )),
16713
+ arrow(1, "\u5411\u53F3\u6EDA\u52A8\u8BBE\u7F6E\u9875\u7B7E", scroll.right)
16714
+ );
16715
+ }
16716
+
16717
+ // plugin-src/client/channels/feishu/group-settings.js
16718
+ var React30 = __toESM(require("react"), 1);
16587
16719
  var GROUP_MESSAGE_PERMISSION_OPERATION2 = FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION;
16588
16720
  function SettingsButton({ children, kind = "secondary", className = "", ...props }) {
16589
16721
  return h2("button", {
@@ -16644,9 +16776,9 @@ function GroupResponseModeEditor({
16644
16776
  onAuthorize
16645
16777
  }) {
16646
16778
  const current = normalizeGroupResponseMode(value);
16647
- const [saving, setSaving] = React29.useState(false);
16648
- const [authorizing, setAuthorizing] = React29.useState(false);
16649
- const [error, setError] = React29.useState(null);
16779
+ const [saving, setSaving] = React30.useState(false);
16780
+ const [authorizing, setAuthorizing] = React30.useState(false);
16781
+ const [error, setError] = React30.useState(null);
16650
16782
  const change = async (event) => {
16651
16783
  const next = normalizeGroupResponseMode(event.target.value);
16652
16784
  if (next === current || saving || disabled) return;
@@ -16727,8 +16859,8 @@ function GroupResponseModeEditor({
16727
16859
  }
16728
16860
  function MentionTopicReplyEditor({ value = true, disabled = false, onSave }) {
16729
16861
  const current = value === true ? "on" : "off";
16730
- const [saving, setSaving] = React29.useState(false);
16731
- const [error, setError] = React29.useState(null);
16862
+ const [saving, setSaving] = React30.useState(false);
16863
+ const [error, setError] = React30.useState(null);
16732
16864
  const change = async (event) => {
16733
16865
  const next = event.target.value === "on";
16734
16866
  if ((next ? "on" : "off") === current || saving || disabled) return;
@@ -16915,14 +17047,14 @@ function PermissionFlow({ provision, now, busy, botName, onRetry, onCancel, onCl
16915
17047
  );
16916
17048
  }
16917
17049
  function FeishuGroupSettingsPage({ account, rpcCall }) {
16918
- const [settings, setSettings] = React29.useState(() => groupSettingsFrom(account));
16919
- const [refreshing, setRefreshing] = React29.useState(false);
16920
- const [refreshError, setRefreshError] = React29.useState(null);
16921
- const [provision, setProvision] = React29.useState(null);
16922
- const [provisionBusy, setProvisionBusy] = React29.useState(false);
16923
- const [now, setNow] = React29.useState(() => Date.now());
16924
- const mounted = React29.useRef(true);
16925
- React29.useEffect(() => {
17050
+ const [settings, setSettings] = React30.useState(() => groupSettingsFrom(account));
17051
+ const [refreshing, setRefreshing] = React30.useState(false);
17052
+ const [refreshError, setRefreshError] = React30.useState(null);
17053
+ const [provision, setProvision] = React30.useState(null);
17054
+ const [provisionBusy, setProvisionBusy] = React30.useState(false);
17055
+ const [now, setNow] = React30.useState(() => Date.now());
17056
+ const mounted = React30.useRef(true);
17057
+ React30.useEffect(() => {
16926
17058
  setSettings(groupSettingsFrom(account));
16927
17059
  }, [
16928
17060
  account.botId,
@@ -16930,22 +17062,22 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
16930
17062
  account.mentionTopicReply,
16931
17063
  account.groupMessagePermissionGranted
16932
17064
  ]);
16933
- React29.useEffect(() => {
17065
+ React30.useEffect(() => {
16934
17066
  mounted.current = true;
16935
17067
  return () => {
16936
17068
  mounted.current = false;
16937
17069
  };
16938
17070
  }, []);
16939
- const invoke = React29.useCallback(async (endpoint, payload = {}, signal) => {
17071
+ const invoke = React30.useCallback(async (endpoint, payload = {}, signal) => {
16940
17072
  if (typeof rpcCall !== "function") throw new Error("\u98DE\u4E66\u7FA4\u804A\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
16941
17073
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
16942
17074
  }, [rpcCall]);
16943
- const applySnapshot = React29.useCallback((value) => {
17075
+ const applySnapshot = React30.useCallback((value) => {
16944
17076
  const result = targetBotFromSnapshot(value, account.botId);
16945
17077
  if (mounted.current) setSettings(groupSettingsFrom(result.bot));
16946
17078
  return result;
16947
17079
  }, [account.botId]);
16948
- const loadSettings = React29.useCallback(async ({ signal, restoreProvisioning = false } = {}) => {
17080
+ const loadSettings = React30.useCallback(async ({ signal, restoreProvisioning = false } = {}) => {
16949
17081
  setRefreshing(true);
16950
17082
  setRefreshError(null);
16951
17083
  try {
@@ -16974,16 +17106,16 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
16974
17106
  if (!signal?.aborted && mounted.current) setRefreshing(false);
16975
17107
  }
16976
17108
  }, [account.botId, applySnapshot, invoke]);
16977
- React29.useEffect(() => {
17109
+ React30.useEffect(() => {
16978
17110
  const controller = new AbortController();
16979
17111
  void loadSettings({ signal: controller.signal, restoreProvisioning: true });
16980
17112
  return () => controller.abort();
16981
17113
  }, [loadSettings]);
16982
- const saveSetting = React29.useCallback(async (endpoint, payload) => {
17114
+ const saveSetting = React30.useCallback(async (endpoint, payload) => {
16983
17115
  const value = await invoke(endpoint, { botId: account.botId, ...payload });
16984
17116
  applySnapshot(value);
16985
17117
  }, [account.botId, applySnapshot, invoke]);
16986
- const startAuthorization = React29.useCallback(async ({ replace = false } = {}) => {
17118
+ const startAuthorization = React30.useCallback(async ({ replace = false } = {}) => {
16987
17119
  if (provisionBusy) return;
16988
17120
  const previousAttemptId = provision?.attemptId;
16989
17121
  setProvisionBusy(true);
@@ -17024,13 +17156,13 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
17024
17156
  if (mounted.current) setProvisionBusy(false);
17025
17157
  }
17026
17158
  }, [account.botId, invoke, provision?.attemptId, provisionBusy]);
17027
- const finishAuthorization = React29.useCallback(async (signal) => {
17159
+ const finishAuthorization = React30.useCallback(async (signal) => {
17028
17160
  const bot = await loadSettings({ signal, restoreProvisioning: false });
17029
17161
  if (signal?.aborted || !mounted.current) return;
17030
17162
  if (!bot) throw new Error("\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001");
17031
17163
  setProvision(null);
17032
17164
  }, [loadSettings]);
17033
- React29.useEffect(() => {
17165
+ React30.useEffect(() => {
17034
17166
  if (!provision?.attemptId || !["qr", "connecting"].includes(provision.phase) || provision.expired) return void 0;
17035
17167
  const timerHost = globalThis.window ?? globalThis;
17036
17168
  const controller = new AbortController();
@@ -17074,7 +17206,7 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
17074
17206
  timerHost.clearTimeout(timer);
17075
17207
  };
17076
17208
  }, [account.botId, finishAuthorization, invoke, provision]);
17077
- React29.useEffect(() => {
17209
+ React30.useEffect(() => {
17078
17210
  if (!provision?.attemptId || provision.phase !== "qr" || provision.expired) return void 0;
17079
17211
  const timerHost = globalThis.window ?? globalThis;
17080
17212
  const tick = () => {
@@ -17088,7 +17220,7 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
17088
17220
  const timer = timerHost.setInterval(tick, 1e3);
17089
17221
  return () => timerHost.clearInterval(timer);
17090
17222
  }, [provision?.attemptId, provision?.expired, provision?.expiresAt, provision?.phase]);
17091
- const cancelAuthorization = React29.useCallback(async () => {
17223
+ const cancelAuthorization = React30.useCallback(async () => {
17092
17224
  if (!provision?.attemptId || provisionBusy) {
17093
17225
  setProvision(null);
17094
17226
  return;
@@ -17177,21 +17309,21 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
17177
17309
  }
17178
17310
 
17179
17311
  // plugin-src/client/channels/feishu/voice-settings.js
17180
- var React31 = __toESM(require("react"), 1);
17312
+ var React32 = __toESM(require("react"), 1);
17181
17313
 
17182
17314
  // plugin-src/client/channels/feishu/voice-editor.js
17183
- var React30 = __toESM(require("react"), 1);
17315
+ var React31 = __toESM(require("react"), 1);
17184
17316
  function VoiceEditor({ value = null, disabled = false, onSave }) {
17185
- const [enabled, setEnabled] = React30.useState(value != null);
17186
- const [secretRef, setSecretRef] = React30.useState(value?.secretRef ?? "DASHSCOPE_API_KEY");
17187
- const [asrModel, setAsrModel] = React30.useState(value?.asrModel ?? "qwen3-asr-flash");
17188
- const [ttsModel, setTtsModel] = React30.useState(value?.ttsModel ?? "qwen3-tts-flash");
17189
- const [ttsVoice, setTtsVoice] = React30.useState(value?.ttsVoice ?? "Momo");
17190
- const [saving, setSaving] = React30.useState(false);
17191
- const [error, setError] = React30.useState(null);
17192
- const titleId = React30.useId();
17317
+ const [enabled, setEnabled] = React31.useState(value != null);
17318
+ const [secretRef, setSecretRef] = React31.useState(value?.secretRef ?? "DASHSCOPE_API_KEY");
17319
+ const [asrModel, setAsrModel] = React31.useState(value?.asrModel ?? "qwen3-asr-flash");
17320
+ const [ttsModel, setTtsModel] = React31.useState(value?.ttsModel ?? "qwen3-tts-flash");
17321
+ const [ttsVoice, setTtsVoice] = React31.useState(value?.ttsVoice ?? "Momo");
17322
+ const [saving, setSaving] = React31.useState(false);
17323
+ const [error, setError] = React31.useState(null);
17324
+ const titleId = React31.useId();
17193
17325
  const helpId = `${titleId}-help`;
17194
- React30.useEffect(() => {
17326
+ React31.useEffect(() => {
17195
17327
  setEnabled(value != null);
17196
17328
  if (value) {
17197
17329
  setSecretRef(value.secretRef ?? "DASHSCOPE_API_KEY");
@@ -17305,20 +17437,20 @@ function VoiceEditor({ value = null, disabled = false, onSave }) {
17305
17437
 
17306
17438
  // plugin-src/client/channels/feishu/voice-settings.js
17307
17439
  function FeishuVoiceSettingsPage({ account, rpcCall }) {
17308
- const [voice, setVoice] = React31.useState(null);
17309
- const [phase, setPhase] = React31.useState("loading");
17310
- const [error, setError] = React31.useState(null);
17311
- const mounted = React31.useRef(true);
17312
- const invoke = React31.useCallback(async (endpoint, payload, signal) => {
17440
+ const [voice, setVoice] = React32.useState(null);
17441
+ const [phase, setPhase] = React32.useState("loading");
17442
+ const [error, setError] = React32.useState(null);
17443
+ const mounted = React32.useRef(true);
17444
+ const invoke = React32.useCallback(async (endpoint, payload, signal) => {
17313
17445
  if (typeof rpcCall !== "function") throw new Error("\u98DE\u4E66\u8BED\u97F3\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
17314
17446
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
17315
17447
  }, [rpcCall]);
17316
- const applySnapshot = React31.useCallback((value) => {
17448
+ const applySnapshot = React32.useCallback((value) => {
17317
17449
  const bot = normalizeBotsSnapshot(value).bots.find((entry) => entry.botId === account.botId);
17318
17450
  if (!bot) throw new Error("\u672A\u627E\u5230\u5F53\u524D\u98DE\u4E66\u673A\u5668\u4EBA\uFF0C\u8BF7\u8FD4\u56DE\u5217\u8868\u540E\u91CD\u8BD5\u3002");
17319
17451
  if (mounted.current) setVoice(bot.voice);
17320
17452
  }, [account.botId]);
17321
- const loadSettings = React31.useCallback(async (signal) => {
17453
+ const loadSettings = React32.useCallback(async (signal) => {
17322
17454
  setPhase("loading");
17323
17455
  setError(null);
17324
17456
  try {
@@ -17332,7 +17464,7 @@ function FeishuVoiceSettingsPage({ account, rpcCall }) {
17332
17464
  setPhase("error");
17333
17465
  }
17334
17466
  }, [applySnapshot, invoke]);
17335
- React31.useEffect(() => {
17467
+ React32.useEffect(() => {
17336
17468
  mounted.current = true;
17337
17469
  const controller = new AbortController();
17338
17470
  void loadSettings(controller.signal);
@@ -17371,7 +17503,7 @@ function FeishuVoiceSettingsPage({ account, rpcCall }) {
17371
17503
  }
17372
17504
 
17373
17505
  // plugin-src/client/channels/feishu/slash-panel-settings.js
17374
- var React32 = __toESM(require("react"), 1);
17506
+ var React33 = __toESM(require("react"), 1);
17375
17507
  var MANIFEST_BY_NAME = new Map(SLASH_COMMAND_MANIFEST.map((entry) => [entry.command, entry]));
17376
17508
  var ALL_COMMANDS = Object.freeze(SLASH_COMMAND_MANIFEST.map((entry) => entry.command));
17377
17509
  function PanelButton({ children, disabled = false, onClick, ...props }) {
@@ -17392,30 +17524,30 @@ function commandText(name2) {
17392
17524
  return `/${name2} \u2014 ${entry?.default ?? name2}`;
17393
17525
  }
17394
17526
  function FeishuSlashPanelSettingsPage({ account, rpcCall, onSaved }) {
17395
- const incoming = React32.useMemo(() => normalizeSlashPanelConfig(account.slashPanel), [account.botId, panelSignature(account.slashPanel)]);
17396
- const [mode, setMode] = React32.useState(incoming.mode);
17397
- const [order, setOrder] = React32.useState(() => [...incoming.order]);
17398
- const [pending, setPending] = React32.useState("");
17399
- const [dirty, setDirty] = React32.useState(false);
17400
- const [saving, setSaving] = React32.useState(false);
17401
- const [saved, setSaved] = React32.useState(false);
17402
- const [syncing, setSyncing] = React32.useState(false);
17403
- const [syncTargets, setSyncTargets] = React32.useState(null);
17404
- const [syncNotice, setSyncNotice] = React32.useState(null);
17405
- const [error, setError] = React32.useState(null);
17406
- const mounted = React32.useRef(true);
17407
- React32.useEffect(() => {
17527
+ const incoming = React33.useMemo(() => normalizeSlashPanelConfig(account.slashPanel), [account.botId, panelSignature(account.slashPanel)]);
17528
+ const [mode, setMode] = React33.useState(incoming.mode);
17529
+ const [order, setOrder] = React33.useState(() => [...incoming.order]);
17530
+ const [pending, setPending] = React33.useState("");
17531
+ const [dirty, setDirty] = React33.useState(false);
17532
+ const [saving, setSaving] = React33.useState(false);
17533
+ const [saved, setSaved] = React33.useState(false);
17534
+ const [syncing, setSyncing] = React33.useState(false);
17535
+ const [syncTargets, setSyncTargets] = React33.useState(null);
17536
+ const [syncNotice, setSyncNotice] = React33.useState(null);
17537
+ const [error, setError] = React33.useState(null);
17538
+ const mounted = React33.useRef(true);
17539
+ React33.useEffect(() => {
17408
17540
  mounted.current = true;
17409
17541
  return () => {
17410
17542
  mounted.current = false;
17411
17543
  };
17412
17544
  }, []);
17413
- React32.useEffect(() => {
17545
+ React33.useEffect(() => {
17414
17546
  if (dirty) return;
17415
17547
  setMode(incoming.mode);
17416
17548
  setOrder([...incoming.order]);
17417
17549
  }, [incoming.mode, incoming.order, dirty]);
17418
- const edit = React32.useCallback((update) => {
17550
+ const edit = React33.useCallback((update) => {
17419
17551
  setSaved(false);
17420
17552
  setDirty(true);
17421
17553
  update();
@@ -17552,7 +17684,7 @@ function FeishuSlashPanelSettingsPage({ account, rpcCall, onSaved }) {
17552
17684
  h2("option", { value: SLASH_PANEL_MODES.CUSTOM }, "\u81EA\u5B9A\u4E49\uFF08\u81EA\u5DF1\u6311\u6307\u4EE4\u548C\u987A\u5E8F\uFF09")
17553
17685
  ),
17554
17686
  mode === SLASH_PANEL_MODES.CUSTOM ? h2(
17555
- React32.Fragment,
17687
+ React33.Fragment,
17556
17688
  null,
17557
17689
  order.length === 0 ? h2("p", { className: "dim-feishuGroupHelp" }, "\u5F53\u524D\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u6307\u4EE4\uFF0C\u4FDD\u5B58\u540E\u300C/\u300D\u9762\u677F\u4F1A\u53D8\u6210\u7A7A\u7684\u3002") : h2(
17558
17690
  "ul",
@@ -17908,13 +18040,13 @@ function TargetForm({
17908
18040
  }) {
17909
18041
  const editing = mode === "edit";
17910
18042
  const initialKind = initialValue?.kind && definition.fields[initialValue.kind] ? initialValue.kind : definition.kinds[0].value;
17911
- const [targetId, setTargetId] = React33.useState(initialValue?.targetId ?? "");
17912
- const [name2, setName] = React33.useState(initialValue?.name ?? "");
17913
- const [kind, setKind] = React33.useState(initialKind);
17914
- const [route, setRoute] = React33.useState(initialValue?.route ?? {});
17915
- const [error, setError] = React33.useState(null);
17916
- const [testing, setTesting] = React33.useState(false);
17917
- const [testState, setTestState] = React33.useState(null);
18043
+ const [targetId, setTargetId] = React34.useState(initialValue?.targetId ?? "");
18044
+ const [name2, setName] = React34.useState(initialValue?.name ?? "");
18045
+ const [kind, setKind] = React34.useState(initialKind);
18046
+ const [route, setRoute] = React34.useState(initialValue?.route ?? {});
18047
+ const [error, setError] = React34.useState(null);
18048
+ const [testing, setTesting] = React34.useState(false);
18049
+ const [testState, setTestState] = React34.useState(null);
17918
18050
  const currentTarget = () => {
17919
18051
  const normalizedRoute = Object.fromEntries(fieldsFor(definition, kind).map((field2) => {
17920
18052
  const raw = String(route[field2.key] ?? "").trim();
@@ -18131,7 +18263,7 @@ function TargetSuggestionPicker({
18131
18263
  suggestions.map((suggestion, index) => {
18132
18264
  const identity = routeIdentity(definition, suggestion);
18133
18265
  const added = configured.has(identity);
18134
- return React33.createElement("option", {
18266
+ return React34.createElement("option", {
18135
18267
  key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
18136
18268
  value: String(index),
18137
18269
  disabled: added
@@ -18148,11 +18280,11 @@ function TargetSuggestionPicker({
18148
18280
  );
18149
18281
  }
18150
18282
  function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
18151
- const [action, setAction] = React33.useState(null);
18152
- const [testState, setTestState] = React33.useState(null);
18153
- const [syncFeedback, setSyncFeedback] = React33.useState(null);
18154
- const [copyState, setCopyState] = React33.useState(null);
18155
- const [confirmDelete, setConfirmDelete] = React33.useState(false);
18283
+ const [action, setAction] = React34.useState(null);
18284
+ const [testState, setTestState] = React34.useState(null);
18285
+ const [syncFeedback, setSyncFeedback] = React34.useState(null);
18286
+ const [copyState, setCopyState] = React34.useState(null);
18287
+ const [confirmDelete, setConfirmDelete] = React34.useState(false);
18156
18288
  const sessionSync = target.sessionSync ?? { enabled: false, state: "unavailable" };
18157
18289
  const syncDescription = sessionSync.state === "active" ? "\u81EA\u52A8\u8DDF\u968F\u8BE5\u79C1\u804A\u7684\u5F53\u524D\u4F1A\u8BDD" : sessionSync.state === "waiting" ? "\u7B49\u5F85\u8BE5\u79C1\u804A\u5EFA\u7ACB\u65B0\u4F1A\u8BDD" : sessionSync.state === "unavailable" ? "\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807" : "\u5173\u95ED\u65F6\u4E0D\u53D1\u9001 DSH \u4F1A\u8BDD\u6D88\u606F";
18158
18290
  const testTarget = async () => {
@@ -18215,7 +18347,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
18215
18347
  h2(
18216
18348
  "div",
18217
18349
  { className: "dim-targetTitle" },
18218
- React33.createElement("strong", null, target.name || target.targetId),
18350
+ React34.createElement("strong", null, target.name || target.targetId),
18219
18351
  h2("span", null, kindLabel(definition, target.kind))
18220
18352
  ),
18221
18353
  h2("code", null, `targetId: ${target.targetId}`)
@@ -18301,30 +18433,30 @@ function DeliveryTargetSettingsPage({
18301
18433
  onBack
18302
18434
  }) {
18303
18435
  const definition = CHANNEL_DEFINITIONS[channel7];
18304
- const [activeTabId, setActiveTabId] = React33.useState(BOT_SETTINGS_TABS[0].id);
18305
- const [phase, setPhase] = React33.useState("loading");
18306
- const [targets, setTargets] = React33.useState([]);
18307
- const [suggestionPhase, setSuggestionPhase] = React33.useState("idle");
18308
- const [suggestions, setSuggestions] = React33.useState([]);
18309
- const [suggestionError, setSuggestionError] = React33.useState(null);
18310
- const [error, setError] = React33.useState(null);
18311
- const [editor, setEditor] = React33.useState(null);
18312
- const [saving, setSaving] = React33.useState(false);
18313
- const [botCopyState, setBotCopyState] = React33.useState(null);
18314
- const [accessPolicy, setAccessPolicy] = React33.useState(account.accessPolicy);
18315
- const [slashPanel, setSlashPanel] = React33.useState(account.slashPanel);
18316
- const mounted = React33.useRef(true);
18317
- React33.useEffect(() => {
18436
+ const [activeTabId, setActiveTabId] = React34.useState(BOT_SETTINGS_TABS[0].id);
18437
+ const [phase, setPhase] = React34.useState("loading");
18438
+ const [targets, setTargets] = React34.useState([]);
18439
+ const [suggestionPhase, setSuggestionPhase] = React34.useState("idle");
18440
+ const [suggestions, setSuggestions] = React34.useState([]);
18441
+ const [suggestionError, setSuggestionError] = React34.useState(null);
18442
+ const [error, setError] = React34.useState(null);
18443
+ const [editor, setEditor] = React34.useState(null);
18444
+ const [saving, setSaving] = React34.useState(false);
18445
+ const [botCopyState, setBotCopyState] = React34.useState(null);
18446
+ const [accessPolicy, setAccessPolicy] = React34.useState(account.accessPolicy);
18447
+ const [slashPanel, setSlashPanel] = React34.useState(account.slashPanel);
18448
+ const mounted = React34.useRef(true);
18449
+ React34.useEffect(() => {
18318
18450
  setAccessPolicy(account.accessPolicy);
18319
18451
  }, [account.botId, account.accessPolicy]);
18320
- React33.useEffect(() => {
18452
+ React34.useEffect(() => {
18321
18453
  setSlashPanel(account.slashPanel);
18322
18454
  }, [account.botId, account.slashPanel]);
18323
- const invoke = React33.useCallback(async (endpoint, payload = {}, signal) => {
18455
+ const invoke = React34.useCallback(async (endpoint, payload = {}, signal) => {
18324
18456
  if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
18325
18457
  return unwrapRpcResult15(await rpcCall(endpoint, payload, signal));
18326
18458
  }, [rpcCall]);
18327
- const loadTargets = React33.useCallback(async ({ signal, silent = false } = {}) => {
18459
+ const loadTargets = React34.useCallback(async ({ signal, silent = false } = {}) => {
18328
18460
  if (!silent) setPhase("loading");
18329
18461
  setError(null);
18330
18462
  try {
@@ -18338,7 +18470,7 @@ function DeliveryTargetSettingsPage({
18338
18470
  setPhase("error");
18339
18471
  }
18340
18472
  }, [account.botId, invoke]);
18341
- const loadSuggestions = React33.useCallback(async () => {
18473
+ const loadSuggestions = React34.useCallback(async () => {
18342
18474
  setSuggestionPhase("loading");
18343
18475
  setSuggestionError(null);
18344
18476
  try {
@@ -18352,7 +18484,7 @@ function DeliveryTargetSettingsPage({
18352
18484
  setSuggestionPhase("error");
18353
18485
  }
18354
18486
  }, [account.botId, definition, invoke]);
18355
- React33.useEffect(() => {
18487
+ React34.useEffect(() => {
18356
18488
  mounted.current = true;
18357
18489
  const controller = new AbortController();
18358
18490
  void loadTargets({ signal: controller.signal });
@@ -18438,25 +18570,7 @@ function DeliveryTargetSettingsPage({
18438
18570
  { className: "dim-deliveryHeader" },
18439
18571
  h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868")
18440
18572
  ),
18441
- h2(
18442
- "div",
18443
- { className: "dim-botSettingsTabsBar" },
18444
- h2("nav", {
18445
- className: "dim-botSettingsTabs",
18446
- role: "tablist",
18447
- "aria-label": "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E"
18448
- }, settingsTabs.map((tab) => h2("button", {
18449
- key: tab.id,
18450
- id: `dim-bot-settings-${tab.id}-tab`,
18451
- type: "button",
18452
- role: "tab",
18453
- className: "dim-botSettingsTab",
18454
- "aria-selected": tab.id === activeTab.id,
18455
- "aria-controls": `dim-bot-settings-${tab.id}-panel`,
18456
- tabIndex: tab.id === activeTab.id ? 0 : -1,
18457
- onClick: () => setActiveTabId(tab.id)
18458
- }, tab.label)))
18459
- ),
18573
+ h2(BotSettingsTabs, { tabs: settingsTabs, activeTabId: activeTab.id, onChange: setActiveTabId }),
18460
18574
  h2(
18461
18575
  "div",
18462
18576
  {
@@ -18482,7 +18596,7 @@ function DeliveryTargetSettingsPage({
18482
18596
  account,
18483
18597
  rpcCall: accessRpcCall
18484
18598
  }) : h2(
18485
- React33.Fragment,
18599
+ React34.Fragment,
18486
18600
  null,
18487
18601
  h2(
18488
18602
  "section",
@@ -18593,7 +18707,7 @@ function DeliveryTargetSettingsPage({
18593
18707
  }
18594
18708
 
18595
18709
  // plugin-src/client/global-settings.js
18596
- var React34 = __toESM(require("react"), 1);
18710
+ var React35 = __toESM(require("react"), 1);
18597
18711
 
18598
18712
  // src/channels/shared/inbound-ttl.mjs
18599
18713
  var DEFAULT_INBOUND_TTL_HOURS = 168;
@@ -18678,14 +18792,14 @@ function GlobalButton({ children, kind = "secondary", className = "", ...props }
18678
18792
  }, children);
18679
18793
  }
18680
18794
  function ImageInputSettings({ rpcCall }) {
18681
- const [values, setValues] = React34.useState(DEFAULT_IMAGE_INPUT_SETTINGS);
18682
- const [phase, setPhase] = React34.useState("loading");
18683
- const [error, setError] = React34.useState(null);
18684
- const [saved, setSaved] = React34.useState(false);
18685
- const mounted = React34.useRef(false);
18686
- const saving = React34.useRef(false);
18687
- const id6 = React34.useId();
18688
- const load = React34.useCallback(async (signal) => {
18795
+ const [values, setValues] = React35.useState(DEFAULT_IMAGE_INPUT_SETTINGS);
18796
+ const [phase, setPhase] = React35.useState("loading");
18797
+ const [error, setError] = React35.useState(null);
18798
+ const [saved, setSaved] = React35.useState(false);
18799
+ const mounted = React35.useRef(false);
18800
+ const saving = React35.useRef(false);
18801
+ const id6 = React35.useId();
18802
+ const load = React35.useCallback(async (signal) => {
18689
18803
  setPhase("loading");
18690
18804
  setError(null);
18691
18805
  try {
@@ -18701,7 +18815,7 @@ function ImageInputSettings({ rpcCall }) {
18701
18815
  setPhase("error");
18702
18816
  }
18703
18817
  }, [rpcCall]);
18704
- React34.useEffect(() => {
18818
+ React35.useEffect(() => {
18705
18819
  mounted.current = true;
18706
18820
  const controller = new AbortController();
18707
18821
  void load(controller.signal);
@@ -18808,29 +18922,29 @@ function ImageInputSettings({ rpcCall }) {
18808
18922
  );
18809
18923
  }
18810
18924
  function GlobalSettingsPanel({ rpcCall }) {
18811
- const [phase, setPhase] = React34.useState("loading");
18812
- const [loadError, setLoadError] = React34.useState(null);
18813
- const [ttlInput, setTtlInput] = React34.useState("");
18814
- const [savedTtl, setSavedTtl] = React34.useState(null);
18815
- const [ttlError, setTtlError] = React34.useState(false);
18816
- const [saveError, setSaveError] = React34.useState(null);
18817
- const [saveSucceeded, setSaveSucceeded] = React34.useState(false);
18818
- const [isSaving, setIsSaving] = React34.useState(false);
18819
- const [sweepConfirming, setSweepConfirming] = React34.useState(false);
18820
- const [sweeping, setSweeping] = React34.useState(false);
18821
- const ttlErrorId = React34.useId();
18822
- const ttlHintsId = React34.useId();
18823
- const sweepTriggerId = React34.useId();
18824
- const sweepConfirmId = React34.useId();
18825
- const sweepConfirmTextId = React34.useId();
18826
- const sweepConfirmButtonId = React34.useId();
18827
- const mounted = React34.useRef(true);
18828
- const saving = React34.useRef(false);
18829
- const invoke = React34.useCallback(async (endpoint, payload = {}, signal) => {
18925
+ const [phase, setPhase] = React35.useState("loading");
18926
+ const [loadError, setLoadError] = React35.useState(null);
18927
+ const [ttlInput, setTtlInput] = React35.useState("");
18928
+ const [savedTtl, setSavedTtl] = React35.useState(null);
18929
+ const [ttlError, setTtlError] = React35.useState(false);
18930
+ const [saveError, setSaveError] = React35.useState(null);
18931
+ const [saveSucceeded, setSaveSucceeded] = React35.useState(false);
18932
+ const [isSaving, setIsSaving] = React35.useState(false);
18933
+ const [sweepConfirming, setSweepConfirming] = React35.useState(false);
18934
+ const [sweeping, setSweeping] = React35.useState(false);
18935
+ const ttlErrorId = React35.useId();
18936
+ const ttlHintsId = React35.useId();
18937
+ const sweepTriggerId = React35.useId();
18938
+ const sweepConfirmId = React35.useId();
18939
+ const sweepConfirmTextId = React35.useId();
18940
+ const sweepConfirmButtonId = React35.useId();
18941
+ const mounted = React35.useRef(true);
18942
+ const saving = React35.useRef(false);
18943
+ const invoke = React35.useCallback(async (endpoint, payload = {}, signal) => {
18830
18944
  if (typeof rpcCall !== "function") throw new Error("\u901A\u7528\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
18831
18945
  return unwrapRpcResult16(await rpcCall(endpoint, payload, signal));
18832
18946
  }, [rpcCall]);
18833
- const loadSettings = React34.useCallback(async ({ signal } = {}) => {
18947
+ const loadSettings = React35.useCallback(async ({ signal } = {}) => {
18834
18948
  setPhase("loading");
18835
18949
  setLoadError(null);
18836
18950
  try {
@@ -18854,7 +18968,7 @@ function GlobalSettingsPanel({ rpcCall }) {
18854
18968
  setPhase("error");
18855
18969
  }
18856
18970
  }, [invoke]);
18857
- React34.useEffect(() => {
18971
+ React35.useEffect(() => {
18858
18972
  mounted.current = true;
18859
18973
  const controller = new AbortController();
18860
18974
  void loadSettings({ signal: controller.signal });
@@ -18863,7 +18977,7 @@ function GlobalSettingsPanel({ rpcCall }) {
18863
18977
  controller.abort();
18864
18978
  };
18865
18979
  }, [loadSettings]);
18866
- React34.useEffect(() => {
18980
+ React35.useEffect(() => {
18867
18981
  if (!sweepConfirming) return;
18868
18982
  globalThis.document?.getElementById(sweepConfirmButtonId)?.focus();
18869
18983
  }, [sweepConfirmButtonId, sweepConfirming]);
@@ -19638,9 +19752,14 @@ var CSS15 = String.raw`
19638
19752
  .dim-moreSettingsChevron { width: 6px; height: 6px; margin-right: 2px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); }
19639
19753
  .dim-deliveryPage { min-width: 0; display: grid; }
19640
19754
  .dim-deliveryHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
19641
- .dim-botSettingsTabsBar { min-width: 0; margin-top: 10px; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
19642
- .dim-botSettingsTabs { min-width: 0; display: flex; align-items: flex-end; gap: 24px; overflow-x: auto; scrollbar-width: none; }
19755
+ .dim-botSettingsTabsBar { min-width: 0; display: flex; align-items: center; gap: 4px; margin-top: 10px; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
19756
+ .dim-botSettingsTabs { min-width: 0; flex: 1; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; }
19643
19757
  .dim-botSettingsTabs::-webkit-scrollbar { display: none; }
19758
+ .dim-botSettingsTabsList { width: max-content; min-width: 100%; display: flex; align-items: flex-end; gap: 24px; }
19759
+ .dim-botSettingsScroll { flex: 0 0 30px; width: 30px; height: 32px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #646a73); cursor: pointer; }
19760
+ .dim-botSettingsScroll:hover:not(:disabled) { color: var(--dsw-alias-state-business-primary, #3370ff); background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
19761
+ .dim-botSettingsScroll:disabled { opacity: .3; cursor: default; }
19762
+ .dim-botSettingsScroll:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: -2px; }
19644
19763
  .dim-botSettingsTab { position: relative; min-height: 38px; flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 6px 2px 9px; border: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 13px; line-height: 20px; font-weight: 560; white-space: nowrap; cursor: pointer; transition: color .15s ease; }
19645
19764
  .dim-botSettingsTab::after { content: ''; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; border-radius: 2px 2px 0 0; background: transparent; transform: scaleX(.45); transition: background .15s ease, transform .15s ease; }
19646
19765
  .dim-botSettingsTab:hover:not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); }
@@ -19906,7 +20025,7 @@ var CSS15 = String.raw`
19906
20025
  .dim-loopbackRecovery { align-items: stretch; flex-direction: column; gap: 12px; }
19907
20026
  .dim-loopbackRecoveryAction { width: 100%; }
19908
20027
  .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; }
19909
- .dim-botSettingsTabs { gap: 18px; }
20028
+ .dim-botSettingsTabsList { gap: 18px; }
19910
20029
  .dim-deliveryBotId { grid-template-columns: minmax(0, 1fr) max-content; }
19911
20030
  .dim-deliveryBotId > span { grid-column: 1 / -1; }
19912
20031
  .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; }
@@ -20193,7 +20312,7 @@ function installSessionChannelLogos(document2 = globalThis.document) {
20193
20312
  }
20194
20313
 
20195
20314
  // plugin-src/client/update-panel.js
20196
- var React35 = __toESM(require("react"), 1);
20315
+ var React36 = __toESM(require("react"), 1);
20197
20316
  var import_react_dom4 = require("react-dom");
20198
20317
  var import_valid = __toESM(require_valid(), 1);
20199
20318
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -20286,11 +20405,11 @@ function manualUpdateCommand(snapshot) {
20286
20405
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
20287
20406
  }
20288
20407
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
20289
- const [copyState, setCopyState] = React35.useState("idle");
20290
- const commandRef = React35.useRef(null);
20291
- const mounted = React35.useRef(false);
20292
- const copying = React35.useRef(false);
20293
- React35.useEffect(() => {
20408
+ const [copyState, setCopyState] = React36.useState("idle");
20409
+ const commandRef = React36.useRef(null);
20410
+ const mounted = React36.useRef(false);
20411
+ const copying = React36.useRef(false);
20412
+ React36.useEffect(() => {
20294
20413
  mounted.current = true;
20295
20414
  return () => {
20296
20415
  mounted.current = false;
@@ -20322,7 +20441,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
20322
20441
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
20323
20442
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
20324
20443
  command ? h2(
20325
- React35.Fragment,
20444
+ React36.Fragment,
20326
20445
  null,
20327
20446
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
20328
20447
  h2(
@@ -20358,7 +20477,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
20358
20477
  "aria-hidden": "true",
20359
20478
  focusable: "false"
20360
20479
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
20361
- React35.Fragment,
20480
+ React36.Fragment,
20362
20481
  null,
20363
20482
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
20364
20483
  h2("path", { d: "M5 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V5" })
@@ -20383,10 +20502,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
20383
20502
  );
20384
20503
  }
20385
20504
  function UpdateDialog({ children, onClose }) {
20386
- const dialogRef = React35.useRef(null);
20387
- const titleId = React35.useId();
20388
- const descriptionId = React35.useId();
20389
- React35.useEffect(() => {
20505
+ const dialogRef = React36.useRef(null);
20506
+ const titleId = React36.useId();
20507
+ const descriptionId = React36.useId();
20508
+ React36.useEffect(() => {
20390
20509
  const previous = globalThis.document?.activeElement;
20391
20510
  dialogRef.current?.focus?.();
20392
20511
  return () => {
@@ -20444,20 +20563,20 @@ function UpdateDialog({ children, onClose }) {
20444
20563
  return typeof document !== "undefined" && document.body ? (0, import_react_dom4.createPortal)(content, document.body) : content;
20445
20564
  }
20446
20565
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20447
- const tooltipId = React35.useId();
20448
- const [snapshot, setSnapshot] = React35.useState(null);
20449
- const [action, setAction] = React35.useState("status");
20450
- const [error, setError] = React35.useState(null);
20451
- const [open, setOpen] = React35.useState(false);
20452
- const [uncertainInstall, setUncertainInstall] = React35.useState(false);
20453
- const mounted = React35.useRef(false);
20454
- const busy = React35.useRef(false);
20455
- const readController = React35.useRef(null);
20456
- const pollReadController = React35.useRef(null);
20457
- const installRequest = React35.useRef(null);
20458
- const onStatusRef = React35.useRef(onStatus);
20566
+ const tooltipId = React36.useId();
20567
+ const [snapshot, setSnapshot] = React36.useState(null);
20568
+ const [action, setAction] = React36.useState("status");
20569
+ const [error, setError] = React36.useState(null);
20570
+ const [open, setOpen] = React36.useState(false);
20571
+ const [uncertainInstall, setUncertainInstall] = React36.useState(false);
20572
+ const mounted = React36.useRef(false);
20573
+ const busy = React36.useRef(false);
20574
+ const readController = React36.useRef(null);
20575
+ const pollReadController = React36.useRef(null);
20576
+ const installRequest = React36.useRef(null);
20577
+ const onStatusRef = React36.useRef(onStatus);
20459
20578
  onStatusRef.current = onStatus;
20460
- const accept = React35.useCallback((next) => {
20579
+ const accept = React36.useCallback((next) => {
20461
20580
  setSnapshot(next);
20462
20581
  onStatusRef.current?.(next);
20463
20582
  }, []);
@@ -20466,7 +20585,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20466
20585
  setUncertainInstall(false);
20467
20586
  accept(next);
20468
20587
  };
20469
- const invoke = React35.useCallback(async (endpoint, payload = {}, signal) => {
20588
+ const invoke = React36.useCallback(async (endpoint, payload = {}, signal) => {
20470
20589
  if (typeof rpcCall !== "function") {
20471
20590
  const unavailable = new Error("\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002");
20472
20591
  unavailable.code = "update-unavailable";
@@ -20474,7 +20593,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20474
20593
  }
20475
20594
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
20476
20595
  }, [rpcCall]);
20477
- React35.useEffect(() => {
20596
+ React36.useEffect(() => {
20478
20597
  mounted.current = true;
20479
20598
  busy.current = true;
20480
20599
  const controller = new AbortController();
@@ -20497,7 +20616,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20497
20616
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
20498
20617
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
20499
20618
  const shouldPoll = activeJob || uncertainInstall;
20500
- React35.useEffect(() => {
20619
+ React36.useEffect(() => {
20501
20620
  if (!shouldPoll) return void 0;
20502
20621
  let controller;
20503
20622
  const scheduler = createPollScheduler({
@@ -20598,7 +20717,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20598
20717
  const buttonLabel = action === "checking" ? "\u68C0\u67E5\u4E2D\u2026" : action === "starting" || activeJob ? "\u6B63\u5728\u66F4\u65B0\u2026" : restartRequired ? "\u5F85\u624B\u52A8\u91CD\u542F" : snapshot?.canInstall ? "\u66F4\u65B0\u81F3" : "\u68C0\u67E5\u66F4\u65B0";
20599
20718
  const buttonDescription = localizeText(buttonLabel) + (buttonLabel === "\u66F4\u65B0\u81F3" ? ` v${snapshot.latestVersion}` : "");
20600
20719
  return h2(
20601
- React35.Fragment,
20720
+ React36.Fragment,
20602
20721
  null,
20603
20722
  h2(
20604
20723
  "span",
@@ -20648,25 +20767,25 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20648
20767
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
20649
20768
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
20650
20769
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
20651
- React35.Fragment,
20770
+ React36.Fragment,
20652
20771
  null,
20653
20772
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
20654
20773
  h2("dd", null, `v${snapshot.installedVersion}`)
20655
20774
  ) : null,
20656
20775
  targetVersion ? h2(
20657
- React35.Fragment,
20776
+ React36.Fragment,
20658
20777
  null,
20659
20778
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
20660
20779
  h2("dd", null, `v${targetVersion}`)
20661
20780
  ) : null,
20662
20781
  latestVersion ? h2(
20663
- React35.Fragment,
20782
+ React36.Fragment,
20664
20783
  null,
20665
20784
  h2("dt", null, "\u6700\u65B0\u7248\u672C"),
20666
20785
  h2("dd", null, `v${latestVersion}`)
20667
20786
  ) : null,
20668
20787
  historicalTargetVersion ? h2(
20669
- React35.Fragment,
20788
+ React36.Fragment,
20670
20789
  null,
20671
20790
  h2("dt", null, "\u4E0A\u6B21\u66F4\u65B0\u76EE\u6807"),
20672
20791
  h2("dd", null, `v${historicalTargetVersion}`)
@@ -20748,8 +20867,8 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
20748
20867
  }
20749
20868
 
20750
20869
  // plugin-src/client/panel-error-boundary.js
20751
- var React36 = __toESM(require("react"), 1);
20752
- var IMPanelErrorBoundary = class extends React36.Component {
20870
+ var React37 = __toESM(require("react"), 1);
20871
+ var IMPanelErrorBoundary = class extends React37.Component {
20753
20872
  state = { failed: false };
20754
20873
  static getDerivedStateFromError() {
20755
20874
  return { failed: true };
@@ -20769,9 +20888,9 @@ var IMPanelErrorBoundary = class extends React36.Component {
20769
20888
  };
20770
20889
 
20771
20890
  // plugin-src/client/plugin-page.js
20772
- var React37 = __toESM(require("react"), 1);
20891
+ var React38 = __toESM(require("react"), 1);
20773
20892
  var IM_MAIN_PANEL_ID = "xmanrui-dsh-im";
20774
- var h3 = React37.createElement;
20893
+ var h3 = React38.createElement;
20775
20894
  function OpenImAction({ subject, packageName, onOpen, t }) {
20776
20895
  if (subject?.kind !== "bundle" && subject?.kind !== "row") return null;
20777
20896
  if (subject.pkg?.name !== packageName) return null;
@@ -21002,28 +21121,28 @@ function IMSettingsTab({
21002
21121
  browserLocation = globalThis.location,
21003
21122
  navigateToRecoveryUrl = replacePageLocation
21004
21123
  }) {
21005
- const [selected, setSelected] = React38.useState(() => preferredSectionId === GLOBAL_SETTINGS_TAB_ID || CHANNELS.some((channel7) => channel7.id === preferredSectionId) ? preferredSectionId : "weixin");
21006
- const [loopbackRecovery, setLoopbackRecovery] = React38.useState(null);
21007
- const [runningVersion, setRunningVersion] = React38.useState(IM_PLUGIN_VERSION);
21008
- const [deliverySettings, setDeliverySettings] = React38.useState(null);
21124
+ const [selected, setSelected] = React39.useState(() => preferredSectionId === GLOBAL_SETTINGS_TAB_ID || CHANNELS.some((channel7) => channel7.id === preferredSectionId) ? preferredSectionId : "weixin");
21125
+ const [loopbackRecovery, setLoopbackRecovery] = React39.useState(null);
21126
+ const [runningVersion, setRunningVersion] = React39.useState(IM_PLUGIN_VERSION);
21127
+ const [deliverySettings, setDeliverySettings] = React39.useState(null);
21009
21128
  const emailEnabled = useEmailChannelEnabled(emailRpcCall);
21010
- const visibleChannels = React38.useMemo(
21129
+ const visibleChannels = React39.useMemo(
21011
21130
  () => CHANNELS.filter((channel7) => channel7.id !== "email" || emailEnabled),
21012
21131
  [emailEnabled]
21013
21132
  );
21014
- const githubTooltipId = React38.useId();
21015
- const generalSettingsTooltipId = React38.useId();
21133
+ const githubTooltipId = React39.useId();
21134
+ const generalSettingsTooltipId = React39.useId();
21016
21135
  const globalSettingsSelected = selected === GLOBAL_SETTINGS_TAB_ID;
21017
21136
  const active = visibleChannels.find((channel7) => channel7.id === selected) ?? visibleChannels[0];
21018
21137
  const activeTabId = globalSettingsSelected ? "dim-general-settings-trigger" : `dim-tab-${active.id}`;
21019
21138
  const activePanelId = globalSettingsSelected ? `dim-panel-${GLOBAL_SETTINGS_TAB_ID}` : `dim-panel-${active.id}`;
21020
- const reportLoopbackRecovery = React38.useCallback((recovery) => {
21139
+ const reportLoopbackRecovery = React39.useCallback((recovery) => {
21021
21140
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
21022
21141
  }, []);
21023
- const reportUpdateStatus = React38.useCallback((snapshot) => {
21142
+ const reportUpdateStatus = React39.useCallback((snapshot) => {
21024
21143
  setRunningVersion(snapshot.runningVersion);
21025
21144
  }, []);
21026
- const rpcCalls = React38.useMemo(() => createLoopbackAwareRpcCalls({
21145
+ const rpcCalls = React39.useMemo(() => createLoopbackAwareRpcCalls({
21027
21146
  dingtalkRpcCall,
21028
21147
  discordRpcCall,
21029
21148
  feishuRpcCall,
@@ -21065,7 +21184,7 @@ function IMSettingsTab({
21065
21184
  weixinRpcCall,
21066
21185
  whatsappRpcCall
21067
21186
  ]);
21068
- const botSettingsContext = React38.useMemo(() => Object.freeze({
21187
+ const botSettingsContext = React39.useMemo(() => Object.freeze({
21069
21188
  openBotSettings: setDeliverySettings
21070
21189
  }), []);
21071
21190
  return h2(
@@ -21289,7 +21408,7 @@ function apply(ctx) {
21289
21408
  };
21290
21409
  const localeSnapshot = () => ctx.locale.getLocale?.()?.active ?? "";
21291
21410
  function IMPanel({ preferredSectionId }) {
21292
- React38.useSyncExternalStore(subscribeLocale, localeSnapshot, localeSnapshot);
21411
+ React39.useSyncExternalStore(subscribeLocale, localeSnapshot, localeSnapshot);
21293
21412
  return h2(
21294
21413
  IMPanelErrorBoundary,
21295
21414
  null,