@opentiny/next-sdk 0.4.0 → 0.4.1

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.
Files changed (130) hide show
  1. package/agent/AgentModelProvider.ts +78 -81
  2. package/agent/type.ts +6 -9
  3. package/agent/utils/getAISDKTools.ts +0 -1
  4. package/agent/utils/getBuiltinMcpTools.ts +7 -7
  5. package/core.ts +0 -3
  6. package/dist/SimulatorMask-BHVXyogh-iNUb4dyF.js +361 -0
  7. package/dist/agent/type.d.ts +4 -12
  8. package/dist/agent/utils/getBuiltinMcpTools.d.ts +3 -3
  9. package/dist/core.d.ts +0 -1
  10. package/dist/core.js +16 -17
  11. package/dist/index-CXK8CcKq.js +6076 -0
  12. package/dist/index.d.ts +0 -3
  13. package/dist/index.js +57 -4969
  14. package/dist/{initialize-builtin-WebMCP-HgObT902.js → initialize-builtin-WebMCP-rALfqS-o.js} +1166 -1056
  15. package/dist/page-tools/a11y/build.d.ts +11 -0
  16. package/dist/page-tools/a11y/constants.d.ts +9 -0
  17. package/dist/page-tools/a11y/search.d.ts +17 -0
  18. package/dist/page-tools/a11y/types.d.ts +104 -0
  19. package/dist/page-tools/a11y/utils.d.ts +34 -0
  20. package/dist/page-tools/a11y/vnode.d.ts +37 -0
  21. package/dist/page-tools/a11y-tree.d.ts +8 -99
  22. package/dist/page-tools/constants.d.ts +22 -0
  23. package/dist/page-tools/context.d.ts +28 -0
  24. package/dist/page-tools/handlers/browserState.d.ts +8 -0
  25. package/dist/page-tools/handlers/click.d.ts +8 -0
  26. package/dist/page-tools/handlers/executeJavascript.d.ts +8 -0
  27. package/dist/page-tools/handlers/fill.d.ts +8 -0
  28. package/dist/page-tools/handlers/scroll.d.ts +8 -0
  29. package/dist/page-tools/handlers/searchTree.d.ts +8 -0
  30. package/dist/page-tools/handlers/select.d.ts +8 -0
  31. package/dist/page-tools/page-agent-highlight/index.d.ts +9 -0
  32. package/dist/page-tools/page-agent-mask/SimulatorMask.d.ts +14 -0
  33. package/dist/page-tools/page-agent-mask/checkDarkMode.d.ts +5 -0
  34. package/dist/page-tools/page-agent-tool.d.ts +3 -8
  35. package/dist/page-tools/schema.d.ts +43 -0
  36. package/dist/page-tools/utils/dom.d.ts +6 -0
  37. package/dist/page-tools/utils/scroll.d.ts +15 -0
  38. package/dist/runtime.d.ts +4 -0
  39. package/dist/runtime.js +744 -0
  40. package/dist/utils/builtinProxy.d.ts +1 -1
  41. package/index.ts +0 -5
  42. package/package.json +6 -17
  43. package/page-tools/a11y/build.ts +70 -0
  44. package/page-tools/a11y/constants.ts +110 -0
  45. package/page-tools/a11y/search.ts +129 -0
  46. package/page-tools/a11y/types.ts +108 -0
  47. package/page-tools/a11y/utils.ts +266 -0
  48. package/page-tools/a11y/vnode.ts +229 -0
  49. package/page-tools/a11y-tree.ts +8 -527
  50. package/page-tools/bridge.ts +1 -1
  51. package/page-tools/constants.ts +72 -0
  52. package/page-tools/context.ts +13 -0
  53. package/page-tools/handlers/browserState.ts +12 -0
  54. package/page-tools/handlers/click.ts +26 -0
  55. package/page-tools/handlers/executeJavascript.ts +22 -0
  56. package/page-tools/handlers/fill.ts +60 -0
  57. package/page-tools/handlers/scroll.ts +66 -0
  58. package/page-tools/handlers/searchTree.ts +30 -0
  59. package/page-tools/handlers/select.ts +34 -0
  60. package/page-tools/page-agent-highlight/index.ts +198 -0
  61. package/page-tools/page-agent-mask/SimulatorMask.module.css +14 -0
  62. package/page-tools/page-agent-mask/SimulatorMask.ts +279 -0
  63. package/page-tools/page-agent-mask/checkDarkMode.ts +181 -0
  64. package/page-tools/page-agent-mask/cursor-border.svg +3 -0
  65. package/page-tools/page-agent-mask/cursor-fill.svg +5 -0
  66. package/page-tools/page-agent-mask/cursor.module.css +70 -0
  67. package/page-tools/page-agent-prompt.md +34 -18
  68. package/page-tools/page-agent-tool.ts +104 -152
  69. package/page-tools/schema.ts +51 -0
  70. package/page-tools/utils/dom.ts +155 -0
  71. package/page-tools/utils/scroll.ts +58 -0
  72. package/runtime.ts +13 -0
  73. package/utils/builtinProxy.ts +45 -13
  74. package/vite.config.runtime.ts +22 -0
  75. package/vite.config.ts +52 -8
  76. package/McpSdk.ts +0 -14
  77. package/WebAgent.ts +0 -5
  78. package/WebMcp.ts +0 -26
  79. package/Zod.ts +0 -1
  80. package/dist/McpSdk.d.ts +0 -14
  81. package/dist/SimulatorMask-BHVXyogh-BFEGpD5S.js +0 -1048
  82. package/dist/SimulatorMask-BHVXyogh-CCYbrb84.js +0 -801
  83. package/dist/WebAgent.d.ts +0 -5
  84. package/dist/WebMcp.d.ts +0 -23
  85. package/dist/Zod.d.ts +0 -1
  86. package/dist/index.es.dev.js +0 -59017
  87. package/dist/index.es.js +0 -46795
  88. package/dist/index.umd.dev.js +0 -60355
  89. package/dist/index.umd.js +0 -1248
  90. package/dist/mcpsdk@1.25.3.dev.js +0 -22780
  91. package/dist/mcpsdk@1.25.3.es.dev.js +0 -22778
  92. package/dist/mcpsdk@1.25.3.es.js +0 -16960
  93. package/dist/mcpsdk@1.25.3.js +0 -48
  94. package/dist/transport/ExtensionClientTransport.d.ts +0 -24
  95. package/dist/transport/ExtensionContentServerTransport.d.ts +0 -39
  96. package/dist/transport/ExtensionPageServerTransport.d.ts +0 -36
  97. package/dist/transport/messages.d.ts +0 -9
  98. package/dist/vite.config.mcpSdk.d.ts +0 -2
  99. package/dist/vite.config.webAgent.d.ts +0 -2
  100. package/dist/vite.config.webMcp.d.ts +0 -2
  101. package/dist/vite.config.webMcpFull.d.ts +0 -2
  102. package/dist/vite.config.zod.d.ts +0 -2
  103. package/dist/webagent.dev.js +0 -49360
  104. package/dist/webagent.es.dev.js +0 -49071
  105. package/dist/webagent.es.js +0 -39219
  106. package/dist/webagent.js +0 -642
  107. package/dist/webmcp-full.dev.js +0 -31336
  108. package/dist/webmcp-full.es.dev.js +0 -30283
  109. package/dist/webmcp-full.es.js +0 -22889
  110. package/dist/webmcp-full.js +0 -645
  111. package/dist/webmcp.dev.js +0 -9572
  112. package/dist/webmcp.es.dev.js +0 -8518
  113. package/dist/webmcp.es.js +0 -6727
  114. package/dist/webmcp.js +0 -602
  115. package/dist/zod@3.25.76.dev.js +0 -4037
  116. package/dist/zod@3.25.76.es.dev.js +0 -4033
  117. package/dist/zod@3.25.76.es.js +0 -2945
  118. package/dist/zod@3.25.76.js +0 -1
  119. package/transport/ExtensionClientTransport.ts +0 -100
  120. package/transport/ExtensionContentServerTransport.ts +0 -162
  121. package/transport/ExtensionPageServerTransport.ts +0 -149
  122. package/transport/messages.ts +0 -63
  123. package/vite-build-tsc.ts +0 -63
  124. package/vite-env.d.ts +0 -10
  125. package/vite.config.mcpSdk.ts +0 -28
  126. package/vite.config.webAgent.ts +0 -19
  127. package/vite.config.webMcp.ts +0 -40
  128. package/vite.config.webMcpFull.ts +0 -19
  129. package/vite.config.zod.ts +0 -23
  130. /package/dist/{vite-build-tsc.d.ts → vite.config.runtime.d.ts} +0 -0
