openclaw-openagent 1.0.6 → 1.0.7
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/dist/src/app/remote-agent-tool.js +3 -2
- package/dist/src/compat.d.ts +2 -0
- package/dist/src/compat.js +9 -0
- package/dist/src/plugin-ui/assets/openagent-override.js +212 -62
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.js +63 -26
- package/dist/src/util/url-resolver.d.ts +8 -0
- package/dist/src/util/url-resolver.js +40 -0
- package/package.json +1 -1
- package/src/app/remote-agent-tool.ts +3 -2
- package/src/compat.ts +12 -0
- package/src/plugin-ui/assets/openagent-override.js +212 -62
- package/src/plugin-ui/build.cjs +1 -0
- package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +63 -31
- package/src/plugin-ui/modules/agent-book/panel/styles.js +49 -28
- package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +8 -2
- package/src/plugin-ui/ui-extension-loader/registry-regex.ts +70 -29
- package/src/util/url-resolver.ts +44 -0
|
@@ -29,7 +29,7 @@ import { sendC2CCustomMessage, waitForC2CReply } from '../tim/c2c.js';
|
|
|
29
29
|
import type { OasnTransport, TaskHandle, TaskRequest } from '../transport/types.js';
|
|
30
30
|
import fs from 'node:fs';
|
|
31
31
|
import nodePath from 'node:path';
|
|
32
|
-
import { resolveOasnAccessUrl } from '../util/url-resolver.js';
|
|
32
|
+
import { resolveOasnAccessUrl, resolveOasnWebuiUrl } from '../util/url-resolver.js';
|
|
33
33
|
|
|
34
34
|
// ── Helpers ──
|
|
35
35
|
|
|
@@ -350,7 +350,8 @@ export function createCallRemoteAgentTool(): ToolDescriptor {
|
|
|
350
350
|
const accessBase = rt.config?.accessApiBase || rt.config?.apiBase;
|
|
351
351
|
if (result.webuiContinuation?.launchUrl) {
|
|
352
352
|
const rawLink = result.webuiContinuation.launchUrl;
|
|
353
|
-
const
|
|
353
|
+
const claimUrl = rt.config?.claimUrl;
|
|
354
|
+
const link = resolveOasnWebuiUrl(accessBase, claimUrl, rawLink);
|
|
354
355
|
const label = result.webuiContinuation.displayName || 'Open WebUI';
|
|
355
356
|
content = content.trim()
|
|
356
357
|
? `${content}\n\n${label}: ${link}`
|
package/src/compat.ts
CHANGED
|
@@ -15,6 +15,9 @@ export const PLUGIN_VERSION = '1.0.0';
|
|
|
15
15
|
|
|
16
16
|
export const SUPPORTED_HOST_MIN = '2026.3.22';
|
|
17
17
|
|
|
18
|
+
/** Plugin has been verified up to this OpenClaw host version */
|
|
19
|
+
export const MAX_TESTED_VERSION = '2026.6.30';
|
|
20
|
+
|
|
18
21
|
export interface OpenClawVersion {
|
|
19
22
|
year: number;
|
|
20
23
|
month: number;
|
|
@@ -71,6 +74,15 @@ export function assertHostCompatibility(hostVersion: string | undefined): void {
|
|
|
71
74
|
}
|
|
72
75
|
if (isHostVersionSupported(hostVersion)) {
|
|
73
76
|
logger.info(`[compat] Host OpenClaw ${hostVersion} >= ${SUPPORTED_HOST_MIN}, OK.`);
|
|
77
|
+
// Warn but don't block for versions beyond tested range
|
|
78
|
+
const host = parseOpenClawVersion(hostVersion);
|
|
79
|
+
const max = parseOpenClawVersion(MAX_TESTED_VERSION);
|
|
80
|
+
if (host && max && compareVersions(host, max) > 0) {
|
|
81
|
+
logger.warn(
|
|
82
|
+
`[compat] Host OpenClaw ${hostVersion} is beyond max tested version ${MAX_TESTED_VERSION}. ` +
|
|
83
|
+
`The plugin may encounter compatibility issues.`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
74
86
|
return;
|
|
75
87
|
}
|
|
76
88
|
throw new Error(
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OpenAgent UI Override — Auto-generated, do not edit directly!
|
|
3
3
|
*
|
|
4
4
|
* Source: src/plugin-ui/modules/
|
|
5
|
-
* Built: 2026-06-
|
|
5
|
+
* Built: 2026-06-25T09:33:52.490Z
|
|
6
6
|
*
|
|
7
7
|
* To modify, edit the source modules and run:
|
|
8
8
|
* npm run build:override
|
|
@@ -1587,6 +1587,97 @@ function AgentCard(agent, opts = {}) {
|
|
|
1587
1587
|
return card;
|
|
1588
1588
|
}
|
|
1589
1589
|
|
|
1590
|
+
// ════════════════════════════════════════════════════════════════
|
|
1591
|
+
// MODULE: agent-book/panel/theme-adapter.js
|
|
1592
|
+
// ════════════════════════════════════════════════════════════════
|
|
1593
|
+
|
|
1594
|
+
// ── OpenAgent Theme Adapter ──
|
|
1595
|
+
//
|
|
1596
|
+
// 运行时检测 OpenClaw Control UI 主题系统(v4.x vscode tokens / v5.x glassmorphism),
|
|
1597
|
+
// 注入 --oa-* CSS 变量供 styles.js 中的 var() fallback 消费。
|
|
1598
|
+
//
|
|
1599
|
+
// 必须早于 styles.js 加载(由 build.cjs MANIFEST 顺序保证)。
|
|
1600
|
+
|
|
1601
|
+
(function _clInitThemeAdapter() {
|
|
1602
|
+
if (window.__openagentThemeAdapterInstalled) return;
|
|
1603
|
+
window.__openagentThemeAdapterInstalled = true;
|
|
1604
|
+
|
|
1605
|
+
/**
|
|
1606
|
+
* 检测当前 OC 主题系统类型。
|
|
1607
|
+
* @returns {{ hasGlass: boolean, hasVscode: boolean, isDark: boolean }}
|
|
1608
|
+
*/
|
|
1609
|
+
function detectThemeSystem() {
|
|
1610
|
+
var cs = getComputedStyle(document.documentElement);
|
|
1611
|
+
var hasGlass = !!(cs.getPropertyValue('--glass-surface').trim());
|
|
1612
|
+
var hasKn = !!(cs.getPropertyValue('--kn-bg-primary').trim());
|
|
1613
|
+
var hasVscode = !!(cs.getPropertyValue('--vscode-editor-background').trim());
|
|
1614
|
+
|
|
1615
|
+
// 多策略暗色检测:v4 data-theme-mode / v5 data-theme / prefers-color-scheme
|
|
1616
|
+
var themeMode = document.documentElement.getAttribute('data-theme-mode') // v4.x
|
|
1617
|
+
|| document.documentElement.getAttribute('data-theme') // v5.x
|
|
1618
|
+
|| '';
|
|
1619
|
+
var isDark = themeMode.indexOf('dark') >= 0
|
|
1620
|
+
|| window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
1621
|
+
|
|
1622
|
+
return {
|
|
1623
|
+
hasGlass: hasGlass || hasKn,
|
|
1624
|
+
hasVscode: hasVscode,
|
|
1625
|
+
isDark: isDark
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/**
|
|
1630
|
+
* 注入 --oa-* CSS 变量到 :root。
|
|
1631
|
+
* styles.js 中的 var(--oa-xxx, fallback) 会优先取这些值。
|
|
1632
|
+
*/
|
|
1633
|
+
function setupThemeVariables() {
|
|
1634
|
+
var system = detectThemeSystem();
|
|
1635
|
+
var root = document.documentElement;
|
|
1636
|
+
|
|
1637
|
+
if (system.hasGlass) {
|
|
1638
|
+
// v5.x Glassmorphism: 使用 --glass-* / --kn-* token 族
|
|
1639
|
+
root.style.setProperty('--oa-panel-bg', 'var(--glass-surface, var(--card))');
|
|
1640
|
+
root.style.setProperty('--oa-panel-text', 'var(--kn-text-primary, var(--text, #e0e0e0))');
|
|
1641
|
+
root.style.setProperty('--oa-card-bg', 'var(--glass-card, var(--card, #f5f5f5))');
|
|
1642
|
+
root.style.setProperty('--oa-card-hover-bg', 'var(--glass-card-hover, var(--card-hover))');
|
|
1643
|
+
root.style.setProperty('--oa-border', 'var(--glass-border, var(--border, #e5e5e5))');
|
|
1644
|
+
root.style.setProperty('--oa-shadow-color', 'rgba(0, 0, 0, 0.08)');
|
|
1645
|
+
root.style.setProperty('--oa-divider', 'var(--glass-divider, var(--border, #eee))');
|
|
1646
|
+
root.style.setProperty('--oa-muted-text', 'var(--kn-text-secondary, var(--text-secondary, #999))');
|
|
1647
|
+
} else if (system.hasVscode) {
|
|
1648
|
+
// v4.x VSCode tokens: 使用 --vscode-* 变量
|
|
1649
|
+
root.style.setProperty('--oa-panel-bg', 'var(--vscode-editor-background, #fff)');
|
|
1650
|
+
root.style.setProperty('--oa-panel-text', 'var(--vscode-editor-foreground, #2F2F33)');
|
|
1651
|
+
root.style.setProperty('--oa-card-bg', 'var(--vscode-sideBar-background, #f5f5f5)');
|
|
1652
|
+
root.style.setProperty('--oa-card-hover-bg', 'var(--vscode-list-hoverBackground, #f0f0f0)');
|
|
1653
|
+
root.style.setProperty('--oa-border', 'var(--vscode-panel-border, #e5e5e5)');
|
|
1654
|
+
root.style.setProperty('--oa-shadow-color', 'rgba(0, 0, 0, 0.10)');
|
|
1655
|
+
root.style.setProperty('--oa-divider', 'var(--vscode-sideBar-border, #eee)');
|
|
1656
|
+
root.style.setProperty('--oa-muted-text', 'var(--vscode-descriptionForeground, #999)');
|
|
1657
|
+
}
|
|
1658
|
+
// 无特征 → 不注入变量,styles.js 中的 var() 会 fallback 到硬编码值
|
|
1659
|
+
|
|
1660
|
+
// 注入暗色 class 标记供 styles.js 选择器使用
|
|
1661
|
+
if (system.isDark) {
|
|
1662
|
+
root.style.setProperty('--oa-is-dark', '1');
|
|
1663
|
+
} else {
|
|
1664
|
+
root.style.setProperty('--oa-is-dark', '0');
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// 立即执行 + 监听主题变更(v5.x 可能运行时切换主题)
|
|
1669
|
+
setupThemeVariables();
|
|
1670
|
+
|
|
1671
|
+
// 监听 data-theme / data-theme-mode 属性变更
|
|
1672
|
+
var themeObserver = new MutationObserver(function () {
|
|
1673
|
+
setupThemeVariables();
|
|
1674
|
+
});
|
|
1675
|
+
themeObserver.observe(document.documentElement, {
|
|
1676
|
+
attributes: true,
|
|
1677
|
+
attributeFilter: ['data-theme', 'data-theme-mode', 'class']
|
|
1678
|
+
});
|
|
1679
|
+
})();
|
|
1680
|
+
|
|
1590
1681
|
// ════════════════════════════════════════════════════════════════
|
|
1591
1682
|
// MODULE: agent-book/panel/styles.js
|
|
1592
1683
|
// ════════════════════════════════════════════════════════════════
|
|
@@ -1704,10 +1795,10 @@ function injectStyles() {
|
|
|
1704
1795
|
.openagent-agent-panel {
|
|
1705
1796
|
position: fixed;
|
|
1706
1797
|
max-height: none;
|
|
1707
|
-
background: #fff;
|
|
1708
|
-
border: 1.2px solid rgba(47, 47, 51, 0.12);
|
|
1798
|
+
background: var(--oa-panel-bg, #fff);
|
|
1799
|
+
border: 1.2px solid var(--oa-border, rgba(47, 47, 51, 0.12));
|
|
1709
1800
|
border-radius: 18px;
|
|
1710
|
-
box-shadow: 0 -4px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04);
|
|
1801
|
+
box-shadow: 0 -4px 24px var(--oa-shadow-color, rgba(0,0,0,0.08)), 0 2px 8px var(--oa-shadow-color, rgba(0,0,0,0.04));
|
|
1711
1802
|
z-index: 99999;
|
|
1712
1803
|
overflow: hidden;
|
|
1713
1804
|
display: none;
|
|
@@ -1751,7 +1842,7 @@ function injectStyles() {
|
|
|
1751
1842
|
flex-shrink: 0;
|
|
1752
1843
|
}
|
|
1753
1844
|
.openagent-panel-close {
|
|
1754
|
-
background: rgba(249, 249, 249, 1);
|
|
1845
|
+
background: var(--oa-card-bg, rgba(249, 249, 249, 1));
|
|
1755
1846
|
border: none;
|
|
1756
1847
|
color: rgba(47, 47, 51, 0.6);
|
|
1757
1848
|
cursor: pointer;
|
|
@@ -2211,41 +2302,61 @@ function injectStyles() {
|
|
|
2211
2302
|
padding: 32px; text-align: center; color: #aaa; font-size: 13px;
|
|
2212
2303
|
}
|
|
2213
2304
|
|
|
2214
|
-
/* ──
|
|
2215
|
-
[data-theme-mode="dark"] .openagent-agent-panel
|
|
2305
|
+
/* ── 深色主题(v4: data-theme-mode / v5: data-theme) ── */
|
|
2306
|
+
[data-theme-mode="dark"] .openagent-agent-panel,
|
|
2307
|
+
[data-theme="dark"] .openagent-agent-panel {
|
|
2216
2308
|
background: #1c1c1c; border-color: rgba(255,255,255,0.12);
|
|
2217
2309
|
box-shadow: 0 -4px 24px rgba(0,0,0,0.3);
|
|
2218
2310
|
}
|
|
2219
|
-
[data-theme-mode="dark"] .openagent-panel-head
|
|
2220
|
-
[data-theme
|
|
2221
|
-
[data-theme-mode="dark"] .openagent-panel-
|
|
2222
|
-
[data-theme
|
|
2223
|
-
[data-theme-mode="dark"] .openagent-
|
|
2224
|
-
[data-theme
|
|
2225
|
-
[data-theme-mode="dark"] .openagent-panel-
|
|
2226
|
-
|
|
2227
|
-
[data-theme-mode="dark"] .
|
|
2228
|
-
[data-theme
|
|
2229
|
-
[data-theme-mode="dark"] .
|
|
2230
|
-
|
|
2231
|
-
[data-theme-mode="dark"] .
|
|
2232
|
-
[data-theme
|
|
2233
|
-
|
|
2311
|
+
[data-theme-mode="dark"] .openagent-panel-head,
|
|
2312
|
+
[data-theme="dark"] .openagent-panel-head { border-color: #2a2a2a; }
|
|
2313
|
+
[data-theme-mode="dark"] .openagent-panel-title,
|
|
2314
|
+
[data-theme="dark"] .openagent-panel-title { color: rgba(224, 224, 224, 0.8); }
|
|
2315
|
+
[data-theme-mode="dark"] .openagent-panel-close,
|
|
2316
|
+
[data-theme="dark"] .openagent-panel-close { background: #2a2a2a; color: #888; }
|
|
2317
|
+
[data-theme-mode="dark"] .openagent-panel-close:hover,
|
|
2318
|
+
[data-theme="dark"] .openagent-panel-close:hover { background: #333; color: #ccc; }
|
|
2319
|
+
[data-theme-mode="dark"] .openagent-section-heading,
|
|
2320
|
+
[data-theme="dark"] .openagent-section-heading { color: rgba(224, 224, 224, 0.45); }
|
|
2321
|
+
[data-theme-mode="dark"] .openagent-parent-heading,
|
|
2322
|
+
[data-theme="dark"] .openagent-parent-heading { color: rgba(224, 224, 224, 0.3); }
|
|
2323
|
+
[data-theme-mode="dark"] .openagent-panel-divider,
|
|
2324
|
+
[data-theme="dark"] .openagent-panel-divider { border-color: rgba(255,255,255,0.1); }
|
|
2325
|
+
|
|
2326
|
+
[data-theme-mode="dark"] .agent-card__name,
|
|
2327
|
+
[data-theme="dark"] .agent-card__name { color: #e0e0e0; }
|
|
2328
|
+
[data-theme-mode="dark"] .agent-card__bio,
|
|
2329
|
+
[data-theme="dark"] .agent-card__bio { color: rgba(224, 224, 224, 0.7); }
|
|
2330
|
+
[data-theme-mode="dark"] .agent-card__stat,
|
|
2331
|
+
[data-theme="dark"] .agent-card__stat { color: #64748b; }
|
|
2332
|
+
|
|
2333
|
+
[data-theme-mode="dark"] .agent-card--mention,
|
|
2334
|
+
[data-theme="dark"] .agent-card--mention { background: transparent; }
|
|
2335
|
+
[data-theme-mode="dark"] .agent-card--mention:hover,
|
|
2336
|
+
[data-theme="dark"] .agent-card--mention:hover { background: #252525; }
|
|
2337
|
+
[data-theme-mode="dark"] .agent-card--mention .agent-card__action,
|
|
2338
|
+
[data-theme="dark"] .agent-card--mention .agent-card__action {
|
|
2234
2339
|
background: #333; border-color: #555; color: #ddd;
|
|
2235
2340
|
}
|
|
2236
|
-
[data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover
|
|
2237
|
-
[data-theme
|
|
2238
|
-
[data-theme-mode="dark"] .agent-card--search
|
|
2341
|
+
[data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover,
|
|
2342
|
+
[data-theme="dark"] .agent-card--mention .agent-card__action:hover { background: #444; }
|
|
2343
|
+
[data-theme-mode="dark"] .agent-card--search:hover,
|
|
2344
|
+
[data-theme="dark"] .agent-card--search:hover { background: #252525; }
|
|
2345
|
+
[data-theme-mode="dark"] .agent-card--search.openagent-card-selected,
|
|
2346
|
+
[data-theme="dark"] .agent-card--search.openagent-card-selected {
|
|
2239
2347
|
background: rgba(99, 102, 241, 0.12);
|
|
2240
2348
|
outline-color: rgba(99, 102, 241, 0.4);
|
|
2241
2349
|
}
|
|
2242
|
-
[data-theme-mode="dark"] .agent-card--search .agent-card__action
|
|
2350
|
+
[data-theme-mode="dark"] .agent-card--search .agent-card__action,
|
|
2351
|
+
[data-theme="dark"] .agent-card--search .agent-card__action {
|
|
2243
2352
|
background: #333; border-color: #555; color: #ddd;
|
|
2244
2353
|
}
|
|
2245
|
-
[data-theme-mode="dark"] .agent-card--search .agent-card__action:hover
|
|
2354
|
+
[data-theme-mode="dark"] .agent-card--search .agent-card__action:hover,
|
|
2355
|
+
[data-theme="dark"] .agent-card--search .agent-card__action:hover { background: #444; }
|
|
2246
2356
|
|
|
2247
2357
|
|
|
2248
|
-
[data-theme-mode="dark"] .agent-card--inline
|
|
2358
|
+
[data-theme-mode="dark"] .agent-card--inline,
|
|
2359
|
+
[data-theme="dark"] .agent-card--inline {
|
|
2249
2360
|
background: linear-gradient(135deg, #1e1b3a 0%, #1a1a2e 100%);
|
|
2250
2361
|
border-color: #2d2b55;
|
|
2251
2362
|
}
|
|
@@ -2255,7 +2366,8 @@ function injectStyles() {
|
|
|
2255
2366
|
}
|
|
2256
2367
|
[data-theme-mode="dark"] .agent-card--inline .agent-card__name { color: #e2e8f0; }
|
|
2257
2368
|
[data-theme-mode="dark"] .agent-card--inline .agent-card__avatar { border-color: #2d2b55; }
|
|
2258
|
-
[data-theme-mode="dark"] .agent-card__loading
|
|
2369
|
+
[data-theme-mode="dark"] .agent-card__loading,
|
|
2370
|
+
[data-theme="dark"] .agent-card__loading {
|
|
2259
2371
|
background: #1e1b3a; border-color: #2d2b55; color: #64748b;
|
|
2260
2372
|
}
|
|
2261
2373
|
`;
|
|
@@ -2890,8 +3002,8 @@ liveTextarea.dispatchEvent(new Event('change', { bubbles: true }));
|
|
|
2890
3002
|
//
|
|
2891
3003
|
// tryInject() 只做 DOM 注入(@ 按钮 + CSS class),不绑定任何事件。
|
|
2892
3004
|
|
|
2893
|
-
// ── textarea
|
|
2894
|
-
//
|
|
3005
|
+
// ── textarea 选择器(多版本兼容)──
|
|
3006
|
+
// v3 "Message..." / v4 "发消息..." / v5.x 结构化选择器
|
|
2895
3007
|
const _CL_TEXTAREA_SELECTOR = [
|
|
2896
3008
|
'textarea[placeholder*="发消息"]',
|
|
2897
3009
|
'textarea[placeholder*="Message"]',
|
|
@@ -2900,6 +3012,8 @@ const _CL_TEXTAREA_SELECTOR = [
|
|
|
2900
3012
|
'.chat-composer textarea',
|
|
2901
3013
|
'.chat-input textarea',
|
|
2902
3014
|
'[class*="composer"] textarea:not([placeholder*="Search"])',
|
|
3015
|
+
'[class*="chat-input"] textarea',
|
|
3016
|
+
'[data-component="chat-input"] textarea',
|
|
2903
3017
|
].join(', ');
|
|
2904
3018
|
|
|
2905
3019
|
function _getTextarea() {
|
|
@@ -3015,37 +3129,59 @@ function _installSendClickInterceptor() {
|
|
|
3015
3129
|
}, true);
|
|
3016
3130
|
}
|
|
3017
3131
|
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
const textareaParent = textarea.parentElement;
|
|
3027
|
-
let toolbarRow = null;
|
|
3132
|
+
/**
|
|
3133
|
+
* 多策略定位 textarea 所在输入区域的工具栏行。
|
|
3134
|
+
* v4.x: 工具栏在 textarea.parentElement.children 中
|
|
3135
|
+
* v5.x: 工具栏可能嵌套在祖父容器或包含 toolbar class
|
|
3136
|
+
* 返回 null 表示未找到则回退到 textarea 父级插入
|
|
3137
|
+
*/
|
|
3138
|
+
function _findToolbarRow(textarea) {
|
|
3139
|
+
var parent = textarea.parentElement;
|
|
3028
3140
|
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
break;
|
|
3034
|
-
}
|
|
3141
|
+
// 策略 1(v4.x): textarea 兄弟元素含 SVG 图标
|
|
3142
|
+
if (parent) {
|
|
3143
|
+
for (var i = 0; i < parent.children.length; i++) {
|
|
3144
|
+
if (parent.children[i] !== textarea && parent.children[i].querySelector('svg')) return parent.children[i];
|
|
3035
3145
|
}
|
|
3036
3146
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3147
|
+
|
|
3148
|
+
// 策略 2(v4.x): 祖父级兄弟元素含 SVG 图标
|
|
3149
|
+
if (parent) {
|
|
3150
|
+
var grandparent = parent.parentElement;
|
|
3039
3151
|
if (grandparent) {
|
|
3040
|
-
for (
|
|
3041
|
-
if (
|
|
3042
|
-
toolbarRow = child;
|
|
3043
|
-
break;
|
|
3044
|
-
}
|
|
3152
|
+
for (var j = 0; j < grandparent.children.length; j++) {
|
|
3153
|
+
if (grandparent.children[j] !== parent && grandparent.children[j].querySelector('svg')) return grandparent.children[j];
|
|
3045
3154
|
}
|
|
3046
3155
|
}
|
|
3047
3156
|
}
|
|
3048
3157
|
|
|
3158
|
+
// 策略 3(v5.x): 搜索包含 toolbar class 的元素
|
|
3159
|
+
var chatContainer = textarea.closest('[class*="chat"]');
|
|
3160
|
+
if (chatContainer) {
|
|
3161
|
+
var toolbarEl = chatContainer.querySelector('[class*="toolbar"]');
|
|
3162
|
+
if (toolbarEl) return toolbarEl;
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
// 策略 4(v5.x fallback): 搜索 voice/mic 按钮定位其容器
|
|
3166
|
+
var voiceBtn = document.querySelector(
|
|
3167
|
+
'button[aria-label*="voice"], button[aria-label*="语音"], ' +
|
|
3168
|
+
'button[aria-label*="mic"], button[aria-label*="麦克风"]'
|
|
3169
|
+
);
|
|
3170
|
+
if (voiceBtn) return voiceBtn.parentElement;
|
|
3171
|
+
|
|
3172
|
+
return null;
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
function tryInject() {
|
|
3176
|
+
const textarea = _getTextarea();
|
|
3177
|
+
|
|
3178
|
+
if (!textarea) return false;
|
|
3179
|
+
_installSendClickInterceptor();
|
|
3180
|
+
if (document.querySelector('.openagent-at-btn')) return true; // 已注入
|
|
3181
|
+
|
|
3182
|
+
// ── 定位插入点:麦克风图标所在的 toolbar 行(多策略)──
|
|
3183
|
+
var toolbarRow = _findToolbarRow(textarea);
|
|
3184
|
+
|
|
3049
3185
|
// ── 创建 Agent Book 按钮 ──
|
|
3050
3186
|
const atBtn = document.createElement('button');
|
|
3051
3187
|
atBtn.className = 'openagent-at-btn';
|
|
@@ -3079,10 +3215,15 @@ function tryInject() {
|
|
|
3079
3215
|
if (toolbarRow) {
|
|
3080
3216
|
const toolbarLeft = toolbarRow.querySelector('[class*="toolbar-left"]') || toolbarRow;
|
|
3081
3217
|
const iconButtons = toolbarLeft.querySelectorAll('button, [role="button"]');
|
|
3218
|
+
// 优先按 aria-label 匹配 voice/mic 按钮,回退到位置假设
|
|
3082
3219
|
let micBtn = null;
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3220
|
+
for (var i = 0; i < iconButtons.length; i++) {
|
|
3221
|
+
var label = (iconButtons[i].getAttribute('aria-label') || '').toLowerCase();
|
|
3222
|
+
if (/voice|mic|语音|麦克风/.test(label)) { micBtn = iconButtons[i]; break; }
|
|
3223
|
+
}
|
|
3224
|
+
if (!micBtn && iconButtons.length >= 2) {
|
|
3225
|
+
micBtn = iconButtons[1]; // fallback: v4.x 中 mic 一般是第 2 个按钮
|
|
3226
|
+
} else if (!micBtn && iconButtons.length === 1) {
|
|
3086
3227
|
micBtn = iconButtons[0];
|
|
3087
3228
|
}
|
|
3088
3229
|
|
|
@@ -3092,16 +3233,19 @@ function tryInject() {
|
|
|
3092
3233
|
toolbarLeft.appendChild(atBtn);
|
|
3093
3234
|
}
|
|
3094
3235
|
} else {
|
|
3095
|
-
|
|
3236
|
+
// 无工具栏时回退到 textarea 的父级插入
|
|
3237
|
+
var fallbackParent = textarea.parentElement;
|
|
3238
|
+
if (fallbackParent) fallbackParent.appendChild(atBtn);
|
|
3096
3239
|
}
|
|
3097
3240
|
|
|
3098
3241
|
// ── 给输入区域加 position: relative 包裹层 ──
|
|
3242
|
+
var inputParent = textarea.parentElement;
|
|
3099
3243
|
const inputContainer = textarea.closest('.agent-chat__composer-combobox')
|
|
3100
3244
|
|| textarea.closest('.chat-input')
|
|
3101
3245
|
|| textarea.closest('[class*="chat-composer"]')
|
|
3102
3246
|
|| textarea.closest('[class*="chat"]')
|
|
3103
|
-
||
|
|
3104
|
-
||
|
|
3247
|
+
|| (inputParent ? inputParent.parentElement : null)
|
|
3248
|
+
|| inputParent;
|
|
3105
3249
|
if (inputContainer) {
|
|
3106
3250
|
inputContainer.classList.add('openagent-input-wrapper');
|
|
3107
3251
|
inputWrapperRef = inputContainer;
|
|
@@ -5418,14 +5562,20 @@ function _clScan() {
|
|
|
5418
5562
|
var SHELL_SELECTORS = [
|
|
5419
5563
|
'.chat-tool-card:not(:has(.cl-remote-agent-card))',
|
|
5420
5564
|
'.chat-tool-msg-collapse:not(:has(.cl-remote-agent-card))',
|
|
5421
|
-
'.chat-tools-collapse:not(:has(.cl-remote-agent-card))'
|
|
5565
|
+
'.chat-tools-collapse:not(:has(.cl-remote-agent-card))',
|
|
5566
|
+
// v5.x+ variants
|
|
5567
|
+
'.tool-card:not(:has(.cl-remote-agent-card))',
|
|
5568
|
+
'.chat-tool-card__wrapper:not(:has(.cl-remote-agent-card))'
|
|
5422
5569
|
];
|
|
5423
5570
|
|
|
5424
5571
|
var SUMMARY_SELECTORS = [
|
|
5425
5572
|
'.chat-tool-card__header',
|
|
5426
5573
|
'.chat-tool-card__title',
|
|
5427
5574
|
'.chat-tool-msg-summary:not(.cl-remote-agent-host-shell *)',
|
|
5428
|
-
'.chat-tools-summary:not(.cl-remote-agent-host-shell *)'
|
|
5575
|
+
'.chat-tools-summary:not(.cl-remote-agent-host-shell *)',
|
|
5576
|
+
// v5.x+ variants
|
|
5577
|
+
'.tool-card__header',
|
|
5578
|
+
'.tool-card__title'
|
|
5429
5579
|
];
|
|
5430
5580
|
|
|
5431
5581
|
var INDENT_SELECTORS = [
|
package/src/plugin-ui/build.cjs
CHANGED
|
@@ -25,6 +25,7 @@ const MANIFEST = [
|
|
|
25
25
|
// ── agent-book/panel ──
|
|
26
26
|
'agent-book/panel/agent-data.js',
|
|
27
27
|
'agent-book/panel/agent-card.js',
|
|
28
|
+
'agent-book/panel/theme-adapter.js', // 必须在 styles.js 之前,设置 --oa-* CSS 变量
|
|
28
29
|
'agent-book/panel/styles.js',
|
|
29
30
|
'agent-book/panel/mention-state.js',
|
|
30
31
|
'agent-book/panel/agent-book.js',
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
//
|
|
10
10
|
// tryInject() 只做 DOM 注入(@ 按钮 + CSS class),不绑定任何事件。
|
|
11
11
|
|
|
12
|
-
// ── textarea
|
|
13
|
-
//
|
|
12
|
+
// ── textarea 选择器(多版本兼容)──
|
|
13
|
+
// v3 "Message..." / v4 "发消息..." / v5.x 结构化选择器
|
|
14
14
|
const _CL_TEXTAREA_SELECTOR = [
|
|
15
15
|
'textarea[placeholder*="发消息"]',
|
|
16
16
|
'textarea[placeholder*="Message"]',
|
|
@@ -19,6 +19,8 @@ const _CL_TEXTAREA_SELECTOR = [
|
|
|
19
19
|
'.chat-composer textarea',
|
|
20
20
|
'.chat-input textarea',
|
|
21
21
|
'[class*="composer"] textarea:not([placeholder*="Search"])',
|
|
22
|
+
'[class*="chat-input"] textarea',
|
|
23
|
+
'[data-component="chat-input"] textarea',
|
|
22
24
|
].join(', ');
|
|
23
25
|
|
|
24
26
|
function _getTextarea() {
|
|
@@ -134,37 +136,59 @@ function _installSendClickInterceptor() {
|
|
|
134
136
|
}, true);
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const textareaParent = textarea.parentElement;
|
|
146
|
-
let toolbarRow = null;
|
|
139
|
+
/**
|
|
140
|
+
* 多策略定位 textarea 所在输入区域的工具栏行。
|
|
141
|
+
* v4.x: 工具栏在 textarea.parentElement.children 中
|
|
142
|
+
* v5.x: 工具栏可能嵌套在祖父容器或包含 toolbar class
|
|
143
|
+
* 返回 null 表示未找到则回退到 textarea 父级插入
|
|
144
|
+
*/
|
|
145
|
+
function _findToolbarRow(textarea) {
|
|
146
|
+
var parent = textarea.parentElement;
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
148
|
+
// 策略 1(v4.x): textarea 兄弟元素含 SVG 图标
|
|
149
|
+
if (parent) {
|
|
150
|
+
for (var i = 0; i < parent.children.length; i++) {
|
|
151
|
+
if (parent.children[i] !== textarea && parent.children[i].querySelector('svg')) return parent.children[i];
|
|
154
152
|
}
|
|
155
153
|
}
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
|
|
155
|
+
// 策略 2(v4.x): 祖父级兄弟元素含 SVG 图标
|
|
156
|
+
if (parent) {
|
|
157
|
+
var grandparent = parent.parentElement;
|
|
158
158
|
if (grandparent) {
|
|
159
|
-
for (
|
|
160
|
-
if (
|
|
161
|
-
toolbarRow = child;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
159
|
+
for (var j = 0; j < grandparent.children.length; j++) {
|
|
160
|
+
if (grandparent.children[j] !== parent && grandparent.children[j].querySelector('svg')) return grandparent.children[j];
|
|
164
161
|
}
|
|
165
162
|
}
|
|
166
163
|
}
|
|
167
164
|
|
|
165
|
+
// 策略 3(v5.x): 搜索包含 toolbar class 的元素
|
|
166
|
+
var chatContainer = textarea.closest('[class*="chat"]');
|
|
167
|
+
if (chatContainer) {
|
|
168
|
+
var toolbarEl = chatContainer.querySelector('[class*="toolbar"]');
|
|
169
|
+
if (toolbarEl) return toolbarEl;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 策略 4(v5.x fallback): 搜索 voice/mic 按钮定位其容器
|
|
173
|
+
var voiceBtn = document.querySelector(
|
|
174
|
+
'button[aria-label*="voice"], button[aria-label*="语音"], ' +
|
|
175
|
+
'button[aria-label*="mic"], button[aria-label*="麦克风"]'
|
|
176
|
+
);
|
|
177
|
+
if (voiceBtn) return voiceBtn.parentElement;
|
|
178
|
+
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function tryInject() {
|
|
183
|
+
const textarea = _getTextarea();
|
|
184
|
+
|
|
185
|
+
if (!textarea) return false;
|
|
186
|
+
_installSendClickInterceptor();
|
|
187
|
+
if (document.querySelector('.openagent-at-btn')) return true; // 已注入
|
|
188
|
+
|
|
189
|
+
// ── 定位插入点:麦克风图标所在的 toolbar 行(多策略)──
|
|
190
|
+
var toolbarRow = _findToolbarRow(textarea);
|
|
191
|
+
|
|
168
192
|
// ── 创建 Agent Book 按钮 ──
|
|
169
193
|
const atBtn = document.createElement('button');
|
|
170
194
|
atBtn.className = 'openagent-at-btn';
|
|
@@ -198,10 +222,15 @@ function tryInject() {
|
|
|
198
222
|
if (toolbarRow) {
|
|
199
223
|
const toolbarLeft = toolbarRow.querySelector('[class*="toolbar-left"]') || toolbarRow;
|
|
200
224
|
const iconButtons = toolbarLeft.querySelectorAll('button, [role="button"]');
|
|
225
|
+
// 优先按 aria-label 匹配 voice/mic 按钮,回退到位置假设
|
|
201
226
|
let micBtn = null;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
227
|
+
for (var i = 0; i < iconButtons.length; i++) {
|
|
228
|
+
var label = (iconButtons[i].getAttribute('aria-label') || '').toLowerCase();
|
|
229
|
+
if (/voice|mic|语音|麦克风/.test(label)) { micBtn = iconButtons[i]; break; }
|
|
230
|
+
}
|
|
231
|
+
if (!micBtn && iconButtons.length >= 2) {
|
|
232
|
+
micBtn = iconButtons[1]; // fallback: v4.x 中 mic 一般是第 2 个按钮
|
|
233
|
+
} else if (!micBtn && iconButtons.length === 1) {
|
|
205
234
|
micBtn = iconButtons[0];
|
|
206
235
|
}
|
|
207
236
|
|
|
@@ -211,16 +240,19 @@ function tryInject() {
|
|
|
211
240
|
toolbarLeft.appendChild(atBtn);
|
|
212
241
|
}
|
|
213
242
|
} else {
|
|
214
|
-
|
|
243
|
+
// 无工具栏时回退到 textarea 的父级插入
|
|
244
|
+
var fallbackParent = textarea.parentElement;
|
|
245
|
+
if (fallbackParent) fallbackParent.appendChild(atBtn);
|
|
215
246
|
}
|
|
216
247
|
|
|
217
248
|
// ── 给输入区域加 position: relative 包裹层 ──
|
|
249
|
+
var inputParent = textarea.parentElement;
|
|
218
250
|
const inputContainer = textarea.closest('.agent-chat__composer-combobox')
|
|
219
251
|
|| textarea.closest('.chat-input')
|
|
220
252
|
|| textarea.closest('[class*="chat-composer"]')
|
|
221
253
|
|| textarea.closest('[class*="chat"]')
|
|
222
|
-
||
|
|
223
|
-
||
|
|
254
|
+
|| (inputParent ? inputParent.parentElement : null)
|
|
255
|
+
|| inputParent;
|
|
224
256
|
if (inputContainer) {
|
|
225
257
|
inputContainer.classList.add('openagent-input-wrapper');
|
|
226
258
|
inputWrapperRef = inputContainer;
|