framewatch-mcp-server 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -1,10 +1,94 @@
1
1
  # FrameWatch MCP Server
2
2
 
3
- > An MCP server that gives AI coding agents visual eyes — smart frame capture, diffing, and interaction replay for web apps.
3
+ **[framewatch.keko.dev](https://framewatch.keko.dev)** · [npm](https://www.npmjs.com/package/framewatch-mcp-server) · MIT
4
4
 
5
- An AI coding agent can write a CSS transition but not watch it run. It can read the DOM but not see that the modal opened behind the header. FrameWatch closes that gap: it launches a real (headless Chromium) browser and returns what it sees as images an MCP client such as Claude Code looks at natively — alongside the console output, network requests, DOM mutations and paint timings from the same moment.
5
+ > Give your AI coding agent eyes. FrameWatch opens your web app in a real browser and shows Claude Code what it looks like, what changed, and why.
6
6
 
7
- The problem with screenshots as a debugging tool is volume. A five-second recording at 10fps is fifty near-identical images, and fifty images is a flooded context window. So FrameWatch records everything and returns almost none of it: only the frames where something meaningful actually changed, each cropped to the region that changed, each carrying the context that explains why.
7
+ You ask Claude Code to fix the login page. It edits the code and says "done". Was it? Without FrameWatch, neither of you knows until you open the browser yourself. With it, Claude Code opens the page, sees the result, reads the console, checks the spacing and the colours, clicks the button, and fixes what it got wrong before it tells you it is done.
8
+
9
+ FrameWatch is an [MCP](https://modelcontextprotocol.io) server. Once registered, Claude Code gets 18 tools it calls on its own: screenshots, recordings that keep only the frames where something changed, a way to name and click every element on a page, measurements of how each element is built, form and link and accessibility checks, and a Vue-aware wait that returns the moment your dev server has hot-reloaded your edit.
10
+
11
+ ## Quick start
12
+
13
+ **1. Register it with Claude Code** (one command, no install step):
14
+
15
+ ```bash
16
+ claude mcp add framewatch npx framewatch-mcp-server
17
+ ```
18
+
19
+ **2. Let it see full results.** Claude Code caps a tool result at 25,000 tokens and counts images toward it, which holds about two screenshots of a real page. Raise it once, in the shell you start Claude Code from:
20
+
21
+ ```bash
22
+ export MAX_MCP_OUTPUT_TOKENS=100000
23
+ ```
24
+
25
+ Add that line to your `~/.zshrc` or `~/.bashrc` to make it permanent.
26
+
27
+ **3. Start your app and ask.** Run your dev server as usual, open Claude Code in the project, and talk to it normally:
28
+
29
+ > Open http://localhost:5173 and tell me what you see.
30
+
31
+ > The header looks wrong on mobile. Check it at phone width and fix it.
32
+
33
+ > Test the login flow with test@example.com / password123 and show me what happens.
34
+
35
+ > I just changed the button styles — show me the page after the hot reload and check the contrast.
36
+
37
+ Claude Code picks the tools itself. You never call them by name, though you can: "use framewatch_dead_clicks on the settings page" works too.
38
+
39
+ **4. Sign in once, if your app has a login.** Ask Claude Code to log in and save the session:
40
+
41
+ > Log in at http://localhost:5173/login with my test account and save the session.
42
+
43
+ It runs `framewatch_save_auth`, which writes `.framewatch/auth.json`. From then on every tool opens pages already signed in, and says so.
44
+
45
+ That is the whole setup. The first tool call downloads Chromium if Playwright has not already; if that fails, run `npx playwright install chromium` once.
46
+
47
+ ### Other MCP clients
48
+
49
+ FrameWatch is a standard MCP server over stdio, so every agent that can run one takes the same command: `npx -y framewatch-mcp-server`. Where it goes:
50
+
51
+ | Agent | Where | Shape |
52
+ | --- | --- | --- |
53
+ | Claude Code | `claude mcp add framewatch -- npx -y framewatch-mcp-server` | one command; add `--scope user` for every project |
54
+ | Cursor | `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` | `mcpServers` |
55
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` |
56
+ | VS Code (Copilot agent mode) | `.vscode/mcp.json` | `servers`, with `"type": "stdio"` |
57
+ | Codex CLI | `~/.codex/config.toml` | `[mcp_servers.framewatch]` |
58
+ | Gemini CLI | `~/.gemini/settings.json` | `mcpServers` |
59
+ | Claude Desktop | `claude_desktop_config.json` | `mcpServers` |
60
+ | Cline | `cline_mcp_settings.json` via MCP Servers, Configure | `mcpServers` |
61
+ | Continue | `~/.continue/config.yaml` | `mcpServers:` list |
62
+ | Zed | `settings.json` | `context_servers`, `"source": "custom"` |
63
+ | JetBrains AI Assistant | Settings, Tools, AI Assistant, MCP, Add | `mcpServers` |
64
+
65
+ The `mcpServers` shape most of them share:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "framewatch": { "command": "npx", "args": ["-y", "framewatch-mcp-server"] }
71
+ }
72
+ }
73
+ ```
74
+
75
+ The `MAX_MCP_OUTPUT_TOKENS` cap is specific to Claude Code; other clients have their own limits or none. The exact snippet for each agent, ready to paste, is on [framewatch.keko.dev](https://framewatch.keko.dev/#install).
76
+
77
+ ### From a checkout
78
+
79
+ ```bash
80
+ git clone https://github.com/kekoDev/framewatch.git
81
+ cd framewatch && npm install && npm run build
82
+ claude mcp add framewatch node "$PWD/dist/index.js"
83
+ ```
84
+
85
+ ### Requirements
86
+
87
+ - **Node 20.9 or newer.**
88
+ - **Chromium**, fetched by Playwright on install. If a tool ever reports the browser is missing, it prints the one command that fixes it.
89
+ - Nothing listens on a port and nothing phones home: FrameWatch only ever talks to the URLs it is given.
90
+
91
+ ## What Claude Code can do with it
8
92
 
9
93
  | Tool | Use it for |
10
94
  | --- | --- |
@@ -26,25 +110,9 @@ The problem with screenshots as a debugging tool is volume. A five-second record
26
110
  | [`framewatch_save_auth`](#framewatch_save_auth) | Sign in once, so every other tool starts past the login. |
27
111
  | [`framewatch_start_server`](#framewatch_start_server--framewatch_stop_server) / [`framewatch_stop_server`](#framewatch_start_server--framewatch_stop_server) | Get the dev server up so there is something to point at. |
28
112
 
29
- ## Install
30
-
31
- ```bash
32
- # Run directly
33
- npx framewatch-mcp-server
34
-
35
- # Register with Claude Code
36
- claude mcp add framewatch npx framewatch-mcp-server
37
- ```
38
-
39
- From a checkout:
40
-
41
- ```bash
42
- npm install
43
- npm run build
44
- claude mcp add framewatch node /absolute/path/to/framewatch/dist/index.js
45
- ```
113
+ Why not just screenshots? Volume. A five-second recording at 10fps is fifty near-identical images, and fifty images is a flooded context window. FrameWatch records everything and returns almost none of it: only the frames where something meaningful changed, each cropped to the region that changed, each carrying the console output, network requests and DOM changes from the same moment — the context that explains why.
46
114
 
47
- ### Result size and `MAX_MCP_OUTPUT_TOKENS`
115
+ ## Why `MAX_MCP_OUTPUT_TOKENS` matters
48
116
 
49
117
  Claude Code caps one MCP tool result at 25,000 tokens by default and counts base64 image data toward it. A result over the cap is written to a file and replaced with a reference, so the model sees **no images at all**. A single screenshot of a real page is 180 KB of PNG, which is over on its own.
50
118
 
@@ -54,22 +122,7 @@ FrameWatch never lets a result cross that line. Every image goes out in the chea
54
122
  Image budget: 4 of 9 images kept — 5 crops dropped, frames at 640px — to fit MAX_MCP_OUTPUT_TOKENS=25000 (~52 KB of images per result). Set MAX_MCP_OUTPUT_TOKENS=100000 in the shell that starts Claude Code for full results.
55
123
  ```
56
124
 
57
- The default cap holds one or two frames of a real page. For capture-heavy work, raise it once:
58
-
59
- ```bash
60
- export MAX_MCP_OUTPUT_TOKENS=100000
61
- claude
62
- ```
63
-
64
- The server reads the same variable and sizes its budget to it.
65
-
66
- ### Requirements
67
-
68
- - **Node 20.9+**.
69
- - **Chromium**, via Playwright. `npm install` normally fetches it; if it did not, run `npx playwright install chromium` once. Every tool detects a missing browser and answers with that exact command rather than a stack trace.
70
- - **No network access needed** beyond whatever the pages under test require — FrameWatch only ever talks to the URLs it is given.
71
-
72
- FrameWatch speaks stdio only, and the client spawns it: nothing listens on a port, and there is nothing to configure beyond the line above.
125
+ The default cap holds one or two frames of a real page, which is why step 2 of the quick start raises it. The server reads the same variable and sizes its budget to it.
73
126
 
74
127
  ## Tools
75
128
 
@@ -1 +1 @@
1
- {"version":3,"file":"inspect.js","sourceRoot":"","sources":["../../src/engine/inspect.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Locator, Page } from \"playwright\";\nimport {\n MAX_INSPECT_TEXT_LENGTH,\n MAX_INVENTORY_ELEMENTS,\n MAX_INVENTORY_VALUES,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { isRef, refSelector } from \"../utils/snapshot-rules.js\";\nimport { parseColour, toHex, type ElementMeasurement, type PageInventory, type Tally } from \"../utils/style-rules.js\";\nimport type { ComponentInfo, ComponentUnavailable } from \"../utils/vue-rules.js\";\nimport { componentOf } from \"./vue.js\";\n\n/**\n * How an element is built, measured inside the page.\n *\n * A screenshot shows what a button looks like; this says what it *is*: the\n * box in viewport pixels, the font, the colours it actually renders in (a\n * transparent background resolved through its ancestors), the spacing, and\n * where it sits against its parent and the sibling before it. That is what an\n * agent needs to check its own work against the design it meant to build.\n *\n * Every number is read in one `evaluate` per element and judged in\n * `style-rules.ts`, which never sees a browser.\n */\n\nexport interface InspectResult {\n target: string;\n measurement?: ElementMeasurement;\n /** Why there is no measurement: the target resolved to nothing. */\n error?: string;\n /** The Vue component behind the element, when asked for and present. */\n component?: ComponentInfo | ComponentUnavailable;\n}\n\nexport interface MeasureOptions {\n /** Also read the Vue component that rendered each element. */\n components?: boolean;\n}\n\n/** Measure each target — a snapshot ref (`e8`) or a CSS selector — in order. */\nexport async function measureElements(page: Page, targets: readonly string[], options: MeasureOptions = {}): Promise<InspectResult[]> {\n const results: InspectResult[] = [];\n for (const target of targets) {\n const result = await measureOne(page, target);\n if (options.components && result.measurement) {\n const component = await componentOf(resolve(page, target));\n if (component) result.component = component;\n }\n results.push(result);\n }\n return results;\n}\n\nasync function measureOne(page: Page, target: string): Promise<InspectResult> {\n const locator = resolve(page, target);\n let count: number;\n try {\n count = await locator.count();\n } catch (error) {\n return { target, error: `could not be resolved: ${firstLine(error)}` };\n }\n if (count === 0) {\n return {\n target,\n error: isRef(target)\n ? \"did not resolve — the page has changed since that snapshot, or none was taken; run framewatch_snapshot again\"\n : \"matched nothing\",\n };\n }\n try {\n const raw = await locator.evaluate(measureInPage, { max_text: MAX_INSPECT_TEXT_LENGTH }, { timeout: SELECTOR_TIMEOUT_MS });\n return { target, measurement: { target, ...raw } };\n } catch (error) {\n return { target, error: `could not be measured: ${firstLine(error)}` };\n }\n}\n\nfunction resolve(page: Page, target: string): Locator {\n return isRef(target) ? page.locator(refSelector(target)) : page.locator(target).first();\n}\n\nfunction firstLine(error: unknown): string {\n return (error instanceof Error ? error.message : String(error)).split(\"\\n\")[0];\n}\n\n/* ── Inventory ────────────────────────────────────────────────────────── */\n\n/** Every font, size, weight, colour, spacing value and radius the page (or `selector`) uses. */\nexport async function inventoryPage(page: Page, selector?: string): Promise<PageInventory> {\n const raw = await page.evaluate(inventoryInPage, { selector: selector ?? null, max_elements: MAX_INVENTORY_ELEMENTS });\n const tally = (map: Record<string, number>, normalise: (value: string) => string | null = (v) => v): Tally[] => {\n const merged = new Map<string, number>();\n for (const [value, count] of Object.entries(map)) {\n const key = normalise(value);\n if (key === null) continue;\n merged.set(key, (merged.get(key) ?? 0) + count);\n }\n return [...merged.entries()]\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value))\n .slice(0, MAX_INVENTORY_VALUES);\n };\n const hex = (value: string): string | null => {\n const colour = parseColour(value);\n if (!colour || colour.a === 0) return null;\n return toHex(colour);\n };\n return {\n elements: raw.elements,\n text_elements: raw.text_elements,\n fonts: tally(raw.fonts),\n font_sizes: tally(raw.font_sizes),\n font_weights: tally(raw.font_weights),\n text_colours: tally(raw.text_colours, hex),\n backgrounds: tally(raw.backgrounds, hex),\n spacing: tally(raw.spacing),\n radii: tally(raw.radii),\n };\n}\n\n/* ── In-page ──────────────────────────────────────────────────────────────\n * Everything below runs inside Chromium. Written against `globalThis` and\n * untyped nodes because this package is compiled with the Node lib only —\n * and nothing here may close over a module value: the function body is\n * serialised and run where the module does not exist (CLAUDE.md, decision 50).\n */\n\ntype RawMeasurement = Omit<ElementMeasurement, \"target\">;\n\nfunction measureInPage(element: any, config: { max_text: number }): RawMeasurement {\n const g = globalThis as any;\n const win = g.window;\n const doc = g.document;\n\n const px = (value: string): number => {\n const n = parseFloat(value);\n return Number.isFinite(n) ? Math.round(n * 100) / 100 : 0;\n };\n const rect = (node: any): { x: number; y: number; width: number; height: number } => {\n const r = node.getBoundingClientRect();\n return { x: Math.round(r.left), y: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) };\n };\n\n const style = win.getComputedStyle(element);\n const box = rect(element);\n\n // Effective background: composite this element's own over each ancestor's\n // until one is opaque. Done here as a list of layers, blended in Node.\n const layers: string[] = [];\n let node: any = element;\n while (node && node.nodeType === 1) {\n const bg = win.getComputedStyle(node).backgroundColor;\n layers.push(bg);\n if (/^rgb\\(/.test(bg)) break;\n node = node.parentElement;\n }\n const background = blend(layers);\n\n // Clipped by an ancestor whose overflow is not visible.\n let clipped = false;\n let ancestor: any = element.parentElement;\n while (ancestor && ancestor !== doc.documentElement) {\n const s = win.getComputedStyle(ancestor);\n if (s.overflow !== \"visible\" || s.overflowX !== \"visible\" || s.overflowY !== \"visible\") {\n const a = ancestor.getBoundingClientRect();\n const r = element.getBoundingClientRect();\n if (r.left < a.left - 0.5 || r.right > a.right + 0.5 || r.top < a.top - 0.5 || r.bottom > a.bottom + 0.5) {\n clipped = true;\n break;\n }\n }\n ancestor = ancestor.parentElement;\n }\n\n const vw = win.innerWidth;\n const vh = win.innerHeight;\n const r = element.getBoundingClientRect();\n const inside = r.left >= 0 && r.top >= 0 && r.right <= vw && r.bottom <= vh;\n const outside = r.right <= 0 || r.bottom <= 0 || r.left >= vw || r.top >= vh;\n const in_viewport: \"fully\" | \"partially\" | \"none\" = inside ? \"fully\" : outside ? \"none\" : \"partially\";\n\n const opacity = px(style.opacity);\n const visible =\n style.display !== \"none\" && style.visibility !== \"hidden\" && opacity > 0 && r.width > 0 && r.height > 0;\n\n const text_overflows = element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1;\n\n const ownText = (): string => {\n let out = \"\";\n const children = element.childNodes;\n for (let i = 0; i < children.length; i++) {\n if (children[i].nodeType === 3) out += children[i].nodeValue;\n }\n out = out.replace(/\\s+/g, \" \").trim();\n if (out === \"\" && typeof element.value === \"string\") out = element.value;\n if (out === \"\") out = String(element.textContent || \"\").replace(/\\s+/g, \" \").trim();\n return out.length > config.max_text ? out.slice(0, config.max_text) + \"…\" : out;\n };\n\n const accessibleName = (): string | undefined => {\n const label = element.getAttribute(\"aria-label\");\n if (label) return String(label).trim();\n const labelledBy = element.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n const names = String(labelledBy)\n .split(/\\s+/)\n .map((id) => doc.getElementById(id))\n .filter(Boolean)\n .map((n: any) => String(n.textContent || \"\").replace(/\\s+/g, \" \").trim());\n if (names.length > 0) return names.join(\" \");\n }\n if (element.labels && element.labels.length > 0) {\n return String(element.labels[0].textContent || \"\").replace(/\\s+/g, \" \").trim();\n }\n const alt = element.getAttribute(\"alt\");\n if (alt) return String(alt).trim();\n const title = element.getAttribute(\"title\");\n if (title) return String(title).trim();\n const tag = String(element.tagName).toLowerCase();\n if (tag === \"button\" || tag === \"a\" || /^h[1-6]$/.test(tag) || element.getAttribute(\"role\")) {\n const text = String(element.textContent || \"\").replace(/\\s+/g, \" \").trim();\n return text === \"\" ? undefined : text.length > config.max_text ? text.slice(0, config.max_text) + \"…\" : text;\n }\n return undefined;\n };\n\n const implicitRole = (): string | undefined => {\n const explicit = element.getAttribute(\"role\");\n if (explicit) return String(explicit);\n const tag = String(element.tagName).toLowerCase();\n const type = String(element.getAttribute(\"type\") || \"\").toLowerCase();\n if (tag === \"button\") return \"button\";\n if (tag === \"a\" && element.hasAttribute(\"href\")) return \"link\";\n if (/^h[1-6]$/.test(tag)) return \"heading\";\n if (tag === \"input\") {\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n if (type === \"button\" || type === \"submit\" || type === \"reset\") return \"button\";\n if (type === \"range\") return \"slider\";\n if (type === \"number\") return \"spinbutton\";\n return \"textbox\";\n }\n if (tag === \"textarea\") return \"textbox\";\n if (tag === \"select\") return \"combobox\";\n if (tag === \"img\") return \"img\";\n if (tag === \"nav\") return \"navigation\";\n if (tag === \"main\") return \"main\";\n if (tag === \"p\") return \"paragraph\";\n if (tag === \"ul\" || tag === \"ol\") return \"list\";\n if (tag === \"li\") return \"listitem\";\n return undefined;\n };\n\n const neighbour = (node: any): { tag: string; box: { x: number; y: number; width: number; height: number } } | undefined =>\n node && node.nodeType === 1 ? { tag: String(node.tagName).toLowerCase(), box: rect(node) } : undefined;\n\n const lineHeight = style.lineHeight === \"normal\" ? null : px(style.lineHeight);\n const gap = style.display.indexOf(\"flex\") !== -1 || style.display.indexOf(\"grid\") !== -1 ? px(style.rowGap === \"normal\" ? \"0\" : style.rowGap) : null;\n\n const name = accessibleName();\n const classes: string[] = [];\n if (element.classList) for (let i = 0; i < element.classList.length && i < 4; i++) classes.push(String(element.classList[i]));\n\n const out: RawMeasurement = {\n tag: String(element.tagName).toLowerCase(),\n classes,\n box,\n visible,\n in_viewport,\n clipped,\n text_overflows,\n display: String(style.display),\n position: String(style.position),\n opacity,\n font: {\n family: String(style.fontFamily).split(\",\")[0].replace(/[\"']/g, \"\").trim(),\n size: px(style.fontSize),\n weight: px(style.fontWeight),\n line_height: lineHeight,\n style: String(style.fontStyle),\n },\n color: String(style.color),\n background,\n padding: [px(style.paddingTop), px(style.paddingRight), px(style.paddingBottom), px(style.paddingLeft)],\n margin: [px(style.marginTop), px(style.marginRight), px(style.marginBottom), px(style.marginLeft)],\n gap,\n border: `${style.borderTopWidth} ${style.borderTopStyle} ${style.borderTopColor}`,\n radius: String(style.borderTopLeftRadius),\n };\n if (element.id) out.id = String(element.id);\n const role = implicitRole();\n if (role !== undefined) out.role = role;\n if (name !== undefined) out.name = name;\n const text = ownText();\n if (text !== \"\") out.text = text;\n const parent = neighbour(element.parentElement);\n if (parent) out.parent = parent;\n const previous = neighbour(element.previousElementSibling);\n if (previous) out.previous = previous;\n return out;\n\n /** Composite `layers[0]` over `layers[1]` over … ; returns an rgb()/rgba() string. */\n function blend(list: string[]): string {\n const parse = (value: string): [number, number, number, number] | null => {\n const m = /^rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*(?:,\\s*([\\d.]+)\\s*)?\\)$/.exec(value);\n if (!m) return value === \"transparent\" ? [0, 0, 0, 0] : null;\n return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] === undefined ? 1 : Number(m[4])];\n };\n // Start from the deepest (last) layer and paint each one above it on top.\n let acc: [number, number, number, number] | null = null;\n for (let i = list.length - 1; i >= 0; i--) {\n const top = parse(list[i]);\n if (!top) continue;\n if (!acc || top[3] >= 1) {\n acc = top;\n continue;\n }\n if (top[3] <= 0) continue;\n const below: [number, number, number, number] = acc;\n const a: number = top[3] + below[3] * (1 - top[3]);\n const mix = (t: number, b: number): number => Math.round((t * top[3] + b * below[3] * (1 - top[3])) / a);\n acc = [mix(top[0], below[0]), mix(top[1], below[1]), mix(top[2], below[2]), a];\n }\n if (!acc) return \"rgba(0, 0, 0, 0)\";\n // The page's canvas is white under everything that is still translucent.\n if (acc[3] < 1) {\n const a = acc[3];\n acc = [Math.round(acc[0] * a + 255 * (1 - a)), Math.round(acc[1] * a + 255 * (1 - a)), Math.round(acc[2] * a + 255 * (1 - a)), 1];\n }\n return `rgb(${acc[0]}, ${acc[1]}, ${acc[2]})`;\n }\n}\n\ninterface RawInventory {\n elements: number;\n text_elements: number;\n fonts: Record<string, number>;\n font_sizes: Record<string, number>;\n font_weights: Record<string, number>;\n text_colours: Record<string, number>;\n backgrounds: Record<string, number>;\n spacing: Record<string, number>;\n radii: Record<string, number>;\n}\n\nfunction inventoryInPage(config: { selector: string | null; max_elements: number }): RawInventory {\n const g = globalThis as any;\n const win = g.window;\n const doc = g.document;\n const out: RawInventory = {\n elements: 0,\n text_elements: 0,\n fonts: {},\n font_sizes: {},\n font_weights: {},\n text_colours: {},\n backgrounds: {},\n spacing: {},\n radii: {},\n };\n const bump = (map: Record<string, number>, value: string): void => {\n map[value] = (map[value] || 0) + 1;\n };\n const SKIP = [\"script\", \"style\", \"link\", \"meta\", \"title\", \"head\", \"noscript\", \"template\", \"br\", \"svg\", \"path\"];\n\n const root = config.selector ? doc.querySelector(config.selector) : doc.body;\n if (!root) return out;\n const all = [root].concat(Array.prototype.slice.call(root.querySelectorAll(\"*\")));\n\n for (const el of all) {\n if (out.elements >= config.max_elements) break;\n const tag = String(el.tagName || \"\").toLowerCase();\n if (SKIP.indexOf(tag) !== -1) continue;\n const s = win.getComputedStyle(el);\n if (s.display === \"none\") continue;\n out.elements++;\n\n let ownText = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n if (el.childNodes[i].nodeType === 3) ownText += el.childNodes[i].nodeValue;\n }\n const hasText = ownText.trim() !== \"\" || (typeof el.value === \"string\" && el.value !== \"\" && tag !== \"option\");\n if (hasText) {\n out.text_elements++;\n bump(out.fonts, String(s.fontFamily).split(\",\")[0].replace(/[\"']/g, \"\").trim());\n bump(out.font_sizes, s.fontSize);\n bump(out.font_weights, s.fontWeight);\n bump(out.text_colours, s.color);\n }\n if (s.backgroundColor && s.backgroundColor !== \"rgba(0, 0, 0, 0)\" && s.backgroundColor !== \"transparent\") {\n bump(out.backgrounds, s.backgroundColor);\n }\n const spacing = [\n s.paddingTop,\n s.paddingRight,\n s.paddingBottom,\n s.paddingLeft,\n s.marginTop,\n s.marginRight,\n s.marginBottom,\n s.marginLeft,\n ];\n if (s.display.indexOf(\"flex\") !== -1 || s.display.indexOf(\"grid\") !== -1) {\n if (s.rowGap !== \"normal\") spacing.push(s.rowGap);\n if (s.columnGap !== \"normal\" && s.columnGap !== s.rowGap) spacing.push(s.columnGap);\n }\n for (const value of spacing) {\n if (value && value !== \"0px\" && parseFloat(value) > 0) bump(out.spacing, value);\n }\n const radius = s.borderTopLeftRadius;\n if (radius && radius !== \"0px\") bump(out.radii, radius);\n }\n return out;\n}\n"]}
1
+ {"version":3,"file":"inspect.js","sourceRoot":"","sources":["../../src/engine/inspect.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Locator, Page } from \"playwright\";\nimport {\n MAX_INSPECT_TEXT_LENGTH,\n MAX_INVENTORY_ELEMENTS,\n MAX_INVENTORY_VALUES,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { isRef, refSelector } from \"../utils/snapshot-rules.js\";\nimport { parseColour, toHex, type ElementMeasurement, type PageInventory, type Tally } from \"../utils/style-rules.js\";\nimport type { ComponentInfo, ComponentUnavailable } from \"../utils/vue-rules.js\";\nimport { componentOf } from \"./vue.js\";\n\n/**\n * How an element is built, measured inside the page.\n *\n * A screenshot shows what a button looks like; this says what it *is*: the\n * box in viewport pixels, the font, the colours it actually renders in (a\n * transparent background resolved through its ancestors), the spacing, and\n * where it sits against its parent and the sibling before it. That is what an\n * agent needs to check its own work against the design it meant to build.\n *\n * Every number is read in one `evaluate` per element and judged in\n * `style-rules.ts`, which never sees a browser.\n */\n\nexport interface InspectResult {\n target: string;\n measurement?: ElementMeasurement;\n /** Why there is no measurement: the target resolved to nothing. */\n error?: string;\n /** The Vue component behind the element, when asked for and present. */\n component?: ComponentInfo | ComponentUnavailable;\n}\n\nexport interface MeasureOptions {\n /** Also read the Vue component that rendered each element. */\n components?: boolean;\n}\n\n/** Measure each target — a snapshot ref (`e8`) or a CSS selector — in order. */\nexport async function measureElements(page: Page, targets: readonly string[], options: MeasureOptions = {}): Promise<InspectResult[]> {\n const results: InspectResult[] = [];\n for (const target of targets) {\n const result = await measureOne(page, target);\n if (options.components && result.measurement) {\n const component = await componentOf(resolve(page, target));\n if (component) result.component = component;\n }\n results.push(result);\n }\n return results;\n}\n\nasync function measureOne(page: Page, target: string): Promise<InspectResult> {\n const locator = resolve(page, target);\n let count: number;\n try {\n count = await locator.count();\n } catch (error) {\n return { target, error: `could not be resolved: ${firstLine(error)}` };\n }\n if (count === 0) {\n return {\n target,\n error: isRef(target)\n ? \"did not resolve — the page has changed since that snapshot, or none was taken; run framewatch_snapshot again\"\n : \"matched nothing\",\n };\n }\n try {\n const raw = await locator.evaluate(measureInPage, { max_text: MAX_INSPECT_TEXT_LENGTH }, { timeout: SELECTOR_TIMEOUT_MS });\n return { target, measurement: { target, ...raw } };\n } catch (error) {\n return { target, error: `could not be measured: ${firstLine(error)}` };\n }\n}\n\nfunction resolve(page: Page, target: string): Locator {\n return isRef(target) ? page.locator(refSelector(target)) : page.locator(target).first();\n}\n\nfunction firstLine(error: unknown): string {\n return (error instanceof Error ? error.message : String(error)).split(\"\\n\")[0];\n}\n\n/* ── Inventory ────────────────────────────────────────────────────────── */\n\n/** Every font, size, weight, colour, spacing value and radius the page (or `selector`) uses. */\nexport async function inventoryPage(page: Page, selector?: string): Promise<PageInventory> {\n const raw = await page.evaluate(inventoryInPage, { selector: selector ?? null, max_elements: MAX_INVENTORY_ELEMENTS });\n const tally = (map: Record<string, number>, normalise: (value: string) => string | null = (v) => v): Tally[] => {\n const merged = new Map<string, number>();\n for (const [value, count] of Object.entries(map)) {\n const key = normalise(value);\n if (key === null) continue;\n merged.set(key, (merged.get(key) ?? 0) + count);\n }\n return [...merged.entries()]\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value))\n .slice(0, MAX_INVENTORY_VALUES);\n };\n const hex = (value: string): string | null => {\n const colour = parseColour(value);\n if (!colour || colour.a === 0) return null;\n return toHex(colour);\n };\n return {\n elements: raw.elements,\n text_elements: raw.text_elements,\n fonts: tally(raw.fonts),\n font_sizes: tally(raw.font_sizes),\n font_weights: tally(raw.font_weights),\n text_colours: tally(raw.text_colours, hex),\n backgrounds: tally(raw.backgrounds, hex),\n spacing: tally(raw.spacing),\n radii: tally(raw.radii),\n };\n}\n\n/* ── In-page ──────────────────────────────────────────────────────────────\n * Everything below runs inside Chromium. Written against `globalThis` and\n * untyped nodes because this package is compiled with the Node lib only —\n * and nothing here may close over a module value: the function body is\n * serialised and run where the module does not exist.\n */\n\ntype RawMeasurement = Omit<ElementMeasurement, \"target\">;\n\nfunction measureInPage(element: any, config: { max_text: number }): RawMeasurement {\n const g = globalThis as any;\n const win = g.window;\n const doc = g.document;\n\n const px = (value: string): number => {\n const n = parseFloat(value);\n return Number.isFinite(n) ? Math.round(n * 100) / 100 : 0;\n };\n const rect = (node: any): { x: number; y: number; width: number; height: number } => {\n const r = node.getBoundingClientRect();\n return { x: Math.round(r.left), y: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) };\n };\n\n const style = win.getComputedStyle(element);\n const box = rect(element);\n\n // Effective background: composite this element's own over each ancestor's\n // until one is opaque. Done here as a list of layers, blended in Node.\n const layers: string[] = [];\n let node: any = element;\n while (node && node.nodeType === 1) {\n const bg = win.getComputedStyle(node).backgroundColor;\n layers.push(bg);\n if (/^rgb\\(/.test(bg)) break;\n node = node.parentElement;\n }\n const background = blend(layers);\n\n // Clipped by an ancestor whose overflow is not visible.\n let clipped = false;\n let ancestor: any = element.parentElement;\n while (ancestor && ancestor !== doc.documentElement) {\n const s = win.getComputedStyle(ancestor);\n if (s.overflow !== \"visible\" || s.overflowX !== \"visible\" || s.overflowY !== \"visible\") {\n const a = ancestor.getBoundingClientRect();\n const r = element.getBoundingClientRect();\n if (r.left < a.left - 0.5 || r.right > a.right + 0.5 || r.top < a.top - 0.5 || r.bottom > a.bottom + 0.5) {\n clipped = true;\n break;\n }\n }\n ancestor = ancestor.parentElement;\n }\n\n const vw = win.innerWidth;\n const vh = win.innerHeight;\n const r = element.getBoundingClientRect();\n const inside = r.left >= 0 && r.top >= 0 && r.right <= vw && r.bottom <= vh;\n const outside = r.right <= 0 || r.bottom <= 0 || r.left >= vw || r.top >= vh;\n const in_viewport: \"fully\" | \"partially\" | \"none\" = inside ? \"fully\" : outside ? \"none\" : \"partially\";\n\n const opacity = px(style.opacity);\n const visible =\n style.display !== \"none\" && style.visibility !== \"hidden\" && opacity > 0 && r.width > 0 && r.height > 0;\n\n const text_overflows = element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1;\n\n const ownText = (): string => {\n let out = \"\";\n const children = element.childNodes;\n for (let i = 0; i < children.length; i++) {\n if (children[i].nodeType === 3) out += children[i].nodeValue;\n }\n out = out.replace(/\\s+/g, \" \").trim();\n if (out === \"\" && typeof element.value === \"string\") out = element.value;\n if (out === \"\") out = String(element.textContent || \"\").replace(/\\s+/g, \" \").trim();\n return out.length > config.max_text ? out.slice(0, config.max_text) + \"…\" : out;\n };\n\n const accessibleName = (): string | undefined => {\n const label = element.getAttribute(\"aria-label\");\n if (label) return String(label).trim();\n const labelledBy = element.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n const names = String(labelledBy)\n .split(/\\s+/)\n .map((id) => doc.getElementById(id))\n .filter(Boolean)\n .map((n: any) => String(n.textContent || \"\").replace(/\\s+/g, \" \").trim());\n if (names.length > 0) return names.join(\" \");\n }\n if (element.labels && element.labels.length > 0) {\n return String(element.labels[0].textContent || \"\").replace(/\\s+/g, \" \").trim();\n }\n const alt = element.getAttribute(\"alt\");\n if (alt) return String(alt).trim();\n const title = element.getAttribute(\"title\");\n if (title) return String(title).trim();\n const tag = String(element.tagName).toLowerCase();\n if (tag === \"button\" || tag === \"a\" || /^h[1-6]$/.test(tag) || element.getAttribute(\"role\")) {\n const text = String(element.textContent || \"\").replace(/\\s+/g, \" \").trim();\n return text === \"\" ? undefined : text.length > config.max_text ? text.slice(0, config.max_text) + \"…\" : text;\n }\n return undefined;\n };\n\n const implicitRole = (): string | undefined => {\n const explicit = element.getAttribute(\"role\");\n if (explicit) return String(explicit);\n const tag = String(element.tagName).toLowerCase();\n const type = String(element.getAttribute(\"type\") || \"\").toLowerCase();\n if (tag === \"button\") return \"button\";\n if (tag === \"a\" && element.hasAttribute(\"href\")) return \"link\";\n if (/^h[1-6]$/.test(tag)) return \"heading\";\n if (tag === \"input\") {\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n if (type === \"button\" || type === \"submit\" || type === \"reset\") return \"button\";\n if (type === \"range\") return \"slider\";\n if (type === \"number\") return \"spinbutton\";\n return \"textbox\";\n }\n if (tag === \"textarea\") return \"textbox\";\n if (tag === \"select\") return \"combobox\";\n if (tag === \"img\") return \"img\";\n if (tag === \"nav\") return \"navigation\";\n if (tag === \"main\") return \"main\";\n if (tag === \"p\") return \"paragraph\";\n if (tag === \"ul\" || tag === \"ol\") return \"list\";\n if (tag === \"li\") return \"listitem\";\n return undefined;\n };\n\n const neighbour = (node: any): { tag: string; box: { x: number; y: number; width: number; height: number } } | undefined =>\n node && node.nodeType === 1 ? { tag: String(node.tagName).toLowerCase(), box: rect(node) } : undefined;\n\n const lineHeight = style.lineHeight === \"normal\" ? null : px(style.lineHeight);\n const gap = style.display.indexOf(\"flex\") !== -1 || style.display.indexOf(\"grid\") !== -1 ? px(style.rowGap === \"normal\" ? \"0\" : style.rowGap) : null;\n\n const name = accessibleName();\n const classes: string[] = [];\n if (element.classList) for (let i = 0; i < element.classList.length && i < 4; i++) classes.push(String(element.classList[i]));\n\n const out: RawMeasurement = {\n tag: String(element.tagName).toLowerCase(),\n classes,\n box,\n visible,\n in_viewport,\n clipped,\n text_overflows,\n display: String(style.display),\n position: String(style.position),\n opacity,\n font: {\n family: String(style.fontFamily).split(\",\")[0].replace(/[\"']/g, \"\").trim(),\n size: px(style.fontSize),\n weight: px(style.fontWeight),\n line_height: lineHeight,\n style: String(style.fontStyle),\n },\n color: String(style.color),\n background,\n padding: [px(style.paddingTop), px(style.paddingRight), px(style.paddingBottom), px(style.paddingLeft)],\n margin: [px(style.marginTop), px(style.marginRight), px(style.marginBottom), px(style.marginLeft)],\n gap,\n border: `${style.borderTopWidth} ${style.borderTopStyle} ${style.borderTopColor}`,\n radius: String(style.borderTopLeftRadius),\n };\n if (element.id) out.id = String(element.id);\n const role = implicitRole();\n if (role !== undefined) out.role = role;\n if (name !== undefined) out.name = name;\n const text = ownText();\n if (text !== \"\") out.text = text;\n const parent = neighbour(element.parentElement);\n if (parent) out.parent = parent;\n const previous = neighbour(element.previousElementSibling);\n if (previous) out.previous = previous;\n return out;\n\n /** Composite `layers[0]` over `layers[1]` over … ; returns an rgb()/rgba() string. */\n function blend(list: string[]): string {\n const parse = (value: string): [number, number, number, number] | null => {\n const m = /^rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*(?:,\\s*([\\d.]+)\\s*)?\\)$/.exec(value);\n if (!m) return value === \"transparent\" ? [0, 0, 0, 0] : null;\n return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] === undefined ? 1 : Number(m[4])];\n };\n // Start from the deepest (last) layer and paint each one above it on top.\n let acc: [number, number, number, number] | null = null;\n for (let i = list.length - 1; i >= 0; i--) {\n const top = parse(list[i]);\n if (!top) continue;\n if (!acc || top[3] >= 1) {\n acc = top;\n continue;\n }\n if (top[3] <= 0) continue;\n const below: [number, number, number, number] = acc;\n const a: number = top[3] + below[3] * (1 - top[3]);\n const mix = (t: number, b: number): number => Math.round((t * top[3] + b * below[3] * (1 - top[3])) / a);\n acc = [mix(top[0], below[0]), mix(top[1], below[1]), mix(top[2], below[2]), a];\n }\n if (!acc) return \"rgba(0, 0, 0, 0)\";\n // The page's canvas is white under everything that is still translucent.\n if (acc[3] < 1) {\n const a = acc[3];\n acc = [Math.round(acc[0] * a + 255 * (1 - a)), Math.round(acc[1] * a + 255 * (1 - a)), Math.round(acc[2] * a + 255 * (1 - a)), 1];\n }\n return `rgb(${acc[0]}, ${acc[1]}, ${acc[2]})`;\n }\n}\n\ninterface RawInventory {\n elements: number;\n text_elements: number;\n fonts: Record<string, number>;\n font_sizes: Record<string, number>;\n font_weights: Record<string, number>;\n text_colours: Record<string, number>;\n backgrounds: Record<string, number>;\n spacing: Record<string, number>;\n radii: Record<string, number>;\n}\n\nfunction inventoryInPage(config: { selector: string | null; max_elements: number }): RawInventory {\n const g = globalThis as any;\n const win = g.window;\n const doc = g.document;\n const out: RawInventory = {\n elements: 0,\n text_elements: 0,\n fonts: {},\n font_sizes: {},\n font_weights: {},\n text_colours: {},\n backgrounds: {},\n spacing: {},\n radii: {},\n };\n const bump = (map: Record<string, number>, value: string): void => {\n map[value] = (map[value] || 0) + 1;\n };\n const SKIP = [\"script\", \"style\", \"link\", \"meta\", \"title\", \"head\", \"noscript\", \"template\", \"br\", \"svg\", \"path\"];\n\n const root = config.selector ? doc.querySelector(config.selector) : doc.body;\n if (!root) return out;\n const all = [root].concat(Array.prototype.slice.call(root.querySelectorAll(\"*\")));\n\n for (const el of all) {\n if (out.elements >= config.max_elements) break;\n const tag = String(el.tagName || \"\").toLowerCase();\n if (SKIP.indexOf(tag) !== -1) continue;\n const s = win.getComputedStyle(el);\n if (s.display === \"none\") continue;\n out.elements++;\n\n let ownText = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n if (el.childNodes[i].nodeType === 3) ownText += el.childNodes[i].nodeValue;\n }\n const hasText = ownText.trim() !== \"\" || (typeof el.value === \"string\" && el.value !== \"\" && tag !== \"option\");\n if (hasText) {\n out.text_elements++;\n bump(out.fonts, String(s.fontFamily).split(\",\")[0].replace(/[\"']/g, \"\").trim());\n bump(out.font_sizes, s.fontSize);\n bump(out.font_weights, s.fontWeight);\n bump(out.text_colours, s.color);\n }\n if (s.backgroundColor && s.backgroundColor !== \"rgba(0, 0, 0, 0)\" && s.backgroundColor !== \"transparent\") {\n bump(out.backgrounds, s.backgroundColor);\n }\n const spacing = [\n s.paddingTop,\n s.paddingRight,\n s.paddingBottom,\n s.paddingLeft,\n s.marginTop,\n s.marginRight,\n s.marginBottom,\n s.marginLeft,\n ];\n if (s.display.indexOf(\"flex\") !== -1 || s.display.indexOf(\"grid\") !== -1) {\n if (s.rowGap !== \"normal\") spacing.push(s.rowGap);\n if (s.columnGap !== \"normal\" && s.columnGap !== s.rowGap) spacing.push(s.columnGap);\n }\n for (const value of spacing) {\n if (value && value !== \"0px\" && parseFloat(value) > 0) bump(out.spacing, value);\n }\n const radius = s.borderTopLeftRadius;\n if (radius && radius !== \"0px\") bump(out.radii, radius);\n }\n return out;\n}\n"]}
@@ -3,7 +3,7 @@ import type { Page } from "playwright";
3
3
  /**
4
4
  * Interaction executor.
5
5
  *
6
- * Turns one declarative interaction step (see CLAUDE.md "Interactions as JSON
6
+ * Turns one declarative interaction step (see the README, "Replaying an interaction
7
7
  * script") into real input on a Playwright page. Every step is validated
8
8
  * before anything touches the page, and every Playwright failure is reduced to
9
9
  * a single actionable line naming the step that failed — a recording is a bad
@@ -5,7 +5,7 @@ import { detectVue, routerNavigate } from "./vue.js";
5
5
  /**
6
6
  * Interaction executor.
7
7
  *
8
- * Turns one declarative interaction step (see CLAUDE.md "Interactions as JSON
8
+ * Turns one declarative interaction step (see the README, "Replaying an interaction
9
9
  * script") into real input on a Playwright page. Every step is validated
10
10
  * before anything touches the page, and every Playwright failure is reduced to
11
11
  * a single actionable line naming the step that failed — a recording is a bad
@@ -1 +1 @@
1
- {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../../src/engine/interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { Page } from \"playwright\";\nimport { NAVIGATION_TIMEOUT_MS, SELECTOR_TIMEOUT_MS, SWIPE_STEPS, SWIPE_STEP_DELAY_MS } from \"../constants.js\";\nimport { classifyNavigate } from \"../utils/vue-rules.js\";\nimport { detectVue, routerNavigate } from \"./vue.js\";\n\n/**\n * Interaction executor.\n *\n * Turns one declarative interaction step (see CLAUDE.md \"Interactions as JSON\n * script\") into real input on a Playwright page. Every step is validated\n * before anything touches the page, and every Playwright failure is reduced to\n * a single actionable line naming the step that failed — a recording is a bad\n * place to surface a 30-line call log.\n */\n\n/** Actions a replayable script (`framewatch_capture`, `framewatch_save_auth`) can perform. */\nexport const CAPTURE_ACTIONS = [\n \"click\",\n \"tap\",\n \"type\",\n \"key\",\n \"scroll\",\n \"swipe\",\n \"hover\",\n \"select\",\n \"wait\",\n \"navigate\",\n] as const;\n\n/** Actions `framewatch_interact` can perform as a one-off — everything but `wait`, which needs a script to sit in. */\nexport const INTERACT_ACTIONS = [\n \"click\",\n \"tap\",\n \"type\",\n \"key\",\n \"scroll\",\n \"swipe\",\n \"navigate\",\n \"select\",\n \"hover\",\n] as const;\n\nexport type InteractionAction = (typeof CAPTURE_ACTIONS)[number] | (typeof INTERACT_ACTIONS)[number];\n\nexport interface Interaction {\n action: InteractionAction;\n /** CSS selector for click/tap/type/select/hover, the field to focus for `key`, or the scroll container. */\n selector?: string;\n /** Text to type, key to press, option value to select, or URL to navigate to. */\n value?: string;\n x?: number;\n y?: number;\n delta_x?: number;\n delta_y?: number;\n /** Wait this long *before* performing the action. */\n delay_ms?: number;\n}\n\nexport interface ExecuteOptions {\n /** Timeout for selector-based actions. Default SELECTOR_TIMEOUT_MS. */\n timeout_ms?: number;\n}\n\n/** What a step has to add to its own description once it has run. */\nexport interface InteractionOutcome {\n /** Appended to `describeInteraction(step)`, e.g. ` (vue-router)`. */\n note?: string;\n}\n\n/** Steps that need a touch-capable browser context (`hasTouch: true`). */\nconst TOUCH_ACTIONS = new Set<InteractionAction>([\"tap\", \"swipe\"]);\n\n/** True when any step needs a touch-enabled browser context. */\nexport function needsTouch(interactions: readonly Interaction[]): boolean {\n return interactions.some((i) => TOUCH_ACTIONS.has(i.action));\n}\n\n/**\n * Check that a step carries the fields its action needs. Returns a message\n * naming what is missing, or null when the step is executable. Used both by\n * the tools (at input-validation time, before a browser is even launched) and\n * by `executeInteraction` itself.\n */\nexport function validateInteraction(interaction: Interaction): string | null {\n const { action, selector, value, x, y, delta_x, delta_y } = interaction;\n const hasPoint = typeof x === \"number\" && typeof y === \"number\";\n const hasPartialPoint = typeof x === \"number\" || typeof y === \"number\";\n\n switch (action) {\n case \"click\":\n case \"tap\":\n case \"hover\":\n if (selector) return null;\n if (hasPoint) return null;\n return hasPartialPoint\n ? `${action} needs both \\`x\\` and \\`y\\` (or a \\`selector\\`)`\n : `${action} needs a \\`selector\\`, or \\`x\\` and \\`y\\``;\n\n case \"type\":\n return typeof value === \"string\"\n ? null\n : \"type needs a `value` (the text to type; `\\n` presses Enter and `\\t` presses Tab)\";\n\n case \"key\":\n return typeof value === \"string\" && value.length > 0\n ? null\n : \"key needs a `value` (the key to press, e.g. `Enter`, `Escape`, `ArrowDown` or `Control+a`)\";\n\n case \"select\":\n if (!selector) return \"select needs a `selector` naming the <select> element\";\n return typeof value === \"string\" ? null : \"select needs a `value` (the option to choose)\";\n\n case \"scroll\":\n return typeof delta_x === \"number\" || typeof delta_y === \"number\"\n ? null\n : \"scroll needs `delta_y` (and/or `delta_x`) — the distance to scroll\";\n\n case \"swipe\":\n if (!hasPoint) return \"swipe needs `x` and `y` — where the finger goes down\";\n return typeof delta_x === \"number\" || typeof delta_y === \"number\"\n ? null\n : \"swipe needs `delta_x` and/or `delta_y` — how far the finger travels\";\n\n case \"navigate\":\n return typeof value === \"string\" && value.length > 0 ? null : \"navigate needs a `value` (the URL to go to)\";\n\n case \"wait\":\n return null;\n\n default:\n return `unknown action \"${String(action)}\"`;\n }\n}\n\n/** Longest `value` printed in a step description before it is elided. */\nconst DESCRIBE_VALUE_MAX = 60;\n\n/**\n * One-line, human-readable summary of a step, used in error messages and in\n * the capture summary. Long values (a pasted token, a password) are elided\n * rather than echoed in full.\n */\nexport function describeInteraction(interaction: Interaction): string {\n const { action, selector, value, x, y, delta_x = 0, delta_y = 0, delay_ms } = interaction;\n const target = selector ? ` \"${selector}\"` : typeof x === \"number\" && typeof y === \"number\" ? ` at ${x},${y}` : \"\";\n\n switch (action) {\n case \"click\":\n case \"tap\":\n case \"hover\":\n return `${action}${target}`;\n case \"type\":\n return `type \"${elide(value)}\"${selector ? ` into \"${selector}\"` : \"\"}`;\n case \"select\":\n return `select \"${elide(value)}\"${selector ? ` in \"${selector}\"` : \"\"}`;\n case \"key\":\n return `press \"${elide(value)}\"${selector ? ` in \"${selector}\"` : \"\"}`;\n case \"scroll\":\n return `scroll${selector ? ` \"${selector}\"` : \"\"} by ${delta_x},${delta_y}`;\n case \"swipe\":\n return `swipe${target} by ${delta_x},${delta_y}`;\n case \"navigate\":\n return `navigate to ${elide(value)}`;\n case \"wait\":\n return `wait ${delay_ms ?? 0}ms`;\n default:\n return String(action);\n }\n}\n\nfunction elide(value: string | undefined): string {\n if (value === undefined) return \"\";\n return value.length > DESCRIBE_VALUE_MAX ? `${value.slice(0, DESCRIBE_VALUE_MAX)}…` : value;\n}\n\n/**\n * Perform one interaction on `page`.\n *\n * Waits `delay_ms` first (that is what `delay_ms` means: \"wait before this\n * action\"), so a `wait` step is simply a step with no action of its own.\n * Throws a single-line Error naming the step on any failure; the page is never\n * touched when the step is invalid.\n */\nexport async function executeInteraction(\n page: Page,\n interaction: Interaction,\n options: ExecuteOptions = {},\n): Promise<InteractionOutcome> {\n const problem = validateInteraction(interaction);\n if (problem) throw new Error(problem);\n\n const delay = interaction.delay_ms;\n if (typeof delay === \"number\" && delay > 0) await sleep(delay);\n\n const timeout = options.timeout_ms ?? SELECTOR_TIMEOUT_MS;\n try {\n return (await perform(page, interaction, timeout)) ?? {};\n } catch (error) {\n throw new Error(describeFailure(interaction, error), { cause: error });\n }\n}\n\nasync function perform(page: Page, interaction: Interaction, timeout: number): Promise<InteractionOutcome | void> {\n const { action, selector, value, x, y, delta_x = 0, delta_y = 0 } = interaction;\n\n switch (action) {\n case \"click\":\n if (selector) await page.locator(selector).first().click({ timeout });\n else await page.mouse.click(x!, y!);\n return;\n\n case \"hover\":\n if (selector) await page.locator(selector).first().hover({ timeout });\n else await page.mouse.move(x!, y!);\n return;\n\n case \"tap\":\n if (selector) await page.locator(selector).first().tap({ timeout });\n else await page.touchscreen.tap(x!, y!);\n return;\n\n case \"type\":\n await typeValue(page, selector, value!, timeout);\n return;\n\n case \"key\":\n // A key goes wherever focus is; `selector` is the way to say where that\n // should be without spending a separate click step on it.\n if (selector) await page.locator(selector).first().focus({ timeout });\n await page.keyboard.press(value!);\n return;\n\n case \"select\":\n await page.locator(selector!).first().selectOption(value!, { timeout });\n return;\n\n case \"scroll\":\n // With a selector, put the pointer over that element first so the wheel\n // scrolls it rather than the page behind it.\n if (selector) await page.locator(selector).first().hover({ timeout });\n await page.mouse.wheel(delta_x, delta_y);\n return;\n\n case \"swipe\":\n await swipe(page, x!, y!, delta_x, delta_y);\n return;\n\n case \"navigate\": {\n // A path on a page with vue-router goes through the router — same\n // document, state kept, no reload — which is what a person clicking a\n // link in the app gets. Anything else, or a path the router does not\n // know, is a full load. Playwright only resolves relative URLs against\n // a context `baseURL`; resolve against the current page instead, which\n // is what a script author means by \"navigate to /login\".\n const vue = await detectVue(page);\n const plan = classifyNavigate(value!, page.url(), vue?.router === true);\n if (plan.via === \"router\") {\n const outcome = await routerNavigate(page, plan.target);\n if (outcome.ok) return { note: \" (vue-router)\" };\n await page.goto(resolveUrl(value!, page.url()), { waitUntil: \"commit\", timeout: NAVIGATION_TIMEOUT_MS });\n return { note: ` (full load — vue-router: ${outcome.error})` };\n }\n await page.goto(plan.target, { waitUntil: \"commit\", timeout: NAVIGATION_TIMEOUT_MS });\n return;\n }\n\n case \"wait\":\n return;\n }\n}\n\n/** Characters a `type` value may carry inline to mean a key press. */\nconst TYPE_KEYS: Record<string, string> = { \"\\n\": \"Enter\", \"\\t\": \"Tab\" };\n\n/**\n * Type `value`, pressing Enter for each `\\n` in it and Tab for each `\\t`.\n *\n * Typing a newline is what ends most forms, and splitting \"duo\\n\" into a type\n * step and a key step is a step the caller should not have to think about.\n * The first text run still goes in with `fill` — it clears the field, so\n * replaying a script twice does not append to what is already there — and\n * everything after a key press is typed on top of wherever focus ended up,\n * which is what a person continuing to type would produce.\n */\nasync function typeValue(page: Page, selector: string | undefined, value: string, timeout: number): Promise<void> {\n // Keep the separators: split(/([\\n\\t])/) yields text, key, text, key, …\n const segments = value.split(/([\\n\\t])/).filter((segment) => segment !== \"\");\n // An empty value still means \"empty the field\", which is `fill(\"\")`.\n if (segments.length === 0) segments.push(\"\");\n\n let focused = false;\n for (const segment of segments) {\n const key = TYPE_KEYS[segment];\n if (key === undefined) {\n // The first run replaces the field's contents; later runs are typed at\n // the caret, since a key press may well have moved focus elsewhere.\n if (selector && !focused) await page.locator(selector).first().fill(segment, { timeout });\n else await page.keyboard.type(segment);\n focused = true;\n continue;\n }\n // A value that opens with a key (\"\\n\" alone, meaning \"press Enter in this\n // field\") still has to land on the element the caller named.\n if (selector && !focused) await page.locator(selector).first().focus({ timeout });\n focused = true;\n await page.keyboard.press(key);\n }\n}\n\n/**\n * A real finger drag: touchStart, several touchMoves, touchEnd.\n *\n * Playwright's Touchscreen can only tap, so the drag goes through CDP (we are\n * Chromium-only). Intermediate moves matter — a carousel or pull-to-refresh\n * needs the travel and its velocity, not a teleport — and each one is spaced\n * about a frame apart so the page's own velocity maths sees a plausible gesture.\n */\nasync function swipe(page: Page, x: number, y: number, deltaX: number, deltaY: number): Promise<void> {\n const cdp = await page.context().newCDPSession(page);\n try {\n await cdp.send(\"Input.dispatchTouchEvent\", { type: \"touchStart\", touchPoints: [{ x, y }] });\n for (let step = 1; step <= SWIPE_STEPS; step++) {\n await sleep(SWIPE_STEP_DELAY_MS);\n await cdp.send(\"Input.dispatchTouchEvent\", {\n // Interpolate from the origin (not from the previous point) so the last\n // move lands exactly on x + deltaX, y + deltaY.\n type: \"touchMove\",\n touchPoints: [{ x: x + (deltaX * step) / SWIPE_STEPS, y: y + (deltaY * step) / SWIPE_STEPS }],\n });\n }\n await cdp.send(\"Input.dispatchTouchEvent\", { type: \"touchEnd\", touchPoints: [] });\n } finally {\n await cdp.detach().catch(() => {});\n }\n}\n\nfunction resolveUrl(value: string, base: string): string {\n try {\n return new URL(value, base).href;\n } catch {\n return value;\n }\n}\n\n/**\n * Reduce a Playwright error to one actionable line naming the step. Touch\n * failures get their own wording: the fix is a context option the caller\n * controls, not anything about the page.\n */\nfunction describeFailure(interaction: Interaction, error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n const step = describeInteraction(interaction);\n\n if (/hasTouch|does not support tap/i.test(message)) {\n return `${step} failed: this page is not touch-enabled. Touch is turned on automatically for tap/swipe steps — if you see this, the page was opened without it.`;\n }\n return `${step} failed: ${firstLine}`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * The shared zod fields every interaction step accepts. Both tools build their\n * own object around these (the action enums differ, and `framewatch_capture`\n * adds `delay_ms` where `framewatch_interact` adds `wait_ms`).\n */\nexport const interactionFieldShape = {\n selector: z\n .string()\n .optional()\n .describe(\n \"CSS selector for click/tap/type/select/hover targets, the field to focus first for `key`, or the scroll container\",\n ),\n value: z\n .string()\n .optional()\n .describe(\n \"Text to type (`\\n` in it presses Enter, `\\t` presses Tab), key to press for `key` \" +\n \"(e.g. `Enter`, `Escape`, `ArrowDown`, `Control+a`), option value to select, or URL to navigate to\",\n ),\n x: z.number().optional().describe(\"X coordinate for click/tap/swipe (used when no selector is given)\"),\n y: z.number().optional().describe(\"Y coordinate for click/tap/swipe (used when no selector is given)\"),\n delta_x: z.number().optional().describe(\"Horizontal distance for scroll/swipe\"),\n delta_y: z.number().optional().describe(\"Vertical distance for scroll/swipe\"),\n};\n\n/**\n * zod `superRefine` hook that rejects a step missing the fields its action\n * needs, so an unusable script is reported as invalid input instead of failing\n * half way through a recording.\n */\nexport function refineInteraction(value: Interaction, ctx: z.RefinementCtx): void {\n const problem = validateInteraction(value);\n if (problem !== null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: problem, path: [\"action\"] });\n }\n}\n"]}
1
+ {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../../src/engine/interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { Page } from \"playwright\";\nimport { NAVIGATION_TIMEOUT_MS, SELECTOR_TIMEOUT_MS, SWIPE_STEPS, SWIPE_STEP_DELAY_MS } from \"../constants.js\";\nimport { classifyNavigate } from \"../utils/vue-rules.js\";\nimport { detectVue, routerNavigate } from \"./vue.js\";\n\n/**\n * Interaction executor.\n *\n * Turns one declarative interaction step (see the README, \"Replaying an interaction\n * script\") into real input on a Playwright page. Every step is validated\n * before anything touches the page, and every Playwright failure is reduced to\n * a single actionable line naming the step that failed — a recording is a bad\n * place to surface a 30-line call log.\n */\n\n/** Actions a replayable script (`framewatch_capture`, `framewatch_save_auth`) can perform. */\nexport const CAPTURE_ACTIONS = [\n \"click\",\n \"tap\",\n \"type\",\n \"key\",\n \"scroll\",\n \"swipe\",\n \"hover\",\n \"select\",\n \"wait\",\n \"navigate\",\n] as const;\n\n/** Actions `framewatch_interact` can perform as a one-off — everything but `wait`, which needs a script to sit in. */\nexport const INTERACT_ACTIONS = [\n \"click\",\n \"tap\",\n \"type\",\n \"key\",\n \"scroll\",\n \"swipe\",\n \"navigate\",\n \"select\",\n \"hover\",\n] as const;\n\nexport type InteractionAction = (typeof CAPTURE_ACTIONS)[number] | (typeof INTERACT_ACTIONS)[number];\n\nexport interface Interaction {\n action: InteractionAction;\n /** CSS selector for click/tap/type/select/hover, the field to focus for `key`, or the scroll container. */\n selector?: string;\n /** Text to type, key to press, option value to select, or URL to navigate to. */\n value?: string;\n x?: number;\n y?: number;\n delta_x?: number;\n delta_y?: number;\n /** Wait this long *before* performing the action. */\n delay_ms?: number;\n}\n\nexport interface ExecuteOptions {\n /** Timeout for selector-based actions. Default SELECTOR_TIMEOUT_MS. */\n timeout_ms?: number;\n}\n\n/** What a step has to add to its own description once it has run. */\nexport interface InteractionOutcome {\n /** Appended to `describeInteraction(step)`, e.g. ` (vue-router)`. */\n note?: string;\n}\n\n/** Steps that need a touch-capable browser context (`hasTouch: true`). */\nconst TOUCH_ACTIONS = new Set<InteractionAction>([\"tap\", \"swipe\"]);\n\n/** True when any step needs a touch-enabled browser context. */\nexport function needsTouch(interactions: readonly Interaction[]): boolean {\n return interactions.some((i) => TOUCH_ACTIONS.has(i.action));\n}\n\n/**\n * Check that a step carries the fields its action needs. Returns a message\n * naming what is missing, or null when the step is executable. Used both by\n * the tools (at input-validation time, before a browser is even launched) and\n * by `executeInteraction` itself.\n */\nexport function validateInteraction(interaction: Interaction): string | null {\n const { action, selector, value, x, y, delta_x, delta_y } = interaction;\n const hasPoint = typeof x === \"number\" && typeof y === \"number\";\n const hasPartialPoint = typeof x === \"number\" || typeof y === \"number\";\n\n switch (action) {\n case \"click\":\n case \"tap\":\n case \"hover\":\n if (selector) return null;\n if (hasPoint) return null;\n return hasPartialPoint\n ? `${action} needs both \\`x\\` and \\`y\\` (or a \\`selector\\`)`\n : `${action} needs a \\`selector\\`, or \\`x\\` and \\`y\\``;\n\n case \"type\":\n return typeof value === \"string\"\n ? null\n : \"type needs a `value` (the text to type; `\\n` presses Enter and `\\t` presses Tab)\";\n\n case \"key\":\n return typeof value === \"string\" && value.length > 0\n ? null\n : \"key needs a `value` (the key to press, e.g. `Enter`, `Escape`, `ArrowDown` or `Control+a`)\";\n\n case \"select\":\n if (!selector) return \"select needs a `selector` naming the <select> element\";\n return typeof value === \"string\" ? null : \"select needs a `value` (the option to choose)\";\n\n case \"scroll\":\n return typeof delta_x === \"number\" || typeof delta_y === \"number\"\n ? null\n : \"scroll needs `delta_y` (and/or `delta_x`) — the distance to scroll\";\n\n case \"swipe\":\n if (!hasPoint) return \"swipe needs `x` and `y` — where the finger goes down\";\n return typeof delta_x === \"number\" || typeof delta_y === \"number\"\n ? null\n : \"swipe needs `delta_x` and/or `delta_y` — how far the finger travels\";\n\n case \"navigate\":\n return typeof value === \"string\" && value.length > 0 ? null : \"navigate needs a `value` (the URL to go to)\";\n\n case \"wait\":\n return null;\n\n default:\n return `unknown action \"${String(action)}\"`;\n }\n}\n\n/** Longest `value` printed in a step description before it is elided. */\nconst DESCRIBE_VALUE_MAX = 60;\n\n/**\n * One-line, human-readable summary of a step, used in error messages and in\n * the capture summary. Long values (a pasted token, a password) are elided\n * rather than echoed in full.\n */\nexport function describeInteraction(interaction: Interaction): string {\n const { action, selector, value, x, y, delta_x = 0, delta_y = 0, delay_ms } = interaction;\n const target = selector ? ` \"${selector}\"` : typeof x === \"number\" && typeof y === \"number\" ? ` at ${x},${y}` : \"\";\n\n switch (action) {\n case \"click\":\n case \"tap\":\n case \"hover\":\n return `${action}${target}`;\n case \"type\":\n return `type \"${elide(value)}\"${selector ? ` into \"${selector}\"` : \"\"}`;\n case \"select\":\n return `select \"${elide(value)}\"${selector ? ` in \"${selector}\"` : \"\"}`;\n case \"key\":\n return `press \"${elide(value)}\"${selector ? ` in \"${selector}\"` : \"\"}`;\n case \"scroll\":\n return `scroll${selector ? ` \"${selector}\"` : \"\"} by ${delta_x},${delta_y}`;\n case \"swipe\":\n return `swipe${target} by ${delta_x},${delta_y}`;\n case \"navigate\":\n return `navigate to ${elide(value)}`;\n case \"wait\":\n return `wait ${delay_ms ?? 0}ms`;\n default:\n return String(action);\n }\n}\n\nfunction elide(value: string | undefined): string {\n if (value === undefined) return \"\";\n return value.length > DESCRIBE_VALUE_MAX ? `${value.slice(0, DESCRIBE_VALUE_MAX)}…` : value;\n}\n\n/**\n * Perform one interaction on `page`.\n *\n * Waits `delay_ms` first (that is what `delay_ms` means: \"wait before this\n * action\"), so a `wait` step is simply a step with no action of its own.\n * Throws a single-line Error naming the step on any failure; the page is never\n * touched when the step is invalid.\n */\nexport async function executeInteraction(\n page: Page,\n interaction: Interaction,\n options: ExecuteOptions = {},\n): Promise<InteractionOutcome> {\n const problem = validateInteraction(interaction);\n if (problem) throw new Error(problem);\n\n const delay = interaction.delay_ms;\n if (typeof delay === \"number\" && delay > 0) await sleep(delay);\n\n const timeout = options.timeout_ms ?? SELECTOR_TIMEOUT_MS;\n try {\n return (await perform(page, interaction, timeout)) ?? {};\n } catch (error) {\n throw new Error(describeFailure(interaction, error), { cause: error });\n }\n}\n\nasync function perform(page: Page, interaction: Interaction, timeout: number): Promise<InteractionOutcome | void> {\n const { action, selector, value, x, y, delta_x = 0, delta_y = 0 } = interaction;\n\n switch (action) {\n case \"click\":\n if (selector) await page.locator(selector).first().click({ timeout });\n else await page.mouse.click(x!, y!);\n return;\n\n case \"hover\":\n if (selector) await page.locator(selector).first().hover({ timeout });\n else await page.mouse.move(x!, y!);\n return;\n\n case \"tap\":\n if (selector) await page.locator(selector).first().tap({ timeout });\n else await page.touchscreen.tap(x!, y!);\n return;\n\n case \"type\":\n await typeValue(page, selector, value!, timeout);\n return;\n\n case \"key\":\n // A key goes wherever focus is; `selector` is the way to say where that\n // should be without spending a separate click step on it.\n if (selector) await page.locator(selector).first().focus({ timeout });\n await page.keyboard.press(value!);\n return;\n\n case \"select\":\n await page.locator(selector!).first().selectOption(value!, { timeout });\n return;\n\n case \"scroll\":\n // With a selector, put the pointer over that element first so the wheel\n // scrolls it rather than the page behind it.\n if (selector) await page.locator(selector).first().hover({ timeout });\n await page.mouse.wheel(delta_x, delta_y);\n return;\n\n case \"swipe\":\n await swipe(page, x!, y!, delta_x, delta_y);\n return;\n\n case \"navigate\": {\n // A path on a page with vue-router goes through the router — same\n // document, state kept, no reload — which is what a person clicking a\n // link in the app gets. Anything else, or a path the router does not\n // know, is a full load. Playwright only resolves relative URLs against\n // a context `baseURL`; resolve against the current page instead, which\n // is what a script author means by \"navigate to /login\".\n const vue = await detectVue(page);\n const plan = classifyNavigate(value!, page.url(), vue?.router === true);\n if (plan.via === \"router\") {\n const outcome = await routerNavigate(page, plan.target);\n if (outcome.ok) return { note: \" (vue-router)\" };\n await page.goto(resolveUrl(value!, page.url()), { waitUntil: \"commit\", timeout: NAVIGATION_TIMEOUT_MS });\n return { note: ` (full load — vue-router: ${outcome.error})` };\n }\n await page.goto(plan.target, { waitUntil: \"commit\", timeout: NAVIGATION_TIMEOUT_MS });\n return;\n }\n\n case \"wait\":\n return;\n }\n}\n\n/** Characters a `type` value may carry inline to mean a key press. */\nconst TYPE_KEYS: Record<string, string> = { \"\\n\": \"Enter\", \"\\t\": \"Tab\" };\n\n/**\n * Type `value`, pressing Enter for each `\\n` in it and Tab for each `\\t`.\n *\n * Typing a newline is what ends most forms, and splitting \"duo\\n\" into a type\n * step and a key step is a step the caller should not have to think about.\n * The first text run still goes in with `fill` — it clears the field, so\n * replaying a script twice does not append to what is already there — and\n * everything after a key press is typed on top of wherever focus ended up,\n * which is what a person continuing to type would produce.\n */\nasync function typeValue(page: Page, selector: string | undefined, value: string, timeout: number): Promise<void> {\n // Keep the separators: split(/([\\n\\t])/) yields text, key, text, key, …\n const segments = value.split(/([\\n\\t])/).filter((segment) => segment !== \"\");\n // An empty value still means \"empty the field\", which is `fill(\"\")`.\n if (segments.length === 0) segments.push(\"\");\n\n let focused = false;\n for (const segment of segments) {\n const key = TYPE_KEYS[segment];\n if (key === undefined) {\n // The first run replaces the field's contents; later runs are typed at\n // the caret, since a key press may well have moved focus elsewhere.\n if (selector && !focused) await page.locator(selector).first().fill(segment, { timeout });\n else await page.keyboard.type(segment);\n focused = true;\n continue;\n }\n // A value that opens with a key (\"\\n\" alone, meaning \"press Enter in this\n // field\") still has to land on the element the caller named.\n if (selector && !focused) await page.locator(selector).first().focus({ timeout });\n focused = true;\n await page.keyboard.press(key);\n }\n}\n\n/**\n * A real finger drag: touchStart, several touchMoves, touchEnd.\n *\n * Playwright's Touchscreen can only tap, so the drag goes through CDP (we are\n * Chromium-only). Intermediate moves matter — a carousel or pull-to-refresh\n * needs the travel and its velocity, not a teleport — and each one is spaced\n * about a frame apart so the page's own velocity maths sees a plausible gesture.\n */\nasync function swipe(page: Page, x: number, y: number, deltaX: number, deltaY: number): Promise<void> {\n const cdp = await page.context().newCDPSession(page);\n try {\n await cdp.send(\"Input.dispatchTouchEvent\", { type: \"touchStart\", touchPoints: [{ x, y }] });\n for (let step = 1; step <= SWIPE_STEPS; step++) {\n await sleep(SWIPE_STEP_DELAY_MS);\n await cdp.send(\"Input.dispatchTouchEvent\", {\n // Interpolate from the origin (not from the previous point) so the last\n // move lands exactly on x + deltaX, y + deltaY.\n type: \"touchMove\",\n touchPoints: [{ x: x + (deltaX * step) / SWIPE_STEPS, y: y + (deltaY * step) / SWIPE_STEPS }],\n });\n }\n await cdp.send(\"Input.dispatchTouchEvent\", { type: \"touchEnd\", touchPoints: [] });\n } finally {\n await cdp.detach().catch(() => {});\n }\n}\n\nfunction resolveUrl(value: string, base: string): string {\n try {\n return new URL(value, base).href;\n } catch {\n return value;\n }\n}\n\n/**\n * Reduce a Playwright error to one actionable line naming the step. Touch\n * failures get their own wording: the fix is a context option the caller\n * controls, not anything about the page.\n */\nfunction describeFailure(interaction: Interaction, error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n const step = describeInteraction(interaction);\n\n if (/hasTouch|does not support tap/i.test(message)) {\n return `${step} failed: this page is not touch-enabled. Touch is turned on automatically for tap/swipe steps — if you see this, the page was opened without it.`;\n }\n return `${step} failed: ${firstLine}`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * The shared zod fields every interaction step accepts. Both tools build their\n * own object around these (the action enums differ, and `framewatch_capture`\n * adds `delay_ms` where `framewatch_interact` adds `wait_ms`).\n */\nexport const interactionFieldShape = {\n selector: z\n .string()\n .optional()\n .describe(\n \"CSS selector for click/tap/type/select/hover targets, the field to focus first for `key`, or the scroll container\",\n ),\n value: z\n .string()\n .optional()\n .describe(\n \"Text to type (`\\n` in it presses Enter, `\\t` presses Tab), key to press for `key` \" +\n \"(e.g. `Enter`, `Escape`, `ArrowDown`, `Control+a`), option value to select, or URL to navigate to\",\n ),\n x: z.number().optional().describe(\"X coordinate for click/tap/swipe (used when no selector is given)\"),\n y: z.number().optional().describe(\"Y coordinate for click/tap/swipe (used when no selector is given)\"),\n delta_x: z.number().optional().describe(\"Horizontal distance for scroll/swipe\"),\n delta_y: z.number().optional().describe(\"Vertical distance for scroll/swipe\"),\n};\n\n/**\n * zod `superRefine` hook that rejects a step missing the fields its action\n * needs, so an unusable script is reported as invalid input instead of failing\n * half way through a recording.\n */\nexport function refineInteraction(value: Interaction, ctx: z.RefinementCtx): void {\n const problem = validateInteraction(value);\n if (problem !== null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: problem, path: [\"action\"] });\n }\n}\n"]}
@@ -12,7 +12,7 @@ import type { ComponentInfo, ComponentNode, ComponentUnavailable, VueInfo } from
12
12
  * the address bar. Production builds keep only the app handle, and say so.
13
13
  *
14
14
  * Everything in-page below is serialised into Chromium: it may not close over
15
- * a module value (CLAUDE.md, decision 50), so every cap is passed in.
15
+ * a module value, so every cap is passed in.
16
16
  */
17
17
  export declare function detectVue(page: Page): Promise<VueInfo | null>;
18
18
  export interface ReadyOptions {
@@ -11,7 +11,7 @@ import { MAX_COMPONENT_ENTRIES, MAX_COMPONENT_TREE_NODES, MAX_COMPONENT_VALUE_LE
11
11
  * the address bar. Production builds keep only the app handle, and say so.
12
12
  *
13
13
  * Everything in-page below is serialised into Chromium: it may not close over
14
- * a module value (CLAUDE.md, decision 50), so every cap is passed in.
14
+ * a module value, so every cap is passed in.
15
15
  */
16
16
  export async function detectVue(page) {
17
17
  try {
@@ -75,7 +75,7 @@ export async function routerNavigate(page, path) {
75
75
  /* ── In-page ──────────────────────────────────────────────────────────────
76
76
  * Each function below is serialised whole into Chromium, so each carries its
77
77
  * own copy of `findApp`: a shared module-level helper would be a
78
- * ReferenceError in the page (CLAUDE.md, decision 50).
78
+ * ReferenceError in the page.
79
79
  */
80
80
  function detectInPage() {
81
81
  const doc = globalThis.document;