@@ -2,6 +2,6 @@ import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
2
2
 
3
3
  /**
4
4
  * 建立原生的 JSON-RPC 拦截层:
5
- * 将发向 remote 的 MCP 请求拦截,代理给所在浏览器的 navigator.modelContextTesting 上执行。
5
+ * 将发向 remote 的 MCP 请求拦截,代理给所在浏览器的 document.modelContext 上执行。
6
6
  */
7
7
  export declare const setupBuiltinProxy: (transport: Transport) => void;
package/index.ts CHANGED
@@ -21,11 +21,6 @@ export type * from '@modelcontextprotocol/sdk/server/mcp.js'
21
21
  export * from './WebMcpServer'
22
22
  export * from './WebMcpClient'
23
23
 
24
- // 浏览器扩展自定义传输层
25
- export * from './transport/ExtensionClientTransport'
26
- export * from './transport/ExtensionPageServerTransport'
27
- export * from './transport/ExtensionContentServerTransport'
28
-
29
24
  // 快速创建一个悬浮图标和菜单,是扫码和聊天框的入口
30
25
  export * from './remoter/createRemoter'
31
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentiny/next-sdk",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.opentiny.design/next-sdk/guide/",
6
6
  "repository": {
@@ -41,7 +41,7 @@
41
41
  "@ai-sdk/provider": "^3.0.0",
42
42
  "@ai-sdk/mcp": "^1.0.0",
43
43
  "@mcp-b/webmcp-polyfill": "^3.0.0",
44
- "@page-agent/page-controller": "^1.9.0",
44
+ "@page-agent/page-controller": "^1.10.0",
45
45
  "qrcode": "^1.5.4",
46
46
  "zod": "^3.25.76",
47
47
  "zod-to-json-schema": "^3.25.2",
@@ -50,7 +50,8 @@
50
50
  "dom-accessibility-api": "^0.6.3",
51
51
  "aria-query": "^5.3.2",
52
52
  "tabbable": "^6.2.0",
53
- "diff": "^7.0.0"
53
+ "diff": "^7.0.0",
54
+ "ai-motion": "^0.4.8"
54
55
  },
