@opentiny/next-sdk 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{SimulatorMask-BHVXyogh-iNUb4dyF.js → SimulatorMask-BHVXyogh-CARX3Rff.js} +1 -1
- package/dist/core.js +1 -1
- package/dist/{index-CXK8CcKq.js → index-R_HIbfUX.js} +1698 -1170
- package/dist/index.d.ts +11 -0
- package/dist/index.js +69 -50
- package/dist/{initialize-builtin-WebMCP-rALfqS-o.js → initialize-builtin-WebMCP-JaoKwVlm.js} +355 -346
- package/dist/page-tools/a11y/build.d.ts +3 -4
- package/dist/page-tools/a11y/config.d.ts +96 -0
- package/dist/page-tools/a11y/constants.d.ts +2 -0
- package/dist/page-tools/a11y/search.d.ts +1 -1
- package/dist/page-tools/a11y/types.d.ts +13 -22
- package/dist/page-tools/a11y/utils.d.ts +32 -11
- package/dist/page-tools/a11y/vnode.d.ts +14 -11
- package/dist/page-tools/a11y-tree.d.ts +1 -0
- package/dist/page-tools/configs/console-cloud.d.ts +6 -0
- package/dist/page-tools/constants.d.ts +5 -17
- package/dist/page-tools/context.d.ts +12 -0
- package/dist/page-tools/handlers/searchTree.d.ts +2 -1
- package/dist/page-tools/page-agent-highlight/index.d.ts +12 -0
- package/dist/page-tools/page-agent-mask/SimulatorMask.d.ts +2 -0
- package/dist/page-tools/page-agent-tool-event.d.ts +26 -0
- package/dist/page-tools/schema.d.ts +1 -0
- package/dist/page-tools/tool-config.d.ts +50 -0
- package/dist/runtime.d.ts +4 -1
- package/dist/runtime.js +37 -49
- package/dist/vitest.config.d.ts +2 -0
- package/index.ts +35 -0
- package/package.json +21 -16
- package/page-tools/a11y/build.ts +38 -34
- package/page-tools/a11y/config.ts +465 -0
- package/page-tools/a11y/constants.ts +21 -0
- package/page-tools/a11y/search.ts +1 -3
- package/page-tools/a11y/types.ts +14 -17
- package/page-tools/a11y/utils.ts +129 -156
- package/page-tools/a11y/vnode.ts +277 -67
- package/page-tools/a11y-tree.ts +1 -0
- package/page-tools/bridge.ts +22 -2
- package/page-tools/configs/console-cloud.ts +172 -0
- package/page-tools/constants.ts +5 -65
- package/page-tools/context.ts +38 -1
- package/page-tools/handlers/click.ts +6 -2
- package/page-tools/handlers/fill.ts +10 -5
- package/page-tools/handlers/searchTree.ts +7 -10
- package/page-tools/handlers/select.ts +10 -5
- package/page-tools/page-agent-highlight/index.ts +66 -19
- package/page-tools/page-agent-mask/SimulatorMask.ts +42 -22
- package/page-tools/page-agent-mask/hauwei.svg +25 -0
- package/page-tools/page-agent-prompt.md +1 -1
- package/page-tools/page-agent-tool-event.ts +113 -0
- package/page-tools/page-agent-tool.ts +85 -53
- package/page-tools/schema.ts +1 -0
- package/page-tools/tool-config.ts +100 -0
- package/page-tools/utils/dom.ts +6 -3
- package/runtime.ts +37 -6
- package/test/page-tools/a11y/build.test.ts +638 -0
- package/test/page-tools/a11y/config.test.ts +370 -0
- package/test/page-tools/configs/console-cloud.test.ts +168 -0
- package/test/page-tools/page-agent-highlight.test.ts +110 -0
- package/test/page-tools/page-agent-tool-dispatch.test.ts +208 -0
- package/test/page-tools/page-agent-tool.test.ts +102 -0
- package/test/page-tools/tool-config.test.ts +112 -0
- package/test/page-tools/utils/dom.test.ts +122 -0
- package/vite.config.runtime.ts +1 -1
- package/vitest.config.ts +10 -0
package/page-tools/constants.ts
CHANGED
|
@@ -1,72 +1,12 @@
|
|
|
1
|
-
/** 校验错误默认选择器:ARIA 标准 + 主流 UI
|
|
2
|
-
export
|
|
3
|
-
// W3C ARIA 标准(最可靠,框架无关)
|
|
4
|
-
'[role="alert"]',
|
|
5
|
-
'[aria-invalid="true"]',
|
|
6
|
-
// Tiny3 / Lego(华为云)
|
|
7
|
-
'.ti3-unifyvalid-error',
|
|
8
|
-
'.ti3-error',
|
|
9
|
-
'.ti-error',
|
|
10
|
-
'.lego-text-error',
|
|
11
|
-
'.lego-error',
|
|
12
|
-
// Element UI / Element Plus
|
|
13
|
-
'.el-form-item__error',
|
|
14
|
-
// Ant Design
|
|
15
|
-
'.ant-form-item-explain-error',
|
|
16
|
-
// Bootstrap
|
|
17
|
-
'.is-invalid',
|
|
18
|
-
'.invalid-feedback',
|
|
19
|
-
// Angular
|
|
20
|
-
'.ng-invalid',
|
|
21
|
-
// 通用命名约定
|
|
22
|
-
'.error-msg',
|
|
23
|
-
'.error-message',
|
|
24
|
-
'.error-text',
|
|
25
|
-
'.field-error',
|
|
26
|
-
'.form-error',
|
|
27
|
-
'.is-error',
|
|
28
|
-
'.has-error',
|
|
29
|
-
'.validate-error',
|
|
30
|
-
'.valid-error'
|
|
31
|
-
]
|
|
1
|
+
/** 校验错误默认选择器:ARIA 标准 + 主流 UI 框架(唯一来源为 a11y/constants.ts,此处重新导出保持旧路径可用) */
|
|
2
|
+
export { DEFAULT_ERROR_SELECTORS, DEFAULT_DIALOG_SELECTORS } from './a11y/constants'
|
|
32
3
|
|
|
33
|
-
/**
|
|
34
|
-
export
|
|
35
|
-
// W3C ARIA 标准
|
|
36
|
-
'[role="dialog"]',
|
|
37
|
-
'[role="alertdialog"]',
|
|
38
|
-
// Tiny3 / Lego(华为云)
|
|
39
|
-
'[class*="ti3-modal"]',
|
|
40
|
-
'[class*="ti3-message-box"]',
|
|
41
|
-
// Element UI / Element Plus
|
|
42
|
-
'[class*="el-dialog"]',
|
|
43
|
-
'[class*="el-message-box"]',
|
|
44
|
-
// Ant Design
|
|
45
|
-
'[class*="ant-modal"]',
|
|
46
|
-
// Bootstrap
|
|
47
|
-
'[class*="modal-content"]',
|
|
48
|
-
// Vuetify
|
|
49
|
-
'[class*="v-dialog"]',
|
|
50
|
-
// Naive UI
|
|
51
|
-
'[class*="n-modal"]'
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
export interface PageAgentToolOptions {
|
|
55
|
-
/** 允许在无障碍树节点中额外暴露的 DOM 属性白名单 */
|
|
56
|
-
exposedAttributes?: string[]
|
|
57
|
-
/** 是否启用元素高亮 */
|
|
58
|
-
enableHighlight?: boolean
|
|
59
|
-
}
|
|
4
|
+
/** registerPageAgentTool(options) 的入参类型,定义见 tool-config.ts(与运行期 setPageAgentToolConfig 的补丁类型一致) */
|
|
5
|
+
export type { PageAgentToolOptions } from './tool-config'
|
|
60
6
|
|
|
61
7
|
declare global {
|
|
62
8
|
interface Window {
|
|
63
|
-
|
|
64
|
-
__webmcpcli_interactiveBlacklist?: Element[]
|
|
65
|
-
__webmcpcli_exposedAttributes?: string[]
|
|
9
|
+
/** 指定网站可覆盖该函数,在每次 getBrowserState 之前调用,常用于配合 setPageAgentToolConfig 动态调整当前页面的配置 */
|
|
66
10
|
__webmcpcli_beforeGetBrowserState?: (() => void) | null
|
|
67
|
-
/** 校验错误元素 CSS 选择器列表(覆盖默认,用于检测页面可见的校验错误) */
|
|
68
|
-
__webmcpcli_errorSelectors?: string[]
|
|
69
|
-
/** 模态弹窗元素 CSS 选择器列表(覆盖默认,用于检测阻塞交互的弹窗) */
|
|
70
|
-
__webmcpcli_dialogSelectors?: string[]
|
|
71
11
|
}
|
|
72
12
|
}
|
package/page-tools/context.ts
CHANGED
|
@@ -2,12 +2,49 @@ import type { PageController } from '@page-agent/page-controller'
|
|
|
2
2
|
import type { PageStateCache } from './page-state-cache'
|
|
3
3
|
import type { RefMap } from './a11y-tree'
|
|
4
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
|
+
|
|
5
39
|
export interface ActionContext {
|
|
6
40
|
pageController: PageController
|
|
7
41
|
stateCache: PageStateCache
|
|
8
42
|
getRefMap: () => RefMap
|
|
9
43
|
setRefMap: (map: RefMap) => void
|
|
10
|
-
buildBrowserStateResponse: (
|
|
44
|
+
buildBrowserStateResponse: (
|
|
45
|
+
mode?: 'full' | 'diff' | 'both'
|
|
46
|
+
) => Promise<{ content: Array<{ type: 'text'; text: string }> }>
|
|
11
47
|
refreshOnStaleRef: (action: string, index: number) => Promise<{ content: Array<{ type: 'text'; text: string }> }>
|
|
12
48
|
errContent: (msg: string) => Promise<{ content: Array<{ type: 'text'; text: string }> }>
|
|
49
|
+
actionError: (msg: string) => Promise<PageAgentToolErrorResult>
|
|
13
50
|
}
|
|
@@ -4,9 +4,13 @@ import type { ActionContext } from '../context'
|
|
|
4
4
|
|
|
5
5
|
export async function handleClick(args: any, ctx: ActionContext) {
|
|
6
6
|
const mode = args.responseMode ?? 'diff'
|
|
7
|
-
if (args.index === undefined) return ctx.
|
|
7
|
+
if (args.index === undefined) return ctx.actionError('点击结果: 缺少元素索引')
|
|
8
8
|
const el = ctx.getRefMap().get(args.index)
|
|
9
|
-
if (!el)
|
|
9
|
+
if (!el) {
|
|
10
|
+
return ctx.actionError(
|
|
11
|
+
`点击失败: ref 索引 ${args.index} 已失效(页面可能已刷新),已自动重新加载页面状态,请使用新的 ref 索引重试。`
|
|
12
|
+
)
|
|
13
|
+
}
|
|
10
14
|
|
|
11
15
|
// 若 ref 指向 shadow host,解析其内部真正的可点击元素
|
|
12
16
|
let targetEl = el as HTMLElement
|
|
@@ -4,15 +4,20 @@ import type { ActionContext } from '../context'
|
|
|
4
4
|
|
|
5
5
|
export async function handleFill(args: any, ctx: ActionContext) {
|
|
6
6
|
const mode = args.responseMode ?? 'diff'
|
|
7
|
-
if (args.index === undefined || typeof args.text !== 'string')
|
|
7
|
+
if (args.index === undefined || typeof args.text !== 'string')
|
|
8
|
+
return ctx.actionError('填写结果: 缺少元素索引或文本内容')
|
|
8
9
|
const el = ctx.getRefMap().get(args.index)
|
|
9
|
-
if (!el)
|
|
10
|
+
if (!el) {
|
|
11
|
+
return ctx.actionError(
|
|
12
|
+
`填写失败: ref 索引 ${args.index} 已失效(页面可能已刷新),已自动重新加载页面状态,请使用新的 ref 索引重试。`
|
|
13
|
+
)
|
|
14
|
+
}
|
|
10
15
|
|
|
11
16
|
let targetEl = el
|
|
12
17
|
if (!(targetEl instanceof HTMLInputElement) && !(targetEl instanceof HTMLTextAreaElement)) {
|
|
13
18
|
// querySelector 不穿透 shadow boundary,对 shadow host 补查其 shadowRoot
|
|
14
|
-
const innerInput =
|
|
15
|
-
?? (el.shadowRoot?.querySelector('input, textarea') as HTMLElement | null)
|
|
19
|
+
const innerInput =
|
|
20
|
+
el.querySelector('input, textarea') ?? (el.shadowRoot?.querySelector('input, textarea') as HTMLElement | null)
|
|
16
21
|
if (innerInput) {
|
|
17
22
|
targetEl = innerInput as HTMLElement
|
|
18
23
|
}
|
|
@@ -46,7 +51,7 @@ export async function handleFill(args: any, ctx: ActionContext) {
|
|
|
46
51
|
}
|
|
47
52
|
}
|
|
48
53
|
if (!fillSuccess) {
|
|
49
|
-
return ctx.
|
|
54
|
+
return ctx.actionError(`填写结果: 无法填写元素 ${args.index},元素不是 input、textarea 或 contenteditable`)
|
|
50
55
|
}
|
|
51
56
|
// shadow DOM 内元素:inputTextElement 派发的合成事件 composed:false,
|
|
52
57
|
// 补发 composed:true 事件确保事件委托框架(如 React)能收到
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { searchA11yTree } from '../a11y-tree'
|
|
2
2
|
import { detectPageDialog, detectValidationErrors } from '../utils/dom'
|
|
3
|
-
import {
|
|
3
|
+
import { getPageAgentToolConfig } from '../tool-config'
|
|
4
4
|
import type { ActionContext } from '../context'
|
|
5
|
+
import type { PageAgentToolInput } from '../schema'
|
|
5
6
|
|
|
6
|
-
export async function handleSearchTree(args:
|
|
7
|
+
export async function handleSearchTree(args: PageAgentToolInput, ctx: ActionContext) {
|
|
7
8
|
if (!args.query) return ctx.errContent('搜索失败: 缺少 query 参数')
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const exposedAttributes = (window.__webmcpcli_exposedAttributes ?? []) as string[]
|
|
11
|
-
const result = searchA11yTree(args.query, document.body, blacklist, whitelist, {
|
|
9
|
+
const result = searchA11yTree(args.query, document.body, {
|
|
10
|
+
...getPageAgentToolConfig().a11yConfig,
|
|
12
11
|
contextLines: args.contextLines,
|
|
13
12
|
maxMatches: args.maxMatches,
|
|
14
|
-
exposedAttributes,
|
|
15
|
-
errorSelectors: (window.__webmcpcli_errorSelectors ?? DEFAULT_ERROR_SELECTORS).join(', '),
|
|
16
13
|
})
|
|
17
|
-
|
|
14
|
+
|
|
18
15
|
ctx.setRefMap(result.refMap)
|
|
19
16
|
ctx.stateCache.update(window.location.href, result.yaml)
|
|
20
|
-
|
|
17
|
+
|
|
21
18
|
await ctx.pageController.hideMask()
|
|
22
19
|
// 检测页面弹窗/遮罩层,帮助 AI 发现可能遮挡目标的确认弹窗
|
|
23
20
|
const dialogAlert = detectPageDialog()
|
|
@@ -4,23 +4,28 @@ import type { ActionContext } from '../context'
|
|
|
4
4
|
|
|
5
5
|
export async function handleSelect(args: any, ctx: ActionContext) {
|
|
6
6
|
const mode = args.responseMode ?? 'diff'
|
|
7
|
-
if (args.index === undefined || typeof args.text !== 'string')
|
|
7
|
+
if (args.index === undefined || typeof args.text !== 'string')
|
|
8
|
+
return ctx.actionError('选择结果: 缺少元素索引或文本内容')
|
|
8
9
|
let el = ctx.getRefMap().get(args.index) as HTMLSelectElement | HTMLElement | undefined
|
|
9
|
-
if (!el)
|
|
10
|
+
if (!el) {
|
|
11
|
+
return ctx.actionError(
|
|
12
|
+
`选择失败: ref 索引 ${args.index} 已失效(页面可能已刷新),已自动重新加载页面状态,请使用新的 ref 索引重试。`
|
|
13
|
+
)
|
|
14
|
+
}
|
|
10
15
|
// 若 ref 指向 shadow host,解析其内部真正的 <select>(与 fill 一致)
|
|
11
16
|
let targetSelect: HTMLSelectElement | null = null
|
|
12
17
|
if (el instanceof HTMLSelectElement) {
|
|
13
18
|
targetSelect = el
|
|
14
19
|
} else {
|
|
15
|
-
const innerSelect =
|
|
16
|
-
?? (el.shadowRoot?.querySelector('select') as HTMLSelectElement | null)
|
|
20
|
+
const innerSelect =
|
|
21
|
+
el.querySelector('select') ?? (el.shadowRoot?.querySelector('select') as HTMLSelectElement | null)
|
|
17
22
|
if (innerSelect) {
|
|
18
23
|
targetSelect = innerSelect
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
if (!targetSelect) {
|
|
23
|
-
return ctx.
|
|
28
|
+
return ctx.actionError(`选择结果: 无法选择元素 ${args.index},未找到对应的 <select> 元素`)
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
await selectOptionElement(targetSelect, args.text)
|
|
@@ -50,14 +50,62 @@ const injectStyles = `
|
|
|
50
50
|
}
|
|
51
51
|
`
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
/** 图标热区回退时父节点宽高上限,避免用到整行容器 */
|
|
54
|
+
const MAX_ICON_FALLBACK_SIZE = 48
|
|
55
|
+
|
|
56
|
+
export type HighlightRect = { top: number; left: number; width: number; height: number }
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 解析高亮矩形。
|
|
60
|
+
* Tiny3 `ti-icon` / `tp-icon` 字形常画在 ::before 上,宿主 getBoundingClientRect 宽或高为 0,
|
|
61
|
+
* 旧逻辑直接跳过导致帮助中心关闭/全屏等图标有 ref 却不高亮。
|
|
62
|
+
*/
|
|
63
|
+
export function resolveHighlightRect(el: HTMLElement): HighlightRect | null {
|
|
64
|
+
const rect = el.getBoundingClientRect()
|
|
65
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
66
|
+
return { top: rect.top, left: rect.left, width: rect.width, height: rect.height }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 优先用紧凑父节点(帮助中心 header 图标外包 28×28 的可点区域)
|
|
70
|
+
let parent: HTMLElement | null = el.parentElement
|
|
71
|
+
for (let depth = 0; depth < 3 && parent; depth++) {
|
|
72
|
+
const pr = parent.getBoundingClientRect()
|
|
73
|
+
if (
|
|
74
|
+
pr.width > 0 &&
|
|
75
|
+
pr.height > 0 &&
|
|
76
|
+
pr.width <= MAX_ICON_FALLBACK_SIZE &&
|
|
77
|
+
pr.height <= MAX_ICON_FALLBACK_SIZE
|
|
78
|
+
) {
|
|
79
|
+
return { top: pr.top, left: pr.left, width: pr.width, height: pr.height }
|
|
80
|
+
}
|
|
81
|
+
parent = parent.parentElement
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 单边有尺寸时,用 font-size 补齐另一边
|
|
85
|
+
if (rect.width > 0 || rect.height > 0) {
|
|
86
|
+
try {
|
|
87
|
+
const fs = parseFloat(getComputedStyle(el).fontSize) || 16
|
|
88
|
+
const width = rect.width > 0 ? rect.width : fs
|
|
89
|
+
const height = rect.height > 0 ? rect.height : fs
|
|
90
|
+
return {
|
|
91
|
+
top: rect.height > 0 ? rect.top : rect.top - (height - rect.height) / 2,
|
|
92
|
+
left: rect.width > 0 ? rect.left : rect.left - (width - rect.width) / 2,
|
|
93
|
+
width,
|
|
94
|
+
height,
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
/* ignore */
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** 与视口有交集即可(侧栏轻微负 left、贴边控件不应被整框剔除) */
|
|
105
|
+
function isRectInViewport(rect: HighlightRect): boolean {
|
|
106
|
+
const vh = window.innerHeight || document.documentElement.clientHeight
|
|
107
|
+
const vw = window.innerWidth || document.documentElement.clientWidth
|
|
108
|
+
return rect.top < vh && rect.top + rect.height > 0 && rect.left < vw && rect.left + rect.width > 0
|
|
61
109
|
}
|
|
62
110
|
|
|
63
111
|
const doHighlight = (refMap: RefMap, parentIframe: HTMLIFrameElement | null = null) => {
|
|
@@ -87,23 +135,23 @@ const doHighlight = (refMap: RefMap, parentIframe: HTMLIFrameElement | null = nu
|
|
|
87
135
|
iframeOffset.y = iframeRect.top
|
|
88
136
|
}
|
|
89
137
|
const fragment = document.createDocumentFragment()
|
|
90
|
-
const overlays = []
|
|
91
138
|
|
|
92
139
|
// 3、 遍历 refMap
|
|
93
140
|
for (const [index, el] of refMap.entries()) {
|
|
94
|
-
|
|
141
|
+
const rect = resolveHighlightRect(el)
|
|
142
|
+
if (!rect) continue
|
|
143
|
+
|
|
144
|
+
const top = rect.top + iframeOffset.y
|
|
145
|
+
const left = rect.left + iframeOffset.x
|
|
146
|
+
const viewportRect = { top, left, width: rect.width, height: rect.height }
|
|
147
|
+
if (!isRectInViewport(viewportRect)) continue
|
|
95
148
|
|
|
96
149
|
const color = colors[index % colors.length]
|
|
97
150
|
const textColor = color + opacity
|
|
98
151
|
|
|
99
|
-
const rect = el.getBoundingClientRect()
|
|
100
|
-
if (rect.width === 0 || rect.height === 0) continue
|
|
101
|
-
|
|
102
152
|
const overlay = document.createElement('div')
|
|
103
153
|
overlay.style.border = `2px solid ${textColor}`
|
|
104
|
-
|
|
105
|
-
const top = rect.top + iframeOffset.y
|
|
106
|
-
const left = rect.left + iframeOffset.x
|
|
154
|
+
overlay.dataset.refIndex = String(index)
|
|
107
155
|
|
|
108
156
|
overlay.style.top = `${top}px`
|
|
109
157
|
overlay.style.left = `${left}px`
|
|
@@ -113,13 +161,12 @@ const doHighlight = (refMap: RefMap, parentIframe: HTMLIFrameElement | null = nu
|
|
|
113
161
|
// 文字
|
|
114
162
|
const text = document.createElement('span')
|
|
115
163
|
text.style.backgroundColor = textColor
|
|
116
|
-
text.style.height = `${rect.height - 4}px`
|
|
117
|
-
text.style.lineHeight = `${rect.height - 4}px`
|
|
164
|
+
text.style.height = `${Math.max(rect.height - 4, 0)}px`
|
|
165
|
+
text.style.lineHeight = `${Math.max(rect.height - 4, 0)}px`
|
|
118
166
|
text.textContent = index.toString()
|
|
119
167
|
overlay.appendChild(text)
|
|
120
168
|
|
|
121
169
|
fragment.appendChild(overlay)
|
|
122
|
-
overlays.push({ element: overlay, initialRect: rect })
|
|
123
170
|
}
|
|
124
171
|
|
|
125
172
|
container.appendChild(fragment)
|
|
@@ -11,31 +11,15 @@ const injectStyles = `
|
|
|
11
11
|
z-index: 10000;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
.webmcp-page-agent-cursorBorder {
|
|
15
|
-
position: absolute;
|
|
16
|
-
width: 100%;
|
|
17
|
-
height: 100%;
|
|
18
|
-
background: linear-gradient(45deg, rgb(57, 182, 255), rgb(189, 69, 251));
|
|
19
|
-
mask-image: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20100%20100'%20fill='none'%3e%3cg%3e%3cpath%20d='M%2015%2042%20L%2015%2036.99%20Q%2015%2031.99%2023.7%2031.99%20L%2028.05%2031.99%20Q%2032.41%2031.99%2032.41%2021.99%20L%2032.41%2017%20Q%2032.41%2012%2041.09%2016.95%20L%2076.31%2037.05%20Q%2085%2042%2076.31%2046.95%20L%2041.09%2067.05%20Q%2032.41%2072%2032.41%2062.01%20L%2032.41%2057.01%20Q%2032.41%2052.01%2023.7%2052.01%20L%2019.35%2052.01%20Q%2015%2052.01%2015%2047.01%20Z'%20fill='none'%20stroke='%23000000'%20stroke-width='6'%20stroke-miterlimit='10'%20style='stroke:%20light-dark(rgb(0,%200,%200),%20rgb(255,%20255,%20255));'/%3e%3c/g%3e%3c/svg%3e");
|
|
20
|
-
mask-size: 100% 100%;
|
|
21
|
-
mask-repeat: no-repeat;
|
|
22
|
-
|
|
23
|
-
transform-origin: center;
|
|
24
|
-
transform: rotate(-90deg) scale(0.8);
|
|
25
|
-
margin-left: -10px;
|
|
26
|
-
margin-top: -18px;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
14
|
.webmcp-page-agent-cursorFilling {
|
|
30
15
|
position: absolute;
|
|
31
16
|
width: 100%;
|
|
32
17
|
height: 100%;
|
|
33
|
-
background: url("data:image/svg+xml,%
|
|
34
|
-
|
|
18
|
+
background: url("data:image/svg+xml,%3Csvg%20viewBox%3D%270%200%2079.0834%2079.4553%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20%20width%3D%2779.083374%27%20height%3D%2779.455322%27%20fill%3D%27none%27%20%3E--%3Cdefs%3E%3Cfilter%20id%3D%27filter_0%27%20width%3D%2779.083374%27%20height%3D%2779.455322%27%20x%3D%270.000000%27%20y%3D%270.000000%27%20filterUnits%3D%27userSpaceOnUse%27%20%20color-interpolation-filters%3D%27sRGB%27%3E%3CfeFlood%20flood-opacity%3D%270%27%20result%3D%27BackgroundImageFix%27%20%2F%3E%3CfeOffset%20dx%3D%276.000000%27%20dy%3D%276.000000%27%20in%3D%27SourceAlpha%27%20%2F%3E%3CfeGaussianBlur%20stdDeviation%3D%276.66666651%27%20%2F%3E%3CfeColorMatrix%20type%3D%27matrix%27%20values%3D%270%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200.18%200%20%27%20%2F%3E%3CfeBlend%20result%3D%27effect_dropShadow_1%27%20in2%3D%27BackgroundImageFix%27%20mode%3D%27normal%27%20%2F%3E%3CfeBlend%20result%3D%27shape%27%20in%3D%27SourceGraphic%27%20in2%3D%27effect_dropShadow_1%27%20mode%3D%27normal%27%20%2F%3E%3C%2Ffilter%3E%3ClinearGradient%20id%3D%27paint_linear_0%27%20x1%3D%2718.4743214%27%20x2%3D%2718.4213886%27%20y1%3D%27-1.86264515e-09%27%20y2%3D%2734.9221306%27%20gradientUnits%3D%27userSpaceOnUse%27%3E%3Cstop%20stop-color%3D%27rgb%28255%2C181%2C96%29%27%20offset%3D%270%27%20stop-opacity%3D%271%27%20%2F%3E%3Cstop%20stop-color%3D%27rgb%28255%2C120%2C114%29%27%20offset%3D%270.5%27%20stop-opacity%3D%271%27%20%2F%3E%3Cstop%20stop-color%3D%27rgb%28241%2C109%2C237%29%27%20offset%3D%271%27%20stop-opacity%3D%271%27%20%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Cg%20filter%3D%27url%28%23filter_0%29%27%3E%3Cpath%20d%3D%27M20.2632%203.57771L34.6257%2032.3027C35.4444%2033.9402%2033.8152%2035.7258%2032.1097%2035.0603L19.2014%2030.0229C18.7339%2029.8404%2018.2148%2029.8404%2017.7472%2030.0229L4.83891%2035.0603C3.13343%2035.7258%201.50423%2033.9402%202.32297%2032.3027L16.6855%203.57771C17.4225%202.10361%2019.5261%202.10361%2020.2632%203.57771Z%27%20fill%3D%27url%28%23paint_linear_0%29%27%20fill-rule%3D%27evenodd%27%20transform%3D%27matrix%280.719908%2C-0.69407%2C0.69407%2C0.719908%2C2.00256%2C28.0095%29%27%20%2F%3E%3Cpath%20d%3D%27M37.2169%2032.6848C37.1578%2032.189%2037.0096%2031.7039%2036.7723%2031.2294L22.4098%202.5044C22.1973%202.07946%2021.9328%201.70554%2021.6163%201.38261C21.3524%201.11342%2021.0524%200.879671%2020.7162%200.681366C20.3925%200.490407%2020.0558%200.34434%2019.7061%200.243163C19.312%200.129145%2018.9014%200.0721366%2018.4743%200.0721366C18.0472%200.0721366%2017.6367%200.129145%2017.2426%200.243163C16.8929%200.34434%2016.5562%200.490407%2016.2324%200.681366C15.8962%200.879671%2015.5962%201.11342%2015.3323%201.38261C15.0158%201.70553%2014.7513%202.07946%2014.5388%202.5044L0.176342%2031.2294C-0.0609121%2031.7039%20-0.209128%2032.189%20-0.268305%2032.6848C-0.316251%2033.0865%20-0.305747%2033.4951%20-0.236792%2033.9108C-0.171059%2034.307%20-0.0570567%2034.6809%200.105216%2035.0326C0.292784%2035.4391%200.544845%2035.8158%200.861397%2036.1627C1.17794%2036.5097%201.53001%2036.7951%201.91761%2037.0191C2.25297%2037.2128%202.61492%2037.3606%203.00348%2037.4623C3.41106%2037.5689%203.81704%2037.6168%204.22142%2037.6058C4.72053%2037.5922%205.21719%2037.4889%205.71141%2037.2961L18.4743%2032.3154L31.2372%2037.2961C31.7315%2037.4889%2032.2281%2037.5922%2032.7272%2037.6058C33.1316%2037.6168%2033.5376%2037.5689%2033.9452%2037.4623C34.3337%2037.3606%2034.6957%2037.2128%2035.0311%2037.0191C35.4187%2036.7951%2035.7707%2036.5097%2036.0873%2036.1627C36.4038%2035.8158%2036.6558%2035.4391%2036.8434%2035.0327C37.0057%2034.681%2037.1197%2034.307%2037.1854%2033.9108C37.2544%2033.4951%2037.2649%2033.0865%2037.2169%2032.6848ZM34.6257%2032.3027L20.2632%203.57771C19.5261%202.10361%2017.4225%202.10361%2016.6855%203.57771L2.32297%2032.3027C1.50423%2033.9402%203.13343%2035.7258%204.83891%2035.0603L17.7472%2030.0229C18.2148%2029.8404%2018.7339%2029.8404%2019.2014%2030.0229L32.1097%2035.0603C33.8152%2035.7258%2035.4444%2033.9402%2034.6257%2032.3027Z%27%20fill%3D%27rgb%28255%2C255%2C255%29%27%20fill-rule%3D%27evenodd%27%20transform%3D%27matrix%280.719908%2C-0.69407%2C0.69407%2C0.719908%2C2.00256%2C28.0095%29%27%20%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
|
|
19
|
+
background-size: 100% 100%;
|
|
35
20
|
background-repeat: no-repeat;
|
|
36
21
|
|
|
37
22
|
transform-origin: center;
|
|
38
|
-
transform: rotate(-90deg) scale(0.8);
|
|
39
23
|
margin-left: -10px;
|
|
40
24
|
margin-top: -18px;
|
|
41
25
|
}
|
|
@@ -117,7 +101,10 @@ export class SimulatorMask extends EventTarget {
|
|
|
117
101
|
try {
|
|
118
102
|
const motion = new Motion({
|
|
119
103
|
mode: isPageDark() ? 'dark' : 'light',
|
|
120
|
-
styles: { position: 'absolute', inset: '0' }
|
|
104
|
+
styles: { position: 'absolute', inset: '0' },
|
|
105
|
+
glowWidth: 80,
|
|
106
|
+
borderWidth: 8,
|
|
107
|
+
colors: ['rgb(121, 216, 247)', 'rgb(171, 219, 110)', 'rgb(252, 188, 114)', 'rgb(235, 117, 231)']
|
|
121
108
|
})
|
|
122
109
|
this.motion = motion
|
|
123
110
|
this.wrapper.appendChild(motion.element)
|
|
@@ -186,9 +173,9 @@ export class SimulatorMask extends EventTarget {
|
|
|
186
173
|
this.#cursor.appendChild(fillingLayer)
|
|
187
174
|
|
|
188
175
|
// Create border layer
|
|
189
|
-
const borderLayer = document.createElement('div')
|
|
190
|
-
borderLayer.className = 'webmcp-page-agent-cursorBorder'
|
|
191
|
-
this.#cursor.appendChild(borderLayer)
|
|
176
|
+
// const borderLayer = document.createElement('div')
|
|
177
|
+
// borderLayer.className = 'webmcp-page-agent-cursorBorder'
|
|
178
|
+
// this.#cursor.appendChild(borderLayer)
|
|
192
179
|
|
|
193
180
|
this.wrapper.appendChild(this.#cursor)
|
|
194
181
|
}
|
|
@@ -276,4 +263,37 @@ export class SimulatorMask extends EventTarget {
|
|
|
276
263
|
this.wrapper.remove()
|
|
277
264
|
this.dispatchEvent(new Event('dispose'))
|
|
278
265
|
}
|
|
266
|
+
// 根据目标元素大小,在 wrapper 添加绝对定位边框:2px 宽、与目标间距 2px,对角渐变 (0,0 → 1,1)
|
|
267
|
+
borderElement(targetElement: HTMLElement) {
|
|
268
|
+
if (!targetElement) return
|
|
269
|
+
|
|
270
|
+
this.wrapper.querySelector('.webmcp-page-agent-border')?.remove()
|
|
271
|
+
|
|
272
|
+
const rect = targetElement.getBoundingClientRect()
|
|
273
|
+
const gap = 2
|
|
274
|
+
const borderWidth = 2
|
|
275
|
+
const offset = gap + borderWidth
|
|
276
|
+
|
|
277
|
+
const el = document.createElement('div')
|
|
278
|
+
el.className = 'webmcp-page-agent-border'
|
|
279
|
+
el.style.position = 'absolute'
|
|
280
|
+
el.style.left = `${rect.left - offset}px`
|
|
281
|
+
el.style.top = `${rect.top - offset}px`
|
|
282
|
+
el.style.width = `${rect.width + offset * 2}px`
|
|
283
|
+
el.style.height = `${rect.height + offset * 2}px`
|
|
284
|
+
el.style.boxSizing = 'border-box'
|
|
285
|
+
el.style.pointerEvents = 'none'
|
|
286
|
+
el.style.zIndex = '10000'
|
|
287
|
+
el.style.border = `${borderWidth}px solid transparent`
|
|
288
|
+
// 对角渐变:左上 (0,0) → 右下 (1,1)
|
|
289
|
+
el.style.borderImageSource = 'linear-gradient(to bottom right, #79D8F7, #ABDB6E, #FCBC72, #FA8682, #EB75E7)'
|
|
290
|
+
el.style.borderImageSlice = '1'
|
|
291
|
+
el.style.borderImageWidth = `${borderWidth}px`
|
|
292
|
+
|
|
293
|
+
this.wrapper.appendChild(el)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
removeBorderElement() {
|
|
297
|
+
this.wrapper.querySelector('.webmcp-page-agent-border')?.remove()
|
|
298
|
+
}
|
|
279
299
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<svg viewBox="0 0 79.0834 79.4553" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="79.083374" height="79.455322" fill="none" customFrame="#000000">
|
|
2
|
+
--
|
|
3
|
+
<defs>
|
|
4
|
+
<g id="pixso_custom_effect_0">
|
|
5
|
+
<effect x="6.000000" y="6.000000" visibility="visible" fill="rgb(0,0,0)" fill-opacity="0.180000007" effectType="dropShadow" stdDeviation="20" radius="0" />
|
|
6
|
+
</g>
|
|
7
|
+
<filter id="filter_0" width="79.083374" height="79.455322" x="0.000000" y="0.000000" filterUnits="userSpaceOnUse" customEffect="url(#pixso_custom_effect_0)" color-interpolation-filters="sRGB">
|
|
8
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
|
9
|
+
<feOffset dx="6.000000" dy="6.000000" in="SourceAlpha" />
|
|
10
|
+
<feGaussianBlur stdDeviation="6.66666651" />
|
|
11
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.18 0 " />
|
|
12
|
+
<feBlend result="effect_dropShadow_1" in2="BackgroundImageFix" mode="normal" />
|
|
13
|
+
<feBlend result="shape" in="SourceGraphic" in2="effect_dropShadow_1" mode="normal" />
|
|
14
|
+
</filter>
|
|
15
|
+
<linearGradient id="paint_linear_0" x1="18.4743214" x2="18.4213886" y1="-1.86264515e-09" y2="34.9221306" gradientUnits="userSpaceOnUse">
|
|
16
|
+
<stop stop-color="rgb(255,181,96)" offset="0" stop-opacity="1" />
|
|
17
|
+
<stop stop-color="rgb(255,120,114)" offset="0.5" stop-opacity="1" />
|
|
18
|
+
<stop stop-color="rgb(241,109,237)" offset="1" stop-opacity="1" />
|
|
19
|
+
</linearGradient>
|
|
20
|
+
</defs>
|
|
21
|
+
<g filter="url(#filter_0)">
|
|
22
|
+
<path id="多边形 11" d="M20.2632 3.57771L34.6257 32.3027C35.4444 33.9402 33.8152 35.7258 32.1097 35.0603L19.2014 30.0229C18.7339 29.8404 18.2148 29.8404 17.7472 30.0229L4.83891 35.0603C3.13343 35.7258 1.50423 33.9402 2.32297 32.3027L16.6855 3.57771C17.4225 2.10361 19.5261 2.10361 20.2632 3.57771Z" fill="url(#paint_linear_0)" fill-rule="evenodd" transform="matrix(0.719908,-0.69407,0.69407,0.719908,2.00256,28.0095)" />
|
|
23
|
+
<path id="多边形 11" d="M37.2169 32.6848C37.1578 32.189 37.0096 31.7039 36.7723 31.2294L22.4098 2.5044C22.1973 2.07946 21.9328 1.70554 21.6163 1.38261C21.3524 1.11342 21.0524 0.879671 20.7162 0.681366C20.3925 0.490407 20.0558 0.34434 19.7061 0.243163C19.312 0.129145 18.9014 0.0721366 18.4743 0.0721366C18.0472 0.0721366 17.6367 0.129145 17.2426 0.243163C16.8929 0.34434 16.5562 0.490407 16.2324 0.681366C15.8962 0.879671 15.5962 1.11342 15.3323 1.38261C15.0158 1.70553 14.7513 2.07946 14.5388 2.5044L0.176342 31.2294C-0.0609121 31.7039 -0.209128 32.189 -0.268305 32.6848C-0.316251 33.0865 -0.305747 33.4951 -0.236792 33.9108C-0.171059 34.307 -0.0570567 34.6809 0.105216 35.0326C0.292784 35.4391 0.544845 35.8158 0.861397 36.1627C1.17794 36.5097 1.53001 36.7951 1.91761 37.0191C2.25297 37.2128 2.61492 37.3606 3.00348 37.4623C3.41106 37.5689 3.81704 37.6168 4.22142 37.6058C4.72053 37.5922 5.21719 37.4889 5.71141 37.2961L18.4743 32.3154L31.2372 37.2961C31.7315 37.4889 32.2281 37.5922 32.7272 37.6058C33.1316 37.6168 33.5376 37.5689 33.9452 37.4623C34.3337 37.3606 34.6957 37.2128 35.0311 37.0191C35.4187 36.7951 35.7707 36.5097 36.0873 36.1627C36.4038 35.8158 36.6558 35.4391 36.8434 35.0327C37.0057 34.681 37.1197 34.307 37.1854 33.9108C37.2544 33.4951 37.2649 33.0865 37.2169 32.6848ZM34.6257 32.3027L20.2632 3.57771C19.5261 2.10361 17.4225 2.10361 16.6855 3.57771L2.32297 32.3027C1.50423 33.9402 3.13343 35.7258 4.83891 35.0603L17.7472 30.0229C18.2148 29.8404 18.7339 29.8404 19.2014 30.0229L32.1097 35.0603C33.8152 35.7258 35.4444 33.9402 34.6257 32.3027Z" fill="rgb(255,255,255)" fill-rule="evenodd" transform="matrix(0.719908,-0.69407,0.69407,0.719908,2.00256,28.0095)" />
|
|
24
|
+
</g>
|
|
25
|
+
</svg>
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
- `- role #N [token1] [token2] "accessible name"`
|
|
44
44
|
- `role`:ARIA 语义角色(如 button/link/radio/heading/listitem/generic 等)
|
|
45
45
|
- `#N`:可交互元素的唯一操作索引,**只有带 `#N` 的节点才能被操作**,操作时将 N 作为 `index` 参数传入
|
|
46
|
-
- `[token]`:可选 token,包括状态标记(如 `[checked]` `[selected]` `[disabled]` `[cursor=pointer]`)和定制属性(如 `[cf-uba="..."]`)
|
|
46
|
+
- `[token]`:可选 token,包括状态标记(如 `[checked]` `[selected]` `[pressed]` `[current]` `[expanded]` `[disabled]` `[readonly]` `[required]` `[invalid]` `[busy]` `[error]` `[warning]` `[cursor=pointer]`)和定制属性(如 `[cf-uba="..."]`)
|
|
47
47
|
- `"accessible name"`:元素的语义化名称,**用双引号包裹**(通过 aria-label/aria-labelledby/innerText 等计算得出);无名称的节点此字段省略
|
|
48
48
|
- 缩进表示父子关系
|
|
49
49
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { inputSchema, type PageAgentToolInput, type PageAgentToolRawInput } from './schema'
|
|
2
|
+
import { PageController } from '@page-agent/page-controller'
|
|
3
|
+
|
|
4
|
+
export const PAGE_AGENT_TOOL_CALL_EVENT = 'page-agent-tool-call'
|
|
5
|
+
export const PAGE_AGENT_TOOL_RESULT_EVENT = 'page-agent-tool-result'
|
|
6
|
+
export const PAGE_AGENT_CHAT_END_EVENT = 'page-agent-chat-end'
|
|
7
|
+
|
|
8
|
+
export type PageAgentToolCallEventDetail = {
|
|
9
|
+
data?: PageAgentToolRawInput
|
|
10
|
+
requestId: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type PageAgentToolResultEventDetail = {
|
|
14
|
+
data: {
|
|
15
|
+
success: boolean
|
|
16
|
+
result?: unknown
|
|
17
|
+
error?: string
|
|
18
|
+
}
|
|
19
|
+
requestId: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type ExecutePageAgentTool = (data: PageAgentToolInput) => Promise<unknown>
|
|
23
|
+
|
|
24
|
+
function isPageAgentToolErrorResult(result: unknown): result is { isError: true; error: string } {
|
|
25
|
+
return (
|
|
26
|
+
typeof result === 'object' &&
|
|
27
|
+
result !== null &&
|
|
28
|
+
'isError' in result &&
|
|
29
|
+
result.isError === true &&
|
|
30
|
+
'error' in result &&
|
|
31
|
+
typeof result.error === 'string'
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare global {
|
|
36
|
+
interface Window {
|
|
37
|
+
__nextSdkPageAgentToolEventCleanup?: () => void
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function dispatchPageAgentToolResult(detail: PageAgentToolResultEventDetail) {
|
|
42
|
+
window.dispatchEvent(new CustomEvent(PAGE_AGENT_TOOL_RESULT_EVENT, { detail }))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function setupPageAgentToolEventBridge(
|
|
46
|
+
executePageAgentTool: ExecutePageAgentTool,
|
|
47
|
+
pageController: PageController
|
|
48
|
+
) {
|
|
49
|
+
window.__nextSdkPageAgentToolEventCleanup?.()
|
|
50
|
+
|
|
51
|
+
const handleToolCall = async (event: Event) => {
|
|
52
|
+
const detail = (event as CustomEvent<PageAgentToolCallEventDetail>).detail
|
|
53
|
+
const requestId = detail?.requestId
|
|
54
|
+
|
|
55
|
+
if (!requestId) {
|
|
56
|
+
dispatchPageAgentToolResult({
|
|
57
|
+
requestId: '',
|
|
58
|
+
data: {
|
|
59
|
+
success: false,
|
|
60
|
+
error: '缺少 requestId'
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const data = inputSchema.parse(detail.data)
|
|
68
|
+
const result = await executePageAgentTool(data)
|
|
69
|
+
if (isPageAgentToolErrorResult(result)) {
|
|
70
|
+
dispatchPageAgentToolResult({
|
|
71
|
+
requestId,
|
|
72
|
+
data: {
|
|
73
|
+
success: false,
|
|
74
|
+
error: result.error,
|
|
75
|
+
result
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
dispatchPageAgentToolResult({
|
|
81
|
+
requestId,
|
|
82
|
+
data: {
|
|
83
|
+
success: true,
|
|
84
|
+
result
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
} catch (error) {
|
|
88
|
+
dispatchPageAgentToolResult({
|
|
89
|
+
requestId,
|
|
90
|
+
data: {
|
|
91
|
+
success: false,
|
|
92
|
+
error: error instanceof Error ? error.message : String(error)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 聊天结束事件,用于在聊天结束时移除遮罩
|
|
99
|
+
const handleChatEnd = async () => {
|
|
100
|
+
try {
|
|
101
|
+
await pageController.hideMask()
|
|
102
|
+
} catch (error) {}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
window.addEventListener(PAGE_AGENT_TOOL_CALL_EVENT, handleToolCall)
|
|
106
|
+
window.addEventListener(PAGE_AGENT_CHAT_END_EVENT, handleChatEnd)
|
|
107
|
+
|
|
108
|
+
window.__nextSdkPageAgentToolEventCleanup = () => {
|
|
109
|
+
window.removeEventListener(PAGE_AGENT_TOOL_CALL_EVENT, handleToolCall)
|
|
110
|
+
window.removeEventListener(PAGE_AGENT_CHAT_END_EVENT, handleChatEnd)
|
|
111
|
+
window.__nextSdkPageAgentToolEventCleanup = undefined
|
|
112
|
+
}
|
|
113
|
+
}
|