diff --git a/README.md b/README.md index d944038..677897c 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ QQ 扫码接入使用腾讯 QQBot v2 官方流程。默认腾讯授权页会把 - Harness 中只注册一个「IM机器人」设置页; - 九个渠道的 Host、客户端与运行时源码都在本仓库维护,不依赖外部独立渠道插件; +- 设置页跟随 DeepSeek Harness 的语言选择,在中文和 English 之间即时切换; - 左侧使用渠道 Logo 切换微信、飞书、钉钉、企业微信、QQ、Slack、Telegram、Discord 和 WhatsApp,不使用启用/停用开关; - 九个渠道保持独立的 RPC、凭据、连接监督和会话映射; - 浏览器只获得二维码、Manifest 和脱敏状态;手动输入的 Secret 或 Token 仅单向提交给本机 Host,任何 RPC 响应都不会返回 App Secret、`bot_token`、钉钉 `client_secret`、企业微信 Secret、QQ `app_secret`、Slack Bot/App Token、Telegram/Discord Bot Token、WhatsApp 关联设备密钥或原始用户标识。 @@ -146,6 +147,7 @@ Feishu QR binding records the scanner as an allowed user. Manual credentials can - Registers a single **IM Bot** settings page in Harness. - Maintains all nine channel Host, client, and runtime sources in this repository without external standalone channel plugins. +- Follows the DeepSeek Harness language preference and switches the settings UI live between Chinese and English. - Uses channel logos for WeChat, Feishu, DingTalk, WeCom, QQ, Slack, Telegram, Discord, and WhatsApp navigation without enable/disable switches. - Keeps RPC endpoints, credentials, connection supervision, and session mappings isolated by channel. - Returns only QR codes, the public Slack Manifest, and redacted status data to the browser. Manually entered secrets and Tokens travel one way to the local Host; no RPC response returns App Secrets, `bot_token`, DingTalk `client_secret`, WeCom Secrets, QQ `app_secret`, Slack Bot/App Tokens, Telegram/Discord Bot Tokens, WhatsApp linked-device keys, or raw user identifiers. diff --git a/lib/client.js b/lib/client.js index 8d9135c..ccb3abc 100644 --- a/lib/client.js +++ b/lib/client.js @@ -40,7 +40,7 @@ __export(index_exports, { name: () => name }); module.exports = __toCommonJS(index_exports); -var React12 = __toESM(require("react"), 1); +var React13 = __toESM(require("react"), 1); // plugin-src/client/channel-logos.js var React = __toESM(require("react"), 1); @@ -353,11 +353,460 @@ function formatRemaining(milliseconds) { } // plugin-src/client/channels/dingtalk/index.js -var React3 = __toESM(require("react"), 1); +var React4 = __toESM(require("react"), 1); // plugin-src/client/credential-binding.js +var React3 = __toESM(require("react"), 1); + +// plugin-src/client/i18n.js var React2 = __toESM(require("react"), 1); -var h2 = React2.createElement; +var IM_LOCALE_NAMESPACE = "dsh-im"; +var EN = Object.freeze({ + "$locale": "en", + "IM\u673A\u5668\u4EBA": "IM bots", + "IM\u673A\u5668\u4EBA\u8BBE\u7F6E": "IM bot settings", + "IM \u6E20\u9053": "IM channels", + "\u628A\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness": "Connect bots to DeepSeek Harness", + "\u5FAE\u4FE1": "WeChat", + "\u98DE\u4E66": "Feishu", + "\u9489\u9489": "DingTalk", + "\u4F01\u4E1A\u5FAE\u4FE1": "WeCom", + "\u5FAE\u4FE1\u673A\u5668\u4EBA": "WeChat bot", + "\u98DE\u4E66\u673A\u5668\u4EBA": "Feishu bot", + "\u9489\u9489\u673A\u5668\u4EBA": "DingTalk bot", + "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA": "WeCom bot", + "QQ\u673A\u5668\u4EBA": "QQ bot", + "WhatsApp\u673A\u5668\u4EBA": "WhatsApp bot", + "WhatsApp\u8D26\u53F7": "WhatsApp account", + "\u5FAE\u4FE1\u8BBE\u7F6E": "WeChat settings", + "\u98DE\u4E66\u673A\u5668\u4EBA\u8BBE\u7F6E": "Feishu bot settings", + "\u9489\u9489\u8BBE\u7F6E": "DingTalk settings", + "\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E": "WeCom settings", + "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA": "Scan QR code", + "\u6B63\u5728\u63A5\u5165": "Connecting", + "\u624B\u52A8\u63A5\u5165": "Manual setup", + "\u6536\u8D77\u51ED\u636E": "Hide credentials", + "\u6536\u8D77\u63A5\u5165": "Hide setup", + "\u63A5\u5165\u673A\u5668\u4EBA": "Connect bot", + "\u5F00\u59CB\u63A5\u5165": "Start setup", + "\u5728\u7EBF": "online", + "\u8FD0\u884C\u6B63\u5E38": "Connected", + "\u6B63\u5728\u8FDE\u63A5": "Connecting", + "\u6B63\u5728\u8FDE\u63A5\u2026": "Connecting\u2026", + "\u8FDE\u63A5\u672A\u5C31\u7EEA": "Not connected", + "\u8FDE\u63A5\u4E2D": "Connecting", + "\u8FDE\u63A5\u4E2D\u65AD": "Disconnected", + "\u9700\u8981\u5904\u7406": "Needs attention", + "\u72B6\u6001\u672A\u77E5": "Unknown status", + "\u79BB\u7EBF": "Offline", + "\u5DF2\u65AD\u5F00": "Disconnected", + "\u6D88\u606F\u901A\u9053": "Message channel", + "\u6700\u8FD1\u68C0\u67E5": "Last checked", + "\u5C1A\u672A\u68C0\u67E5": "Not checked yet", + "\u521A\u521A": "Just now", + "\u68C0\u67E5\u8FDE\u63A5": "Check connection", + "\u68C0\u67E5\u4E2D\u2026": "Checking\u2026", + "\u91CD\u8BD5\u8FDE\u63A5": "Reconnect", + "\u91CD\u8BD5\u4E2D\u2026": "Retrying\u2026", + "\u79FB\u9664\u63A5\u5165": "Remove connection", + "\u786E\u8BA4\u79FB\u9664\u63A5\u5165": "Remove connection", + "\u786E\u8BA4\u79FB\u9664": "Remove", + "\u6B63\u5728\u79FB\u9664\u2026": "Removing\u2026", + "\u4FDD\u7559\u673A\u5668\u4EBA": "Keep bot", + "\u4FDD\u7559\u8D26\u53F7": "Keep account", + "\u53D6\u6D88": "Cancel", + "\u5173\u95ED": "Close", + "\u7ACB\u5373\u91CD\u8BD5": "Retry now", + "\u91CD\u65B0\u8BFB\u53D6": "Reload", + "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801": "Generate a new QR code", + "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED": "Generate a new QR code", + "\u5237\u65B0\u4E8C\u7EF4\u7801": "Refresh QR code", + "\u5237\u65B0\u4E2D\u2026": "Refreshing\u2026", + "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801": "Get another QR code", + "\u7EE7\u7EED\u8FDE\u63A5": "Continue connecting", + "\u7ED1\u5B9A\u5E76\u8FDE\u63A5": "Connect", + "\u6B63\u5728\u7ED1\u5B9A\u2026": "Connecting\u2026", + "\u9A8C\u8BC1\u5E76\u8FDE\u63A5": "Verify and connect", + "\u6B63\u5728\u9A8C\u8BC1\u5E76\u8FDE\u63A5\u2026": "Verifying and connecting\u2026", + "\u6B63\u5728\u9A8C\u8BC1\u2026": "Verifying\u2026", + "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5": "The operation failed. Try again later.", + "\u8BF7\u7A0D\u540E\u91CD\u8BD5": "Try again later.", + "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4": "QR code expires in", + "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4": "QR code expires in", + "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F": "QR code expired", + "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548": "QR code expired", + "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\n\u8BF7\u91CD\u65B0\u751F\u6210": "QR code expired\nGenerate a new one", + "\u4E8C\u7EF4\u7801\u56FE\u7247\u6B63\u5728\u751F\u6210\u2026": "Generating QR code\u2026", + "\u4E8C\u7EF4\u7801\u6B63\u5728\u751F\u6210\u2026": "Generating QR code\u2026", + "\u4E8C\u7EF4\u7801\u6B63\u5728\u81EA\u52A8\u5237\u65B0\u2026": "Refreshing QR code\u2026", + "\u4E8C\u7EF4\u7801\u672A\u5C31\u7EEA\uFF0C\u8BF7\u6253\u5F00\u6388\u6743\u94FE\u63A5": "The QR code is not ready. Open the authorization link.", + "\u4E8C\u7EF4\u7801\u56FE\u7247\u672A\u5C31\u7EEA\uFF0C\u8BF7\u4F7F\u7528\u5907\u7528\u94FE\u63A5\u3002": "The QR code is not ready. Use the alternate link.", + "\u4E8C\u7EF4\u7801\u56FE\u7247\u672A\u5C31\u7EEA\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u3002": "The QR code is not ready. Generate a new one.", + "\u7B49\u5F85\u5237\u65B0": "Waiting to refresh", + "\u6B63\u5728\u5237\u65B0\u4E8C\u7EF4\u7801": "Refreshing QR code", + "\u6253\u5F00\u5907\u7528\u94FE\u63A5": "Open alternate link", + "\u751F\u6210\u4E8C\u7EF4\u7801": "Generate QR code", + "\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801": "Generate WeChat QR code", + "\u751F\u6210\u98DE\u4E66\u4E8C\u7EF4\u7801": "Generate Feishu QR code", + "\u751F\u6210\u9489\u9489\u4E8C\u7EF4\u7801": "Generate DingTalk QR code", + "\u751F\u6210\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801": "Generate WeCom QR code", + "\u751F\u6210 QQ \u4E8C\u7EF4\u7801": "Generate QQ QR code", + "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026": "Generating QR code\u2026", + "\u6B63\u5728\u51C6\u5907\u6388\u6743\u4E8C\u7EF4\u7801": "Preparing authorization QR code", + "\u6B63\u5728\u51C6\u5907\u5FAE\u4FE1\u4E8C\u7EF4\u7801": "Preparing WeChat QR code", + "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA": "Adding a new bot", + "\u6B63\u5728\u7533\u8BF7\u9489\u9489\u6388\u6743\u4E8C\u7EF4\u7801\u2026": "Requesting DingTalk authorization QR code\u2026", + "\u6B63\u5728\u7533\u8BF7\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u2026": "Requesting WeCom QR code\u2026", + "\u6B63\u5728\u7533\u8BF7 QQ \u4E8C\u7EF4\u7801\u2026": "Requesting QQ QR code\u2026", + "\u6B63\u5728\u751F\u6210 WhatsApp \u4E8C\u7EF4\u7801": "Generating WhatsApp QR code", + "\u626B\u7801\uFF0C\u521B\u5EFA\u7B2C\u4E00\u4E2A\u98DE\u4E66\u5165\u53E3": "Scan to create your first Feishu bot", + "\u626B\u7801\u53EA\u4F1A\u65B0\u589E\u4E00\u4E2A\u673A\u5668\u4EBA\uFF0C\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA\u4F1A\u7EE7\u7EED\u6B63\u5E38\u6536\u53D1\u6D88\u606F\u3002": "Scanning adds one bot. Existing bots will continue to send and receive messages.", + "\u65E0\u9700\u624B\u52A8\u586B\u5199 App ID\u3002\u4EE5\u540E\u8FD8\u53EF\u4EE5\u7EE7\u7EED\u6DFB\u52A0\u673A\u5668\u4EBA\uFF0C\u5206\u522B\u670D\u52A1\u4E0D\u540C\u56E2\u961F\u6216\u98DE\u4E66\u79DF\u6237\u3002": "No App ID is required. You can add more bots later for different teams or Feishu tenants.", + "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u521B\u5EFA\u673A\u5668\u4EBA": "Scan with Feishu to create a bot", + "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED": "Refresh the QR code to continue", + "\u6253\u5F00\u98DE\u4E66\u79FB\u52A8\u7AEF\uFF0C\u4F7F\u7528\u626B\u4E00\u626B\u8BFB\u53D6\u4E8C\u7EF4\u7801": "Open Feishu on your phone and scan the QR code", + "\u6838\u5BF9\u5E94\u7528\u540D\u79F0\u4E0E\u6743\u9650\u8303\u56F4\uFF0C\u5E76\u786E\u8BA4\u521B\u5EFA": "Review the app name and permissions, then confirm", + "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u65B0\u673A\u5668\u4EBA\u7684\u957F\u8FDE\u63A5\u5C31\u7EEA": "Keep this page open until the bot connection is ready", + "\u5728\u98DE\u4E66\u4E2D\u6253\u5F00": "Open in Feishu", + "\u53D6\u6D88\u6DFB\u52A0": "Cancel", + "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u8FDE\u63A5\u65B0\u673A\u5668\u4EBA": "Confirmed. Connecting the new bot", + "\u6B63\u5728\u5B89\u5168\u4FDD\u5B58\u51ED\u636E\u5E76\u68C0\u67E5\u65B0\u673A\u5668\u4EBA\u7684\u6D88\u606F\u901A\u9053\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002": "Saving credentials and checking the new bot connection. Existing bots will not be interrupted.", + "\u6B63\u5728\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002": "Requesting a one-time authorization QR code from Feishu\u2026", + "\u65B0\u673A\u5668\u4EBA\u6CA1\u6709\u6DFB\u52A0\u5B8C\u6210": "The new bot was not added", + "\u65B0\u98DE\u4E66\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5F00\u59CB\u804A\u5929\u3002": "The new Feishu bot is connected and ready to chat.", + "\u98DE\u4E66\u5E94\u7528\u521B\u5EFA\u5931\u8D25": "Could not create the Feishu app", + "\u673A\u5668\u4EBA\u5DF2\u7ECF\u521B\u5EFA\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u8FDE\u63A5\u72B6\u6001": "The bot was created, but its connection could not be confirmed yet", + "\u673A\u5668\u4EBA\u4ECD\u672A\u8FDE\u63A5": "The bot is still offline", + "\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5": "The bot is not connected yet", + "\u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38": "Persistent connection is healthy", + "\u957F\u8FDE\u63A5": "Persistent connection", + "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58": "App identifier stored securely", + "\u673A\u5668\u4EBA\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58": "Bot identifier stored securely", + "\u5DF2\u5B89\u5168\u4FDD\u5B58": "Stored securely", + "\u5DF2\u63A5\u5165\u7684\u5FAE\u4FE1\u8D26\u53F7": "Connected WeChat accounts", + "\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA": "Connected bots", + "\u5DF2\u63A5\u5165\u7684\u9489\u9489\u673A\u5668\u4EBA": "Connected DingTalk bots", + "\u5DF2\u7ED1\u5B9A\u7684\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA": "Connected WeCom bots", + "\u5DF2\u7ED1\u5B9A\u7684 QQ \u673A\u5668\u4EBA": "Connected QQ bots", + "\u5DF2\u63A5\u5165\u7684 WhatsApp \u673A\u5668\u4EBA": "Connected WhatsApp accounts", + "\u4F7F\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u63CF\u4E8C\u7EF4\u7801": "Scan with WeChat on your phone", + "\u626B\u4E00\u6B21\u7801\uFF0C\u5C31\u80FD\u5728\u5FAE\u4FE1\u91CC\u4F7F\u7528 Harness": "Scan once to use Harness in WeChat", + "\u6253\u5F00\u624B\u673A\u5FAE\u4FE1\u5E76\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801": "Open WeChat on your phone and scan the QR code", + "\u5728\u5FAE\u4FE1\u4E2D\u786E\u8BA4\u8FDE\u63A5\u8BE5\u673A\u5668\u4EBA": "Confirm the bot connection in WeChat", + "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u673A\u5668\u4EBA\u81EA\u52A8\u8FDE\u63A5": "Keep this page open while the bot connects", + "\u7B49\u5F85\u5FAE\u4FE1\u626B\u7801": "Waiting for WeChat scan", + "\u9700\u8981\u914D\u5BF9\u7801": "Pairing code required", + "\u8F93\u5165\u624B\u673A\u5FAE\u4FE1\u663E\u793A\u7684\u6570\u5B57": "Enter the number shown in WeChat", + "\u5FAE\u4FE1\u914D\u5BF9\u7801": "WeChat pairing code", + "\u5DF2\u626B\u7801\uFF0C\u8BF7\u5728\u624B\u673A\u4E0A\u786E\u8BA4": "Scanned. Confirm on your phone", + "\u914D\u5BF9\u7801\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u7B49\u5F85\u5FAE\u4FE1\u786E\u8BA4\u3002": "Pairing code submitted. Waiting for WeChat confirmation.", + "\u8FD9\u662F\u5FAE\u4FE1\u9644\u52A0\u7684\u5B89\u5168\u786E\u8BA4\u6B65\u9AA4\u3002\u914D\u5BF9\u7801\u53EA\u7528\u4E8E\u672C\u6B21\u626B\u7801\u8F6E\u8BE2\uFF0C\u4E0D\u4F1A\u5199\u5165\u914D\u7F6E\u6216\u65E5\u5FD7\u3002": "This is an additional WeChat confirmation step. The pairing code is used only for this connection and is never stored.", + "\u6B63\u5728\u4FDD\u5B58\u51ED\u636E\u5E76\u9A8C\u8BC1 Harness \u4E0E\u5FAE\u4FE1\u957F\u8F6E\u8BE2\u3002": "Saving credentials and verifying the WeChat connection.", + "\u5FAE\u4FE1\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u542F\u52A8\u6D88\u606F\u8FDE\u63A5": "Confirmed in WeChat. Starting the message connection", + "\u5FAE\u4FE1\u5DF2\u7ED1\u5B9A\uFF0C\u53EF\u4EE5\u5F00\u59CB\u5411\u5DF2\u7ED1\u5B9A\u7684\u673A\u5668\u4EBA\u53D1\u6D88\u606F\u3002": "WeChat is connected and ready for messages.", + "\u8FD9\u4E2A\u5FAE\u4FE1\u8D26\u53F7\u5DF2\u7ECF\u7ED1\u5B9A\u5E76\u4FDD\u6301\u5728\u7EBF\u3002": "This WeChat account is connected and online.", + "\u5FAE\u4FE1\u8D26\u53F7\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002": "The WeChat account and local credentials were removed.", + "\u5DF2\u53D6\u6D88\u5FAE\u4FE1\u7ED1\u5B9A\u3002": "WeChat setup was cancelled.", + "\u6B63\u5728\u8054\u7CFB\u817E\u8BAF\u5FAE\u4FE1 iLink \u670D\u52A1\u3002": "Contacting the WeChat iLink service.", + "iLink \u957F\u8F6E\u8BE2": "iLink long polling", + "\u626B\u4E00\u6B21\u7801\uFF0C\u81EA\u52A8\u521B\u5EFA\u5E76\u8FDE\u63A5\u673A\u5668\u4EBA": "Scan once to create and connect a bot", + "\u4F7F\u7528\u9489\u9489 App \u5B8C\u6210\u673A\u5668\u4EBA\u6388\u6743": "Authorize the bot with the DingTalk app", + "\u4F7F\u7528\u5DF2\u52A0\u5165\u4F01\u4E1A/\u7EC4\u7EC7\u7684\u9489\u9489\u8D26\u53F7\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801": "Scan the QR code with a DingTalk account that belongs to an organization", + "\u5728\u6388\u6743\u9875\u70B9\u51FB\u201C\u4E00\u952E\u521B\u5EFA\u65B0\u673A\u5668\u4EBA\u201D": "Select \u201CCreate new bot\u201D on the authorization page", + "\u8BF7\u52FF\u5173\u95ED\u672C\u9875\uFF0C\u9489\u9489\u5B8C\u6210\u6388\u6743\u540E\u5C06\u81EA\u52A8\u7EE7\u7EED\u3002": "Keep this page open. Setup will continue after DingTalk authorization.", + "\u7B49\u5F85\u9489\u9489\u626B\u7801\u6388\u6743": "Waiting for DingTalk authorization", + "\u6388\u6743\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u521B\u5EFA\u9489\u9489\u673A\u5668\u4EBA": "Authorized. Creating the DingTalk bot", + "\u6B63\u5728\u786E\u8BA4\u9489\u9489\u6388\u6743": "Confirming DingTalk authorization", + "\u6B63\u5728\u68C0\u67E5\u9489\u9489 Stream \u957F\u8FDE\u63A5\uFF0C\u6210\u529F\u540E\u4F1A\u81EA\u52A8\u663E\u793A\u4E3A\u5728\u7EBF\u3002": "Checking the DingTalk Stream connection. It will appear online when ready.", + "\u9489\u9489\u673A\u5668\u4EBA\u5DF2\u63A5\u5165\uFF0C\u53EF\u4EE5\u5F00\u59CB\u53D1\u9001\u6D88\u606F\u3002": "The DingTalk bot is connected and ready for messages.", + "\u8FD9\u4E2A\u9489\u9489\u673A\u5668\u4EBA\u5DF2\u7ECF\u63A5\u5165\u5E76\u4FDD\u6301\u5728\u7EBF\u3002": "This DingTalk bot is connected and online.", + "Stream \u957F\u8FDE\u63A5": "Stream persistent connection", + "\u4F7F\u7528\u4F01\u4E1A\u5FAE\u4FE1 App \u626B\u7801\u521B\u5EFA\u667A\u80FD\u673A\u5668\u4EBA": "Scan with WeCom to create an AI bot", + "\u4F7F\u7528\u4F01\u4E1A\u5FAE\u4FE1 App \u5B8C\u6210\u667A\u80FD\u673A\u5668\u4EBA\u6388\u6743": "Authorize the AI bot with WeCom", + "\u6253\u5F00\u4F01\u4E1A\u5FAE\u4FE1 App\uFF0C\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801": "Open WeCom and scan the QR code", + "\u5728\u817E\u8BAF\u6388\u6743\u9875\u9762\u786E\u8BA4\u521B\u5EFA\u667A\u80FD\u673A\u5668\u4EBA": "Confirm bot creation on the Tencent authorization page", + "\u8FD4\u56DE\u8FD9\u91CC\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210": "Return here and wait for the connection to complete", + "\u7B49\u5F85\u4F01\u4E1A\u5FAE\u4FE1 App \u626B\u7801": "Waiting for WeCom scan", + "\u4F01\u4E1A\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u6B63\u5728\u8FDE\u63A5\u673A\u5668\u4EBA": "Authorized in WeCom. Connecting the bot", + "\u51ED\u636E\u6B63\u5728\u5199\u5165\u672C\u673A\uFF0C\u5E76\u542F\u52A8\u4F01\u4E1A\u5FAE\u4FE1 WebSocket \u6D88\u606F\u8FDE\u63A5\u3002": "Saving credentials locally and starting the WeCom WebSocket connection.", + "WebSocket \u957F\u8FDE\u63A5": "WebSocket persistent connection", + "\u4F7F\u7528\u624B\u673A QQ \u626B\u7801\u521B\u5EFA\u5E76\u7ED1\u5B9A\u673A\u5668\u4EBA": "Scan with mobile QQ to create and connect a bot", + "\u4F7F\u7528\u624B\u673A QQ \u5B8C\u6210\u673A\u5668\u4EBA\u7ED1\u5B9A": "Complete bot setup with mobile QQ", + "\u6253\u5F00\u624B\u673A QQ\uFF0C\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801": "Open mobile QQ and scan the QR code", + "\u5728\u817E\u8BAF\u6388\u6743\u9875\u9762\u786E\u8BA4\u521B\u5EFA\u6216\u7ED1\u5B9A\u673A\u5668\u4EBA": "Confirm bot creation or connection on the Tencent authorization page", + "\u7B49\u5F85\u624B\u673A QQ \u626B\u7801": "Waiting for mobile QQ scan", + "QQ \u5DF2\u6388\u6743\uFF0C\u6B63\u5728\u8FDE\u63A5\u673A\u5668\u4EBA": "Authorized in QQ. Connecting the bot", + "\u51ED\u636E\u6B63\u5728\u5199\u5165\u672C\u673A\uFF0C\u5E76\u542F\u52A8 QQ WebSocket \u6D88\u606F\u8FDE\u63A5\u3002": "Saving credentials locally and starting the QQ WebSocket connection.", + "\u4F7F\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801\u5373\u53EF\u63A5\u5165\u3002": "Scan the QR code with WhatsApp to connect.", + "\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801": "Scan with WhatsApp on your phone", + "\u6253\u5F00 WhatsApp \u2192 \u8BBE\u7F6E \u2192 \u5DF2\u5173\u8054\u8BBE\u5907": "Open WhatsApp \u2192 Settings \u2192 Linked devices", + "\u70B9\u51FB\u201C\u5173\u8054\u8BBE\u5907\u201D\u5E76\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801": "Select \u201CLink a device\u201D and scan the QR code", + "\u7B49\u5F85 WhatsApp \u626B\u7801": "Waiting for WhatsApp scan", + "\u5DF2\u626B\u7801\uFF0C\u6B63\u5728\u8FDE\u63A5 WhatsApp": "Scanned. Connecting WhatsApp", + "\u6B63\u5728\u5EFA\u7ACB\u5B89\u5168\u7684\u5173\u8054\u8BBE\u5907\u4F1A\u8BDD\u3002": "Creating a secure linked-device session.", + "\u5173\u8054\u8BBE\u5907\u6B63\u5728\u63A5\u5165 DeepSeek Harness\u3002": "Linking the device to DeepSeek Harness.", + "WhatsApp Web \u5173\u8054\u8BBE\u5907\u8FD0\u884C\u6B63\u5E38": "WhatsApp linked device is healthy", + "Bot API \u957F\u8F6E\u8BE2": "Bot API long polling", + " Gateway \u957F\u8FDE\u63A5": " Gateway persistent connection", + "Gateway \u957F\u8FDE\u63A5": "Gateway persistent connection", + " Socket Mode \u957F\u8FDE\u63A5": " Socket Mode persistent connection", + "Socket Mode \u957F\u8FDE\u63A5": "Socket Mode persistent connection", + "\u63A5\u5165 Telegram \u673A\u5668\u4EBA": "Connect a Telegram bot", + "\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002": "Get a Bot Token from @BotFather, then connect it here.", + "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token": "Enter the Bot Token from @BotFather", + "\u63A5\u5165 Discord \u673A\u5668\u4EBA": "Connect a Discord bot", + "\u5148\u5728 Developer Portal \u521B\u5EFA Bot \u5E76\u9080\u8BF7\u5230\u670D\u52A1\u5668\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002": "Create a bot in the Developer Portal and invite it to your server, then connect it here.", + "\u586B\u5199 Discord Developer Portal \u7684 Bot Token": "Enter the Bot Token from the Discord Developer Portal", + "\u63A5\u5165 Slack \u673A\u5668\u4EBA": "Connect a Slack bot", + "\u5148\u7528 Manifest \u521B\u5EFA\u5E76\u914D\u7F6E Slack App": "Create and configure a Slack app with the manifest", + "\u590D\u5236\u914D\u7F6E\u540E\uFF0C\u5728 Slack \u9009\u62E9 From a manifest\uFF1B\u521B\u5EFA\u5B8C\u6210\u540E\u751F\u6210 connections:write App Token\uFF0C\u5E76\u5C06\u5E94\u7528\u5B89\u88C5\u5230\u5DE5\u4F5C\u533A\u3002": "Copy the manifest and choose \u201CFrom a manifest\u201D in Slack. Then create a connections:write App Token and install the app to your workspace.", + "\u590D\u5236 Manifest": "Copy manifest", + "\u5DF2\u590D\u5236 Manifest": "Manifest copied", + "\u6253\u5F00 Slack \u521B\u5EFA\u9875": "Open Slack app creation", + "Bot Token \u6765\u81EA OAuth & Permissions\uFF1BApp Token \u6765\u81EA Basic Information\uFF0C\u5E76\u4E14\u5FC5\u987B\u5305\u542B connections:write\u3002": "Get the Bot Token from OAuth & Permissions and the App Token from Basic Information. The App Token must include connections:write.", + "\u4F7F\u7528\u5B98\u65B9 App Manifest \u5FEB\u901F\u914D\u7F6E\u673A\u5668\u4EBA\uFF0C\u518D\u586B\u5199 Bot Token \u4E0E App Token \u5EFA\u7ACB\u672C\u5730 Socket Mode \u8FDE\u63A5\u3002": "Configure the bot with the official app manifest, then enter the Bot Token and App Token to start a local Socket Mode connection.", + "Slack \u5DE5\u4F5C\u533A": "Slack workspace", + "Bot Token \u4E0E App Token": "Bot Token and App Token", + "\u586B\u5199 Bot Token": "Enter Bot Token", + "\u624B\u52A8\u63A5\u5165\u98DE\u4E66\u673A\u5668\u4EBA": "Connect Feishu bot manually", + "\u624B\u52A8\u63A5\u5165\u9489\u9489\u673A\u5668\u4EBA": "Connect DingTalk bot manually", + "\u624B\u52A8\u63A5\u5165\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA": "Connect WeCom bot manually", + "\u624B\u52A8\u63A5\u5165QQ\u673A\u5668\u4EBA": "Connect QQ bot manually", + "\u586B\u5199\u98DE\u4E66\u5F00\u653E\u5E73\u53F0 App ID": "Enter the Feishu Open Platform App ID", + "\u586B\u5199\u98DE\u4E66\u5F00\u653E\u5E73\u53F0 App Secret": "Enter the Feishu Open Platform App Secret", + "\u586B\u5199\u9489\u9489\u5E94\u7528 Client ID": "Enter the DingTalk Client ID", + "\u586B\u5199\u9489\u9489\u5E94\u7528 Client Secret": "Enter the DingTalk Client Secret", + "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u667A\u80FD\u673A\u5668\u4EBA Bot ID": "Enter the WeCom AI Bot ID", + "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u667A\u80FD\u673A\u5668\u4EBA Secret": "Enter the WeCom AI Bot Secret", + "\u586B\u5199 QQ \u5F00\u653E\u5E73\u53F0 AppID": "Enter the QQ Open Platform AppID", + "\u586B\u5199 QQ \u5F00\u653E\u5E73\u53F0 AppSecret": "Enter the QQ Open Platform AppSecret", + "\u626B\u7801\u63A5\u5165\u5FAE\u4FE1\u673A\u5668\u4EBA": "Connect WeChat bot by QR code", + "\u626B\u7801\u63A5\u5165\u98DE\u4E66\u673A\u5668\u4EBA": "Connect Feishu bot by QR code", + "\u626B\u7801\u63A5\u5165\u9489\u9489\u673A\u5668\u4EBA": "Connect DingTalk bot by QR code", + "\u626B\u7801\u63A5\u5165\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA": "Connect WeCom bot by QR code", + "\u626B\u7801\u63A5\u5165 QQ \u673A\u5668\u4EBA": "Connect QQ bot by QR code", + "\u626B\u7801\u63A5\u5165 WhatsApp \u673A\u5668\u4EBA": "Connect WhatsApp by QR code", + "\u626B\u7801\u7ED1\u5B9A WhatsApp \u673A\u5668\u4EBA": "Connect WhatsApp by QR code", + "\u4F7F\u7528 App ID \u548C App Secret \u7ED1\u5B9A\u98DE\u4E66\u673A\u5668\u4EBA": "Connect a Feishu bot with App ID and App Secret", + "\u4F7F\u7528 Client ID \u548C Client Secret \u7ED1\u5B9A\u9489\u9489\u673A\u5668\u4EBA": "Connect a DingTalk bot with Client ID and Client Secret", + "\u4F7F\u7528 Bot ID \u548C Secret \u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA": "Connect a WeCom bot with Bot ID and Secret", + "\u4F7F\u7528 AppID \u548C AppSecret \u7ED1\u5B9A QQ \u673A\u5668\u4EBA": "Connect a QQ bot with AppID and AppSecret", + "\u4F7F\u7528 Manifest \u548C\u53CC Token \u63A5\u5165 Slack \u673A\u5668\u4EBA": "Connect a Slack bot with a manifest and two tokens", + "\u4F7F\u7528 Bot Token \u63A5\u5165 Telegram \u673A\u5668\u4EBA": "Connect a Telegram bot with a Bot Token", + "\u4F7F\u7528 Bot Token \u63A5\u5165 Discord \u673A\u5668\u4EBA": "Connect a Discord bot with a Bot Token", + "\u53D6\u6D88\u7ED1\u5B9A": "Cancel setup", + "\u53D6\u6D88\u63A5\u5165": "Cancel setup", + "\u4E8C\u7EF4\u7801\u7531\u817E\u8BAF\u5FAE\u4FE1 iLink \u670D\u52A1\u7B7E\u53D1\u3002\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u63CF\u5E76\u786E\u8BA4\u540E\uFF0C\u8D26\u53F7\u51ED\u636E\u4F1A\u76F4\u63A5\u5199\u5165 Harness Host\uFF0C\u6D4F\u89C8\u5668\u4E0D\u4F1A\u6536\u5230 bot_token\u3002": "The QR code is issued by Tencent WeChat iLink. After you scan and confirm, account credentials are written directly to the Harness Host and are never exposed to the browser.", + "\u626B\u7801\u8D26\u53F7\u5FC5\u987B\u5DF2\u52A0\u5165\u4F01\u4E1A/\u7EC4\u7EC7\u3002\u5982\u679C\u9489\u9489\u63D0\u793A\u5C1A\u672A\u52A0\u5165\u7EC4\u7EC7\uFF0C\u8BF7\u5728\u63D0\u793A\u9875\u521B\u5EFA\u7EC4\u7EC7\uFF0C\u6216\u6362\u7528\u5DF2\u52A0\u5165\u7EC4\u7EC7\u7684\u8D26\u53F7\u3002": "The DingTalk account must belong to an organization. If prompted, create an organization or use an account that already belongs to one.", + "\u8BF7\u5728\u624B\u673A\u4E0A\u6838\u5BF9\u5E76\u786E\u8BA4\u6388\u6743\u3002\u90E8\u5206\u8D26\u53F7\u4F1A\u989D\u5916\u663E\u793A\u4E00\u4E2A\u914D\u5BF9\u6570\u5B57\uFF0C\u9875\u9762\u4F1A\u5728\u9700\u8981\u65F6\u63D0\u793A\u8F93\u5165\u3002": "Review and confirm authorization on your phone. Some accounts may also require a pairing number.", + "\u6388\u6743\u7531\u9489\u9489\u5B98\u65B9\u9875\u9762\u5B8C\u6210\u3002\u626B\u7801\u8D26\u53F7\u5FC5\u987B\u5DF2\u52A0\u5165\u4E00\u4E2A\u4F01\u4E1A/\u7EC4\u7EC7\u5E76\u6709\u6743\u521B\u5EFA\u673A\u5668\u4EBA\uFF1B\u521B\u5EFA\u6210\u529F\u540E\uFF0C\u5E94\u7528\u51ED\u636E\u4F1A\u76F4\u63A5\u5199\u5165 Harness Host\u3002": "Authorization is completed on DingTalk\u2019s official page. The account must belong to an organization and be allowed to create bots. Credentials are written directly to the Harness Host.", + "\u626B\u7801\u7531\u817E\u8BAF\u5B98\u65B9\u9875\u9762\u5B8C\u6210\uFF0C\u4E0D\u9700\u8981\u624B\u52A8\u586B\u5199 AppID \u6216 AppSecret\u3002\u626B\u7801\u6210\u529F\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u8FDE\u63A5 DeepSeek Harness\u3002": "Scanning is completed on Tencent\u2019s official page. No AppID or AppSecret is required, and the bot connects automatically.", + "\u626B\u7801\u7531\u817E\u8BAF\u5B98\u65B9\u9875\u9762\u5B8C\u6210\uFF0C\u4E0D\u9700\u8981\u624B\u52A8\u586B\u5199 Bot ID \u6216 Secret\u3002\u521B\u5EFA\u6210\u529F\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u8FDE\u63A5 DeepSeek Harness\u3002": "Scanning is completed on Tencent\u2019s official page. No Bot ID or Secret is required, and the bot connects automatically.", + "\u817E\u8BAF\u9875\u9762\u4F1A\u521B\u5EFA\u6216\u7ED1\u5B9A\u4E00\u4E2A QQ \u673A\u5668\u4EBA\uFF0C\u5E76\u628A\u8FDE\u63A5\u51ED\u636E\u5B89\u5168\u4EA4\u7ED9\u672C\u673A Harness Host\u3002": "Tencent will create or connect a QQ bot and securely deliver its credentials to the local Harness Host.", + "\u4F01\u4E1A\u5FAE\u4FE1\u5B98\u65B9\u9875\u9762\u4F1A\u521B\u5EFA\u4E00\u4E2A\u667A\u80FD\u673A\u5668\u4EBA\uFF0C\u5E76\u628A\u8FDE\u63A5\u51ED\u636E\u5B89\u5168\u4EA4\u7ED9\u672C\u673A Harness Host\u3002": "WeCom will create an AI bot and securely deliver its credentials to the local Harness Host.", + "\u4ECE\u6B64 Harness \u79FB\u9664\u8FD9\u4E2A\u5FAE\u4FE1\u8D26\u53F7\uFF1F": "Remove this WeChat account from Harness?", + "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 bot_token\u3001\u8D26\u53F7\u914D\u7F6E\u548C\u4F1A\u8BDD\u6620\u5C04\u3002\u5176\u4ED6\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u53D7\u5F71\u54CD\u3002": "This stops the message connection and removes the locally stored bot_token, account configuration, and session mappings. Other WeChat accounts are not affected.", + "\u6B64\u64CD\u4F5C\u4F1A\u505C\u6B62\u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u4FDD\u5B58\u5728\u672C\u673A\u7684\u63A5\u5165\u914D\u7F6E\u548C\u51ED\u636E\u3002\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u5E94\u7528\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E5F\u4E0D\u53D7\u5F71\u54CD\u3002": "This stops the bot connection and removes the locally stored configuration and credentials. The app in Feishu Open Platform is not deleted, and other bots are not affected.", + "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u9489\u9489\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002": "This stops the message connection and removes the locally stored app credentials, bot configuration, and session mappings. The bot in DingTalk Open Platform is not deleted.", + "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u4F01\u4E1A\u5FAE\u4FE1\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002": "This stops the message connection and removes the locally stored app credentials, bot configuration, and session mappings. The bot in WeCom is not deleted.", + "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u817E\u8BAF\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002": "This stops the message connection and removes the locally stored app credentials, bot configuration, and session mappings. The bot on Tencent\u2019s platform is not deleted.", + "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 WhatsApp \u5173\u8054\u8BBE\u5907\u548C\u4F1A\u8BDD\u6620\u5C04\u3002": "This stops the message connection and removes the locally stored WhatsApp linked device and session mappings.", + "\u6B63\u5728\u8BFB\u53D6\u98DE\u4E66\u673A\u5668\u4EBA\u5217\u8868": "Loading Feishu bots", + "\u6B63\u5728\u8BFB\u53D6\u98DE\u4E66\u8FDE\u63A5\u72B6\u6001\u2026": "Loading Feishu connection status\u2026", + "\u6B63\u5728\u8BFB\u53D6\u5FAE\u4FE1\u8FDE\u63A5\u72B6\u6001\u2026": "Loading WeChat connection status\u2026", + "\u6B63\u5728\u8BFB\u53D6\u9489\u9489\u8FDE\u63A5\u72B6\u6001\u2026": "Loading DingTalk connection status\u2026", + "\u901A\u8FC7\u626B\u7801\u628A\u9489\u9489\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness": "Connect a DingTalk bot to DeepSeek Harness by QR code", + "\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u7ED1\u5B9A\u8FDB\u5EA6": "DingTalk did not return QR setup progress", + "\u9489\u9489\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1": "DingTalk did not return a valid setup attempt", + "\u9489\u9489 Stream \u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38": "DingTalk Stream connection is healthy", + "\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868": "DingTalk did not return a valid bot list", + "${totals.connected} / ${totals.configured} \u5728\u7EBF": "${totals.connected} / ${totals.configured} online", + "\u7528\u4E8E\u628A\u9489\u9489\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness \u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801": "One-time QR code for connecting a DingTalk bot to DeepSeek Harness", + "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\\n\u8BF7\u91CD\u65B0\u751F\u6210": "QR code expired\\nGenerate a new one", + "\u673A\u5668\u4EBA\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u6D88\u606F\u8FDE\u63A5": "Bot created. Starting the message connection", + "\u9489\u9489\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u5B89\u5168\u7684\u4E8C\u7EF4\u7801": "DingTalk did not return a secure QR code", + "\u9489\u9489\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u9489\u9489 App \u626B\u63CF\u3002": "DingTalk QR code generated. Scan it with the DingTalk app.", + "\u9489\u9489\u673A\u5668\u4EBA\u51ED\u636E\u5DF2\u7ED1\u5B9A\u3002": "DingTalk bot credentials connected.", + "\u5DF2\u53D6\u6D88\u9489\u9489\u673A\u5668\u4EBA\u63A5\u5165\u3002": "DingTalk bot setup cancelled.", + "\u9489\u9489\u673A\u5668\u4EBA\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002": "DingTalk bot and local credentials removed.", + "\u98DE\u4E66\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u4E8C\u7EF4\u7801\u4FE1\u606F": "Feishu did not return QR code information", + "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u7684\u4E8C\u7EF4\u7801\u4FE1\u606F\u4E0D\u5B8C\u6574": "Feishu returned incomplete QR code information", + "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u673A\u5668\u4EBA\u72B6\u6001": "Feishu returned an invalid bot status", + "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u7684\u673A\u5668\u4EBA\u7F3A\u5C11 botId": "The Feishu bot is missing botId", + "\u98DE\u4E66\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u8FDE\u63A5\u72B6\u6001": "Feishu did not return connection status", + "\u98DE\u4E66\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u521B\u5EFA\u8FDB\u5EA6": "Feishu did not return creation progress", + "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u672A\u77E5\u7684\u521B\u5EFA\u72B6\u6001": "Feishu returned an unknown creation status", + "\u5DF2\u63A5\u5165 ${totals.configured} \u4E2A\u673A\u5668\u4EBA\uFF0C\u5176\u4E2D ${totals.connected} \u4E2A\u5728\u7EBF": "${totals.connected} of ${totals.configured} bots online", + "\u5C1A\u672A\u63A5\u5165\u673A\u5668\u4EBA": "No bot connected yet", + "\u7528\u4E8E\u65B0\u589E DeepSeek Harness \u98DE\u4E66\u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801": "One-time authorization QR code for adding a Feishu bot to DeepSeek Harness", + "\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u626B\u7801": "Refresh and scan again", + '${connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"}${bot.name}': '${connected ? "Check connection" : "Reconnect"} ${bot.name}', + "\u65E0\u6CD5\u8BFB\u53D6\u98DE\u4E66\u673A\u5668\u4EBA": "Could not load Feishu bots", + "\u6388\u6743\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u3002": "Authorization QR code generated. Scan it with Feishu.", + "\u98DE\u4E66\u673A\u5668\u4EBA\u51ED\u636E\u5DF2\u7ED1\u5B9A\u3002": "Feishu bot credentials connected.", + "\u5DF2\u53D6\u6D88\u6DFB\u52A0\u673A\u5668\u4EBA\u3002": "Adding the bot was cancelled.", + "${newBot.bot.name}\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5728\u98DE\u4E66\u4E2D\u5F00\u59CB\u804A\u5929\u3002": "${newBot.bot.name} is connected and ready to chat in Feishu.", + "${bot.name}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u673A\u5668\u4EBA\u72B6\u6001\u3002": "${bot.name} operation failed. Check the bot status.", + "${bot.name}\u5DF2\u4ECE\u6B64 DeepSeek Harness \u79FB\u9664\uFF1B\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u5E94\u7528\u672A\u88AB\u5220\u9664\u3002": "${bot.name} was removed from this DeepSeek Harness. The app in Feishu Open Platform was not deleted.", + "\u65E0\u6CD5\u8BFB\u53D6\u8FDE\u63A5\u72B6\u6001": "Could not load connection status", + "QQ \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u7ED1\u5B9A\u8FDB\u5EA6": "QQ did not return QR setup progress", + "QQ \u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1": "QQ did not return a valid setup attempt", + "QQ WebSocket \u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38": "QQ WebSocket connection is healthy", + "QQ \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868": "QQ did not return a valid bot list", + "\u5C1A\u672A\u7ED1\u5B9A QQ \u673A\u5668\u4EBA": "No QQ bot connected yet", + "\u7528\u4E8E\u7ED1\u5B9A QQ \u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801": "One-time QR code for connecting a QQ bot", + "${channel}${connectionSummary}\u8FD0\u884C\u6B63\u5E38": "${channel}${connectionSummary} is healthy", + "${channel} \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868": "${channel} did not return a valid bot list", + "\u4F7F\u7528 Bot Token \u63A5\u5165 ${channel} \u673A\u5668\u4EBA": "Connect a ${channel} bot with a Bot Token", + "${model.totals.connected} / ${model.totals.configured} \u5728\u7EBF": "${model.totals.connected}/${model.totals.configured} online", + " Bot API \u957F\u8F6E\u8BE2": " Bot API long polling", + "\u4F01\u4E1A\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u7ED1\u5B9A\u8FDB\u5EA6": "WeCom did not return QR setup progress", + "\u4F01\u4E1A\u5FAE\u4FE1\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1": "WeCom did not return a valid setup attempt", + "\u4F01\u4E1A\u5FAE\u4FE1 WebSocket \u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38": "WeCom WebSocket connection is healthy", + "\u4F01\u4E1A\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868": "WeCom did not return a valid bot list", + "\u5C1A\u672A\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA": "No WeCom bot connected yet", + "\u7528\u4E8E\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801": "One-time QR code for connecting a WeCom bot", + "\u5FAE\u4FE1\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1": "WeChat did not return a valid setup attempt", + "\u5FAE\u4FE1\u7ED1\u5B9A\u6CA1\u6709\u5B8C\u6210": "WeChat setup did not complete", + "\u5FAE\u4FE1\u8FDE\u63A5\u6B63\u5E38": "WeChat connection is healthy", + "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA": "WeChat connection is not ready", + "\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u8D26\u53F7\u5217\u8868": "WeChat did not return a valid account list", + "\u5C1A\u672A\u7ED1\u5B9A\u5FAE\u4FE1": "No WeChat account connected yet", + "\u7528\u4E8E\u628A\u5FAE\u4FE1\u673A\u5668\u4EBA\u7ED1\u5B9A\u5230 DeepSeek Harness \u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801": "One-time QR code for connecting a WeChat bot to DeepSeek Harness", + "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6D88\u606F\u957F\u8F6E\u8BE2\u53D8\u4E3A\u5728\u7EBF": "Keep this page open until long polling is online", + "\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u63CF\u3002": "WeChat QR code generated. Scan it with WeChat on your phone.", + "\u79FB\u9664\u5931\u8D25\uFF1A${presentError(error).message}": "Removal failed: ${presentError(error).message}", + "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u72B6\u6001": "Could not load WeChat status", + "WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u8FDB\u5EA6": "WhatsApp did not return QR setup progress", + "WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u626B\u7801\u4EFB\u52A1": "WhatsApp did not return a valid setup attempt", + "WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868": "WhatsApp did not return a valid account list", + "\u7528\u4E8E\u5173\u8054 WhatsApp \u8BBE\u5907\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801": "One-time QR code for linking a WhatsApp device" +}); +var en = EN; +var zh = Object.freeze(Object.fromEntries( + Object.keys(EN).map((key) => [key, key === "$locale" ? "zh" : key]) +)); +var translate = (key) => key; +function setImTranslator(next) { + translate = typeof next === "function" ? next : (key) => key; +} +function isEnglish() { + return translate("$locale") === "en"; +} +function channelName(value) { + return localizeText(value); +} +function translateDynamic(text5) { + let match = /^(\d+) \/ (\d+) 在线$/.exec(text5); + if (match) return `${match[1]}/${match[2]} online`; + match = /^已接入 (\d+) 个机器人,其中 (\d+) 个在线$/.exec(text5); + if (match) return `${match[2]} of ${match[1]} bots online`; + match = /^正在读取\s*(.+?)\s*机器人状态…$/.exec(text5); + if (match) return `Loading ${channelName(match[1])} bot status\u2026`; + match = /^无法读取\s*(.+?)\s*机器人状态$/.exec(text5); + if (match) return `Could not load ${channelName(match[1])} bot status`; + match = /^尚未接入\s*(.+?)\s*机器人$/.exec(text5); + if (match) return `No ${channelName(match[1])} bot connected yet`; + match = /^已接入的\s*(.+?)\s*机器人$/.exec(text5); + if (match) return `Connected ${channelName(match[1])} bots`; + match = /^手动接入(.+)机器人$/.exec(text5); + if (match) return `Connect ${channelName(match[1])} bot manually`; + match = /^(.+) 设置$/.exec(text5); + if (match) return `${channelName(match[1])} settings`; + match = /^从 DeepSeek Harness 移除“(.+)”?$/.exec(text5); + if (match) return `Remove \u201C${match[1]}\u201D from DeepSeek Harness?`; + match = /^从 DeepSeek Harness 移除(.+)$/.exec(text5); + if (match) return `Remove ${match[1]} from DeepSeek Harness`; + match = /^(检查连接|重试连接)(.+)$/.exec(text5); + if (match) return `${localizeText(match[1])} ${match[2]}`; + match = /^移除(.+)$/.exec(text5); + if (match) return `Remove ${match[1]}`; + match = /^这会停止消息连接,并删除本机保存的 (.+)、机器人配置及会话映射。(.+)中的机器人不会被自动删除。$/.exec(text5); + if (match) { + return `This stops the message connection and removes the locally stored ${localizeText(match[1])}, bot configuration, and session mappings. The bot in ${localizeText(match[2])} is not deleted.`; + } + match = /^二维码剩余 (.+)$/.exec(text5); + if (match) return `QR code expires in ${match[1]}`; + match = /^状态刷新失败:(.+)$/.exec(text5); + if (match) return `Status refresh failed: ${match[1]}`; + match = /^状态自动刷新失败:(.+)$/.exec(text5); + if (match) return `Automatic status refresh failed: ${match[1]}`; + match = /^操作失败:(.+)$/.exec(text5); + if (match) return `Operation failed: ${match[1]}`; + match = /^连接检查失败:(.+)$/.exec(text5); + if (match) return `Connection check failed: ${match[1]}`; + match = /^移除失败:(.+)$/.exec(text5); + if (match) return `Removal failed: ${match[1]}`; + const phrases = [ + ["\u4F01\u4E1A\u5FAE\u4FE1", "WeCom"], + ["DeepSeek Harness", "DeepSeek Harness"], + ["WhatsApp", "WhatsApp"], + ["Telegram", "Telegram"], + ["Discord", "Discord"], + ["Slack", "Slack"], + ["\u98DE\u4E66", "Feishu"], + ["\u9489\u9489", "DingTalk"], + ["\u5FAE\u4FE1", "WeChat"], + ["\u673A\u5668\u4EBA", "bot"], + ["\u8D26\u53F7", "account"], + ["\u5E94\u7528", "app"], + ["\u51ED\u636E", "credentials"], + ["\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94", "service returned an unrecognized response"], + ["\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868", "service did not return a valid bot list"], + ["\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5", "operation failed; try again later"], + ["\u64CD\u4F5C\u5931\u8D25", "operation failed"], + ["\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA", "connection is not ready"], + ["\u6CA1\u6709\u63A5\u5165\u5B8C\u6210", "was not connected"], + ["\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210", "was not connected"], + ["\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5", "settings are missing an RPC connection"], + ["\u8BBE\u7F6E", "settings"], + ["\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210", "connection check completed"], + ["\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5", "is still offline; the plugin will keep retrying"], + ["\u5DF2\u91CD\u65B0\u8FDE\u63A5", "reconnected"], + ["\u79FB\u9664\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5", "could not be removed; try again"], + ["\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5F00\u59CB\u804A\u5929", "is connected and ready to chat"], + ["\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5F00\u59CB\u53D1\u9001\u6D88\u606F", "is connected and ready for messages"], + ["\u670D\u52A1\u8BF7\u6C42\u5931\u8D25", "service request failed"], + ["\u8FDE\u63A5\u9047\u5230\u95EE\u9898", "connection encountered a problem"], + ["\u6B63\u5728\u8BFB\u53D6", "Loading "], + ["\u8FDE\u63A5\u72B6\u6001", "connection status"], + ["\u4E8C\u7EF4\u7801", "QR code"] + ]; + let output = text5; + for (const [source, target] of phrases) output = output.replaceAll(source, target); + return output; +} +function localizeText(value) { + if (typeof value !== "string") return value; + const exact = translate(value); + if (exact !== value || !isEnglish()) return exact; + return translateDynamic(value); +} +var LOCALIZED_PROPS = Object.freeze([ + "aria-label", + "alt", + "placeholder", + "title" +]); +function localizeChild(child) { + if (typeof child === "string") return localizeText(child); + if (Array.isArray(child)) return child.map(localizeChild); + return child; +} +function h2(type, props, ...children) { + let localizedProps = props; + if (props) { + for (const key of LOCALIZED_PROPS) { + if (typeof props[key] === "string") { + localizedProps = localizedProps === props ? { ...props } : localizedProps; + localizedProps[key] = localizeText(props[key]); + } + } + } + return React2.createElement(type, localizedProps, ...children.map(localizeChild)); +} + +// plugin-src/client/credential-binding.js function ActionIcon({ children }) { return h2("svg", { className: "dim-actionIcon", @@ -417,9 +866,9 @@ function CredentialBindingPanel({ onSubmit, onCancel }) { - const [identity, setIdentity] = React2.useState(""); - const [secret, setSecret] = React2.useState(""); - const headingId = React2.useId(); + const [identity, setIdentity] = React3.useState(""); + const [secret, setSecret] = React3.useState(""); + const headingId = React3.useId(); const hasIdentity = Boolean(identityLabel); const submit = (event) => { event.preventDefault(); @@ -640,10 +1089,9 @@ function installDingtalkStyles() { } // plugin-src/client/channels/dingtalk/index.js -var h3 = React3.createElement; var ACTIVE_PROVISION_STATES = /* @__PURE__ */ new Set(["pending", "scanned", "authorizing", "creating", "connecting"]); function DingtalkIcon({ size = 28 }) { - return h3("svg", { + return h2("svg", { width: size, height: size, viewBox: "0 0 48 48", @@ -651,13 +1099,13 @@ function DingtalkIcon({ size = 28 }) { xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false" - }, h3("path", { + }, h2("path", { fill: "currentColor", 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" })); } -var Button = React3.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) { - return h3("button", { +var Button = React4.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) { + return h2("button", { ...props, ref, type: "button", @@ -666,158 +1114,158 @@ var Button = React3.forwardRef(function Button2({ children, kind = "secondary", }, children); }); function Heading({ totals, adding, busy, onAdd, onCredential, credentialOpen, addButtonRef }) { - return h3( + return h2( "div", { className: "ddt-heading" }, - h3( + h2( "div", { className: "ddt-headingCopy" }, - h3("div", { className: "ddt-eyebrow" }, "Channel"), - h3("h2", null, "\u9489\u9489\u673A\u5668\u4EBA"), - h3("p", null, "\u901A\u8FC7\u626B\u7801\u628A\u9489\u9489\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness") + h2("div", { className: "ddt-eyebrow" }, "Channel"), + h2("h2", null, "\u9489\u9489\u673A\u5668\u4EBA"), + h2("p", null, "\u901A\u8FC7\u626B\u7801\u628A\u9489\u9489\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness") ), - h3( + h2( "div", { className: "ddt-tools" }, - h3( + h2( "div", { className: "dim-bindActions" }, - h3(Button, { + h2(Button, { kind: "primary", className: "dim-scanButton", onClick: onAdd, disabled: adding || busy, ref: addButtonRef, "aria-label": "\u626B\u7801\u63A5\u5165\u9489\u9489\u673A\u5668\u4EBA" - }, h3(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), - h3(Button, { + }, h2(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), + h2(Button, { kind: "credential", className: "dim-credentialButton", onClick: onCredential, disabled: adding || busy, "aria-pressed": credentialOpen, "aria-label": "\u4F7F\u7528 Client ID \u548C Client Secret \u7ED1\u5B9A\u9489\u9489\u673A\u5668\u4EBA" - }, h3(CredentialActionIcon), credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165") + }, h2(CredentialActionIcon), credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165") ), - totals.configured > 0 ? h3( + totals.configured > 0 ? h2( "div", { className: "ddt-badge dim-onlineBadge" }, - h3("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) + h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) ) : null ) ); } function LoadingView() { - return h3( + return h2( "div", { className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" }, - h3("div", { className: "ddt-spinner dim-spinner" }), - h3("span", null, "\u6B63\u5728\u8BFB\u53D6\u9489\u9489\u8FDE\u63A5\u72B6\u6001\u2026") + h2("div", { className: "ddt-spinner dim-spinner" }), + h2("span", null, "\u6B63\u5728\u8BFB\u53D6\u9489\u9489\u8FDE\u63A5\u72B6\u6001\u2026") ); } function EmptyView({ busy, onStart }) { - return h3( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h3( + h2( "div", { className: "ddt-cardBody ddt-empty dim-surfaceBody dim-emptyView" }, - h3( + h2( "div", { className: "dim-emptyCopy" }, - h3( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h3("span", { className: "ddt-dot dim-stateDot" }), - h3("span", null, "\u5C1A\u672A\u63A5\u5165\u9489\u9489\u673A\u5668\u4EBA") + h2("span", { className: "ddt-dot dim-stateDot" }), + h2("span", null, "\u5C1A\u672A\u63A5\u5165\u9489\u9489\u673A\u5668\u4EBA") ), - h3("h3", null, "\u626B\u4E00\u6B21\u7801\uFF0C\u81EA\u52A8\u521B\u5EFA\u5E76\u8FDE\u63A5\u673A\u5668\u4EBA"), - h3("p", null, "\u6388\u6743\u7531\u9489\u9489\u5B98\u65B9\u9875\u9762\u5B8C\u6210\u3002\u626B\u7801\u8D26\u53F7\u5FC5\u987B\u5DF2\u52A0\u5165\u4E00\u4E2A\u4F01\u4E1A/\u7EC4\u7EC7\u5E76\u6709\u6743\u521B\u5EFA\u673A\u5668\u4EBA\uFF1B\u521B\u5EFA\u6210\u529F\u540E\uFF0C\u5E94\u7528\u51ED\u636E\u4F1A\u76F4\u63A5\u5199\u5165 Harness Host\u3002"), - h3( + h2("h3", null, "\u626B\u4E00\u6B21\u7801\uFF0C\u81EA\u52A8\u521B\u5EFA\u5E76\u8FDE\u63A5\u673A\u5668\u4EBA"), + h2("p", null, "\u6388\u6743\u7531\u9489\u9489\u5B98\u65B9\u9875\u9762\u5B8C\u6210\u3002\u626B\u7801\u8D26\u53F7\u5FC5\u987B\u5DF2\u52A0\u5165\u4E00\u4E2A\u4F01\u4E1A/\u7EC4\u7EC7\u5E76\u6709\u6743\u521B\u5EFA\u673A\u5668\u4EBA\uFF1B\u521B\u5EFA\u6210\u529F\u540E\uFF0C\u5E94\u7528\u51ED\u636E\u4F1A\u76F4\u63A5\u5199\u5165 Harness Host\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h3( + h2( Button, { kind: "primary", onClick: onStart, disabled: busy }, busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u9489\u9489\u4E8C\u7EF4\u7801" ) ) ), - h3( + h2( "div", { className: "ddt-brandMark dim-emptyBrand", "aria-hidden": "true" }, - h3(DingtalkIcon, { size: 68 }) + h2(DingtalkIcon, { size: 68 }) ) ) ); } function QrPanel({ provision, now, busy, onRefresh, onCancel }) { - const [imageFailed, setImageFailed] = React3.useState(false); + const [imageFailed, setImageFailed] = React4.useState(false); const source = safeQrSource(provision.qrCodeDataUrl); const remaining = Math.max(0, provision.expiresAt - now); const expired = remaining === 0 || provision.status === "expired"; const duration = Math.max(1, provision.durationMs ?? 10 * 6e4); const progress = Math.round(Math.min(1, remaining / duration) * 100); - React3.useEffect(() => setImageFailed(false), [source]); - return h3( + React4.useEffect(() => setImageFailed(false), [source]); + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h3( + h2( "div", { className: "ddt-cardBody ddt-qrLayout dim-surfaceBody dim-qrLayout" }, - h3( + h2( "div", { className: "ddt-qrColumn dim-qrColumn" }, - h3( + h2( "div", { className: "ddt-qrFrame dim-qrFrame" }, - source && !imageFailed ? h3("img", { + source && !imageFailed ? h2("img", { src: source, alt: "\u7528\u4E8E\u628A\u9489\u9489\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness \u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801", onError: () => setImageFailed(true) - }) : h3("div", { className: "ddt-qrFallback dim-qrFallback" }, "\u4E8C\u7EF4\u7801\u56FE\u7247\u672A\u5C31\u7EEA\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u3002"), - expired ? h3("div", { className: "ddt-expired dim-qrExpired" }, "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\n\u8BF7\u91CD\u65B0\u751F\u6210") : null + }) : h2("div", { className: "ddt-qrFallback dim-qrFallback" }, "\u4E8C\u7EF4\u7801\u56FE\u7247\u672A\u5C31\u7EEA\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u3002"), + expired ? h2("div", { className: "ddt-expired dim-qrExpired" }, "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\n\u8BF7\u91CD\u65B0\u751F\u6210") : null ), - h3( + h2( "div", { className: "ddt-countdown dim-countdown" }, - h3( + h2( "div", { className: "ddt-countdownTop dim-countdownTop" }, - h3("span", null, "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), - h3("strong", null, formatRemaining(remaining)) + h2("span", null, "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), + h2("strong", null, formatRemaining(remaining)) ), - h3( + h2( "div", { className: "ddt-progress dim-progress", "aria-hidden": "true" }, - h3("span", { style: { "--ddt-progress": `${progress}%` } }) + h2("span", { style: { "--ddt-progress": `${progress}%` } }) ) ) ), - h3( + h2( "div", { className: "ddt-qrCopy dim-qrCopy" }, - h3( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h3("span", { className: "ddt-dot dim-stateDot", "data-tone": expired ? "error" : "warning" }), - h3("span", null, expired ? "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548" : "\u7B49\u5F85\u9489\u9489\u626B\u7801\u6388\u6743") + h2("span", { className: "ddt-dot dim-stateDot", "data-tone": expired ? "error" : "warning" }), + h2("span", null, expired ? "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548" : "\u7B49\u5F85\u9489\u9489\u626B\u7801\u6388\u6743") ), - h3("h3", null, expired ? "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : "\u4F7F\u7528\u9489\u9489 App \u5B8C\u6210\u673A\u5668\u4EBA\u6388\u6743"), - h3("p", null, "\u626B\u7801\u8D26\u53F7\u5FC5\u987B\u5DF2\u52A0\u5165\u4F01\u4E1A/\u7EC4\u7EC7\u3002\u5982\u679C\u9489\u9489\u63D0\u793A\u5C1A\u672A\u52A0\u5165\u7EC4\u7EC7\uFF0C\u8BF7\u5728\u63D0\u793A\u9875\u521B\u5EFA\u7EC4\u7EC7\uFF0C\u6216\u6362\u7528\u5DF2\u52A0\u5165\u7EC4\u7EC7\u7684\u8D26\u53F7\u3002"), - h3( + h2("h3", null, expired ? "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : "\u4F7F\u7528\u9489\u9489 App \u5B8C\u6210\u673A\u5668\u4EBA\u6388\u6743"), + h2("p", null, "\u626B\u7801\u8D26\u53F7\u5FC5\u987B\u5DF2\u52A0\u5165\u4F01\u4E1A/\u7EC4\u7EC7\u3002\u5982\u679C\u9489\u9489\u63D0\u793A\u5C1A\u672A\u52A0\u5165\u7EC4\u7EC7\uFF0C\u8BF7\u5728\u63D0\u793A\u9875\u521B\u5EFA\u7EC4\u7EC7\uFF0C\u6216\u6362\u7528\u5DF2\u52A0\u5165\u7EC4\u7EC7\u7684\u8D26\u53F7\u3002"), + h2( "ol", { className: "ddt-steps dim-steps" }, - h3("li", null, "\u4F7F\u7528\u5DF2\u52A0\u5165\u4F01\u4E1A/\u7EC4\u7EC7\u7684\u9489\u9489\u8D26\u53F7\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), - h3("li", null, "\u5728\u6388\u6743\u9875\u70B9\u51FB\u201C\u4E00\u952E\u521B\u5EFA\u65B0\u673A\u5668\u4EBA\u201D"), - h3("li", null, "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u673A\u5668\u4EBA\u81EA\u52A8\u8FDE\u63A5") + h2("li", null, "\u4F7F\u7528\u5DF2\u52A0\u5165\u4F01\u4E1A/\u7EC4\u7EC7\u7684\u9489\u9489\u8D26\u53F7\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), + h2("li", null, "\u5728\u6388\u6743\u9875\u70B9\u51FB\u201C\u4E00\u952E\u521B\u5EFA\u65B0\u673A\u5668\u4EBA\u201D"), + h2("li", null, "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u673A\u5668\u4EBA\u81EA\u52A8\u8FDE\u63A5") ), - h3( + h2( "div", { className: "ddt-actions dim-viewActions" }, - expired ? h3(Button, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null, - !expired ? h3(Button, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null, - h3(Button, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88") + expired ? h2(Button, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null, + !expired ? h2(Button, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null, + h2(Button, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88") ) ) ) @@ -826,16 +1274,16 @@ function QrPanel({ provision, now, busy, onRefresh, onCancel }) { function ProgressPanel({ status, busy, onCancel }) { const connecting = status === "connecting"; const creating = status === "creating"; - return h3( + return h2( "div", { className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" }, - h3("div", { className: "ddt-spinner dim-spinner" }), - h3("h3", null, connecting ? "\u673A\u5668\u4EBA\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u6D88\u606F\u8FDE\u63A5" : creating ? "\u6388\u6743\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u521B\u5EFA\u9489\u9489\u673A\u5668\u4EBA" : "\u6B63\u5728\u786E\u8BA4\u9489\u9489\u6388\u6743"), - h3("p", null, connecting ? "\u6B63\u5728\u68C0\u67E5\u9489\u9489 Stream \u957F\u8FDE\u63A5\uFF0C\u6210\u529F\u540E\u4F1A\u81EA\u52A8\u663E\u793A\u4E3A\u5728\u7EBF\u3002" : "\u8BF7\u52FF\u5173\u95ED\u672C\u9875\uFF0C\u9489\u9489\u5B8C\u6210\u6388\u6743\u540E\u5C06\u81EA\u52A8\u7EE7\u7EED\u3002"), - h3( + h2("div", { className: "ddt-spinner dim-spinner" }), + h2("h3", null, connecting ? "\u673A\u5668\u4EBA\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u6D88\u606F\u8FDE\u63A5" : creating ? "\u6388\u6743\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u521B\u5EFA\u9489\u9489\u673A\u5668\u4EBA" : "\u6B63\u5728\u786E\u8BA4\u9489\u9489\u6388\u6743"), + h2("p", null, connecting ? "\u6B63\u5728\u68C0\u67E5\u9489\u9489 Stream \u957F\u8FDE\u63A5\uFF0C\u6210\u529F\u540E\u4F1A\u81EA\u52A8\u663E\u793A\u4E3A\u5728\u7EBF\u3002" : "\u8BF7\u52FF\u5173\u95ED\u672C\u9875\uFF0C\u9489\u9489\u5B8C\u6210\u6388\u6743\u540E\u5C06\u81EA\u52A8\u7EE7\u7EED\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions", style: { justifyContent: "center", marginTop: 14 } }, - h3(Button, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u63A5\u5165") + h2(Button, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u63A5\u5165") ) ); } @@ -844,20 +1292,20 @@ function ProvisionError({ provision, busy, onRetry, onClose }) { code: "DINGTALK_PROVISION_FAILED", message: "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210" }; - return h3( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h3( + h2( "div", { className: "ddt-inlineError dim-inlineError", role: "alert" }, - h3("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"), - h3("p", null, error.message), - h3("span", { className: "ddt-errorCode" }, error.code), - h3( + h2("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"), + h2("p", null, error.message), + h2("span", { className: "ddt-errorCode" }, error.code), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h3(Button, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h3(Button, { onClick: onClose, disabled: busy }, "\u5173\u95ED") + h2(Button, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button, { onClick: onClose, disabled: busy }, "\u5173\u95ED") ) ) ); @@ -875,9 +1323,9 @@ function checkedTime(value) { } } function RemoveConfirmation({ account, busy, onConfirm, onCancel }) { - const cancelRef = React3.useRef(null); - React3.useEffect(() => cancelRef.current?.focus(), []); - return h3( + const cancelRef = React4.useRef(null); + React4.useEffect(() => cancelRef.current?.focus(), []); + return h2( "div", { className: "ddt-confirm dim-confirm", @@ -887,13 +1335,13 @@ function RemoveConfirmation({ account, busy, onConfirm, onCancel }) { if (event.key === "Escape" && !busy) onCancel(); } }, - h3("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), - h3("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u9489\u9489\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), - h3( + h2("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), + h2("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u9489\u9489\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h3(Button, { ref: cancelRef, onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), - h3( + h2(Button, { ref: cancelRef, onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), + h2( Button, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165" @@ -914,62 +1362,62 @@ function AccountCard({ const tone = account.connected ? "success" : state === "error" ? "error" : "warning"; const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA"; const summary = account.error?.message ?? (account.connected ? null : account.health.summary); - return h3( + return h2( "article", { className: "ddt-card dim-botCard", tabIndex: -1, "data-bot-id": account.botId }, - h3( + h2( "div", { className: "ddt-cardBody dim-botCardBody" }, - h3( + h2( "div", { className: "ddt-accountTop dim-botCardTop" }, - h3( + h2( "div", { className: "ddt-accountIdentity dim-botIdentity" }, - h3("div", { className: "ddt-avatar dim-botAvatar", "aria-hidden": "true" }, h3(DingtalkIcon, { size: 29 })), - h3( + h2("div", { className: "ddt-avatar dim-botAvatar", "aria-hidden": "true" }, h2(DingtalkIcon, { size: 29 })), + h2( "div", { className: "dim-botName" }, - h3("h3", { title: account.bot.name }, account.bot.name), - h3("p", { title: account.bot.clientIdMasked }, account.bot.clientIdMasked) + h2("h3", { title: account.bot.name }, account.bot.name), + h2("p", { title: account.bot.clientIdMasked }, account.bot.clientIdMasked) ) ), - h3( + h2( "div", { className: "ddt-health dim-botHealth" }, - h3("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), - h3("span", null, stateLabel) + h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), + h2("span", null, stateLabel) ) ), - h3( + h2( "dl", { className: "ddt-metrics dim-botMetrics" }, - h3( + h2( "div", { className: "ddt-metric dim-botMetric" }, - h3("dt", null, "\u6D88\u606F\u901A\u9053"), - h3("dd", null, account.connected ? "Stream \u957F\u8FDE\u63A5" : "\u79BB\u7EBF") + h2("dt", null, "\u6D88\u606F\u901A\u9053"), + h2("dd", null, account.connected ? "Stream \u957F\u8FDE\u63A5" : "\u79BB\u7EBF") ), - h3( + h2( "div", { className: "ddt-metric dim-botMetric" }, - h3("dt", null, "\u6700\u8FD1\u68C0\u67E5"), - h3("dd", null, checkedTime(account.health.lastCheckedAt)) + h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), + h2("dd", null, checkedTime(account.health.lastCheckedAt)) ) ), - h3( + h2( "div", { className: "ddt-accountFooter dim-cardFooter" }, - summary ? h3("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, - h3( + summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, + h2( "div", { className: "ddt-actions dim-cardActions" }, - h3( + h2( Button, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5" ), - h3( + h2( Button, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165" @@ -977,7 +1425,7 @@ function AccountCard({ ) ) ), - removing ? h3(RemoveConfirmation, { + removing ? h2(RemoveConfirmation, { account, busy: busy === "delete", onConfirm: onConfirmRemove, @@ -986,18 +1434,18 @@ function AccountCard({ ); } function AccountList(props) { - return h3( + return h2( "section", { className: "dim-listSection" }, - h3( + h2( "div", { className: "ddt-listHeading dim-listHeading" }, - h3("h3", null, "\u5DF2\u63A5\u5165\u7684\u9489\u9489\u673A\u5668\u4EBA") + h2("h3", null, "\u5DF2\u63A5\u5165\u7684\u9489\u9489\u673A\u5668\u4EBA") ), - h3("ul", { className: "ddt-list dim-botList" }, props.bots.map((account) => h3( + h2("ul", { className: "ddt-list dim-botList" }, props.bots.map((account) => h2( "li", { key: account.botId }, - h3(AccountCard, { + h2(AccountCard, { account, busy: props.busyByBot[account.botId], removing: props.removeTarget === account.botId, @@ -1011,27 +1459,27 @@ function AccountList(props) { } var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 }); function DingtalkSettingsTab({ rpcCall }) { - const [model, setModel] = React3.useState({ + const [model, setModel] = React4.useState({ phase: "loading", bots: [], totals: EMPTY_TOTALS, revision: 0, error: null }); - const [provision, setProvision] = React3.useState(null); - const [busy, setBusy] = React3.useState(false); - const [busyByBot, setBusyByBot] = React3.useState({}); - const [removeTarget, setRemoveTarget] = React3.useState(null); - const [credentialOpen, setCredentialOpen] = React3.useState(false); - const [credentialError, setCredentialError] = React3.useState(null); - const [notice, setNotice] = React3.useState(""); - const [now, setNow] = React3.useState(() => Date.now()); - const addButtonRef = React3.useRef(null); - const mountedRef = React3.useRef(true); - const statusRequestRef = React3.useRef(0); - const noticeFrameRef = React3.useRef(null); - const focusFrameRef = React3.useRef(null); - React3.useEffect(() => { + const [provision, setProvision] = React4.useState(null); + const [busy, setBusy] = React4.useState(false); + const [busyByBot, setBusyByBot] = React4.useState({}); + const [removeTarget, setRemoveTarget] = React4.useState(null); + const [credentialOpen, setCredentialOpen] = React4.useState(false); + const [credentialError, setCredentialError] = React4.useState(null); + const [notice, setNotice] = React4.useState(""); + const [now, setNow] = React4.useState(() => Date.now()); + const addButtonRef = React4.useRef(null); + const mountedRef = React4.useRef(true); + const statusRequestRef = React4.useRef(0); + const noticeFrameRef = React4.useRef(null); + const focusFrameRef = React4.useRef(null); + React4.useEffect(() => { mountedRef.current = true; return () => { mountedRef.current = false; @@ -1046,8 +1494,8 @@ function DingtalkSettingsTab({ rpcCall }) { } }; }, []); - React3.useEffect(() => installDingtalkStyles(), []); - const announce = React3.useCallback((message) => { + React4.useEffect(() => installDingtalkStyles(), []); + const announce = React4.useCallback((message) => { if (!mountedRef.current) return; if (noticeFrameRef.current !== null) { window.cancelAnimationFrame(noticeFrameRef.current); @@ -1061,7 +1509,7 @@ function DingtalkSettingsTab({ rpcCall }) { }); } }, []); - const focusAddButton = React3.useCallback(() => { + const focusAddButton = React4.useCallback(() => { if (!mountedRef.current) return; if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current); focusFrameRef.current = window.requestAnimationFrame(() => { @@ -1069,11 +1517,11 @@ function DingtalkSettingsTab({ rpcCall }) { if (mountedRef.current) addButtonRef.current?.focus(); }); }, []); - const invoke = React3.useCallback(async (endpoint, payload = {}, signal) => { + const invoke = React4.useCallback(async (endpoint, payload = {}, signal) => { if (typeof rpcCall !== "function") throw new TypeError("\u9489\u9489\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5"); return unwrapRpcResult(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React3.useCallback(async ({ + const loadStatus = React4.useCallback(async ({ signal, silent = false, restoreProvisioning = false @@ -1113,12 +1561,12 @@ function DingtalkSettingsTab({ rpcCall }) { return void 0; } }, [invoke]); - React3.useEffect(() => { + React4.useEffect(() => { const controller = new AbortController(); void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); - React3.useEffect(() => { + React4.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); let running = false; @@ -1137,14 +1585,14 @@ function DingtalkSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React3.useEffect(() => { + React4.useEffect(() => { if (!provision || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0; const timer = window.setInterval(() => { if (mountedRef.current) setNow(Date.now()); }, 1e3); return () => window.clearInterval(timer); }, [provision?.attemptId, provision?.status]); - const startProvisioning = React3.useCallback(async ({ replace = false } = {}) => { + const startProvisioning = React4.useCallback(async ({ replace = false } = {}) => { if (!mountedRef.current) return; setCredentialOpen(false); setCredentialError(null); @@ -1182,7 +1630,7 @@ function DingtalkSettingsTab({ rpcCall }) { if (mountedRef.current) setBusy(false); } }, [announce, invoke, provision?.attemptId]); - const bindCredentials = React3.useCallback(async ({ identity, secret }) => { + const bindCredentials = React4.useCallback(async ({ identity, secret }) => { if (!mountedRef.current) return; setBusy(true); setCredentialError(null); @@ -1207,7 +1655,7 @@ function DingtalkSettingsTab({ rpcCall }) { if (mountedRef.current) setBusy(false); } }, [announce, invoke]); - const cancelProvisioning = React3.useCallback(async () => { + const cancelProvisioning = React4.useCallback(async () => { if (!mountedRef.current) return; setBusy(true); try { @@ -1225,7 +1673,7 @@ function DingtalkSettingsTab({ rpcCall }) { if (mountedRef.current) setBusy(false); } }, [announce, focusAddButton, invoke, provision?.attemptId, provision?.status]); - React3.useEffect(() => { + React4.useEffect(() => { const attemptId = provision?.attemptId; if (!attemptId || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0; const controller = new AbortController(); @@ -1284,7 +1732,7 @@ function DingtalkSettingsTab({ rpcCall }) { timer = null; }; }, [announce, invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]); - const setBotBusy = React3.useCallback((botId, operation) => { + const setBotBusy = React4.useCallback((botId, operation) => { if (!mountedRef.current) return; setBusyByBot((current) => { const next = { ...current }; @@ -1293,7 +1741,7 @@ function DingtalkSettingsTab({ rpcCall }) { return next; }); }, []); - const runBotAction = React3.useCallback(async ({ account, operation, endpoint, payload, success }) => { + const runBotAction = React4.useCallback(async ({ account, operation, endpoint, payload, success }) => { if (!mountedRef.current) return void 0; setBotBusy(account.botId, operation); try { @@ -1311,14 +1759,14 @@ function DingtalkSettingsTab({ rpcCall }) { if (mountedRef.current) setBotBusy(account.botId, null); } }, [announce, invoke, loadStatus, setBotBusy]); - const reconnect = React3.useCallback((account) => runBotAction({ + const reconnect = React4.useCallback((account) => runBotAction({ account, operation: "reconnect", endpoint: DINGTALK_ENDPOINTS.reconnectBot, payload: { botId: account.botId }, success: (snapshot) => snapshot?.bots.find((bot) => bot.botId === account.botId)?.connected ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002" : "\u9489\u9489\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002" }), [runBotAction]); - const remove = React3.useCallback(async (account) => { + const remove = React4.useCallback(async (account) => { const snapshot = await runBotAction({ account, operation: "delete", @@ -1330,14 +1778,14 @@ function DingtalkSettingsTab({ rpcCall }) { }, [runBotAction]); let provisionView = null; if (provision?.status === "starting") { - provisionView = h3( + provisionView = h2( "div", { className: "ddt-card ddt-loading", "aria-busy": "true" }, - h3("div", { className: "ddt-spinner" }), - h3("span", null, "\u6B63\u5728\u7533\u8BF7\u9489\u9489\u6388\u6743\u4E8C\u7EF4\u7801\u2026") + h2("div", { className: "ddt-spinner" }), + h2("span", null, "\u6B63\u5728\u7533\u8BF7\u9489\u9489\u6388\u6743\u4E8C\u7EF4\u7801\u2026") ); } else if (provision?.status === "pending") { - provisionView = h3(QrPanel, { + provisionView = h2(QrPanel, { provision, now, busy, @@ -1345,20 +1793,20 @@ function DingtalkSettingsTab({ rpcCall }) { onCancel: () => void cancelProvisioning() }); } else if (["scanned", "authorizing", "creating", "connecting"].includes(provision?.status)) { - provisionView = h3(ProgressPanel, { + provisionView = h2(ProgressPanel, { status: provision.status, busy, onCancel: () => void cancelProvisioning() }); } else if (provision && ["failed", "expired", "cancelled"].includes(provision.status)) { - provisionView = h3(ProvisionError, { + provisionView = h2(ProvisionError, { provision, busy, onRetry: () => void startProvisioning({ replace: Boolean(provision.attemptId) }), onClose: () => void cancelProvisioning() }); } - const credentialView = credentialOpen ? h3(CredentialBindingPanel, { + const credentialView = credentialOpen ? h2(CredentialBindingPanel, { channel: "\u9489\u9489", identityLabel: "Client ID", identityPlaceholder: "\u586B\u5199\u9489\u9489\u5E94\u7528 Client ID", @@ -1372,10 +1820,10 @@ function DingtalkSettingsTab({ rpcCall }) { setCredentialError(null); } }) : null; - return h3( + return h2( "section", { className: "ddt-page dim-channelPage", "aria-label": "\u9489\u9489\u8BBE\u7F6E" }, - h3(Heading, { + h2(Heading, { totals: model.totals, adding: Boolean(provision), busy, @@ -1387,25 +1835,25 @@ function DingtalkSettingsTab({ rpcCall }) { credentialOpen, addButtonRef }), - h3("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice), - model.error && model.phase === "ready" ? h3("div", { className: "ddt-statusNotice dim-statusNotice", role: "alert" }, `\u72B6\u6001\u5237\u65B0\u5931\u8D25\uFF1A${model.error.message}`) : null, - model.phase === "loading" ? h3(LoadingView) : model.phase === "error" ? h3( + h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice), + model.error && model.phase === "ready" ? h2("div", { className: "ddt-statusNotice dim-statusNotice", role: "alert" }, `\u72B6\u6001\u5237\u65B0\u5931\u8D25\uFF1A${model.error.message}`) : null, + model.phase === "loading" ? h2(LoadingView) : model.phase === "error" ? h2( "div", { className: "ddt-card dim-surfaceCard" }, - h3( + h2( "div", { className: "ddt-inlineError dim-inlineError", role: "alert" }, - h3("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u9489\u9489\u673A\u5668\u4EBA\u72B6\u6001"), - h3("p", null, model.error?.message ?? "\u8BF7\u7A0D\u540E\u91CD\u8BD5"), - h3(Button, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") + h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u9489\u9489\u673A\u5668\u4EBA\u72B6\u6001"), + h2("p", null, model.error?.message ?? "\u8BF7\u7A0D\u540E\u91CD\u8BD5"), + h2(Button, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") ) - ) : h3( - React3.Fragment, + ) : h2( + React4.Fragment, null, credentialView, provisionView, - model.bots.length === 0 && !provision && !credentialOpen ? h3(EmptyView, { busy, onStart: () => void startProvisioning() }) : null, - model.bots.length > 0 ? h3(AccountList, { + model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView, { busy, onStart: () => void startProvisioning() }) : null, + model.bots.length > 0 ? h2(AccountList, { bots: model.bots, busyByBot, removeTarget, @@ -1507,10 +1955,9 @@ var normalizeSnapshot2 = api.normalizeSnapshot; var presentError2 = api.presentError; // plugin-src/client/channels/shared/token-channel.js -var React4 = __toESM(require("react"), 1); -var h4 = React4.createElement; -var Button3 = React4.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) { - return h4("button", { +var React5 = __toESM(require("react"), 1); +var Button3 = React5.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) { + return h2("button", { ...props, ref, type: "button", @@ -1558,66 +2005,66 @@ function createTokenChannelSettings(definition) { const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA"; const summary = account.error?.message ?? (account.connected ? null : account.health.summary); const identity = account.bot.username ? `@${account.bot.username}` : account.bot.idMasked; - return h4( + return h2( "article", { className: "ddt-card dim-botCard", "data-bot-id": account.botId }, - h4( + h2( "div", { className: "ddt-cardBody dim-botCardBody" }, - h4( + h2( "div", { className: "ddt-accountTop dim-botCardTop" }, - h4( + h2( "div", { className: "ddt-accountIdentity dim-botIdentity" }, - h4( + h2( "div", { className: `ddt-avatar dim-botAvatar ${avatarClass}`, "aria-hidden": "true" }, - h4(LogoGlyph, { size: 29 }) + h2(LogoGlyph, { size: 29 }) ), - h4( + h2( "div", { className: "dim-botName" }, - h4("h3", null, account.bot.name), - h4("p", null, identity) + h2("h3", null, account.bot.name), + h2("p", null, identity) ) ), - h4( + h2( "div", { className: "ddt-health dim-botHealth" }, - h4("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), - h4("span", null, stateLabel) + h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), + h2("span", null, stateLabel) ) ), - h4( + h2( "dl", { className: "ddt-metrics dim-botMetrics" }, - h4( + h2( "div", { className: "ddt-metric dim-botMetric" }, - h4("dt", null, "\u6D88\u606F\u901A\u9053"), - h4("dd", null, account.connected ? connectionLabel : "\u79BB\u7EBF") + h2("dt", null, "\u6D88\u606F\u901A\u9053"), + h2("dd", null, account.connected ? connectionLabel : "\u79BB\u7EBF") ), - h4( + h2( "div", { className: "ddt-metric dim-botMetric" }, - h4("dt", null, "\u6700\u8FD1\u68C0\u67E5"), - h4("dd", null, checkedTime2(account.health.lastCheckedAt)) + h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), + h2("dd", null, checkedTime2(account.health.lastCheckedAt)) ) ), - h4( + h2( "div", { className: "ddt-accountFooter dim-cardFooter" }, - summary ? h4("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, - h4( + summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, + h2( "div", { className: "ddt-actions dim-cardActions" }, - h4(Button3, { + h2(Button3, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"), - h4(Button3, { + h2(Button3, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, @@ -1626,16 +2073,16 @@ function createTokenChannelSettings(definition) { ) ) ), - removing ? h4( + removing ? h2( "div", { className: "ddt-confirm dim-confirm", role: "alertdialog" }, - h4("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), - h4("p", null, `\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 ${credentialNoun}\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002${platformLabel}\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002`), - h4( + h2("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), + h2("p", null, `\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 ${credentialNoun}\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002${platformLabel}\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002`), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h4(Button3, { onClick: onCancelRemove, disabled: Boolean(busy) }, "\u4FDD\u7559\u673A\u5668\u4EBA"), - h4( + h2(Button3, { onClick: onCancelRemove, disabled: Boolean(busy) }, "\u4FDD\u7559\u673A\u5668\u4EBA"), + h2( Button3, { kind: "danger", onClick: onConfirmRemove, disabled: Boolean(busy) }, busy === "delete" ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165" @@ -1645,19 +2092,19 @@ function createTokenChannelSettings(definition) { ); } function SettingsTab({ rpcCall }) { - const [model, setModel] = React4.useState({ + const [model, setModel] = React5.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null }); - const [credentialOpen, setCredentialOpen] = React4.useState(false); - const [credentialError, setCredentialError] = React4.useState(null); - const [busy, setBusy] = React4.useState(false); - const [busyByBot, setBusyByBot] = React4.useState({}); - const [removeTarget, setRemoveTarget] = React4.useState(null); - const mounted = React4.useRef(true); - React4.useEffect(() => { + const [credentialOpen, setCredentialOpen] = React5.useState(false); + const [credentialError, setCredentialError] = React5.useState(null); + const [busy, setBusy] = React5.useState(false); + const [busyByBot, setBusyByBot] = React5.useState({}); + const [removeTarget, setRemoveTarget] = React5.useState(null); + const mounted = React5.useRef(true); + React5.useEffect(() => { const disposeDingtalk = installDingtalkStyles(); const disposeChannel = installStyles(); mounted.current = true; @@ -1667,11 +2114,11 @@ function createTokenChannelSettings(definition) { disposeDingtalk(); }; }, []); - const invoke = React4.useCallback(async (endpoint, payload = {}, signal) => { + const invoke = React5.useCallback(async (endpoint, payload = {}, signal) => { if (typeof rpcCall !== "function") throw new TypeError(`${channel4} \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5`); return api4.unwrapRpcResult(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React4.useCallback(async ({ signal, silent = false } = {}) => { + const loadStatus = React5.useCallback(async ({ signal, silent = false } = {}) => { if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null })); try { const snapshot = api4.normalizeSnapshot(await invoke(endpoints.status, {}, signal)); @@ -1687,12 +2134,12 @@ function createTokenChannelSettings(definition) { } } }, [invoke]); - React4.useEffect(() => { + React5.useEffect(() => { const controller = new AbortController(); void loadStatus({ signal: controller.signal }); return () => controller.abort(); }, [loadStatus]); - React4.useEffect(() => { + React5.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); const timer = window.setInterval( @@ -1704,7 +2151,7 @@ function createTokenChannelSettings(definition) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - const bindCredentials = React4.useCallback(async (values) => { + const bindCredentials = React5.useCallback(async (values) => { setBusy(true); setCredentialError(null); try { @@ -1721,7 +2168,7 @@ function createTokenChannelSettings(definition) { if (mounted.current) setBusy(false); } }, [invoke]); - const botAction = React4.useCallback(async (account, operation, endpoint, payload) => { + const botAction = React5.useCallback(async (account, operation, endpoint, payload) => { setBusyByBot((current) => ({ ...current, [account.botId]: operation })); try { const snapshot = api4.normalizeSnapshot(await invoke(endpoint, payload)); @@ -1736,15 +2183,15 @@ function createTokenChannelSettings(definition) { }); } }, [invoke]); - const botList = model.bots.length > 0 ? h4( + const botList = model.bots.length > 0 ? h2( "section", { className: "dim-listSection" }, - h4( + h2( "div", { className: "ddt-listHeading dim-listHeading" }, - h4("h3", null, `\u5DF2\u63A5\u5165\u7684 ${channel4} \u673A\u5668\u4EBA`) + h2("h3", null, `\u5DF2\u63A5\u5165\u7684 ${channel4} \u673A\u5668\u4EBA`) ), - h4("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h4("li", { key: account.botId }, h4(AccountCard5, { + h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard5, { account, busy: busyByBot[account.botId], removing: removeTarget === account.botId, @@ -1765,22 +2212,22 @@ function createTokenChannelSettings(definition) { } })))) ) : null; - return h4( + return h2( "section", { className: `ddt-page ${pageClass} dim-channelPage`, "aria-label": `${channel4} \u8BBE\u7F6E` }, - h4( + h2( "div", { className: "ddt-heading" }, - h4( + h2( "div", { className: "ddt-tools" }, - h4( + h2( "div", { className: "dim-bindActions" }, - h4(Button3, { + h2(Button3, { kind: "credential", className: "dim-credentialButton", onClick: () => { @@ -1790,32 +2237,32 @@ function createTokenChannelSettings(definition) { disabled: busy, "aria-pressed": credentialOpen, "aria-label": credentialAriaLabel - }, h4(CredentialActionIcon), credentialOpen ? credentialCloseLabel : credentialOpenLabel) + }, h2(CredentialActionIcon), credentialOpen ? credentialCloseLabel : credentialOpenLabel) ), - model.totals.configured > 0 ? h4( + model.totals.configured > 0 ? h2( "div", { className: "ddt-badge dim-onlineBadge" }, - h4("span", null, `${model.totals.connected} / ${model.totals.configured} \u5728\u7EBF`) + h2("span", null, `${model.totals.connected} / ${model.totals.configured} \u5728\u7EBF`) ) : null ) ), - model.phase === "loading" ? h4("div", { + model.phase === "loading" ? h2("div", { className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" - }, h4("div", { className: "ddt-spinner dim-spinner" }), `\u6B63\u5728\u8BFB\u53D6 ${channel4} \u673A\u5668\u4EBA\u72B6\u6001\u2026`) : model.phase === "error" ? h4( + }, h2("div", { className: "ddt-spinner dim-spinner" }), `\u6B63\u5728\u8BFB\u53D6 ${channel4} \u673A\u5668\u4EBA\u72B6\u6001\u2026`) : model.phase === "error" ? h2( "div", { className: "ddt-card dim-surfaceCard" }, - h4( + h2( "div", { className: "ddt-inlineError dim-inlineError" }, - h4("h3", null, `\u65E0\u6CD5\u8BFB\u53D6 ${channel4} \u673A\u5668\u4EBA\u72B6\u6001`), - h4("p", null, model.error?.message), - h4(Button3, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") + h2("h3", null, `\u65E0\u6CD5\u8BFB\u53D6 ${channel4} \u673A\u5668\u4EBA\u72B6\u6001`), + h2("p", null, model.error?.message), + h2(Button3, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") ) - ) : h4( - React4.Fragment, + ) : h2( + React5.Fragment, null, - credentialOpen ? CredentialPanel ? h4(CredentialPanel, { + credentialOpen ? CredentialPanel ? h2(CredentialPanel, { busy, error: credentialError, onSubmit: bindCredentials, @@ -1823,7 +2270,7 @@ function createTokenChannelSettings(definition) { setCredentialOpen(false); setCredentialError(null); } - }) : h4(CredentialBindingPanel, { + }) : h2(CredentialBindingPanel, { channel: channel4, secretLabel: "Bot Token", secretPlaceholder: tokenPlaceholder, @@ -1835,36 +2282,36 @@ function createTokenChannelSettings(definition) { setCredentialError(null); } }) : null, - model.bots.length === 0 && !credentialOpen ? h4( + model.bots.length === 0 && !credentialOpen ? h2( "div", { className: "ddt-card dim-surfaceCard" }, - h4( + h2( "div", { className: "ddt-cardBody ddt-empty dim-surfaceBody dim-emptyView" }, - h4( + h2( "div", { className: "dim-emptyCopy" }, - h4( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h4("span", { className: "ddt-dot dim-stateDot" }), - h4("span", null, `\u5C1A\u672A\u63A5\u5165 ${channel4} \u673A\u5668\u4EBA`) + h2("span", { className: "ddt-dot dim-stateDot" }), + h2("span", null, `\u5C1A\u672A\u63A5\u5165 ${channel4} \u673A\u5668\u4EBA`) ), - h4("h3", null, emptyTitle), - h4("p", null, emptyDescription), - h4( + h2("h3", null, emptyTitle), + h2("p", null, emptyDescription), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h4(Button3, { + h2(Button3, { kind: "primary", onClick: () => setCredentialOpen(true) }, emptyActionLabel) ) ), - h4("div", { + h2("div", { className: `ddt-brandMark dim-emptyBrand ${avatarClass}`, "aria-hidden": "true" - }, h4(LogoGlyph, { size: 64 })) + }, h2(LogoGlyph, { size: 64 })) ) ) : null, botList @@ -1914,7 +2361,7 @@ var DiscordSettingsTab = channel.SettingsTab; var DiscordAccountCard = channel.AccountCard; // plugin-src/client/channels/feishu/index.js -var React6 = __toESM(require("react"), 1); +var React7 = __toESM(require("react"), 1); // plugin-src/client/channels/feishu/api.js var FEISHU_RPC_CHANNEL = "/feishu"; @@ -2132,7 +2579,7 @@ function formatRemaining2(milliseconds) { } // plugin-src/client/lifecycle.js -var React5 = __toESM(require("react"), 1); +var React6 = __toESM(require("react"), 1); function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) { let disposed = false; let timer; @@ -2194,8 +2641,8 @@ function createAnimationFrameScheduler({ requestFrame, cancelFrame }) { }; } function useAnimationFrameScheduler() { - const schedulerRef = React5.useRef(null); - React5.useEffect(() => { + const schedulerRef = React6.useRef(null); + React6.useEffect(() => { const scheduler = createAnimationFrameScheduler({ requestFrame: (callback) => window.requestAnimationFrame(callback), cancelFrame: (frame) => window.cancelAnimationFrame(frame) @@ -2206,7 +2653,7 @@ function useAnimationFrameScheduler() { if (schedulerRef.current === scheduler) schedulerRef.current = null; }; }, []); - return React5.useCallback( + return React6.useCallback( (callback, key) => schedulerRef.current?.schedule(callback, key) ?? false, [] ); @@ -2717,9 +3164,8 @@ function installFeishuStyles() { } // plugin-src/client/channels/feishu/index.js -var h5 = React6.createElement; function SvgIcon({ children, size = 18, className, viewBox = "0 0 24 24" }) { - return h5("svg", { + return h2("svg", { width: size, height: size, viewBox, @@ -2731,10 +3177,10 @@ function SvgIcon({ children, size = 18, className, viewBox = "0 0 24 24" }) { }, children); } function RobotIcon({ size = 26 }) { - return h5( + return h2( SvgIcon, { size }, - h5("rect", { + h2("rect", { x: "5", y: "7.5", width: "14", @@ -2743,7 +3189,7 @@ function RobotIcon({ size = 26 }) { stroke: "currentColor", strokeWidth: "1.7" }), - h5("path", { + h2("path", { d: "M12 4.5v3M8.7 12h.01M15.3 12h.01M9.2 15.3c1.67 1.08 3.93 1.08 5.6 0M3.5 11.5v3M20.5 11.5v3", stroke: "currentColor", strokeWidth: "1.7", @@ -2752,16 +3198,16 @@ function RobotIcon({ size = 26 }) { ); } function AlertIcon({ size = 22 }) { - return h5( + return h2( SvgIcon, { size }, - h5("path", { + h2("path", { d: "M12 3.4 21 19H3L12 3.4Z", stroke: "currentColor", strokeWidth: "1.7", strokeLinejoin: "round" }), - h5("path", { + h2("path", { d: "M12 9v4.4M12 16.6v.01", stroke: "currentColor", strokeWidth: "1.9", @@ -2770,36 +3216,36 @@ function AlertIcon({ size = 22 }) { ); } function QrIcon({ size = 58 }) { - return h5(SvgIcon, { size }, h5("path", { + return h2(SvgIcon, { size }, h2("path", { d: "M4 4h6v6H4V4Zm10 0h6v6h-6V4ZM4 14h6v6H4v-6Zm10 0h2v2h-2v-2Zm4 0h2v4h-2v-4Zm-4 4h4v2h-4v-2Z", fill: "currentColor" })); } -var Button5 = React6.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) { - return h5("button", { +var Button5 = React7.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) { + return h2("button", { ...props, ref, type: "button", className: `bxf-button ${className}`.trim(), "data-kind": kind, "data-size": size - }, icon, h5("span", null, children)); + }, icon, h2("span", null, children)); }); function BrandMark() { - return h5("div", { className: "bxf-brandMark" }, h5(RobotIcon, { size: 34 })); + return h2("div", { className: "bxf-brandMark" }, h2(RobotIcon, { size: 34 })); } function Heading2({ totals, onAdd, onCredential, credentialOpen, adding, busy, addButtonRef }) { const hasBots = totals.configured > 0; - return h5( + return h2( "div", { className: "bxf-heading" }, - h5( + h2( "div", { className: "bxf-headingTools" }, - h5( + h2( "div", { className: "dim-bindActions" }, - h5(Button5, { + h2(Button5, { kind: "primary", size: "small", className: "bxf-bindButton dim-scanButton", @@ -2808,9 +3254,9 @@ function Heading2({ totals, onAdd, onCredential, credentialOpen, adding, busy, a ref: addButtonRef, "aria-busy": busy ? "true" : void 0, "aria-label": "\u626B\u7801\u63A5\u5165\u98DE\u4E66\u673A\u5668\u4EBA", - icon: h5(QrActionIcon) + icon: h2(QrActionIcon) }, adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), - h5(Button5, { + h2(Button5, { kind: "credential", size: "small", className: "dim-credentialButton", @@ -2818,50 +3264,50 @@ function Heading2({ totals, onAdd, onCredential, credentialOpen, adding, busy, a disabled: adding || busy, "aria-pressed": credentialOpen, "aria-label": "\u4F7F\u7528 App ID \u548C App Secret \u7ED1\u5B9A\u98DE\u4E66\u673A\u5668\u4EBA", - icon: h5(CredentialActionIcon) + icon: h2(CredentialActionIcon) }, credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165") ), - hasBots ? h5("div", { + hasBots ? h2("div", { className: "bxf-totalBadge dim-onlineBadge", "aria-label": `\u5DF2\u63A5\u5165 ${totals.configured} \u4E2A\u673A\u5668\u4EBA\uFF0C\u5176\u4E2D ${totals.connected} \u4E2A\u5728\u7EBF` - }, h5("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`)) : null + }, h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`)) : null ) ); } function LoadingView2() { - return h5( + return h2( "div", { className: "bxf-card dim-surfaceCard dim-loadingView", "aria-busy": "true", "aria-label": "\u6B63\u5728\u8BFB\u53D6\u98DE\u4E66\u673A\u5668\u4EBA\u5217\u8868" }, - h5("div", { className: "dim-spinner", "aria-hidden": "true" }), - h5("span", null, "\u6B63\u5728\u8BFB\u53D6\u98DE\u4E66\u8FDE\u63A5\u72B6\u6001\u2026") + h2("div", { className: "dim-spinner", "aria-hidden": "true" }), + h2("span", null, "\u6B63\u5728\u8BFB\u53D6\u98DE\u4E66\u8FDE\u63A5\u72B6\u6001\u2026") ); } function EmptyView2({ onStart, busy }) { - return h5( + return h2( "div", { className: "bxf-card dim-surfaceCard" }, - h5( + h2( "div", { className: "bxf-cardBody bxf-intro dim-surfaceBody dim-emptyView" }, - h5( + h2( "div", { className: "bxf-introCopy dim-emptyCopy" }, - h5( + h2( "div", { className: "bxf-stateLabel dim-stateLabel" }, - h5("span", { className: "bxf-dot dim-stateDot" }), - h5("span", null, "\u5C1A\u672A\u63A5\u5165\u673A\u5668\u4EBA") + h2("span", { className: "bxf-dot dim-stateDot" }), + h2("span", null, "\u5C1A\u672A\u63A5\u5165\u673A\u5668\u4EBA") ), - h5("h3", null, "\u626B\u7801\uFF0C\u521B\u5EFA\u7B2C\u4E00\u4E2A\u98DE\u4E66\u5165\u53E3"), - h5("p", null, "\u65E0\u9700\u624B\u52A8\u586B\u5199 App ID\u3002\u4EE5\u540E\u8FD8\u53EF\u4EE5\u7EE7\u7EED\u6DFB\u52A0\u673A\u5668\u4EBA\uFF0C\u5206\u522B\u670D\u52A1\u4E0D\u540C\u56E2\u961F\u6216\u98DE\u4E66\u79DF\u6237\u3002"), - h5( + h2("h3", null, "\u626B\u7801\uFF0C\u521B\u5EFA\u7B2C\u4E00\u4E2A\u98DE\u4E66\u5165\u53E3"), + h2("p", null, "\u65E0\u9700\u624B\u52A8\u586B\u5199 App ID\u3002\u4EE5\u540E\u8FD8\u53EF\u4EE5\u7EE7\u7EED\u6DFB\u52A0\u673A\u5668\u4EBA\uFF0C\u5206\u522B\u670D\u52A1\u4E0D\u540C\u56E2\u961F\u6216\u98DE\u4E66\u79DF\u6237\u3002"), + h2( "div", { className: "bxf-actions dim-viewActions" }, - h5(Button5, { + h2(Button5, { kind: "primary", onClick: onStart, disabled: busy, @@ -2869,7 +3315,7 @@ function EmptyView2({ onStart, busy }) { }, busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u98DE\u4E66\u4E8C\u7EF4\u7801") ) ), - h5("div", { className: "bxf-markStage dim-emptyBrand", "aria-hidden": "true" }, h5(BrandMark)) + h2("div", { className: "bxf-markStage dim-emptyBrand", "aria-hidden": "true" }, h2(BrandMark)) ) ); } @@ -2887,93 +3333,93 @@ function safeQrSource2(value) { return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0; } function QrPane({ provision, now, onRefresh, onCancel, busy }) { - const [imageFailed, setImageFailed] = React6.useState(false); + const [imageFailed, setImageFailed] = React7.useState(false); const qrSource = safeQrSource2(provision.qrCodeDataUrl); const href = safeVerificationHref(provision.verificationUrl); const remaining = Math.max(0, provision.expiresAt - now); const expired = provision.expired === true || remaining === 0; const progress = Math.min(1, remaining / Math.max(1, provision.durationMs ?? remaining)); - React6.useEffect(() => setImageFailed(false), [qrSource]); - return h5( + React7.useEffect(() => setImageFailed(false), [qrSource]); + return h2( "div", { className: "bxf-card bxf-provisionCard dim-surfaceCard" }, - h5( + h2( "div", { className: "bxf-cardBody bxf-qrLayout dim-surfaceBody dim-qrLayout" }, - h5( + h2( "div", { className: "bxf-qrColumn dim-qrColumn" }, - h5( + h2( "div", { className: "bxf-qrFrame dim-qrFrame" }, - qrSource && !imageFailed ? h5("img", { + qrSource && !imageFailed ? h2("img", { src: qrSource, alt: "\u7528\u4E8E\u65B0\u589E DeepSeek Harness \u98DE\u4E66\u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801", onError: () => setImageFailed(true) - }) : h5( + }) : h2( "div", { className: "bxf-qrFallback dim-qrFallback" }, - h5("div", null, h5(QrIcon), h5("span", null, "\u4E8C\u7EF4\u7801\u672A\u5C31\u7EEA\uFF0C\u8BF7\u6253\u5F00\u6388\u6743\u94FE\u63A5")) + h2("div", null, h2(QrIcon), h2("span", null, "\u4E8C\u7EF4\u7801\u672A\u5C31\u7EEA\uFF0C\u8BF7\u6253\u5F00\u6388\u6743\u94FE\u63A5")) ), - expired ? h5( + expired ? h2( "div", { className: "bxf-expiredOverlay dim-qrExpired", role: "status" }, - h5("div", null, "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548", h5("br"), "\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u626B\u7801") + h2("div", null, "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548", h2("br"), "\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u626B\u7801") ) : null ), - h5( + h2( "div", { className: "bxf-countdown dim-countdown", "aria-label": expired ? "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548" : `\u4E8C\u7EF4\u7801\u5269\u4F59 ${formatRemaining2(remaining)}` }, - h5( + h2( "div", { className: "bxf-countdownTop dim-countdownTop", "aria-hidden": "true" }, - h5("span", null, expired ? "\u7B49\u5F85\u5237\u65B0" : "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), - h5("strong", null, formatRemaining2(remaining)) + h2("span", null, expired ? "\u7B49\u5F85\u5237\u65B0" : "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), + h2("strong", null, formatRemaining2(remaining)) ), - h5( + h2( "div", { className: "bxf-progress dim-progress", "aria-hidden": "true" }, - h5("span", { style: { "--bxf-progress": `${Math.round(progress * 100)}%` } }) + h2("span", { style: { "--bxf-progress": `${Math.round(progress * 100)}%` } }) ) ) ), - h5( + h2( "div", { className: "bxf-qrCopy dim-qrCopy" }, - h5( + h2( "div", { className: "bxf-stateLabel dim-stateLabel" }, - h5("span", { className: "bxf-dot dim-stateDot", "data-tone": "warning" }), - h5("span", null, "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA") + h2("span", { className: "bxf-dot dim-stateDot", "data-tone": "warning" }), + h2("span", null, "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA") ), - h5("h3", null, expired ? "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u521B\u5EFA\u673A\u5668\u4EBA"), - h5("p", null, "\u626B\u7801\u53EA\u4F1A\u65B0\u589E\u4E00\u4E2A\u673A\u5668\u4EBA\uFF0C\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA\u4F1A\u7EE7\u7EED\u6B63\u5E38\u6536\u53D1\u6D88\u606F\u3002"), - h5( + h2("h3", null, expired ? "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u521B\u5EFA\u673A\u5668\u4EBA"), + h2("p", null, "\u626B\u7801\u53EA\u4F1A\u65B0\u589E\u4E00\u4E2A\u673A\u5668\u4EBA\uFF0C\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA\u4F1A\u7EE7\u7EED\u6B63\u5E38\u6536\u53D1\u6D88\u606F\u3002"), + h2( "ol", { className: "bxf-steps dim-steps" }, - h5("li", null, "\u6253\u5F00\u98DE\u4E66\u79FB\u52A8\u7AEF\uFF0C\u4F7F\u7528\u626B\u4E00\u626B\u8BFB\u53D6\u4E8C\u7EF4\u7801"), - h5("li", null, "\u6838\u5BF9\u5E94\u7528\u540D\u79F0\u4E0E\u6743\u9650\u8303\u56F4\uFF0C\u5E76\u786E\u8BA4\u521B\u5EFA"), - h5("li", null, "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u65B0\u673A\u5668\u4EBA\u7684\u957F\u8FDE\u63A5\u5C31\u7EEA") + h2("li", null, "\u6253\u5F00\u98DE\u4E66\u79FB\u52A8\u7AEF\uFF0C\u4F7F\u7528\u626B\u4E00\u626B\u8BFB\u53D6\u4E8C\u7EF4\u7801"), + h2("li", null, "\u6838\u5BF9\u5E94\u7528\u540D\u79F0\u4E0E\u6743\u9650\u8303\u56F4\uFF0C\u5E76\u786E\u8BA4\u521B\u5EFA"), + h2("li", null, "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u65B0\u673A\u5668\u4EBA\u7684\u957F\u8FDE\u63A5\u5C31\u7EEA") ), - h5( + h2( "div", { className: "bxf-actions dim-viewActions" }, - expired ? h5(Button5, { + expired ? h2(Button5, { kind: "primary", onClick: onRefresh, disabled: busy - }, busy ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0\u4E8C\u7EF4\u7801") : href ? h5("a", { + }, busy ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0\u4E8C\u7EF4\u7801") : href ? h2("a", { className: "bxf-button bxf-link", "data-kind": "secondary", href, target: "_blank", rel: "noopener noreferrer" - }, h5("span", null, "\u5728\u98DE\u4E66\u4E2D\u6253\u5F00")) : null, - !expired ? h5(Button5, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null, - h5(Button5, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u6DFB\u52A0") + }, h2("span", null, "\u5728\u98DE\u4E66\u4E2D\u6253\u5F00")) : null, + !expired ? h2(Button5, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null, + h2(Button5, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u6DFB\u52A0") ) ) ) @@ -2981,44 +3427,44 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) { } function ProvisionProgress({ phase, onCancel, busy }) { const connecting = phase === "connecting"; - return h5( + return h2( "div", { className: "bxf-card bxf-provisionCard dim-surfaceCard dim-loadingView", "aria-busy": "true" }, - h5("div", { className: "dim-spinner", "aria-hidden": "true" }), - h5("h3", null, connecting ? "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u8FDE\u63A5\u65B0\u673A\u5668\u4EBA" : "\u6B63\u5728\u51C6\u5907\u6388\u6743\u4E8C\u7EF4\u7801"), - h5("p", null, connecting ? "\u6B63\u5728\u5B89\u5168\u4FDD\u5B58\u51ED\u636E\u5E76\u68C0\u67E5\u65B0\u673A\u5668\u4EBA\u7684\u6D88\u606F\u901A\u9053\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : "\u6B63\u5728\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002"), - connecting ? h5( + h2("div", { className: "dim-spinner", "aria-hidden": "true" }), + h2("h3", null, connecting ? "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u8FDE\u63A5\u65B0\u673A\u5668\u4EBA" : "\u6B63\u5728\u51C6\u5907\u6388\u6743\u4E8C\u7EF4\u7801"), + h2("p", null, connecting ? "\u6B63\u5728\u5B89\u5168\u4FDD\u5B58\u51ED\u636E\u5E76\u68C0\u67E5\u65B0\u673A\u5668\u4EBA\u7684\u6D88\u606F\u901A\u9053\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : "\u6B63\u5728\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002"), + connecting ? h2( "div", { className: "bxf-actions dim-viewActions", style: { justifyContent: "center" } }, - h5(Button5, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u6DFB\u52A0") + h2(Button5, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u6DFB\u52A0") ) : null ); } function ProvisionError2({ error, onRetry, onCancel, busy }) { - return h5( + return h2( "div", { className: "bxf-card bxf-provisionCard dim-surfaceCard" }, - h5( + h2( "div", { className: "bxf-inlineError dim-inlineError", role: "alert" }, - h5( + h2( "div", null, - h5("h3", null, "\u65B0\u673A\u5668\u4EBA\u6CA1\u6709\u6DFB\u52A0\u5B8C\u6210"), - h5("p", null, error.message), - error.code ? h5("span", { className: "bxf-errorCode" }, error.code) : null, - h5( + h2("h3", null, "\u65B0\u673A\u5668\u4EBA\u6CA1\u6709\u6DFB\u52A0\u5B8C\u6210"), + h2("p", null, error.message), + error.code ? h2("span", { className: "bxf-errorCode" }, error.code) : null, + h2( "div", { className: "bxf-actions dim-viewActions" }, - h5( + h2( Button5, { kind: "primary", onClick: onRetry, disabled: busy }, busy ? "\u91CD\u8BD5\u4E2D\u2026" : "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801" ), - h5(Button5, { onClick: onCancel, disabled: busy }, "\u5173\u95ED") + h2(Button5, { onClick: onCancel, disabled: busy }, "\u5173\u95ED") ) ) ) @@ -3043,12 +3489,12 @@ function formatCheckedTime(timestamp7) { } } function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) { - const cancelRef = React6.useRef(null); + const cancelRef = React7.useRef(null); const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-"); const titleId = `bxf-remove-title-${idPart}`; const descriptionId = `bxf-remove-description-${idPart}`; - React6.useEffect(() => cancelRef.current?.focus(), []); - return h5( + React7.useEffect(() => cancelRef.current?.focus(), []); + return h2( "div", { className: "bxf-confirm dim-confirm", @@ -3062,17 +3508,17 @@ function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) { } } }, - h5("h4", { id: titleId }, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${bot.bot.name}\u201D\uFF1F`), - h5( + h2("h4", { id: titleId }, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${bot.bot.name}\u201D\uFF1F`), + h2( "p", { id: descriptionId }, "\u6B64\u64CD\u4F5C\u4F1A\u505C\u6B62\u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u4FDD\u5B58\u5728\u672C\u673A\u7684\u63A5\u5165\u914D\u7F6E\u548C\u51ED\u636E\u3002\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u5E94\u7528\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E5F\u4E0D\u53D7\u5F71\u54CD\u3002" ), - h5( + h2( "div", { className: "bxf-actions dim-viewActions" }, - h5(Button5, { ref: cancelRef, onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), - h5( + h2(Button5, { ref: cancelRef, onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), + h2( Button5, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165" @@ -3097,7 +3543,7 @@ function BotCard({ const tone = stateForDisplay === "connected" ? "success" : stateForDisplay === "connecting" ? "warning" : "error"; const summary = actionError?.message ?? connection.error?.message ?? (connected ? null : health.summary); const titleId = `bxf-bot-${connection.botId.replace(/[^a-zA-Z0-9_-]/g, "-")}`; - return h5( + return h2( "article", { className: "bxf-card bxf-botCard dim-botCard", @@ -3106,69 +3552,69 @@ function BotCard({ tabIndex: -1, ref: cardRef }, - h5( + h2( "div", { className: "bxf-cardBody dim-botCardBody" }, - h5( + h2( "div", { className: "bxf-connectedTop dim-botCardTop" }, - h5( + h2( "div", { className: "bxf-botIdentity dim-botIdentity" }, - h5( + h2( "div", { className: "bxf-avatar dim-botAvatar", "aria-hidden": "true" }, - h5(FeishuLogoGlyph, { size: 34 }) + h2(FeishuLogoGlyph, { size: 34 }) ), - h5( + h2( "div", { className: "bxf-botName dim-botName" }, - h5("h3", { id: titleId, title: bot.name }, bot.name), - h5("p", { title: bot.appIdMasked }, bot.appIdMasked ?? "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58") + h2("h3", { id: titleId, title: bot.name }, bot.name), + h2("p", { title: bot.appIdMasked }, bot.appIdMasked ?? "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58") ) ), - h5( + h2( "div", { className: "bxf-healthPill dim-botHealth", "data-health": stateForDisplay }, - h5("span", { className: "bxf-dot dim-healthDot", "data-tone": tone }), - h5("span", null, HEALTH_LABELS[stateForDisplay] ?? "\u72B6\u6001\u672A\u77E5") + h2("span", { className: "bxf-dot dim-healthDot", "data-tone": tone }), + h2("span", null, HEALTH_LABELS[stateForDisplay] ?? "\u72B6\u6001\u672A\u77E5") ) ), - h5( + h2( "dl", { className: "bxf-statusGrid dim-botMetrics" }, - h5( + h2( "div", { className: "bxf-metric dim-botMetric" }, - h5("dt", null, "\u6D88\u606F\u901A\u9053"), - h5("dd", null, connected ? "\u957F\u8FDE\u63A5" : stateForDisplay === "connecting" ? "\u8FDE\u63A5\u4E2D" : "\u5DF2\u65AD\u5F00") + h2("dt", null, "\u6D88\u606F\u901A\u9053"), + h2("dd", null, connected ? "\u957F\u8FDE\u63A5" : stateForDisplay === "connecting" ? "\u8FDE\u63A5\u4E2D" : "\u5DF2\u65AD\u5F00") ), - h5( + h2( "div", { className: "bxf-metric dim-botMetric" }, - h5("dt", null, "\u6700\u8FD1\u68C0\u67E5"), - h5("dd", null, formatCheckedTime(health.lastCheckedAt)) + h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), + h2("dd", null, formatCheckedTime(health.lastCheckedAt)) ) ), - h5( + h2( "div", { className: "bxf-connectedFooter dim-cardFooter" }, - summary ? h5( + summary ? h2( "div", { className: "bxf-healthSummary dim-cardSummary", "data-error": actionError || connection.error ? "true" : void 0 }, summary ) : null, - h5( + h2( "div", { className: "bxf-actions bxf-botActions dim-cardActions" }, - h5(Button5, { + h2(Button5, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy), "aria-busy": busy === "reconnect" ? "true" : void 0, "aria-label": `${connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"}${bot.name}` }, busy === "reconnect" ? connected ? "\u68C0\u67E5\u4E2D\u2026" : "\u6B63\u5728\u8FDE\u63A5\u2026" : connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"), - h5(Button5, { + h2(Button5, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, @@ -3179,7 +3625,7 @@ function BotCard({ ) ) ), - removing ? h5(RemoveConfirmation2, { + removing ? h2(RemoveConfirmation2, { bot: connection, busy: busy === "delete", onConfirm: onConfirmRemove, @@ -3188,21 +3634,21 @@ function BotCard({ ); } function BotList(props) { - return h5( + return h2( "section", { className: "bxf-listSection dim-listSection", "aria-labelledby": "bxf-bot-list-title" }, - h5( + h2( "div", { className: "bxf-listHeading dim-listHeading" }, - h5("h3", { id: "bxf-bot-list-title" }, "\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA") + h2("h3", { id: "bxf-bot-list-title" }, "\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA") ), - h5( + h2( "ul", { className: "bxf-botList dim-botList", role: "list" }, - props.bots.map((bot) => h5( + props.bots.map((bot) => h2( "li", { key: bot.botId }, - h5(BotCard, { + h2(BotCard, { connection: bot, busy: props.busyByBot[bot.botId], actionError: props.errorsByBot[bot.botId], @@ -3219,22 +3665,22 @@ function BotList(props) { ); } function PageError({ error, onRetry, busy }) { - return h5( + return h2( "div", { className: "bxf-card dim-surfaceCard" }, - h5( + h2( "div", { className: "bxf-error dim-inlineError", role: "alert" }, - h5( + h2( "div", null, - h5("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u98DE\u4E66\u673A\u5668\u4EBA"), - h5("p", null, error.message), - error.code ? h5("span", { className: "bxf-errorCode" }, error.code) : null, - h5( + h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u98DE\u4E66\u673A\u5668\u4EBA"), + h2("p", null, error.message), + error.code ? h2("span", { className: "bxf-errorCode" }, error.code) : null, + h2( "div", { className: "bxf-actions dim-viewActions" }, - h5( + h2( Button5, { kind: "primary", onClick: onRetry, disabled: busy }, busy ? "\u91CD\u8BD5\u4E2D\u2026" : "\u91CD\u65B0\u8BFB\u53D6" @@ -3268,7 +3714,7 @@ function mergeFeishuSnapshotState(current, snapshot, { restoreProvisioning = fal }; } function FeishuSettingsTab({ rpcCall }) { - const [model, setModel] = React6.useState({ + const [model, setModel] = React7.useState({ phase: "loading", revision: 0, bots: [], @@ -3277,31 +3723,31 @@ function FeishuSettingsTab({ rpcCall }) { pageError: null, statusError: null }); - const [pageBusy, setPageBusy] = React6.useState(false); - const [provisionBusy, setProvisionBusy] = React6.useState(false); - const [credentialOpen, setCredentialOpen] = React6.useState(false); - const [credentialBusy, setCredentialBusy] = React6.useState(false); - const [credentialError, setCredentialError] = React6.useState(null); - const [busyByBot, setBusyByBot] = React6.useState({}); - const [errorsByBot, setErrorsByBot] = React6.useState({}); - const [removeTargetId, setRemoveTargetId] = React6.useState(null); - const [announcement, setAnnouncement] = React6.useState(""); - const [now, setNow] = React6.useState(() => Date.now()); - const [focusBotId, setFocusBotId] = React6.useState(null); - const cardRefs = React6.useRef(/* @__PURE__ */ new Map()); - const removeButtonRefs = React6.useRef(/* @__PURE__ */ new Map()); - const addButtonRef = React6.useRef(null); + const [pageBusy, setPageBusy] = React7.useState(false); + const [provisionBusy, setProvisionBusy] = React7.useState(false); + const [credentialOpen, setCredentialOpen] = React7.useState(false); + const [credentialBusy, setCredentialBusy] = React7.useState(false); + const [credentialError, setCredentialError] = React7.useState(null); + const [busyByBot, setBusyByBot] = React7.useState({}); + const [errorsByBot, setErrorsByBot] = React7.useState({}); + const [removeTargetId, setRemoveTargetId] = React7.useState(null); + const [announcement, setAnnouncement] = React7.useState(""); + const [now, setNow] = React7.useState(() => Date.now()); + const [focusBotId, setFocusBotId] = React7.useState(null); + const cardRefs = React7.useRef(/* @__PURE__ */ new Map()); + const removeButtonRefs = React7.useRef(/* @__PURE__ */ new Map()); + const addButtonRef = React7.useRef(null); const scheduleAnimationFrame = useAnimationFrameScheduler(); - const announce = React6.useCallback((message) => { + const announce = React7.useCallback((message) => { setAnnouncement(""); scheduleAnimationFrame(() => { if (message) setAnnouncement(message); }, "announcement"); }, [scheduleAnimationFrame]); - const invoke = React6.useCallback(async (endpoint, payload = {}, signal) => { + const invoke = React7.useCallback(async (endpoint, payload = {}, signal) => { return unwrapRpcResult3(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const mergeSnapshot = React6.useCallback((snapshot, { restoreProvisioning = false } = {}) => { + const mergeSnapshot = React7.useCallback((snapshot, { restoreProvisioning = false } = {}) => { const now2 = Date.now(); setModel((current) => mergeFeishuSnapshotState( current, @@ -3309,7 +3755,7 @@ function FeishuSettingsTab({ rpcCall }) { { restoreProvisioning, now: now2 } )); }, []); - const loadStatus = React6.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => { + const loadStatus = React7.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => { if (!silent) setPageBusy(true); try { const snapshot = normalizeBotsSnapshot(await invoke(FEISHU_ENDPOINTS.status, {}, signal)); @@ -3325,12 +3771,12 @@ function FeishuSettingsTab({ rpcCall }) { if (!silent && !signal?.aborted) setPageBusy(false); } }, [invoke, mergeSnapshot]); - React6.useEffect(() => { + React7.useEffect(() => { const controller = new AbortController(); void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); - React6.useEffect(() => { + React7.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); let inFlight = false; @@ -3349,7 +3795,7 @@ function FeishuSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React6.useEffect(() => { + React7.useEffect(() => { if (!focusBotId) return; const node = cardRefs.current.get(focusBotId); if (!node) return; @@ -3357,7 +3803,7 @@ function FeishuSettingsTab({ rpcCall }) { node.focus({ preventScroll: true }); setFocusBotId(null); }, [focusBotId, model.bots]); - const startProvisioning = React6.useCallback(async ({ replace = false } = {}) => { + const startProvisioning = React7.useCallback(async ({ replace = false } = {}) => { setCredentialOpen(false); setCredentialError(null); setProvisionBusy(true); @@ -3397,7 +3843,7 @@ function FeishuSettingsTab({ rpcCall }) { setProvisionBusy(false); } }, [announce, invoke, model.provisioning?.attemptId]); - const bindCredentials = React6.useCallback(async ({ identity, secret }) => { + const bindCredentials = React7.useCallback(async ({ identity, secret }) => { setCredentialBusy(true); setCredentialError(null); try { @@ -3414,7 +3860,7 @@ function FeishuSettingsTab({ rpcCall }) { setCredentialBusy(false); } }, [announce, invoke, mergeSnapshot]); - const cancelProvisioning = React6.useCallback(async () => { + const cancelProvisioning = React7.useCallback(async () => { const attemptId = model.provisioning?.attemptId; setProvisionBusy(true); try { @@ -3436,7 +3882,7 @@ function FeishuSettingsTab({ rpcCall }) { const countdownPhase = model.provisioning?.phase; const countdownExpiresAt = model.provisioning?.expiresAt; const countdownExpired = model.provisioning?.expired; - React6.useEffect(() => { + React7.useEffect(() => { if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0; const tick = () => { const timestamp7 = Date.now(); @@ -3449,7 +3895,7 @@ function FeishuSettingsTab({ rpcCall }) { const timer = window.setInterval(tick, 1e3); return () => window.clearInterval(timer); }, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]); - React6.useEffect(() => { + React7.useEffect(() => { const provision2 = model.provisioning; if (!provision2 || !["qr", "connecting"].includes(provision2.phase) || !provision2.attemptId || provision2.expired) return void 0; const controller = new AbortController(); @@ -3513,7 +3959,7 @@ function FeishuSettingsTab({ rpcCall }) { window.clearTimeout(timer); }; }, [announce, invoke, loadStatus, model.provisioning]); - const setBotBusy = React6.useCallback((botId, value) => { + const setBotBusy = React7.useCallback((botId, value) => { setBusyByBot((current) => { const next = { ...current }; if (value) next[botId] = value; @@ -3521,7 +3967,7 @@ function FeishuSettingsTab({ rpcCall }) { return next; }); }, []); - const setBotError = React6.useCallback((botId, error) => { + const setBotError = React7.useCallback((botId, error) => { setErrorsByBot((current) => { const next = { ...current }; if (error) next[botId] = presentError3(error); @@ -3529,7 +3975,7 @@ function FeishuSettingsTab({ rpcCall }) { return next; }); }, []); - const reconnectOneBot = React6.useCallback(async (connection) => { + const reconnectOneBot = React7.useCallback(async (connection) => { const { botId, bot } = connection; setBotBusy(botId, "reconnect"); setBotError(botId, null); @@ -3552,15 +3998,15 @@ function FeishuSettingsTab({ rpcCall }) { setBotBusy(botId, null); } }, [announce, invoke, mergeSnapshot, setBotBusy, setBotError]); - const requestRemove = React6.useCallback((connection) => { + const requestRemove = React7.useCallback((connection) => { setRemoveTargetId(connection.botId); }, []); - const cancelRemove = React6.useCallback(() => { + const cancelRemove = React7.useCallback(() => { const botId = removeTargetId; setRemoveTargetId(null); scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus"); }, [removeTargetId, scheduleAnimationFrame]); - const confirmRemove = React6.useCallback(async (connection) => { + const confirmRemove = React7.useCallback(async (connection) => { const { botId, bot } = connection; setBotBusy(botId, "delete"); setBotError(botId, null); @@ -3591,9 +4037,9 @@ function FeishuSettingsTab({ rpcCall }) { const provision = model.provisioning; let provisionContent = null; if (provision?.phase === "creating") { - provisionContent = h5(ProvisionProgress, { phase: "creating", busy: provisionBusy }); + provisionContent = h2(ProvisionProgress, { phase: "creating", busy: provisionBusy }); } else if (provision?.phase === "qr") { - provisionContent = h5(QrPane, { + provisionContent = h2(QrPane, { provision, now, onRefresh: () => void startProvisioning({ replace: true }), @@ -3601,20 +4047,20 @@ function FeishuSettingsTab({ rpcCall }) { busy: provisionBusy || model.phase !== "ready" }); } else if (provision?.phase === "connecting") { - provisionContent = h5(ProvisionProgress, { + provisionContent = h2(ProvisionProgress, { phase: "connecting", onCancel: () => void cancelProvisioning(), busy: provisionBusy }); } else if (provision?.phase === "error") { - provisionContent = h5(ProvisionError2, { + provisionContent = h2(ProvisionError2, { error: provision.error, onRetry: () => void startProvisioning({ replace: Boolean(provision.attemptId) }), onCancel: () => void cancelProvisioning(), busy: provisionBusy }); } - const credentialContent = credentialOpen ? h5(CredentialBindingPanel, { + const credentialContent = credentialOpen ? h2(CredentialBindingPanel, { channel: "\u98DE\u4E66", identityLabel: "App ID", identityPlaceholder: "\u586B\u5199\u98DE\u4E66\u5F00\u653E\u5E73\u53F0 App ID", @@ -3628,18 +4074,18 @@ function FeishuSettingsTab({ rpcCall }) { setCredentialError(null); } }) : null; - const setCardRef = React6.useCallback((botId, node) => { + const setCardRef = React7.useCallback((botId, node) => { if (node) cardRefs.current.set(botId, node); else cardRefs.current.delete(botId); }, []); - const setRemoveButtonRef = React6.useCallback((botId, node) => { + const setRemoveButtonRef = React7.useCallback((botId, node) => { if (node) removeButtonRefs.current.set(botId, node); else removeButtonRefs.current.delete(botId); }, []); - return h5( + return h2( "section", { className: "bxf-page dim-channelPage", "aria-label": "\u98DE\u4E66\u673A\u5668\u4EBA\u8BBE\u7F6E" }, - h5(Heading2, { + h2(Heading2, { totals: model.totals, onAdd: () => void startProvisioning(), onCredential: () => { @@ -3651,30 +4097,30 @@ function FeishuSettingsTab({ rpcCall }) { busy: provisionBusy || credentialBusy, addButtonRef }), - h5("div", { + h2("div", { className: "bxf-visuallyHidden", role: "status", "aria-live": "polite", "aria-atomic": "true" }, announcement), - model.statusError ? h5( + model.statusError ? h2( "div", { className: "bxf-statusNotice dim-statusNotice", role: "status" }, - h5(AlertIcon, { size: 16 }), - h5("span", null, `\u72B6\u6001\u81EA\u52A8\u5237\u65B0\u5931\u8D25\uFF1A${model.statusError.message}`), - h5(Button5, { size: "small", onClick: () => void loadStatus({ silent: true }), disabled: pageBusy }, "\u7ACB\u5373\u91CD\u8BD5") + h2(AlertIcon, { size: 16 }), + h2("span", null, `\u72B6\u6001\u81EA\u52A8\u5237\u65B0\u5931\u8D25\uFF1A${model.statusError.message}`), + h2(Button5, { size: "small", onClick: () => void loadStatus({ silent: true }), disabled: pageBusy }, "\u7ACB\u5373\u91CD\u8BD5") ) : null, - model.phase === "loading" ? h5(LoadingView2) : model.phase === "error" ? h5(PageError, { + model.phase === "loading" ? h2(LoadingView2) : model.phase === "error" ? h2(PageError, { error: model.pageError ?? { message: "\u65E0\u6CD5\u8BFB\u53D6\u8FDE\u63A5\u72B6\u6001" }, onRetry: () => void loadStatus(), busy: pageBusy - }) : h5( - React6.Fragment, + }) : h2( + React7.Fragment, null, credentialContent, provisionContent, - model.bots.length === 0 && !provision && !credentialOpen ? h5(EmptyView2, { onStart: () => void startProvisioning(), busy: provisionBusy }) : null, - model.bots.length > 0 ? h5(BotList, { + model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView2, { onStart: () => void startProvisioning(), busy: provisionBusy }) : null, + model.bots.length > 0 ? h2(BotList, { bots: model.bots, busyByBot, errorsByBot, @@ -3798,7 +4244,7 @@ function formatRemaining3(milliseconds) { } // plugin-src/client/channels/qq/index.js -var React7 = __toESM(require("react"), 1); +var React8 = __toESM(require("react"), 1); // plugin-src/client/channels/qq/styles.js var QQ_STYLE_ID = "xmanrui-dsh-im-qq-settings"; @@ -3822,10 +4268,9 @@ function installQqStyles() { } // plugin-src/client/channels/qq/index.js -var h6 = React7.createElement; var ACTIVE_STATES = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]); -var Button7 = React7.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) { - return h6("button", { +var Button7 = React8.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) { + return h2("button", { ...props, ref, type: "button", @@ -3846,80 +4291,80 @@ function checkedTime3(value) { } } function Heading3({ totals, adding, busy, onAdd, onCredential, credentialOpen, addButtonRef }) { - return h6( + return h2( "div", { className: "ddt-heading" }, - h6( + h2( "div", { className: "ddt-tools" }, - h6( + h2( "div", { className: "dim-bindActions" }, - h6(Button7, { + h2(Button7, { kind: "primary", className: "dim-scanButton", onClick: onAdd, disabled: adding || busy, ref: addButtonRef, "aria-label": "\u626B\u7801\u63A5\u5165 QQ \u673A\u5668\u4EBA" - }, h6(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), - h6(Button7, { + }, h2(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), + h2(Button7, { kind: "credential", className: "dim-credentialButton", onClick: onCredential, disabled: adding || busy, "aria-pressed": credentialOpen, "aria-label": "\u4F7F\u7528 AppID \u548C AppSecret \u7ED1\u5B9A QQ \u673A\u5668\u4EBA" - }, h6(CredentialActionIcon), credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165") + }, h2(CredentialActionIcon), credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165") ), - totals.configured > 0 ? h6( + totals.configured > 0 ? h2( "div", { className: "ddt-badge dim-onlineBadge" }, - h6("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) + h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) ) : null ) ); } function LoadingView3() { - return h6( + return h2( "div", { className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" }, - h6("div", { className: "ddt-spinner dim-spinner" }), - h6("span", null, "\u6B63\u5728\u8BFB\u53D6 QQ \u673A\u5668\u4EBA\u72B6\u6001\u2026") + h2("div", { className: "ddt-spinner dim-spinner" }), + h2("span", null, "\u6B63\u5728\u8BFB\u53D6 QQ \u673A\u5668\u4EBA\u72B6\u6001\u2026") ); } function EmptyView3({ busy, onStart }) { - return h6( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h6( + h2( "div", { className: "ddt-cardBody ddt-empty dim-surfaceBody dim-emptyView" }, - h6( + h2( "div", { className: "dim-emptyCopy" }, - h6( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h6("span", { className: "ddt-dot dim-stateDot" }), - h6("span", null, "\u5C1A\u672A\u7ED1\u5B9A QQ \u673A\u5668\u4EBA") + h2("span", { className: "ddt-dot dim-stateDot" }), + h2("span", null, "\u5C1A\u672A\u7ED1\u5B9A QQ \u673A\u5668\u4EBA") ), - h6("h3", null, "\u4F7F\u7528\u624B\u673A QQ \u626B\u7801\u521B\u5EFA\u5E76\u7ED1\u5B9A\u673A\u5668\u4EBA"), - h6("p", null, "\u626B\u7801\u7531\u817E\u8BAF\u5B98\u65B9\u9875\u9762\u5B8C\u6210\uFF0C\u4E0D\u9700\u8981\u624B\u52A8\u586B\u5199 AppID \u6216 AppSecret\u3002\u626B\u7801\u6210\u529F\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u8FDE\u63A5 DeepSeek Harness\u3002"), - h6( + h2("h3", null, "\u4F7F\u7528\u624B\u673A QQ \u626B\u7801\u521B\u5EFA\u5E76\u7ED1\u5B9A\u673A\u5668\u4EBA"), + h2("p", null, "\u626B\u7801\u7531\u817E\u8BAF\u5B98\u65B9\u9875\u9762\u5B8C\u6210\uFF0C\u4E0D\u9700\u8981\u624B\u52A8\u586B\u5199 AppID \u6216 AppSecret\u3002\u626B\u7801\u6210\u529F\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u8FDE\u63A5 DeepSeek Harness\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h6( + h2( Button7, { kind: "primary", onClick: onStart, disabled: busy }, busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210 QQ \u4E8C\u7EF4\u7801" ) ) ), - h6( + h2( "div", { className: "ddt-brandMark dim-emptyBrand dqq-brand", "aria-hidden": "true" }, - h6(QqLogoGlyph, { size: 64 }) + h2(QqLogoGlyph, { size: 64 }) ) ) ); @@ -3930,59 +4375,59 @@ function QrPanel2({ provision, now, busy, onRefresh, onCancel }) { const duration = Math.max(1, provision.durationMs ?? 5 * 6e4); const progress = Math.round(Math.min(1, remaining / duration) * 100); const refreshing = provision.status === "refreshing"; - return h6( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h6( + h2( "div", { className: "ddt-cardBody ddt-qrLayout dim-surfaceBody dim-qrLayout" }, - h6( + h2( "div", { className: "ddt-qrColumn dim-qrColumn" }, - h6( + h2( "div", { className: "ddt-qrFrame dim-qrFrame" }, - source ? h6("img", { src: source, alt: "\u7528\u4E8E\u7ED1\u5B9A QQ \u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801" }) : h6( + source ? h2("img", { src: source, alt: "\u7528\u4E8E\u7ED1\u5B9A QQ \u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801" }) : h2( "div", { className: "ddt-qrFallback dim-qrFallback" }, refreshing ? "\u4E8C\u7EF4\u7801\u6B63\u5728\u81EA\u52A8\u5237\u65B0\u2026" : "\u4E8C\u7EF4\u7801\u56FE\u7247\u6B63\u5728\u751F\u6210\u2026" ) ), - h6( + h2( "div", { className: "ddt-countdown dim-countdown" }, - h6( + h2( "div", { className: "ddt-countdownTop dim-countdownTop" }, - h6("span", null, "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), - h6("strong", null, refreshing ? "--:--" : formatRemaining3(remaining)) + h2("span", null, "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), + h2("strong", null, refreshing ? "--:--" : formatRemaining3(remaining)) ), - h6("div", { className: "ddt-progress dim-progress", style: { "--ddt-progress": `${progress}%` } }, h6("span")) + h2("div", { className: "ddt-progress dim-progress", style: { "--ddt-progress": `${progress}%` } }, h2("span")) ) ), - h6( + h2( "div", { className: "ddt-qrCopy dim-qrCopy" }, - h6( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h6("span", { className: "ddt-dot dim-stateDot", "data-tone": "warning" }), - h6("span", null, refreshing ? "\u6B63\u5728\u5237\u65B0\u4E8C\u7EF4\u7801" : "\u7B49\u5F85\u624B\u673A QQ \u626B\u7801") + h2("span", { className: "ddt-dot dim-stateDot", "data-tone": "warning" }), + h2("span", null, refreshing ? "\u6B63\u5728\u5237\u65B0\u4E8C\u7EF4\u7801" : "\u7B49\u5F85\u624B\u673A QQ \u626B\u7801") ), - h6("h3", null, "\u4F7F\u7528\u624B\u673A QQ \u5B8C\u6210\u673A\u5668\u4EBA\u7ED1\u5B9A"), - h6("p", null, "\u817E\u8BAF\u9875\u9762\u4F1A\u521B\u5EFA\u6216\u7ED1\u5B9A\u4E00\u4E2A QQ \u673A\u5668\u4EBA\uFF0C\u5E76\u628A\u8FDE\u63A5\u51ED\u636E\u5B89\u5168\u4EA4\u7ED9\u672C\u673A Harness Host\u3002"), - h6( + h2("h3", null, "\u4F7F\u7528\u624B\u673A QQ \u5B8C\u6210\u673A\u5668\u4EBA\u7ED1\u5B9A"), + h2("p", null, "\u817E\u8BAF\u9875\u9762\u4F1A\u521B\u5EFA\u6216\u7ED1\u5B9A\u4E00\u4E2A QQ \u673A\u5668\u4EBA\uFF0C\u5E76\u628A\u8FDE\u63A5\u51ED\u636E\u5B89\u5168\u4EA4\u7ED9\u672C\u673A Harness Host\u3002"), + h2( "ol", { className: "ddt-steps dim-steps" }, - h6("li", null, "\u6253\u5F00\u624B\u673A QQ\uFF0C\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), - h6("li", null, "\u5728\u817E\u8BAF\u6388\u6743\u9875\u9762\u786E\u8BA4\u521B\u5EFA\u6216\u7ED1\u5B9A\u673A\u5668\u4EBA"), - h6("li", null, "\u8FD4\u56DE\u8FD9\u91CC\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210") + h2("li", null, "\u6253\u5F00\u624B\u673A QQ\uFF0C\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), + h2("li", null, "\u5728\u817E\u8BAF\u6388\u6743\u9875\u9762\u786E\u8BA4\u521B\u5EFA\u6216\u7ED1\u5B9A\u673A\u5668\u4EBA"), + h2("li", null, "\u8FD4\u56DE\u8FD9\u91CC\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210") ), - h6( + h2( "div", { className: "ddt-actions dim-viewActions" }, - h6(Button7, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h6(Button7, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88") + h2(Button7, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button7, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88") ) ) ) @@ -3990,44 +4435,44 @@ function QrPanel2({ provision, now, busy, onRefresh, onCancel }) { } function ProvisionView({ provision, busy, onRetry, onClose }) { if (provision.status === "connecting") { - return h6( + return h2( "div", { className: "ddt-card ddt-loading dim-surfaceCard dim-specialView", "aria-busy": "true" }, - h6("div", { className: "ddt-spinner dim-spinner" }), - h6("h3", null, "QQ \u5DF2\u6388\u6743\uFF0C\u6B63\u5728\u8FDE\u63A5\u673A\u5668\u4EBA"), - h6("p", null, "\u51ED\u636E\u6B63\u5728\u5199\u5165\u672C\u673A\uFF0C\u5E76\u542F\u52A8 QQ WebSocket \u6D88\u606F\u8FDE\u63A5\u3002") + h2("div", { className: "ddt-spinner dim-spinner" }), + h2("h3", null, "QQ \u5DF2\u6388\u6743\uFF0C\u6B63\u5728\u8FDE\u63A5\u673A\u5668\u4EBA"), + h2("p", null, "\u51ED\u636E\u6B63\u5728\u5199\u5165\u672C\u673A\uFF0C\u5E76\u542F\u52A8 QQ WebSocket \u6D88\u606F\u8FDE\u63A5\u3002") ); } const error = provision.error ?? { code: "QQ_PROVISION_FAILED", message: "QQ \u673A\u5668\u4EBA\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210" }; - return h6( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h6( + h2( "div", { className: "ddt-inlineError dim-inlineError", role: "alert" }, - h6("h3", null, "QQ \u673A\u5668\u4EBA\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210"), - h6("p", null, error.message), - h6("span", { className: "ddt-errorCode" }, error.code), - h6( + h2("h3", null, "QQ \u673A\u5668\u4EBA\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210"), + h2("p", null, error.message), + h2("span", { className: "ddt-errorCode" }, error.code), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h6(Button7, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h6(Button7, { onClick: onClose, disabled: busy }, "\u5173\u95ED") + h2(Button7, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button7, { onClick: onClose, disabled: busy }, "\u5173\u95ED") ) ) ); } function RemoveConfirmation3({ account, busy, onConfirm, onCancel }) { - return h6( + return h2( "div", { className: "ddt-confirm dim-confirm", role: "alertdialog" }, - h6("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), - h6("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u817E\u8BAF\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), - h6( + h2("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), + h2("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u817E\u8BAF\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h6(Button7, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), - h6(Button7, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165") + h2(Button7, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), + h2(Button7, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165") ) ); } @@ -4035,52 +4480,52 @@ function AccountCard2({ account, busy, removing, onReconnect, onRequestRemove, o const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning"; const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA"; const summary = account.error?.message ?? (account.connected ? null : account.health.summary); - return h6( + return h2( "article", { className: "ddt-card dim-botCard", "data-bot-id": account.botId }, - h6( + h2( "div", { className: "ddt-cardBody dim-botCardBody" }, - h6( + h2( "div", { className: "ddt-accountTop dim-botCardTop" }, - h6( + h2( "div", { className: "ddt-accountIdentity dim-botIdentity" }, - h6("div", { className: "ddt-avatar dim-botAvatar dqq-avatar", "aria-hidden": "true" }, h6(QqLogoGlyph, { size: 29 })), - h6( + h2("div", { className: "ddt-avatar dim-botAvatar dqq-avatar", "aria-hidden": "true" }, h2(QqLogoGlyph, { size: 29 })), + h2( "div", { className: "dim-botName" }, - h6("h3", null, account.bot.name), - h6("p", null, account.bot.appIdMasked) + h2("h3", null, account.bot.name), + h2("p", null, account.bot.appIdMasked) ) ), - h6( + h2( "div", { className: "ddt-health dim-botHealth" }, - h6("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), - h6("span", null, stateLabel) + h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), + h2("span", null, stateLabel) ) ), - h6( + h2( "dl", { className: "ddt-metrics dim-botMetrics" }, - h6("div", { className: "ddt-metric dim-botMetric" }, h6("dt", null, "\u6D88\u606F\u901A\u9053"), h6("dd", null, account.connected ? "WebSocket \u957F\u8FDE\u63A5" : "\u79BB\u7EBF")), - h6("div", { className: "ddt-metric dim-botMetric" }, h6("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h6("dd", null, checkedTime3(account.health.lastCheckedAt))) + 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")), + h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h2("dd", null, checkedTime3(account.health.lastCheckedAt))) ), - h6( + h2( "div", { className: "ddt-accountFooter dim-cardFooter" }, - summary ? h6("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, - h6( + summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, + h2( "div", { className: "ddt-actions dim-cardActions" }, - h6(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"), - h6(Button7, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165") + 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"), + h2(Button7, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165") ) ) ), - removing ? h6(RemoveConfirmation3, { + removing ? h2(RemoveConfirmation3, { account, busy: busy === "delete", onConfirm: onConfirmRemove, @@ -4089,17 +4534,17 @@ function AccountCard2({ account, busy, removing, onReconnect, onRequestRemove, o ); } function QqSettingsTab({ rpcCall }) { - const [model, setModel] = React7.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null }); - const [provision, setProvision] = React7.useState(null); - const [busy, setBusy] = React7.useState(false); - const [busyByBot, setBusyByBot] = React7.useState({}); - const [removeTarget, setRemoveTarget] = React7.useState(null); - const [credentialOpen, setCredentialOpen] = React7.useState(false); - const [credentialError, setCredentialError] = React7.useState(null); - const [now, setNow] = React7.useState(Date.now()); - const mounted = React7.useRef(true); - const addButtonRef = React7.useRef(null); - React7.useEffect(() => { + const [model, setModel] = React8.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null }); + const [provision, setProvision] = React8.useState(null); + const [busy, setBusy] = React8.useState(false); + const [busyByBot, setBusyByBot] = React8.useState({}); + const [removeTarget, setRemoveTarget] = React8.useState(null); + const [credentialOpen, setCredentialOpen] = React8.useState(false); + const [credentialError, setCredentialError] = React8.useState(null); + const [now, setNow] = React8.useState(Date.now()); + const mounted = React8.useRef(true); + const addButtonRef = React8.useRef(null); + React8.useEffect(() => { const disposeDingtalk = installDingtalkStyles(); const disposeQq = installQqStyles(); mounted.current = true; @@ -4109,11 +4554,11 @@ function QqSettingsTab({ rpcCall }) { disposeDingtalk(); }; }, []); - const invoke = React7.useCallback(async (endpoint, payload = {}, signal) => { + const invoke = React8.useCallback(async (endpoint, payload = {}, signal) => { if (typeof rpcCall !== "function") throw new TypeError("QQ \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5"); return unwrapRpcResult4(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React7.useCallback(async ({ signal, silent = false, restore = false } = {}) => { + const loadStatus = React8.useCallback(async ({ signal, silent = false, restore = false } = {}) => { if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null })); try { const snapshot = normalizeSnapshot3(await invoke(QQ_ENDPOINTS.status, {}, signal)); @@ -4131,12 +4576,12 @@ function QqSettingsTab({ rpcCall }) { return void 0; } }, [invoke]); - React7.useEffect(() => { + React8.useEffect(() => { const controller = new AbortController(); void loadStatus({ signal: controller.signal, restore: true }); return () => controller.abort(); }, [loadStatus]); - React7.useEffect(() => { + React8.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3); @@ -4145,12 +4590,12 @@ function QqSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React7.useEffect(() => { + React8.useEffect(() => { if (!provision || !ACTIVE_STATES.has(provision.status)) return void 0; const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3); return () => window.clearInterval(timer); }, [provision?.attemptId, provision?.status]); - const startProvisioning = React7.useCallback(async (replace = false) => { + const startProvisioning = React8.useCallback(async (replace = false) => { setCredentialOpen(false); setCredentialError(null); setBusy(true); @@ -4168,7 +4613,7 @@ function QqSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke, provision?.attemptId]); - const bindCredentials = React7.useCallback(async ({ identity, secret }) => { + const bindCredentials = React8.useCallback(async ({ identity, secret }) => { setBusy(true); setCredentialError(null); try { @@ -4185,7 +4630,7 @@ function QqSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke]); - const closeProvision = React7.useCallback(async () => { + const closeProvision = React8.useCallback(async () => { setBusy(true); try { if (provision?.attemptId && ACTIVE_STATES.has(provision.status)) { @@ -4196,7 +4641,7 @@ function QqSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke, provision?.attemptId, provision?.status]); - React7.useEffect(() => { + React8.useEffect(() => { const attemptId = provision?.attemptId; if (!attemptId || !ACTIVE_STATES.has(provision.status)) return void 0; const controller = new AbortController(); @@ -4226,7 +4671,7 @@ function QqSettingsTab({ rpcCall }) { window.clearTimeout(timer); }; }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]); - const botAction = React7.useCallback(async (account, operation, endpoint, payload) => { + const botAction = React8.useCallback(async (account, operation, endpoint, payload) => { setBusyByBot((current) => ({ ...current, [account.botId]: operation })); try { const snapshot = normalizeSnapshot3(await invoke(endpoint, payload)); @@ -4241,29 +4686,29 @@ function QqSettingsTab({ rpcCall }) { } }, [invoke]); let provisionView = null; - if (provision?.status === "starting") provisionView = h6("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h6("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7 QQ \u4E8C\u7EF4\u7801\u2026"); - else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h6(QrPanel2, { + if (provision?.status === "starting") provisionView = h2("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h2("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7 QQ \u4E8C\u7EF4\u7801\u2026"); + else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h2(QrPanel2, { provision, now, busy, onRefresh: () => void startProvisioning(true), onCancel: () => void closeProvision() }); - else if (provision) provisionView = h6(ProvisionView, { + else if (provision) provisionView = h2(ProvisionView, { provision, busy, onRetry: () => void startProvisioning(true), onClose: () => void closeProvision() }); - const botList = model.bots.length > 0 ? h6( + const botList = model.bots.length > 0 ? h2( "section", { className: "dim-listSection" }, - h6( + h2( "div", { className: "ddt-listHeading dim-listHeading" }, - h6("h3", null, "\u5DF2\u7ED1\u5B9A\u7684 QQ \u673A\u5668\u4EBA") + h2("h3", null, "\u5DF2\u7ED1\u5B9A\u7684 QQ \u673A\u5668\u4EBA") ), - h6("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h6("li", { key: account.botId }, h6(AccountCard2, { + h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard2, { account, busy: busyByBot[account.botId], removing: removeTarget === account.botId, @@ -4276,7 +4721,7 @@ function QqSettingsTab({ rpcCall }) { } })))) ) : null; - const credentialView = credentialOpen ? h6(CredentialBindingPanel, { + const credentialView = credentialOpen ? h2(CredentialBindingPanel, { channel: "QQ", identityLabel: "AppID", identityPlaceholder: "\u586B\u5199 QQ \u5F00\u653E\u5E73\u53F0 AppID", @@ -4290,10 +4735,10 @@ function QqSettingsTab({ rpcCall }) { setCredentialError(null); } }) : null; - return h6( + return h2( "section", { className: "ddt-page dqq-page dim-channelPage", "aria-label": "QQ \u8BBE\u7F6E" }, - h6(Heading3, { + h2(Heading3, { totals: model.totals, adding: Boolean(provision), busy, @@ -4305,12 +4750,12 @@ function QqSettingsTab({ rpcCall }) { credentialOpen, addButtonRef }), - model.phase === "loading" ? h6(LoadingView3) : model.phase === "error" ? h6("div", { className: "ddt-card dim-surfaceCard" }, h6("div", { className: "ddt-inlineError dim-inlineError" }, h6("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 QQ \u673A\u5668\u4EBA\u72B6\u6001"), h6("p", null, model.error?.message), h6(Button7, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h6( - React7.Fragment, + 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( + React8.Fragment, null, credentialView, provisionView, - model.bots.length === 0 && !provision && !credentialOpen ? h6(EmptyView3, { busy, onStart: () => void startProvisioning() }) : null, + model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView3, { busy, onStart: () => void startProvisioning() }) : null, botList ) ); @@ -4325,7 +4770,7 @@ var normalizeSnapshot4 = api2.normalizeSnapshot; var presentError5 = api2.presentError; // plugin-src/client/channels/slack/index.js -var React8 = __toESM(require("react"), 1); +var React9 = __toESM(require("react"), 1); // src/channels/slack/manifest.mjs var SLACK_APP_MANIFEST_YAML = `_metadata: @@ -4396,12 +4841,11 @@ function installSlackStyles() { } // plugin-src/client/channels/slack/index.js -var h7 = React8.createElement; function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) { - const [botToken, setBotToken] = React8.useState(""); - const [appToken, setAppToken] = React8.useState(""); - const [copied, setCopied] = React8.useState(false); - const headingId = React8.useId(); + const [botToken, setBotToken] = React9.useState(""); + const [appToken, setAppToken] = React9.useState(""); + const [copied, setCopied] = React9.useState(false); + const headingId = React9.useId(); const copyManifest = async () => { try { await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML); @@ -4418,32 +4862,32 @@ function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) { if (!normalizedBotToken || !normalizedAppToken || busy) return; void onSubmit?.({ botToken: normalizedBotToken, appToken: normalizedAppToken }); }; - return h7( + return h2( "section", { className: "ddt-card dim-surfaceCard dim-credentialPanel dsl-setup", "aria-labelledby": headingId }, - h7("h3", { id: headingId, className: "dim-credentialTitle" }, "\u63A5\u5165 Slack \u673A\u5668\u4EBA"), - h7( + h2("h3", { id: headingId, className: "dim-credentialTitle" }, "\u63A5\u5165 Slack \u673A\u5668\u4EBA"), + h2( "div", { className: "dsl-guide" }, - h7( + h2( "div", { className: "dsl-guideCopy" }, - h7("strong", null, "\u5148\u7528 Manifest \u521B\u5EFA\u5E76\u914D\u7F6E Slack App"), - h7("p", null, "\u590D\u5236\u914D\u7F6E\u540E\uFF0C\u5728 Slack \u9009\u62E9 From a manifest\uFF1B\u521B\u5EFA\u5B8C\u6210\u540E\u751F\u6210 connections:write App Token\uFF0C\u5E76\u5C06\u5E94\u7528\u5B89\u88C5\u5230\u5DE5\u4F5C\u533A\u3002") + h2("strong", null, "\u5148\u7528 Manifest \u521B\u5EFA\u5E76\u914D\u7F6E Slack App"), + h2("p", null, "\u590D\u5236\u914D\u7F6E\u540E\uFF0C\u5728 Slack \u9009\u62E9 From a manifest\uFF1B\u521B\u5EFA\u5B8C\u6210\u540E\u751F\u6210 connections:write App Token\uFF0C\u5E76\u5C06\u5E94\u7528\u5B89\u88C5\u5230\u5DE5\u4F5C\u533A\u3002") ), - h7( + h2( "div", { className: "dsl-guideActions" }, - h7("button", { + h2("button", { type: "button", className: "ddt-button", onClick: () => void copyManifest(), disabled: busy - }, copied ? h7("span", { className: "dsl-copyState" }, "\u5DF2\u590D\u5236 Manifest") : "\u590D\u5236 Manifest"), - h7("a", { + }, copied ? h2("span", { className: "dsl-copyState" }, "\u5DF2\u590D\u5236 Manifest") : "\u590D\u5236 Manifest"), + h2("a", { className: "ddt-button", href: SLACK_CREATE_APP_URL, target: "_blank", @@ -4451,17 +4895,17 @@ function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) { }, "\u6253\u5F00 Slack \u521B\u5EFA\u9875") ) ), - h7( + h2( "form", { className: "dim-credentialForm dim-credentialFormSingle", onSubmit: submit }, - h7( + h2( "div", { className: "dsl-fields" }, - h7( + h2( "label", { className: "dim-credentialField" }, - h7("span", null, "Bot Token"), - h7("input", { + h2("span", null, "Bot Token"), + h2("input", { type: "password", value: botToken, onChange: (event) => setBotToken(event.target.value), @@ -4475,11 +4919,11 @@ function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) { required: true }) ), - h7( + h2( "label", { className: "dim-credentialField" }, - h7("span", null, "App Token"), - h7("input", { + h2("span", null, "App Token"), + h2("input", { type: "password", value: appToken, onChange: (event) => setAppToken(event.target.value), @@ -4493,19 +4937,19 @@ function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) { required: true }) ), - h7("p", { className: "dsl-tokenHint" }, "Bot Token \u6765\u81EA OAuth & Permissions\uFF1BApp Token \u6765\u81EA Basic Information\uFF0C\u5E76\u4E14\u5FC5\u987B\u5305\u542B connections:write\u3002") + h2("p", { className: "dsl-tokenHint" }, "Bot Token \u6765\u81EA OAuth & Permissions\uFF1BApp Token \u6765\u81EA Basic Information\uFF0C\u5E76\u4E14\u5FC5\u987B\u5305\u542B connections:write\u3002") ), - error ? h7("p", { className: "dim-credentialError", role: "alert" }, error.message ?? String(error)) : null, - h7( + error ? h2("p", { className: "dim-credentialError", role: "alert" }, error.message ?? String(error)) : null, + h2( "div", { className: "ddt-actions dim-viewActions dim-credentialActions" }, - h7("button", { + h2("button", { type: "submit", className: "ddt-button", "data-kind": "primary", disabled: busy || !botToken.trim() || !appToken.trim() }, busy ? "\u6B63\u5728\u9A8C\u8BC1\u5E76\u8FDE\u63A5\u2026" : "\u9A8C\u8BC1\u5E76\u8FDE\u63A5"), - h7("button", { + h2("button", { type: "button", className: "ddt-button", onClick: onCancel, @@ -4693,7 +5137,7 @@ function formatRemaining4(milliseconds) { } // plugin-src/client/channels/wecom/index.js -var React9 = __toESM(require("react"), 1); +var React10 = __toESM(require("react"), 1); // plugin-src/client/channels/wecom/styles.js var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings"; @@ -4717,10 +5161,9 @@ function installWecomStyles() { } // plugin-src/client/channels/wecom/index.js -var h8 = React9.createElement; var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]); -var Button9 = React9.forwardRef(function Button10({ children, kind = "secondary", className = "", ...props }, ref) { - return h8("button", { +var Button9 = React10.forwardRef(function Button10({ children, kind = "secondary", className = "", ...props }, ref) { + return h2("button", { ...props, ref, type: "button", @@ -4741,80 +5184,80 @@ function checkedTime4(value) { } } function Heading4({ totals, adding, busy, onAdd, onCredential, credentialOpen, addButtonRef }) { - return h8( + return h2( "div", { className: "ddt-heading" }, - h8( + h2( "div", { className: "ddt-tools" }, - h8( + h2( "div", { className: "dim-bindActions" }, - h8(Button9, { + h2(Button9, { kind: "primary", className: "dim-scanButton", onClick: onAdd, disabled: adding || busy, ref: addButtonRef, "aria-label": "\u626B\u7801\u63A5\u5165\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA" - }, h8(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), - h8(Button9, { + }, h2(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), + h2(Button9, { kind: "credential", className: "dim-credentialButton", onClick: onCredential, disabled: adding || busy, "aria-pressed": credentialOpen, "aria-label": "\u4F7F\u7528 Bot ID \u548C Secret \u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA" - }, h8(CredentialActionIcon), credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165") + }, h2(CredentialActionIcon), credentialOpen ? "\u6536\u8D77\u51ED\u636E" : "\u624B\u52A8\u63A5\u5165") ), - totals.configured > 0 ? h8( + totals.configured > 0 ? h2( "div", { className: "ddt-badge dim-onlineBadge" }, - h8("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) + h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) ) : null ) ); } function LoadingView4() { - return h8( + return h2( "div", { className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" }, - h8("div", { className: "ddt-spinner dim-spinner" }), - h8("span", null, "\u6B63\u5728\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001\u2026") + h2("div", { className: "ddt-spinner dim-spinner" }), + h2("span", null, "\u6B63\u5728\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001\u2026") ); } function EmptyView4({ busy, onStart }) { - return h8( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h8( + h2( "div", { className: "ddt-cardBody ddt-empty dim-surfaceBody dim-emptyView" }, - h8( + h2( "div", { className: "dim-emptyCopy" }, - h8( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h8("span", { className: "ddt-dot dim-stateDot" }), - h8("span", null, "\u5C1A\u672A\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA") + h2("span", { className: "ddt-dot dim-stateDot" }), + h2("span", null, "\u5C1A\u672A\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA") ), - h8("h3", null, "\u4F7F\u7528\u4F01\u4E1A\u5FAE\u4FE1 App \u626B\u7801\u521B\u5EFA\u667A\u80FD\u673A\u5668\u4EBA"), - h8("p", null, "\u626B\u7801\u7531\u817E\u8BAF\u5B98\u65B9\u9875\u9762\u5B8C\u6210\uFF0C\u4E0D\u9700\u8981\u624B\u52A8\u586B\u5199 Bot ID \u6216 Secret\u3002\u521B\u5EFA\u6210\u529F\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u8FDE\u63A5 DeepSeek Harness\u3002"), - h8( + h2("h3", null, "\u4F7F\u7528\u4F01\u4E1A\u5FAE\u4FE1 App \u626B\u7801\u521B\u5EFA\u667A\u80FD\u673A\u5668\u4EBA"), + h2("p", null, "\u626B\u7801\u7531\u817E\u8BAF\u5B98\u65B9\u9875\u9762\u5B8C\u6210\uFF0C\u4E0D\u9700\u8981\u624B\u52A8\u586B\u5199 Bot ID \u6216 Secret\u3002\u521B\u5EFA\u6210\u529F\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u8FDE\u63A5 DeepSeek Harness\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h8( + h2( Button9, { kind: "primary", onClick: onStart, disabled: busy }, busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801" ) ) ), - h8( + h2( "div", { className: "ddt-brandMark dim-emptyBrand dwecom-brand", "aria-hidden": "true" }, - h8(WecomLogoGlyph, { size: 64 }) + h2(WecomLogoGlyph, { size: 64 }) ) ) ); @@ -4825,59 +5268,59 @@ function QrPanel3({ provision, now, busy, onRefresh, onCancel }) { const duration = Math.max(1, provision.durationMs ?? 5 * 6e4); const progress = Math.round(Math.min(1, remaining / duration) * 100); const refreshing = provision.status === "refreshing"; - return h8( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h8( + h2( "div", { className: "ddt-cardBody ddt-qrLayout dim-surfaceBody dim-qrLayout" }, - h8( + h2( "div", { className: "ddt-qrColumn dim-qrColumn" }, - h8( + h2( "div", { className: "ddt-qrFrame dim-qrFrame" }, - source ? h8("img", { src: source, alt: "\u7528\u4E8E\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801" }) : h8( + source ? h2("img", { src: source, alt: "\u7528\u4E8E\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801" }) : h2( "div", { className: "ddt-qrFallback dim-qrFallback" }, refreshing ? "\u4E8C\u7EF4\u7801\u6B63\u5728\u81EA\u52A8\u5237\u65B0\u2026" : "\u4E8C\u7EF4\u7801\u56FE\u7247\u6B63\u5728\u751F\u6210\u2026" ) ), - h8( + h2( "div", { className: "ddt-countdown dim-countdown" }, - h8( + h2( "div", { className: "ddt-countdownTop dim-countdownTop" }, - h8("span", null, "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), - h8("strong", null, refreshing ? "--:--" : formatRemaining4(remaining)) + h2("span", null, "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), + h2("strong", null, refreshing ? "--:--" : formatRemaining4(remaining)) ), - h8("div", { className: "ddt-progress dim-progress", style: { "--ddt-progress": `${progress}%` } }, h8("span")) + h2("div", { className: "ddt-progress dim-progress", style: { "--ddt-progress": `${progress}%` } }, h2("span")) ) ), - h8( + h2( "div", { className: "ddt-qrCopy dim-qrCopy" }, - h8( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h8("span", { className: "ddt-dot dim-stateDot", "data-tone": "warning" }), - h8("span", null, refreshing ? "\u6B63\u5728\u5237\u65B0\u4E8C\u7EF4\u7801" : "\u7B49\u5F85\u4F01\u4E1A\u5FAE\u4FE1 App \u626B\u7801") + h2("span", { className: "ddt-dot dim-stateDot", "data-tone": "warning" }), + h2("span", null, refreshing ? "\u6B63\u5728\u5237\u65B0\u4E8C\u7EF4\u7801" : "\u7B49\u5F85\u4F01\u4E1A\u5FAE\u4FE1 App \u626B\u7801") ), - h8("h3", null, "\u4F7F\u7528\u4F01\u4E1A\u5FAE\u4FE1 App \u5B8C\u6210\u667A\u80FD\u673A\u5668\u4EBA\u6388\u6743"), - h8("p", null, "\u4F01\u4E1A\u5FAE\u4FE1\u5B98\u65B9\u9875\u9762\u4F1A\u521B\u5EFA\u4E00\u4E2A\u667A\u80FD\u673A\u5668\u4EBA\uFF0C\u5E76\u628A\u8FDE\u63A5\u51ED\u636E\u5B89\u5168\u4EA4\u7ED9\u672C\u673A Harness Host\u3002"), - h8( + h2("h3", null, "\u4F7F\u7528\u4F01\u4E1A\u5FAE\u4FE1 App \u5B8C\u6210\u667A\u80FD\u673A\u5668\u4EBA\u6388\u6743"), + h2("p", null, "\u4F01\u4E1A\u5FAE\u4FE1\u5B98\u65B9\u9875\u9762\u4F1A\u521B\u5EFA\u4E00\u4E2A\u667A\u80FD\u673A\u5668\u4EBA\uFF0C\u5E76\u628A\u8FDE\u63A5\u51ED\u636E\u5B89\u5168\u4EA4\u7ED9\u672C\u673A Harness Host\u3002"), + h2( "ol", { className: "ddt-steps dim-steps" }, - h8("li", null, "\u6253\u5F00\u4F01\u4E1A\u5FAE\u4FE1 App\uFF0C\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), - h8("li", null, "\u5728\u817E\u8BAF\u6388\u6743\u9875\u9762\u786E\u8BA4\u521B\u5EFA\u667A\u80FD\u673A\u5668\u4EBA"), - h8("li", null, "\u8FD4\u56DE\u8FD9\u91CC\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210") + h2("li", null, "\u6253\u5F00\u4F01\u4E1A\u5FAE\u4FE1 App\uFF0C\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), + h2("li", null, "\u5728\u817E\u8BAF\u6388\u6743\u9875\u9762\u786E\u8BA4\u521B\u5EFA\u667A\u80FD\u673A\u5668\u4EBA"), + h2("li", null, "\u8FD4\u56DE\u8FD9\u91CC\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210") ), - h8( + h2( "div", { className: "ddt-actions dim-viewActions" }, - h8(Button9, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h8(Button9, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88") + h2(Button9, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button9, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88") ) ) ) @@ -4885,44 +5328,44 @@ function QrPanel3({ provision, now, busy, onRefresh, onCancel }) { } function ProvisionView2({ provision, busy, onRetry, onClose }) { if (provision.status === "connecting") { - return h8( + return h2( "div", { className: "ddt-card ddt-loading dim-surfaceCard dim-specialView", "aria-busy": "true" }, - h8("div", { className: "ddt-spinner dim-spinner" }), - h8("h3", null, "\u4F01\u4E1A\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u6B63\u5728\u8FDE\u63A5\u673A\u5668\u4EBA"), - h8("p", null, "\u51ED\u636E\u6B63\u5728\u5199\u5165\u672C\u673A\uFF0C\u5E76\u542F\u52A8\u4F01\u4E1A\u5FAE\u4FE1 WebSocket \u6D88\u606F\u8FDE\u63A5\u3002") + h2("div", { className: "ddt-spinner dim-spinner" }), + h2("h3", null, "\u4F01\u4E1A\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u6B63\u5728\u8FDE\u63A5\u673A\u5668\u4EBA"), + h2("p", null, "\u51ED\u636E\u6B63\u5728\u5199\u5165\u672C\u673A\uFF0C\u5E76\u542F\u52A8\u4F01\u4E1A\u5FAE\u4FE1 WebSocket \u6D88\u606F\u8FDE\u63A5\u3002") ); } const error = provision.error ?? { code: "WECOM_PROVISION_FAILED", message: "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210" }; - return h8( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h8( + h2( "div", { className: "ddt-inlineError dim-inlineError", role: "alert" }, - h8("h3", null, "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210"), - h8("p", null, error.message), - h8("span", { className: "ddt-errorCode" }, error.code), - h8( + h2("h3", null, "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210"), + h2("p", null, error.message), + h2("span", { className: "ddt-errorCode" }, error.code), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h8(Button9, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h8(Button9, { onClick: onClose, disabled: busy }, "\u5173\u95ED") + h2(Button9, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button9, { onClick: onClose, disabled: busy }, "\u5173\u95ED") ) ) ); } function RemoveConfirmation4({ account, busy, onConfirm, onCancel }) { - return h8( + return h2( "div", { className: "ddt-confirm dim-confirm", role: "alertdialog" }, - h8("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), - h8("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u4F01\u4E1A\u5FAE\u4FE1\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), - h8( + h2("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), + h2("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u4F01\u4E1A\u5FAE\u4FE1\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h8(Button9, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), - h8(Button9, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165") + h2(Button9, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), + h2(Button9, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165") ) ); } @@ -4930,52 +5373,52 @@ function AccountCard3({ account, busy, removing, onReconnect, onRequestRemove, o const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning"; const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA"; const summary = account.error?.message ?? (account.connected ? null : account.health.summary); - return h8( + return h2( "article", { className: "ddt-card dim-botCard", "data-bot-id": account.botId }, - h8( + h2( "div", { className: "ddt-cardBody dim-botCardBody" }, - h8( + h2( "div", { className: "ddt-accountTop dim-botCardTop" }, - h8( + h2( "div", { className: "ddt-accountIdentity dim-botIdentity" }, - h8("div", { className: "ddt-avatar dim-botAvatar dwecom-avatar", "aria-hidden": "true" }, h8(WecomLogoGlyph, { size: 29 })), - h8( + h2("div", { className: "ddt-avatar dim-botAvatar dwecom-avatar", "aria-hidden": "true" }, h2(WecomLogoGlyph, { size: 29 })), + h2( "div", { className: "dim-botName" }, - h8("h3", null, account.bot.name), - h8("p", null, account.bot.appIdMasked) + h2("h3", null, account.bot.name), + h2("p", null, account.bot.appIdMasked) ) ), - h8( + h2( "div", { className: "ddt-health dim-botHealth" }, - h8("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), - h8("span", null, stateLabel) + h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), + h2("span", null, stateLabel) ) ), - h8( + h2( "dl", { className: "ddt-metrics dim-botMetrics" }, - h8("div", { className: "ddt-metric dim-botMetric" }, h8("dt", null, "\u6D88\u606F\u901A\u9053"), h8("dd", null, account.connected ? "WebSocket \u957F\u8FDE\u63A5" : "\u79BB\u7EBF")), - h8("div", { className: "ddt-metric dim-botMetric" }, h8("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h8("dd", null, checkedTime4(account.health.lastCheckedAt))) + 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")), + h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h2("dd", null, checkedTime4(account.health.lastCheckedAt))) ), - h8( + h2( "div", { className: "ddt-accountFooter dim-cardFooter" }, - summary ? h8("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, - h8( + summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, + h2( "div", { className: "ddt-actions dim-cardActions" }, - h8(Button9, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"), - h8(Button9, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165") + h2(Button9, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"), + h2(Button9, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165") ) ) ), - removing ? h8(RemoveConfirmation4, { + removing ? h2(RemoveConfirmation4, { account, busy: busy === "delete", onConfirm: onConfirmRemove, @@ -4984,17 +5427,17 @@ function AccountCard3({ account, busy, removing, onReconnect, onRequestRemove, o ); } function WecomSettingsTab({ rpcCall }) { - const [model, setModel] = React9.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null }); - const [provision, setProvision] = React9.useState(null); - const [busy, setBusy] = React9.useState(false); - const [busyByBot, setBusyByBot] = React9.useState({}); - const [removeTarget, setRemoveTarget] = React9.useState(null); - const [credentialOpen, setCredentialOpen] = React9.useState(false); - const [credentialError, setCredentialError] = React9.useState(null); - const [now, setNow] = React9.useState(Date.now()); - const mounted = React9.useRef(true); - const addButtonRef = React9.useRef(null); - React9.useEffect(() => { + const [model, setModel] = React10.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null }); + const [provision, setProvision] = React10.useState(null); + const [busy, setBusy] = React10.useState(false); + const [busyByBot, setBusyByBot] = React10.useState({}); + const [removeTarget, setRemoveTarget] = React10.useState(null); + const [credentialOpen, setCredentialOpen] = React10.useState(false); + const [credentialError, setCredentialError] = React10.useState(null); + const [now, setNow] = React10.useState(Date.now()); + const mounted = React10.useRef(true); + const addButtonRef = React10.useRef(null); + React10.useEffect(() => { const disposeDingtalk = installDingtalkStyles(); const disposeWecom = installWecomStyles(); mounted.current = true; @@ -5004,11 +5447,11 @@ function WecomSettingsTab({ rpcCall }) { disposeDingtalk(); }; }, []); - const invoke = React9.useCallback(async (endpoint, payload = {}, signal) => { + const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => { if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5"); return unwrapRpcResult7(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React9.useCallback(async ({ signal, silent = false, restore = false } = {}) => { + const loadStatus = React10.useCallback(async ({ signal, silent = false, restore = false } = {}) => { if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null })); try { const snapshot = normalizeSnapshot6(await invoke(WECOM_ENDPOINTS.status, {}, signal)); @@ -5026,12 +5469,12 @@ function WecomSettingsTab({ rpcCall }) { return void 0; } }, [invoke]); - React9.useEffect(() => { + React10.useEffect(() => { const controller = new AbortController(); void loadStatus({ signal: controller.signal, restore: true }); return () => controller.abort(); }, [loadStatus]); - React9.useEffect(() => { + React10.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3); @@ -5040,12 +5483,12 @@ function WecomSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React9.useEffect(() => { + React10.useEffect(() => { if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0; const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3); return () => window.clearInterval(timer); }, [provision?.attemptId, provision?.status]); - const startProvisioning = React9.useCallback(async (replace = false) => { + const startProvisioning = React10.useCallback(async (replace = false) => { setCredentialOpen(false); setCredentialError(null); setBusy(true); @@ -5063,7 +5506,7 @@ function WecomSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke, provision?.attemptId]); - const bindCredentials = React9.useCallback(async ({ identity, secret }) => { + const bindCredentials = React10.useCallback(async ({ identity, secret }) => { setBusy(true); setCredentialError(null); try { @@ -5080,7 +5523,7 @@ function WecomSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke]); - const closeProvision = React9.useCallback(async () => { + const closeProvision = React10.useCallback(async () => { setBusy(true); try { if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) { @@ -5091,7 +5534,7 @@ function WecomSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke, provision?.attemptId, provision?.status]); - React9.useEffect(() => { + React10.useEffect(() => { const attemptId = provision?.attemptId; if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0; const controller = new AbortController(); @@ -5121,7 +5564,7 @@ function WecomSettingsTab({ rpcCall }) { window.clearTimeout(timer); }; }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]); - const botAction = React9.useCallback(async (account, operation, endpoint, payload) => { + const botAction = React10.useCallback(async (account, operation, endpoint, payload) => { setBusyByBot((current) => ({ ...current, [account.botId]: operation })); try { const snapshot = normalizeSnapshot6(await invoke(endpoint, payload)); @@ -5136,29 +5579,29 @@ function WecomSettingsTab({ rpcCall }) { } }, [invoke]); let provisionView = null; - if (provision?.status === "starting") provisionView = h8("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h8("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u2026"); - else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h8(QrPanel3, { + if (provision?.status === "starting") provisionView = h2("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h2("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u2026"); + else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h2(QrPanel3, { provision, now, busy, onRefresh: () => void startProvisioning(true), onCancel: () => void closeProvision() }); - else if (provision) provisionView = h8(ProvisionView2, { + else if (provision) provisionView = h2(ProvisionView2, { provision, busy, onRetry: () => void startProvisioning(true), onClose: () => void closeProvision() }); - const botList = model.bots.length > 0 ? h8( + const botList = model.bots.length > 0 ? h2( "section", { className: "dim-listSection" }, - h8( + h2( "div", { className: "ddt-listHeading dim-listHeading" }, - h8("h3", null, "\u5DF2\u7ED1\u5B9A\u7684\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA") + h2("h3", null, "\u5DF2\u7ED1\u5B9A\u7684\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA") ), - h8("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h8("li", { key: account.botId }, h8(AccountCard3, { + h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard3, { account, busy: busyByBot[account.botId], removing: removeTarget === account.botId, @@ -5171,7 +5614,7 @@ function WecomSettingsTab({ rpcCall }) { } })))) ) : null; - const credentialView = credentialOpen ? h8(CredentialBindingPanel, { + const credentialView = credentialOpen ? h2(CredentialBindingPanel, { channel: "\u4F01\u4E1A\u5FAE\u4FE1", identityLabel: "Bot ID", identityPlaceholder: "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u667A\u80FD\u673A\u5668\u4EBA Bot ID", @@ -5185,10 +5628,10 @@ function WecomSettingsTab({ rpcCall }) { setCredentialError(null); } }) : null; - return h8( + return h2( "section", { className: "ddt-page dwecom-page dim-channelPage", "aria-label": "\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E" }, - h8(Heading4, { + h2(Heading4, { totals: model.totals, adding: Boolean(provision), busy, @@ -5200,19 +5643,19 @@ function WecomSettingsTab({ rpcCall }) { credentialOpen, addButtonRef }), - model.phase === "loading" ? h8(LoadingView4) : model.phase === "error" ? h8("div", { className: "ddt-card dim-surfaceCard" }, h8("div", { className: "ddt-inlineError dim-inlineError" }, h8("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h8("p", null, model.error?.message), h8(Button9, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h8( - React9.Fragment, + 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(Button9, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2( + React10.Fragment, null, credentialView, provisionView, - model.bots.length === 0 && !provision && !credentialOpen ? h8(EmptyView4, { busy, onStart: () => void startProvisioning() }) : null, + model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView4, { busy, onStart: () => void startProvisioning() }) : null, botList ) ); } // plugin-src/client/channels/weixin/index.js -var React10 = __toESM(require("react"), 1); +var React11 = __toESM(require("react"), 1); // plugin-src/client/channels/weixin/api.js var WEIXIN_RPC_CHANNEL = "/weixin"; @@ -5475,9 +5918,8 @@ function installWeixinStyles() { } // plugin-src/client/channels/weixin/index.js -var h9 = React10.createElement; -var Button11 = React10.forwardRef(function Button12({ children, kind = "secondary", className = "", ...props }, ref) { - return h9("button", { +var Button11 = React11.forwardRef(function Button12({ children, kind = "secondary", className = "", ...props }, ref) { + return h2("button", { ...props, ref, type: "button", @@ -5486,150 +5928,150 @@ var Button11 = React10.forwardRef(function Button12({ children, kind = "secondar }, children); }); function Heading5({ totals, adding, busy, onAdd, addButtonRef }) { - return h9( + return h2( "div", { className: "dxw-heading" }, - h9( + h2( "div", { className: "dxw-tools" }, - h9(Button11, { + h2(Button11, { kind: "primary", className: "dim-scanButton", onClick: onAdd, disabled: adding || busy, ref: addButtonRef, "aria-label": "\u626B\u7801\u63A5\u5165\u5FAE\u4FE1\u673A\u5668\u4EBA" - }, h9(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), - totals.configured > 0 ? h9( + }, h2(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"), + totals.configured > 0 ? h2( "div", { className: "dxw-badge dim-onlineBadge" }, - h9("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) + h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) ) : null ) ); } function LoadingView5() { - return h9( + return h2( "div", { className: "dxw-card dxw-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" }, - h9("div", { className: "dxw-spinner dim-spinner" }), - h9("span", null, "\u6B63\u5728\u8BFB\u53D6\u5FAE\u4FE1\u8FDE\u63A5\u72B6\u6001\u2026") + h2("div", { className: "dxw-spinner dim-spinner" }), + h2("span", null, "\u6B63\u5728\u8BFB\u53D6\u5FAE\u4FE1\u8FDE\u63A5\u72B6\u6001\u2026") ); } function EmptyView5({ onStart, busy }) { - return h9( + return h2( "div", { className: "dxw-card dim-surfaceCard" }, - h9( + h2( "div", { className: "dxw-cardBody dxw-empty dim-surfaceBody dim-emptyView" }, - h9( + h2( "div", { className: "dim-emptyCopy" }, - h9( + h2( "div", { className: "dxw-stateLabel dim-stateLabel" }, - h9("span", { className: "dxw-dot dim-stateDot" }), - h9("span", null, "\u5C1A\u672A\u7ED1\u5B9A\u5FAE\u4FE1") + h2("span", { className: "dxw-dot dim-stateDot" }), + h2("span", null, "\u5C1A\u672A\u7ED1\u5B9A\u5FAE\u4FE1") ), - h9("h3", null, "\u626B\u4E00\u6B21\u7801\uFF0C\u5C31\u80FD\u5728\u5FAE\u4FE1\u91CC\u4F7F\u7528 Harness"), - h9("p", null, "\u4E8C\u7EF4\u7801\u7531\u817E\u8BAF\u5FAE\u4FE1 iLink \u670D\u52A1\u7B7E\u53D1\u3002\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u63CF\u5E76\u786E\u8BA4\u540E\uFF0C\u8D26\u53F7\u51ED\u636E\u4F1A\u76F4\u63A5\u5199\u5165 Harness Host\uFF0C\u6D4F\u89C8\u5668\u4E0D\u4F1A\u6536\u5230 bot_token\u3002"), - h9( + h2("h3", null, "\u626B\u4E00\u6B21\u7801\uFF0C\u5C31\u80FD\u5728\u5FAE\u4FE1\u91CC\u4F7F\u7528 Harness"), + h2("p", null, "\u4E8C\u7EF4\u7801\u7531\u817E\u8BAF\u5FAE\u4FE1 iLink \u670D\u52A1\u7B7E\u53D1\u3002\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u63CF\u5E76\u786E\u8BA4\u540E\uFF0C\u8D26\u53F7\u51ED\u636E\u4F1A\u76F4\u63A5\u5199\u5165 Harness Host\uFF0C\u6D4F\u89C8\u5668\u4E0D\u4F1A\u6536\u5230 bot_token\u3002"), + h2( "div", { className: "dxw-actions dim-viewActions" }, - h9( + h2( Button11, { kind: "primary", onClick: onStart, disabled: busy }, busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801" ) ) ), - h9("div", { className: "dxw-logo dim-emptyBrand", "aria-hidden": "true" }, h9(WeixinLogoGlyph, { size: 64 })) + h2("div", { className: "dxw-logo dim-emptyBrand", "aria-hidden": "true" }, h2(WeixinLogoGlyph, { size: 64 })) ) ); } function QrPanel4({ provision, now, busy, onRefresh, onCancel }) { - const [imageFailed, setImageFailed] = React10.useState(false); + const [imageFailed, setImageFailed] = React11.useState(false); const source = safeQrSource5(provision.qrCodeDataUrl); const href = safeVerificationUrl(provision.verificationUrl); const remaining = Math.max(0, provision.expiresAt - now); const expired = remaining === 0 || provision.status === "expired"; const duration = Math.max(1, provision.durationMs ?? 5 * 6e4); const progress = Math.round(Math.min(1, remaining / duration) * 100); - React10.useEffect(() => setImageFailed(false), [source]); - return h9( + React11.useEffect(() => setImageFailed(false), [source]); + return h2( "div", { className: "dxw-card dim-surfaceCard" }, - h9( + h2( "div", { className: "dxw-cardBody dxw-qrLayout dim-surfaceBody dim-qrLayout" }, - h9( + h2( "div", { className: "dxw-qrColumn dim-qrColumn" }, - h9( + h2( "div", { className: "dxw-qrFrame dim-qrFrame" }, - source && !imageFailed ? h9("img", { + source && !imageFailed ? h2("img", { src: source, alt: "\u7528\u4E8E\u628A\u5FAE\u4FE1\u673A\u5668\u4EBA\u7ED1\u5B9A\u5230 DeepSeek Harness \u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801", onError: () => setImageFailed(true) - }) : h9("div", { className: "dxw-qrFallback dim-qrFallback" }, "\u4E8C\u7EF4\u7801\u56FE\u7247\u672A\u5C31\u7EEA\uFF0C\u8BF7\u4F7F\u7528\u5907\u7528\u94FE\u63A5\u3002"), - expired ? h9("div", { className: "dxw-expired dim-qrExpired" }, "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\n\u8BF7\u91CD\u65B0\u751F\u6210") : null + }) : h2("div", { className: "dxw-qrFallback dim-qrFallback" }, "\u4E8C\u7EF4\u7801\u56FE\u7247\u672A\u5C31\u7EEA\uFF0C\u8BF7\u4F7F\u7528\u5907\u7528\u94FE\u63A5\u3002"), + expired ? h2("div", { className: "dxw-expired dim-qrExpired" }, "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\n\u8BF7\u91CD\u65B0\u751F\u6210") : null ), - h9( + h2( "div", { className: "dxw-countdown dim-countdown" }, - h9("div", { className: "dim-countdownTop" }, h9("span", null, "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), h9("strong", null, formatRemaining5(remaining))), - h9( + h2("div", { className: "dim-countdownTop" }, h2("span", null, "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), h2("strong", null, formatRemaining5(remaining))), + h2( "div", { className: "dxw-progress dim-progress", "aria-hidden": "true" }, - h9("span", { style: { "--dxw-progress": `${progress}%` } }) + h2("span", { style: { "--dxw-progress": `${progress}%` } }) ) ) ), - h9( + h2( "div", { className: "dxw-qrCopy dim-qrCopy" }, - h9( + h2( "div", { className: "dxw-stateLabel dim-stateLabel" }, - h9("span", { className: "dxw-dot dim-stateDot", "data-tone": provision.status === "scanned" ? "success" : "warning" }), - h9("span", null, provision.status === "scanned" ? "\u5DF2\u626B\u7801\uFF0C\u8BF7\u5728\u624B\u673A\u4E0A\u786E\u8BA4" : "\u7B49\u5F85\u5FAE\u4FE1\u626B\u7801") + h2("span", { className: "dxw-dot dim-stateDot", "data-tone": provision.status === "scanned" ? "success" : "warning" }), + h2("span", null, provision.status === "scanned" ? "\u5DF2\u626B\u7801\uFF0C\u8BF7\u5728\u624B\u673A\u4E0A\u786E\u8BA4" : "\u7B49\u5F85\u5FAE\u4FE1\u626B\u7801") ), - h9("h3", null, expired ? "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548" : "\u4F7F\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u63CF\u4E8C\u7EF4\u7801"), - h9("p", null, "\u8BF7\u5728\u624B\u673A\u4E0A\u6838\u5BF9\u5E76\u786E\u8BA4\u6388\u6743\u3002\u90E8\u5206\u8D26\u53F7\u4F1A\u989D\u5916\u663E\u793A\u4E00\u4E2A\u914D\u5BF9\u6570\u5B57\uFF0C\u9875\u9762\u4F1A\u5728\u9700\u8981\u65F6\u63D0\u793A\u8F93\u5165\u3002"), - h9( + h2("h3", null, expired ? "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548" : "\u4F7F\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u63CF\u4E8C\u7EF4\u7801"), + h2("p", null, "\u8BF7\u5728\u624B\u673A\u4E0A\u6838\u5BF9\u5E76\u786E\u8BA4\u6388\u6743\u3002\u90E8\u5206\u8D26\u53F7\u4F1A\u989D\u5916\u663E\u793A\u4E00\u4E2A\u914D\u5BF9\u6570\u5B57\uFF0C\u9875\u9762\u4F1A\u5728\u9700\u8981\u65F6\u63D0\u793A\u8F93\u5165\u3002"), + h2( "ol", { className: "dxw-steps dim-steps" }, - h9("li", null, "\u6253\u5F00\u624B\u673A\u5FAE\u4FE1\u5E76\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), - h9("li", null, "\u5728\u5FAE\u4FE1\u4E2D\u786E\u8BA4\u8FDE\u63A5\u8BE5\u673A\u5668\u4EBA"), - h9("li", null, "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6D88\u606F\u957F\u8F6E\u8BE2\u53D8\u4E3A\u5728\u7EBF") + h2("li", null, "\u6253\u5F00\u624B\u673A\u5FAE\u4FE1\u5E76\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801"), + h2("li", null, "\u5728\u5FAE\u4FE1\u4E2D\u786E\u8BA4\u8FDE\u63A5\u8BE5\u673A\u5668\u4EBA"), + h2("li", null, "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6D88\u606F\u957F\u8F6E\u8BE2\u53D8\u4E3A\u5728\u7EBF") ), - h9( + h2( "div", { className: "dxw-actions dim-viewActions" }, - expired ? h9(Button11, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null, - href ? h9("a", { + expired ? h2(Button11, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null, + href ? h2("a", { className: "dxw-button", href, target: "_blank", rel: "noopener noreferrer" }, "\u6253\u5F00\u5907\u7528\u94FE\u63A5") : null, - !expired ? h9(Button11, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null, - h9(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88") + !expired ? h2(Button11, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null, + h2(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88") ) ) ) ); } function VerificationPanel({ provision, busy, onSubmit, onCancel }) { - const [code, setCode] = React10.useState(""); + const [code, setCode] = React11.useState(""); const valid = /^\d{4,8}$/.test(code); - React10.useEffect(() => setCode(""), [provision.attemptId]); - return h9( + React11.useEffect(() => setCode(""), [provision.attemptId]); + return h2( "div", { className: "dxw-card dim-surfaceCard" }, - h9( + h2( "form", { className: "dxw-verify dim-specialView", @@ -5638,18 +6080,18 @@ function VerificationPanel({ provision, busy, onSubmit, onCancel }) { if (valid && !busy) onSubmit(code); } }, - h9( + h2( "div", { className: "dxw-stateLabel" }, - h9("span", { className: "dxw-dot", "data-tone": "warning" }), - h9("span", null, "\u9700\u8981\u914D\u5BF9\u7801") + h2("span", { className: "dxw-dot", "data-tone": "warning" }), + h2("span", null, "\u9700\u8981\u914D\u5BF9\u7801") ), - h9("h3", null, "\u8F93\u5165\u624B\u673A\u5FAE\u4FE1\u663E\u793A\u7684\u6570\u5B57"), - h9("p", null, "\u8FD9\u662F\u5FAE\u4FE1\u9644\u52A0\u7684\u5B89\u5168\u786E\u8BA4\u6B65\u9AA4\u3002\u914D\u5BF9\u7801\u53EA\u7528\u4E8E\u672C\u6B21\u626B\u7801\u8F6E\u8BE2\uFF0C\u4E0D\u4F1A\u5199\u5165\u914D\u7F6E\u6216\u65E5\u5FD7\u3002"), - h9( + h2("h3", null, "\u8F93\u5165\u624B\u673A\u5FAE\u4FE1\u663E\u793A\u7684\u6570\u5B57"), + h2("p", null, "\u8FD9\u662F\u5FAE\u4FE1\u9644\u52A0\u7684\u5B89\u5168\u786E\u8BA4\u6B65\u9AA4\u3002\u914D\u5BF9\u7801\u53EA\u7528\u4E8E\u672C\u6B21\u626B\u7801\u8F6E\u8BE2\uFF0C\u4E0D\u4F1A\u5199\u5165\u914D\u7F6E\u6216\u65E5\u5FD7\u3002"), + h2( "div", { className: "dxw-codeRow" }, - h9("input", { + h2("input", { className: "dxw-input", value: code, inputMode: "numeric", @@ -5659,47 +6101,47 @@ function VerificationPanel({ provision, busy, onSubmit, onCancel }) { onChange: (event) => setCode(event.target.value.replace(/\D/g, "").slice(0, 8)), autoFocus: true }), - h9("button", { + h2("button", { type: "submit", className: "dxw-button", "data-kind": "primary", disabled: !valid || busy }, busy ? "\u6B63\u5728\u9A8C\u8BC1\u2026" : "\u7EE7\u7EED\u8FDE\u63A5") ), - h9(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u7ED1\u5B9A") + h2(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u7ED1\u5B9A") ) ); } function ProgressPanel2({ scanned, onCancel, busy }) { - return h9( + return h2( "div", { className: "dxw-card dxw-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" }, - h9("div", { className: "dxw-spinner dim-spinner" }), - h9("h3", null, scanned ? "\u5FAE\u4FE1\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u542F\u52A8\u6D88\u606F\u8FDE\u63A5" : "\u6B63\u5728\u51C6\u5907\u5FAE\u4FE1\u4E8C\u7EF4\u7801"), - h9("p", null, scanned ? "\u6B63\u5728\u4FDD\u5B58\u51ED\u636E\u5E76\u9A8C\u8BC1 Harness \u4E0E\u5FAE\u4FE1\u957F\u8F6E\u8BE2\u3002" : "\u6B63\u5728\u8054\u7CFB\u817E\u8BAF\u5FAE\u4FE1 iLink \u670D\u52A1\u3002"), - onCancel ? h9( + h2("div", { className: "dxw-spinner dim-spinner" }), + h2("h3", null, scanned ? "\u5FAE\u4FE1\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u542F\u52A8\u6D88\u606F\u8FDE\u63A5" : "\u6B63\u5728\u51C6\u5907\u5FAE\u4FE1\u4E8C\u7EF4\u7801"), + h2("p", null, scanned ? "\u6B63\u5728\u4FDD\u5B58\u51ED\u636E\u5E76\u9A8C\u8BC1 Harness \u4E0E\u5FAE\u4FE1\u957F\u8F6E\u8BE2\u3002" : "\u6B63\u5728\u8054\u7CFB\u817E\u8BAF\u5FAE\u4FE1 iLink \u670D\u52A1\u3002"), + onCancel ? h2( "div", { className: "dxw-actions dim-viewActions", style: { justifyContent: "center", marginTop: 14 } }, - h9(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88") + h2(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88") ) : null ); } function ProvisionError3({ provision, busy, onRetry, onClose }) { const error = provision.error ?? { code: "WEIXIN_PROVISION_FAILED", message: "\u5FAE\u4FE1\u7ED1\u5B9A\u6CA1\u6709\u5B8C\u6210" }; - return h9( + return h2( "div", { className: "dxw-card dim-surfaceCard" }, - h9( + h2( "div", { className: "dxw-error dim-inlineError", role: "alert" }, - h9("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : "\u5FAE\u4FE1\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210"), - h9("p", null, error.message), - h9("span", { className: "dxw-errorCode" }, error.code), - h9( + h2("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : "\u5FAE\u4FE1\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210"), + h2("p", null, error.message), + h2("span", { className: "dxw-errorCode" }, error.code), + h2( "div", { className: "dxw-actions dim-viewActions" }, - h9(Button11, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h9(Button11, { onClick: onClose, disabled: busy }, "\u5173\u95ED") + h2(Button11, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button11, { onClick: onClose, disabled: busy }, "\u5173\u95ED") ) ) ); @@ -5720,70 +6162,70 @@ function AccountCard4({ account, busy, removing, onReconnect, onRequestRemove, o const state = busy === "reconnect" ? "connecting" : account.state; const tone = account.connected ? "success" : state === "error" ? "error" : "warning"; const summary = account.error?.message ?? (account.connected ? null : account.health.summary); - return h9( + return h2( "article", { className: "dxw-card dim-botCard", tabIndex: -1, "data-bot-id": account.botId }, - h9( + h2( "div", { className: "dxw-cardBody dim-botCardBody" }, - h9( + h2( "div", { className: "dxw-accountTop dim-botCardTop" }, - h9( + h2( "div", { className: "dxw-accountIdentity dim-botIdentity" }, - h9("div", { className: "dxw-avatar dim-botAvatar", "aria-hidden": "true" }, h9(WeixinLogoGlyph, { size: 27 })), - h9("div", { className: "dim-botName" }, h9("h3", null, account.bot.name), h9("p", null, account.bot.accountIdMasked)) + h2("div", { className: "dxw-avatar dim-botAvatar", "aria-hidden": "true" }, h2(WeixinLogoGlyph, { size: 27 })), + h2("div", { className: "dim-botName" }, h2("h3", null, account.bot.name), h2("p", null, account.bot.accountIdMasked)) ), - h9( + h2( "div", { className: "dxw-health dim-botHealth" }, - h9("span", { className: "dxw-dot dim-healthDot", "data-tone": tone }), - h9("span", null, account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA") + h2("span", { className: "dxw-dot dim-healthDot", "data-tone": tone }), + h2("span", null, account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA") ) ), - h9( + h2( "dl", { className: "dxw-metrics dim-botMetrics" }, - h9( + h2( "div", { className: "dxw-metric dim-botMetric" }, - h9("dt", null, "\u6D88\u606F\u901A\u9053"), - h9("dd", null, account.connected ? "iLink \u957F\u8F6E\u8BE2" : "\u79BB\u7EBF") + h2("dt", null, "\u6D88\u606F\u901A\u9053"), + h2("dd", null, account.connected ? "iLink \u957F\u8F6E\u8BE2" : "\u79BB\u7EBF") ), - h9( + h2( "div", { className: "dxw-metric dim-botMetric" }, - h9("dt", null, "\u6700\u8FD1\u68C0\u67E5"), - h9("dd", null, checkedTime5(account.health.lastCheckedAt)) + h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), + h2("dd", null, checkedTime5(account.health.lastCheckedAt)) ) ), - h9( + h2( "div", { className: "dxw-accountFooter dim-cardFooter" }, - summary ? h9("div", { className: "dxw-summary dim-cardSummary" }, summary) : null, - h9( + summary ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary) : null, + h2( "div", { className: "dxw-actions dim-cardActions" }, - h9( + h2( Button11, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5" ), - h9(Button11, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165") + h2(Button11, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165") ) ) ), - removing ? h9( + removing ? h2( "div", { className: "dxw-confirm dim-confirm", role: "alertdialog" }, - h9("strong", null, "\u4ECE\u6B64 Harness \u79FB\u9664\u8FD9\u4E2A\u5FAE\u4FE1\u8D26\u53F7\uFF1F"), - h9("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 bot_token\u3001\u8D26\u53F7\u914D\u7F6E\u548C\u4F1A\u8BDD\u6620\u5C04\u3002\u5176\u4ED6\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u53D7\u5F71\u54CD\u3002"), - h9( + h2("strong", null, "\u4ECE\u6B64 Harness \u79FB\u9664\u8FD9\u4E2A\u5FAE\u4FE1\u8D26\u53F7\uFF1F"), + h2("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 bot_token\u3001\u8D26\u53F7\u914D\u7F6E\u548C\u4F1A\u8BDD\u6620\u5C04\u3002\u5176\u4ED6\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u53D7\u5F71\u54CD\u3002"), + h2( "div", { className: "dxw-actions dim-viewActions" }, - h9(Button11, { onClick: onCancelRemove, disabled: busy === "delete" }, "\u4FDD\u7559\u8D26\u53F7"), - h9( + h2(Button11, { onClick: onCancelRemove, disabled: busy === "delete" }, "\u4FDD\u7559\u8D26\u53F7"), + h2( Button11, { kind: "danger", onClick: onConfirmRemove, disabled: busy === "delete" }, busy === "delete" ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664" @@ -5793,14 +6235,14 @@ function AccountCard4({ account, busy, removing, onReconnect, onRequestRemove, o ); } function AccountList2(props) { - return h9( + return h2( "section", { className: "dim-listSection" }, - h9("div", { className: "dxw-listHeading dim-listHeading" }, h9("h3", null, "\u5DF2\u63A5\u5165\u7684\u5FAE\u4FE1\u8D26\u53F7")), - h9("ul", { className: "dxw-list dim-botList" }, props.bots.map((account) => h9( + h2("div", { className: "dxw-listHeading dim-listHeading" }, h2("h3", null, "\u5DF2\u63A5\u5165\u7684\u5FAE\u4FE1\u8D26\u53F7")), + h2("ul", { className: "dxw-list dim-botList" }, props.bots.map((account) => h2( "li", { key: account.botId }, - h9(AccountCard4, { + h2(AccountCard4, { account, busy: props.busyByBot[account.botId], removing: props.removeTarget === account.botId, @@ -5823,31 +6265,31 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin }; } function WeixinSettingsTab({ rpcCall }) { - const [model, setModel] = React10.useState({ + const [model, setModel] = React11.useState({ phase: "loading", bots: [], totals: EMPTY_TOTALS3, revision: 0, error: null }); - const [provision, setProvision] = React10.useState(null); - const [busy, setBusy] = React10.useState(false); - const [busyByBot, setBusyByBot] = React10.useState({}); - const [removeTarget, setRemoveTarget] = React10.useState(null); - const [notice, setNotice] = React10.useState(""); - const [now, setNow] = React10.useState(() => Date.now()); - const addButtonRef = React10.useRef(null); + const [provision, setProvision] = React11.useState(null); + const [busy, setBusy] = React11.useState(false); + const [busyByBot, setBusyByBot] = React11.useState({}); + const [removeTarget, setRemoveTarget] = React11.useState(null); + const [notice, setNotice] = React11.useState(""); + const [now, setNow] = React11.useState(() => Date.now()); + const addButtonRef = React11.useRef(null); const scheduleAnimationFrame = useAnimationFrameScheduler(); - const announce = React10.useCallback((value) => { + const announce = React11.useCallback((value) => { setNotice(""); scheduleAnimationFrame(() => { if (value) setNotice(value); }, "announcement"); }, [scheduleAnimationFrame]); - const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => { + const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => { return unwrapRpcResult8(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React10.useCallback(async ({ + const loadStatus = React11.useCallback(async ({ signal, silent = false, restoreProvisioning = false @@ -5881,12 +6323,12 @@ function WeixinSettingsTab({ rpcCall }) { return void 0; } }, [invoke]); - React10.useEffect(() => { + React11.useEffect(() => { const controller = new AbortController(); void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); - React10.useEffect(() => { + React11.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); let running = false; @@ -5905,12 +6347,12 @@ function WeixinSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React10.useEffect(() => { + React11.useEffect(() => { if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0; const timer = window.setInterval(() => setNow(Date.now()), 1e3); return () => window.clearInterval(timer); }, [provision?.attemptId, provision?.status]); - const startProvisioning = React10.useCallback(async ({ replace = false } = {}) => { + const startProvisioning = React11.useCallback(async ({ replace = false } = {}) => { setBusy(true); try { if (replace && provision?.attemptId) { @@ -5931,7 +6373,7 @@ function WeixinSettingsTab({ rpcCall }) { setBusy(false); } }, [announce, invoke, provision?.attemptId]); - const cancelProvisioning = React10.useCallback(async () => { + const cancelProvisioning = React11.useCallback(async () => { setBusy(true); try { if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) { @@ -5946,7 +6388,7 @@ function WeixinSettingsTab({ rpcCall }) { setBusy(false); } }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]); - const submitVerification = React10.useCallback(async (verifyCode) => { + const submitVerification = React11.useCallback(async (verifyCode) => { if (!provision?.attemptId) return; setBusy(true); try { @@ -5962,7 +6404,7 @@ function WeixinSettingsTab({ rpcCall }) { setBusy(false); } }, [announce, invoke, provision?.attemptId]); - React10.useEffect(() => { + React11.useEffect(() => { const attemptId = provision?.attemptId; if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0; const controller = new AbortController(); @@ -6010,7 +6452,7 @@ function WeixinSettingsTab({ rpcCall }) { controller.abort(); }; }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]); - const setBotBusy = React10.useCallback((botId, value) => { + const setBotBusy = React11.useCallback((botId, value) => { setBusyByBot((current) => { const next = { ...current }; if (value) next[botId] = value; @@ -6018,7 +6460,7 @@ function WeixinSettingsTab({ rpcCall }) { return next; }); }, []); - const reconnect = React10.useCallback(async (account) => { + const reconnect = React11.useCallback(async (account) => { setBotBusy(account.botId, "reconnect"); try { const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.reconnectBot, { botId: account.botId })); @@ -6031,7 +6473,7 @@ function WeixinSettingsTab({ rpcCall }) { setBotBusy(account.botId, null); } }, [announce, invoke, setBotBusy]); - const remove = React10.useCallback(async (account) => { + const remove = React11.useCallback(async (account) => { setBotBusy(account.botId, "delete"); try { const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.deleteBot, { @@ -6049,9 +6491,9 @@ function WeixinSettingsTab({ rpcCall }) { }, [announce, invoke, setBotBusy]); let provisionView = null; if (provision?.status === "starting") { - provisionView = h9(ProgressPanel2, { busy }); + provisionView = h2(ProgressPanel2, { busy }); } else if (["pending", "scanned"].includes(provision?.status)) { - provisionView = h9(QrPanel4, { + provisionView = h2(QrPanel4, { provision, now, busy, @@ -6059,54 +6501,54 @@ function WeixinSettingsTab({ rpcCall }) { onCancel: () => void cancelProvisioning() }); } else if (provision?.status === "needs_verification") { - provisionView = h9(VerificationPanel, { + provisionView = h2(VerificationPanel, { provision, busy, onSubmit: (code) => void submitVerification(code), onCancel: () => void cancelProvisioning() }); } else if (provision?.status === "connecting") { - provisionView = h9(ProgressPanel2, { + provisionView = h2(ProgressPanel2, { scanned: true, busy, onCancel: () => void cancelProvisioning() }); } else if (provision && ["failed", "expired", "cancelled"].includes(provision.status)) { - provisionView = h9(ProvisionError3, { + provisionView = h2(ProvisionError3, { provision, busy, onRetry: () => void startProvisioning({ replace: Boolean(provision.attemptId) }), onClose: () => void cancelProvisioning() }); } - return h9( + return h2( "section", { className: "dxw-page dim-channelPage", "aria-label": "\u5FAE\u4FE1\u8BBE\u7F6E" }, - h9(Heading5, { + h2(Heading5, { totals: model.totals, adding: Boolean(provision), busy, onAdd: () => void startProvisioning(), addButtonRef }), - h9("div", { className: "dxw-visuallyHidden", role: "status", "aria-live": "polite" }, notice), - model.error && model.phase === "ready" ? h9("div", { className: "dxw-statusNotice dim-statusNotice" }, `\u72B6\u6001\u5237\u65B0\u5931\u8D25\uFF1A${model.error.message}`) : null, - model.phase === "loading" ? h9(LoadingView5) : model.phase === "error" ? h9( + h2("div", { className: "dxw-visuallyHidden", role: "status", "aria-live": "polite" }, notice), + model.error && model.phase === "ready" ? h2("div", { className: "dxw-statusNotice dim-statusNotice" }, `\u72B6\u6001\u5237\u65B0\u5931\u8D25\uFF1A${model.error.message}`) : null, + model.phase === "loading" ? h2(LoadingView5) : model.phase === "error" ? h2( "div", { className: "dxw-card dim-surfaceCard" }, - h9( + h2( "div", { className: "dxw-error dim-inlineError" }, - h9("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u72B6\u6001"), - h9("p", null, model.error?.message ?? "\u8BF7\u7A0D\u540E\u91CD\u8BD5"), - h9(Button11, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") + h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u72B6\u6001"), + h2("p", null, model.error?.message ?? "\u8BF7\u7A0D\u540E\u91CD\u8BD5"), + h2(Button11, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") ) - ) : h9( - React10.Fragment, + ) : h2( + React11.Fragment, null, provisionView, - model.bots.length === 0 && !provision ? h9(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null, - model.bots.length > 0 ? h9(AccountList2, { + model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null, + model.bots.length > 0 ? h2(AccountList2, { bots: model.bots, busyByBot, removeTarget, @@ -6230,7 +6672,7 @@ function formatRemaining6(milliseconds) { } // plugin-src/client/channels/whatsapp/index.js -var React11 = __toESM(require("react"), 1); +var React12 = __toESM(require("react"), 1); // plugin-src/client/channels/whatsapp/styles.js var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings"; @@ -6254,10 +6696,9 @@ function installWhatsappStyles() { } // plugin-src/client/channels/whatsapp/index.js -var h10 = React11.createElement; var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]); -var Button13 = React11.forwardRef(function Button14({ children, kind = "secondary", className = "", ...props }, ref) { - return h10("button", { +var Button13 = React12.forwardRef(function Button14({ children, kind = "secondary", className = "", ...props }, ref) { + return h2("button", { ...props, ref, type: "button", @@ -6278,70 +6719,70 @@ function checkedTime6(value) { } } function Heading6({ totals, busy, onAdd, addButtonRef }) { - return h10( + return h2( "div", { className: "ddt-heading" }, - h10( + h2( "div", { className: "ddt-tools" }, - h10( + h2( "div", { className: "dim-bindActions" }, - h10(Button13, { + h2(Button13, { kind: "primary", className: "dim-scanButton", onClick: onAdd, disabled: busy, ref: addButtonRef, "aria-label": "\u626B\u7801\u63A5\u5165 WhatsApp \u673A\u5668\u4EBA" - }, h10(QrActionIcon), busy ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA") + }, h2(QrActionIcon), busy ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA") ), - totals.configured > 0 ? h10( + totals.configured > 0 ? h2( "div", { className: "ddt-badge dim-onlineBadge" }, - h10("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) + h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) ) : null ) ); } function LoadingView6() { - return h10("div", { + return h2("div", { className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" - }, h10("div", { className: "ddt-spinner dim-spinner" }), "\u6B63\u5728\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001\u2026"); + }, h2("div", { className: "ddt-spinner dim-spinner" }), "\u6B63\u5728\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001\u2026"); } function EmptyView6({ busy, onStart }) { - return h10( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h10( + h2( "div", { className: "ddt-cardBody ddt-empty dim-surfaceBody dim-emptyView" }, - h10( + h2( "div", { className: "dim-emptyCopy" }, - h10( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h10("span", { className: "ddt-dot dim-stateDot" }), - h10("span", null, "\u5C1A\u672A\u63A5\u5165 WhatsApp \u673A\u5668\u4EBA") + h2("span", { className: "ddt-dot dim-stateDot" }), + h2("span", null, "\u5C1A\u672A\u63A5\u5165 WhatsApp \u673A\u5668\u4EBA") ), - h10("h3", null, "\u626B\u7801\u7ED1\u5B9A WhatsApp \u673A\u5668\u4EBA"), - h10("p", null, "\u4F7F\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801\u5373\u53EF\u63A5\u5165\u3002"), - h10( + h2("h3", null, "\u626B\u7801\u7ED1\u5B9A WhatsApp \u673A\u5668\u4EBA"), + h2("p", null, "\u4F7F\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801\u5373\u53EF\u63A5\u5165\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h10( + h2( Button13, { kind: "primary", onClick: onStart, disabled: busy }, busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u4E8C\u7EF4\u7801" ) ) ), - h10("div", { + h2("div", { className: "ddt-brandMark dim-emptyBrand dwa-avatar", "aria-hidden": "true" - }, h10(WhatsappLogoGlyph, { size: 64 })) + }, h2(WhatsappLogoGlyph, { size: 64 })) ) ); } @@ -6350,59 +6791,59 @@ function QrPanel5({ provision, now, busy, onRefresh, onCancel }) { const remaining = Math.max(0, provision.expiresAt - now); const duration = Math.max(1, provision.durationMs ?? 6e4); const progress = Math.round(Math.min(1, remaining / duration) * 100); - return h10( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h10( + h2( "div", { className: "ddt-cardBody ddt-qrLayout dim-surfaceBody dim-qrLayout" }, - h10( + h2( "div", { className: "ddt-qrColumn dim-qrColumn" }, - h10( + h2( "div", { className: "ddt-qrFrame dim-qrFrame" }, - source ? h10("img", { + source ? h2("img", { src: source, alt: "\u7528\u4E8E\u5173\u8054 WhatsApp \u8BBE\u5907\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801" - }) : h10("div", { className: "ddt-qrFallback dim-qrFallback" }, "\u4E8C\u7EF4\u7801\u6B63\u5728\u751F\u6210\u2026") + }) : h2("div", { className: "ddt-qrFallback dim-qrFallback" }, "\u4E8C\u7EF4\u7801\u6B63\u5728\u751F\u6210\u2026") ), - h10( + h2( "div", { className: "ddt-countdown dim-countdown" }, - h10( + h2( "div", { className: "ddt-countdownTop dim-countdownTop" }, - h10("span", null, "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), - h10("strong", null, formatRemaining6(remaining)) + h2("span", null, "\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"), + h2("strong", null, formatRemaining6(remaining)) ), - h10("div", { + h2("div", { className: "ddt-progress dim-progress", style: { "--ddt-progress": `${progress}%` } - }, h10("span")) + }, h2("span")) ) ), - h10( + h2( "div", { className: "ddt-qrCopy dim-qrCopy" }, - h10( + h2( "div", { className: "ddt-stateLabel dim-stateLabel" }, - h10("span", { className: "ddt-dot dim-stateDot", "data-tone": "warning" }), - h10("span", null, "\u7B49\u5F85 WhatsApp \u626B\u7801") + h2("span", { className: "ddt-dot dim-stateDot", "data-tone": "warning" }), + h2("span", null, "\u7B49\u5F85 WhatsApp \u626B\u7801") ), - h10("h3", null, "\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801"), - h10( + h2("h3", null, "\u7528\u624B\u673A WhatsApp \u626B\u63CF\u4E8C\u7EF4\u7801"), + h2( "ol", { className: "ddt-steps dim-steps" }, - h10("li", null, "\u6253\u5F00 WhatsApp \u2192 \u8BBE\u7F6E \u2192 \u5DF2\u5173\u8054\u8BBE\u5907"), - h10("li", null, "\u70B9\u51FB\u201C\u5173\u8054\u8BBE\u5907\u201D\u5E76\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801") + h2("li", null, "\u6253\u5F00 WhatsApp \u2192 \u8BBE\u7F6E \u2192 \u5DF2\u5173\u8054\u8BBE\u5907"), + h2("li", null, "\u70B9\u51FB\u201C\u5173\u8054\u8BBE\u5907\u201D\u5E76\u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801") ), - h10( + h2( "div", { className: "ddt-actions dim-viewActions" }, - h10(Button13, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h10(Button13, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88") + h2(Button13, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button13, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88") ) ) ) @@ -6411,50 +6852,50 @@ function QrPanel5({ provision, now, busy, onRefresh, onCancel }) { function ProvisionView3({ provision, busy, onRetry, onClose }) { if (provision.status === "starting" || provision.status === "connecting") { const starting = provision.status === "starting"; - return h10( + return h2( "div", { className: "ddt-card ddt-loading dim-surfaceCard dim-specialView", "aria-busy": "true" }, - h10("div", { className: "ddt-spinner dim-spinner" }), - h10("h3", null, starting ? "\u6B63\u5728\u751F\u6210 WhatsApp \u4E8C\u7EF4\u7801" : "\u5DF2\u626B\u7801\uFF0C\u6B63\u5728\u8FDE\u63A5 WhatsApp"), - h10("p", null, starting ? "\u6B63\u5728\u5EFA\u7ACB\u5B89\u5168\u7684\u5173\u8054\u8BBE\u5907\u4F1A\u8BDD\u3002" : "\u5173\u8054\u8BBE\u5907\u6B63\u5728\u63A5\u5165 DeepSeek Harness\u3002") + h2("div", { className: "ddt-spinner dim-spinner" }), + h2("h3", null, starting ? "\u6B63\u5728\u751F\u6210 WhatsApp \u4E8C\u7EF4\u7801" : "\u5DF2\u626B\u7801\uFF0C\u6B63\u5728\u8FDE\u63A5 WhatsApp"), + h2("p", null, starting ? "\u6B63\u5728\u5EFA\u7ACB\u5B89\u5168\u7684\u5173\u8054\u8BBE\u5907\u4F1A\u8BDD\u3002" : "\u5173\u8054\u8BBE\u5907\u6B63\u5728\u63A5\u5165 DeepSeek Harness\u3002") ); } const error = provision.error ?? { code: "WHATSAPP_PROVISION_FAILED", message: "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210" }; - return h10( + return h2( "div", { className: "ddt-card dim-surfaceCard" }, - h10( + h2( "div", { className: "ddt-inlineError dim-inlineError", role: "alert" }, - h10("h3", null, "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210"), - h10("p", null, error.message), - h10("span", { className: "ddt-errorCode" }, error.code), - h10( + h2("h3", null, "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210"), + h2("p", null, error.message), + h2("span", { className: "ddt-errorCode" }, error.code), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h10(Button13, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), - h10(Button13, { onClick: onClose, disabled: busy }, "\u5173\u95ED") + h2(Button13, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"), + h2(Button13, { onClick: onClose, disabled: busy }, "\u5173\u95ED") ) ) ); } function RemoveConfirmation5({ account, busy, onConfirm, onCancel }) { - return h10( + return h2( "div", { className: "ddt-confirm dim-confirm", role: "alertdialog" }, - h10("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), - h10("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 WhatsApp \u5173\u8054\u8BBE\u5907\u548C\u4F1A\u8BDD\u6620\u5C04\u3002"), - h10( + h2("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), + h2("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684 WhatsApp \u5173\u8054\u8BBE\u5907\u548C\u4F1A\u8BDD\u6620\u5C04\u3002"), + h2( "div", { className: "ddt-actions dim-viewActions" }, - h10(Button13, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), - h10( + h2(Button13, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"), + h2( Button13, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165" @@ -6475,65 +6916,65 @@ function WhatsappAccountCard({ const tone = account.connected ? "success" : state === "error" ? "error" : "warning"; const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA"; const summary = account.error?.message ?? (account.connected ? null : account.health.summary); - return h10( + return h2( "article", { className: "ddt-card dim-botCard", "data-bot-id": account.botId }, - h10( + h2( "div", { className: "ddt-cardBody dim-botCardBody" }, - h10( + h2( "div", { className: "ddt-accountTop dim-botCardTop" }, - h10( + h2( "div", { className: "ddt-accountIdentity dim-botIdentity" }, - h10("div", { + h2("div", { className: "ddt-avatar dim-botAvatar dwa-avatar", "aria-hidden": "true" - }, h10(WhatsappLogoGlyph, { size: 29 })), - h10( + }, h2(WhatsappLogoGlyph, { size: 29 })), + h2( "div", { className: "dim-botName" }, - h10("h3", null, account.bot.name), - h10("p", null, account.bot.idMasked) + h2("h3", null, account.bot.name), + h2("p", null, account.bot.idMasked) ) ), - h10( + h2( "div", { className: "ddt-health dim-botHealth" }, - h10("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), - h10("span", null, stateLabel) + h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }), + h2("span", null, stateLabel) ) ), - h10( + h2( "dl", { className: "ddt-metrics dim-botMetrics" }, - h10( + h2( "div", { className: "ddt-metric dim-botMetric" }, - h10("dt", null, "\u6D88\u606F\u901A\u9053"), - h10("dd", null, account.connected ? "WhatsApp Web" : "\u79BB\u7EBF") + h2("dt", null, "\u6D88\u606F\u901A\u9053"), + h2("dd", null, account.connected ? "WhatsApp Web" : "\u79BB\u7EBF") ), - h10( + h2( "div", { className: "ddt-metric dim-botMetric" }, - h10("dt", null, "\u6700\u8FD1\u68C0\u67E5"), - h10("dd", null, checkedTime6(account.health.lastCheckedAt)) + h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), + h2("dd", null, checkedTime6(account.health.lastCheckedAt)) ) ), - h10( + h2( "div", { className: "ddt-accountFooter dim-cardFooter" }, - summary ? h10("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, - h10( + summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null, + h2( "div", { className: "ddt-actions dim-cardActions" }, - h10(Button13, { + h2(Button13, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"), - h10(Button13, { + h2(Button13, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, @@ -6542,7 +6983,7 @@ function WhatsappAccountCard({ ) ) ), - removing ? h10(RemoveConfirmation5, { + removing ? h2(RemoveConfirmation5, { account, busy: busy === "delete", onConfirm: onConfirmRemove, @@ -6551,20 +6992,20 @@ function WhatsappAccountCard({ ); } function WhatsappSettingsTab({ rpcCall }) { - const [model, setModel] = React11.useState({ + const [model, setModel] = React12.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null }); - const [provision, setProvision] = React11.useState(null); - const [busy, setBusy] = React11.useState(false); - const [busyByBot, setBusyByBot] = React11.useState({}); - const [removeTarget, setRemoveTarget] = React11.useState(null); - const [now, setNow] = React11.useState(Date.now()); - const mounted = React11.useRef(true); - const addButtonRef = React11.useRef(null); - React11.useEffect(() => { + const [provision, setProvision] = React12.useState(null); + const [busy, setBusy] = React12.useState(false); + const [busyByBot, setBusyByBot] = React12.useState({}); + const [removeTarget, setRemoveTarget] = React12.useState(null); + const [now, setNow] = React12.useState(Date.now()); + const mounted = React12.useRef(true); + const addButtonRef = React12.useRef(null); + React12.useEffect(() => { const disposeDingtalk = installDingtalkStyles(); const disposeWhatsapp = installWhatsappStyles(); mounted.current = true; @@ -6574,11 +7015,11 @@ function WhatsappSettingsTab({ rpcCall }) { disposeDingtalk(); }; }, []); - const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => { + const invoke = React12.useCallback(async (endpoint, payload = {}, signal) => { if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5"); return unwrapRpcResult9(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React11.useCallback(async ({ signal, silent = false, restore = false } = {}) => { + const loadStatus = React12.useCallback(async ({ signal, silent = false, restore = false } = {}) => { if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null })); try { const snapshot = normalizeSnapshot8(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal)); @@ -6600,12 +7041,12 @@ function WhatsappSettingsTab({ rpcCall }) { return void 0; } }, [invoke]); - React11.useEffect(() => { + React12.useEffect(() => { const controller = new AbortController(); void loadStatus({ signal: controller.signal, restore: true }); return () => controller.abort(); }, [loadStatus]); - React11.useEffect(() => { + React12.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); const timer = window.setInterval( @@ -6617,12 +7058,12 @@ function WhatsappSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React11.useEffect(() => { + React12.useEffect(() => { if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0; const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3); return () => window.clearInterval(timer); }, [provision?.attemptId, provision?.status]); - const startProvisioning = React11.useCallback(async (replace = false) => { + const startProvisioning = React12.useCallback(async (replace = false) => { setBusy(true); try { if (replace && provision?.attemptId) { @@ -6640,7 +7081,7 @@ function WhatsappSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke, provision?.attemptId]); - const closeProvision = React11.useCallback(async () => { + const closeProvision = React12.useCallback(async () => { setBusy(true); try { if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) { @@ -6651,7 +7092,7 @@ function WhatsappSettingsTab({ rpcCall }) { if (mounted.current) setBusy(false); } }, [invoke, provision?.attemptId, provision?.status]); - React11.useEffect(() => { + React12.useEffect(() => { const attemptId = provision?.attemptId; if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0; const controller = new AbortController(); @@ -6692,7 +7133,7 @@ function WhatsappSettingsTab({ rpcCall }) { if (timer) window.clearTimeout(timer); }; }, [invoke, loadStatus, provision?.attemptId, provision?.status]); - const botAction = React11.useCallback(async (account, operation, endpoint, payload) => { + const botAction = React12.useCallback(async (account, operation, endpoint, payload) => { setBusyByBot((current) => ({ ...current, [account.botId]: operation })); try { const snapshot = normalizeSnapshot8(await invoke(endpoint, payload)); @@ -6707,15 +7148,15 @@ function WhatsappSettingsTab({ rpcCall }) { }); } }, [invoke]); - const botList = model.bots.length > 0 ? h10( + const botList = model.bots.length > 0 ? h2( "section", { className: "dim-listSection" }, - h10( + h2( "div", { className: "ddt-listHeading dim-listHeading" }, - h10("h3", null, "\u5DF2\u63A5\u5165\u7684 WhatsApp \u673A\u5668\u4EBA") + h2("h3", null, "\u5DF2\u63A5\u5165\u7684 WhatsApp \u673A\u5668\u4EBA") ), - h10("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h10("li", { key: account.botId }, h10(WhatsappAccountCard, { + h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(WhatsappAccountCard, { account, busy: busyByBot[account.botId], removing: removeTarget === account.botId, @@ -6736,43 +7177,43 @@ function WhatsappSettingsTab({ rpcCall }) { } })))) ) : null; - return h10( + return h2( "section", { className: "ddt-page dwa-page dim-channelPage", "aria-label": "WhatsApp \u8BBE\u7F6E" }, - h10(Heading6, { + h2(Heading6, { totals: model.totals, busy, onAdd: () => void startProvisioning(false), addButtonRef }), - model.phase === "loading" ? h10(LoadingView6) : model.phase === "error" ? h10( + model.phase === "loading" ? h2(LoadingView6) : model.phase === "error" ? h2( "div", { className: "ddt-card dim-surfaceCard" }, - h10( + h2( "div", { className: "ddt-inlineError dim-inlineError" }, - h10("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001"), - h10("p", null, model.error?.message), - h10(Button13, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") + h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001"), + h2("p", null, model.error?.message), + h2(Button13, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") ) - ) : h10( - React11.Fragment, + ) : h2( + React12.Fragment, null, - provision?.status === "pending" ? h10(QrPanel5, { + provision?.status === "pending" ? h2(QrPanel5, { provision, now, busy, onRefresh: () => void startProvisioning(true), onCancel: () => void closeProvision() - }) : provision ? h10(ProvisionView3, { + }) : provision ? h2(ProvisionView3, { provision, busy, onRetry: () => void startProvisioning(true), onClose: () => void closeProvision() - }) : model.bots.length === 0 ? h10(EmptyView6, { busy, onStart: () => void startProvisioning(false) }) : null, + }) : model.bots.length === 0 ? h2(EmptyView6, { busy, onStart: () => void startProvisioning(false) }) : null, botList ) ); @@ -6827,15 +7268,16 @@ var CSS10 = String.raw` .dim-panel { min-width: 0; container-type: inline-size; } .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page, .dim-panel .dqq-page, .dim-panel .dwecom-page, .dim-panel .dsl-page, .dim-panel .dwa-page { width: 100%; max-width: none; padding: 0 0 24px; } .dim-panel .bxf-heading, .dim-panel .dxw-heading, .dim-panel .ddt-heading { justify-content: flex-end; } -.dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; justify-content: space-between; } +.dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; justify-content: stretch; gap: 8px; } .dim-panel .dim-bindActions { min-width: 0; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; } -.dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; border: 1px solid #1677ff; border-radius: 8px; color: #fff; background: #1677ff; box-shadow: none; font: inherit; font-size: 13px; font-weight: 560; white-space: nowrap; } +.dim-panel .dim-bindActions > button { min-width: 0; } +.dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; justify-self: start; gap: 6px; padding: 0 10px; border: 1px solid #1677ff; border-radius: 8px; color: #fff; background: #1677ff; box-shadow: none; font: inherit; font-size: 13px; font-weight: 560; white-space: nowrap; } .dim-panel .bxf-headingTools .dim-scanButton:hover:not(:disabled), .dim-panel .dxw-tools .dim-scanButton:hover:not(:disabled), .dim-panel .ddt-tools .dim-scanButton:hover:not(:disabled) { border-color: #0958d9; background: #0958d9; } .dim-panel .dim-credentialButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; border: 1px solid #86909c; border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 5%); font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; } .dim-panel .dim-actionIcon { width: 15px; height: 15px; flex: 0 0 15px; } .dim-panel .dim-credentialButton:hover:not(:disabled) { border-color: #4e5969; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); } .dim-panel .dim-credentialButton[aria-pressed="true"] { border-color: #4e5969; background: var(--dsw-alias-bg-module-platform, #f2f3f5); box-shadow: inset 0 0 0 1px rgb(78 89 105 / 8%); } -.dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { flex: none; min-height: 30px; display: inline-flex; align-items: center; gap: 0; padding: 0 11px; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f2f3f5); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; } +.dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { min-height: 30px; display: inline-flex; align-items: center; justify-self: end; gap: 0; padding: 0 11px; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f2f3f5); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; } .dim-panel .dim-channelPage { width: 100%; max-width: none; display: flex; flex-direction: column; gap: 18px; padding: 0 0 24px; color: var(--dsw-alias-label-primary, #1f2329); box-sizing: border-box; } .dim-panel .dim-surfaceCard { position: relative; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); } .dim-panel .dim-surfaceCard::before { display: none; } @@ -6931,6 +7373,11 @@ var CSS10 = String.raw` .dim-panel .ddt-headingCopy { display: none; } .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); } @container (max-width: 680px) { + .dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { gap: 6px; } + .dim-panel .dim-bindActions { gap: 6px; } + .dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton, .dim-panel .dim-credentialButton { gap: 5px; padding-inline: 8px; font-size: 12px; } + .dim-panel .dim-actionIcon { width: 13px; height: 13px; flex-basis: 13px; } + .dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { padding-inline: 8px; font-size: 11px; } .dim-panel .dim-credentialForm { grid-template-columns: minmax(0, 1fr); } .dim-panel .dim-credentialError, .dim-panel .dim-credentialActions { grid-column: auto; } .dim-panel .dim-emptyView { min-height: 0; grid-template-columns: minmax(0, 1fr); } @@ -6977,9 +7424,8 @@ function installImStyles() { } // plugin-src/client/index.js -var h11 = React12.createElement; var name = "im-settings"; -var inject = ["slots", "connection"]; +var inject = ["slots", "connection", "locale"]; var CHANNELS = Object.freeze([ { id: "weixin", label: "\u5FAE\u4FE1" }, { id: "feishu", label: "\u98DE\u4E66" }, @@ -6992,70 +7438,70 @@ var CHANNELS = Object.freeze([ { id: "whatsapp", label: "WhatsApp" } ]); function WeixinLogo() { - return h11( + return h2( "span", { className: "dim-logo dim-logoWeixin", "aria-hidden": "true" }, - h11(WeixinLogoGlyph) + h2(WeixinLogoGlyph) ); } function FeishuLogo() { - return h11( + return h2( "span", { className: "dim-logo dim-logoFeishu", "aria-hidden": "true" }, - h11(FeishuLogoGlyph) + h2(FeishuLogoGlyph) ); } function DingtalkLogo() { - return h11( + return h2( "span", { className: "dim-logo dim-logoDingtalk", "aria-hidden": "true" }, - h11(DingtalkLogoGlyph) + h2(DingtalkLogoGlyph) ); } function QqLogo() { - return h11("span", { className: "dim-logo dim-logoQq", "aria-hidden": "true" }, h11(QqLogoGlyph)); + return h2("span", { className: "dim-logo dim-logoQq", "aria-hidden": "true" }, h2(QqLogoGlyph)); } function WecomLogo() { - return h11("span", { className: "dim-logo dim-logoWecom", "aria-hidden": "true" }, h11(WecomLogoGlyph)); + return h2("span", { className: "dim-logo dim-logoWecom", "aria-hidden": "true" }, h2(WecomLogoGlyph)); } function TelegramLogo() { - return h11( + return h2( "span", { className: "dim-logo dim-logoTelegram", "aria-hidden": "true" }, - h11(TelegramLogoGlyph) + h2(TelegramLogoGlyph) ); } function SlackLogo() { - return h11( + return h2( "span", { className: "dim-logo dim-logoSlack", "aria-hidden": "true" }, - h11(SlackLogoGlyph) + h2(SlackLogoGlyph) ); } function DiscordLogo() { - return h11( + return h2( "span", { className: "dim-logo dim-logoDiscord", "aria-hidden": "true" }, - h11(DiscordLogoGlyph) + h2(DiscordLogoGlyph) ); } function WhatsappLogo() { - return h11( + return h2( "span", { className: "dim-logo dim-logoWhatsapp", "aria-hidden": "true" }, - h11(WhatsappLogoGlyph) + h2(WhatsappLogoGlyph) ); } function ChannelLogo({ channel: channel4 }) { - if (channel4 === "weixin") return h11(WeixinLogo); - if (channel4 === "feishu") return h11(FeishuLogo); - if (channel4 === "dingtalk") return h11(DingtalkLogo); - if (channel4 === "wecom") return h11(WecomLogo); - if (channel4 === "qq") return h11(QqLogo); - if (channel4 === "slack") return h11(SlackLogo); - if (channel4 === "telegram") return h11(TelegramLogo); - if (channel4 === "discord") return h11(DiscordLogo); - return h11(WhatsappLogo); + if (channel4 === "weixin") return h2(WeixinLogo); + if (channel4 === "feishu") return h2(FeishuLogo); + if (channel4 === "dingtalk") return h2(DingtalkLogo); + if (channel4 === "wecom") return h2(WecomLogo); + if (channel4 === "qq") return h2(QqLogo); + if (channel4 === "slack") return h2(SlackLogo); + if (channel4 === "telegram") return h2(TelegramLogo); + if (channel4 === "discord") return h2(DiscordLogo); + return h2(WhatsappLogo); } function IMSettingsTab({ dingtalkRpcCall, @@ -7068,23 +7514,23 @@ function IMSettingsTab({ weixinRpcCall, whatsappRpcCall }) { - const [selected, setSelected] = React12.useState("weixin"); + const [selected, setSelected] = React13.useState("weixin"); const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0]; - return h11( + return h2( "section", { className: "dim-page", "aria-label": "IM\u673A\u5668\u4EBA\u8BBE\u7F6E" }, - h11( + h2( "header", { className: "dim-title" }, - h11("p", null, "\u628A\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness") + h2("p", null, "\u628A\u673A\u5668\u4EBA\u63A5\u5165 DeepSeek Harness") ), - h11( + h2( "div", { className: "dim-layout" }, - h11( + h2( "nav", { className: "dim-rail", role: "tablist", "aria-label": "IM \u6E20\u9053" }, - CHANNELS.map((channel4) => h11( + CHANNELS.map((channel4) => h2( "button", { key: channel4.id, @@ -7096,25 +7542,31 @@ function IMSettingsTab({ "aria-controls": `dim-panel-${channel4.id}`, onClick: () => setSelected(channel4.id) }, - h11(ChannelLogo, { channel: channel4.id }), - h11( + h2(ChannelLogo, { channel: channel4.id }), + h2( "span", { className: "dim-channelCopy" }, - h11("strong", null, channel4.label) + h2("strong", null, channel4.label) ) )) ), - h11("div", { className: "dim-divider", "aria-hidden": "true" }), - h11("main", { + h2("div", { className: "dim-divider", "aria-hidden": "true" }), + h2("main", { className: "dim-panel", role: "tabpanel", id: `dim-panel-${active.id}`, "aria-labelledby": `dim-tab-${active.id}` - }, active.id === "weixin" ? h11(WeixinSettingsTab, { rpcCall: weixinRpcCall }) : active.id === "feishu" ? h11(FeishuSettingsTab, { rpcCall: feishuRpcCall }) : active.id === "dingtalk" ? h11(DingtalkSettingsTab, { rpcCall: dingtalkRpcCall }) : active.id === "wecom" ? h11(WecomSettingsTab, { rpcCall: wecomRpcCall }) : active.id === "qq" ? h11(QqSettingsTab, { rpcCall: qqRpcCall }) : active.id === "slack" ? h11(SlackSettingsTab, { rpcCall: slackRpcCall }) : active.id === "telegram" ? h11(TelegramSettingsTab, { rpcCall: telegramRpcCall }) : active.id === "discord" ? h11(DiscordSettingsTab, { rpcCall: discordRpcCall }) : h11(WhatsappSettingsTab, { rpcCall: whatsappRpcCall })) + }, active.id === "weixin" ? h2(WeixinSettingsTab, { rpcCall: weixinRpcCall }) : active.id === "feishu" ? h2(FeishuSettingsTab, { rpcCall: feishuRpcCall }) : active.id === "dingtalk" ? h2(DingtalkSettingsTab, { rpcCall: dingtalkRpcCall }) : active.id === "wecom" ? h2(WecomSettingsTab, { rpcCall: wecomRpcCall }) : active.id === "qq" ? h2(QqSettingsTab, { rpcCall: qqRpcCall }) : active.id === "slack" ? h2(SlackSettingsTab, { rpcCall: slackRpcCall }) : active.id === "telegram" ? h2(TelegramSettingsTab, { rpcCall: telegramRpcCall }) : active.id === "discord" ? h2(DiscordSettingsTab, { rpcCall: discordRpcCall }) : h2(WhatsappSettingsTab, { rpcCall: whatsappRpcCall })) ) ); } function apply(ctx) { + ctx.effect( + () => ctx.locale.register(IM_LOCALE_NAMESPACE, { zh, en }), + "im-settings: bilingual dictionaries" + ); + const t = ctx.locale.bind(IM_LOCALE_NAMESPACE); + setImTranslator(t); ctx.effect(() => { const disposers = [ installFeishuStyles(), @@ -7144,7 +7596,8 @@ function apply(ctx) { name: "settings.plugins.tab", id: "im", order: 20, - label: "IM\u673A\u5668\u4EBA", + label: () => t("IM\u673A\u5668\u4EBA"), + locale: IM_LOCALE_NAMESPACE, inject: () => ({ dingtalkRpcCall, discordRpcCall, diff --git a/plugin-src/client/channels/dingtalk/index.js b/plugin-src/client/channels/dingtalk/index.js index 48db31d..8f0b431 100644 --- a/plugin-src/client/channels/dingtalk/index.js +++ b/plugin-src/client/channels/dingtalk/index.js @@ -1,6 +1,7 @@ import * as React from 'react'; import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js'; +import { h } from '../../i18n.js'; import { DINGTALK_ENDPOINTS, DINGTALK_RPC_CHANNEL, @@ -13,7 +14,6 @@ import { } from './api.js'; import { installDingtalkStyles } from './styles.js'; -const h = React.createElement; const ACTIVE_PROVISION_STATES = new Set(['pending', 'scanned', 'authorizing', 'creating', 'connecting']); export const name = 'dingtalk-settings'; diff --git a/plugin-src/client/channels/feishu/index.js b/plugin-src/client/channels/feishu/index.js index 956d6fb..ff67c53 100644 --- a/plugin-src/client/channels/feishu/index.js +++ b/plugin-src/client/channels/feishu/index.js @@ -2,6 +2,7 @@ import * as React from "react"; import { FeishuLogoGlyph } from "../../channel-logos.js"; import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from "../../credential-binding.js"; +import { h } from "../../i18n.js"; import { FEISHU_ENDPOINTS, FEISHU_RPC_CHANNEL, @@ -15,8 +16,6 @@ import { import { useAnimationFrameScheduler } from "../../lifecycle.js"; import { installFeishuStyles } from "./styles.js"; -const h = React.createElement; - export const name = "feishu-settings"; export const inject = ["slots", "connection"]; diff --git a/plugin-src/client/channels/qq/index.js b/plugin-src/client/channels/qq/index.js index 76ac805..a78838d 100644 --- a/plugin-src/client/channels/qq/index.js +++ b/plugin-src/client/channels/qq/index.js @@ -2,6 +2,7 @@ import * as React from 'react'; import { QqLogoGlyph } from '../../channel-logos.js'; import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js'; +import { h } from '../../i18n.js'; import { installDingtalkStyles } from '../dingtalk/styles.js'; import { QQ_ENDPOINTS, @@ -15,7 +16,6 @@ import { } from './api.js'; import { installQqStyles } from './styles.js'; -const h = React.createElement; const ACTIVE_STATES = new Set(['pending', 'refreshing', 'connecting']); const Button = React.forwardRef(function Button({ children, kind = 'secondary', className = '', ...props }, ref) { diff --git a/plugin-src/client/channels/shared/token-channel.js b/plugin-src/client/channels/shared/token-channel.js index 847e9f0..2371b95 100644 --- a/plugin-src/client/channels/shared/token-channel.js +++ b/plugin-src/client/channels/shared/token-channel.js @@ -1,10 +1,9 @@ import * as React from 'react'; import { CredentialActionIcon, CredentialBindingPanel } from '../../credential-binding.js'; +import { h } from '../../i18n.js'; import { installDingtalkStyles } from '../dingtalk/styles.js'; -const h = React.createElement; - const Button = React.forwardRef(function Button( { children, kind = 'secondary', className = '', ...props }, ref, diff --git a/plugin-src/client/channels/slack/index.js b/plugin-src/client/channels/slack/index.js index a2b2fdf..c16fce9 100644 --- a/plugin-src/client/channels/slack/index.js +++ b/plugin-src/client/channels/slack/index.js @@ -1,6 +1,7 @@ import * as React from 'react'; import { SlackLogoGlyph } from '../../channel-logos.js'; +import { h } from '../../i18n.js'; import { createTokenChannelSettings } from '../shared/token-channel.js'; import { SLACK_CREATE_APP_URL, @@ -9,8 +10,6 @@ import { import { SLACK_ENDPOINTS, slackClientApi } from './api.js'; import { installSlackStyles } from './styles.js'; -const h = React.createElement; - export function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) { const [botToken, setBotToken] = React.useState(''); const [appToken, setAppToken] = React.useState(''); diff --git a/plugin-src/client/channels/wecom/index.js b/plugin-src/client/channels/wecom/index.js index 421c7f1..83799e7 100644 --- a/plugin-src/client/channels/wecom/index.js +++ b/plugin-src/client/channels/wecom/index.js @@ -2,6 +2,7 @@ import * as React from 'react'; import { WecomLogoGlyph } from '../../channel-logos.js'; import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js'; +import { h } from '../../i18n.js'; import { installDingtalkStyles } from '../dingtalk/styles.js'; import { WECOM_ENDPOINTS, @@ -15,7 +16,6 @@ import { } from './api.js'; import { installWecomStyles } from './styles.js'; -const h = React.createElement; const ACTIVE_STATES = new Set(['pending', 'refreshing', 'connecting']); const Button = React.forwardRef(function Button({ children, kind = 'secondary', className = '', ...props }, ref) { diff --git a/plugin-src/client/channels/weixin/index.js b/plugin-src/client/channels/weixin/index.js index e5fc20f..bdcd6d6 100644 --- a/plugin-src/client/channels/weixin/index.js +++ b/plugin-src/client/channels/weixin/index.js @@ -2,6 +2,7 @@ import * as React from 'react'; import { WeixinLogoGlyph } from '../../channel-logos.js'; import { QrActionIcon } from '../../credential-binding.js'; +import { h } from '../../i18n.js'; import { WEIXIN_ENDPOINTS, WEIXIN_RPC_CHANNEL, @@ -16,8 +17,6 @@ import { import { createPollScheduler, useAnimationFrameScheduler } from '../../lifecycle.js'; import { installWeixinStyles } from './styles.js'; -const h = React.createElement; - export const name = 'weixin-settings'; export const inject = ['slots', 'connection']; diff --git a/plugin-src/client/channels/whatsapp/index.js b/plugin-src/client/channels/whatsapp/index.js index b77a4e3..6cb9f27 100644 --- a/plugin-src/client/channels/whatsapp/index.js +++ b/plugin-src/client/channels/whatsapp/index.js @@ -2,6 +2,7 @@ import * as React from 'react'; import { WhatsappLogoGlyph } from '../../channel-logos.js'; import { QrActionIcon } from '../../credential-binding.js'; +import { h } from '../../i18n.js'; import { installDingtalkStyles } from '../dingtalk/styles.js'; import { WHATSAPP_ENDPOINTS, @@ -14,7 +15,6 @@ import { } from './api.js'; import { installWhatsappStyles } from './styles.js'; -const h = React.createElement; const ACTIVE_STATES = new Set(['pending', 'connecting']); const Button = React.forwardRef(function Button( diff --git a/plugin-src/client/credential-binding.js b/plugin-src/client/credential-binding.js index 05d5d26..c3346ce 100644 --- a/plugin-src/client/credential-binding.js +++ b/plugin-src/client/credential-binding.js @@ -1,6 +1,5 @@ import * as React from 'react'; - -const h = React.createElement; +import { h } from './i18n.js'; function ActionIcon({ children }) { return h('svg', { diff --git a/plugin-src/client/i18n.js b/plugin-src/client/i18n.js new file mode 100644 index 0000000..1fed170 --- /dev/null +++ b/plugin-src/client/i18n.js @@ -0,0 +1,443 @@ +import * as React from 'react'; + +export const IM_LOCALE_NAMESPACE = 'dsh-im'; + +const EN = Object.freeze({ + '$locale': 'en', + 'IM机器人': 'IM bots', + 'IM机器人设置': 'IM bot settings', + 'IM 渠道': 'IM channels', + '把机器人接入 DeepSeek Harness': 'Connect bots to DeepSeek Harness', + '微信': 'WeChat', + '飞书': 'Feishu', + '钉钉': 'DingTalk', + '企业微信': 'WeCom', + '微信机器人': 'WeChat bot', + '飞书机器人': 'Feishu bot', + '钉钉机器人': 'DingTalk bot', + '企业微信机器人': 'WeCom bot', + 'QQ机器人': 'QQ bot', + 'WhatsApp机器人': 'WhatsApp bot', + 'WhatsApp账号': 'WhatsApp account', + '微信设置': 'WeChat settings', + '飞书机器人设置': 'Feishu bot settings', + '钉钉设置': 'DingTalk settings', + '企业微信设置': 'WeCom settings', + '扫码接入机器人': 'Scan QR code', + '正在接入': 'Connecting', + '手动接入': 'Manual setup', + '收起凭据': 'Hide credentials', + '收起接入': 'Hide setup', + '接入机器人': 'Connect bot', + '开始接入': 'Start setup', + '在线': 'online', + '运行正常': 'Connected', + '正在连接': 'Connecting', + '正在连接…': 'Connecting…', + '连接未就绪': 'Not connected', + '连接中': 'Connecting', + '连接中断': 'Disconnected', + '需要处理': 'Needs attention', + '状态未知': 'Unknown status', + '离线': 'Offline', + '已断开': 'Disconnected', + '消息通道': 'Message channel', + '最近检查': 'Last checked', + '尚未检查': 'Not checked yet', + '刚刚': 'Just now', + '检查连接': 'Check connection', + '检查中…': 'Checking…', + '重试连接': 'Reconnect', + '重试中…': 'Retrying…', + '移除接入': 'Remove connection', + '确认移除接入': 'Remove connection', + '确认移除': 'Remove', + '正在移除…': 'Removing…', + '保留机器人': 'Keep bot', + '保留账号': 'Keep account', + '取消': 'Cancel', + '关闭': 'Close', + '立即重试': 'Retry now', + '重新读取': 'Reload', + '重新生成二维码': 'Generate a new QR code', + '重新生成二维码后继续': 'Generate a new QR code', + '刷新二维码': 'Refresh QR code', + '刷新中…': 'Refreshing…', + '换一个二维码': 'Get another QR code', + '继续连接': 'Continue connecting', + '绑定并连接': 'Connect', + '正在绑定…': 'Connecting…', + '验证并连接': 'Verify and connect', + '正在验证并连接…': 'Verifying and connecting…', + '正在验证…': 'Verifying…', + '操作失败,请稍后重试': 'The operation failed. Try again later.', + '请稍后重试': 'Try again later.', + '当前二维码有效时间': 'QR code expires in', + '二维码有效时间': 'QR code expires in', + '二维码已过期': 'QR code expired', + '二维码已失效': 'QR code expired', + '二维码已过期\n请重新生成': 'QR code expired\nGenerate a new one', + '二维码图片正在生成…': 'Generating QR code…', + '二维码正在生成…': 'Generating QR code…', + '二维码正在自动刷新…': 'Refreshing QR code…', + '二维码未就绪,请打开授权链接': 'The QR code is not ready. Open the authorization link.', + '二维码图片未就绪,请使用备用链接。': 'The QR code is not ready. Use the alternate link.', + '二维码图片未就绪,请重新生成。': 'The QR code is not ready. Generate a new one.', + '等待刷新': 'Waiting to refresh', + '正在刷新二维码': 'Refreshing QR code', + '打开备用链接': 'Open alternate link', + '生成二维码': 'Generate QR code', + '生成微信二维码': 'Generate WeChat QR code', + '生成飞书二维码': 'Generate Feishu QR code', + '生成钉钉二维码': 'Generate DingTalk QR code', + '生成企业微信二维码': 'Generate WeCom QR code', + '生成 QQ 二维码': 'Generate QQ QR code', + '正在生成二维码…': 'Generating QR code…', + '正在准备授权二维码': 'Preparing authorization QR code', + '正在准备微信二维码': 'Preparing WeChat QR code', + '正在添加新机器人': 'Adding a new bot', + '正在申请钉钉授权二维码…': 'Requesting DingTalk authorization QR code…', + '正在申请企业微信二维码…': 'Requesting WeCom QR code…', + '正在申请 QQ 二维码…': 'Requesting QQ QR code…', + '正在生成 WhatsApp 二维码': 'Generating WhatsApp QR code', + '扫码,创建第一个飞书入口': 'Scan to create your first Feishu bot', + '扫码只会新增一个机器人,已接入的机器人会继续正常收发消息。': 'Scanning adds one bot. Existing bots will continue to send and receive messages.', + '无需手动填写 App ID。以后还可以继续添加机器人,分别服务不同团队或飞书租户。': 'No App ID is required. You can add more bots later for different teams or Feishu tenants.', + '使用飞书扫码创建机器人': 'Scan with Feishu to create a bot', + '刷新二维码后继续': 'Refresh the QR code to continue', + '打开飞书移动端,使用扫一扫读取二维码': 'Open Feishu on your phone and scan the QR code', + '核对应用名称与权限范围,并确认创建': 'Review the app name and permissions, then confirm', + '保持本页打开,等待新机器人的长连接就绪': 'Keep this page open until the bot connection is ready', + '在飞书中打开': 'Open in Feishu', + '取消添加': 'Cancel', + '已确认,正在连接新机器人': 'Confirmed. Connecting the new bot', + '正在安全保存凭据并检查新机器人的消息通道,其他机器人不会中断。': 'Saving credentials and checking the new bot connection. Existing bots will not be interrupted.', + '正在向飞书申请一次性授权二维码,请稍候。': 'Requesting a one-time authorization QR code from Feishu…', + '新机器人没有添加完成': 'The new bot was not added', + '新飞书机器人已连接,可以开始聊天。': 'The new Feishu bot is connected and ready to chat.', + '飞书应用创建失败': 'Could not create the Feishu app', + '机器人已经创建,但暂时无法确认连接状态': 'The bot was created, but its connection could not be confirmed yet', + '机器人仍未连接': 'The bot is still offline', + '机器人尚未连接': 'The bot is not connected yet', + '长连接运行正常': 'Persistent connection is healthy', + '长连接': 'Persistent connection', + '应用标识已安全保存': 'App identifier stored securely', + '机器人标识已安全保存': 'Bot identifier stored securely', + '已安全保存': 'Stored securely', + '已接入的微信账号': 'Connected WeChat accounts', + '已接入的机器人': 'Connected bots', + '已接入的钉钉机器人': 'Connected DingTalk bots', + '已绑定的企业微信机器人': 'Connected WeCom bots', + '已绑定的 QQ 机器人': 'Connected QQ bots', + '已接入的 WhatsApp 机器人': 'Connected WhatsApp accounts', + '使用手机微信扫描二维码': 'Scan with WeChat on your phone', + '扫一次码,就能在微信里使用 Harness': 'Scan once to use Harness in WeChat', + '打开手机微信并扫描左侧二维码': 'Open WeChat on your phone and scan the QR code', + '在微信中确认连接该机器人': 'Confirm the bot connection in WeChat', + '保持本页打开,等待机器人自动连接': 'Keep this page open while the bot connects', + '等待微信扫码': 'Waiting for WeChat scan', + '需要配对码': 'Pairing code required', + '输入手机微信显示的数字': 'Enter the number shown in WeChat', + '微信配对码': 'WeChat pairing code', + '已扫码,请在手机上确认': 'Scanned. Confirm on your phone', + '配对码已提交,正在等待微信确认。': 'Pairing code submitted. Waiting for WeChat confirmation.', + '这是微信附加的安全确认步骤。配对码只用于本次扫码轮询,不会写入配置或日志。': 'This is an additional WeChat confirmation step. The pairing code is used only for this connection and is never stored.', + '正在保存凭据并验证 Harness 与微信长轮询。': 'Saving credentials and verifying the WeChat connection.', + '微信已确认,正在启动消息连接': 'Confirmed in WeChat. Starting the message connection', + '微信已绑定,可以开始向已绑定的机器人发消息。': 'WeChat is connected and ready for messages.', + '这个微信账号已经绑定并保持在线。': 'This WeChat account is connected and online.', + '微信账号及本机凭据已移除。': 'The WeChat account and local credentials were removed.', + '已取消微信绑定。': 'WeChat setup was cancelled.', + '正在联系腾讯微信 iLink 服务。': 'Contacting the WeChat iLink service.', + 'iLink 长轮询': 'iLink long polling', + '扫一次码,自动创建并连接机器人': 'Scan once to create and connect a bot', + '使用钉钉 App 完成机器人授权': 'Authorize the bot with the DingTalk app', + '使用已加入企业/组织的钉钉账号扫描左侧二维码': 'Scan the QR code with a DingTalk account that belongs to an organization', + '在授权页点击“一键创建新机器人”': 'Select “Create new bot” on the authorization page', + '请勿关闭本页,钉钉完成授权后将自动继续。': 'Keep this page open. Setup will continue after DingTalk authorization.', + '等待钉钉扫码授权': 'Waiting for DingTalk authorization', + '授权已确认,正在创建钉钉机器人': 'Authorized. Creating the DingTalk bot', + '正在确认钉钉授权': 'Confirming DingTalk authorization', + '正在检查钉钉 Stream 长连接,成功后会自动显示为在线。': 'Checking the DingTalk Stream connection. It will appear online when ready.', + '钉钉机器人已接入,可以开始发送消息。': 'The DingTalk bot is connected and ready for messages.', + '这个钉钉机器人已经接入并保持在线。': 'This DingTalk bot is connected and online.', + 'Stream 长连接': 'Stream persistent connection', + '使用企业微信 App 扫码创建智能机器人': 'Scan with WeCom to create an AI bot', + '使用企业微信 App 完成智能机器人授权': 'Authorize the AI bot with WeCom', + '打开企业微信 App,扫描左侧二维码': 'Open WeCom and scan the QR code', + '在腾讯授权页面确认创建智能机器人': 'Confirm bot creation on the Tencent authorization page', + '返回这里等待连接完成': 'Return here and wait for the connection to complete', + '等待企业微信 App 扫码': 'Waiting for WeCom scan', + '企业微信已授权,正在连接机器人': 'Authorized in WeCom. Connecting the bot', + '凭据正在写入本机,并启动企业微信 WebSocket 消息连接。': 'Saving credentials locally and starting the WeCom WebSocket connection.', + 'WebSocket 长连接': 'WebSocket persistent connection', + '使用手机 QQ 扫码创建并绑定机器人': 'Scan with mobile QQ to create and connect a bot', + '使用手机 QQ 完成机器人绑定': 'Complete bot setup with mobile QQ', + '打开手机 QQ,扫描左侧二维码': 'Open mobile QQ and scan the QR code', + '在腾讯授权页面确认创建或绑定机器人': 'Confirm bot creation or connection on the Tencent authorization page', + '等待手机 QQ 扫码': 'Waiting for mobile QQ scan', + 'QQ 已授权,正在连接机器人': 'Authorized in QQ. Connecting the bot', + '凭据正在写入本机,并启动 QQ WebSocket 消息连接。': 'Saving credentials locally and starting the QQ WebSocket connection.', + '使用手机 WhatsApp 扫描二维码即可接入。': 'Scan the QR code with WhatsApp to connect.', + '用手机 WhatsApp 扫描二维码': 'Scan with WhatsApp on your phone', + '打开 WhatsApp → 设置 → 已关联设备': 'Open WhatsApp → Settings → Linked devices', + '点击“关联设备”并扫描左侧二维码': 'Select “Link a device” and scan the QR code', + '等待 WhatsApp 扫码': 'Waiting for WhatsApp scan', + '已扫码,正在连接 WhatsApp': 'Scanned. Connecting WhatsApp', + '正在建立安全的关联设备会话。': 'Creating a secure linked-device session.', + '关联设备正在接入 DeepSeek Harness。': 'Linking the device to DeepSeek Harness.', + 'WhatsApp Web 关联设备运行正常': 'WhatsApp linked device is healthy', + 'Bot API 长轮询': 'Bot API long polling', + ' Gateway 长连接': ' Gateway persistent connection', + 'Gateway 长连接': 'Gateway persistent connection', + ' Socket Mode 长连接': ' Socket Mode persistent connection', + 'Socket Mode 长连接': 'Socket Mode persistent connection', + '接入 Telegram 机器人': 'Connect a Telegram bot', + '先通过 @BotFather 获取 Bot Token,再在这里完成接入。': 'Get a Bot Token from @BotFather, then connect it here.', + '填写 @BotFather 生成的 Bot Token': 'Enter the Bot Token from @BotFather', + '接入 Discord 机器人': 'Connect a Discord bot', + '先在 Developer Portal 创建 Bot 并邀请到服务器,再在这里完成接入。': 'Create a bot in the Developer Portal and invite it to your server, then connect it here.', + '填写 Discord Developer Portal 的 Bot Token': 'Enter the Bot Token from the Discord Developer Portal', + '接入 Slack 机器人': 'Connect a Slack bot', + '先用 Manifest 创建并配置 Slack App': 'Create and configure a Slack app with the manifest', + '复制配置后,在 Slack 选择 From a manifest;创建完成后生成 connections:write App Token,并将应用安装到工作区。': 'Copy the manifest and choose “From a manifest” in Slack. Then create a connections:write App Token and install the app to your workspace.', + '复制 Manifest': 'Copy manifest', + '已复制 Manifest': 'Manifest copied', + '打开 Slack 创建页': 'Open Slack app creation', + 'Bot Token 来自 OAuth & Permissions;App Token 来自 Basic Information,并且必须包含 connections:write。': 'Get the Bot Token from OAuth & Permissions and the App Token from Basic Information. The App Token must include connections:write.', + '使用官方 App Manifest 快速配置机器人,再填写 Bot Token 与 App Token 建立本地 Socket Mode 连接。': 'Configure the bot with the official app manifest, then enter the Bot Token and App Token to start a local Socket Mode connection.', + 'Slack 工作区': 'Slack workspace', + 'Bot Token 与 App Token': 'Bot Token and App Token', + '填写 Bot Token': 'Enter Bot Token', + '手动接入飞书机器人': 'Connect Feishu bot manually', + '手动接入钉钉机器人': 'Connect DingTalk bot manually', + '手动接入企业微信机器人': 'Connect WeCom bot manually', + '手动接入QQ机器人': 'Connect QQ bot manually', + '填写飞书开放平台 App ID': 'Enter the Feishu Open Platform App ID', + '填写飞书开放平台 App Secret': 'Enter the Feishu Open Platform App Secret', + '填写钉钉应用 Client ID': 'Enter the DingTalk Client ID', + '填写钉钉应用 Client Secret': 'Enter the DingTalk Client Secret', + '填写企业微信智能机器人 Bot ID': 'Enter the WeCom AI Bot ID', + '填写企业微信智能机器人 Secret': 'Enter the WeCom AI Bot Secret', + '填写 QQ 开放平台 AppID': 'Enter the QQ Open Platform AppID', + '填写 QQ 开放平台 AppSecret': 'Enter the QQ Open Platform AppSecret', + '扫码接入微信机器人': 'Connect WeChat bot by QR code', + '扫码接入飞书机器人': 'Connect Feishu bot by QR code', + '扫码接入钉钉机器人': 'Connect DingTalk bot by QR code', + '扫码接入企业微信机器人': 'Connect WeCom bot by QR code', + '扫码接入 QQ 机器人': 'Connect QQ bot by QR code', + '扫码接入 WhatsApp 机器人': 'Connect WhatsApp by QR code', + '扫码绑定 WhatsApp 机器人': 'Connect WhatsApp by QR code', + '使用 App ID 和 App Secret 绑定飞书机器人': 'Connect a Feishu bot with App ID and App Secret', + '使用 Client ID 和 Client Secret 绑定钉钉机器人': 'Connect a DingTalk bot with Client ID and Client Secret', + '使用 Bot ID 和 Secret 绑定企业微信机器人': 'Connect a WeCom bot with Bot ID and Secret', + '使用 AppID 和 AppSecret 绑定 QQ 机器人': 'Connect a QQ bot with AppID and AppSecret', + '使用 Manifest 和双 Token 接入 Slack 机器人': 'Connect a Slack bot with a manifest and two tokens', + '使用 Bot Token 接入 Telegram 机器人': 'Connect a Telegram bot with a Bot Token', + '使用 Bot Token 接入 Discord 机器人': 'Connect a Discord bot with a Bot Token', + '取消绑定': 'Cancel setup', + '取消接入': 'Cancel setup', + '二维码由腾讯微信 iLink 服务签发。用手机微信扫描并确认后,账号凭据会直接写入 Harness Host,浏览器不会收到 bot_token。': 'The QR code is issued by Tencent WeChat iLink. After you scan and confirm, account credentials are written directly to the Harness Host and are never exposed to the browser.', + '扫码账号必须已加入企业/组织。如果钉钉提示尚未加入组织,请在提示页创建组织,或换用已加入组织的账号。': 'The DingTalk account must belong to an organization. If prompted, create an organization or use an account that already belongs to one.', + '请在手机上核对并确认授权。部分账号会额外显示一个配对数字,页面会在需要时提示输入。': 'Review and confirm authorization on your phone. Some accounts may also require a pairing number.', + '授权由钉钉官方页面完成。扫码账号必须已加入一个企业/组织并有权创建机器人;创建成功后,应用凭据会直接写入 Harness Host。': 'Authorization is completed on DingTalk’s official page. The account must belong to an organization and be allowed to create bots. Credentials are written directly to the Harness Host.', + '扫码由腾讯官方页面完成,不需要手动填写 AppID 或 AppSecret。扫码成功后,机器人会自动连接 DeepSeek Harness。': 'Scanning is completed on Tencent’s official page. No AppID or AppSecret is required, and the bot connects automatically.', + '扫码由腾讯官方页面完成,不需要手动填写 Bot ID 或 Secret。创建成功后,机器人会自动连接 DeepSeek Harness。': 'Scanning is completed on Tencent’s official page. No Bot ID or Secret is required, and the bot connects automatically.', + '腾讯页面会创建或绑定一个 QQ 机器人,并把连接凭据安全交给本机 Harness Host。': 'Tencent will create or connect a QQ bot and securely deliver its credentials to the local Harness Host.', + '企业微信官方页面会创建一个智能机器人,并把连接凭据安全交给本机 Harness Host。': 'WeCom will create an AI bot and securely deliver its credentials to the local Harness Host.', + '从此 Harness 移除这个微信账号?': 'Remove this WeChat account from Harness?', + '这会停止消息连接,并删除本机保存的 bot_token、账号配置和会话映射。其他微信账号不受影响。': 'This stops the message connection and removes the locally stored bot_token, account configuration, and session mappings. Other WeChat accounts are not affected.', + '此操作会停止这个机器人的连接,并删除保存在本机的接入配置和凭据。飞书开放平台中的应用不会被自动删除,其他机器人也不受影响。': 'This stops the bot connection and removes the locally stored configuration and credentials. The app in Feishu Open Platform is not deleted, and other bots are not affected.', + '这会停止消息连接,并删除本机保存的应用凭据、机器人配置及会话映射。钉钉开放平台中的机器人不会被自动删除。': 'This stops the message connection and removes the locally stored app credentials, bot configuration, and session mappings. The bot in DingTalk Open Platform is not deleted.', + '这会停止消息连接,并删除本机保存的应用凭据、机器人配置及会话映射。企业微信平台中的机器人不会被自动删除。': 'This stops the message connection and removes the locally stored app credentials, bot configuration, and session mappings. The bot in WeCom is not deleted.', + '这会停止消息连接,并删除本机保存的应用凭据、机器人配置及会话映射。腾讯平台中的机器人不会被自动删除。': 'This stops the message connection and removes the locally stored app credentials, bot configuration, and session mappings. The bot on Tencent’s platform is not deleted.', + '这会停止消息连接,并删除本机保存的 WhatsApp 关联设备和会话映射。': 'This stops the message connection and removes the locally stored WhatsApp linked device and session mappings.', + '正在读取飞书机器人列表': 'Loading Feishu bots', + '正在读取飞书连接状态…': 'Loading Feishu connection status…', + '正在读取微信连接状态…': 'Loading WeChat connection status…', + '正在读取钉钉连接状态…': 'Loading DingTalk connection status…', + '通过扫码把钉钉机器人接入 DeepSeek Harness': 'Connect a DingTalk bot to DeepSeek Harness by QR code', + '钉钉服务没有返回扫码绑定进度': 'DingTalk did not return QR setup progress', + '钉钉扫码服务没有返回有效的绑定任务': 'DingTalk did not return a valid setup attempt', + '钉钉 Stream 长连接运行正常': 'DingTalk Stream connection is healthy', + '钉钉服务没有返回有效的机器人列表': 'DingTalk did not return a valid bot list', + '${totals.connected} / ${totals.configured} 在线': '${totals.connected} / ${totals.configured} online', + '用于把钉钉机器人接入 DeepSeek Harness 的一次性二维码': 'One-time QR code for connecting a DingTalk bot to DeepSeek Harness', + '二维码已过期\\n请重新生成': 'QR code expired\\nGenerate a new one', + '机器人已创建,正在建立消息连接': 'Bot created. Starting the message connection', + '钉钉扫码服务没有返回安全的二维码': 'DingTalk did not return a secure QR code', + '钉钉二维码已生成,请使用钉钉 App 扫描。': 'DingTalk QR code generated. Scan it with the DingTalk app.', + '钉钉机器人凭据已绑定。': 'DingTalk bot credentials connected.', + '已取消钉钉机器人接入。': 'DingTalk bot setup cancelled.', + '钉钉机器人及本机凭据已移除。': 'DingTalk bot and local credentials removed.', + '飞书服务没有返回二维码信息': 'Feishu did not return QR code information', + '飞书服务返回的二维码信息不完整': 'Feishu returned incomplete QR code information', + '飞书服务返回了无效的机器人状态': 'Feishu returned an invalid bot status', + '飞书服务返回的机器人缺少 botId': 'The Feishu bot is missing botId', + '飞书服务没有返回连接状态': 'Feishu did not return connection status', + '飞书服务没有返回创建进度': 'Feishu did not return creation progress', + '飞书服务返回了未知的创建状态': 'Feishu returned an unknown creation status', + '已接入 ${totals.configured} 个机器人,其中 ${totals.connected} 个在线': '${totals.connected} of ${totals.configured} bots online', + '尚未接入机器人': 'No bot connected yet', + '用于新增 DeepSeek Harness 飞书机器人的一次性授权二维码': 'One-time authorization QR code for adding a Feishu bot to DeepSeek Harness', + '请刷新后重新扫码': 'Refresh and scan again', + '${connected ? "检查连接" : "重试连接"}${bot.name}': '${connected ? "Check connection" : "Reconnect"} ${bot.name}', + '无法读取飞书机器人': 'Could not load Feishu bots', + '授权二维码已生成,请使用飞书扫码。': 'Authorization QR code generated. Scan it with Feishu.', + '飞书机器人凭据已绑定。': 'Feishu bot credentials connected.', + '已取消添加机器人。': 'Adding the bot was cancelled.', + '${newBot.bot.name}已连接,可以在飞书中开始聊天。': '${newBot.bot.name} is connected and ready to chat in Feishu.', + '${bot.name}操作失败,请查看机器人状态。': '${bot.name} operation failed. Check the bot status.', + '${bot.name}已从此 DeepSeek Harness 移除;飞书开放平台中的应用未被删除。': '${bot.name} was removed from this DeepSeek Harness. The app in Feishu Open Platform was not deleted.', + '无法读取连接状态': 'Could not load connection status', + 'QQ 服务没有返回扫码绑定进度': 'QQ did not return QR setup progress', + 'QQ 扫码服务没有返回有效的绑定任务': 'QQ did not return a valid setup attempt', + 'QQ WebSocket 长连接运行正常': 'QQ WebSocket connection is healthy', + 'QQ 服务没有返回有效的机器人列表': 'QQ did not return a valid bot list', + '尚未绑定 QQ 机器人': 'No QQ bot connected yet', + '用于绑定 QQ 机器人的一次性二维码': 'One-time QR code for connecting a QQ bot', + '${channel}${connectionSummary}运行正常': '${channel}${connectionSummary} is healthy', + '${channel} 服务没有返回有效的机器人列表': '${channel} did not return a valid bot list', + '使用 Bot Token 接入 ${channel} 机器人': 'Connect a ${channel} bot with a Bot Token', + '${model.totals.connected} / ${model.totals.configured} 在线': '${model.totals.connected}/${model.totals.configured} online', + ' Bot API 长轮询': ' Bot API long polling', + '企业微信服务没有返回扫码绑定进度': 'WeCom did not return QR setup progress', + '企业微信扫码服务没有返回有效的绑定任务': 'WeCom did not return a valid setup attempt', + '企业微信 WebSocket 长连接运行正常': 'WeCom WebSocket connection is healthy', + '企业微信服务没有返回有效的机器人列表': 'WeCom did not return a valid bot list', + '尚未绑定企业微信机器人': 'No WeCom bot connected yet', + '用于绑定企业微信机器人的一次性二维码': 'One-time QR code for connecting a WeCom bot', + '微信扫码服务没有返回有效的绑定任务': 'WeChat did not return a valid setup attempt', + '微信绑定没有完成': 'WeChat setup did not complete', + '微信连接正常': 'WeChat connection is healthy', + '微信连接未就绪': 'WeChat connection is not ready', + '微信服务没有返回有效的账号列表': 'WeChat did not return a valid account list', + '尚未绑定微信': 'No WeChat account connected yet', + '用于把微信机器人绑定到 DeepSeek Harness 的一次性二维码': 'One-time QR code for connecting a WeChat bot to DeepSeek Harness', + '保持本页打开,等待消息长轮询变为在线': 'Keep this page open until long polling is online', + '微信二维码已生成,请使用手机微信扫描。': 'WeChat QR code generated. Scan it with WeChat on your phone.', + '移除失败:${presentError(error).message}': 'Removal failed: ${presentError(error).message}', + '无法读取微信状态': 'Could not load WeChat status', + 'WhatsApp 服务没有返回扫码进度': 'WhatsApp did not return QR setup progress', + 'WhatsApp 服务没有返回有效的扫码任务': 'WhatsApp did not return a valid setup attempt', + 'WhatsApp 服务没有返回有效的机器人列表': 'WhatsApp did not return a valid account list', + '用于关联 WhatsApp 设备的一次性二维码': 'One-time QR code for linking a WhatsApp device', +}); + +export const en = EN; +export const zh = Object.freeze(Object.fromEntries( + Object.keys(EN).map((key) => [key, key === '$locale' ? 'zh' : key]), +)); + +let translate = (key) => key; + +export function setImTranslator(next) { + translate = typeof next === 'function' ? next : (key) => key; +} + +export function isEnglish() { + return translate('$locale') === 'en'; +} + +function channelName(value) { + return localizeText(value); +} + +function translateDynamic(text) { + let match = /^(\d+) \/ (\d+) 在线$/.exec(text); + if (match) return `${match[1]}/${match[2]} online`; + match = /^已接入 (\d+) 个机器人,其中 (\d+) 个在线$/.exec(text); + if (match) return `${match[2]} of ${match[1]} bots online`; + match = /^正在读取\s*(.+?)\s*机器人状态…$/.exec(text); + if (match) return `Loading ${channelName(match[1])} bot status…`; + match = /^无法读取\s*(.+?)\s*机器人状态$/.exec(text); + if (match) return `Could not load ${channelName(match[1])} bot status`; + match = /^尚未接入\s*(.+?)\s*机器人$/.exec(text); + if (match) return `No ${channelName(match[1])} bot connected yet`; + match = /^已接入的\s*(.+?)\s*机器人$/.exec(text); + if (match) return `Connected ${channelName(match[1])} bots`; + match = /^手动接入(.+)机器人$/.exec(text); + if (match) return `Connect ${channelName(match[1])} bot manually`; + match = /^(.+) 设置$/.exec(text); + if (match) return `${channelName(match[1])} settings`; + match = /^从 DeepSeek Harness 移除“(.+)”?$/.exec(text); + if (match) return `Remove “${match[1]}” from DeepSeek Harness?`; + match = /^从 DeepSeek Harness 移除(.+)$/.exec(text); + if (match) return `Remove ${match[1]} from DeepSeek Harness`; + match = /^(检查连接|重试连接)(.+)$/.exec(text); + if (match) return `${localizeText(match[1])} ${match[2]}`; + match = /^移除(.+)$/.exec(text); + if (match) return `Remove ${match[1]}`; + match = /^这会停止消息连接,并删除本机保存的 (.+)、机器人配置及会话映射。(.+)中的机器人不会被自动删除。$/.exec(text); + if (match) { + return `This stops the message connection and removes the locally stored ${localizeText(match[1])}, bot configuration, and session mappings. The bot in ${localizeText(match[2])} is not deleted.`; + } + match = /^二维码剩余 (.+)$/.exec(text); + if (match) return `QR code expires in ${match[1]}`; + match = /^状态刷新失败:(.+)$/.exec(text); + if (match) return `Status refresh failed: ${match[1]}`; + match = /^状态自动刷新失败:(.+)$/.exec(text); + if (match) return `Automatic status refresh failed: ${match[1]}`; + match = /^操作失败:(.+)$/.exec(text); + if (match) return `Operation failed: ${match[1]}`; + match = /^连接检查失败:(.+)$/.exec(text); + if (match) return `Connection check failed: ${match[1]}`; + match = /^移除失败:(.+)$/.exec(text); + if (match) return `Removal failed: ${match[1]}`; + + const phrases = [ + ['企业微信', 'WeCom'], ['DeepSeek Harness', 'DeepSeek Harness'], + ['WhatsApp', 'WhatsApp'], ['Telegram', 'Telegram'], ['Discord', 'Discord'], + ['Slack', 'Slack'], ['飞书', 'Feishu'], ['钉钉', 'DingTalk'], ['微信', 'WeChat'], + ['机器人', 'bot'], ['账号', 'account'], ['应用', 'app'], ['凭据', 'credentials'], + ['服务返回了无法识别的响应', 'service returned an unrecognized response'], + ['服务没有返回有效的机器人列表', 'service did not return a valid bot list'], + ['操作失败,请稍后重试', 'operation failed; try again later'], + ['操作失败', 'operation failed'], ['连接尚未就绪', 'connection is not ready'], + ['没有接入完成', 'was not connected'], ['没有绑定完成', 'was not connected'], + ['设置页缺少 RPC 连接', 'settings are missing an RPC connection'], + ['设置', 'settings'], ['连接检查完成', 'connection check completed'], + ['仍未连接,插件会继续自动重试', 'is still offline; the plugin will keep retrying'], + ['已重新连接', 'reconnected'], ['移除失败,请重试', 'could not be removed; try again'], + ['已连接,可以开始聊天', 'is connected and ready to chat'], + ['已连接,可以开始发送消息', 'is connected and ready for messages'], + ['服务请求失败', 'service request failed'], ['连接遇到问题', 'connection encountered a problem'], + ['正在读取', 'Loading '], ['连接状态', 'connection status'], ['二维码', 'QR code'], + ]; + let output = text; + for (const [source, target] of phrases) output = output.replaceAll(source, target); + return output; +} + +export function localizeText(value) { + if (typeof value !== 'string') return value; + const exact = translate(value); + if (exact !== value || !isEnglish()) return exact; + return translateDynamic(value); +} + +const LOCALIZED_PROPS = Object.freeze([ + 'aria-label', + 'alt', + 'placeholder', + 'title', +]); + +function localizeChild(child) { + if (typeof child === 'string') return localizeText(child); + if (Array.isArray(child)) return child.map(localizeChild); + return child; +} + +export function h(type, props, ...children) { + let localizedProps = props; + if (props) { + for (const key of LOCALIZED_PROPS) { + if (typeof props[key] === 'string') { + localizedProps = localizedProps === props ? { ...props } : localizedProps; + localizedProps[key] = localizeText(props[key]); + } + } + } + return React.createElement(type, localizedProps, ...children.map(localizeChild)); +} diff --git a/plugin-src/client/index.js b/plugin-src/client/index.js index 1ad2dc1..aae1e66 100644 --- a/plugin-src/client/index.js +++ b/plugin-src/client/index.js @@ -37,12 +37,11 @@ import { installWeixinStyles } from './channels/weixin/styles.js'; import { WHATSAPP_RPC_CHANNEL } from './channels/whatsapp/api.js'; import { WhatsappSettingsTab } from './channels/whatsapp/index.js'; import { installWhatsappStyles } from './channels/whatsapp/styles.js'; +import { en, h, IM_LOCALE_NAMESPACE, setImTranslator, zh } from './i18n.js'; import { installImStyles } from './styles.js'; -const h = React.createElement; - export const name = 'im-settings'; -export const inject = ['slots', 'connection']; +export const inject = ['slots', 'connection', 'locale']; const CHANNELS = Object.freeze([ { id: 'weixin', label: '微信' }, @@ -172,6 +171,13 @@ export function IMSettingsTab({ } export function apply(ctx) { + ctx.effect( + () => ctx.locale.register(IM_LOCALE_NAMESPACE, { zh, en }), + 'im-settings: bilingual dictionaries', + ); + const t = ctx.locale.bind(IM_LOCALE_NAMESPACE); + setImTranslator(t); + ctx.effect(() => { const disposers = [ installFeishuStyles(), @@ -212,7 +218,8 @@ export function apply(ctx) { name: 'settings.plugins.tab', id: 'im', order: 20, - label: 'IM机器人', + label: () => t('IM机器人'), + locale: IM_LOCALE_NAMESPACE, inject: () => ({ dingtalkRpcCall, discordRpcCall, diff --git a/plugin-src/client/styles.js b/plugin-src/client/styles.js index 518a248..b44c609 100644 --- a/plugin-src/client/styles.js +++ b/plugin-src/client/styles.js @@ -47,15 +47,16 @@ const CSS = String.raw` .dim-panel { min-width: 0; container-type: inline-size; } .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page, .dim-panel .dqq-page, .dim-panel .dwecom-page, .dim-panel .dsl-page, .dim-panel .dwa-page { width: 100%; max-width: none; padding: 0 0 24px; } .dim-panel .bxf-heading, .dim-panel .dxw-heading, .dim-panel .ddt-heading { justify-content: flex-end; } -.dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; justify-content: space-between; } +.dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; justify-content: stretch; gap: 8px; } .dim-panel .dim-bindActions { min-width: 0; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; } -.dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; border: 1px solid #1677ff; border-radius: 8px; color: #fff; background: #1677ff; box-shadow: none; font: inherit; font-size: 13px; font-weight: 560; white-space: nowrap; } +.dim-panel .dim-bindActions > button { min-width: 0; } +.dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; justify-self: start; gap: 6px; padding: 0 10px; border: 1px solid #1677ff; border-radius: 8px; color: #fff; background: #1677ff; box-shadow: none; font: inherit; font-size: 13px; font-weight: 560; white-space: nowrap; } .dim-panel .bxf-headingTools .dim-scanButton:hover:not(:disabled), .dim-panel .dxw-tools .dim-scanButton:hover:not(:disabled), .dim-panel .ddt-tools .dim-scanButton:hover:not(:disabled) { border-color: #0958d9; background: #0958d9; } .dim-panel .dim-credentialButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; border: 1px solid #86909c; border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 5%); font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; } .dim-panel .dim-actionIcon { width: 15px; height: 15px; flex: 0 0 15px; } .dim-panel .dim-credentialButton:hover:not(:disabled) { border-color: #4e5969; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); } .dim-panel .dim-credentialButton[aria-pressed="true"] { border-color: #4e5969; background: var(--dsw-alias-bg-module-platform, #f2f3f5); box-shadow: inset 0 0 0 1px rgb(78 89 105 / 8%); } -.dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { flex: none; min-height: 30px; display: inline-flex; align-items: center; gap: 0; padding: 0 11px; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f2f3f5); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; } +.dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { min-height: 30px; display: inline-flex; align-items: center; justify-self: end; gap: 0; padding: 0 11px; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f2f3f5); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; } .dim-panel .dim-channelPage { width: 100%; max-width: none; display: flex; flex-direction: column; gap: 18px; padding: 0 0 24px; color: var(--dsw-alias-label-primary, #1f2329); box-sizing: border-box; } .dim-panel .dim-surfaceCard { position: relative; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); } .dim-panel .dim-surfaceCard::before { display: none; } @@ -151,6 +152,11 @@ const CSS = String.raw` .dim-panel .ddt-headingCopy { display: none; } .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); } @container (max-width: 680px) { + .dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { gap: 6px; } + .dim-panel .dim-bindActions { gap: 6px; } + .dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton, .dim-panel .dim-credentialButton { gap: 5px; padding-inline: 8px; font-size: 12px; } + .dim-panel .dim-actionIcon { width: 13px; height: 13px; flex-basis: 13px; } + .dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { padding-inline: 8px; font-size: 11px; } .dim-panel .dim-credentialForm { grid-template-columns: minmax(0, 1fr); } .dim-panel .dim-credentialError, .dim-panel .dim-credentialActions { grid-column: auto; } .dim-panel .dim-emptyView { min-height: 0; grid-template-columns: minmax(0, 1fr); } diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs index 0db6fb7..29bcf9f 100644 --- a/scripts/verify-package.mjs +++ b/scripts/verify-package.mjs @@ -12,6 +12,7 @@ const required = [ 'THIRD_PARTY_NOTICES.md', 'plugin-src/client/channels/dingtalk/index.js', 'plugin-src/client/channels/slack/index.js', + 'plugin-src/client/i18n.js', 'plugin-src/host/channels/feishu/index.mjs', 'plugin-src/host/channels/weixin/index.mjs', 'plugin-src/host/channels/dingtalk/index.mjs', @@ -50,8 +51,11 @@ const lock = JSON.parse(lockText); if (!client.includes('id: "@xmanrui/dsh-im"')) { throw new Error('client bundle does not register the dsh-im loader id'); } -if (!client.includes('id: "im"') || !client.includes('label: "IM\\u673A\\u5668\\u4EBA"')) { - throw new Error('client bundle does not register the IM机器人 settings tab'); +if (!client.includes('id: "im"') + || !client.includes('label: () => t("IM\\u673A\\u5668\\u4EBA")') + || !client.includes('locale: IM_LOCALE_NAMESPACE') + || !client.includes('IM_LOCALE_NAMESPACE = "dsh-im"')) { + throw new Error('client bundle does not register the localized IM settings tab'); } if ((client.match(/ctx\.slots\.inject\("settings\.plugins\.tab"/g) ?? []).length !== 1) { throw new Error('client bundle must register exactly one settings tab'); diff --git a/test/client-ui.test.mjs b/test/client-ui.test.mjs index 1d96e10..63e75ed 100644 --- a/test/client-ui.test.mjs +++ b/test/client-ui.test.mjs @@ -1,11 +1,15 @@ import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; +import { readFile, readdir } from 'node:fs/promises'; import test from 'node:test'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; -import { IMSettingsTab } from '../plugin-src/client/index.js'; +import { + apply as applyClient, + IMSettingsTab, + inject as clientInject, +} from '../plugin-src/client/index.js'; import { CredentialBindingPanel } from '../plugin-src/client/credential-binding.js'; import { DINGTALK_ENDPOINTS } from '../plugin-src/client/channels/dingtalk/api.js'; import { @@ -24,7 +28,33 @@ import { AccountCard as WecomAccountCard, WecomSettingsTab, } from '../plugin-src/client/channels/wecom/index.js'; -import { QqSettingsTab } from '../plugin-src/client/channels/qq/index.js'; +import { + AccountCard as QqAccountCard, + QqSettingsTab, +} from '../plugin-src/client/channels/qq/index.js'; +import { + SlackAccountCard, + SlackSettingsTab, +} from '../plugin-src/client/channels/slack/index.js'; +import { + TelegramAccountCard, + TelegramSettingsTab, +} from '../plugin-src/client/channels/telegram/index.js'; +import { + DiscordAccountCard, + DiscordSettingsTab, +} from '../plugin-src/client/channels/discord/index.js'; +import { + WhatsappAccountCard, + WhatsappSettingsTab, +} from '../plugin-src/client/channels/whatsapp/index.js'; +import { + en, + IM_LOCALE_NAMESPACE, + localizeText, + setImTranslator, + zh, +} from '../plugin-src/client/i18n.js'; const STYLES_URL = new URL('../plugin-src/client/styles.js', import.meta.url); const FEISHU_STYLES_URL = new URL( @@ -52,6 +82,7 @@ const WEIXIN_SOURCE_URL = new URL( import.meta.url, ); const CLIENT_BUNDLE_URL = new URL('../lib/client.js', import.meta.url); +const CLIENT_SOURCE_DIRECTORY_URL = new URL('../plugin-src/client/', import.meta.url); const DINGTALK_CLIENT_SOURCE_URL = new URL( '../plugin-src/client/channels/dingtalk/index.js', import.meta.url, @@ -263,7 +294,10 @@ test('scan actions align left while online totals align right in every channel', readFile(WECOM_SOURCE_URL, 'utf8'), ]); - assert.match(imStyles, /\.dim-panel \.bxf-headingTools, \.dim-panel \.dxw-tools, \.dim-panel \.ddt-tools \{[^}]*justify-content: space-between;/); + assert.match(imStyles, /\.dim-panel \.bxf-headingTools, \.dim-panel \.dxw-tools, \.dim-panel \.ddt-tools \{[^}]*display: grid;[^}]*grid-template-columns: minmax\(0, 1fr\) max-content;[^}]*justify-content: stretch;/); + assert.match(imStyles, /\.dim-panel \.dim-bindActions > button \{[^}]*min-width: 0;/); + assert.match(imStyles, /\.dim-panel \.bxf-headingTools \.dim-scanButton,[^}]*justify-self: start;/); + assert.match(imStyles, /\.dim-panel \.bxf-headingTools \.dim-onlineBadge,[^}]*justify-self: end;/); assert.match(feishuStyles, /\.bxf-headingTools \{[^}]*justify-content: space-between;/); assert.match(weixinStyles, /\.dxw-tools \{[^}]*justify-content: space-between;/); assert.match(dingtalkStyles, /\.ddt-tools \{[^}]*justify-content: space-between;/); @@ -482,3 +516,160 @@ test('the bundled DingTalk channel has no local sender approval workflow', async /bot\.sender\.approve|bot\.sender\.revoke|允许使用机器人的钉钉账号|批准使用/, ); }); + +test('every shipped Chinese client string has an English projection', async () => { + const paths = (await readdir(CLIENT_SOURCE_DIRECTORY_URL, { recursive: true })) + .filter((path) => path.endsWith('.js') && path !== 'i18n.js'); + const sources = await Promise.all(paths.map((path) => + readFile(new URL(path, CLIENT_SOURCE_DIRECTORY_URL), 'utf8'))); + const strings = new Set(); + for (const source of sources) { + for (const match of source.matchAll(/(['"`])((?:\\.|(?!\1)[\s\S])*?)\1/g)) { + if (/[\p{Script=Han}]/u.test(match[2])) strings.add(match[2]); + } + } + + setImTranslator((key) => en[key] ?? key); + try { + const untranslated = [...strings].filter((value) => + /[\p{Script=Han}]/u.test(localizeText(value))); + assert.deepEqual(untranslated, []); + assert.ok(strings.size > 350); + } finally { + setImTranslator(null); + } +}); + +test('client registers a live bilingual locale seat for the IM settings tab', () => { + const effects = []; + const registrations = []; + const dictionaries = []; + const rpcCall = async () => ({ ok: true, value: {} }); + const ctx = { + effect(install, label) { + effects.push({ install, label }); + }, + locale: { + bind(namespace) { + assert.equal(namespace, IM_LOCALE_NAMESPACE); + return (key) => en[key] ?? key; + }, + register(namespace, value) { + dictionaries.push({ namespace, value }); + return () => {}; + }, + }, + connection: { rpc: { call: rpcCall } }, + slots: { + inject(name, install) { + assert.equal(name, 'settings.plugins.tab'); + install(); + }, + register(options, component) { + registrations.push({ options, component }); + return () => {}; + }, + }, + }; + + try { + applyClient(ctx); + const dictionaryEffect = effects.find((entry) => entry.label === 'im-settings: bilingual dictionaries'); + assert.ok(dictionaryEffect); + dictionaryEffect.install(); + + assert.deepEqual(clientInject, ['slots', 'connection', 'locale']); + assert.equal(dictionaries[0].namespace, IM_LOCALE_NAMESPACE); + assert.deepEqual(Object.keys(dictionaries[0].value.en).sort(), Object.keys(dictionaries[0].value.zh).sort()); + assert.equal(registrations.length, 1); + assert.equal(registrations[0].options.locale, IM_LOCALE_NAMESPACE); + assert.equal(registrations[0].options.label(), 'IM bots'); + + const markup = renderToStaticMarkup(React.createElement( + registrations[0].component, + registrations[0].options.inject(), + )); + assert.match(markup, /Connect bots to DeepSeek Harness/); + assert.match(markup, />WeChat<|>Feishu<|>DingTalk<|>WeComQQ<[^]*>Slack<[^]*>Telegram<[^]*>Discord<[^]*>WhatsApp { + const rpcCall = async () => ({ ok: true, value: {} }); + const noop = () => {}; + const account = { + botId: 'bot-english', + state: 'connected', + connected: true, + bot: { + name: 'Demo Bot', + accountIdMasked: 'account••01', + appIdMasked: 'app••01', + clientIdMasked: 'client••01', + idMasked: 'bot••01', + username: 'demo_bot', + }, + health: { summary: 'Connection is healthy', lastCheckedAt: '2026-08-16T08:00:00.000Z' }, + }; + + setImTranslator((key) => en[key] ?? key); + try { + const pages = [ + WeixinSettingsTab, + FeishuSettingsTab, + DingtalkSettingsTab, + WecomSettingsTab, + QqSettingsTab, + SlackSettingsTab, + TelegramSettingsTab, + DiscordSettingsTab, + WhatsappSettingsTab, + ]; + const pageMarkup = pages.map((Component) => + renderToStaticMarkup(React.createElement(Component, { rpcCall }))).join('\n'); + assert.match(pageMarkup, /Scan QR code/); + assert.match(pageMarkup, /Manual setup/); + assert.match(pageMarkup, /Loading WeChat connection status/); + assert.match(pageMarkup, /Loading Feishu bots/); + assert.match(pageMarkup, /Loading DingTalk connection status/); + assert.match(pageMarkup, /Loading WeCom bot status/); + assert.match(pageMarkup, /Loading QQ bot status/); + assert.match(pageMarkup, /Loading Slack bot status/); + assert.match(pageMarkup, /Loading Telegram bot status/); + assert.match(pageMarkup, /Loading Discord bot status/); + assert.match(pageMarkup, /Loading WhatsApp bot status/); + assert.doesNotMatch(pageMarkup, /[\p{Script=Han}]/u); + + const sharedCardProps = { + removing: true, + onReconnect: noop, + onRequestRemove: noop, + onConfirmRemove: noop, + onCancelRemove: noop, + }; + const cards = [ + React.createElement(WeixinAccountCard, { ...sharedCardProps, account }), + React.createElement(FeishuBotCard, { ...sharedCardProps, connection: account }), + React.createElement(DingtalkAccountCard, { ...sharedCardProps, account }), + React.createElement(WecomAccountCard, { ...sharedCardProps, account }), + React.createElement(QqAccountCard, { ...sharedCardProps, account }), + React.createElement(SlackAccountCard, { ...sharedCardProps, account }), + React.createElement(TelegramAccountCard, { ...sharedCardProps, account }), + React.createElement(DiscordAccountCard, { ...sharedCardProps, account }), + React.createElement(WhatsappAccountCard, { ...sharedCardProps, account }), + ]; + const cardMarkup = cards.map(renderToStaticMarkup).join('\n'); + assert.match(cardMarkup, /Connected/); + assert.match(cardMarkup, /Message channel/); + assert.match(cardMarkup, /Last checked/); + assert.match(cardMarkup, /Check connection/); + assert.match(cardMarkup, /Remove connection/); + assert.doesNotMatch(cardMarkup, /[\p{Script=Han}]/u); + } finally { + setImTranslator(null); + } +});