55
56
  "devDependencies": {
56
57
  "typescript": "~5.9.3",
@@ -64,19 +65,7 @@
64
65
  "access": "public"
65
66
  },
66
67
  "scripts": {
67
- "build:all": "pnpm build:cdn && pnpm build:cdn:dev && pnpm build",
68
- "build": "vite build --config vite-build-tsc.ts",
69
- "build:cdn": "vite build && pnpm build:webAgent && pnpm build:webMcp && pnpm build:mcpSdk && pnpm build:zod && pnpm build:webMcpFull",
70
- "build:cdn:dev": "vite build --mode dev && pnpm build:webAgent:dev && pnpm build:webMcp:dev && pnpm build:mcpSdk:dev && pnpm build:zod:dev && pnpm build:webMcpFull:dev",
71
- "build:webAgent": "vite build --config vite.config.webAgent.ts",
72
- "build:webAgent:dev": "vite build --config vite.config.webAgent.ts --mode dev",
73
- "build:webMcp": "vite build --config vite.config.webMcp.ts",
74
- "build:webMcp:dev": "vite build --config vite.config.webMcp.ts --mode dev",
75
- "build:mcpSdk": "vite build --config vite.config.mcpSdk.ts",
76
- "build:mcpSdk:dev": "vite build --config vite.config.mcpSdk.ts --mode dev",
77
- "build:zod": "vite build --config vite.config.zod.ts",
78
- "build:zod:dev": "vite build --config vite.config.zod.ts --mode dev",
79
- "build:webMcpFull": "vite build --config vite.config.webMcpFull.ts",
80
- "build:webMcpFull:dev": "vite build --config vite.config.webMcpFull.ts --mode dev"
68
+ "build": "vite build && vite build --config vite.config.runtime.ts",
69
+ "build:runtime": "vite build --config vite.config.runtime.ts"
81
70
  }
