@opentiny/next-sdk 0.4.2 → 0.4.3
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/{SimulatorMask-BHVXyogh-CARX3Rff.js → SimulatorMask-BHVXyogh-CKmEDsYG.js} +1 -1
- package/dist/{index-R_HIbfUX.js → index-CHMa2zG4.js} +1065 -1042
- package/dist/index.js +1 -1
- package/dist/page-tools/a11y/config.d.ts +6 -3
- package/dist/page-tools/a11y/constants.d.ts +20 -0
- package/dist/page-tools/a11y/utils.d.ts +14 -0
- package/dist/page-tools/constants.d.ts +4 -2
- package/dist/page-tools/context.d.ts +2 -0
- package/dist/page-tools/handlers/hover.d.ts +9 -0
- package/dist/page-tools/schema.d.ts +3 -3
- package/dist/page-tools/utils/dom.d.ts +8 -2
- package/dist/runtime.js +19 -172
- package/package.json +6 -1
- package/WebMcpClient.ts +0 -505
- package/WebMcpServer.ts +0 -447
- package/agent/AgentModelProvider.ts +0 -932
- package/agent/type.ts +0 -91
- package/agent/utils/generateReActPrompt.ts +0 -71
- package/agent/utils/getAISDKTools.ts +0 -34
- package/agent/utils/getBuiltinMcpTools.ts +0 -86
- package/agent/utils/parseReActAction.ts +0 -34
- package/core.ts +0 -26
- package/index.ts +0 -91
- package/page-tools/a11y/build.ts +0 -74
- package/page-tools/a11y/config.ts +0 -465
- package/page-tools/a11y/constants.ts +0 -131
- package/page-tools/a11y/search.ts +0 -127
- package/page-tools/a11y/types.ts +0 -105
- package/page-tools/a11y/utils.ts +0 -239
- package/page-tools/a11y/vnode.ts +0 -439
- package/page-tools/a11y-tree.ts +0 -14
- package/page-tools/bridge.ts +0 -722
- package/page-tools/configs/console-cloud.ts +0 -172
- package/page-tools/constants.ts +0 -12
- package/page-tools/context.ts +0 -50
- package/page-tools/effects.ts +0 -345
- package/page-tools/handlers/browserState.ts +0 -12
- package/page-tools/handlers/click.ts +0 -30
- package/page-tools/handlers/executeJavascript.ts +0 -22
- package/page-tools/handlers/fill.ts +0 -65
- package/page-tools/handlers/scroll.ts +0 -66
- package/page-tools/handlers/searchTree.ts +0 -27
- package/page-tools/handlers/select.ts +0 -39
- package/page-tools/initialize-builtin-WebMCP.ts +0 -20
- package/page-tools/page-agent-highlight/index.ts +0 -245
- package/page-tools/page-agent-mask/SimulatorMask.module.css +0 -14
- package/page-tools/page-agent-mask/SimulatorMask.ts +0 -299
- package/page-tools/page-agent-mask/checkDarkMode.ts +0 -181
- package/page-tools/page-agent-mask/cursor-border.svg +0 -3
- package/page-tools/page-agent-mask/cursor-fill.svg +0 -5
- package/page-tools/page-agent-mask/cursor.module.css +0 -70
- package/page-tools/page-agent-mask/hauwei.svg +0 -25
- package/page-tools/page-agent-prompt.md +0 -139
- package/page-tools/page-agent-tool-event.ts +0 -113
- package/page-tools/page-agent-tool.ts +0 -207
- package/page-tools/page-state-cache.ts +0 -78
- package/page-tools/schema.ts +0 -52
- package/page-tools/tool-config.ts +0 -100
- package/page-tools/utils/dom.ts +0 -158
- package/page-tools/utils/scroll.ts +0 -58
- package/remoter/QrCode.ts +0 -54
- package/remoter/createRemoter.ts +0 -957
- package/remoter/svgs/chat.svg +0 -43
- package/remoter/svgs/icon-copy.svg +0 -25
- package/remoter/svgs/link.svg +0 -28
- package/remoter/svgs/logo.svg +0 -45
- package/remoter/svgs/qrcode.svg +0 -35
- package/remoter/svgs/scan.svg +0 -21
- package/remoter/tooltips.ts +0 -260
- package/runtime.html +0 -39
- package/runtime.ts +0 -44
- package/script/utils.ts +0 -26
- package/skills/index.ts +0 -287
- package/test/page-tools/a11y/build.test.ts +0 -638
- package/test/page-tools/a11y/config.test.ts +0 -370
- package/test/page-tools/configs/console-cloud.test.ts +0 -168
- package/test/page-tools/page-agent-highlight.test.ts +0 -110
- package/test/page-tools/page-agent-tool-dispatch.test.ts +0 -208
- package/test/page-tools/page-agent-tool.test.ts +0 -102
- package/test/page-tools/tool-config.test.ts +0 -112
- package/test/page-tools/utils/dom.test.ts +0 -122
- package/tsconfig.json +0 -16
- package/utils/builtinProxy.ts +0 -144
- package/utils/env.ts +0 -13
- package/utils/uuid.ts +0 -10
- package/vite.config.runtime.ts +0 -22
- package/vite.config.ts +0 -63
- package/vitest.config.ts +0 -10
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 云控制台(consoleCloud)专用 PageAgentToolOptions。
|
|
3
|
-
*
|
|
4
|
-
* 控制台基于 Tiny3(ti3-*)+ Angular,大量自定义组件缺少 role / aria-*:
|
|
5
|
-
* - Tab:`.ti3-tab-li` 无 role=tab,选中靠 `.ti3-tab-active`
|
|
6
|
-
* - 下拉:`.ti3-select-dominator-container` / `.selected-label` 无 combobox 语义
|
|
7
|
-
* - 图标按钮:仅有 `cf-uba` / 子节点 `title`,无 aria-label
|
|
8
|
-
* - 服务列表侧栏、区域选择等用 class 表达选中态
|
|
9
|
-
*
|
|
10
|
-
* 书写约定:优先 selector;仅当需要计算样式等复杂判断时再用 match。
|
|
11
|
-
* (roles 的 selector 为自身 matches;states 的 selector 为 closest)
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { defineA11yConfig } from '../a11y/config'
|
|
15
|
-
import type { PageAgentToolOptions } from '../tool-config'
|
|
16
|
-
|
|
17
|
-
/** 云控制台 page-agent-tool 预设配置 */
|
|
18
|
-
export const consoleCloudPageAgentToolOptions: PageAgentToolOptions = {
|
|
19
|
-
enableHighlight: false,
|
|
20
|
-
a11yConfig: defineA11yConfig({
|
|
21
|
-
roles: [
|
|
22
|
-
// Tiny3 Tabs:真正可聚焦/可点击的是内部 .ti3-tabs-text(tabindex=0)
|
|
23
|
-
{ role: 'tablist', selector: 'ul.ti3-tabs' },
|
|
24
|
-
{ role: 'tab', selector: '.ti3-tabs-text' },
|
|
25
|
-
{ role: 'tabpanel', selector: ['ti-tab.ti3-tab-pane', '.ti3-tab-pane'] },
|
|
26
|
-
|
|
27
|
-
// 自定义下拉 / 区域选择触发器
|
|
28
|
-
{
|
|
29
|
-
role: 'combobox',
|
|
30
|
-
selector: [
|
|
31
|
-
'.ti3-select-dominator-container',
|
|
32
|
-
'.selected-label',
|
|
33
|
-
'.sort-select',
|
|
34
|
-
'.service-select',
|
|
35
|
-
'.region-select',
|
|
36
|
-
'[cf-uba="searchableRegion..open"]'
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
// 图标型 / 无语义按钮
|
|
41
|
-
{
|
|
42
|
-
role: 'button',
|
|
43
|
-
selector: [
|
|
44
|
-
'.modules-service-list-menu-service-icon-container',
|
|
45
|
-
'.ti3-btn-only-icon-noborder',
|
|
46
|
-
'.ti3-select-dominator-dropdown-btn',
|
|
47
|
-
'.modules-right-sidebar-icon-item-wrapper',
|
|
48
|
-
'.modules-right-sidebar-hide-state-plugin',
|
|
49
|
-
'.components-service-list-container-service-list-input-service-close',
|
|
50
|
-
'.modules-searchable-region-region-panel-close',
|
|
51
|
-
'.modules-searchable-region-region-panel-search-btn',
|
|
52
|
-
'[cf-uba="rightSidebar..hideBtn"]',
|
|
53
|
-
// Tiny3 图标组件:帮助中心固定/全屏/关闭等(ti-icon / tp-icon 无原生 button 语义)
|
|
54
|
-
'.ti-global-help-panel-header-icon',
|
|
55
|
-
'ti-icon[name]',
|
|
56
|
-
'tp-icon[name]',
|
|
57
|
-
// 无 name 的箭头/操作图标(如 latest news 区 common-icon 展开箭头)
|
|
58
|
-
'tp-icon.common-icon',
|
|
59
|
-
'ti-icon.common-icon',
|
|
60
|
-
'ti-icon.ti3-icon-full-screen',
|
|
61
|
-
'ti-icon.ti3-icon-close',
|
|
62
|
-
'tp-icon.ti3-icon-full-screen',
|
|
63
|
-
'tp-icon.ti3-icon-close'
|
|
64
|
-
]
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
// 服务列表侧栏分类 / 顶栏菜单项
|
|
68
|
-
{
|
|
69
|
-
role: 'menuitem',
|
|
70
|
-
selector: [
|
|
71
|
-
'.components-service-list-left-box-sidebar-visit-panel > li',
|
|
72
|
-
'.modules-menus-menu-item',
|
|
73
|
-
'.components-menu-wrapper-menu-text'
|
|
74
|
-
]
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
// 区域筛选项 / 下拉选项
|
|
78
|
-
{
|
|
79
|
-
role: 'option',
|
|
80
|
-
selector: [
|
|
81
|
-
'.region-selector-item',
|
|
82
|
-
'.modules-searchable-region-project-item-region-item',
|
|
83
|
-
'.modules-searchable-region-region-panel-recent-region-item',
|
|
84
|
-
'.ti3-multiselect-box-cell'
|
|
85
|
-
]
|
|
86
|
-
}
|
|
87
|
-
],
|
|
88
|
-
states: {
|
|
89
|
-
// 选中态:挂在容器 class 上,用 selector + closest 即可命中内部交互节点
|
|
90
|
-
selected: [
|
|
91
|
-
{ selector: 'li.ti3-tab-li.ti3-tab-active' },
|
|
92
|
-
// Tiny3 按钮组(计费模式等):ti3-active 在 .ti3-btn-item-container 上,不在 button 上
|
|
93
|
-
{ selector: '.ti3-btn-item-container.ti3-active' },
|
|
94
|
-
{ selector: '.components-service-list-left-box-active' },
|
|
95
|
-
{
|
|
96
|
-
selector: [
|
|
97
|
-
'.modules-searchable-region-project-item-region-item-selected',
|
|
98
|
-
'.components-search-content-search-select-search-select-selected'
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
],
|
|
102
|
-
hasPopup: [
|
|
103
|
-
{
|
|
104
|
-
selector: [
|
|
105
|
-
'.ti3-select-dominator-container',
|
|
106
|
-
'.selected-label',
|
|
107
|
-
'.sort-select',
|
|
108
|
-
'.service-select',
|
|
109
|
-
'.region-select',
|
|
110
|
-
'[cf-uba="searchableRegion..open"]',
|
|
111
|
-
'[cf-uba="serviceList..open"]',
|
|
112
|
-
'[cf-uba="userinfo"]'
|
|
113
|
-
]
|
|
114
|
-
}
|
|
115
|
-
],
|
|
116
|
-
disabled: [{ selector: '.ti3-disabled' }],
|
|
117
|
-
// 展开态:需结合计算样式,选择器表达不了「可见」
|
|
118
|
-
expanded: [
|
|
119
|
-
{
|
|
120
|
-
match: (el) => {
|
|
121
|
-
try {
|
|
122
|
-
if (
|
|
123
|
-
!el.matches(
|
|
124
|
-
'.modules-layout-module-sidebar-panel, .modules-searchable-region-region-panel, .ti3-dropdown-container'
|
|
125
|
-
)
|
|
126
|
-
) {
|
|
127
|
-
return false
|
|
128
|
-
}
|
|
129
|
-
const style = window.getComputedStyle(el as HTMLElement)
|
|
130
|
-
return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'
|
|
131
|
-
} catch {
|
|
132
|
-
return false
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
]
|
|
137
|
-
},
|
|
138
|
-
// exposedAttributes 输出属性 token;带 name 的 ti-icon/tp-icon 可交互性由 roles/whitelist 中 ti-icon[name] 等规则判定。
|
|
139
|
-
exposedAttributes: ['cf-uba', 'data-qa-id', 'name'],
|
|
140
|
-
whitelist: [
|
|
141
|
-
'.modules-service-list-menu-service-icon-container',
|
|
142
|
-
'.ti3-tabs-text',
|
|
143
|
-
'[cf-uba="cloudShell"]',
|
|
144
|
-
'[cf-uba="messageBox"]',
|
|
145
|
-
'[cf-uba="helpFeedback"]',
|
|
146
|
-
'[cf-uba="globalMessage"]',
|
|
147
|
-
'[cf-uba="rightSidebar..hideBtn"]',
|
|
148
|
-
// 帮助中心等区域的 ti-icon / tp-icon 图标按钮
|
|
149
|
-
'.ti-global-help-panel-header-icon',
|
|
150
|
-
'ti-icon[name]',
|
|
151
|
-
'tp-icon[name]',
|
|
152
|
-
'tp-icon.common-icon',
|
|
153
|
-
'ti-icon.common-icon',
|
|
154
|
-
'ti-icon.ti3-icon-full-screen'
|
|
155
|
-
],
|
|
156
|
-
blacklist: ['noscript', 'pan-gu', '#J_header'],
|
|
157
|
-
dialogSelectors: [
|
|
158
|
-
'[class*="ti3-modal"]',
|
|
159
|
-
'[class*="ti3-message-box"]',
|
|
160
|
-
'[class*="drawer"]',
|
|
161
|
-
'.modules-layout-module-sidebar-panel'
|
|
162
|
-
]
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** 判断当前页面是否应使用云控制台(consoleCloud)预设 */
|
|
167
|
-
export function isConsoleCloudHost(
|
|
168
|
-
hostname: string = typeof location !== 'undefined' ? location.hostname : ''
|
|
169
|
-
): boolean {
|
|
170
|
-
// 要求 console 前为域名边界(开头或 `.`),避免误匹配 xconsole.huaweicloud.com
|
|
171
|
-
return /(^|\.)console\.huaweicloud\.com$/i.test(hostname)
|
|
172
|
-
}
|
package/page-tools/constants.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/** 校验错误默认选择器:ARIA 标准 + 主流 UI 框架(唯一来源为 a11y/constants.ts,此处重新导出保持旧路径可用) */
|
|
2
|
-
export { DEFAULT_ERROR_SELECTORS, DEFAULT_DIALOG_SELECTORS } from './a11y/constants'
|
|
3
|
-
|
|
4
|
-
/** registerPageAgentTool(options) 的入参类型,定义见 tool-config.ts(与运行期 setPageAgentToolConfig 的补丁类型一致) */
|
|
5
|
-
export type { PageAgentToolOptions } from './tool-config'
|
|
6
|
-
|
|
7
|
-
declare global {
|
|
8
|
-
interface Window {
|
|
9
|
-
/** 指定网站可覆盖该函数,在每次 getBrowserState 之前调用,常用于配合 setPageAgentToolConfig 动态调整当前页面的配置 */
|
|
10
|
-
__webmcpcli_beforeGetBrowserState?: (() => void) | null
|
|
11
|
-
}
|
|
12
|
-
}
|
package/page-tools/context.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { PageController } from '@page-agent/page-controller'
|
|
2
|
-
import type { PageStateCache } from './page-state-cache'
|
|
3
|
-
import type { RefMap } from './a11y-tree'
|
|
4
|
-
|
|
5
|
-
export type PageAgentToolContentResult = {
|
|
6
|
-
content: Array<{ type: 'text'; text: string }>
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type PageAgentToolErrorResult = PageAgentToolContentResult & {
|
|
10
|
-
isError: true
|
|
11
|
-
error: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export async function createActionErrorResult(
|
|
15
|
-
message: string,
|
|
16
|
-
buildBrowserStateResponse: ActionContext['buildBrowserStateResponse']
|
|
17
|
-
): Promise<PageAgentToolErrorResult> {
|
|
18
|
-
try {
|
|
19
|
-
const browserState = await buildBrowserStateResponse('full')
|
|
20
|
-
return {
|
|
21
|
-
isError: true,
|
|
22
|
-
error: message,
|
|
23
|
-
content: [{ type: 'text', text: `${message}\n\n${browserState.content[0].text}` }]
|
|
24
|
-
}
|
|
25
|
-
} catch (error) {
|
|
26
|
-
return {
|
|
27
|
-
isError: true,
|
|
28
|
-
error: message,
|
|
29
|
-
content: [
|
|
30
|
-
{
|
|
31
|
-
type: 'text',
|
|
32
|
-
text: `${message}\n\n获取最新浏览器状态失败: ${error instanceof Error ? error.message : String(error)}`
|
|
33
|
-
}
|
|
34
|
-
]
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface ActionContext {
|
|
40
|
-
pageController: PageController
|
|
41
|
-
stateCache: PageStateCache
|
|
42
|
-
getRefMap: () => RefMap
|
|
43
|
-
setRefMap: (map: RefMap) => void
|
|
44
|
-
buildBrowserStateResponse: (
|
|
45
|
-
mode?: 'full' | 'diff' | 'both'
|
|
46
|
-
) => Promise<{ content: Array<{ type: 'text'; text: string }> }>
|
|
47
|
-
refreshOnStaleRef: (action: string, index: number) => Promise<{ content: Array<{ type: 'text'; text: string }> }>
|
|
48
|
-
errContent: (msg: string) => Promise<{ content: Array<{ type: 'text'; text: string }> }>
|
|
49
|
-
actionError: (msg: string) => Promise<PageAgentToolErrorResult>
|
|
50
|
-
}
|
package/page-tools/effects.ts
DELETED
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* page-tools/effects - 页面工具调用提示效果模块(框架无关)
|
|
3
|
-
*
|
|
4
|
-
* 作用:
|
|
5
|
-
* - 在调用通过 withPageTools 注册的页面工具时,显示页面级的调用状态提示
|
|
6
|
-
* - 以左下角小 tip 形式展示“当前正在调用的工具”文案,尽量不打扰用户操作
|
|
7
|
-
* - 纯 DOM + CSS 实现,不依赖 Vue/React 等框架
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export type ToolInvokeEffectConfig = {
|
|
11
|
-
/**
|
|
12
|
-
* 自定义提示文案,默认使用“工具标题 || 工具名称”
|
|
13
|
-
* 例如:"正在为你整理订单数据"
|
|
14
|
-
*/
|
|
15
|
-
label?: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
type RuntimeEffectConfig = {
|
|
19
|
-
label: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let overlayElement: HTMLDivElement | null = null
|
|
23
|
-
let labelElement: HTMLDivElement | null = null
|
|
24
|
-
let styleElement: HTMLStyleElement | null = null
|
|
25
|
-
let activeCount = 0
|
|
26
|
-
|
|
27
|
-
import { isDomAvailable } from '../utils/env'
|
|
28
|
-
|
|
29
|
-
const BODY_GLOW_CLASS = 'next-sdk-tool-body-glow'
|
|
30
|
-
|
|
31
|
-
function ensureDomReady() {
|
|
32
|
-
return isDomAvailable()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function ensureStyleElement() {
|
|
36
|
-
if (!ensureDomReady()) return
|
|
37
|
-
if (styleElement) return
|
|
38
|
-
|
|
39
|
-
const style = document.createElement('style')
|
|
40
|
-
style.textContent = `
|
|
41
|
-
.${BODY_GLOW_CLASS} {
|
|
42
|
-
position: relative;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.next-sdk-tool-overlay {
|
|
46
|
-
position: fixed;
|
|
47
|
-
inset: 0;
|
|
48
|
-
z-index: 999999;
|
|
49
|
-
pointer-events: none;
|
|
50
|
-
display: flex;
|
|
51
|
-
align-items: flex-end;
|
|
52
|
-
justify-content: flex-start;
|
|
53
|
-
padding: 0 0 18px 18px;
|
|
54
|
-
background: transparent;
|
|
55
|
-
animation: next-sdk-overlay-fade-in 260ms ease-out;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.next-sdk-tool-overlay--exit {
|
|
59
|
-
animation: next-sdk-overlay-fade-out 220ms ease-in forwards;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.next-sdk-tool-overlay__glow-ring {
|
|
63
|
-
display: none;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.next-sdk-tool-overlay__panel {
|
|
67
|
-
position: relative;
|
|
68
|
-
min-width: min(320px, 78vw);
|
|
69
|
-
max-width: min(420px, 82vw);
|
|
70
|
-
padding: 10px 14px;
|
|
71
|
-
border-radius: 999px;
|
|
72
|
-
background:
|
|
73
|
-
linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(17, 24, 39, 0.9)),
|
|
74
|
-
radial-gradient(circle at top left, rgba(96, 165, 250, 0.25), transparent 55%),
|
|
75
|
-
radial-gradient(circle at bottom right, rgba(45, 212, 191, 0.22), transparent 60%);
|
|
76
|
-
box-shadow:
|
|
77
|
-
0 12px 28px rgba(15, 23, 42, 0.78),
|
|
78
|
-
0 0 0 1px rgba(148, 163, 184, 0.26);
|
|
79
|
-
display: flex;
|
|
80
|
-
align-items: center;
|
|
81
|
-
gap: 10px;
|
|
82
|
-
pointer-events: none;
|
|
83
|
-
transform-origin: center;
|
|
84
|
-
animation: next-sdk-panel-pop-in 260ms cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
|
85
|
-
color: #e5e7eb;
|
|
86
|
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.next-sdk-tool-overlay__indicator {
|
|
90
|
-
width: 26px;
|
|
91
|
-
height: 26px;
|
|
92
|
-
border-radius: 999px;
|
|
93
|
-
background: radial-gradient(circle at 30% 10%, #f9fafb, #93c5fd);
|
|
94
|
-
box-shadow:
|
|
95
|
-
0 0 0 1px rgba(191, 219, 254, 0.6),
|
|
96
|
-
0 8px 18px rgba(37, 99, 235, 0.8),
|
|
97
|
-
0 0 28px rgba(56, 189, 248, 0.9);
|
|
98
|
-
position: relative;
|
|
99
|
-
flex-shrink: 0;
|
|
100
|
-
display: flex;
|
|
101
|
-
align-items: center;
|
|
102
|
-
justify-content: center;
|
|
103
|
-
overflow: hidden;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.next-sdk-tool-overlay__indicator-orbit {
|
|
107
|
-
position: absolute;
|
|
108
|
-
inset: 2px;
|
|
109
|
-
border-radius: inherit;
|
|
110
|
-
border: 1px solid rgba(248, 250, 252, 0.6);
|
|
111
|
-
box-sizing: border-box;
|
|
112
|
-
opacity: 0.9;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.next-sdk-tool-overlay__indicator-orbit::before {
|
|
116
|
-
content: '';
|
|
117
|
-
position: absolute;
|
|
118
|
-
width: 6px;
|
|
119
|
-
height: 6px;
|
|
120
|
-
border-radius: 999px;
|
|
121
|
-
background: #f9fafb;
|
|
122
|
-
box-shadow:
|
|
123
|
-
0 0 12px rgba(248, 250, 252, 0.9),
|
|
124
|
-
0 0 24px rgba(250, 249, 246, 0.9);
|
|
125
|
-
top: 0;
|
|
126
|
-
left: 50%;
|
|
127
|
-
transform: translate(-50%, -50%);
|
|
128
|
-
transform-origin: 50% 18px;
|
|
129
|
-
animation: next-sdk-indicator-orbit 1.4s linear infinite;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.next-sdk-tool-overlay__indicator-core {
|
|
133
|
-
width: 14px;
|
|
134
|
-
height: 14px;
|
|
135
|
-
border-radius: inherit;
|
|
136
|
-
background: radial-gradient(circle at 30% 20%, #f9fafb, #bfdbfe);
|
|
137
|
-
box-shadow:
|
|
138
|
-
0 0 12px rgba(248, 250, 252, 0.9),
|
|
139
|
-
0 0 32px rgba(191, 219, 254, 0.8);
|
|
140
|
-
opacity: 0.96;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.next-sdk-tool-overlay__content {
|
|
144
|
-
display: flex;
|
|
145
|
-
flex-direction: column;
|
|
146
|
-
gap: 1px;
|
|
147
|
-
min-width: 0;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.next-sdk-tool-overlay__title {
|
|
151
|
-
font-size: 11px;
|
|
152
|
-
letter-spacing: 0.08em;
|
|
153
|
-
text-transform: uppercase;
|
|
154
|
-
color: rgba(156, 163, 175, 0.96);
|
|
155
|
-
display: flex;
|
|
156
|
-
align-items: center;
|
|
157
|
-
gap: 6px;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.next-sdk-tool-overlay__title-dot {
|
|
161
|
-
width: 6px;
|
|
162
|
-
height: 6px;
|
|
163
|
-
border-radius: 999px;
|
|
164
|
-
background: #22c55e;
|
|
165
|
-
box-shadow:
|
|
166
|
-
0 0 8px rgba(34, 197, 94, 0.9),
|
|
167
|
-
0 0 14px rgba(22, 163, 74, 0.9);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
.next-sdk-tool-overlay__label {
|
|
171
|
-
font-size: 12px;
|
|
172
|
-
font-weight: 500;
|
|
173
|
-
color: #e5e7eb;
|
|
174
|
-
white-space: nowrap;
|
|
175
|
-
text-overflow: ellipsis;
|
|
176
|
-
overflow: hidden;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
@keyframes next-sdk-overlay-fade-in {
|
|
180
|
-
from { opacity: 0; }
|
|
181
|
-
to { opacity: 1; }
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
@keyframes next-sdk-overlay-fade-out {
|
|
185
|
-
from { opacity: 1; }
|
|
186
|
-
to { opacity: 0; }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
@keyframes next-sdk-indicator-orbit {
|
|
190
|
-
from {
|
|
191
|
-
transform: translate(-50%, -50%) rotate(0deg) translateY(0);
|
|
192
|
-
}
|
|
193
|
-
to {
|
|
194
|
-
transform: translate(-50%, -50%) rotate(360deg) translateY(0);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
@keyframes next-sdk-panel-pop-in {
|
|
199
|
-
0% {
|
|
200
|
-
opacity: 0;
|
|
201
|
-
transform: scale(0.92) translateY(10px);
|
|
202
|
-
}
|
|
203
|
-
100% {
|
|
204
|
-
opacity: 1;
|
|
205
|
-
transform: scale(1) translateY(0);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
`
|
|
209
|
-
|
|
210
|
-
document.head.appendChild(style)
|
|
211
|
-
styleElement = style
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function ensureOverlayElement() {
|
|
215
|
-
if (!ensureDomReady()) return
|
|
216
|
-
if (overlayElement) return
|
|
217
|
-
|
|
218
|
-
ensureStyleElement()
|
|
219
|
-
|
|
220
|
-
const overlay = document.createElement('div')
|
|
221
|
-
overlay.className = 'next-sdk-tool-overlay'
|
|
222
|
-
|
|
223
|
-
const glowRing = document.createElement('div')
|
|
224
|
-
glowRing.className = 'next-sdk-tool-overlay__glow-ring'
|
|
225
|
-
|
|
226
|
-
const panel = document.createElement('div')
|
|
227
|
-
panel.className = 'next-sdk-tool-overlay__panel'
|
|
228
|
-
|
|
229
|
-
const indicator = document.createElement('div')
|
|
230
|
-
indicator.className = 'next-sdk-tool-overlay__indicator'
|
|
231
|
-
|
|
232
|
-
const indicatorOrbit = document.createElement('div')
|
|
233
|
-
indicatorOrbit.className = 'next-sdk-tool-overlay__indicator-orbit'
|
|
234
|
-
|
|
235
|
-
const indicatorCore = document.createElement('div')
|
|
236
|
-
indicatorCore.className = 'next-sdk-tool-overlay__indicator-core'
|
|
237
|
-
|
|
238
|
-
const content = document.createElement('div')
|
|
239
|
-
content.className = 'next-sdk-tool-overlay__content'
|
|
240
|
-
|
|
241
|
-
const titleRow = document.createElement('div')
|
|
242
|
-
titleRow.className = 'next-sdk-tool-overlay__title'
|
|
243
|
-
titleRow.textContent = 'AI 正在调用页面工具'
|
|
244
|
-
|
|
245
|
-
const titleDot = document.createElement('span')
|
|
246
|
-
titleDot.className = 'next-sdk-tool-overlay__title-dot'
|
|
247
|
-
|
|
248
|
-
const label = document.createElement('div')
|
|
249
|
-
label.className = 'next-sdk-tool-overlay__label'
|
|
250
|
-
|
|
251
|
-
titleRow.prepend(titleDot)
|
|
252
|
-
content.appendChild(titleRow)
|
|
253
|
-
content.appendChild(label)
|
|
254
|
-
|
|
255
|
-
indicator.appendChild(indicatorOrbit)
|
|
256
|
-
indicator.appendChild(indicatorCore)
|
|
257
|
-
panel.appendChild(indicator)
|
|
258
|
-
panel.appendChild(content)
|
|
259
|
-
overlay.appendChild(glowRing)
|
|
260
|
-
overlay.appendChild(panel)
|
|
261
|
-
|
|
262
|
-
document.body.appendChild(overlay)
|
|
263
|
-
|
|
264
|
-
overlayElement = overlay
|
|
265
|
-
labelElement = label
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function updateOverlay(config: RuntimeEffectConfig) {
|
|
269
|
-
if (!ensureDomReady()) return
|
|
270
|
-
ensureOverlayElement()
|
|
271
|
-
if (!overlayElement || !labelElement) return
|
|
272
|
-
overlayElement.classList.remove('next-sdk-tool-overlay--exit')
|
|
273
|
-
labelElement.textContent = config.label
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function removeOverlayWithAnimation() {
|
|
277
|
-
if (!overlayElement) return
|
|
278
|
-
overlayElement.classList.add('next-sdk-tool-overlay--exit')
|
|
279
|
-
const localOverlay = overlayElement
|
|
280
|
-
let handled = false
|
|
281
|
-
let timerId: ReturnType<typeof setTimeout> | undefined
|
|
282
|
-
const handle = () => {
|
|
283
|
-
if (handled) return
|
|
284
|
-
handled = true
|
|
285
|
-
if (timerId !== undefined) {
|
|
286
|
-
clearTimeout(timerId)
|
|
287
|
-
timerId = undefined
|
|
288
|
-
}
|
|
289
|
-
if (localOverlay.parentNode) {
|
|
290
|
-
localOverlay.parentNode.removeChild(localOverlay)
|
|
291
|
-
}
|
|
292
|
-
if (overlayElement === localOverlay) {
|
|
293
|
-
overlayElement = null
|
|
294
|
-
labelElement = null
|
|
295
|
-
}
|
|
296
|
-
localOverlay.removeEventListener('animationend', handle)
|
|
297
|
-
}
|
|
298
|
-
localOverlay.addEventListener('animationend', handle)
|
|
299
|
-
// 兜底:若 animationend 未触发(如 prefers-reduced-motion 或样式被覆盖),确保清理
|
|
300
|
-
timerId = setTimeout(handle, 500)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* 在页面上显示工具调用提示效果。
|
|
305
|
-
* - 若当前已有其他工具在执行,仅更新文案,不重复创建 DOM
|
|
306
|
-
* - 会增加 activeCount 计数,需配对调用 hideToolInvokeEffect
|
|
307
|
-
*/
|
|
308
|
-
export function showToolInvokeEffect(config: RuntimeEffectConfig) {
|
|
309
|
-
if (!ensureDomReady()) return
|
|
310
|
-
activeCount += 1
|
|
311
|
-
updateOverlay(config)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* 隐藏工具调用提示效果。
|
|
316
|
-
* - 使用引用计数:只有当所有调用结束后才真正移除 Overlay
|
|
317
|
-
*/
|
|
318
|
-
export function hideToolInvokeEffect() {
|
|
319
|
-
if (!ensureDomReady() || activeCount <= 0) return
|
|
320
|
-
activeCount -= 1
|
|
321
|
-
if (activeCount === 0) {
|
|
322
|
-
removeOverlayWithAnimation()
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 将 RouteConfig 中的 invokeEffect 配置编译成运行时效果配置。
|
|
328
|
-
* - boolean / undefined:关闭或开启默认文案
|
|
329
|
-
* - 对象:允许自定义 label
|
|
330
|
-
*/
|
|
331
|
-
export function resolveRuntimeEffectConfig(
|
|
332
|
-
toolName: string,
|
|
333
|
-
toolTitle: string | undefined,
|
|
334
|
-
value: boolean | ToolInvokeEffectConfig | undefined
|
|
335
|
-
): RuntimeEffectConfig | undefined {
|
|
336
|
-
if (!value) return undefined
|
|
337
|
-
const baseLabel = toolTitle || toolName
|
|
338
|
-
if (typeof value === 'boolean') {
|
|
339
|
-
return value ? { label: baseLabel } : undefined
|
|
340
|
-
}
|
|
341
|
-
return {
|
|
342
|
-
label: value.label || baseLabel
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ActionContext } from '../context'
|
|
2
|
-
|
|
3
|
-
export async function handleBrowserState(args: any, ctx: ActionContext) {
|
|
4
|
-
if (window.__webmcpcli_beforeGetBrowserState) {
|
|
5
|
-
try {
|
|
6
|
-
window.__webmcpcli_beforeGetBrowserState()
|
|
7
|
-
} catch (e) {
|
|
8
|
-
console.warn('__webmcpcli_beforeGetBrowserState error:', e)
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return ctx.buildBrowserStateResponse(args.responseMode ?? 'diff')
|
|
12
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { clickElement } from '@page-agent/page-controller'
|
|
2
|
-
import { waitForDomSettled } from '../utils/dom'
|
|
3
|
-
import type { ActionContext } from '../context'
|
|
4
|
-
|
|
5
|
-
export async function handleClick(args: any, ctx: ActionContext) {
|
|
6
|
-
const mode = args.responseMode ?? 'diff'
|
|
7
|
-
if (args.index === undefined) return ctx.actionError('点击结果: 缺少元素索引')
|
|
8
|
-
const el = ctx.getRefMap().get(args.index)
|
|
9
|
-
if (!el) {
|
|
10
|
-
return ctx.actionError(
|
|
11
|
-
`点击失败: ref 索引 ${args.index} 已失效(页面可能已刷新),已自动重新加载页面状态,请使用新的 ref 索引重试。`
|
|
12
|
-
)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// 若 ref 指向 shadow host,解析其内部真正的可点击元素
|
|
16
|
-
let targetEl = el as HTMLElement
|
|
17
|
-
if (el.shadowRoot && !(el instanceof HTMLButtonElement) && !(el instanceof HTMLAnchorElement)) {
|
|
18
|
-
const innerClickable = el.shadowRoot.querySelector(
|
|
19
|
-
'button, a, [role="button"], [role="link"]'
|
|
20
|
-
) as HTMLElement | null
|
|
21
|
-
if (innerClickable) {
|
|
22
|
-
targetEl = innerClickable
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
await clickElement(targetEl)
|
|
26
|
-
// 等待框架异步插入的校验错误/条件渲染内容稳定后再采集状态
|
|
27
|
-
await waitForDomSettled()
|
|
28
|
-
// 操作成功后自动返回 diff/both/full
|
|
29
|
-
return ctx.buildBrowserStateResponse(mode)
|
|
30
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ActionContext } from '../context'
|
|
2
|
-
|
|
3
|
-
export async function handleExecuteJavascript(args: any, ctx: ActionContext) {
|
|
4
|
-
if (!args.script) return ctx.errContent('脚本执行异常: 缺少javascript代码')
|
|
5
|
-
// eslint-disable-next-line no-new-func
|
|
6
|
-
// 方式1:将脚本包裹在 async IIFE 中执行,允许 return 语句
|
|
7
|
-
let result = await new Function(`return (async () => { ${args.script} })()`)()
|
|
8
|
-
// 方式2:若 result 为 undefined(脚本没有 return),降级尝试以表达式方式求值
|
|
9
|
-
// 场景:Agent 写了 "Array.from(...).map(...)" 但没有 return 关键字
|
|
10
|
-
if (result === undefined) {
|
|
11
|
-
try {
|
|
12
|
-
// eslint-disable-next-line no-new-func
|
|
13
|
-
result = await new Function(`return (async () => (${args.script}))()`)()
|
|
14
|
-
} catch {
|
|
15
|
-
// 表达式求值也失败(如含 await/let/const 等语句),保持 undefined
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
await ctx.pageController.hideMask()
|
|
19
|
-
return {
|
|
20
|
-
content: [{ type: 'text' as const, text: `脚本执行结果: ${JSON.stringify(result)}` }]
|
|
21
|
-
}
|
|
22
|
-
}
|