@opentiny/next-sdk 0.4.0 → 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/agent/AgentModelProvider.ts +78 -81
- package/agent/type.ts +6 -9
- package/agent/utils/getAISDKTools.ts +0 -1
- package/agent/utils/getBuiltinMcpTools.ts +7 -7
- package/core.ts +0 -3
- package/dist/SimulatorMask-BHVXyogh-CARX3Rff.js +361 -0
- package/dist/agent/type.d.ts +4 -12
- package/dist/agent/utils/getBuiltinMcpTools.d.ts +3 -3
- package/dist/core.d.ts +0 -1
- package/dist/core.js +16 -17
- package/dist/index-R_HIbfUX.js +6604 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.js +76 -4969
- package/dist/{initialize-builtin-WebMCP-HgObT902.js → initialize-builtin-WebMCP-JaoKwVlm.js} +1156 -1037
- package/dist/page-tools/a11y/build.d.ts +10 -0
- package/dist/page-tools/a11y/config.d.ts +96 -0
- package/dist/page-tools/a11y/constants.d.ts +11 -0
- package/dist/page-tools/a11y/search.d.ts +17 -0
- package/dist/page-tools/a11y/types.d.ts +95 -0
- package/dist/page-tools/a11y/utils.d.ts +55 -0
- package/dist/page-tools/a11y/vnode.d.ts +40 -0
- package/dist/page-tools/a11y-tree.d.ts +9 -99
- package/dist/page-tools/configs/console-cloud.d.ts +6 -0
- package/dist/page-tools/constants.d.ts +10 -0
- package/dist/page-tools/context.d.ts +40 -0
- package/dist/page-tools/handlers/browserState.d.ts +8 -0
- package/dist/page-tools/handlers/click.d.ts +8 -0
- package/dist/page-tools/handlers/executeJavascript.d.ts +8 -0
- package/dist/page-tools/handlers/fill.d.ts +8 -0
- package/dist/page-tools/handlers/scroll.d.ts +8 -0
- package/dist/page-tools/handlers/searchTree.d.ts +9 -0
- package/dist/page-tools/handlers/select.d.ts +8 -0
- package/dist/page-tools/page-agent-highlight/index.d.ts +21 -0
- package/dist/page-tools/page-agent-mask/SimulatorMask.d.ts +16 -0
- package/dist/page-tools/page-agent-mask/checkDarkMode.d.ts +5 -0
- package/dist/page-tools/page-agent-tool-event.d.ts +26 -0
- package/dist/page-tools/page-agent-tool.d.ts +3 -8
- package/dist/page-tools/schema.d.ts +44 -0
- package/dist/page-tools/tool-config.d.ts +50 -0
- package/dist/page-tools/utils/dom.d.ts +6 -0
- package/dist/page-tools/utils/scroll.d.ts +15 -0
- package/dist/runtime.d.ts +7 -0
- package/dist/runtime.js +732 -0
- package/dist/utils/builtinProxy.d.ts +1 -1
- package/dist/vitest.config.d.ts +2 -0
- package/index.ts +35 -5
- package/package.json +23 -29
- package/page-tools/a11y/build.ts +74 -0
- package/page-tools/a11y/config.ts +465 -0
- package/page-tools/a11y/constants.ts +131 -0
- package/page-tools/a11y/search.ts +127 -0
- package/page-tools/a11y/types.ts +105 -0
- package/page-tools/a11y/utils.ts +239 -0
- package/page-tools/a11y/vnode.ts +439 -0
- package/page-tools/a11y-tree.ts +9 -527
- package/page-tools/bridge.ts +23 -3
- package/page-tools/configs/console-cloud.ts +172 -0
- package/page-tools/constants.ts +12 -0
- package/page-tools/context.ts +50 -0
- package/page-tools/handlers/browserState.ts +12 -0
- package/page-tools/handlers/click.ts +30 -0
- package/page-tools/handlers/executeJavascript.ts +22 -0
- package/page-tools/handlers/fill.ts +65 -0
- package/page-tools/handlers/scroll.ts +66 -0
- package/page-tools/handlers/searchTree.ts +27 -0
- package/page-tools/handlers/select.ts +39 -0
- package/page-tools/page-agent-highlight/index.ts +245 -0
- package/page-tools/page-agent-mask/SimulatorMask.module.css +14 -0
- package/page-tools/page-agent-mask/SimulatorMask.ts +299 -0
- package/page-tools/page-agent-mask/checkDarkMode.ts +181 -0
- package/page-tools/page-agent-mask/cursor-border.svg +3 -0
- package/page-tools/page-agent-mask/cursor-fill.svg +5 -0
- package/page-tools/page-agent-mask/cursor.module.css +70 -0
- package/page-tools/page-agent-mask/hauwei.svg +25 -0
- package/page-tools/page-agent-prompt.md +34 -18
- package/page-tools/page-agent-tool-event.ts +113 -0
- package/page-tools/page-agent-tool.ts +146 -162
- package/page-tools/schema.ts +52 -0
- package/page-tools/tool-config.ts +100 -0
- package/page-tools/utils/dom.ts +158 -0
- package/page-tools/utils/scroll.ts +58 -0
- package/runtime.ts +44 -0
- 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/utils/builtinProxy.ts +45 -13
- package/vite.config.runtime.ts +22 -0
- package/vite.config.ts +52 -8
- package/vitest.config.ts +10 -0
- package/McpSdk.ts +0 -14
- package/WebAgent.ts +0 -5
- package/WebMcp.ts +0 -26
- package/Zod.ts +0 -1
- package/dist/McpSdk.d.ts +0 -14
- package/dist/SimulatorMask-BHVXyogh-BFEGpD5S.js +0 -1048
- package/dist/SimulatorMask-BHVXyogh-CCYbrb84.js +0 -801
- package/dist/WebAgent.d.ts +0 -5
- package/dist/WebMcp.d.ts +0 -23
- package/dist/Zod.d.ts +0 -1
- package/dist/index.es.dev.js +0 -59017
- package/dist/index.es.js +0 -46795
- package/dist/index.umd.dev.js +0 -60355
- package/dist/index.umd.js +0 -1248
- package/dist/mcpsdk@1.25.3.dev.js +0 -22780
- package/dist/mcpsdk@1.25.3.es.dev.js +0 -22778
- package/dist/mcpsdk@1.25.3.es.js +0 -16960
- package/dist/mcpsdk@1.25.3.js +0 -48
- package/dist/transport/ExtensionClientTransport.d.ts +0 -24
- package/dist/transport/ExtensionContentServerTransport.d.ts +0 -39
- package/dist/transport/ExtensionPageServerTransport.d.ts +0 -36
- package/dist/transport/messages.d.ts +0 -9
- package/dist/vite.config.mcpSdk.d.ts +0 -2
- package/dist/vite.config.webAgent.d.ts +0 -2
- package/dist/vite.config.webMcp.d.ts +0 -2
- package/dist/vite.config.webMcpFull.d.ts +0 -2
- package/dist/vite.config.zod.d.ts +0 -2
- package/dist/webagent.dev.js +0 -49360
- package/dist/webagent.es.dev.js +0 -49071
- package/dist/webagent.es.js +0 -39219
- package/dist/webagent.js +0 -642
- package/dist/webmcp-full.dev.js +0 -31336
- package/dist/webmcp-full.es.dev.js +0 -30283
- package/dist/webmcp-full.es.js +0 -22889
- package/dist/webmcp-full.js +0 -645
- package/dist/webmcp.dev.js +0 -9572
- package/dist/webmcp.es.dev.js +0 -8518
- package/dist/webmcp.es.js +0 -6727
- package/dist/webmcp.js +0 -602
- package/dist/zod@3.25.76.dev.js +0 -4037
- package/dist/zod@3.25.76.es.dev.js +0 -4033
- package/dist/zod@3.25.76.es.js +0 -2945
- package/dist/zod@3.25.76.js +0 -1
- package/transport/ExtensionClientTransport.ts +0 -100
- package/transport/ExtensionContentServerTransport.ts +0 -162
- package/transport/ExtensionPageServerTransport.ts +0 -149
- package/transport/messages.ts +0 -63
- package/vite-build-tsc.ts +0 -63
- package/vite-env.d.ts +0 -10
- package/vite.config.mcpSdk.ts +0 -28
- package/vite.config.webAgent.ts +0 -19
- package/vite.config.webMcp.ts +0 -40
- package/vite.config.webMcpFull.ts +0 -19
- package/vite.config.zod.ts +0 -23
- /package/dist/{vite-build-tsc.d.ts → vite.config.runtime.d.ts} +0 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { type RefMap } from '../a11y-tree'
|
|
2
|
+
|
|
3
|
+
export const HIGHLIGHT_CONTAINER_ID = 'webmcpcli-highlight-container'
|
|
4
|
+
export const HIGHLIGHT_CONTAINER_STYLE_ID = 'webmcpcli-highlight-container-style'
|
|
5
|
+
const colors = [
|
|
6
|
+
'#FF0000',
|
|
7
|
+
'#00FF00',
|
|
8
|
+
'#0000FF',
|
|
9
|
+
'#FFA500',
|
|
10
|
+
'#800080',
|
|
11
|
+
'#008080',
|
|
12
|
+
'#FF69B4',
|
|
13
|
+
'#4B0082',
|
|
14
|
+
'#FF4500',
|
|
15
|
+
'#2E8B57',
|
|
16
|
+
'#DC143C',
|
|
17
|
+
'#4682B4'
|
|
18
|
+
]
|
|
19
|
+
// border + label opacity
|
|
20
|
+
const opacity = Math.floor(0.3 * 255)
|
|
21
|
+
.toString(16)
|
|
22
|
+
.padStart(2, '0')
|
|
23
|
+
|
|
24
|
+
const injectStyles = `
|
|
25
|
+
#${HIGHLIGHT_CONTAINER_ID} {
|
|
26
|
+
position: fixed;
|
|
27
|
+
pointer-events: none;
|
|
28
|
+
top: 0;
|
|
29
|
+
left: 0;
|
|
30
|
+
width: 100%;
|
|
31
|
+
height: 100%;
|
|
32
|
+
z-index: 2147483640;
|
|
33
|
+
background-color: transparent;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#${HIGHLIGHT_CONTAINER_ID} div {
|
|
37
|
+
position: fixed;
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
background-color: transparent;
|
|
40
|
+
text-align: right;
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
border-radius: 4px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#${HIGHLIGHT_CONTAINER_ID} div span {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
color: #fff;
|
|
48
|
+
padding: 0 2px;
|
|
49
|
+
border-radius: 4px;
|
|
50
|
+
}
|
|
51
|
+
`
|
|
52
|
+
|
|
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
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const doHighlight = (refMap: RefMap, parentIframe: HTMLIFrameElement | null = null) => {
|
|
112
|
+
// 1、保证注入脚本
|
|
113
|
+
if (!document.head.querySelector(`#${HIGHLIGHT_CONTAINER_STYLE_ID}`)) {
|
|
114
|
+
const style = document.head.appendChild(document.createElement('style'))
|
|
115
|
+
style.id = HIGHLIGHT_CONTAINER_STYLE_ID
|
|
116
|
+
style.textContent = injectStyles
|
|
117
|
+
document.head.appendChild(style)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 2、保证contrainer( container 在 a11y 时已加入黑名单)
|
|
121
|
+
let container = document.getElementById(HIGHLIGHT_CONTAINER_ID)
|
|
122
|
+
if (!container) {
|
|
123
|
+
container = document.createElement('div')
|
|
124
|
+
container.id = HIGHLIGHT_CONTAINER_ID
|
|
125
|
+
|
|
126
|
+
document.body.appendChild(container)
|
|
127
|
+
} else {
|
|
128
|
+
container.innerHTML = ''
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let iframeOffset = { x: 0, y: 0 }
|
|
132
|
+
if (parentIframe) {
|
|
133
|
+
const iframeRect = parentIframe.getBoundingClientRect() // Keep getBoundingClientRect for iframe offset
|
|
134
|
+
iframeOffset.x = iframeRect.left
|
|
135
|
+
iframeOffset.y = iframeRect.top
|
|
136
|
+
}
|
|
137
|
+
const fragment = document.createDocumentFragment()
|
|
138
|
+
|
|
139
|
+
// 3、 遍历 refMap
|
|
140
|
+
for (const [index, el] of refMap.entries()) {
|
|
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
|
|
148
|
+
|
|
149
|
+
const color = colors[index % colors.length]
|
|
150
|
+
const textColor = color + opacity
|
|
151
|
+
|
|
152
|
+
const overlay = document.createElement('div')
|
|
153
|
+
overlay.style.border = `2px solid ${textColor}`
|
|
154
|
+
overlay.dataset.refIndex = String(index)
|
|
155
|
+
|
|
156
|
+
overlay.style.top = `${top}px`
|
|
157
|
+
overlay.style.left = `${left}px`
|
|
158
|
+
overlay.style.width = `${rect.width}px`
|
|
159
|
+
overlay.style.height = `${rect.height}px`
|
|
160
|
+
|
|
161
|
+
// 文字
|
|
162
|
+
const text = document.createElement('span')
|
|
163
|
+
text.style.backgroundColor = textColor
|
|
164
|
+
text.style.height = `${Math.max(rect.height - 4, 0)}px`
|
|
165
|
+
text.style.lineHeight = `${Math.max(rect.height - 4, 0)}px`
|
|
166
|
+
text.textContent = index.toString()
|
|
167
|
+
overlay.appendChild(text)
|
|
168
|
+
|
|
169
|
+
fragment.appendChild(overlay)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
container.appendChild(fragment)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const highlight = (refMap: RefMap, parentIframe: HTMLIFrameElement | null = null) => {
|
|
176
|
+
doHighlight(refMap, parentIframe)
|
|
177
|
+
|
|
178
|
+
// 4、监听滚动和resize事件,更新位置
|
|
179
|
+
const updatePositions = () => doHighlight(refMap, parentIframe)
|
|
180
|
+
const throttleFunction = (func: (...args: any[]) => void, delay: number) => {
|
|
181
|
+
let lastCall = 0
|
|
182
|
+
return (...args: any[]) => {
|
|
183
|
+
const now = performance.now()
|
|
184
|
+
if (now - lastCall < delay) return
|
|
185
|
+
lastCall = now
|
|
186
|
+
return func(...args)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const throttledUpdatePositions = throttleFunction(updatePositions, 16)
|
|
191
|
+
window.addEventListener('scroll', throttledUpdatePositions, true)
|
|
192
|
+
window.addEventListener('resize', throttledUpdatePositions)
|
|
193
|
+
|
|
194
|
+
const cleanupFn = () => {
|
|
195
|
+
window.removeEventListener('scroll', throttledUpdatePositions, true)
|
|
196
|
+
window.removeEventListener('resize', throttledUpdatePositions)
|
|
197
|
+
}
|
|
198
|
+
const win = window as any
|
|
199
|
+
;(win._highlightCleanupFunctions = win._highlightCleanupFunctions || []).push(cleanupFn)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** 移除高亮, 可反复调用 */
|
|
203
|
+
export const unhighlight = () => {
|
|
204
|
+
document.getElementById(HIGHLIGHT_CONTAINER_ID)?.remove()
|
|
205
|
+
|
|
206
|
+
const cleanupFunctions = (window as any)._highlightCleanupFunctions || []
|
|
207
|
+
for (const cleanup of cleanupFunctions) {
|
|
208
|
+
if (typeof cleanup === 'function') {
|
|
209
|
+
cleanup()
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
;(window as any)._highlightCleanupFunctions = []
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** 全局监听变化,随时准备移除高亮, 可反复调用 */
|
|
217
|
+
export const globalRemoveListener = () => {
|
|
218
|
+
if ((window as any).__registerGlobalRemoveListener) return
|
|
219
|
+
;(window as any).__registerGlobalRemoveListener = true
|
|
220
|
+
window.addEventListener('popstate', () => {
|
|
221
|
+
unhighlight()
|
|
222
|
+
})
|
|
223
|
+
window.addEventListener('hashchange', () => {
|
|
224
|
+
unhighlight()
|
|
225
|
+
})
|
|
226
|
+
window.addEventListener('beforeunload', () => {
|
|
227
|
+
unhighlight()
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const navigation = (window as any).navigation
|
|
231
|
+
if (navigation && typeof navigation.addEventListener === 'function') {
|
|
232
|
+
navigation.addEventListener('navigate', () => {
|
|
233
|
+
unhighlight()
|
|
234
|
+
})
|
|
235
|
+
} else {
|
|
236
|
+
// 定时器
|
|
237
|
+
let currentUrl = window.location.href
|
|
238
|
+
setInterval(() => {
|
|
239
|
+
if (window.location.href !== currentUrl) {
|
|
240
|
+
currentUrl = window.location.href
|
|
241
|
+
unhighlight()
|
|
242
|
+
}
|
|
243
|
+
}, 500)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { Motion } from 'ai-motion'
|
|
2
|
+
|
|
3
|
+
import { isPageDark } from './checkDarkMode'
|
|
4
|
+
|
|
5
|
+
const injectStyles = `
|
|
6
|
+
.webmcp-page-agent-cursor {
|
|
7
|
+
position: absolute;
|
|
8
|
+
width: var(--cursor-size, 75px);
|
|
9
|
+
height: var(--cursor-size, 75px);
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
z-index: 10000;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.webmcp-page-agent-cursorFilling {
|
|
15
|
+
position: absolute;
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 100%;
|
|
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%;
|
|
20
|
+
background-repeat: no-repeat;
|
|
21
|
+
|
|
22
|
+
transform-origin: center;
|
|
23
|
+
margin-left: -10px;
|
|
24
|
+
margin-top: -18px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.webmcp-page-agent-cursorRipple {
|
|
28
|
+
position: absolute;
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
margin-left: -50%;
|
|
33
|
+
margin-top: -50%;
|
|
34
|
+
|
|
35
|
+
&::after {
|
|
36
|
+
content: '';
|
|
37
|
+
opacity: 0;
|
|
38
|
+
position: absolute;
|
|
39
|
+
inset: 0;
|
|
40
|
+
border: 4px solid rgba(57, 182, 255, 1);
|
|
41
|
+
border-radius: 50%;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.webmcp-page-agent-cursor.clicking .webmcp-page-agent-cursorRipple::after {
|
|
46
|
+
animation: webmcp-page-agent-cursor-ripple 300ms ease-out forwards;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@keyframes webmcp-page-agent-cursor-ripple {
|
|
50
|
+
0% {
|
|
51
|
+
transform: scale(0);
|
|
52
|
+
opacity: 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
100% {
|
|
56
|
+
transform: scale(2);
|
|
57
|
+
opacity: 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.webmcp-page-agent-wrapper {
|
|
62
|
+
position: fixed;
|
|
63
|
+
inset: 0;
|
|
64
|
+
z-index: 2147483641;
|
|
65
|
+
/* 确保在所有元素之上,除了 panel */
|
|
66
|
+
cursor: wait;
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
|
|
69
|
+
display: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.webmcp-page-agent-wrapper.visible {
|
|
73
|
+
display: block;
|
|
74
|
+
}
|
|
75
|
+
`
|
|
76
|
+
|
|
77
|
+
export class SimulatorMask extends EventTarget {
|
|
78
|
+
shown: boolean = false
|
|
79
|
+
wrapper = document.createElement('div')
|
|
80
|
+
motion: Motion | null = null
|
|
81
|
+
|
|
82
|
+
#disposed = false
|
|
83
|
+
|
|
84
|
+
#cursor = document.createElement('div')
|
|
85
|
+
|
|
86
|
+
#currentCursorX = 0
|
|
87
|
+
#currentCursorY = 0
|
|
88
|
+
|
|
89
|
+
#targetCursorX = 0
|
|
90
|
+
#targetCursorY = 0
|
|
91
|
+
|
|
92
|
+
constructor() {
|
|
93
|
+
super()
|
|
94
|
+
// 注入样式,防止打包失败
|
|
95
|
+
document.head.appendChild(document.createElement('style')).textContent = injectStyles
|
|
96
|
+
this.wrapper.id = 'page-agent-runtime_simulator-mask'
|
|
97
|
+
this.wrapper.className = 'webmcp-page-agent-wrapper'
|
|
98
|
+
this.wrapper.setAttribute('data-browser-use-ignore', 'true')
|
|
99
|
+
this.wrapper.setAttribute('data-page-agent-ignore', 'true')
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const motion = new Motion({
|
|
103
|
+
mode: isPageDark() ? 'dark' : 'light',
|
|
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)']
|
|
108
|
+
})
|
|
109
|
+
this.motion = motion
|
|
110
|
+
this.wrapper.appendChild(motion.element)
|
|
111
|
+
motion.autoResize(this.wrapper)
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.warn('[SimulatorMask] Motion overlay unavailable:', e)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const stopEvent = (e: Event) => {
|
|
117
|
+
e.stopPropagation()
|
|
118
|
+
e.preventDefault()
|
|
119
|
+
}
|
|
120
|
+
;['click', 'mousedown', 'mouseup', 'mousemove', 'wheel', 'keydown', 'keyup'].forEach((eventName) =>
|
|
121
|
+
this.wrapper.addEventListener(eventName, stopEvent)
|
|
122
|
+
)
|
|
123
|
+
// Create AI cursor
|
|
124
|
+
this.#createCursor()
|
|
125
|
+
// this.show()
|
|
126
|
+
|
|
127
|
+
document.body.appendChild(this.wrapper)
|
|
128
|
+
|
|
129
|
+
this.#moveCursorToTarget()
|
|
130
|
+
|
|
131
|
+
// global events
|
|
132
|
+
// @note Mask should be isolated from the rest of the code.
|
|
133
|
+
// Global events are easier to manage and cleanup.
|
|
134
|
+
|
|
135
|
+
const movePointerToListener = (event: Event) => {
|
|
136
|
+
const { x, y } = (event as CustomEvent).detail
|
|
137
|
+
this.setCursorPosition(x, y)
|
|
138
|
+
}
|
|
139
|
+
const clickPointerListener = () => {
|
|
140
|
+
this.triggerClickAnimation()
|
|
141
|
+
}
|
|
142
|
+
const enablePassThroughListener = () => {
|
|
143
|
+
this.wrapper.style.pointerEvents = 'none'
|
|
144
|
+
}
|
|
145
|
+
const disablePassThroughListener = () => {
|
|
146
|
+
this.wrapper.style.pointerEvents = 'auto'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
window.addEventListener('PageAgent::MovePointerTo', movePointerToListener)
|
|
150
|
+
window.addEventListener('PageAgent::ClickPointer', clickPointerListener)
|
|
151
|
+
window.addEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)
|
|
152
|
+
window.addEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)
|
|
153
|
+
|
|
154
|
+
this.addEventListener('dispose', () => {
|
|
155
|
+
window.removeEventListener('PageAgent::MovePointerTo', movePointerToListener)
|
|
156
|
+
window.removeEventListener('PageAgent::ClickPointer', clickPointerListener)
|
|
157
|
+
window.removeEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)
|
|
158
|
+
window.removeEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#createCursor() {
|
|
163
|
+
this.#cursor.className = 'webmcp-page-agent-cursor'
|
|
164
|
+
|
|
165
|
+
// Create ripple effect container
|
|
166
|
+
const rippleContainer = document.createElement('div')
|
|
167
|
+
rippleContainer.className = 'webmcp-page-agent-cursorRipple'
|
|
168
|
+
this.#cursor.appendChild(rippleContainer)
|
|
169
|
+
|
|
170
|
+
// Create filling layer
|
|
171
|
+
const fillingLayer = document.createElement('div')
|
|
172
|
+
fillingLayer.className = 'webmcp-page-agent-cursorFilling'
|
|
173
|
+
this.#cursor.appendChild(fillingLayer)
|
|
174
|
+
|
|
175
|
+
// Create border layer
|
|
176
|
+
// const borderLayer = document.createElement('div')
|
|
177
|
+
// borderLayer.className = 'webmcp-page-agent-cursorBorder'
|
|
178
|
+
// this.#cursor.appendChild(borderLayer)
|
|
179
|
+
|
|
180
|
+
this.wrapper.appendChild(this.#cursor)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#moveCursorToTarget() {
|
|
184
|
+
if (this.#disposed) return
|
|
185
|
+
|
|
186
|
+
const newX = this.#currentCursorX + (this.#targetCursorX - this.#currentCursorX) * 0.2
|
|
187
|
+
const newY = this.#currentCursorY + (this.#targetCursorY - this.#currentCursorY) * 0.2
|
|
188
|
+
|
|
189
|
+
const xDistance = Math.abs(newX - this.#targetCursorX)
|
|
190
|
+
if (xDistance > 0) {
|
|
191
|
+
if (xDistance < 2) {
|
|
192
|
+
this.#currentCursorX = this.#targetCursorX
|
|
193
|
+
} else {
|
|
194
|
+
this.#currentCursorX = newX
|
|
195
|
+
}
|
|
196
|
+
this.#cursor.style.left = `${this.#currentCursorX}px`
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const yDistance = Math.abs(newY - this.#targetCursorY)
|
|
200
|
+
if (yDistance > 0) {
|
|
201
|
+
if (yDistance < 2) {
|
|
202
|
+
this.#currentCursorY = this.#targetCursorY
|
|
203
|
+
} else {
|
|
204
|
+
this.#currentCursorY = newY
|
|
205
|
+
}
|
|
206
|
+
this.#cursor.style.top = `${this.#currentCursorY}px`
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
requestAnimationFrame(() => this.#moveCursorToTarget())
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
setCursorPosition(x: number, y: number) {
|
|
213
|
+
if (this.#disposed) return
|
|
214
|
+
|
|
215
|
+
this.#targetCursorX = x
|
|
216
|
+
this.#targetCursorY = y
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
triggerClickAnimation() {
|
|
220
|
+
if (this.#disposed) return
|
|
221
|
+
|
|
222
|
+
this.#cursor.classList.remove('clicking')
|
|
223
|
+
// Force reflow to restart animation
|
|
224
|
+
void this.#cursor.offsetHeight
|
|
225
|
+
this.#cursor.classList.add('clicking')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
show() {
|
|
229
|
+
if (this.shown || this.#disposed) return
|
|
230
|
+
|
|
231
|
+
this.shown = true
|
|
232
|
+
this.motion?.start()
|
|
233
|
+
this.motion?.fadeIn()
|
|
234
|
+
|
|
235
|
+
this.wrapper.classList.add('visible')
|
|
236
|
+
|
|
237
|
+
// Initialize cursor position
|
|
238
|
+
this.#currentCursorX = window.innerWidth / 2
|
|
239
|
+
this.#currentCursorY = window.innerHeight / 2
|
|
240
|
+
this.#targetCursorX = this.#currentCursorX
|
|
241
|
+
this.#targetCursorY = this.#currentCursorY
|
|
242
|
+
this.#cursor.style.left = `${this.#currentCursorX}px`
|
|
243
|
+
this.#cursor.style.top = `${this.#currentCursorY}px`
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
hide() {
|
|
247
|
+
if (!this.shown || this.#disposed) return
|
|
248
|
+
|
|
249
|
+
this.shown = false
|
|
250
|
+
this.motion?.fadeOut()
|
|
251
|
+
this.motion?.pause()
|
|
252
|
+
|
|
253
|
+
this.#cursor.classList.remove('clicking')
|
|
254
|
+
|
|
255
|
+
setTimeout(() => {
|
|
256
|
+
this.wrapper.classList.remove('visible')
|
|
257
|
+
}, 800) // Match the animation duration
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
dispose() {
|
|
261
|
+
this.#disposed = true
|
|
262
|
+
this.motion?.dispose()
|
|
263
|
+
this.wrapper.remove()
|
|
264
|
+
this.dispatchEvent(new Event('dispose'))
|
|
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
|
+
}
|
|
299
|
+
}
|