@agent-browser-io/browser 0.1.0 → 0.2.0
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 +70 -0
- package/bin/agent-browser-cli.cjs +2 -0
- package/bin/agent-browser-mcp.cjs +2 -0
- package/bin/index.cjs +54 -0
- package/dist/cjs/ai-sdk/createBrowserTools.d.ts +58 -0
- package/dist/cjs/ai-sdk/createBrowserTools.d.ts.map +1 -0
- package/dist/cjs/ai-sdk/createBrowserTools.js +192 -0
- package/dist/cjs/ai-sdk/createBrowserTools.js.map +1 -0
- package/dist/cjs/cli/cli.d.ts +6 -0
- package/dist/cjs/cli/cli.d.ts.map +1 -0
- package/dist/cjs/cli/cli.js +149 -0
- package/dist/cjs/cli/cli.js.map +1 -0
- package/dist/cjs/core/agent-browser/agent-browser.d.ts +51 -0
- package/dist/cjs/core/agent-browser/agent-browser.d.ts.map +1 -0
- package/dist/cjs/core/agent-browser/agent-browser.js +80 -0
- package/dist/cjs/core/agent-browser/agent-browser.js.map +1 -0
- package/dist/cjs/core/agent-browser/normalize-script.d.ts +3 -0
- package/dist/cjs/core/agent-browser/normalize-script.d.ts.map +1 -0
- package/dist/cjs/core/agent-browser/normalize-script.js +531 -0
- package/dist/cjs/core/agent-browser/normalize-script.js.map +1 -0
- package/dist/cjs/core/browser-backend/browser-backend.d.ts +23 -0
- package/dist/cjs/core/browser-backend/browser-backend.d.ts.map +1 -0
- package/dist/cjs/core/browser-backend/browser-backend.js +6 -0
- package/dist/cjs/core/browser-backend/browser-backend.js.map +1 -0
- package/dist/cjs/core/browser-backend/index.d.ts +3 -0
- package/dist/cjs/core/browser-backend/index.d.ts.map +1 -0
- package/dist/cjs/core/browser-backend/index.js +6 -0
- package/dist/cjs/core/browser-backend/index.js.map +1 -0
- package/dist/cjs/core/browser-backend/playwright-browser-backend.d.ts +27 -0
- package/dist/cjs/core/browser-backend/playwright-browser-backend.d.ts.map +1 -0
- package/dist/cjs/core/browser-backend/playwright-browser-backend.js +86 -0
- package/dist/cjs/core/browser-backend/playwright-browser-backend.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/mcp/server.d.ts +5 -0
- package/dist/cjs/mcp/server.d.ts.map +1 -0
- package/dist/cjs/mcp/server.js +284 -0
- package/dist/cjs/mcp/server.js.map +1 -0
- package/dist/esm/ai-sdk/createBrowserTools.d.ts +58 -0
- package/dist/esm/ai-sdk/createBrowserTools.d.ts.map +1 -0
- package/dist/esm/ai-sdk/createBrowserTools.js +189 -0
- package/dist/esm/ai-sdk/createBrowserTools.js.map +1 -0
- package/dist/esm/cli/cli.d.ts +6 -0
- package/dist/esm/cli/cli.d.ts.map +1 -0
- package/dist/esm/cli/cli.js +114 -0
- package/dist/esm/cli/cli.js.map +1 -0
- package/dist/esm/core/agent-browser/agent-browser.d.ts +51 -0
- package/dist/esm/core/agent-browser/agent-browser.d.ts.map +1 -0
- package/dist/esm/core/agent-browser/agent-browser.js +76 -0
- package/dist/esm/core/agent-browser/agent-browser.js.map +1 -0
- package/dist/esm/core/agent-browser/normalize-script.d.ts +3 -0
- package/dist/esm/core/agent-browser/normalize-script.d.ts.map +1 -0
- package/dist/esm/core/agent-browser/normalize-script.js +528 -0
- package/dist/esm/core/agent-browser/normalize-script.js.map +1 -0
- package/dist/esm/core/browser-backend/browser-backend.d.ts +23 -0
- package/dist/esm/core/browser-backend/browser-backend.d.ts.map +1 -0
- package/dist/esm/core/browser-backend/browser-backend.js +5 -0
- package/dist/esm/core/browser-backend/browser-backend.js.map +1 -0
- package/dist/esm/core/browser-backend/index.d.ts +3 -0
- package/dist/esm/core/browser-backend/index.d.ts.map +1 -0
- package/dist/esm/core/browser-backend/index.js +2 -0
- package/dist/esm/core/browser-backend/index.js.map +1 -0
- package/dist/esm/core/browser-backend/playwright-browser-backend.d.ts +27 -0
- package/dist/esm/core/browser-backend/playwright-browser-backend.d.ts.map +1 -0
- package/dist/esm/core/browser-backend/playwright-browser-backend.js +82 -0
- package/dist/esm/core/browser-backend/playwright-browser-backend.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/mcp/server.d.ts +5 -0
- package/dist/esm/mcp/server.d.ts.map +1 -0
- package/dist/esm/mcp/server.js +282 -0
- package/dist/esm/mcp/server.js.map +1 -0
- package/package.json +14 -5
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentBrowser = void 0;
|
|
4
|
+
const normalize_script_js_1 = require("./normalize-script.js");
|
|
5
|
+
class AgentBrowser {
|
|
6
|
+
constructor(browserBackend) {
|
|
7
|
+
this.browserBackend = browserBackend;
|
|
8
|
+
}
|
|
9
|
+
async injectNormalizeScript() {
|
|
10
|
+
await this.browserBackend.evaluate(normalize_script_js_1.NORMALIZE_SCRIPT);
|
|
11
|
+
}
|
|
12
|
+
async launch() {
|
|
13
|
+
await this.browserBackend.launch();
|
|
14
|
+
}
|
|
15
|
+
async navigate(url) {
|
|
16
|
+
await this.browserBackend.navigate(url);
|
|
17
|
+
await this.injectNormalizeScript();
|
|
18
|
+
}
|
|
19
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
20
|
+
async click(ref) {
|
|
21
|
+
await this.browserBackend.click(`[data-ref-id="${ref}"]`);
|
|
22
|
+
await this.injectNormalizeScript();
|
|
23
|
+
}
|
|
24
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
25
|
+
async type(ref, text) {
|
|
26
|
+
await this.browserBackend.type(`[data-ref-id="${ref}"]`, text);
|
|
27
|
+
await this.injectNormalizeScript();
|
|
28
|
+
}
|
|
29
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
30
|
+
async dblclick(ref) {
|
|
31
|
+
await this.browserBackend.dblclick(`[data-ref-id="${ref}"]`);
|
|
32
|
+
await this.injectNormalizeScript();
|
|
33
|
+
}
|
|
34
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
35
|
+
async fill(ref, text) {
|
|
36
|
+
await this.browserBackend.fill(`[data-ref-id="${ref}"]`, text);
|
|
37
|
+
await this.injectNormalizeScript();
|
|
38
|
+
}
|
|
39
|
+
async press(key) {
|
|
40
|
+
await this.browserBackend.press(key);
|
|
41
|
+
await this.injectNormalizeScript();
|
|
42
|
+
}
|
|
43
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
44
|
+
async hover(ref) {
|
|
45
|
+
await this.browserBackend.hover(`[data-ref-id="${ref}"]`);
|
|
46
|
+
await this.injectNormalizeScript();
|
|
47
|
+
}
|
|
48
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
49
|
+
async select(ref, value) {
|
|
50
|
+
await this.browserBackend.select(`[data-ref-id="${ref}"]`, value);
|
|
51
|
+
await this.injectNormalizeScript();
|
|
52
|
+
}
|
|
53
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
54
|
+
async check(ref) {
|
|
55
|
+
await this.browserBackend.check(`[data-ref-id="${ref}"]`);
|
|
56
|
+
await this.injectNormalizeScript();
|
|
57
|
+
}
|
|
58
|
+
/** Ref is the data-ref-id value from the wireframe (e.g. "1"). */
|
|
59
|
+
async uncheck(ref) {
|
|
60
|
+
await this.browserBackend.uncheck(`[data-ref-id="${ref}"]`);
|
|
61
|
+
await this.injectNormalizeScript();
|
|
62
|
+
}
|
|
63
|
+
async scroll(direction, pixels) {
|
|
64
|
+
await this.browserBackend.scroll(direction, pixels);
|
|
65
|
+
await this.injectNormalizeScript();
|
|
66
|
+
}
|
|
67
|
+
async screenshot(path, options) {
|
|
68
|
+
return this.browserBackend.screenshot(path, options);
|
|
69
|
+
}
|
|
70
|
+
async close() {
|
|
71
|
+
await this.browserBackend.close();
|
|
72
|
+
}
|
|
73
|
+
async getWireframe() {
|
|
74
|
+
await this.injectNormalizeScript();
|
|
75
|
+
const result = await this.browserBackend.evaluate("typeof window.generateWireframeString === 'function' ? window.generateWireframeString() : ''");
|
|
76
|
+
return typeof result === "string" ? result : "";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.AgentBrowser = AgentBrowser;
|
|
80
|
+
//# sourceMappingURL=agent-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-browser.js","sourceRoot":"","sources":["../../../../src/core/agent-browser/agent-browser.ts"],"names":[],"mappings":";;;AACA,+DAAyD;AAoBzD,MAAa,YAAY;IACrB,YAA6B,cAA+B;QAA/B,mBAAc,GAAd,cAAc,CAAiB;IAAG,CAAC;IAExD,KAAK,CAAC,qBAAqB;QAC/B,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,sCAAgB,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,MAAM;QACR,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAW;QACtB,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,KAAK,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,IAAY;QAChC,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,iBAAiB,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,QAAQ,CAAC,GAAW;QACtB,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;QAC7D,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,IAAY;QAChC,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,iBAAiB,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,KAAK,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,KAAa;QACnC,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,iBAAiB,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;QAClE,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,KAAK,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,OAAO,CAAC,GAAW;QACrB,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;QAC5D,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,MAAM,CACR,SAA2C,EAC3C,MAAe;QAEf,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,UAAU,CACZ,IAAa,EACb,OAAgC;QAEhC,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,KAAK;QACP,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,YAAY;QACd,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAC7C,8FAA8F,CACjG,CAAC;QACF,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,CAAC;CACJ;AA/FD,oCA+FC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Injected browser script for wireframe normalization. Exported as string for evaluation in the page. */
|
|
2
|
+
export declare const NORMALIZE_SCRIPT = "\n// This script is intended to be used in the browser.\n// It is not intended to be used in node.\n(function applyWireframeMode() {\n // --- PART A: INJECT CSS STYLES (idempotent) ---\n if (!document.getElementById(\"wf-normalize\")) {\n const cssStyles = `\n /* FORCE GLOBAL MONOSPACE & METRICS */\n * {\n font-family: \"Courier New\", Courier, monospace !important;\n font-size: 12px !important;\n font-weight: normal !important;\n color: #000000 !important;\n line-height: 18px !important;\n letter-spacing: 0px !important;\n box-shadow: none !important;\n text-shadow: none !important;\n border-radius: 0 !important;\n transition: none !important;\n }\n\n /* HIGH CONTRAST THEME */\n body {\n background-color: #ffffff !important;\n color: #000000 !important;\n }\n\n /* STRUCTURAL OUTLINES */\n div, section, article, header, footer, nav, aside, main {\n border: 1px dotted #cccccc !important;\n }\n\n /* KILL PSEUDO-ELEMENT BACKGROUNDS */\n *::before, *::after {\n background-image: none !important;\n background-color: transparent !important;\n border: none !important;\n }\n\n /* INTERACTIVE ELEMENTS */\n a, button, input[type=\"submit\"], input[type=\"button\"], [role=\"button\"] {\n border: 2px solid #000000 !important;\n background-color: #ffffff !important;\n color: #000000 !important;\n text-transform: uppercase !important;\n text-decoration: none !important;\n font-weight: bold !important;\n }\n\n /* FORM INPUTS */\n input, textarea, select {\n border: 1px solid #000000 !important;\n background-color: #ffffff !important;\n color: #000000 !important;\n font-family: monospace !important;\n }\n\n /* HIDE DECORATIVE MEDIA */\n img, video, canvas, svg {\n opacity: 0.5 !important;\n filter: grayscale(100%) !important;\n border: 1px dashed #000 !important;\n }\n\n /* BACKGROUND IMAGE INDICATORS */\n [data-bg-image=\"true\"] {\n background-color: #f4f4f4 !important;\n border: 1px dashed #555555 !important;\n position: relative !important;\n }\n [data-bg-image=\"true\"]::before {\n content: \"[ BG IMAGE ]\";\n position: absolute;\n top: 0; right: 0;\n background: #000000; color: #ffffff;\n font-size: 10px !important;\n padding: 2px 4px;\n opacity: 0.8;\n z-index: 9999;\n pointer-events: none;\n font-weight: normal !important;\n }\n `;\n\n const styleSheet = document.createElement(\"style\");\n styleSheet.id = \"wf-normalize\";\n styleSheet.type = \"text/css\";\n styleSheet.innerText = cssStyles;\n document.head.appendChild(styleSheet);\n }\n\n // --- PART B: NORMALIZE DOM CONTENT (idempotent) ---\n if (!document.body) return; // page not ready\n\n // Helper: check if element is the top-most (not behind an overlay) at its center\n function isElementVisible(el) {\n var rect = el.getBoundingClientRect();\n var cx = rect.left + rect.width / 2;\n var cy = rect.top + rect.height / 2;\n if (cx < 0 || cy < 0 || cx >= window.innerWidth || cy >= window.innerHeight) return false;\n var topEl = document.elementFromPoint(cx, cy);\n if (!topEl) return false;\n return el.contains(topEl) || topEl.contains(el);\n }\n\n // Remove old ref IDs and reset counter\n document.querySelectorAll(\"[data-ref-id]\").forEach(function(el) {\n el.removeAttribute(\"data-ref-id\");\n });\n var refCounter = 1;\n\n // Only run DOM mutations (media replacement, accessibility labels, bg stripping) once\n if (!document.body.hasAttribute(\"data-wf-normalized\")) {\n document.body.setAttribute(\"data-wf-normalized\", \"true\");\n\n // 1. REVEAL ACCESSIBILITY LABELS (ICON BUTTONS)\n var interactives = document.querySelectorAll('a, button, [role=\"button\"]');\n interactives.forEach(function(el) {\n if (el.innerText.trim() === '') {\n var label = el.getAttribute('aria-label') || el.getAttribute('title');\n // Also check child elements for hints (e.g. HN vote arrows: <a><div title=\"upvote\"></a>)\n if (!label) {\n var hintEl = el.querySelector('[aria-label], [title]');\n if (hintEl) label = hintEl.getAttribute('aria-label') || hintEl.getAttribute('title');\n }\n if (label) {\n el.innerText = label;\n el.style.display = 'inline-block';\n el.style.width = 'auto';\n el.style.height = 'auto';\n }\n }\n });\n\n // 2. REPLACE MEDIA WITH INTELLIGENT PLACEHOLDERS\n var mediaElements = document.querySelectorAll('img, svg, video, canvas');\n mediaElements.forEach(function(el) {\n var rect = el.getBoundingClientRect();\n if (rect.width < 10 || rect.height < 10) return;\n\n var placeholder = document.createElement('div');\n placeholder.style.cssText =\n 'width:' + rect.width + 'px;' +\n 'height:' + rect.height + 'px;' +\n 'background:#f0f0f0;color:#000;font-family:monospace;font-weight:bold;' +\n 'display:flex;align-items:center;justify-content:center;text-align:center;' +\n 'overflow:hidden;box-sizing:border-box;';\n\n var altText = el.getAttribute('alt') ||\n el.getAttribute('title') ||\n el.getAttribute('aria-label');\n\n if (!altText && el.tagName.toLowerCase() === 'svg') {\n var titleEl = el.querySelector('title');\n if (titleEl) altText = titleEl.textContent;\n }\n\n if (rect.width <= 50 && rect.height <= 50) {\n placeholder.style.border = '1px solid #333';\n placeholder.style.fontSize = '8px';\n placeholder.style.padding = '0';\n // Use short label for tiny icons to avoid overflow\n if (rect.width <= 20 || rect.height <= 20) {\n placeholder.innerText = altText ? altText.substring(0, 1) : '*';\n } else {\n var iconLabel = altText ? altText.substring(0, 8) : \"ICON\";\n placeholder.innerText = '[' + iconLabel + ']';\n }\n } else {\n placeholder.style.border = '2px solid #000';\n placeholder.style.fontSize = '10px';\n placeholder.style.padding = '2px';\n placeholder.style.wordBreak = 'break-word';\n var imgLabel = altText ? altText : \"IMAGE\";\n placeholder.innerText = '[' + imgLabel + ']';\n }\n\n if (el.parentNode) {\n el.parentNode.replaceChild(placeholder, el);\n }\n });\n\n // 3. CONVERT BACKGROUND IMAGES & STRIP COLORS\n var allElements = document.querySelectorAll('*');\n allElements.forEach(function(el) {\n var style = window.getComputedStyle(el);\n var hasBgImage = style.backgroundImage !== 'none' && style.backgroundImage !== '';\n var hasBgColor = style.backgroundColor !== 'rgba(0, 0, 0, 0)' &&\n style.backgroundColor !== 'transparent' &&\n style.backgroundColor !== 'rgb(255, 255, 255)';\n\n if (hasBgImage) {\n el.style.backgroundImage = 'none';\n el.setAttribute('data-bg-image', 'true');\n }\n\n if (hasBgColor) {\n el.style.backgroundColor = '#ffffff';\n }\n });\n }\n\n // 4. REMOVE TRANSPARENT OVERLAY BACKDROPS (run every time \u2014 new overlays can appear)\n // Full-viewport high z-index divs with no meaningful text are just backdrop overlays\n // that block elementFromPoint for content behind them\n var overlayDivs = document.querySelectorAll('div');\n overlayDivs.forEach(function(el) {\n var style = window.getComputedStyle(el);\n var z = parseInt(style.zIndex) || 0;\n if (z < 100 || style.display === 'none') return;\n var rect = el.getBoundingClientRect();\n if (rect.width < window.innerWidth * 0.9 || rect.height < window.innerHeight * 0.9) return;\n var text = (el.innerText || '').trim();\n if (text.length < 5) {\n el.style.display = 'none';\n }\n });\n\n // Overflow buffer: capture high-z-index elements pushed below viewport by CSS normalization\n var OVERFLOW_BUFFER = 200;\n var captureHeight = window.innerHeight + OVERFLOW_BUFFER;\n\n // 5. TAG INTERACTIVE ELEMENTS (always re-run since we cleared ref IDs)\n // Only tag elements visible, in viewport (+ overflow buffer), and not behind overlays\n function tagIfVisible(el) {\n if (el.hasAttribute('data-ref-id')) return; // already tagged\n var rect = el.getBoundingClientRect();\n var style = window.getComputedStyle(el);\n if (rect.width === 0 || rect.height === 0 || style.display === 'none' || style.visibility === 'hidden') return;\n if (rect.bottom < 0 || rect.top >= captureHeight || rect.right < 0 || rect.left >= window.innerWidth) return;\n // Elements in overflow zone (below viewport): can't use elementFromPoint, just tag them\n if (rect.top >= window.innerHeight) {\n el.setAttribute('data-ref-id', String(refCounter++));\n return;\n }\n if (!isElementVisible(el)) return;\n el.setAttribute('data-ref-id', String(refCounter++));\n }\n\n // Pass 1: semantic interactive elements\n var allInteractives = document.querySelectorAll('a, button, input, textarea, select, [role=\"button\"], [onclick], [tabindex=\"0\"]');\n allInteractives.forEach(tagIfVisible);\n\n // Pass 2: non-semantic clickable elements (divs/spans with cursor:pointer and text)\n // This catches sites that use <div> as buttons without proper ARIA roles\n var potentialClickables = document.querySelectorAll('div, span');\n potentialClickables.forEach(function(el) {\n if (el.hasAttribute('data-ref-id')) return;\n // Skip if already inside a tagged interactive element\n if (el.closest && el.closest('[data-ref-id]')) return;\n var style = window.getComputedStyle(el);\n if (style.cursor !== 'pointer') return;\n // Must have direct text content (not just children with text)\n var hasDirectText = false;\n for (var i = 0; i < el.childNodes.length; i++) {\n if (el.childNodes[i].nodeType === 3 && el.childNodes[i].textContent.trim()) {\n hasDirectText = true;\n break;\n }\n }\n if (!hasDirectText) return;\n tagIfVisible(el);\n });\n\n console.log(\"Normalization Complete.\");\n\n // --- PART C: WIREFRAME STRING GENERATOR ---\n function generateWireframeString() {\n // 1. Measure CHAR_W dynamically\n var probe = document.createElement('span');\n probe.style.cssText = 'font-family:\"Courier New\",Courier,monospace;font-size:12px;line-height:18px;letter-spacing:0px;position:absolute;top:-9999px;left:-9999px;white-space:pre;visibility:hidden;';\n probe.textContent = 'MMMMMMMMMM';\n document.body.appendChild(probe);\n var CHAR_W = probe.getBoundingClientRect().width / 10;\n document.body.removeChild(probe);\n if (CHAR_W <= 0) CHAR_W = 7.2; // fallback\n\n var CHAR_H = 18;\n\n // 2. Create grid (includes overflow buffer for elements pushed below viewport)\n var OVERFLOW_BUFFER_C = 200;\n var captureHeightC = window.innerHeight + OVERFLOW_BUFFER_C;\n var gridWidth = Math.ceil(window.innerWidth / CHAR_W);\n var gridHeight = Math.ceil(captureHeightC / CHAR_H);\n var grid = [];\n for (var r = 0; r < gridHeight; r++) {\n var row = [];\n for (var c = 0; c < gridWidth; c++) {\n row.push(' ');\n }\n grid.push(row);\n }\n\n function writeToGrid(x, y, str) {\n if (y < 0 || y >= gridHeight) return;\n for (var i = 0; i < str.length; i++) {\n var curX = x + i;\n if (curX >= 0 && curX < gridWidth) {\n grid[y][curX] = str[i];\n }\n }\n }\n\n // 3. Draw borders only for block-level interactive elements (buttons, form controls)\n // Skip inline links \u2014 they just get ref labels.\n var refElements = document.querySelectorAll('[data-ref-id]');\n refElements.forEach(function(el) {\n var rect = el.getBoundingClientRect();\n var style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden' || rect.width === 0 || rect.height === 0) return;\n // Skip elements outside capture area\n if (rect.bottom < 0 || rect.top >= captureHeightC || rect.right < 0 || rect.left >= window.innerWidth) return;\n\n // Only draw borders for buttons, form elements, and block-level role=button\n var tag = el.tagName;\n var isFormEl = (tag === 'BUTTON' || tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT');\n var isBlockButton = (el.getAttribute('role') === 'button' && style.display !== 'inline');\n if (!isFormEl && !isBlockButton) return;\n\n var x = Math.floor(rect.left / CHAR_W);\n var y = Math.floor(rect.top / CHAR_H);\n var w = Math.max(2, Math.ceil(rect.width / CHAR_W));\n var h = Math.max(2, Math.ceil(rect.height / CHAR_H));\n\n // Top border\n writeToGrid(x, y, '+' + repeat('-', Math.max(0, w - 2)) + '+');\n // Side borders\n for (var i = 1; i < h - 1; i++) {\n writeToGrid(x, y + i, '|');\n writeToGrid(x + w - 1, y + i, '|');\n }\n // Bottom border\n if (h > 1) {\n writeToGrid(x, y + h - 1, '+' + repeat('-', Math.max(0, w - 2)) + '+');\n }\n });\n\n // Helper: check if element is the top-most at a given point\n function isVisibleAt(el, px, py) {\n if (px < 0 || py < 0 || px >= window.innerWidth) return false;\n // Overflow zone: elementFromPoint doesn't work below viewport, assume visible\n if (py >= window.innerHeight) return py < captureHeightC;\n var topEl = document.elementFromPoint(px, py);\n if (!topEl) return false;\n return el.contains(topEl) || topEl.contains(el);\n }\n\n // 4. Render text via TreeWalker (text only, no labels yet)\n var labeledRefs = {}; // refId -> {row, col} where the element's first text starts\n var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);\n var textNode;\n while ((textNode = walker.nextNode())) {\n var parent = textNode.parentElement;\n if (!parent) continue;\n\n // Skip script/style\n var tag = parent.tagName;\n if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT') continue;\n\n // Skip invisible parents\n var pStyle = window.getComputedStyle(parent);\n if (pStyle.display === 'none' || pStyle.visibility === 'hidden' || pStyle.opacity === '0') continue;\n\n // Skip empty text\n var rawText = textNode.textContent;\n if (!rawText || !rawText.trim()) continue;\n\n // Skip form elements (handled separately)\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || tag === 'OPTION') continue;\n\n // Collapse whitespace\n var collapsed = rawText.replace(/\\s+/g, ' ').trim();\n if (!collapsed) continue;\n\n // Track first text position for ref label insertion\n var refAncestor = parent.closest ? parent.closest('[data-ref-id]') : null;\n\n // Use Range + getClientRects for accurate per-line positioning\n var range = document.createRange();\n range.selectNodeContents(textNode);\n var rects = range.getClientRects();\n\n if (rects.length === 0) continue;\n\n var charOffset = 0;\n var firstWritten = false;\n for (var ri = 0; ri < rects.length; ri++) {\n var rr = rects[ri];\n if (rr.width < 1 || rr.height < 1) continue;\n\n // Skip rects outside capture area or covered by overlays\n if (rr.bottom < 0 || rr.top >= captureHeightC || rr.right < 0 || rr.left >= window.innerWidth) {\n charOffset += Math.max(1, Math.ceil(rr.width / CHAR_W));\n continue;\n }\n var rcx = rr.left + rr.width / 2;\n var rcy = rr.top + rr.height / 2;\n if (!isVisibleAt(parent, rcx, rcy)) {\n charOffset += Math.max(1, Math.ceil(rr.width / CHAR_W));\n continue;\n }\n\n var gx = Math.floor(rr.left / CHAR_W);\n var gy = Math.floor(rr.top / CHAR_H);\n var charsInLine = Math.max(1, Math.ceil(rr.width / CHAR_W));\n // Cap to actual remaining text length\n var remaining = collapsed.length - charOffset;\n if (charsInLine > remaining) charsInLine = remaining;\n var slice = collapsed.substring(charOffset, charOffset + charsInLine);\n if (slice) {\n writeToGrid(gx, gy, slice);\n\n // Record insertion point for this ref's label\n if (!firstWritten && refAncestor) {\n var rid = refAncestor.getAttribute('data-ref-id');\n if (!labeledRefs[rid]) {\n labeledRefs[rid] = {row: gy, col: gx};\n }\n firstWritten = true;\n }\n }\n charOffset += charsInLine;\n }\n }\n\n // 5. Handle form inputs\n var formEls = document.querySelectorAll('input, textarea, select');\n formEls.forEach(function(el) {\n var rect = el.getBoundingClientRect();\n var style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden' || rect.width === 0 || rect.height === 0) return;\n\n var x = Math.floor(rect.left / CHAR_W);\n var y = Math.floor(rect.top / CHAR_H);\n var w = Math.max(2, Math.ceil(rect.width / CHAR_W));\n var h = Math.max(2, Math.ceil(rect.height / CHAR_H));\n var midY = y + Math.floor((h - 1) / 2);\n var refId = el.getAttribute('data-ref-id');\n\n if (el.tagName === 'SELECT') {\n var selectedText = el.options && el.selectedIndex >= 0 ? el.options[el.selectedIndex].text : '';\n var selectContent = selectedText.substring(0, Math.max(0, w - 4));\n if (selectContent) writeToGrid(x + 1, midY, selectContent);\n writeToGrid(x + w - 2, midY, 'v');\n } else {\n var val = el.value || el.getAttribute('placeholder') || '';\n if (val) writeToGrid(x + 1, midY, val);\n }\n\n // Track form element position for label insertion\n if (refId && !labeledRefs[refId]) {\n labeledRefs[refId] = {row: midY, col: x};\n }\n });\n\n // Also track ref elements with no text at all (empty buttons, icons)\n refElements.forEach(function(el) {\n var refId = el.getAttribute('data-ref-id');\n if (labeledRefs[refId]) return;\n var rect = el.getBoundingClientRect();\n var style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden' || rect.width === 0 || rect.height === 0) return;\n labeledRefs[refId] = {row: Math.floor(rect.top / CHAR_H), col: Math.floor(rect.left / CHAR_W)};\n });\n\n // 6. Insert ref labels by splicing into rows (never overwrites text)\n // Collect all labels per row, sorted right-to-left so insertions don't shift later positions\n var rowInsertions = {}; // row -> [{col, label}]\n refElements.forEach(function(el) {\n var refId = el.getAttribute('data-ref-id');\n if (!refId) return;\n var pos = labeledRefs[refId];\n if (!pos) return;\n var label = '[' + refId + ']';\n if (!rowInsertions[pos.row]) rowInsertions[pos.row] = [];\n rowInsertions[pos.row].push({col: pos.col, label: label});\n });\n\n // For each row with insertions, splice labels into the row string\n for (var rowIdx in rowInsertions) {\n var r = parseInt(rowIdx, 10);\n if (r < 0 || r >= gridHeight) continue;\n var inserts = rowInsertions[r];\n // Sort by column descending so we insert right-to-left (preserves earlier positions)\n inserts.sort(function(a, b) { return b.col - a.col; });\n // Convert row to string, splice in labels, write back\n var rowStr = grid[r].join('');\n for (var ii = 0; ii < inserts.length; ii++) {\n var ins = inserts[ii];\n var c = Math.max(0, Math.min(ins.col, rowStr.length));\n // Avoid splitting inside a [...] bracket sequence \u2014 shift to before the '['\n var openBracket = rowStr.lastIndexOf('[', c);\n if (openBracket >= 0) {\n var closeBracket = rowStr.indexOf(']', openBracket);\n if (closeBracket >= c) {\n // Insertion point is inside [..], shift to before the [\n c = openBracket;\n }\n }\n rowStr = rowStr.substring(0, c) + ins.label + rowStr.substring(c);\n }\n // Write back to grid as variable-width row (will be joined as string in step 7)\n grid[r] = rowStr.split('');\n }\n\n // 7. Trim output\n var lines = [];\n for (var r = 0; r < gridHeight; r++) {\n lines.push(grid[r].join('').replace(/\\s+$/, ''));\n }\n // Drop trailing empty rows\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n return lines.join('\\n');\n }\n\n function repeat(ch, count) {\n var s = '';\n for (var i = 0; i < count; i++) s += ch;\n return s;\n }\n\n window.generateWireframeString = generateWireframeString;\n})();\n";
|
|
3
|
+
//# sourceMappingURL=normalize-script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-script.d.ts","sourceRoot":"","sources":["../../../../src/core/agent-browser/normalize-script.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,eAAO,MAAM,gBAAgB,44uBA6gB5B,CAAC"}
|