@xmanrui/dsh-im 4.13.0 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +8 -1
- package/README.md +8 -1
- package/lib/client.js +327 -3
- package/lib/index.js +249 -245
- package/package.json +1 -1
- package/plugin-src/client/channels/feishu/api.js +2 -0
- package/plugin-src/client/channels/feishu/index.js +58 -1
- package/plugin-src/client/i18n.js +6 -1
- package/plugin-src/client/index.js +3 -0
- package/plugin-src/client/session-channel-logos.js +241 -0
- package/plugin-src/host/channels/feishu/production.mjs +1 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
- package/plugin-src/host/index.mjs +14 -0
- package/plugin-src/host/session-title-prefix.mjs +122 -0
- package/scripts/verify-session-channel-logos.mjs +59 -0
- package/scripts/verify-session-title-prefix.mjs +149 -0
- package/src/channels/feishu/bridge.mjs +520 -6
- package/src/channels/feishu/feishu-runtime.mjs +9 -0
- package/src/channels/feishu/multi-bot-controller.mjs +16 -0
- package/src/channels/feishu/plugin-config-store.mjs +1 -0
- package/src/channels/feishu/plugin-controller.mjs +1 -0
- package/src/channels/feishu/repair-manager.mjs +3 -2
- package/src/channels/shared/bot-workspace-store.mjs +14 -1
- package/src/channels/shared/harness-client.mjs +21 -2
- package/src/channels/shared/i18n-en/feishu.mjs +4 -2
- package/src/channels/shared/semantic/artifact.mjs +3 -1
- package/src/channels/shared/session-channel-labels.mjs +26 -0
package/README.en.md
CHANGED
|
@@ -60,6 +60,8 @@ Connect IM bots to DeepSeek Harness by scanning a QR code, using an App Manifest
|
|
|
60
60
|
|
|
61
61
|
Other IM platforms can be added through the same channel-adapter structure.
|
|
62
62
|
|
|
63
|
+
Feishu group chats accept messages from other bots that explicitly mention the current bot by default, with no extra setting. Messages without that mention, mentions of only other members or everyone, self-sent messages, and bot DMs are still ignored, even in all-message mode. Group allowlists and command permissions still apply. The app needs the `im:message.group_at_msg.include_bot:readonly` tenant scope. New apps request it through the QR flow; existing or manually connected apps can use **Complete permissions** or `/repair` in a direct chat, scan the QR code, and complete any publication or approval required by Feishu. See the [Feishu message-event permissions](https://open.feishu.cn/document/server-docs/im-v1/message/events/receive).
|
|
64
|
+
|
|
63
65
|
All nine built-in channels can send JPEG, PNG, and WebP images, plus GIFs sent as image files, with optional captions to Harness. Each image is limited to 5 MB, and images in one message are limited to 20 MB in total. Downloading images or files from Feishu user messages requires the `im:message:readonly` tenant scope, shown on the confirmation page as **Read direct and group messages**; Feishu currently offers no narrower image-only scope for that download endpoint. Apps created through the built-in QR flow request it by default; for existing or manually connected apps, click **Complete permissions** on the IM Bot settings page and scan the QR code to incrementally add that scope, `im:resource` for uploading bot-sent images or files, `application:app_slash_command:read` / `write` for the native command panel, and the card callback.
|
|
64
66
|
|
|
65
67
|
### Results after a reply timeout
|
|
@@ -132,6 +134,11 @@ Use the proxy URL required by your network and restart the Host after changing i
|
|
|
132
134
|
| Model | Every bot in all nine IM channels can choose a model directly below its workspace, or follow the Host default. A change applies only to later new Sessions; send `/new` and then an ordinary message in the current chat to use it. |
|
|
133
135
|
| Agent Preset | Each bot can choose an Agent Preset on its settings card. When none is chosen, new Sessions follow the Host's `agent-presets.default`. A channel-level `config.agentPreset` is only the default for later new bots on that channel. Changing the preset never modifies or clears existing Sessions; if the current chat already has a Session, send `/new` and then a regular message to create one with the new selection. |
|
|
134
136
|
| Context enhancement | Open settings from a bot card to enable groups and DMs independently. Both switches default to off, including for existing bots after an upgrade. |
|
|
137
|
+
| Session channel identity | Sessions from all nine IM channels and AI Office on the local Host are marked with their source. The Web Session list and search results display channel logos in place of prefixes such as “WeChat ·”, preserving DSH's automatic title generation and updates. Existing Sessions receive the prefix when next loaded. |
|
|
138
|
+
|
|
139
|
+
Channel prefixes are appended after DSH produces a title, preserving its complete text and automatic/manual source without pinning automatic titles or making additional model calls. Regeneration, refresh, and restarts do not stack prefixes; actual manual renames retain DSH's normal pinning behavior. This feature uses the current Host's Session events; an explicit remote `harnessBaseUrl` requires the plugin on the destination Host.
|
|
140
|
+
|
|
141
|
+
Logos are a dsh-im browser enhancement and require no changes to DSH. The adapter preserves original text nodes, clicks, menus, and dragging; copied titles, screen readers, and other surfaces retain the textual channel name. Unrecognized DSH page structures, unsupported browsers, or image-loading failures keep the text prefix. Unloading the plugin restores the original display.
|
|
135
142
|
|
|
136
143
|
### Proactive delivery
|
|
137
144
|
|
|
@@ -179,7 +186,7 @@ See the [Proactive Delivery Guide](PROACTIVE_DELIVERY.en.md) ([简体中文](PRO
|
|
|
179
186
|
| `/batch` | Start batch input in a direct chat and collect up to 10 text messages. |
|
|
180
187
|
| `/send` | Submit the collected messages, in order, as one input. |
|
|
181
188
|
| `/cancel` | Cancel batch input and discard its collected messages. |
|
|
182
|
-
| `/repair` | In a Feishu direct chat, incrementally repair the card callback and permissions required for media and the native Slash Command panel. |
|
|
189
|
+
| `/repair` | In a Feishu direct chat, incrementally repair the card callback and permissions required for media, group bot mentions, and the native Slash Command panel. |
|
|
183
190
|
| `/compact` | Immediately compact older context in the Session bound to the current chat. |
|
|
184
191
|
| `/workspace <workspace index or absolute path>`, `/ws <workspace index or absolute path>` | Switch the current bot's Harness workspace by `/workspacelist` index or absolute path. |
|
|
185
192
|
| `/workspacelist`, `/workspaces`, `/wsl` | List workspace absolute paths that still exist on the current Harness Host. |
|
package/README.md
CHANGED
|
@@ -63,6 +63,8 @@ Connect IM bots to DeepSeek Harness by scanning a QR code, using an App Manifest
|
|
|
63
63
|
|
|
64
64
|
其他 IM 平台可继续按同一渠道适配器结构接入。
|
|
65
65
|
|
|
66
|
+
飞书群聊默认接收其他机器人明确 @ 当前机器人的消息,无需额外开关;未 @、仅 @ 其他成员或全体、机器人自身发送的消息和机器人私聊消息仍会忽略,即使群聊响应方式设为“全部”。消息仍受群聊白名单与命令权限约束。飞书应用需要租户权限 `im:message.group_at_msg.include_bot:readonly`(“获取群组中其他机器人和用户@当前机器人的消息”);扫码新建应用会默认申请,已有或手动绑定的应用可点击“补全权限”或私聊执行 `/repair`,扫码并完成飞书要求的发布审批后生效。详见[飞书接收消息权限说明](https://open.feishu.cn/document/server-docs/im-v1/message/events/receive)。
|
|
67
|
+
|
|
66
68
|
九个内置渠道均支持把 JPEG、PNG、WebP 图片,以及以图片文件方式发送的 GIF,连同可选文字说明发送给 Harness;单张图片上限为 5 MB,单条消息中的图片总大小上限为 20 MB。飞书下载用户消息中的图片或文件需要租户权限 `im:message:readonly`,确认页将其显示为“获取单聊、群组消息”;飞书目前没有为该下载接口提供仅限图片的更窄权限。扫码新建的应用会默认申请;已有或手动绑定的应用可私聊机器人执行 `/repair`,或在「IM机器人」设置页点击“补全权限”,扫码增量补全该权限、上传机器人图片或文件所需的 `im:resource`、原生命令面板所需的 `application:app_slash_command:read` / `write`,以及卡片回调。
|
|
67
69
|
|
|
68
70
|
### 超时后的结果补发
|
|
@@ -135,6 +137,11 @@ dsh web
|
|
|
135
137
|
| 模型 | 九个 IM 渠道的每个机器人都可在工作区下方独立选择模型;未选择时跟随 Host 默认。切换只影响之后新建的会话;当前聊天先发送 `/new`,再发送普通消息才会使用新选择。 |
|
|
136
138
|
| Agent Preset | 每个机器人可在设置页卡片中选择 Agent Preset。未选择时跟随 Host 的 `agent-presets.default`;渠道级 `config.agentPreset` 只作为该渠道之后新接入机器人的默认值。切换不会修改或清空已有会话;若当前聊天已有会话,需先发送 `/new`,再发送一条普通消息,才会按新选择创建会话。 |
|
|
137
139
|
| 上下文增强 | 从机器人卡片打开设置,分别决定群聊、私聊是否增强;两个开关默认均关闭,旧机器人升级后也不会自动开启。 |
|
|
140
|
+
| 会话渠道标识 | 本机 Host 的九个 IM 渠道与 AI Office 会话自动标记来源。Web 会话列表和搜索结果将「微信 ·」等前缀显示为渠道 Logo;保留 DSH 原有的自动标题生成与更新。已有会话在下次加载时补上。 |
|
|
141
|
+
|
|
142
|
+
渠道前缀在 DSH 生成标题后追加,完整保留原始标题与自动/手动来源,不会将自动标题锁定为手动命名,也不会额外调用模型。重复生成、刷新或重启不会叠加前缀;真实的手动命名仍遵循 DSH 原有的锁定规则。此功能由当前 Host 的会话事件驱动,显式连接远程 `harnessBaseUrl` 时需在目标 Host 上安装该插件。
|
|
143
|
+
|
|
144
|
+
Logo 由 dsh-im 的浏览器适配显示,无需修改 DSH。适配保留原始文字节点和点击、菜单、拖拽操作;复制、读屏及其他界面仍保留文字渠道名。DSH 页面结构不匹配、浏览器不支持或图标加载失败时,自动保留文字前缀;插件卸载后恢复原始显示。
|
|
138
145
|
|
|
139
146
|
### 主动投递
|
|
140
147
|
|
|
@@ -182,7 +189,7 @@ dsh web
|
|
|
182
189
|
| `/batch` | 在私聊中开启批量输入,最多收集 10 条纯文字消息。 |
|
|
183
190
|
| `/send` | 将已收集的消息按原顺序作为一次输入提交。 |
|
|
184
191
|
| `/cancel` | 取消批量输入并丢弃已收集的消息。 |
|
|
185
|
-
| `/repair` |
|
|
192
|
+
| `/repair` | 在飞书私聊中增量修复卡片回调,并补全媒体、群聊机器人 @ 消息与原生 Slash Command 面板所需的权限。 |
|
|
186
193
|
| `/compact` | 立即压缩当前聊天绑定会话的较早上下文。 |
|
|
187
194
|
| `/workspace <工作区序号或绝对路径>`、`/ws <工作区序号或绝对路径>` | 按 `/workspacelist` 序号或绝对路径切换当前机器人的 Harness 工作区。 |
|
|
188
195
|
| `/workspacelist`、`/workspaces`、`/wsl` | 列出当前 Harness Host 上仍然存在的工作区绝对路径。 |
|
package/lib/client.js
CHANGED
|
@@ -571,7 +571,7 @@ var React27 = __toESM(require("react"), 1);
|
|
|
571
571
|
// package.json
|
|
572
572
|
var package_default = {
|
|
573
573
|
name: "@xmanrui/dsh-im",
|
|
574
|
-
version: "4.
|
|
574
|
+
version: "4.15.0",
|
|
575
575
|
description: "\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
|
|
576
576
|
keywords: [
|
|
577
577
|
"deepseek-harness",
|
|
@@ -1074,6 +1074,11 @@ var EN = Object.freeze({
|
|
|
1074
1074
|
"\u5173\u95ED\uFF08\u7FA4\u5185\u76F4\u63A5\u56DE\u590D\uFF09": "Off (reply in the group feed)",
|
|
1075
1075
|
"\u5F00\u542F\uFF08\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF09": "On (auto-open a dedicated Feishu topic)",
|
|
1076
1076
|
"\u5F00\u542F\u540E\uFF0C\u7FA4\u804A\u4E2D\u5411\u673A\u5668\u4EBA\u63D0\u95EE\u4F1A\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF0C\u56DE\u590D\u843D\u5728\u8BDD\u9898\u5185\uFF1B\u6BCF\u4E2A\u8BDD\u9898\u662F dsh \u4F1A\u8BDD\u5217\u8868\u91CC\u4E00\u6761\u72EC\u7ACB\u4F1A\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u4E92\u4E0D\u4E32\u3002\u79C1\u804A\u4E0D\u53D7\u5F71\u54CD\u3002": "When enabled, questions to the bot in a group auto-open a dedicated Feishu topic and replies stay inside it. Each topic is an independent conversation in dsh with its own context. Private chats are unaffected.",
|
|
1077
|
+
"\u5206\u6B65\u76F4\u63A8": "Step push",
|
|
1078
|
+
"\u5F00\u542F\u540E\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E": "Push tool calls and process notes step by step",
|
|
1079
|
+
"\u5173\u95ED\uFF08\u4FDD\u6301\u6D41\u5F0F\u5361\u6A21\u5F0F\uFF09": "Off (keep the streaming card)",
|
|
1080
|
+
"\u5F00\u542F\uFF08\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E\uFF09": "On (push tool calls and process notes step by step)",
|
|
1081
|
+
"\u5206\u6B65\u76F4\u63A8\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not save the step push setting. Try again.",
|
|
1077
1082
|
"\u9489\u9489\u8BBE\u7F6E": "DingTalk settings",
|
|
1078
1083
|
"\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E": "WeCom settings",
|
|
1079
1084
|
"\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA": "Scan QR code",
|
|
@@ -1308,7 +1313,7 @@ var EN = Object.freeze({
|
|
|
1308
1313
|
"\u6743\u9650\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u542F\u7528\u5168\u90E8\u6D88\u606F\u6A21\u5F0F\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002": "The permission update was submitted. Enabling all-message mode and reconnecting this bot. This stage cannot be cancelled; other bots will not be interrupted.",
|
|
1309
1314
|
"\u6B63\u5728\u4E3A\u73B0\u6709\u98DE\u4E66\u5E94\u7528\u7533\u8BF7\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002": "Requesting a group-message permission QR code for the existing Feishu app\u2026",
|
|
1310
1315
|
"\u7FA4\u6D88\u606F\u6743\u9650\u6CA1\u6709\u5F00\u901A\u5B8C\u6210": "Group-message permission was not granted",
|
|
1311
|
-
"\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002": "Scanning updates the existing Feishu app with the missing card-button callback, im:message:readonly for reading images or files in user messages (shown by Feishu as \u201CRead direct and group messages\u201D), im:resource for uploading images or files sent by the bot, and application:app_slash_command:read / write for the native command panel. It does not create a new app. The confirmation page shows only missing items; this bot reconnects briefly afterward, while other bots are unaffected.",
|
|
1316
|
+
"\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\u3001\u63A5\u6536\u7FA4\u5185\u5176\u4ED6\u673A\u5668\u4EBA @ \u5F53\u524D\u673A\u5668\u4EBA\u6240\u9700\u7684 im:message.group_at_msg.include_bot:readonly\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002": "Scanning updates the existing Feishu app with the missing card-button callback, im:message:readonly for reading images or files in user messages (shown by Feishu as \u201CRead direct and group messages\u201D), im:resource for uploading images or files sent by the bot, im:message.group_at_msg.include_bot:readonly for receiving group messages from other bots that mention this bot, and application:app_slash_command:read / write for the native command panel. It does not create a new app. The confirmation page shows only missing items; this bot reconnects briefly afterward, while other bots are unaffected.",
|
|
1312
1317
|
"\u6838\u5BF9\u73B0\u6709\u5E94\u7528\u540D\u79F0\uFF0C\u5E76\u786E\u8BA4\u53EA\u65B0\u589E\u5F53\u524D\u7F3A\u5C11\u7684\u4E0A\u8FF0\u914D\u7F6E": "Review the existing app name and confirm that only the missing items described above are added",
|
|
1313
1318
|
"\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6743\u9650\u4E0E\u56DE\u8C03\u8865\u5168\u5B8C\u6210": "Keep this page open until permissions and the callback are complete",
|
|
1314
1319
|
"\u53D6\u6D88\u8865\u5168": "Cancel setup",
|
|
@@ -5758,6 +5763,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
|
|
|
5758
5763
|
setAccessPolicy: "bot.access-policy.set",
|
|
5759
5764
|
setGroupResponseMode: "bot.group-response-mode.set",
|
|
5760
5765
|
setGroupTopicReply: "bot.group-topic-reply.set",
|
|
5766
|
+
setStepPush: "bot.step-push.set",
|
|
5761
5767
|
// Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
|
|
5762
5768
|
testConnection: "connection.test",
|
|
5763
5769
|
disconnect: "connection.disconnect"
|
|
@@ -5910,6 +5916,7 @@ function normalizeBotConnection(value, fallbackBotId) {
|
|
|
5910
5916
|
...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
|
|
5911
5917
|
groupResponseMode: normalizeGroupResponseMode(value.groupResponseMode),
|
|
5912
5918
|
groupTopicReply: value.groupTopicReply === true,
|
|
5919
|
+
stepPush: value.stepPush === true,
|
|
5913
5920
|
groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
|
|
5914
5921
|
bot: normalizeBot2(value.bot),
|
|
5915
5922
|
health: normalizeHealth(value.health, connected),
|
|
@@ -6337,7 +6344,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
|
|
|
6337
6344
|
h2("span", null, repairing ? `\u6B63\u5728\u4E3A\u300C${botName}\u300D\u8865\u5168\u6743\u9650\u4E0E\u56DE\u8C03` : grantingGroupMessages ? `\u6B63\u5728\u4E3A\u300C${botName}\u300D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650` : "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA")
|
|
6338
6345
|
),
|
|
6339
6346
|
h2("h3", null, expired ? "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : repairing ? "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u8865\u5168\u6743\u9650" : grantingGroupMessages ? "\u4F7F\u7528\u98DE\u4E66\u786E\u8BA4\u7FA4\u6D88\u606F\u6743\u9650" : "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u521B\u5EFA\u673A\u5668\u4EBA"),
|
|
6340
|
-
h2("p", null, repairing ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : grantingGroupMessages ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : "\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"),
|
|
6347
|
+
h2("p", null, repairing ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\u3001\u63A5\u6536\u7FA4\u5185\u5176\u4ED6\u673A\u5668\u4EBA @ \u5F53\u524D\u673A\u5668\u4EBA\u6240\u9700\u7684 im:message.group_at_msg.include_bot:readonly\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : grantingGroupMessages ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : "\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"),
|
|
6341
6348
|
h2(
|
|
6342
6349
|
"ol",
|
|
6343
6350
|
{ className: "bxf-steps dim-steps" },
|
|
@@ -6480,6 +6487,61 @@ function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
|
|
|
6480
6487
|
)
|
|
6481
6488
|
);
|
|
6482
6489
|
}
|
|
6490
|
+
function StepPushEditor({ value = false, disabled = false, onSave }) {
|
|
6491
|
+
const titleId = React15.useId();
|
|
6492
|
+
const current = value === true ? "on" : "off";
|
|
6493
|
+
const [saving, setSaving] = React15.useState(false);
|
|
6494
|
+
const [error, setError] = React15.useState(null);
|
|
6495
|
+
const change = async (event) => {
|
|
6496
|
+
const next = event.target.value === "on";
|
|
6497
|
+
if ((next ? "on" : "off") === current || saving || disabled) return;
|
|
6498
|
+
setSaving(true);
|
|
6499
|
+
setError(null);
|
|
6500
|
+
try {
|
|
6501
|
+
await onSave?.(next);
|
|
6502
|
+
} catch (cause) {
|
|
6503
|
+
setError(cause?.message ?? "\u5206\u6B65\u76F4\u63A8\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
6504
|
+
} finally {
|
|
6505
|
+
setSaving(false);
|
|
6506
|
+
}
|
|
6507
|
+
};
|
|
6508
|
+
return h2(
|
|
6509
|
+
"section",
|
|
6510
|
+
{
|
|
6511
|
+
className: "dim-feishuGroupControl",
|
|
6512
|
+
"aria-labelledby": titleId
|
|
6513
|
+
},
|
|
6514
|
+
h2(
|
|
6515
|
+
"div",
|
|
6516
|
+
{ className: "dim-feishuGroupControlHeader" },
|
|
6517
|
+
h2("h3", { id: titleId }, "\u5206\u6B65\u76F4\u63A8"),
|
|
6518
|
+
saving ? h2("span", { className: "dim-feishuGroupControlStatus", role: "status" }, "\u4FDD\u5B58\u4E2D\u2026") : null
|
|
6519
|
+
),
|
|
6520
|
+
h2(
|
|
6521
|
+
"select",
|
|
6522
|
+
{
|
|
6523
|
+
className: "dim-feishuGroupSelect",
|
|
6524
|
+
value: current,
|
|
6525
|
+
disabled: disabled || saving,
|
|
6526
|
+
"aria-label": "\u5206\u6B65\u76F4\u63A8",
|
|
6527
|
+
onChange: (event) => {
|
|
6528
|
+
void change(event);
|
|
6529
|
+
}
|
|
6530
|
+
},
|
|
6531
|
+
h2("option", { value: "off" }, "\u5173\u95ED\uFF08\u4FDD\u6301\u6D41\u5F0F\u5361\u6A21\u5F0F\uFF09"),
|
|
6532
|
+
h2("option", { value: "on" }, "\u5F00\u542F\uFF08\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E\uFF09")
|
|
6533
|
+
),
|
|
6534
|
+
h2(
|
|
6535
|
+
"p",
|
|
6536
|
+
{ className: "dim-feishuGroupHelp" },
|
|
6537
|
+
"\u5F00\u542F\u540E\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E"
|
|
6538
|
+
),
|
|
6539
|
+
error ? h2("p", {
|
|
6540
|
+
className: "dim-feishuGroupError",
|
|
6541
|
+
role: "alert"
|
|
6542
|
+
}, error) : null
|
|
6543
|
+
);
|
|
6544
|
+
}
|
|
6483
6545
|
function BotCard({
|
|
6484
6546
|
connection,
|
|
6485
6547
|
busy,
|
|
@@ -6495,6 +6557,7 @@ function BotCard({
|
|
|
6495
6557
|
onModelSave,
|
|
6496
6558
|
onAgentPresetSave,
|
|
6497
6559
|
onContextEnhancementSave,
|
|
6560
|
+
onStepPushSave,
|
|
6498
6561
|
onRequestRemove,
|
|
6499
6562
|
onConfirmRemove,
|
|
6500
6563
|
onCancelRemove,
|
|
@@ -6597,6 +6660,11 @@ function BotCard({
|
|
|
6597
6660
|
disabled: Boolean(busy),
|
|
6598
6661
|
onSave: onContextEnhancementSave
|
|
6599
6662
|
}),
|
|
6663
|
+
h2(StepPushEditor, {
|
|
6664
|
+
value: connection.stepPush,
|
|
6665
|
+
disabled: Boolean(busy),
|
|
6666
|
+
onSave: onStepPushSave
|
|
6667
|
+
}),
|
|
6600
6668
|
provisionContent ? h2("section", {
|
|
6601
6669
|
className: "bxf-botProvision dim-botProvision",
|
|
6602
6670
|
"aria-label": `${bot.name}\u7684\u98DE\u4E66\u6388\u6743\u6D41\u7A0B`,
|
|
@@ -6707,6 +6775,7 @@ function BotList(props) {
|
|
|
6707
6775
|
onModelSave: (model) => props.onModelSave(bot, model),
|
|
6708
6776
|
onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(bot, agentPreset),
|
|
6709
6777
|
onContextEnhancementSave: (config) => props.onContextEnhancementSave(bot, config),
|
|
6778
|
+
onStepPushSave: (stepPush) => props.onStepPushSave(bot, stepPush),
|
|
6710
6779
|
onRequestRemove: () => props.onRequestRemove(bot),
|
|
6711
6780
|
onConfirmRemove: () => props.onConfirmRemove(bot),
|
|
6712
6781
|
onCancelRemove: props.onCancelRemove,
|
|
@@ -7409,6 +7478,12 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
7409
7478
|
FEISHU_ENDPOINTS.setContextEnhancement,
|
|
7410
7479
|
{ config }
|
|
7411
7480
|
),
|
|
7481
|
+
onStepPushSave: (connection, stepPush) => saveBotSetting(
|
|
7482
|
+
connection,
|
|
7483
|
+
"step-push",
|
|
7484
|
+
FEISHU_ENDPOINTS.setStepPush,
|
|
7485
|
+
{ stepPush }
|
|
7486
|
+
),
|
|
7412
7487
|
onRequestRemove: requestRemove,
|
|
7413
7488
|
onConfirmRemove: (bot) => void confirmRemove(bot),
|
|
7414
7489
|
onCancelRemove: cancelRemove,
|
|
@@ -15186,6 +15261,254 @@ function installImStyles() {
|
|
|
15186
15261
|
return () => style.remove();
|
|
15187
15262
|
}
|
|
15188
15263
|
|
|
15264
|
+
// src/channels/shared/session-channel-labels.mjs
|
|
15265
|
+
var SESSION_CHANNEL_LABELS = Object.freeze(Object.fromEntries(Object.entries({
|
|
15266
|
+
weixin: ["\u5FAE\u4FE1", "WeChat"],
|
|
15267
|
+
feishu: ["\u98DE\u4E66", "Feishu"],
|
|
15268
|
+
dingtalk: ["\u9489\u9489", "DingTalk"],
|
|
15269
|
+
wecom: ["\u4F01\u4E1A\u5FAE\u4FE1", "WeCom"],
|
|
15270
|
+
qq: ["QQ", "QQ"],
|
|
15271
|
+
slack: ["Slack", "Slack"],
|
|
15272
|
+
telegram: ["Telegram", "Telegram"],
|
|
15273
|
+
discord: ["Discord", "Discord"],
|
|
15274
|
+
whatsapp: ["WhatsApp", "WhatsApp"],
|
|
15275
|
+
office: ["AI Office", "AI Office"]
|
|
15276
|
+
}).map(([channel4, labels]) => [channel4, Object.freeze(labels)])));
|
|
15277
|
+
function parseSessionChannelTitle(title) {
|
|
15278
|
+
if (typeof title !== "string") return null;
|
|
15279
|
+
for (const [channel4, labels] of Object.entries(SESSION_CHANNEL_LABELS)) {
|
|
15280
|
+
for (const label of labels) {
|
|
15281
|
+
const prefix = `${label} \xB7 `;
|
|
15282
|
+
if (title.startsWith(prefix) && title.slice(prefix.length).trim()) {
|
|
15283
|
+
return { channel: channel4, title: title.slice(prefix.length) };
|
|
15284
|
+
}
|
|
15285
|
+
}
|
|
15286
|
+
}
|
|
15287
|
+
return null;
|
|
15288
|
+
}
|
|
15289
|
+
|
|
15290
|
+
// plugin-src/client/session-channel-logos.js
|
|
15291
|
+
var GLYPHS = Object.freeze({
|
|
15292
|
+
weixin: [WeixinLogoGlyph, "#07c160"],
|
|
15293
|
+
feishu: [FeishuLogoGlyph, "#3370ff"],
|
|
15294
|
+
dingtalk: [DingtalkLogoGlyph, "#1677ff"],
|
|
15295
|
+
wecom: [WecomLogoGlyph, "#3370ff"],
|
|
15296
|
+
qq: [QqLogoGlyph, "#12b7f5"],
|
|
15297
|
+
slack: [SlackLogoGlyph, "#e01e5a"],
|
|
15298
|
+
telegram: [TelegramLogoGlyph, "#229ed9"],
|
|
15299
|
+
discord: [DiscordLogoGlyph, "#5865f2"],
|
|
15300
|
+
whatsapp: [WhatsappLogoGlyph, "#25d366"],
|
|
15301
|
+
office: [OfficeLogoGlyph, "#f97316"]
|
|
15302
|
+
});
|
|
15303
|
+
var CHANNEL_ATTR = "data-dsh-im-session-channel";
|
|
15304
|
+
var TITLE_ATTR = "data-dsh-im-session-text";
|
|
15305
|
+
var MARKED = `[${CHANNEL_ATTR}]`;
|
|
15306
|
+
var ROW = '[role="treeitem"][aria-selected]';
|
|
15307
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
15308
|
+
var installations = /* @__PURE__ */ new WeakMap();
|
|
15309
|
+
function hasClassPart(element, part) {
|
|
15310
|
+
const match = new RegExp(`(?:^|[_-])${part}(?:$|[_-])`, "u");
|
|
15311
|
+
return [...element.classList].some((token) => match.test(token));
|
|
15312
|
+
}
|
|
15313
|
+
function titleOf(row) {
|
|
15314
|
+
if (!row.matches(ROW) || row.closest('input, textarea, [contenteditable]:not([contenteditable="false"])')) return null;
|
|
15315
|
+
let title;
|
|
15316
|
+
if (row.tagName === "DIV" && hasClassPart(row, "sessionRow")) {
|
|
15317
|
+
title = [...row.children].find((child) => child.tagName === "SPAN" && hasClassPart(child, "title"));
|
|
15318
|
+
} else if (row.tagName === "BUTTON" && hasClassPart(row, "searchResultRow")) {
|
|
15319
|
+
const heading = [...row.children].find((child) => hasClassPart(child, "searchResultHeading"));
|
|
15320
|
+
title = heading && [...heading.children].find((child) => child.tagName === "SPAN" && hasClassPart(child, "searchResultTitle"));
|
|
15321
|
+
}
|
|
15322
|
+
return title?.childNodes.length === 1 && title.firstChild.nodeType === 3 ? title : null;
|
|
15323
|
+
}
|
|
15324
|
+
function logoUri(document2, channel4) {
|
|
15325
|
+
const [Glyph, color] = GLYPHS[channel4];
|
|
15326
|
+
const render = (node) => {
|
|
15327
|
+
if (!["svg", "path"].includes(node.type)) throw new Error("Unsupported channel logo element");
|
|
15328
|
+
const element = document2.createElementNS(SVG_NS, node.type);
|
|
15329
|
+
for (const [name2, value] of Object.entries(node.props)) {
|
|
15330
|
+
if (name2 === "children") continue;
|
|
15331
|
+
const attribute = name2.startsWith("stroke") ? name2.replace(/[A-Z]/gu, (letter) => `-${letter.toLowerCase()}`) : name2;
|
|
15332
|
+
element.setAttribute(attribute, String(value));
|
|
15333
|
+
}
|
|
15334
|
+
for (const child of [node.props.children].flat().filter(Boolean)) element.appendChild(render(child));
|
|
15335
|
+
return element;
|
|
15336
|
+
};
|
|
15337
|
+
const svg = render(Glyph({ size: 16 }));
|
|
15338
|
+
svg.setAttribute("xmlns", SVG_NS);
|
|
15339
|
+
svg.setAttribute("color", color);
|
|
15340
|
+
return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`;
|
|
15341
|
+
}
|
|
15342
|
+
function createInstallation(document2) {
|
|
15343
|
+
const view = document2.defaultView;
|
|
15344
|
+
const ready = /* @__PURE__ */ new Set();
|
|
15345
|
+
const owned = /* @__PURE__ */ new Map();
|
|
15346
|
+
const queued = /* @__PURE__ */ new Set();
|
|
15347
|
+
const images = [];
|
|
15348
|
+
let closed = false;
|
|
15349
|
+
let scheduled = false;
|
|
15350
|
+
const style = document2.createElement("style");
|
|
15351
|
+
style.dataset.pluginCss = "dsh-im-session-channel-logos";
|
|
15352
|
+
const uris = Object.fromEntries(Object.keys(GLYPHS).map((channel4) => [channel4, logoUri(document2, channel4)]));
|
|
15353
|
+
style.textContent = `
|
|
15354
|
+
${MARKED}[${TITLE_ATTR}] {
|
|
15355
|
+
position: relative;
|
|
15356
|
+
-webkit-text-fill-color: transparent;
|
|
15357
|
+
text-overflow: clip !important;
|
|
15358
|
+
overflow: hidden;
|
|
15359
|
+
}
|
|
15360
|
+
${MARKED}[${TITLE_ATTR}]::before {
|
|
15361
|
+
content: "";
|
|
15362
|
+
position: absolute;
|
|
15363
|
+
inset-inline-start: 0;
|
|
15364
|
+
top: 50%;
|
|
15365
|
+
transform: translateY(-50%);
|
|
15366
|
+
width: 16px;
|
|
15367
|
+
height: 16px;
|
|
15368
|
+
background: var(--dsh-im-session-logo) center / contain no-repeat;
|
|
15369
|
+
text-indent: 0;
|
|
15370
|
+
pointer-events: none;
|
|
15371
|
+
}
|
|
15372
|
+
${MARKED}[${TITLE_ATTR}]::after {
|
|
15373
|
+
content: attr(${TITLE_ATTR}) / "";
|
|
15374
|
+
position: absolute;
|
|
15375
|
+
inset: 0;
|
|
15376
|
+
inset-inline-start: 22px;
|
|
15377
|
+
-webkit-text-fill-color: currentColor;
|
|
15378
|
+
text-indent: 0;
|
|
15379
|
+
overflow: hidden;
|
|
15380
|
+
white-space: nowrap;
|
|
15381
|
+
text-overflow: ellipsis;
|
|
15382
|
+
pointer-events: none;
|
|
15383
|
+
}
|
|
15384
|
+
${Object.entries(uris).map(([channel4, uri]) => `[${CHANNEL_ATTR}="${channel4}"] { --dsh-im-session-logo: url("${uri}"); }`).join("\n")}
|
|
15385
|
+
`;
|
|
15386
|
+
document2.head.appendChild(style);
|
|
15387
|
+
const restore = (title) => {
|
|
15388
|
+
const previous = owned.get(title);
|
|
15389
|
+
if (!previous) return;
|
|
15390
|
+
owned.delete(title);
|
|
15391
|
+
for (const [attribute, value] of previous) {
|
|
15392
|
+
if (value === null) title.removeAttribute(attribute);
|
|
15393
|
+
else title.setAttribute(attribute, value);
|
|
15394
|
+
}
|
|
15395
|
+
};
|
|
15396
|
+
const update = (row) => {
|
|
15397
|
+
const title = row.isConnected ? titleOf(row) : null;
|
|
15398
|
+
for (const marked of row.querySelectorAll(MARKED)) {
|
|
15399
|
+
if (marked !== title) restore(marked);
|
|
15400
|
+
}
|
|
15401
|
+
if (!title) return;
|
|
15402
|
+
const parsed = parseSessionChannelTitle(title.textContent);
|
|
15403
|
+
if (!parsed || !ready.has(parsed.channel)) {
|
|
15404
|
+
restore(title);
|
|
15405
|
+
return;
|
|
15406
|
+
}
|
|
15407
|
+
if (!owned.has(title)) {
|
|
15408
|
+
owned.set(title, [CHANNEL_ATTR, TITLE_ATTR].map((attribute) => [attribute, title.getAttribute(attribute)]));
|
|
15409
|
+
}
|
|
15410
|
+
title.setAttribute(TITLE_ATTR, parsed.title);
|
|
15411
|
+
title.setAttribute(CHANNEL_ATTR, parsed.channel);
|
|
15412
|
+
};
|
|
15413
|
+
const schedule = () => {
|
|
15414
|
+
if (closed || scheduled || !queued.size) return;
|
|
15415
|
+
scheduled = true;
|
|
15416
|
+
queueMicrotask(() => {
|
|
15417
|
+
scheduled = false;
|
|
15418
|
+
if (closed) return;
|
|
15419
|
+
const rows = [...queued];
|
|
15420
|
+
queued.clear();
|
|
15421
|
+
for (const row of rows) {
|
|
15422
|
+
try {
|
|
15423
|
+
update(row);
|
|
15424
|
+
} catch {
|
|
15425
|
+
for (const marked of row.querySelectorAll(MARKED)) restore(marked);
|
|
15426
|
+
}
|
|
15427
|
+
}
|
|
15428
|
+
});
|
|
15429
|
+
};
|
|
15430
|
+
const collect = (node, descendants = false) => {
|
|
15431
|
+
const element = node.nodeType === 1 ? node : node.parentElement;
|
|
15432
|
+
if (!element) return;
|
|
15433
|
+
const row = element.closest(ROW);
|
|
15434
|
+
if (row) queued.add(row);
|
|
15435
|
+
if (descendants) {
|
|
15436
|
+
for (const child of element.querySelectorAll(ROW)) queued.add(child);
|
|
15437
|
+
const marked = [...element.querySelectorAll(MARKED)];
|
|
15438
|
+
if (owned.has(element)) marked.push(element);
|
|
15439
|
+
for (const title of marked) {
|
|
15440
|
+
const owner = title.closest(ROW);
|
|
15441
|
+
if (owner) queued.add(owner);
|
|
15442
|
+
else restore(title);
|
|
15443
|
+
}
|
|
15444
|
+
}
|
|
15445
|
+
schedule();
|
|
15446
|
+
};
|
|
15447
|
+
const observer = new view.MutationObserver((records) => {
|
|
15448
|
+
if (closed) return;
|
|
15449
|
+
for (const record2 of records) {
|
|
15450
|
+
collect(record2.target, record2.type === "attributes");
|
|
15451
|
+
if (record2.type !== "childList") continue;
|
|
15452
|
+
for (const node of record2.addedNodes) collect(node, true);
|
|
15453
|
+
for (const node of record2.removedNodes) {
|
|
15454
|
+
if (node.nodeType !== 1 || node.isConnected) continue;
|
|
15455
|
+
if (owned.has(node)) restore(node);
|
|
15456
|
+
for (const title of node.querySelectorAll(MARKED)) restore(title);
|
|
15457
|
+
}
|
|
15458
|
+
}
|
|
15459
|
+
});
|
|
15460
|
+
observer.observe(document2.body, {
|
|
15461
|
+
subtree: true,
|
|
15462
|
+
childList: true,
|
|
15463
|
+
characterData: true,
|
|
15464
|
+
attributes: true,
|
|
15465
|
+
attributeFilter: ["class", "role", "aria-selected", "contenteditable"]
|
|
15466
|
+
});
|
|
15467
|
+
for (const [channel4, uri] of Object.entries(uris)) {
|
|
15468
|
+
const image = new view.Image();
|
|
15469
|
+
images.push(image);
|
|
15470
|
+
image.onload = () => {
|
|
15471
|
+
if (closed) return;
|
|
15472
|
+
ready.add(channel4);
|
|
15473
|
+
collect(document2.body, true);
|
|
15474
|
+
};
|
|
15475
|
+
image.src = uri;
|
|
15476
|
+
}
|
|
15477
|
+
return () => {
|
|
15478
|
+
closed = true;
|
|
15479
|
+
observer.disconnect();
|
|
15480
|
+
queued.clear();
|
|
15481
|
+
for (const image of images) image.onload = null;
|
|
15482
|
+
for (const title of [...owned.keys()]) restore(title);
|
|
15483
|
+
style.remove();
|
|
15484
|
+
};
|
|
15485
|
+
}
|
|
15486
|
+
function installSessionChannelLogos(document2 = globalThis.document) {
|
|
15487
|
+
const view = document2?.defaultView;
|
|
15488
|
+
if (!document2?.body || !document2?.head || !view?.MutationObserver || !view?.Image || !view.CSS?.supports?.("content", `attr(${TITLE_ATTR}) / ""`) || !view.CSS.supports("-webkit-text-fill-color", "transparent")) return () => {
|
|
15489
|
+
};
|
|
15490
|
+
let entry = installations.get(document2);
|
|
15491
|
+
if (!entry) {
|
|
15492
|
+
try {
|
|
15493
|
+
entry = { references: 0, close: createInstallation(document2) };
|
|
15494
|
+
} catch {
|
|
15495
|
+
return () => {
|
|
15496
|
+
};
|
|
15497
|
+
}
|
|
15498
|
+
installations.set(document2, entry);
|
|
15499
|
+
}
|
|
15500
|
+
entry.references += 1;
|
|
15501
|
+
let released = false;
|
|
15502
|
+
return () => {
|
|
15503
|
+
if (released) return;
|
|
15504
|
+
released = true;
|
|
15505
|
+
entry.references -= 1;
|
|
15506
|
+
if (entry.references > 0) return;
|
|
15507
|
+
entry.close();
|
|
15508
|
+
installations.delete(document2);
|
|
15509
|
+
};
|
|
15510
|
+
}
|
|
15511
|
+
|
|
15189
15512
|
// plugin-src/client/update-panel.js
|
|
15190
15513
|
var React26 = __toESM(require("react"), 1);
|
|
15191
15514
|
var import_react_dom3 = require("react-dom");
|
|
@@ -16014,6 +16337,7 @@ function apply(ctx) {
|
|
|
16014
16337
|
);
|
|
16015
16338
|
const t = ctx.locale.bind(IM_LOCALE_NAMESPACE);
|
|
16016
16339
|
setImTranslator(t);
|
|
16340
|
+
ctx.effect(() => installSessionChannelLogos(), "im-settings: Session channel logos");
|
|
16017
16341
|
ctx.effect(() => {
|
|
16018
16342
|
const disposers = [
|
|
16019
16343
|
installFeishuStyles(),
|