playwriter 0.0.105 → 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/dist/bippy.js +5 -5
- package/dist/cdp-relay.d.ts.map +1 -1
- package/dist/cdp-relay.js +17 -5
- package/dist/cdp-relay.js.map +1 -1
- package/dist/cli-help.test.d.ts +2 -0
- package/dist/cli-help.test.d.ts.map +1 -0
- package/dist/cli-help.test.js +53 -0
- package/dist/cli-help.test.js.map +1 -0
- package/dist/cli.js +74 -25
- package/dist/cli.js.map +1 -1
- package/dist/executor.d.ts +1 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +55 -12
- package/dist/executor.js.map +1 -1
- package/dist/extension/background.js +675 -27
- package/dist/extension/manifest.json +1 -1
- package/dist/ghost-cursor-client.js +170 -83
- package/dist/{recording-ghost-cursor.d.ts → ghost-cursor-controller.d.ts} +15 -10
- package/dist/ghost-cursor-controller.d.ts.map +1 -0
- package/dist/ghost-cursor-controller.js +98 -0
- package/dist/ghost-cursor-controller.js.map +1 -0
- package/dist/ghost-cursor.d.ts.map +1 -1
- package/dist/ghost-cursor.js +42 -26
- package/dist/ghost-cursor.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +6 -1
- package/dist/mcp.js.map +1 -1
- package/dist/on-mouse-action.test.js +25 -0
- package/dist/on-mouse-action.test.js.map +1 -1
- package/dist/performance-examples.d.ts +5 -0
- package/dist/performance-examples.d.ts.map +1 -0
- package/dist/performance-examples.js +112 -0
- package/dist/performance-examples.js.map +1 -0
- package/dist/performance-profiling.md +417 -0
- package/dist/prompt.md +22 -8
- package/dist/react-source.d.ts +44 -0
- package/dist/react-source.d.ts.map +1 -1
- package/dist/react-source.js +207 -20
- package/dist/react-source.js.map +1 -1
- package/dist/readability.js +1 -1
- package/dist/relay-core.test.d.ts.map +1 -1
- package/dist/relay-core.test.js +101 -1
- package/dist/relay-core.test.js.map +1 -1
- package/dist/relay-session.test.js +34 -6
- package/dist/relay-session.test.js.map +1 -1
- package/dist/screen-recording.d.ts +2 -2
- package/dist/screen-recording.d.ts.map +1 -1
- package/dist/screen-recording.js +19 -7
- package/dist/screen-recording.js.map +1 -1
- package/dist/selector-generator.js +1 -1
- package/package.json +7 -7
- package/src/aria-snapshots/github-interactive.txt +5 -3
- package/src/aria-snapshots/github-raw.txt +8 -5
- package/src/aria-snapshots/hackernews-interactive.txt +241 -238
- package/src/aria-snapshots/hackernews-raw.txt +269 -265
- package/src/aria-snapshots/prosemirror-interactive.txt +3 -1
- package/src/aria-snapshots/prosemirror-raw.txt +4 -1
- package/src/assets/aria-labels-hacker-news.png +0 -0
- package/src/assets/aria-labels-old-reddit.png +0 -0
- package/src/cdp-relay.ts +17 -5
- package/src/cli-help.test.ts +63 -0
- package/src/cli.ts +80 -28
- package/src/executor.ts +65 -15
- package/src/ghost-cursor-client.ts +221 -96
- package/src/{recording-ghost-cursor.ts → ghost-cursor-controller.ts} +50 -34
- package/src/ghost-cursor.ts +54 -41
- package/src/mcp.ts +6 -1
- package/src/on-mouse-action.test.ts +30 -0
- package/src/performance-examples.ts +186 -0
- package/src/react-source.ts +310 -24
- package/src/relay-core.test.ts +117 -0
- package/src/relay-session.test.ts +36 -10
- package/src/screen-recording.ts +23 -10
- package/src/skill.md +33 -9
- package/src/snapshots/shadcn-ui-accessibility-full.md +6 -3
- package/src/snapshots/shadcn-ui-accessibility-interactive.md +2 -0
- package/dist/recording-ghost-cursor.d.ts.map +0 -1
- package/dist/recording-ghost-cursor.js +0 -79
- package/dist/recording-ghost-cursor.js.map +0 -1
|
@@ -1,4 +1,173 @@
|
|
|
1
|
-
//#region ../node_modules/.pnpm/
|
|
1
|
+
//#region ../node_modules/.pnpm/string-dedent@3.0.2/node_modules/string-dedent/dist/dedent.mjs
|
|
2
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
var newline = /(\n|\r\n?|\u2028|\u2029)/g;
|
|
4
|
+
var leadingWhitespace = /^\s*/;
|
|
5
|
+
var nonWhitespace = /\S/;
|
|
6
|
+
var slice = Array.prototype.slice;
|
|
7
|
+
var zero = "0".charCodeAt(0);
|
|
8
|
+
var nine = "9".charCodeAt(0);
|
|
9
|
+
var lowerA = "a".charCodeAt(0);
|
|
10
|
+
var lowerF = "f".charCodeAt(0);
|
|
11
|
+
var upperA = "A".charCodeAt(0);
|
|
12
|
+
var upperF = "F".charCodeAt(0);
|
|
13
|
+
function dedent(arg) {
|
|
14
|
+
if (typeof arg === "string") return process([arg])[0];
|
|
15
|
+
if (typeof arg === "function") return function() {
|
|
16
|
+
const args = slice.call(arguments);
|
|
17
|
+
args[0] = processTemplateStringsArray(args[0]);
|
|
18
|
+
return arg.apply(this, args);
|
|
19
|
+
};
|
|
20
|
+
const strings = processTemplateStringsArray(arg);
|
|
21
|
+
let s = getCooked(strings, 0);
|
|
22
|
+
for (let i = 1; i < strings.length; i++) s += arguments[i] + getCooked(strings, i);
|
|
23
|
+
return s;
|
|
24
|
+
}
|
|
25
|
+
function getCooked(strings, index) {
|
|
26
|
+
const str = strings[index];
|
|
27
|
+
if (str === void 0) throw new TypeError(`invalid cooked string at index ${index}`);
|
|
28
|
+
return str;
|
|
29
|
+
}
|
|
30
|
+
function processTemplateStringsArray(strings) {
|
|
31
|
+
const cached = cache.get(strings);
|
|
32
|
+
if (cached) return cached;
|
|
33
|
+
const raw = process(strings.raw);
|
|
34
|
+
const cooked = raw.map(cook);
|
|
35
|
+
Object.defineProperty(cooked, "raw", { value: Object.freeze(raw) });
|
|
36
|
+
Object.freeze(cooked);
|
|
37
|
+
cache.set(strings, cooked);
|
|
38
|
+
return cooked;
|
|
39
|
+
}
|
|
40
|
+
function process(strings) {
|
|
41
|
+
const splitQuasis = strings.map((quasi) => quasi.split(newline));
|
|
42
|
+
let common;
|
|
43
|
+
for (let i = 0; i < splitQuasis.length; i++) {
|
|
44
|
+
const lines = splitQuasis[i];
|
|
45
|
+
const firstSplit = i === 0;
|
|
46
|
+
const lastSplit = i + 1 === splitQuasis.length;
|
|
47
|
+
if (firstSplit) {
|
|
48
|
+
if (lines.length === 1 || lines[0].length > 0) throw new Error("invalid content on opening line");
|
|
49
|
+
lines[1] = "";
|
|
50
|
+
}
|
|
51
|
+
if (lastSplit) {
|
|
52
|
+
if (lines.length === 1 || nonWhitespace.test(lines[lines.length - 1])) throw new Error("invalid content on closing line");
|
|
53
|
+
lines[lines.length - 2] = "";
|
|
54
|
+
lines[lines.length - 1] = "";
|
|
55
|
+
}
|
|
56
|
+
for (let j = 2; j < lines.length; j += 2) {
|
|
57
|
+
const text = lines[j];
|
|
58
|
+
const lineContainsTemplateExpression = j + 1 === lines.length && !lastSplit;
|
|
59
|
+
const leading = leadingWhitespace.exec(text)[0];
|
|
60
|
+
if (!lineContainsTemplateExpression && leading.length === text.length) {
|
|
61
|
+
lines[j] = "";
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
common = commonStart(leading, common);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const min = common ? common.length : 0;
|
|
68
|
+
return splitQuasis.map((lines) => {
|
|
69
|
+
let quasi = lines[0];
|
|
70
|
+
for (let i = 1; i < lines.length; i += 2) {
|
|
71
|
+
const newline = lines[i];
|
|
72
|
+
const text = lines[i + 1];
|
|
73
|
+
quasi += newline + text.slice(min);
|
|
74
|
+
}
|
|
75
|
+
return quasi;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function commonStart(a, b) {
|
|
79
|
+
if (b === void 0 || a === b) return a;
|
|
80
|
+
let i = 0;
|
|
81
|
+
for (const len = Math.min(a.length, b.length); i < len; i++) if (a[i] !== b[i]) break;
|
|
82
|
+
return a.slice(0, i);
|
|
83
|
+
}
|
|
84
|
+
function cook(raw) {
|
|
85
|
+
let out = "";
|
|
86
|
+
let start = 0;
|
|
87
|
+
let i = 0;
|
|
88
|
+
while ((i = raw.indexOf("\\", i)) > -1) {
|
|
89
|
+
out += raw.slice(start, i);
|
|
90
|
+
if (++i === raw.length) return void 0;
|
|
91
|
+
const next = raw[i++];
|
|
92
|
+
switch (next) {
|
|
93
|
+
case "b":
|
|
94
|
+
out += "\b";
|
|
95
|
+
break;
|
|
96
|
+
case "t":
|
|
97
|
+
out += " ";
|
|
98
|
+
break;
|
|
99
|
+
case "n":
|
|
100
|
+
out += "\n";
|
|
101
|
+
break;
|
|
102
|
+
case "v":
|
|
103
|
+
out += "\v";
|
|
104
|
+
break;
|
|
105
|
+
case "f":
|
|
106
|
+
out += "\f";
|
|
107
|
+
break;
|
|
108
|
+
case "r":
|
|
109
|
+
out += "\r";
|
|
110
|
+
break;
|
|
111
|
+
case "\r": if (i < raw.length && raw[i] === "\n") ++i;
|
|
112
|
+
case "\n":
|
|
113
|
+
case "\u2028":
|
|
114
|
+
case "\u2029": break;
|
|
115
|
+
case "0":
|
|
116
|
+
if (isDigit(raw, i)) return void 0;
|
|
117
|
+
out += "\0";
|
|
118
|
+
break;
|
|
119
|
+
case "x": {
|
|
120
|
+
const n = parseHex(raw, i, i + 2);
|
|
121
|
+
if (n === -1) return void 0;
|
|
122
|
+
i += 2;
|
|
123
|
+
out += String.fromCharCode(n);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case "u": {
|
|
127
|
+
let n;
|
|
128
|
+
if (i < raw.length && raw[i] === "{") {
|
|
129
|
+
const end = raw.indexOf("}", ++i);
|
|
130
|
+
if (end === -1) return void 0;
|
|
131
|
+
n = parseHex(raw, i, end);
|
|
132
|
+
i = end + 1;
|
|
133
|
+
} else {
|
|
134
|
+
n = parseHex(raw, i, i + 4);
|
|
135
|
+
i += 4;
|
|
136
|
+
}
|
|
137
|
+
if (n === -1 || n > 1114111) return void 0;
|
|
138
|
+
out += String.fromCodePoint(n);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
default:
|
|
142
|
+
if (isDigit(next, 0)) return void 0;
|
|
143
|
+
out += next;
|
|
144
|
+
}
|
|
145
|
+
start = i;
|
|
146
|
+
}
|
|
147
|
+
return out + raw.slice(start);
|
|
148
|
+
}
|
|
149
|
+
function isDigit(str, index) {
|
|
150
|
+
const c = str.charCodeAt(index);
|
|
151
|
+
return c >= zero && c <= nine;
|
|
152
|
+
}
|
|
153
|
+
function parseHex(str, index, end) {
|
|
154
|
+
if (end >= str.length) return -1;
|
|
155
|
+
let n = 0;
|
|
156
|
+
for (; index < end; index++) {
|
|
157
|
+
const c = hexToInt(str.charCodeAt(index));
|
|
158
|
+
if (c === -1) return -1;
|
|
159
|
+
n = n * 16 + c;
|
|
160
|
+
}
|
|
161
|
+
return n;
|
|
162
|
+
}
|
|
163
|
+
function hexToInt(c) {
|
|
164
|
+
if (c >= zero && c <= nine) return c - zero;
|
|
165
|
+
if (c >= lowerA && c <= lowerF) return c - lowerA + 10;
|
|
166
|
+
if (c >= upperA && c <= upperF) return c - upperA + 10;
|
|
167
|
+
return -1;
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region ../node_modules/.pnpm/zustand@5.0.13_@types+react@19.2.7_react@19.2.6/node_modules/zustand/esm/vanilla.mjs
|
|
2
171
|
var createStoreImpl = (createState) => {
|
|
3
172
|
let state;
|
|
4
173
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -27,6 +196,308 @@ var createStoreImpl = (createState) => {
|
|
|
27
196
|
};
|
|
28
197
|
var createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
|
|
29
198
|
//#endregion
|
|
199
|
+
//#region src/toolbar/toolbar.ts
|
|
200
|
+
function initPlaywriterToolbar() {
|
|
201
|
+
if (window.__playwriterToolbarInstalled) return;
|
|
202
|
+
window.__playwriterToolbarInstalled = true;
|
|
203
|
+
try {
|
|
204
|
+
if (window !== window.top) return;
|
|
205
|
+
} catch {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
let pinModeActive = false;
|
|
209
|
+
let pinCount = 0;
|
|
210
|
+
let toastTimer = null;
|
|
211
|
+
let overlayEl = null;
|
|
212
|
+
let pinBtn;
|
|
213
|
+
const host = document.createElement("div");
|
|
214
|
+
host.setAttribute("data-playwriter-toolbar", "1");
|
|
215
|
+
host.style.cssText = "position:fixed;top:12px;right:12px;z-index:2147483647;pointer-events:none;font-size:0;line-height:0;";
|
|
216
|
+
const shadow = host.attachShadow({ mode: "closed" });
|
|
217
|
+
const styleEl = document.createElement("style");
|
|
218
|
+
styleEl.textContent = `
|
|
219
|
+
*,*::before,*::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
220
|
+
.toolbar {
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
gap: 2px;
|
|
224
|
+
padding: 3px;
|
|
225
|
+
background: #fff;
|
|
226
|
+
border-radius: 10px;
|
|
227
|
+
pointer-events: all;
|
|
228
|
+
user-select: none;
|
|
229
|
+
box-shadow: 0px 0px 0.5px rgba(0,0,0,0.18), 0px 3px 8px rgba(0,0,0,0.1), 0px 1px 3px rgba(0,0,0,0.1);
|
|
230
|
+
}
|
|
231
|
+
.divider {
|
|
232
|
+
width: 1px;
|
|
233
|
+
height: 12px;
|
|
234
|
+
background: rgba(0, 0, 0, 0.08);
|
|
235
|
+
margin: 0 1px;
|
|
236
|
+
flex-shrink: 0;
|
|
237
|
+
}
|
|
238
|
+
.btn {
|
|
239
|
+
display: flex;
|
|
240
|
+
align-items: center;
|
|
241
|
+
justify-content: center;
|
|
242
|
+
width: 26px;
|
|
243
|
+
height: 26px;
|
|
244
|
+
border: none;
|
|
245
|
+
border-radius: 7px;
|
|
246
|
+
background: transparent;
|
|
247
|
+
color: #000;
|
|
248
|
+
cursor: pointer;
|
|
249
|
+
transition: background 0.1s;
|
|
250
|
+
padding: 0;
|
|
251
|
+
flex-shrink: 0;
|
|
252
|
+
outline: none;
|
|
253
|
+
}
|
|
254
|
+
.btn:hover {
|
|
255
|
+
background: rgba(0, 0, 0, 0.04);
|
|
256
|
+
}
|
|
257
|
+
.btn.active {
|
|
258
|
+
background: #0d99ff;
|
|
259
|
+
color: #fff;
|
|
260
|
+
}
|
|
261
|
+
.btn.active:hover {
|
|
262
|
+
background: #0d99ff;
|
|
263
|
+
filter: brightness(1.05);
|
|
264
|
+
}
|
|
265
|
+
/* When active, the logo inner cursor path needs to match the blue bg
|
|
266
|
+
so it appears as a "cutout" through the white outer shape */
|
|
267
|
+
.btn.active .logo-inner { fill: #0d99ff; }
|
|
268
|
+
.toast {
|
|
269
|
+
position: fixed;
|
|
270
|
+
background: #0f172a;
|
|
271
|
+
border-radius: 8px;
|
|
272
|
+
padding: 9px 18px;
|
|
273
|
+
color: rgba(255, 255, 255, 0.85);
|
|
274
|
+
font-size: 11px;
|
|
275
|
+
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
|
276
|
+
pointer-events: none;
|
|
277
|
+
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
|
|
278
|
+
white-space: nowrap;
|
|
279
|
+
z-index: 1;
|
|
280
|
+
--toast-transform: translateX(-50%);
|
|
281
|
+
animation: toast-in 0.15s ease;
|
|
282
|
+
}
|
|
283
|
+
@keyframes toast-in {
|
|
284
|
+
from { opacity: 0; transform: var(--toast-transform) translateY(4px); }
|
|
285
|
+
to { opacity: 1; transform: var(--toast-transform); }
|
|
286
|
+
}
|
|
287
|
+
`;
|
|
288
|
+
const toolbarEl = document.createElement("div");
|
|
289
|
+
toolbarEl.className = "toolbar";
|
|
290
|
+
toolbarEl.setAttribute("role", "toolbar");
|
|
291
|
+
toolbarEl.setAttribute("aria-label", "Playwriter tools");
|
|
292
|
+
shadow.appendChild(styleEl);
|
|
293
|
+
shadow.appendChild(toolbarEl);
|
|
294
|
+
function showToast(msg, anchorRect) {
|
|
295
|
+
shadow.querySelectorAll(".toast").forEach((el) => {
|
|
296
|
+
el.remove();
|
|
297
|
+
});
|
|
298
|
+
if (toastTimer !== null) clearTimeout(toastTimer);
|
|
299
|
+
const toastEl = document.createElement("div");
|
|
300
|
+
toastEl.className = "toast";
|
|
301
|
+
toastEl.textContent = msg;
|
|
302
|
+
if (anchorRect) {
|
|
303
|
+
const GAP = 8;
|
|
304
|
+
const centerX = anchorRect.left + anchorRect.width / 2;
|
|
305
|
+
const belowY = anchorRect.bottom + GAP;
|
|
306
|
+
const fitsBelow = belowY + 36 < window.innerHeight;
|
|
307
|
+
const top = fitsBelow ? belowY : anchorRect.top - GAP;
|
|
308
|
+
const transformOrigin = fitsBelow ? "top center" : "bottom center";
|
|
309
|
+
toastEl.style.left = Math.max(8, Math.min(centerX, window.innerWidth - 8)) + "px";
|
|
310
|
+
toastEl.style.top = top + "px";
|
|
311
|
+
const baseTransform = fitsBelow ? "translateX(-50%)" : "translateX(-50%) translateY(-100%)";
|
|
312
|
+
toastEl.style.setProperty("--toast-transform", baseTransform);
|
|
313
|
+
toastEl.style.transform = baseTransform;
|
|
314
|
+
toastEl.style.transformOrigin = transformOrigin;
|
|
315
|
+
} else {
|
|
316
|
+
toastEl.style.bottom = "20px";
|
|
317
|
+
toastEl.style.left = "50%";
|
|
318
|
+
toastEl.style.transform = "translateX(-50%)";
|
|
319
|
+
}
|
|
320
|
+
shadow.appendChild(toastEl);
|
|
321
|
+
toastTimer = window.setTimeout(() => {
|
|
322
|
+
toastEl.remove();
|
|
323
|
+
}, 1900);
|
|
324
|
+
}
|
|
325
|
+
function getOverlay() {
|
|
326
|
+
if (!overlayEl) {
|
|
327
|
+
const EDGE = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
|
|
328
|
+
const FILL = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
|
|
329
|
+
const container = document.createElement("div");
|
|
330
|
+
container.setAttribute("data-playwriter-overlay", "1");
|
|
331
|
+
container.style.cssText = [
|
|
332
|
+
"position:fixed",
|
|
333
|
+
"pointer-events:none",
|
|
334
|
+
"z-index:2147483646",
|
|
335
|
+
`background:${FILL}`,
|
|
336
|
+
"display:none"
|
|
337
|
+
].join(";");
|
|
338
|
+
const edgeTop = document.createElement("div");
|
|
339
|
+
edgeTop.style.cssText = `position:absolute;top:0;left:0;width:100%;height:1px;background:${EDGE};`;
|
|
340
|
+
const edgeRight = document.createElement("div");
|
|
341
|
+
edgeRight.style.cssText = `position:absolute;top:0;right:0;width:1px;height:100%;background:${EDGE};`;
|
|
342
|
+
const edgeBottom = document.createElement("div");
|
|
343
|
+
edgeBottom.style.cssText = `position:absolute;bottom:0;left:0;width:100%;height:1px;background:${EDGE};`;
|
|
344
|
+
const edgeLeft = document.createElement("div");
|
|
345
|
+
edgeLeft.style.cssText = `position:absolute;top:0;left:0;width:1px;height:100%;background:${EDGE};`;
|
|
346
|
+
container.appendChild(edgeTop);
|
|
347
|
+
container.appendChild(edgeRight);
|
|
348
|
+
container.appendChild(edgeBottom);
|
|
349
|
+
container.appendChild(edgeLeft);
|
|
350
|
+
document.documentElement.appendChild(container);
|
|
351
|
+
overlayEl = container;
|
|
352
|
+
}
|
|
353
|
+
return overlayEl;
|
|
354
|
+
}
|
|
355
|
+
function positionOverlay(target) {
|
|
356
|
+
const rect = target.getBoundingClientRect();
|
|
357
|
+
if (!rect.width && !rect.height) return;
|
|
358
|
+
const overlay = getOverlay();
|
|
359
|
+
overlay.style.display = "block";
|
|
360
|
+
overlay.style.top = rect.top + "px";
|
|
361
|
+
overlay.style.left = rect.left + "px";
|
|
362
|
+
overlay.style.width = rect.width + "px";
|
|
363
|
+
overlay.style.height = rect.height + "px";
|
|
364
|
+
}
|
|
365
|
+
function hideOverlay() {
|
|
366
|
+
if (overlayEl) overlayEl.style.display = "none";
|
|
367
|
+
}
|
|
368
|
+
function removeOverlay() {
|
|
369
|
+
if (overlayEl) {
|
|
370
|
+
overlayEl.remove();
|
|
371
|
+
overlayEl = null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function getTargetAt(x, y) {
|
|
375
|
+
return document.elementsFromPoint(x, y).find((el) => !el.hasAttribute("data-playwriter-overlay") && !el.hasAttribute("data-playwriter-toolbar") && el !== document.documentElement && el !== document.body) ?? null;
|
|
376
|
+
}
|
|
377
|
+
function isOverToolbar(e) {
|
|
378
|
+
return e.composedPath().some((node) => node === host);
|
|
379
|
+
}
|
|
380
|
+
function flashElement(el) {
|
|
381
|
+
const s = el.style;
|
|
382
|
+
if (!s) return;
|
|
383
|
+
const prevOutline = s.outline;
|
|
384
|
+
const prevOffset = s.outlineOffset;
|
|
385
|
+
s.outline = "1px solid #22c55e";
|
|
386
|
+
s.outlineOffset = "2px";
|
|
387
|
+
window.setTimeout(() => {
|
|
388
|
+
s.outline = prevOutline;
|
|
389
|
+
s.outlineOffset = prevOffset;
|
|
390
|
+
}, 350);
|
|
391
|
+
}
|
|
392
|
+
function copyText(text) {
|
|
393
|
+
navigator.clipboard.writeText(text).catch(() => {
|
|
394
|
+
try {
|
|
395
|
+
const ta = document.createElement("textarea");
|
|
396
|
+
ta.value = text;
|
|
397
|
+
ta.style.cssText = "position:fixed;top:0;left:0;opacity:0;pointer-events:none;";
|
|
398
|
+
document.body.appendChild(ta);
|
|
399
|
+
ta.focus();
|
|
400
|
+
ta.select();
|
|
401
|
+
document.execCommand("copy");
|
|
402
|
+
ta.remove();
|
|
403
|
+
} catch {}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function allocatePinName() {
|
|
407
|
+
const shared = window.__playwriterPinCount;
|
|
408
|
+
if (typeof shared === "number" && shared > pinCount) pinCount = shared;
|
|
409
|
+
pinCount++;
|
|
410
|
+
window.__playwriterPinCount = pinCount;
|
|
411
|
+
return `playwriterPinnedElem${pinCount}`;
|
|
412
|
+
}
|
|
413
|
+
function onMouseMove(e) {
|
|
414
|
+
if (isOverToolbar(e)) {
|
|
415
|
+
hideOverlay();
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const target = getTargetAt(e.clientX, e.clientY);
|
|
419
|
+
if (target) positionOverlay(target);
|
|
420
|
+
else hideOverlay();
|
|
421
|
+
}
|
|
422
|
+
function buildInspectionCode(n, url) {
|
|
423
|
+
return `inspectPinnedElement(${JSON.stringify(url).replace(/'/g, "\\u0027")},"globalThis.playwriterPinnedElem${n}")`;
|
|
424
|
+
}
|
|
425
|
+
function onClick(e) {
|
|
426
|
+
if (isOverToolbar(e)) return;
|
|
427
|
+
e.preventDefault();
|
|
428
|
+
e.stopImmediatePropagation();
|
|
429
|
+
const target = getTargetAt(e.clientX, e.clientY);
|
|
430
|
+
if (!target) return;
|
|
431
|
+
const name = allocatePinName();
|
|
432
|
+
const n = pinCount;
|
|
433
|
+
window[name] = target;
|
|
434
|
+
flashElement(target);
|
|
435
|
+
const url = location.href;
|
|
436
|
+
copyText("playwriter -e '" + buildInspectionCode(n, url) + "'");
|
|
437
|
+
showToast("Copied playwriter element reference, use it in your agent prompt", target.getBoundingClientRect());
|
|
438
|
+
setPinMode(false);
|
|
439
|
+
}
|
|
440
|
+
function onKeyDown(e) {
|
|
441
|
+
if (e.key === "Escape") setPinMode(false);
|
|
442
|
+
}
|
|
443
|
+
function setPinMode(on) {
|
|
444
|
+
pinModeActive = on;
|
|
445
|
+
pinBtn.classList.toggle("active", on);
|
|
446
|
+
if (on) {
|
|
447
|
+
document.documentElement.style.cursor = "crosshair";
|
|
448
|
+
getOverlay();
|
|
449
|
+
document.addEventListener("mousemove", onMouseMove, {
|
|
450
|
+
capture: true,
|
|
451
|
+
passive: true
|
|
452
|
+
});
|
|
453
|
+
document.addEventListener("click", onClick, true);
|
|
454
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
455
|
+
} else {
|
|
456
|
+
document.documentElement.style.cursor = "";
|
|
457
|
+
hideOverlay();
|
|
458
|
+
document.removeEventListener("mousemove", onMouseMove, true);
|
|
459
|
+
document.removeEventListener("click", onClick, true);
|
|
460
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const CLIPBOARD_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="none" viewBox="0 0 424 424" aria-hidden="true"><path d="M 0 212 C 0 112.063 0 62.095 31.037 31.037 C 62.116 0 112.063 0 212 0 C 311.937 0 361.905 0 392.942 31.037 C 424 62.116 424 112.063 424 212 C 424 311.937 424 361.905 392.942 392.942 C 361.926 424 311.937 424 212 424 C 112.063 424 62.095 424 31.037 392.942 C 0 361.926 0 311.937 0 212" fill="currentColor"/><path class="logo-inner" d="M 225.732 260.521 L 277.905 312.673 C 283.311 318.1 286.003 320.793 289.014 322.043 C 293.042 323.718 297.557 323.718 301.585 322.043 C 304.596 320.793 307.309 318.1 312.694 312.694 C 318.1 307.288 320.793 304.596 322.043 301.585 C 323.722 297.563 323.722 293.036 322.043 289.014 C 320.793 286.003 318.1 283.29 312.694 277.905 L 260.521 225.732 L 276.442 209.789 C 292.766 193.465 300.907 185.325 298.999 176.548 C 297.07 167.792 286.237 163.785 264.591 155.814 L 192.384 129.208 C 149.2 113.308 127.618 105.358 116.488 116.488 C 105.358 127.618 113.308 149.2 129.208 192.384 L 155.814 264.591 C 163.785 286.237 167.792 297.07 176.548 298.999 C 185.303 300.928 193.465 292.766 209.789 276.442 Z" fill="white"/></svg>`;
|
|
464
|
+
const CLOSE_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
|
|
465
|
+
pinBtn = document.createElement("button");
|
|
466
|
+
pinBtn.className = "btn";
|
|
467
|
+
pinBtn.setAttribute("aria-label", "Pin element — click any element to copy inspection code for a playwriter -e call");
|
|
468
|
+
pinBtn.setAttribute("title", "Pin element (click to copy inspection code)");
|
|
469
|
+
pinBtn.innerHTML = CLIPBOARD_SVG;
|
|
470
|
+
pinBtn.addEventListener("click", (e) => {
|
|
471
|
+
e.stopPropagation();
|
|
472
|
+
setPinMode(!pinModeActive);
|
|
473
|
+
});
|
|
474
|
+
const dividerEl = document.createElement("div");
|
|
475
|
+
dividerEl.className = "divider";
|
|
476
|
+
dividerEl.setAttribute("aria-hidden", "true");
|
|
477
|
+
const closeBtn = document.createElement("button");
|
|
478
|
+
closeBtn.className = "btn";
|
|
479
|
+
closeBtn.setAttribute("aria-label", "Close Playwriter toolbar");
|
|
480
|
+
closeBtn.setAttribute("title", "Close toolbar");
|
|
481
|
+
closeBtn.innerHTML = CLOSE_SVG;
|
|
482
|
+
closeBtn.addEventListener("click", (e) => {
|
|
483
|
+
e.stopPropagation();
|
|
484
|
+
setPinMode(false);
|
|
485
|
+
host.style.display = "none";
|
|
486
|
+
});
|
|
487
|
+
toolbarEl.appendChild(pinBtn);
|
|
488
|
+
toolbarEl.appendChild(dividerEl);
|
|
489
|
+
toolbarEl.appendChild(closeBtn);
|
|
490
|
+
document.documentElement.appendChild(host);
|
|
491
|
+
window.__playwriterToolbarDestroy = function() {
|
|
492
|
+
setPinMode(false);
|
|
493
|
+
removeOverlay();
|
|
494
|
+
host.remove();
|
|
495
|
+
delete window.__playwriterToolbarInstalled;
|
|
496
|
+
delete window.__playwriterToolbarDestroy;
|
|
497
|
+
delete window.__playwriterPinCount;
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
//#endregion
|
|
30
501
|
//#region ../playwriter/src/ghost-browser.ts
|
|
31
502
|
/**
|
|
32
503
|
* Handles ghost-browser commands in the extension.
|
|
@@ -72,6 +543,12 @@ async function handleGhostBrowserCommand(params, chromeApi) {
|
|
|
72
543
|
}
|
|
73
544
|
}
|
|
74
545
|
//#endregion
|
|
546
|
+
//#region ../playwriter/dist/ghost-cursor-client.js?raw
|
|
547
|
+
var ghost_cursor_client_default = "(() => {\n var __defProp = Object.defineProperty;\n var __getOwnPropNames = Object.getOwnPropertyNames;\n var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n var __hasOwnProp = Object.prototype.hasOwnProperty;\n function __accessProp(key) {\n return this[key];\n }\n var __toCommonJS = (from) => {\n var entry = (__moduleCache ??= new WeakMap).get(from), desc;\n if (entry)\n return entry;\n entry = __defProp({}, \"__esModule\", { value: true });\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (var key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(entry, key))\n __defProp(entry, key, {\n get: __accessProp.bind(from, key),\n enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable\n });\n }\n __moduleCache.set(from, entry);\n return entry;\n };\n var __moduleCache;\n\n // src/ghost-cursor-client.ts\n var exports_ghost_cursor_client = {};\n\n // src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts\n var SCREENSTUDIO_POINTER_MACOS_TAHOE_DATA_URL = \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjE4IiBoZWlnaHQ9Ijk1OCIgdmlld0JveD0iMCAwIDYxOCA5NTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RfMzg0XzI3KSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTI3LjA2MiAzNy4wMzMxTDU0MC42OTYgNDUxLjU1NUM1OTIuNjUzIDUwMy42NiA1NTUuNzk0IDU5Mi41NzQgNDgyLjIyNiA1OTIuNTc0TDQyMS44MzEgNTkyLjU2OUw0ODEuODIxIDczNS4wNTRDNDkyLjMzMSA3NjAuMDIxIDQ5Mi40NzkgNzg3LjY1MiA0ODIuMjY1IDgxMi43NjdDNDcyLjAwMiA4MzcuOTMyIDQ1Mi41NjEgODU3LjU3IDQyNy40OTYgODY4LjA4QzQxNC44NjQgODczLjM1OSA0MDEuNjQgODc2LjAyNCAzODguMTIxIDg3Ni4wMjRDMzQ3LjExNyA4NzYuMDI0IDMxMC4zNTggODUxLjYgMjk0LjQ3IDgxMy44MDRMMjMxLjQyIDY2My45MThMMTkwLjM2OCA3MDAuMzM3QzEzNy4wMjkgNzQ3LjUwOCA1MyA3MDkuNjYzIDUzIDYzOC40MTNWNjcuNjc0NEM1MyAyOC45OTAzIDk5LjcyNjggOS42NDgyOCAxMjcuMDYyIDM3LjAzMzFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTEwMi4zMTYgOTkuNjUyQzEwMi4zMTYgOTMuMTg4MiAxMTAuMTYyIDg5LjkzMTYgMTE0LjcwMSA5NC41MjA0TDUwNC44OTcgNDg1LjU1NUM1MjYuMTY0IDUwNi44NzEgNTExLjA2NSA1NDMuMjM2IDQ4MC45NjcgNTQzLjIzNkwzNDcuNTQ2IDU0My4xNjFMNDM2LjM0MiA3NTQuMTQzQzQ0Ny41NDIgNzgwLjc4OCA0MzUuMDA5IDgxMS40MjkgNDA4LjQxNCA4MjIuNTgxQzM4MS43MiA4MzMuNzgxIDM1MS4xMjggODIxLjI5OCAzMzkuOTc3IDc5NC43MDJMMjUwLjI5MyA1ODEuMzUyTDE1OC41MTcgNjYyLjY0NEMxMzcuOTkxIDY4MC44MDEgMTA2LjMxOSA2NjguMTQ1IDEwMi42NjQgNjQyLjMyM0wxMDIuMzE2IDYzNy4zMzFWOTkuNjUyWiIgZmlsbD0iYmxhY2siLz4KPC9nPgo8ZGVmcz4KPGZpbHRlciBpZD0iZmlsdGVyMF9kXzM4NF8yNyIgeD0iMC4zNCIgeT0iMC43OTkyMTkiIHdpZHRoPSI2MTcuMzIiIGhlaWdodD0iOTU3LjE0NCIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPgo8ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz4KPGZlT2Zmc2V0IGR5PSIyOS4yNiIvPgo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyNi4zMyIvPgo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz4KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuNjUgMCIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd18zODRfMjciLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3dfMzg0XzI3IiByZXN1bHQ9InNoYXBlIi8+CjwvZmlsdGVyPgo8L2RlZnM+Cjwvc3ZnPg==\";\n\n // src/ghost-cursor-client.ts\n var isTopFrame = (() => {\n try {\n return window === window.top;\n } catch {\n return false;\n }\n })();\n var CURSOR_ID = \"__playwriter_ghost_cursor__\";\n var SCREENSTUDIO_POINTER_ASPECT_RATIO = 618 / 958;\n var SCREENSTUDIO_HOTSPOT_X_RATIO = 0.14;\n var SCREENSTUDIO_HOTSPOT_Y_RATIO = 0.06;\n var MINIMAL_TRIANGLE_HOTSPOT_X_RATIO = 0.07;\n var MINIMAL_TRIANGLE_HOTSPOT_Y_RATIO = 0.06;\n var MOVE_EASING = \"cubic-bezier(0.65, 0, 0.35, 1)\";\n var PRESS_EASING = \"cubic-bezier(0.23, 1, 0.32, 1)\";\n var PRESS_DURATION_MS = 140;\n var IDLE_HIDE_DELAY_MS = 5000;\n var IDLE_FADE_OUT_MS = 600;\n var DEFAULT_OPTIONS = {\n style: \"minimal\",\n color: \"#111827\",\n size: 22,\n zIndex: 2147483647,\n easing: MOVE_EASING,\n minDurationMs: 220,\n maxDurationMs: 1500,\n speedPxPerMs: 1.2\n };\n var runtime = {\n outerElement: null,\n innerElement: null,\n options: DEFAULT_OPTIONS,\n x: 0,\n y: 0,\n scale: 1,\n hasPosition: false,\n enabled: false,\n idleHidden: false\n };\n var idleHideTimer = null;\n function clamp(options) {\n const { value, min, max } = options;\n return Math.min(max, Math.max(min, value));\n }\n function mergeOptions(options) {\n if (!options) {\n return DEFAULT_OPTIONS;\n }\n return {\n style: options.style ?? DEFAULT_OPTIONS.style,\n color: options.color ?? DEFAULT_OPTIONS.color,\n size: options.size ?? DEFAULT_OPTIONS.size,\n zIndex: options.zIndex ?? DEFAULT_OPTIONS.zIndex,\n easing: options.easing ?? DEFAULT_OPTIONS.easing,\n minDurationMs: options.minDurationMs ?? DEFAULT_OPTIONS.minDurationMs,\n maxDurationMs: options.maxDurationMs ?? DEFAULT_OPTIONS.maxDurationMs,\n speedPxPerMs: options.speedPxPerMs ?? DEFAULT_OPTIONS.speedPxPerMs\n };\n }\n function getCursorDimensions() {\n if (runtime.options.style === \"screenstudio\") {\n const height = runtime.options.size;\n const width = Math.max(10, Math.round(height * SCREENSTUDIO_POINTER_ASPECT_RATIO));\n return { width, height };\n }\n if (runtime.options.style === \"minimal\") {\n const size = Math.max(12, runtime.options.size);\n return { width: size, height: size };\n }\n return { width: runtime.options.size, height: runtime.options.size };\n }\n function getHotspotOffsetPx() {\n const dimensions = getCursorDimensions();\n if (runtime.options.style === \"screenstudio\") {\n return {\n x: Math.round(dimensions.width * SCREENSTUDIO_HOTSPOT_X_RATIO),\n y: Math.round(dimensions.height * SCREENSTUDIO_HOTSPOT_Y_RATIO)\n };\n }\n if (runtime.options.style === \"minimal\") {\n return {\n x: Math.round(dimensions.width * MINIMAL_TRIANGLE_HOTSPOT_X_RATIO),\n y: Math.round(dimensions.height * MINIMAL_TRIANGLE_HOTSPOT_Y_RATIO)\n };\n }\n return {\n x: Math.round(dimensions.width / 2),\n y: Math.round(dimensions.height / 2)\n };\n }\n function getBaseOpacity() {\n if (runtime.options.style === \"screenstudio\") {\n return \"0.95\";\n }\n if (runtime.options.style === \"minimal\") {\n return \"1\";\n }\n return \"0.72\";\n }\n function applyTranslate() {\n if (!runtime.outerElement) {\n return;\n }\n const hotspot = getHotspotOffsetPx();\n runtime.outerElement.style.transform = `translate3d(${runtime.x - hotspot.x}px, ${runtime.y - hotspot.y}px, 0)`;\n }\n function applyScale() {\n if (!runtime.innerElement) {\n return;\n }\n runtime.innerElement.style.transform = `scale(${runtime.scale})`;\n }\n function computeDurationMs(options) {\n if (!runtime.hasPosition) {\n return 0;\n }\n const dx = options.targetX - runtime.x;\n const dy = options.targetY - runtime.y;\n const distance = Math.hypot(dx, dy);\n const rawDurationMs = distance / runtime.options.speedPxPerMs;\n return clamp({\n value: rawDurationMs,\n min: runtime.options.minDurationMs,\n max: runtime.options.maxDurationMs\n });\n }\n function createCursorElement() {\n const outer = document.createElement(\"div\");\n outer.id = CURSOR_ID;\n outer.setAttribute(\"aria-hidden\", \"true\");\n outer.style.position = \"fixed\";\n outer.style.left = \"0\";\n outer.style.top = \"0\";\n outer.style.pointerEvents = \"none\";\n outer.style.zIndex = `${runtime.options.zIndex}`;\n outer.style.transitionProperty = \"transform\";\n outer.style.transitionTimingFunction = runtime.options.easing;\n outer.style.transitionDuration = \"0ms\";\n outer.style.willChange = \"transform\";\n const inner = document.createElement(\"div\");\n inner.style.transitionProperty = \"transform, opacity\";\n inner.style.transitionTimingFunction = PRESS_EASING;\n inner.style.transitionDuration = `${PRESS_DURATION_MS}ms`;\n inner.style.opacity = getBaseOpacity();\n outer.appendChild(inner);\n runtime.outerElement = outer;\n runtime.innerElement = inner;\n applyRuntimeVisualOptions();\n return outer;\n }\n function ensureCursorElement() {\n const existing = document.getElementById(CURSOR_ID);\n if (existing) {\n runtime.outerElement = existing;\n runtime.innerElement = existing.firstElementChild || null;\n return existing;\n }\n const outer = createCursorElement();\n const root = document.documentElement || document.body;\n root.appendChild(outer);\n return outer;\n }\n function applyRuntimeVisualOptions() {\n if (!runtime.innerElement) {\n return;\n }\n const dimensions = getCursorDimensions();\n runtime.innerElement.style.width = `${dimensions.width}px`;\n runtime.innerElement.style.height = `${dimensions.height}px`;\n if (runtime.outerElement) {\n runtime.outerElement.style.zIndex = `${runtime.options.zIndex}`;\n runtime.outerElement.style.transitionTimingFunction = runtime.options.easing;\n }\n const hotspot = getHotspotOffsetPx();\n runtime.innerElement.style.transformOrigin = `${hotspot.x}px ${hotspot.y}px`;\n if (runtime.options.style === \"screenstudio\") {\n runtime.innerElement.style.borderRadius = \"0\";\n runtime.innerElement.style.border = \"none\";\n runtime.innerElement.style.backgroundColor = \"transparent\";\n runtime.innerElement.style.backgroundImage = `url(\"${SCREENSTUDIO_POINTER_MACOS_TAHOE_DATA_URL}\")`;\n runtime.innerElement.style.backgroundRepeat = \"no-repeat\";\n runtime.innerElement.style.backgroundPosition = \"left top\";\n runtime.innerElement.style.backgroundSize = \"contain\";\n runtime.innerElement.style.backdropFilter = \"none\";\n runtime.innerElement.style.filter = \"none\";\n runtime.innerElement.style.boxShadow = \"none\";\n runtime.innerElement.style.opacity = getBaseOpacity();\n return;\n }\n if (runtime.options.style === \"minimal\") {\n const triangleSvg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"-1 -1 26 26\"><path fill=\"white\" stroke=\"${runtime.options.color}\" stroke-width=\"1.5\" stroke-linejoin=\"round\" d=\"m23.284 19.124l-6.866-6.895a.4.4 0 0 1-.118-.296a.43.43 0 0 1 .163-.282l4.439-3.077a1.48 1.48 0 0 0 .621-1.48a1.48 1.48 0 0 0-1.036-1.198L1.623.302a1.14 1.14 0 0 0-1.11.282A1.13 1.13 0 0 0 .29 1.649L5.928 20.44a1.48 1.48 0 0 0 1.183 1.035a1.48 1.48 0 0 0 1.48-.621l3.078-4.44a.37.37 0 0 1 .31-.118a.43.43 0 0 1 .296.104l6.91 6.91a1.48 1.48 0 0 0 2.087 0l2.086-2.086a1.48 1.48 0 0 0-.074-2.101\"/></svg>`;\n const triangleDataUrl = `url(\"data:image/svg+xml,${encodeURIComponent(triangleSvg)}\")`;\n runtime.innerElement.style.borderRadius = \"0\";\n runtime.innerElement.style.border = \"none\";\n runtime.innerElement.style.backgroundColor = \"transparent\";\n runtime.innerElement.style.backgroundImage = triangleDataUrl;\n runtime.innerElement.style.backgroundRepeat = \"no-repeat\";\n runtime.innerElement.style.backgroundSize = \"contain\";\n runtime.innerElement.style.backgroundPosition = \"left top\";\n runtime.innerElement.style.backdropFilter = \"none\";\n runtime.innerElement.style.boxShadow = \"none\";\n runtime.innerElement.style.filter = \"drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4))\";\n runtime.innerElement.style.opacity = getBaseOpacity();\n return;\n }\n runtime.innerElement.style.borderRadius = \"999px\";\n runtime.innerElement.style.border = \"none\";\n runtime.innerElement.style.backgroundColor = runtime.options.color;\n runtime.innerElement.style.backgroundImage = \"none\";\n runtime.innerElement.style.backdropFilter = \"none\";\n runtime.innerElement.style.filter = \"none\";\n runtime.innerElement.style.boxShadow = \"0 2px 10px rgba(0, 0, 0, 0.18), inset 0 0 0 2px rgba(255, 255, 255, 0.55)\";\n runtime.innerElement.style.opacity = getBaseOpacity();\n }\n function clearIdleHideTimer() {\n if (idleHideTimer !== null) {\n clearTimeout(idleHideTimer);\n idleHideTimer = null;\n }\n }\n function scheduleIdleHide() {\n clearIdleHideTimer();\n idleHideTimer = setTimeout(() => {\n idleHideTimer = null;\n if (!runtime.enabled || !runtime.innerElement) {\n return;\n }\n runtime.idleHidden = true;\n runtime.innerElement.style.transitionDuration = `${IDLE_FADE_OUT_MS}ms`;\n runtime.innerElement.style.transitionTimingFunction = PRESS_EASING;\n runtime.innerElement.style.opacity = \"0\";\n }, IDLE_HIDE_DELAY_MS);\n }\n function wakeFromIdle(options) {\n runtime.x = options.x;\n runtime.y = options.y;\n runtime.hasPosition = true;\n if (runtime.innerElement) {\n runtime.innerElement.style.transitionDuration = `${PRESS_DURATION_MS}ms`;\n runtime.innerElement.style.transitionTimingFunction = PRESS_EASING;\n runtime.innerElement.style.opacity = getBaseOpacity();\n }\n }\n function moveCursor(options) {\n if (!runtime.enabled) {\n return;\n }\n ensureCursorElement();\n const durationMs = computeDurationMs({ targetX: options.x, targetY: options.y });\n if (runtime.outerElement) {\n runtime.outerElement.style.transitionDuration = `${Math.round(durationMs)}ms`;\n runtime.outerElement.style.transitionTimingFunction = runtime.options.easing;\n }\n runtime.x = options.x;\n runtime.y = options.y;\n runtime.hasPosition = true;\n applyTranslate();\n }\n function setPressed(options) {\n if (!runtime.enabled || !runtime.innerElement) {\n return;\n }\n runtime.scale = options.pressed ? runtime.options.style === \"dot\" ? 0.92 : 0.95 : 1;\n runtime.innerElement.style.transitionDuration = `${PRESS_DURATION_MS}ms`;\n runtime.innerElement.style.transitionTimingFunction = PRESS_EASING;\n runtime.innerElement.style.opacity = options.pressed ? \"1\" : getBaseOpacity();\n applyScale();\n }\n function enable(options) {\n runtime.options = mergeOptions(options);\n runtime.enabled = true;\n ensureCursorElement();\n applyRuntimeVisualOptions();\n if (!runtime.hasPosition) {\n runtime.x = Math.round(window.innerWidth / 2);\n runtime.y = Math.round(window.innerHeight / 2);\n runtime.scale = 1;\n runtime.hasPosition = true;\n }\n runtime.idleHidden = false;\n if (runtime.innerElement) {\n runtime.innerElement.style.opacity = getBaseOpacity();\n }\n applyTranslate();\n applyScale();\n scheduleIdleHide();\n }\n function disable() {\n runtime.enabled = false;\n runtime.scale = 1;\n runtime.hasPosition = false;\n runtime.idleHidden = false;\n clearIdleHideTimer();\n if (runtime.outerElement) {\n runtime.outerElement.remove();\n runtime.outerElement = null;\n runtime.innerElement = null;\n }\n }\n function applyMouseAction(action) {\n if (!runtime.enabled) {\n return;\n }\n if (runtime.idleHidden) {\n runtime.idleHidden = false;\n wakeFromIdle({ x: action.x, y: action.y });\n }\n if (action.type === \"move\" || action.type === \"wheel\") {\n moveCursor({ x: action.x, y: action.y });\n } else if (action.type === \"down\") {\n moveCursor({ x: action.x, y: action.y });\n setPressed({ pressed: true });\n } else if (action.type === \"up\") {\n moveCursor({ x: action.x, y: action.y });\n setPressed({ pressed: false });\n }\n scheduleIdleHide();\n }\n var api = {\n enable,\n disable,\n applyMouseAction,\n isEnabled: () => {\n return runtime.enabled;\n }\n };\n if (isTopFrame) {\n globalThis.__playwriterGhostCursor = api;\n try {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", () => {\n try {\n api.enable();\n } catch {}\n }, { once: true });\n } else {\n api.enable();\n }\n } catch {}\n }\n})();\n";
|
|
548
|
+
//#endregion
|
|
549
|
+
//#region ../playwriter/dist/bippy.js?raw
|
|
550
|
+
var bippy_default = "(() => {\n // ../node_modules/.pnpm/bippy@0.5.27_@types+react@19.2.7_react@19.2.6/node_modules/bippy/dist/rdt-hook-BZMdLD7S.js\n var e = `0.5.27`;\n var t = `bippy-${e}`;\n var n = Object.defineProperty;\n var r = Object.prototype.hasOwnProperty;\n var i = () => {};\n var a = (e2) => {\n try {\n let t2 = Function.prototype.toString.call(e2);\n t2.indexOf(`^_^`) > -1 && setTimeout(() => {\n throw Error(`React is running in production mode, but dead code elimination has not been applied. Read how to correctly configure React for production: https://reactjs.org/link/perf-use-production-build`);\n });\n } catch {}\n };\n var o = (e2 = h()) => (`getFiberRoots` in e2);\n var s = false;\n var c;\n var l = (e2 = h()) => s ? true : (typeof e2.inject == `function` && (c = e2.inject.toString()), !!c?.includes(`(injected)`));\n var u = new Set;\n var d = new Set;\n var f = (e2) => {\n let r2 = new Map, o2 = 0, s2 = { _instrumentationIsActive: false, _instrumentationSource: t, checkDCE: a, hasUnsupportedRendererAttached: false, inject(e3) {\n let t2 = ++o2;\n return r2.set(t2, e3), d.add(e3), s2._instrumentationIsActive || (s2._instrumentationIsActive = true, u.forEach((e4) => e4())), t2;\n }, on: i, onCommitFiberRoot: i, onCommitFiberUnmount: i, onPostCommitFiberRoot: i, renderers: r2, supportsFiber: true, supportsFlight: true };\n try {\n n(globalThis, `__REACT_DEVTOOLS_GLOBAL_HOOK__`, { configurable: true, enumerable: true, get() {\n return s2;\n }, set(t3) {\n if (t3 && typeof t3 == `object`) {\n let n2 = s2.renderers;\n s2 = t3, n2.size > 0 && (n2.forEach((e3, n3) => {\n d.add(e3), t3.renderers.set(n3, e3);\n }), p(e2));\n }\n } });\n let t2 = window.hasOwnProperty, r3 = false;\n n(window, `hasOwnProperty`, { configurable: true, value: function(...e3) {\n try {\n if (!r3 && e3[0] === `__REACT_DEVTOOLS_GLOBAL_HOOK__`)\n return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = undefined, r3 = true, -0;\n } catch {}\n return t2.apply(this, e3);\n }, writable: true });\n } catch {\n p(e2);\n }\n return s2;\n };\n var p = (e2) => {\n e2 && u.add(e2);\n try {\n let n2 = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;\n if (!n2)\n return;\n if (!n2._instrumentationSource) {\n n2.checkDCE = a, n2.supportsFiber = true, n2.supportsFlight = true, n2.hasUnsupportedRendererAttached = false, n2._instrumentationSource = t, n2._instrumentationIsActive = false;\n let e3 = o(n2);\n if (e3 || (n2.on = i), n2.renderers.size) {\n n2._instrumentationIsActive = true, u.forEach((e4) => e4());\n return;\n }\n let r2 = n2.inject, c2 = l(n2);\n if (c2 && !e3) {\n s = true;\n let e4 = n2.inject({ scheduleRefresh() {} });\n e4 && (n2._instrumentationIsActive = true);\n }\n n2.inject = (e4) => {\n let t2 = r2(e4);\n return d.add(e4), c2 && n2.renderers.set(t2, e4), n2._instrumentationIsActive = true, u.forEach((e5) => e5()), t2;\n };\n }\n (n2.renderers.size || n2._instrumentationIsActive || l()) && e2?.();\n } catch {}\n };\n var m = () => r.call(globalThis, `__REACT_DEVTOOLS_GLOBAL_HOOK__`);\n var h = (e2) => m() ? (p(e2), globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__) : f(e2);\n var g = () => !!(typeof window < `u` && (window.document?.createElement || window.navigator?.product === `ReactNative`));\n var _ = () => {\n try {\n g() && h();\n } catch {}\n };\n\n // ../node_modules/.pnpm/bippy@0.5.27_@types+react@19.2.7_react@19.2.6/node_modules/bippy/dist/install-hook-only-BOBPiBkc.js\n _();\n\n // ../node_modules/.pnpm/bippy@0.5.27_@types+react@19.2.7_react@19.2.6/node_modules/bippy/dist/core-coQbWNwP.js\n var a2 = 0;\n var o2 = 1;\n var c2 = 5;\n var f2 = 11;\n var p2 = 13;\n var m2 = 14;\n var h2 = 15;\n var ee = 16;\n var te = 19;\n var y = 26;\n var b = 27;\n var ne = 28;\n var re = 30;\n var ie = 2;\n var ae = 4096;\n var oe = 4;\n var se = 16;\n var ce = 32;\n var le = 1024;\n var ue = 8192;\n var O = ie | oe | se | ce | ae | ue | le;\n var k = (e2) => {\n switch (e2.tag) {\n case c2:\n case y:\n case b:\n return true;\n default:\n return typeof e2.type == `string`;\n }\n };\n var pe = (e2) => {\n switch (e2.tag) {\n case o2:\n case f2:\n case a2:\n case m2:\n case h2:\n return true;\n default:\n return false;\n }\n };\n function N(e2, t2, n2 = false) {\n if (!e2)\n return null;\n let r2 = t2(e2);\n if (r2 instanceof Promise)\n return (async () => {\n if (await r2 === true)\n return e2;\n let i3 = n2 ? e2.return : e2.child;\n for (;i3; ) {\n let e3 = await F(i3, t2, n2);\n if (e3)\n return e3;\n i3 = n2 ? null : i3.sibling;\n }\n return null;\n })();\n if (r2 === true)\n return e2;\n let i2 = n2 ? e2.return : e2.child;\n for (;i2; ) {\n let e3 = P(i2, t2, n2);\n if (e3)\n return e3;\n i2 = n2 ? null : i2.sibling;\n }\n return null;\n }\n var P = (e2, t2, n2 = false) => {\n if (!e2)\n return null;\n if (t2(e2) === true)\n return e2;\n let r2 = n2 ? e2.return : e2.child;\n for (;r2; ) {\n let e3 = P(r2, t2, n2);\n if (e3)\n return e3;\n r2 = n2 ? null : r2.sibling;\n }\n return null;\n };\n var F = async (e2, t2, n2 = false) => {\n if (!e2)\n return null;\n if (await t2(e2) === true)\n return e2;\n let r2 = n2 ? e2.return : e2.child;\n for (;r2; ) {\n let e3 = await F(r2, t2, n2);\n if (e3)\n return e3;\n r2 = n2 ? null : r2.sibling;\n }\n return null;\n };\n var I = (e2) => {\n let t2 = e2;\n return typeof t2 == `function` ? t2 : typeof t2 == `object` && t2 ? I(t2.type || t2.render) : null;\n };\n var Te = (e2) => {\n let t2 = e2;\n if (typeof t2 == `string`)\n return t2;\n if (typeof t2 != `function` && !(typeof t2 == `object` && t2))\n return null;\n let n2 = t2.displayName || t2.name || null;\n if (n2)\n return n2;\n let r2 = I(t2);\n return r2 && (r2.displayName || r2.name) || null;\n };\n var z = new WeakMap;\n var K = new WeakMap;\n var Pe = (e2) => {\n let n2 = h();\n for (let t2 of n2.renderers.values())\n try {\n let n3 = t2.findFiberByHostInstance?.(e2);\n if (n3)\n return n3;\n } catch {}\n if (typeof e2 == `object` && e2) {\n if (`_reactRootContainer` in e2)\n return e2._reactRootContainer?._internalRoot?.current?.child;\n for (let t2 in e2)\n if (t2.startsWith(`__reactContainer$`) || t2.startsWith(`__reactInternalInstance$`) || t2.startsWith(`__reactFiber`))\n return e2[t2] || null;\n }\n return null;\n };\n var Fe = Error();\n var $ = new Set;\n\n // ../node_modules/.pnpm/bippy@0.5.27_@types+react@19.2.7_react@19.2.6/node_modules/bippy/dist/source.js\n var g3 = Object.create;\n var _3 = Object.defineProperty;\n var v2 = Object.getOwnPropertyDescriptor;\n var y2 = Object.getOwnPropertyNames;\n var b2 = Object.getPrototypeOf;\n var x2 = Object.prototype.hasOwnProperty;\n var S2 = (e2, t2) => () => (t2 || e2((t2 = { exports: {} }).exports, t2), t2.exports);\n var ee2 = (e2, t2, n2, r2) => {\n if (t2 && typeof t2 == `object` || typeof t2 == `function`)\n for (var i2 = y2(t2), a3 = 0, o3 = i2.length, s3;a3 < o3; a3++)\n s3 = i2[a3], !x2.call(e2, s3) && s3 !== n2 && _3(e2, s3, { get: ((e3) => t2[e3]).bind(null, s3), enumerable: !(r2 = v2(t2, s3)) || r2.enumerable });\n return e2;\n };\n var C2 = (e2, t2, n2) => (n2 = e2 == null ? {} : g3(b2(e2)), ee2(t2 || !e2 || !e2.__esModule ? _3(n2, `default`, { value: e2, enumerable: true }) : n2, e2));\n var w2 = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:/;\n var te2 = [`rsc://`, `file:///`, `webpack://`, `webpack-internal://`, `node:`, `turbopack://`, `metro://`, `/app-pages-browser/`];\n var T2 = `about://React/`;\n var ne2 = [`<anonymous>`, `eval`, ``];\n var re2 = /\\.(jsx|tsx|ts|js)$/;\n var ie2 = /(\\.min|bundle|chunk|vendor|vendors|runtime|polyfill|polyfills)\\.(js|mjs|cjs)$|(chunk|bundle|vendor|vendors|runtime|polyfill|polyfills|framework|app|main|index)[-_.][A-Za-z0-9_-]{4,}\\.(js|mjs|cjs)$|[\\da-f]{8,}\\.(js|mjs|cjs)$|[-_.][\\da-f]{20,}\\.(js|mjs|cjs)$|\\/dist\\/|\\/build\\/|\\/.next\\/|\\/out\\/|\\/node_modules\\/|\\.webpack\\.|\\.vite\\.|\\.turbopack\\./i;\n var ae2 = /^\\?[\\w~.\\-]+(?:=[^&#]*)?(?:&[\\w~.\\-]+(?:=[^&#]*)?)*$/;\n var E = `(at Server)`;\n var oe2 = /(^|@)\\S+:\\d+/;\n var se2 = /^\\s*at .*(\\S+:\\d+|\\(native\\))/m;\n var ce2 = /^(eval@)?(\\[native code\\])?$/;\n var D = (e2, t2) => {\n if (t2?.includeInElement !== false) {\n let n2 = e2.split(`\n`), r2 = [];\n for (let e3 of n2)\n if (/^\\s*at\\s+/.test(e3)) {\n let t3 = A2(e3, undefined)[0];\n t3 && r2.push(t3);\n } else if (/^\\s*in\\s+/.test(e3)) {\n let t3 = e3.replace(/^\\s*in\\s+/, ``).replace(/\\s*\\(at .*\\)$/, ``);\n r2.push({ functionName: t3, source: e3 });\n } else if (e3.match(oe2)) {\n let t3 = j2(e3, undefined)[0];\n t3 && r2.push(t3);\n }\n return k2(r2, t2);\n }\n return e2.match(se2) ? A2(e2, t2) : j2(e2, t2);\n };\n var O2 = (e2) => {\n if (!e2.includes(`:`))\n return [e2, undefined, undefined];\n let t2 = /(.+?)(?::(\\d+))?(?::(\\d+))?$/, n2 = e2.startsWith(`(`) && e2.endsWith(`)`) ? e2.slice(1, -1) : e2, r2 = t2.exec(n2);\n return [r2[1], r2[2] || undefined, r2[3] || undefined];\n };\n var k2 = (e2, t2) => t2 && t2.slice != null ? Array.isArray(t2.slice) ? e2.slice(t2.slice[0], t2.slice[1]) : e2.slice(0, t2.slice) : e2;\n var A2 = (e2, t2) => {\n let n2 = k2(e2.split(`\n`).filter((e3) => !!e3.match(se2)), t2);\n return n2.map((e3) => {\n let t3 = e3;\n t3.includes(`(eval `) && (t3 = t3.replace(/eval code/g, `eval`).replace(/(\\(eval at [^()]*)|(,.*$)/g, ``));\n let n3 = t3.replace(/^\\s+/, ``).replace(/\\(eval code/g, `(`).replace(/^.*?\\s+/, ``), r2 = n3.match(/ (\\(.+\\)$)/);\n n3 = r2 ? n3.replace(r2[0], ``) : n3;\n let i2 = O2(r2 ? r2[1] : n3), a3 = r2 && n3 || undefined, o3 = [`eval`, `<anonymous>`].includes(i2[0]) ? undefined : i2[0];\n return { functionName: a3, fileName: o3, lineNumber: i2[1] ? +i2[1] : undefined, columnNumber: i2[2] ? +i2[2] : undefined, source: t3 };\n });\n };\n var j2 = (e2, t2) => {\n let n2 = k2(e2.split(`\n`).filter((e3) => !e3.match(ce2)), t2);\n return n2.map((e3) => {\n let t3 = e3;\n if (t3.includes(` > eval`) && (t3 = t3.replace(/ line (\\d+)(?: > eval line \\d+)* > eval:\\d+:\\d+/g, `:$1`)), !t3.includes(`@`) && !t3.includes(`:`))\n return { functionName: t3 };\n {\n let e4 = /(([^\\n\\r\"\\u2028\\u2029]*\".[^\\n\\r\"\\u2028\\u2029]*\"[^\\n\\r@\\u2028\\u2029]*(?:@[^\\n\\r\"\\u2028\\u2029]*\"[^\\n\\r@\\u2028\\u2029]*)*(?:[\\n\\r\\u2028\\u2029][^@]*)?)?[^@]*)@/, n3 = t3.match(e4), r2 = n3 && n3[1] ? n3[1] : undefined, i2 = O2(t3.replace(e4, ``));\n return { functionName: r2, fileName: i2[0], lineNumber: i2[1] ? +i2[1] : undefined, columnNumber: i2[2] ? +i2[2] : undefined, source: t3 };\n }\n });\n };\n var pe2 = S2((exports, t2) => {\n (function(n2, r2) {\n typeof exports == `object` && t2 !== undefined ? r2(exports) : typeof define == `function` && define.amd ? define([`exports`], r2) : (n2 = typeof globalThis < `u` ? globalThis : n2 || self, r2(n2.sourcemapCodec = {}));\n })(undefined, function(e2) {\n let t3 = 44, n2 = 59, r2 = `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`, i2 = new Uint8Array(64), a3 = new Uint8Array(128);\n for (let e3 = 0;e3 < r2.length; e3++) {\n let t4 = r2.charCodeAt(e3);\n i2[e3] = t4, a3[t4] = e3;\n }\n function o3(e3, t4) {\n let n3 = 0, r3 = 0, i3 = 0;\n do {\n let t5 = e3.next();\n i3 = a3[t5], n3 |= (i3 & 31) << r3, r3 += 5;\n } while (i3 & 32);\n let o4 = n3 & 1;\n return n3 >>>= 1, o4 && (n3 = -2147483648 | -n3), t4 + n3;\n }\n function s3(e3, t4, n3) {\n let r3 = t4 - n3;\n r3 = r3 < 0 ? -r3 << 1 | 1 : r3 << 1;\n do {\n let t5 = r3 & 31;\n r3 >>>= 5, r3 > 0 && (t5 |= 32), e3.write(i2[t5]);\n } while (r3 > 0);\n return t4;\n }\n function c3(e3, n3) {\n return e3.pos >= n3 ? false : e3.peek() !== t3;\n }\n let l3 = 1024 * 16, u3 = typeof TextDecoder < `u` ? new TextDecoder : typeof Buffer < `u` ? { decode(e3) {\n let t4 = Buffer.from(e3.buffer, e3.byteOffset, e3.byteLength);\n return t4.toString();\n } } : { decode(e3) {\n let t4 = ``;\n for (let n3 = 0;n3 < e3.length; n3++)\n t4 += String.fromCharCode(e3[n3]);\n return t4;\n } };\n\n class d3 {\n constructor() {\n this.pos = 0, this.out = ``, this.buffer = new Uint8Array(l3);\n }\n write(e3) {\n let { buffer: t4 } = this;\n t4[this.pos++] = e3, this.pos === l3 && (this.out += u3.decode(t4), this.pos = 0);\n }\n flush() {\n let { buffer: e3, out: t4, pos: n3 } = this;\n return n3 > 0 ? t4 + u3.decode(e3.subarray(0, n3)) : t4;\n }\n }\n\n class f3 {\n constructor(e3) {\n this.pos = 0, this.buffer = e3;\n }\n next() {\n return this.buffer.charCodeAt(this.pos++);\n }\n peek() {\n return this.buffer.charCodeAt(this.pos);\n }\n indexOf(e3) {\n let { buffer: t4, pos: n3 } = this, r3 = t4.indexOf(e3, n3);\n return r3 === -1 ? t4.length : r3;\n }\n }\n let p3 = [];\n function m3(e3) {\n let { length: t4 } = e3, n3 = new f3(e3), r3 = [], i3 = [], a4 = 0;\n for (;n3.pos < t4; n3.pos++) {\n a4 = o3(n3, a4);\n let e4 = o3(n3, 0);\n if (!c3(n3, t4)) {\n let t5 = i3.pop();\n t5[2] = a4, t5[3] = e4;\n continue;\n }\n let s4 = o3(n3, 0), l4 = o3(n3, 0), u4 = l4 & 1, d4 = u4 ? [a4, e4, 0, 0, s4, o3(n3, 0)] : [a4, e4, 0, 0, s4], f4 = p3;\n if (c3(n3, t4)) {\n f4 = [];\n do {\n let e5 = o3(n3, 0);\n f4.push(e5);\n } while (c3(n3, t4));\n }\n d4.vars = f4, r3.push(d4), i3.push(d4);\n }\n return r3;\n }\n function h3(e3) {\n let t4 = new d3;\n for (let n3 = 0;n3 < e3.length; )\n n3 = g4(e3, n3, t4, [0]);\n return t4.flush();\n }\n function g4(e3, n3, r3, i3) {\n let a4 = e3[n3], { 0: o4, 1: c4, 2: l4, 3: u4, 4: d4, vars: f4 } = a4;\n n3 > 0 && r3.write(t3), i3[0] = s3(r3, o4, i3[0]), s3(r3, c4, 0), s3(r3, d4, 0);\n let p4 = a4.length === 6 ? 1 : 0;\n s3(r3, p4, 0), a4.length === 6 && s3(r3, a4[5], 0);\n for (let e4 of f4)\n s3(r3, e4, 0);\n for (n3++;n3 < e3.length; ) {\n let t4 = e3[n3], { 0: a5, 1: o5 } = t4;\n if (a5 > l4 || a5 === l4 && o5 >= u4)\n break;\n n3 = g4(e3, n3, r3, i3);\n }\n return r3.write(t3), i3[0] = s3(r3, l4, i3[0]), s3(r3, u4, 0), n3;\n }\n function _4(e3) {\n let { length: t4 } = e3, n3 = new f3(e3), r3 = [], i3 = [], a4 = 0, s4 = 0, l4 = 0, u4 = 0, d4 = 0, m4 = 0, h4 = 0, g5 = 0;\n do {\n let e4 = n3.indexOf(`;`), t5 = 0;\n for (;n3.pos < e4; n3.pos++) {\n if (t5 = o3(n3, t5), !c3(n3, e4)) {\n let e5 = i3.pop();\n e5[2] = a4, e5[3] = t5;\n continue;\n }\n let f4 = o3(n3, 0), _5 = f4 & 1, v4 = f4 & 2, y4 = f4 & 4, b4 = null, x4 = p3, S4;\n if (_5) {\n let e5 = o3(n3, s4);\n l4 = o3(n3, s4 === e5 ? l4 : 0), s4 = e5, S4 = [a4, t5, 0, 0, e5, l4];\n } else\n S4 = [a4, t5, 0, 0];\n if (S4.isScope = !!y4, v4) {\n let e5 = u4, t6 = d4;\n u4 = o3(n3, u4);\n let r4 = e5 === u4;\n d4 = o3(n3, r4 ? d4 : 0), m4 = o3(n3, r4 && t6 === d4 ? m4 : 0), b4 = [u4, d4, m4];\n }\n if (S4.callsite = b4, c3(n3, e4)) {\n x4 = [];\n do {\n h4 = a4, g5 = t5;\n let e5 = o3(n3, 0), r4;\n if (e5 < -1) {\n r4 = [[o3(n3, 0)]];\n for (let t6 = -1;t6 > e5; t6--) {\n let e6 = h4;\n h4 = o3(n3, h4), g5 = o3(n3, h4 === e6 ? g5 : 0);\n let t7 = o3(n3, 0);\n r4.push([t7, h4, g5]);\n }\n } else\n r4 = [[e5]];\n x4.push(r4);\n } while (c3(n3, e4));\n }\n S4.bindings = x4, r3.push(S4), i3.push(S4);\n }\n a4++, n3.pos = e4 + 1;\n } while (n3.pos < t4);\n return r3;\n }\n function v3(e3) {\n if (e3.length === 0)\n return ``;\n let t4 = new d3;\n for (let n3 = 0;n3 < e3.length; )\n n3 = y3(e3, n3, t4, [0, 0, 0, 0, 0, 0, 0]);\n return t4.flush();\n }\n function y3(e3, n3, r3, i3) {\n let a4 = e3[n3], { 0: o4, 1: c4, 2: l4, 3: u4, isScope: d4, callsite: f4, bindings: p4 } = a4;\n i3[0] < o4 ? (b3(r3, i3[0], o4), i3[0] = o4, i3[1] = 0) : n3 > 0 && r3.write(t3), i3[1] = s3(r3, a4[1], i3[1]);\n let m4 = (a4.length === 6 ? 1 : 0) | (f4 ? 2 : 0) | (d4 ? 4 : 0);\n if (s3(r3, m4, 0), a4.length === 6) {\n let { 4: e4, 5: t4 } = a4;\n e4 !== i3[2] && (i3[3] = 0), i3[2] = s3(r3, e4, i3[2]), i3[3] = s3(r3, t4, i3[3]);\n }\n if (f4) {\n let { 0: e4, 1: t4, 2: n4 } = a4.callsite;\n e4 === i3[4] ? t4 !== i3[5] && (i3[6] = 0) : (i3[5] = 0, i3[6] = 0), i3[4] = s3(r3, e4, i3[4]), i3[5] = s3(r3, t4, i3[5]), i3[6] = s3(r3, n4, i3[6]);\n }\n if (p4)\n for (let e4 of p4) {\n e4.length > 1 && s3(r3, -e4.length, 0);\n let t4 = e4[0][0];\n s3(r3, t4, 0);\n let n4 = o4, i4 = c4;\n for (let t5 = 1;t5 < e4.length; t5++) {\n let a5 = e4[t5];\n n4 = s3(r3, a5[1], n4), i4 = s3(r3, a5[2], i4), s3(r3, a5[0], 0);\n }\n }\n for (n3++;n3 < e3.length; ) {\n let t4 = e3[n3], { 0: a5, 1: o5 } = t4;\n if (a5 > l4 || a5 === l4 && o5 >= u4)\n break;\n n3 = y3(e3, n3, r3, i3);\n }\n return i3[0] < l4 ? (b3(r3, i3[0], l4), i3[0] = l4, i3[1] = 0) : r3.write(t3), i3[1] = s3(r3, u4, i3[1]), n3;\n }\n function b3(e3, t4, r3) {\n do\n e3.write(n2);\n while (++t4 < r3);\n }\n function x3(e3) {\n let { length: t4 } = e3, n3 = new f3(e3), r3 = [], i3 = 0, a4 = 0, s4 = 0, l4 = 0, u4 = 0;\n do {\n let e4 = n3.indexOf(`;`), t5 = [], d4 = true, f4 = 0;\n for (i3 = 0;n3.pos < e4; ) {\n let r4;\n i3 = o3(n3, i3), i3 < f4 && (d4 = false), f4 = i3, c3(n3, e4) ? (a4 = o3(n3, a4), s4 = o3(n3, s4), l4 = o3(n3, l4), c3(n3, e4) ? (u4 = o3(n3, u4), r4 = [i3, a4, s4, l4, u4]) : r4 = [i3, a4, s4, l4]) : r4 = [i3], t5.push(r4), n3.pos++;\n }\n d4 || S3(t5), r3.push(t5), n3.pos = e4 + 1;\n } while (n3.pos <= t4);\n return r3;\n }\n function S3(e3) {\n e3.sort(ee3);\n }\n function ee3(e3, t4) {\n return e3[0] - t4[0];\n }\n function C3(e3) {\n let r3 = new d3, i3 = 0, a4 = 0, o4 = 0, c4 = 0;\n for (let l4 = 0;l4 < e3.length; l4++) {\n let u4 = e3[l4];\n if (l4 > 0 && r3.write(n2), u4.length === 0)\n continue;\n let d4 = 0;\n for (let e4 = 0;e4 < u4.length; e4++) {\n let n3 = u4[e4];\n e4 > 0 && r3.write(t3), d4 = s3(r3, n3[0], d4), n3.length !== 1 && (i3 = s3(r3, n3[1], i3), a4 = s3(r3, n3[2], a4), o4 = s3(r3, n3[3], o4), n3.length !== 4 && (c4 = s3(r3, n3[4], c4)));\n }\n }\n return r3.flush();\n }\n e2.decode = x3, e2.decodeGeneratedRanges = _4, e2.decodeOriginalScopes = m3, e2.encode = C3, e2.encodeGeneratedRanges = v3, e2.encodeOriginalScopes = h3, Object.defineProperty(e2, `__esModule`, { value: true });\n });\n });\n var F2 = C2(pe2(), 1);\n var I2 = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:/;\n var me2 = /^data:application\\/json[^,]+base64,/;\n var he2 = /(?:\\/\\/[@#][ \\t]+sourceMappingURL=([^\\s'\"]+?)[ \\t]*$)|(?:\\/\\*[@#][ \\t]+sourceMappingURL=([^*]+?)[ \\t]*(?:\\*\\/)[ \\t]*$)/;\n var L2 = typeof WeakRef < `u`;\n var R = new Map;\n var z2 = new Map;\n var ge2 = (e2) => L2 && e2 instanceof WeakRef;\n var B2 = (e2, t2, n2, r2) => {\n if (n2 < 0 || n2 >= e2.length)\n return null;\n let i2 = e2[n2];\n if (!i2 || i2.length === 0)\n return null;\n let a3 = null;\n for (let e3 of i2)\n if (e3[0] <= r2)\n a3 = e3;\n else\n break;\n if (!a3 || a3.length < 4)\n return null;\n let [, o3, s3, c3] = a3;\n if (o3 === undefined || s3 === undefined || c3 === undefined)\n return null;\n let l3 = t2[o3];\n return l3 ? { columnNumber: c3, fileName: l3, lineNumber: s3 + 1 } : null;\n };\n var V2 = (e2, t2, n2) => {\n if (e2.sections) {\n let r2 = null;\n for (let i3 of e2.sections)\n if (t2 > i3.offset.line || t2 === i3.offset.line && n2 >= i3.offset.column)\n r2 = i3;\n else\n break;\n if (!r2)\n return null;\n let i2 = t2 - r2.offset.line, a3 = t2 === r2.offset.line ? n2 - r2.offset.column : n2;\n return B2(r2.map.mappings, r2.map.sources, i2, a3);\n }\n return B2(e2.mappings, e2.sources, t2 - 1, n2);\n };\n var _e2 = (e2, t2) => {\n let n2 = t2.split(`\n`), r2;\n for (let e3 = n2.length - 1;e3 >= 0 && !r2; e3--) {\n let t3 = n2[e3].match(he2);\n t3 && (r2 = t3[1] || t3[2]);\n }\n if (!r2)\n return null;\n let i2 = I2.test(r2);\n if (!(me2.test(r2) || i2 || r2.startsWith(`/`))) {\n let t3 = e2.split(`/`);\n t3[t3.length - 1] = r2, r2 = t3.join(`/`);\n }\n return r2;\n };\n var ve2 = (e2) => ({ file: e2.file, mappings: (0, F2.decode)(e2.mappings), names: e2.names, sourceRoot: e2.sourceRoot, sources: e2.sources, sourcesContent: e2.sourcesContent, version: 3 });\n var ye2 = (e2) => {\n let t2 = e2.sections.map(({ map: e3, offset: t3 }) => ({ map: { ...e3, mappings: (0, F2.decode)(e3.mappings) }, offset: t3 })), n2 = new Set;\n for (let e3 of t2)\n for (let t3 of e3.map.sources)\n n2.add(t3);\n return { file: e2.file, mappings: [], names: [], sections: t2, sourceRoot: undefined, sources: Array.from(n2), sourcesContent: undefined, version: 3 };\n };\n var H2 = (e2) => {\n if (!e2)\n return false;\n let t2 = e2.trim();\n if (!t2)\n return false;\n let n2 = t2.match(I2);\n if (!n2)\n return true;\n let r2 = n2[0].toLowerCase();\n return r2 === `http:` || r2 === `https:`;\n };\n var U2 = async (e2, t2 = fetch) => {\n if (!H2(e2))\n return null;\n let n2;\n try {\n let r3 = await t2(e2);\n n2 = await r3.text();\n } catch {\n return null;\n }\n if (!n2)\n return null;\n let r2 = _e2(e2, n2);\n if (!r2 || !H2(r2))\n return null;\n try {\n let e3 = await t2(r2), n3 = await e3.json();\n return `sections` in n3 ? ye2(n3) : ve2(n3);\n } catch {\n return null;\n }\n };\n var W2 = async (e2, t2 = true, n2) => {\n if (t2 && R.has(e2)) {\n let t3 = R.get(e2);\n if (t3 == null)\n return null;\n if (ge2(t3)) {\n let n3 = t3.deref();\n if (n3)\n return n3;\n R.delete(e2);\n } else\n return t3;\n }\n if (t2 && z2.has(e2))\n return z2.get(e2);\n let r2 = U2(e2, n2);\n t2 && z2.set(e2, r2);\n let i2 = await r2;\n return t2 && z2.delete(e2), t2 && (i2 === null ? R.set(e2, null) : R.set(e2, L2 ? new WeakRef(i2) : i2)), i2;\n };\n var G2 = async (e2, t2 = true, n2) => await Promise.all(e2.map(async (e3) => {\n if (!e3.fileName)\n return e3;\n let r2 = await W2(e3.fileName, t2, n2);\n if (!r2 || typeof e3.lineNumber != `number` || typeof e3.columnNumber != `number`)\n return e3;\n let i2 = V2(r2, e3.lineNumber, e3.columnNumber);\n return i2 ? { ...e3, source: i2.fileName && e3.source ? e3.source.replace(e3.fileName, i2.fileName) : e3.source, fileName: i2.fileName, lineNumber: i2.lineNumber, columnNumber: i2.columnNumber, isSymbolicated: true } : e3;\n }));\n var K2 = (e2) => e2._debugStack instanceof Error && typeof e2._debugStack?.stack == `string`;\n var be2 = () => {\n let n2 = h();\n for (let t2 of [...Array.from(d), ...Array.from(n2.renderers.values())]) {\n let e2 = t2.currentDispatcherRef;\n if (e2 && typeof e2 == `object`)\n return `H` in e2 ? e2.H : e2.current;\n }\n return null;\n };\n var q = (t2) => {\n for (let n2 of d) {\n let e2 = n2.currentDispatcherRef;\n e2 && typeof e2 == `object` && (`H` in e2 ? e2.H = t2 : e2.current = t2);\n }\n };\n var J = (e2) => `\n in ${e2}`;\n var Y = (e2, t2) => {\n let n2 = J(e2);\n return t2 && (n2 += ` (at ${t2})`), n2;\n };\n var X2 = false;\n var Z = (e2, t2) => {\n if (!e2 || X2)\n return ``;\n let n2 = Error.prepareStackTrace;\n Error.prepareStackTrace = undefined, X2 = true;\n let r2 = be2();\n q(null);\n let { error: i2, warn: a3 } = console;\n console.error = () => {}, console.warn = () => {};\n try {\n let n3 = { DetermineComponentFrameRoot() {\n let n4;\n try {\n if (t2) {\n let t3 = function() {\n throw Error();\n };\n if (Object.defineProperty(t3.prototype, `props`, { set: function() {\n throw Error();\n } }), typeof Reflect == `object` && Reflect.construct) {\n try {\n Reflect.construct(t3, []);\n } catch (e3) {\n n4 = e3;\n }\n Reflect.construct(e2, [], t3);\n } else {\n try {\n t3.call();\n } catch (e3) {\n n4 = e3;\n }\n e2.call(t3.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (e3) {\n n4 = e3;\n }\n let t3 = e2();\n t3 && typeof t3.catch == `function` && t3.catch(() => {});\n }\n } catch (e3) {\n if (e3 instanceof Error && n4 instanceof Error && typeof e3.stack == `string`)\n return [e3.stack, n4.stack];\n }\n return [null, null];\n } };\n n3.DetermineComponentFrameRoot.displayName = `DetermineComponentFrameRoot`;\n let r3 = Object.getOwnPropertyDescriptor(n3.DetermineComponentFrameRoot, `name`);\n r3?.configurable && Object.defineProperty(n3.DetermineComponentFrameRoot, `name`, { value: `DetermineComponentFrameRoot` });\n let [i3, a4] = n3.DetermineComponentFrameRoot();\n if (i3 && a4) {\n let t3 = i3.split(`\n`), n4 = a4.split(`\n`), r4 = 0, o4 = 0;\n for (;r4 < t3.length && !t3[r4].includes(`DetermineComponentFrameRoot`); )\n r4++;\n for (;o4 < n4.length && !n4[o4].includes(`DetermineComponentFrameRoot`); )\n o4++;\n if (r4 === t3.length || o4 === n4.length)\n for (r4 = t3.length - 1, o4 = n4.length - 1;r4 >= 1 && o4 >= 0 && t3[r4] !== n4[o4]; )\n o4--;\n for (;r4 >= 1 && o4 >= 0; r4--, o4--)\n if (t3[r4] !== n4[o4]) {\n if (r4 !== 1 || o4 !== 1)\n do\n if (r4--, o4--, o4 < 0 || t3[r4] !== n4[o4]) {\n let n5 = `\n${t3[r4].replace(` at new `, ` at `)}`, i4 = Te(e2);\n return i4 && n5.includes(`<anonymous>`) && (n5 = n5.replace(`<anonymous>`, i4)), n5;\n }\n while (r4 >= 1 && o4 >= 0);\n break;\n }\n }\n } finally {\n X2 = false, Error.prepareStackTrace = n2, q(r2), console.error = i2, console.warn = a3;\n }\n let o3 = e2 ? Te(e2) : ``, s3 = o3 ? J(o3) : ``;\n return s3;\n };\n var xe2 = (e2, t2) => {\n let m3 = e2.tag, h3 = ``;\n switch (m3) {\n case ne:\n h3 = J(`Activity`);\n break;\n case o2:\n h3 = Z(e2.type, true);\n break;\n case f2:\n h3 = Z(e2.type.render, false);\n break;\n case a2:\n case h2:\n h3 = Z(e2.type, false);\n break;\n case c2:\n case y:\n case b:\n h3 = J(e2.type);\n break;\n case ee:\n h3 = J(`Lazy`);\n break;\n case p2:\n h3 = e2.child !== t2 && t2 !== null ? J(`Suspense Fallback`) : J(`Suspense`);\n break;\n case te:\n h3 = J(`SuspenseList`);\n break;\n case re:\n h3 = J(`ViewTransition`);\n break;\n default:\n return ``;\n }\n return h3;\n };\n var Se2 = (e2) => {\n try {\n let t2 = ``, n2 = e2, r2 = null;\n do {\n t2 += xe2(n2, r2);\n let e3 = n2._debugInfo;\n if (e3 && Array.isArray(e3))\n for (let n3 = e3.length - 1;n3 >= 0; n3--) {\n let r3 = e3[n3];\n typeof r3.name == `string` && (t2 += Y(r3.name, r3.env));\n }\n r2 = n2, n2 = n2.return;\n } while (n2);\n return t2;\n } catch (e3) {\n return e3 instanceof Error ? `\nError generating stack: ${e3.message}\n${e3.stack}` : ``;\n }\n };\n var Ce2 = (e2) => {\n let t2 = Error.prepareStackTrace;\n Error.prepareStackTrace = undefined;\n let n2 = e2;\n if (!n2)\n return ``;\n Error.prepareStackTrace = t2, n2.startsWith(`Error: react-stack-top-frame\n`) && (n2 = n2.slice(29));\n let r2 = n2.indexOf(`\n`);\n if (r2 !== -1 && (n2 = n2.slice(r2 + 1)), r2 = Math.max(n2.indexOf(`react_stack_bottom_frame`), n2.indexOf(`react-stack-bottom-frame`)), r2 !== -1 && (r2 = n2.lastIndexOf(`\n`, r2)), r2 !== -1)\n n2 = n2.slice(0, r2);\n else\n return ``;\n return n2;\n };\n var we2 = (e2) => !!(e2.fileName?.startsWith(`rsc://`) && e2.functionName);\n var Te2 = (e2, t2) => e2.fileName === t2.fileName && e2.lineNumber === t2.lineNumber && e2.columnNumber === t2.columnNumber;\n var Ee2 = (e2) => {\n let t2 = new Map;\n for (let n2 of e2)\n for (let e3 of n2.stackFrames) {\n if (!we2(e3))\n continue;\n let n3 = e3.functionName, r2 = t2.get(n3) ?? [], i2 = r2.some((t3) => Te2(t3, e3));\n i2 || (r2.push(e3), t2.set(n3, r2));\n }\n return t2;\n };\n var De2 = (e2, t2, n2) => {\n if (!e2.functionName)\n return { ...e2, isServer: true };\n let r2 = t2.get(e2.functionName);\n if (!r2 || r2.length === 0)\n return { ...e2, isServer: true };\n let i2 = n2.get(e2.functionName) ?? 0, a3 = r2[i2 % r2.length];\n return n2.set(e2.functionName, i2 + 1), { ...e2, isServer: true, fileName: a3.fileName, lineNumber: a3.lineNumber, columnNumber: a3.columnNumber, source: e2.source?.replace(E, `(${a3.fileName}:${a3.lineNumber}:${a3.columnNumber})`) };\n };\n var Oe = (e2) => {\n let t2 = [];\n return N(e2, (e3) => {\n if (!K2(e3))\n return;\n let n2 = typeof e3.type == `string` ? e3.type : Te(e3.type) || `<anonymous>`;\n t2.push({ componentName: n2, stackFrames: D(Ce2(e3._debugStack?.stack)) });\n }, true), t2;\n };\n var Q = async (e2, t2 = true, n2) => {\n let r2 = Oe(e2), i2 = D(Se2(e2)), a3 = Ee2(r2), o3 = new Map, s3 = i2.map((e3) => {\n let t3 = e3.source?.includes(E) ?? false;\n return t3 ? De2(e3, a3, o3) : e3;\n }), c3 = s3.filter((e3, t3, n3) => {\n if (t3 === 0)\n return true;\n let r3 = n3[t3 - 1];\n return e3.functionName !== r3.functionName;\n });\n return G2(c3, t2, n2);\n };\n var ke2 = (e2) => {\n let t2 = e2._debugSource;\n return t2 ? typeof t2 == `object` && !!t2 && `fileName` in t2 && typeof t2.fileName == `string` && `lineNumber` in t2 && typeof t2.lineNumber == `number` : false;\n };\n var Ae2 = async (e2, t2 = true, n2) => {\n if (ke2(e2)) {\n let t3 = e2._debugSource;\n return t3 || null;\n }\n let r2 = await Q(e2, t2, n2);\n for (let e3 of r2)\n if (e3.fileName)\n return { fileName: e3.fileName, lineNumber: e3.lineNumber, columnNumber: e3.columnNumber, functionName: e3.functionName };\n return null;\n };\n var $2 = (e2) => {\n if (!e2 || ne2.some((t3) => t3 === e2))\n return ``;\n let t2 = e2;\n if (t2.startsWith(`http://`) || t2.startsWith(`https://`))\n try {\n let e3 = new URL(t2);\n t2 = e3.pathname;\n } catch {}\n if (t2.startsWith(T2)) {\n let e3 = t2.slice(T2.length), n3 = e3.indexOf(`/`), r3 = e3.indexOf(`:`);\n t2 = n3 !== -1 && (r3 === -1 || n3 < r3) ? e3.slice(n3 + 1) : e3;\n }\n let n2 = true;\n for (;n2; ) {\n n2 = false;\n for (let e3 of te2)\n if (t2.startsWith(e3)) {\n t2 = t2.slice(e3.length), e3 === `file:///` && (t2 = `/${t2.replace(/^\\/+/, ``)}`), n2 = true;\n break;\n }\n }\n if (w2.test(t2)) {\n let e3 = t2.match(w2);\n e3 && (t2 = t2.slice(e3[0].length));\n }\n if (t2.startsWith(`//`)) {\n let e3 = t2.indexOf(`/`, 2);\n t2 = e3 === -1 ? `` : t2.slice(e3);\n }\n let r2 = t2.indexOf(`?`);\n if (r2 !== -1) {\n let e3 = t2.slice(r2);\n ae2.test(e3) && (t2 = t2.slice(0, r2));\n }\n return t2;\n };\n var je2 = (e2) => {\n let t2 = $2(e2);\n return !(!t2 || !re2.test(t2) || ie2.test(t2));\n };\n\n // dist/_bippy-entry-26306-1779191184823.js\n globalThis.__bippy = {\n getFiberFromHostInstance: Pe,\n getDisplayName: Te,\n traverseFiber: N,\n isCompositeFiber: pe,\n isHostFiber: k,\n getSource: Ae2,\n getOwnerStack: Q,\n normalizeFileName: $2,\n isSourceFile: je2\n };\n})();\n";
|
|
551
|
+
//#endregion
|
|
75
552
|
//#region src/recording.ts
|
|
76
553
|
var activeRecordings = /* @__PURE__ */ new Map();
|
|
77
554
|
var offscreenDocumentCreating = null;
|
|
@@ -290,6 +767,7 @@ async function cleanupRecordingForTab(tabId) {
|
|
|
290
767
|
}
|
|
291
768
|
//#endregion
|
|
292
769
|
//#region src/background.ts
|
|
770
|
+
var js = dedent;
|
|
293
771
|
var RELAY_HOST = "127.0.0.1";
|
|
294
772
|
var RELAY_PORT = 19988;
|
|
295
773
|
function sleep(ms) {
|
|
@@ -444,7 +922,7 @@ var ConnectionManager = class {
|
|
|
444
922
|
if (identity.email) relayUrl.searchParams.set("email", identity.email);
|
|
445
923
|
if (identity.id) relayUrl.searchParams.set("id", identity.id);
|
|
446
924
|
if (identity.installId) relayUrl.searchParams.set("installId", identity.installId);
|
|
447
|
-
relayUrl.searchParams.set("v", "0.0
|
|
925
|
+
relayUrl.searchParams.set("v", "0.2.0");
|
|
448
926
|
logger.debug("Creating WebSocket connection to:", relayUrl);
|
|
449
927
|
const socket = new WebSocket(relayUrl.toString());
|
|
450
928
|
await new Promise((resolve, reject) => {
|
|
@@ -1180,13 +1658,19 @@ async function attachTab(tabId, { skipAttachedEvent = false } = {}) {
|
|
|
1180
1658
|
} catch (error) {
|
|
1181
1659
|
logger.debug("Failed to apply auto-attach for tab:", tabId, error);
|
|
1182
1660
|
}
|
|
1183
|
-
const contextMenuScript = `
|
|
1661
|
+
const contextMenuScript = js`
|
|
1184
1662
|
document.addEventListener('contextmenu', (e) => {
|
|
1185
1663
|
window.__playwriter_lastRightClicked = e.target;
|
|
1186
1664
|
}, true);
|
|
1187
1665
|
`;
|
|
1188
1666
|
await chrome.debugger.sendCommand(debuggee, "Page.addScriptToEvaluateOnNewDocument", { source: contextMenuScript });
|
|
1189
1667
|
await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", { expression: contextMenuScript });
|
|
1668
|
+
try {
|
|
1669
|
+
await chrome.debugger.sendCommand(debuggee, "Page.addScriptToEvaluateOnNewDocument", { source: ghost_cursor_client_default });
|
|
1670
|
+
await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", { expression: ghost_cursor_client_default });
|
|
1671
|
+
} catch (err) {
|
|
1672
|
+
logger.debug("Could not inject ghost cursor (restricted page):", err.message);
|
|
1673
|
+
}
|
|
1190
1674
|
const targetInfo = (await chrome.debugger.sendCommand(debuggee, "Target.getTargetInfo")).targetInfo;
|
|
1191
1675
|
if (!targetInfo.url || targetInfo.url === "" || targetInfo.url === ":") logger.error("WARNING: Target.attachedToTarget will be sent with empty URL! tabId:", tabId, "targetInfo:", JSON.stringify(targetInfo));
|
|
1192
1676
|
const attachOrder = nextSessionId;
|
|
@@ -1220,6 +1704,16 @@ async function attachTab(tabId, { skipAttachedEvent = false } = {}) {
|
|
|
1220
1704
|
}
|
|
1221
1705
|
});
|
|
1222
1706
|
logger.debug("Tab attached successfully:", tabId, "sessionId:", sessionId, "targetId:", targetInfo.targetId, "url:", targetInfo.url, "skipAttachedEvent:", skipAttachedEvent);
|
|
1707
|
+
chrome.scripting.executeScript({
|
|
1708
|
+
target: {
|
|
1709
|
+
tabId,
|
|
1710
|
+
allFrames: false
|
|
1711
|
+
},
|
|
1712
|
+
world: "MAIN",
|
|
1713
|
+
func: initPlaywriterToolbar
|
|
1714
|
+
}).catch((err) => {
|
|
1715
|
+
logger.debug("Could not inject toolbar (restricted page):", err.message);
|
|
1716
|
+
});
|
|
1223
1717
|
return {
|
|
1224
1718
|
targetInfo,
|
|
1225
1719
|
sessionId
|
|
@@ -1239,6 +1733,20 @@ function detachTab(tabId, shouldDetachDebugger) {
|
|
|
1239
1733
|
return;
|
|
1240
1734
|
}
|
|
1241
1735
|
cleanupRecordingForTab(tabId);
|
|
1736
|
+
chrome.scripting.executeScript({
|
|
1737
|
+
target: { tabId },
|
|
1738
|
+
world: "MAIN",
|
|
1739
|
+
func: () => {
|
|
1740
|
+
window.__playwriterToolbarDestroy?.();
|
|
1741
|
+
}
|
|
1742
|
+
}).catch(() => {});
|
|
1743
|
+
chrome.scripting.executeScript({
|
|
1744
|
+
target: { tabId },
|
|
1745
|
+
world: "MAIN",
|
|
1746
|
+
func: () => {
|
|
1747
|
+
globalThis.__playwriterGhostCursor?.disable?.();
|
|
1748
|
+
}
|
|
1749
|
+
}).catch(() => {});
|
|
1242
1750
|
logger.warn(`DISCONNECT: detachTab tabId=${tabId} shouldDetach=${shouldDetachDebugger} stack=${getCallStack()}`);
|
|
1243
1751
|
if (tab.sessionId && tab.targetId) sendMessage({
|
|
1244
1752
|
method: "forwardCDPEvent",
|
|
@@ -1575,10 +2083,22 @@ chrome.contextMenus.remove("playwriter-pin-element").catch(() => {}).finally(()
|
|
|
1575
2083
|
visible: false
|
|
1576
2084
|
});
|
|
1577
2085
|
});
|
|
2086
|
+
chrome.contextMenus.remove("playwriter-copy-react-source").catch(() => {}).finally(() => {
|
|
2087
|
+
chrome.contextMenus?.create({
|
|
2088
|
+
id: "playwriter-copy-react-source",
|
|
2089
|
+
title: "Copy React Component Source Path",
|
|
2090
|
+
contexts: ["all"],
|
|
2091
|
+
visible: false
|
|
2092
|
+
});
|
|
2093
|
+
});
|
|
1578
2094
|
function updateContextMenuVisibility() {
|
|
1579
2095
|
const { currentTabId, tabs } = store.getState();
|
|
1580
2096
|
const isConnected = currentTabId !== void 0 && tabs.get(currentTabId)?.state === "connected";
|
|
1581
2097
|
chrome.contextMenus?.update("playwriter-pin-element", { visible: isConnected });
|
|
2098
|
+
chrome.contextMenus?.update("playwriter-copy-react-source", { visible: isConnected });
|
|
2099
|
+
}
|
|
2100
|
+
function buildPinnedElementInspectionCode(options) {
|
|
2101
|
+
return `inspectPinnedElement(${JSON.stringify(options.url).replace(/'/g, "\\u0027")},"globalThis.${options.pinName}")`;
|
|
1582
2102
|
}
|
|
1583
2103
|
chrome.runtime.onInstalled.addListener((details) => {});
|
|
1584
2104
|
function serializeTabs(tabs) {
|
|
@@ -1715,46 +2235,45 @@ chrome.windows.onCreated.addListener(async (popupWindow) => {
|
|
|
1715
2235
|
}
|
|
1716
2236
|
});
|
|
1717
2237
|
chrome.contextMenus?.onClicked.addListener(async (info, tab) => {
|
|
1718
|
-
if (
|
|
2238
|
+
if (!tab?.id) return;
|
|
1719
2239
|
const tabInfo = store.getState().tabs.get(tab.id);
|
|
1720
2240
|
if (!tabInfo || tabInfo.state !== "connected") {
|
|
1721
2241
|
logger.debug("Tab not connected, ignoring");
|
|
1722
2242
|
return;
|
|
1723
2243
|
}
|
|
1724
2244
|
const debuggee = { tabId: tab.id };
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
const pageIndex = connectedTabs.findIndex(([id]) => id === tab.id);
|
|
1738
|
-
const hasMultiplePages = connectedTabs.length > 1;
|
|
1739
|
-
try {
|
|
1740
|
-
const result = await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", {
|
|
1741
|
-
expression: `
|
|
2245
|
+
if (info.menuItemId === "playwriter-pin-element") try {
|
|
2246
|
+
const jsAllocatePin = js`
|
|
2247
|
+
(function() {
|
|
2248
|
+
window.__playwriterPinCount = (window.__playwriterPinCount || 0) + 1;
|
|
2249
|
+
return window.__playwriterPinCount;
|
|
2250
|
+
})()
|
|
2251
|
+
`;
|
|
2252
|
+
const name = `playwriterPinnedElem${(await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", {
|
|
2253
|
+
expression: jsAllocatePin,
|
|
2254
|
+
returnByValue: true
|
|
2255
|
+
})).result?.value ?? 1}`;
|
|
2256
|
+
const jsAssignPin = js`
|
|
1742
2257
|
if (window.__playwriter_lastRightClicked) {
|
|
1743
2258
|
window.${name} = window.__playwriter_lastRightClicked;
|
|
1744
2259
|
'${name}';
|
|
1745
2260
|
} else {
|
|
1746
2261
|
throw new Error('No element was right-clicked');
|
|
1747
2262
|
}
|
|
1748
|
-
|
|
2263
|
+
`;
|
|
2264
|
+
const result = await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", {
|
|
2265
|
+
expression: jsAssignPin,
|
|
1749
2266
|
returnByValue: true
|
|
1750
2267
|
});
|
|
1751
2268
|
if (result.exceptionDetails) {
|
|
1752
2269
|
logger.error("Failed to pin element:", result.exceptionDetails.text);
|
|
1753
2270
|
return;
|
|
1754
2271
|
}
|
|
1755
|
-
const clipboardText =
|
|
1756
|
-
|
|
1757
|
-
|
|
2272
|
+
const clipboardText = "playwriter -e '" + buildPinnedElementInspectionCode({
|
|
2273
|
+
pinName: name,
|
|
2274
|
+
url: tab.url || ""
|
|
2275
|
+
}) + "'";
|
|
2276
|
+
const jsPinFlashAndCopy = js`
|
|
1758
2277
|
(() => {
|
|
1759
2278
|
const el = window.${name};
|
|
1760
2279
|
if (!el) return;
|
|
@@ -1763,13 +2282,126 @@ chrome.contextMenus?.onClicked.addListener(async (info, tab) => {
|
|
|
1763
2282
|
setTimeout(() => el.setAttribute('style', orig), 300);
|
|
1764
2283
|
return navigator.clipboard.writeText(${JSON.stringify(clipboardText)});
|
|
1765
2284
|
})()
|
|
1766
|
-
|
|
1767
|
-
|
|
2285
|
+
`;
|
|
2286
|
+
await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", {
|
|
2287
|
+
expression: jsPinFlashAndCopy,
|
|
2288
|
+
awaitPromise: true,
|
|
2289
|
+
userGesture: true
|
|
1768
2290
|
});
|
|
1769
2291
|
logger.debug("Pinned element as:", name);
|
|
1770
2292
|
} catch (error) {
|
|
1771
2293
|
logger.error("Failed to pin element:", error.message);
|
|
1772
2294
|
}
|
|
2295
|
+
if (info.menuItemId === "playwriter-copy-react-source") try {
|
|
2296
|
+
if (!(await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", {
|
|
2297
|
+
expression: "!!globalThis.__bippy",
|
|
2298
|
+
returnByValue: true
|
|
2299
|
+
})).result?.value) await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", { expression: bippy_default });
|
|
2300
|
+
const jsResolveSource = js`
|
|
2301
|
+
(async () => {
|
|
2302
|
+
const el = window.__playwriter_lastRightClicked;
|
|
2303
|
+
if (!el) return JSON.stringify({ error: 'No element was right-clicked' });
|
|
2304
|
+
|
|
2305
|
+
const bippy = globalThis.__bippy;
|
|
2306
|
+
if (!bippy) return JSON.stringify({ error: 'bippy not loaded' });
|
|
2307
|
+
|
|
2308
|
+
// bippy.normalizeFileName strips "/app-pages-browser/" but not the parenthesized
|
|
2309
|
+
// form "/(app-pages-browser)/" that Next.js webpack actually uses. This regex
|
|
2310
|
+
// strips all Next.js webpack layer prefixes: (app-pages-browser), (ssr), (rsc),
|
|
2311
|
+
// (action-browser), (pages-dir-browser), (pages-dir-edge), (pages-dir-node).
|
|
2312
|
+
// Also strips leading "./" that often follows the layer prefix.
|
|
2313
|
+
const cleanFileName = (name) => {
|
|
2314
|
+
let f = bippy.normalizeFileName(name);
|
|
2315
|
+
f = f.replace(/^\/?\\([-\\w]+\\)\\//, '');
|
|
2316
|
+
f = f.replace(/^\\.[\\/]/, '');
|
|
2317
|
+
return f;
|
|
2318
|
+
};
|
|
2319
|
+
|
|
2320
|
+
let fiber;
|
|
2321
|
+
try { fiber = bippy.getFiberFromHostInstance(el); } catch {}
|
|
2322
|
+
if (!fiber) return JSON.stringify({ error: 'No React fiber found. Is this a React app?' });
|
|
2323
|
+
|
|
2324
|
+
// Walk up to find nearest composite fiber with source info
|
|
2325
|
+
let current = fiber;
|
|
2326
|
+
for (let i = 0; i < 50 && current; i++) {
|
|
2327
|
+
try {
|
|
2328
|
+
if (bippy.isCompositeFiber(current)) {
|
|
2329
|
+
const source = await bippy.getSource(current);
|
|
2330
|
+
if (source && source.fileName && bippy.isSourceFile(source.fileName)) {
|
|
2331
|
+
return JSON.stringify({
|
|
2332
|
+
fileName: cleanFileName(source.fileName),
|
|
2333
|
+
lineNumber: source.lineNumber || null,
|
|
2334
|
+
columnNumber: source.columnNumber || null,
|
|
2335
|
+
componentName: source.functionName || bippy.getDisplayName(current.type) || null,
|
|
2336
|
+
});
|
|
2337
|
+
}
|
|
2338
|
+
// Try owner stack as fallback for this fiber
|
|
2339
|
+
const ownerStack = await bippy.getOwnerStack(current);
|
|
2340
|
+
for (const frame of ownerStack) {
|
|
2341
|
+
if (frame.fileName && bippy.isSourceFile(frame.fileName)) {
|
|
2342
|
+
return JSON.stringify({
|
|
2343
|
+
fileName: cleanFileName(frame.fileName),
|
|
2344
|
+
lineNumber: frame.lineNumber || null,
|
|
2345
|
+
columnNumber: frame.columnNumber || null,
|
|
2346
|
+
componentName: frame.functionName || bippy.getDisplayName(current.type) || null,
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
} catch {}
|
|
2352
|
+
current = current.return;
|
|
2353
|
+
}
|
|
2354
|
+
return JSON.stringify({ error: 'No React source location found. Is this a dev build with source maps?' });
|
|
2355
|
+
})()
|
|
2356
|
+
`;
|
|
2357
|
+
const sourceResult = await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", {
|
|
2358
|
+
expression: jsResolveSource,
|
|
2359
|
+
returnByValue: true,
|
|
2360
|
+
awaitPromise: true
|
|
2361
|
+
});
|
|
2362
|
+
if (sourceResult.exceptionDetails) {
|
|
2363
|
+
logger.error("Failed to get React source:", sourceResult.exceptionDetails.text);
|
|
2364
|
+
return;
|
|
2365
|
+
}
|
|
2366
|
+
const parsed = JSON.parse(sourceResult.result?.value || "{}");
|
|
2367
|
+
if (!parsed.fileName && !parsed.error) parsed.error = "React source result missing fileName";
|
|
2368
|
+
if (parsed.error) {
|
|
2369
|
+
const jsFlashRed = js`
|
|
2370
|
+
(() => {
|
|
2371
|
+
const el = window.__playwriter_lastRightClicked;
|
|
2372
|
+
if (!el) return;
|
|
2373
|
+
const orig = el.getAttribute('style') || '';
|
|
2374
|
+
el.setAttribute('style', orig + '; outline: 3px solid #ef4444 !important; outline-offset: 2px !important;');
|
|
2375
|
+
setTimeout(() => el.setAttribute('style', orig), 600);
|
|
2376
|
+
})()
|
|
2377
|
+
`;
|
|
2378
|
+
await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", { expression: jsFlashRed });
|
|
2379
|
+
logger.debug("React source not found:", parsed.error);
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
const clipboardText = (() => {
|
|
2383
|
+
if (parsed.lineNumber) return `${parsed.fileName}:${parsed.lineNumber}`;
|
|
2384
|
+
return parsed.fileName;
|
|
2385
|
+
})();
|
|
2386
|
+
const jsFlashGreenAndCopy = js`
|
|
2387
|
+
(() => {
|
|
2388
|
+
const el = window.__playwriter_lastRightClicked;
|
|
2389
|
+
if (!el) return;
|
|
2390
|
+
const orig = el.getAttribute('style') || '';
|
|
2391
|
+
el.setAttribute('style', orig + '; outline: 3px solid #22c55e !important; outline-offset: 2px !important; box-shadow: 0 0 0 3px #22c55e !important;');
|
|
2392
|
+
setTimeout(() => el.setAttribute('style', orig), 300);
|
|
2393
|
+
return navigator.clipboard.writeText(${JSON.stringify(clipboardText)});
|
|
2394
|
+
})()
|
|
2395
|
+
`;
|
|
2396
|
+
await chrome.debugger.sendCommand(debuggee, "Runtime.evaluate", {
|
|
2397
|
+
expression: jsFlashGreenAndCopy,
|
|
2398
|
+
awaitPromise: true,
|
|
2399
|
+
userGesture: true
|
|
2400
|
+
});
|
|
2401
|
+
logger.debug("Copied React source path:", clipboardText, "component:", parsed.componentName);
|
|
2402
|
+
} catch (error) {
|
|
2403
|
+
logger.error("Failed to copy React source:", error.message);
|
|
2404
|
+
}
|
|
1773
2405
|
});
|
|
1774
2406
|
updateIcons();
|
|
1775
2407
|
chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => {
|
|
@@ -1817,4 +2449,20 @@ chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => {
|
|
|
1817
2449
|
}
|
|
1818
2450
|
return false;
|
|
1819
2451
|
});
|
|
2452
|
+
chrome.webNavigation.onDOMContentLoaded.addListener((details) => {
|
|
2453
|
+
if (details.frameId !== 0) return;
|
|
2454
|
+
const { tabs } = store.getState();
|
|
2455
|
+
const tabInfo = tabs.get(details.tabId);
|
|
2456
|
+
if (!tabInfo || tabInfo.state !== "connected") return;
|
|
2457
|
+
chrome.scripting.executeScript({
|
|
2458
|
+
target: {
|
|
2459
|
+
tabId: details.tabId,
|
|
2460
|
+
allFrames: false
|
|
2461
|
+
},
|
|
2462
|
+
world: "MAIN",
|
|
2463
|
+
func: initPlaywriterToolbar
|
|
2464
|
+
}).catch((err) => {
|
|
2465
|
+
logger.debug("Could not re-inject toolbar after navigation:", err.message);
|
|
2466
|
+
});
|
|
2467
|
+
});
|
|
1820
2468
|
//#endregion
|