82
71
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * a11y/build.ts
3
+ *
4
+ * 暴露生成整棵无障碍树的主逻辑方法。
5
+ */
6
+
7
+ import type { A11yTreeOptions, A11yTreeResult, RefMap } from './types'
8
+ import { DEFAULT_ERROR_SELECTORS, DEFAULT_WARNING_SELECTORS } from './constants'
9
+ import { buildVNode, serializeVNode } from './vnode'
10
+ import { getComposedChildren } from './utils'
11
+ import { HIGHLIGHT_CONTAINER_ID } from '../page-agent-highlight'
12
+
13
+ const DEFAULT_OPTIONS: Required<A11yTreeOptions> = {
14
+ pruneUnnamed: true,
15
+ preserveRoles: [],
16
+ exposedAttributes: [],
17
+ errorSelectors: DEFAULT_ERROR_SELECTORS,
18
+ warningSelectors: DEFAULT_WARNING_SELECTORS
19
+ }
20
+
21
+ /**
22
+ * 生成当前页面的语义化 ARIA YAML 树
23
+ *
24
+ * @param root 遍历起点,默认 document.body
25
+ * @param blacklist 需要跳过的元素(用户自定义黑名单)
26
+ * @param whitelist 需要识别为可交互的白名单元素列表
27
+ * @param options 过滤选项
28
+ */
29
+ export function buildA11yTree(
30
+ root: Element = document.body,
31
+ blacklist: Element[] = [],
32
+ whitelist: Element[] = [],
33
+ options?: A11yTreeOptions
34
+ ): A11yTreeResult {
35
+ const opts: Required<A11yTreeOptions> = { ...DEFAULT_OPTIONS, ...options }
36
+ // 使用对象引用避免全局可变状态,消除并发调用隐患
37
+ const refCounter = { value: 0 }
38
+ const refMap: RefMap = new Map()
39
+ const blacklistSet = new Set(blacklist)
40
+ const whitelistSet = new Set(whitelist)
41
+ const lines: string[] = []
42
+
43
+ // 永远过滤高亮容器
44
+ blacklistSet.add(document.getElementById(HIGHLIGHT_CONTAINER_ID)!)
45
+
46
+ for (const child of getComposedChildren(root)) {
47
+ const vnode = buildVNode(
48
+ child,
49
+ refCounter,
50
+ refMap,
51
+ blacklistSet,
52
+ whitelistSet,
53
+ opts.exposedAttributes,
54
+ opts.errorSelectors,
55
+ opts.warningSelectors
56
+ )
57
+ if (vnode) {
58
+ lines.push(...serializeVNode(vnode, 0, opts))
59
+ }
60
+ }
61
+
62
+ const yaml = '```yaml\n' + lines.join('\n') + '\n```'
63
+
64
+ return {
65
+ yaml,
66
+ refMap,
67
+ interactiveCount: refMap.size,
68
+ lines
69
+ }
70
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * a11y/constants.ts
3
+ *
4
+ * 存放无障碍树依赖的静态常量配置、默认选择器及角色映射表。
5
+ */
6
+
7
+ // ─── 默认校验错误/警告选择器(ARIA 标准 + 主流 UI 框架) ──────────────────
8
+
9
+ export const DEFAULT_ERROR_SELECTORS = [
10
+ '[role="alert"]', '[aria-invalid="true"]',
11
+ '.ti3-unifyvalid-error', '.ti3-error', '.ti-error',
12
+ '.lego-text-error', '.lego-error',
13
+ '.el-form-item__error',
14
+ '.ant-form-item-explain-error',
15
+ '.is-invalid', '.invalid-feedback',
16
+ '.ng-invalid',
17
+ '.error-msg', '.error-message', '.error-text',
18
+ '.field-error', '.form-error',
19
+ '.is-error', '.has-error',
20
+ '.validate-error', '.valid-error',
21
+ ]
22
+
23
+ export const DEFAULT_WARNING_SELECTORS = [
24
+ '.ti3-warning', '.ti-warning', '.lego-text-warning',
25
+ '.warning-msg', '.warning-text', '.is-warning', '.has-warning',
26
+ ]
27
+
28
+ // ─── ARIA 隐式角色静态映射表(覆盖页面 95%+ 的常用标签)───────────────────────
29
+
30
+ export const TAG_ROLE_MAP: Record<string, string> = {
31
+ a: 'link',
32
+ article: 'article',
33
+ aside: 'complementary',
34
+ button: 'button',
35
+ caption: 'caption',
36
+ cell: 'cell',
37
+ checkbox: 'checkbox',
38
+ code: 'code',
39
+ columnheader: 'columnheader',
40
+ combobox: 'combobox',
41
+ datalist: 'listbox',
42
+ dd: 'definition',
43
+ details: 'group',
44
+ dialog: 'dialog',
45
+ dt: 'term',
46
+ em: 'emphasis',
47
+ fieldset: 'group',
48
+ figure: 'figure',
49
+ footer: 'contentinfo',
50
+ form: 'form',
51
+ h1: 'heading',
52
+ h2: 'heading',
53
+ h3: 'heading',
54
+ h4: 'heading',
55
+ h5: 'heading',
56
+ h6: 'heading',
57
+ header: 'banner',
58
+ hr: 'separator',
59
+ img: 'img',
60
+ input: 'textbox', // 默认,具体 type 下面会覆盖
61
+ li: 'listitem',
62
+ link: 'link',
63
+ main: 'main',
64
+ mark: 'mark',
65
+ math: 'math',
66
+ menu: 'list',
67
+ menuitem: 'menuitem',
68
+ meter: 'meter',
69
+ nav: 'navigation',
70
+ ol: 'list',
71
+ option: 'option',
72
+ output: 'status',
73
+ p: 'paragraph',
74
+ progress: 'progressbar',
75
+ rowheader: 'rowheader',
76
+ search: 'search',
77
+ section: 'region',
78
+ select: 'listbox',
79
+ strong: 'strong',
80
+ summary: 'button',
81
+ table: 'table',
82
+ tbody: 'rowgroup',
83
+ td: 'cell',
84
+ textarea: 'textbox',
85
+ tfoot: 'rowgroup',
86
+ th: 'columnheader',
87
+ thead: 'rowgroup',
88
+ time: 'time',
89
+ tr: 'row',
90
+ ul: 'list',
91
+ }
92
+
93
+ // input[type=*] 的角色覆盖
94
+ export const INPUT_TYPE_ROLE: Record<string, string> = {
95
+ button: 'button',
96
+ checkbox: 'checkbox',
97
+ color: 'textbox',
98
+ email: 'textbox',
99
+ file: 'textbox',
100
+ image: 'button',
101
+ number: 'spinbutton',
102
+ radio: 'radio',
103
+ range: 'slider',
104
+ reset: 'button',
105
+ search: 'searchbox',
106
+ submit: 'button',
107
+ tel: 'textbox',
108
+ text: 'textbox',
109
+ url: 'textbox',
110
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * a11y/search.ts
3
+ *
4
+ * 在无障碍树内执行基于文本或引用的搜索操作。
5
+ */
6
+
7
+ import type { SearchA11yTreeOptions, SearchA11yTreeResult, A11ySearchMatch } from './types'
8
+ import { buildA11yTree } from './build'
9
+
10
+ /**
11
+ * 在无障碍树中按关键词搜索,返回带行号的匹配结果和上下文
12
+ *
13
+ * 支持的搜索维度(均对同一个 query 字符串做包含匹配):
14
+ * - role:如 `button`、`link`、`heading`
15
+ * - accessible name:节点的语义化名称(引号内文本)
16
+ * - state token:如 `checked`、`disabled`、`expanded`
17
+ * - ref 索引:如 `#5`
18
+ *
19
+ * @example
20
+ * searchA11yTree('button') // 找全部按钮
21
+ * searchA11yTree('提交') // 找名称含"提交"的节点
22
+ * searchA11yTree('#3') // 找 ref #3
23
+ */
24
+ export function searchA11yTree(
25
+ query: string,
26
+ root: Element = document.body,
27
+ blacklist: Element[] = [],
28
+ whitelist: Element[] = [],
29
+ options?: SearchA11yTreeOptions,
30
+ ): SearchA11yTreeResult {
31
+ const {
32
+ contextLines = 2,
33
+ caseInsensitive = true,
34
+ maxMatches = 20,
35
+ ...treeOptions
36
+ } = options ?? {}
37
+
38
+ const safeContextLines = Math.max(0, contextLines)
39
+ const safeMaxMatches = Math.max(1, maxMatches)
40
+
41
+ // 复用 buildA11yTree 生成完整树,直接取 lines 数组(不重复构建 DOM 遍历)
42
+ const { lines, refMap, yaml } = buildA11yTree(root, blacklist, whitelist, treeOptions)
43
+
44
+ const needle = caseInsensitive ? query.toLowerCase() : query
45
+ const totalLines = lines.length
46
+
47
+ const isRefQuery = /^#\d+$/.test(query)
48
+ const refRegex = isRefQuery ? new RegExp(`\\s${query}(?:\\s|[\\[]|$)`) : null
49
+
50
+ // 找出所有命中行的下标(0-based)
51
+ const hitIndices: number[] = []
52
+ for (let i = 0; i < lines.length; i++) {
53
+ let matched = false
54
+ if (refRegex) {
55
+ matched = refRegex.test(lines[i])
56
+ } else {
57
+ const haystack = caseInsensitive ? lines[i].toLowerCase() : lines[i]
58
+ matched = haystack.includes(needle)
59
+ }
60
+ if (matched) {
61
+ hitIndices.push(i)
62
+ }
63
+ }
64
+
65
+ // 合并重叠的上下文区间,避免重复输出行
66
+ const mergedRanges: Array<{ start: number; end: number; hits: number[] }> = []
67
+ let isTruncated = false
68
+ for (const idx of hitIndices) {
69
+ const start = Math.max(0, idx - safeContextLines)
70
+ const end = Math.min(totalLines - 1, idx + safeContextLines)
71
+ const last = mergedRanges[mergedRanges.length - 1]
72
+ if (last && start <= last.end + 1) {
73
+ // 区间重叠或紧邻,合并
74
+ last.end = Math.max(last.end, end)
75
+ last.hits.push(idx)
76
+ } else {
77
+ if (mergedRanges.length >= safeMaxMatches) {
78
+ isTruncated = true
79
+ break
80
+ }
81
+ mergedRanges.push({ start, end, hits: [idx] })
82
+ }
83
+ }
84
+
85
+ // 构建结构化结果
86
+ const matches: A11ySearchMatch[] = mergedRanges.map((range) => ({
87
+ lineNumber: range.hits[0] + 1,
88
+ line: lines[range.hits[0]],
89
+ context: Array.from({ length: range.end - range.start + 1 }, (_, k) => ({
90
+ lineNumber: range.start + k + 1,
91
+ line: lines[range.start + k],
92
+ })),
93
+ }))
94
+
95
+ // 格式化为可读文本(模仿 grep -n -C 风格,便于 LLM 直接理解)
96
+ const textParts: string[] = [
97
+ `无障碍树搜索结果 — 关键词: "${query}" | 总行数: ${totalLines} | 命中: ${hitIndices.length} 行 | 返回分组: ${matches.length}`,
98
+ '',
99
+ ]
100
+
101
+ if (matches.length === 0) {
102
+ textParts.push('(未找到匹配项)')
103
+ } else {
104
+ matches.forEach((match, m) => {
105
+ const range = mergedRanges[m]
106
+ textParts.push(`── 分组 ${m + 1}(第 ${range.start + 1}–${range.end + 1} 行)──`)
107
+ match.context.forEach(({ lineNumber, line }) => {
108
+ const isHit = range.hits.includes(lineNumber - 1)
109
+ const ln = String(lineNumber).padStart(4)
110
+ // 命中行用 >>> 标记,上下文行用普通行号前缀
111
+ textParts.push(isHit ? `>>>${ln} | ${line}` : ` ${ln} | ${line}`)
112
+ })
113
+ textParts.push('')
114
+ })
115
+ if (isTruncated) {
116
+ textParts.push(`⚠️ 命中过多,已截断至前 ${safeMaxMatches} 个分组,建议缩小搜索范围`)
117
+ }
118
+ textParts.push(`提示:如需操作命中元素,使用其 #N 索引;如需查看完整树,请使用 browserState。`)
119
+ }
120
+
121
+ return {
122
+ text: textParts.join('\n'),
123
+ matches,
124
+ totalLines,
125
+ matchCount: hitIndices.length,
126
+ refMap,
127
+ yaml,
128
+ }
129
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * a11y/types.ts
3
+ *
4
+ * 定义无障碍树模块(A11y Tree)的核心接口和类型。
5
+ */
6
+
7
+ /** ref 索引 → HTMLElement 映射,供 click/fill/select 操作使用 */
8
+ export type RefMap = Map<number, HTMLElement>
9
+
10
+ /** 内部中间态节点,与 DOM 解耦,便于剪枝和序列化 */
11
+ export interface VNode {
12
+ /** 节点的 ARIA 角色 (如 'button', 'link', 'textbox') */
13
+ role: string
14
+ /** W3C AccName 算法计算出的语义化名称 */
15
+ name: string
16
+ /** 元素相关的状态和属性标记 (如 'checked', 'disabled', 'value="xxx"') */
17
+ tokens: string[]
18
+ /** 只有交互节点才有 ref (内部索引,用于标识可在其上执行交互动作的元素) */
19
+ ref?: number
20
+ /** 关联的 DOM 元素实例 */
21
+ el: HTMLElement
22
+ /** 包含的子节点列表 */
23
+ children: VNode[]
24
+ }
25
+
26
+ /** 构建无障碍树时的配置选项 */
27
+ export interface A11yTreeOptions {
28
+ /**
29
+ * 是否启用剪枝:无 ref 且无 accessible name 的节点透明穿透
30
+ * 默认 true(推荐)
31
+ */
32
+ pruneUnnamed?: boolean
33
+ /**
34
+ * 强制保留的角色列表,即使无 name 也不穿透(优先级最高)
35
+ * 例如:['table', 'row'] 用于保留表格结构
36
+ */
37
+ preserveRoles?: string[]
38
+ /**
39
+ * 允许在无障碍树节点中作为 token 额外输出的 DOM 属性白名单
40
+ * 包含这些属性的节点会被自动视为需要暴露/交互的节点(分配 ref 并保留),
41
+ * 且属性及其值会显示在节点的 token 列表中,如 [cf-uba="cloudShell"]
42
+ */
43
+ exposedAttributes?: string[]
44
+ /**
45
+ * 校验错误元素 CSS 选择器(逗号分隔或选择器数组),用于在 token 中标记 [error]
46
+ * 默认覆盖 ARIA 标准 + 主流 UI 框架
47
+ */
48
+ errorSelectors?: string | string[]
49
+ /**
50
+ * 校验警告元素 CSS 选择器(逗号分隔或选择器数组)
51
+ */
52
+ warningSelectors?: string | string[]
53
+ }
54
+
55
+ /** 构建无障碍树的返回结果 */
56
+ export interface A11yTreeResult {
57
+ /** 语义化 YAML 文本(供 AI 阅读和 Diff 计算) */
58
+ yaml: string
59
+ /** ref 索引 → HTMLElement 映射(供后续操作使用) */
60
+ refMap: RefMap
61
+ /** 可交互元素总数 */
62
+ interactiveCount: number
63
+ /** 原始行数组(不含 yaml 代码块包裹),供搜索使用 */
64
+ lines: string[]
65
+ }
66
+
67
+ /** 关键词搜索选项 */
68
+ export interface SearchA11yTreeOptions extends A11yTreeOptions {
69
+ /**
70
+ * 每个匹配行前后保留的上下文行数(类似 grep -C N)
71
+ * 默认 2
72
+ */
73
+ contextLines?: number
74
+ /**
75
+ * 是否大小写不敏感,默认 true
76
+ */
77
+ caseInsensitive?: boolean
78
+ /**
79
+ * 最大返回匹配分组数(防止命中过多撑爆上下文),默认 20
80
+ */
81
+ maxMatches?: number
82
+ }
83
+
84
+ /** 单个搜索匹配的分组结构 */
85
+ export interface A11ySearchMatch {
86
+ /** 主命中行行号(1-based) */
87
+ lineNumber: number
88
+ /** 主命中行内容 */
89
+ line: string
90
+ /** 含上下文的行列表(带行号) */
91
+ context: Array<{ lineNumber: number; line: string }>
92
+ }
93
+
94
+ /** searchA11yTree 函数的返回结果 */
95
+ export interface SearchA11yTreeResult {
96
+ /** 格式化后可直接发给 LLM 的文本 */
97
+ text: string
98
+ /** 结构化匹配列表 */
99
+ matches: A11ySearchMatch[]
100
+ /** 无障碍树总行数 */
101
+ totalLines: number
102
+ /** 原始命中行数(去重前) */
103
+ matchCount: number
104
+ /** 返回搜索时的 ref 映射 */
105
+ refMap: RefMap
106
+ /** 返回搜索时的 yaml 状态,用于更新缓存 */
107
+ yaml: string
108
+ }