react-grab 0.0.20 → 0.0.21
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/index.cjs +547 -1027
- package/dist/index.d.cts +1 -46
- package/dist/index.d.ts +1 -46
- package/dist/index.global.js +92 -26
- package/dist/index.js +548 -1026
- package/package.json +6 -21
- package/dist/plugins/vite.cjs +0 -46
- package/dist/plugins/vite.d.cts +0 -14
- package/dist/plugins/vite.d.ts +0 -14
- package/dist/plugins/vite.js +0 -44
package/dist/index.cjs
CHANGED
|
@@ -12,18 +12,7 @@ var source = require('bippy/dist/source');
|
|
|
12
12
|
* LICENSE file in the root directory of this source tree.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
// src/
|
|
16
|
-
var cursorAdapter = {
|
|
17
|
-
name: "cursor",
|
|
18
|
-
open: (promptText) => {
|
|
19
|
-
if (!promptText) return;
|
|
20
|
-
const url = new URL("cursor://anysphere.cursor-deeplink/prompt");
|
|
21
|
-
url.searchParams.set("text", promptText);
|
|
22
|
-
window.open(url.toString(), "_blank");
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// src/hotkeys.ts
|
|
15
|
+
// src/utils/is-keyboard-event-triggered-by-input.ts
|
|
27
16
|
var FORM_TAGS_AND_ROLES = [
|
|
28
17
|
"input",
|
|
29
18
|
"textarea",
|
|
@@ -71,394 +60,51 @@ var isHotkeyEnabledOnTagName = (event, enabledOnTags = false) => {
|
|
|
71
60
|
var isKeyboardEventTriggeredByInput = (event) => {
|
|
72
61
|
return isHotkeyEnabledOnTagName(event, FORM_TAGS_AND_ROLES);
|
|
73
62
|
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
libStore.setState((state) => {
|
|
83
|
-
const newTimestamps = new Map(state.keyPressTimestamps);
|
|
84
|
-
if (!state.pressedKeys.has(event.key)) {
|
|
85
|
-
newTimestamps.set(event.key, Date.now());
|
|
86
|
-
}
|
|
87
|
-
return {
|
|
88
|
-
...state,
|
|
89
|
-
keyPressTimestamps: newTimestamps,
|
|
90
|
-
pressedKeys: /* @__PURE__ */ new Set([event.key, ...state.pressedKeys])
|
|
91
|
-
};
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
const handleKeyUp = (event) => {
|
|
95
|
-
if (event.code === void 0) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
libStore.setState((state) => {
|
|
99
|
-
const newTimestamps = new Map(state.keyPressTimestamps);
|
|
100
|
-
newTimestamps.delete(event.key);
|
|
101
|
-
return {
|
|
102
|
-
...state,
|
|
103
|
-
keyPressTimestamps: newTimestamps,
|
|
104
|
-
pressedKeys: new Set(
|
|
105
|
-
[...state.pressedKeys].filter((key) => key !== event.key)
|
|
106
|
-
)
|
|
107
|
-
};
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
const handleBlur = () => {
|
|
111
|
-
libStore.setState((state) => ({
|
|
112
|
-
...state,
|
|
113
|
-
keyPressTimestamps: /* @__PURE__ */ new Map(),
|
|
114
|
-
pressedKeys: /* @__PURE__ */ new Set()
|
|
115
|
-
}));
|
|
116
|
-
};
|
|
117
|
-
const handleContextmenu = () => {
|
|
118
|
-
libStore.setState((state) => ({
|
|
119
|
-
...state,
|
|
120
|
-
keyPressTimestamps: /* @__PURE__ */ new Map(),
|
|
121
|
-
pressedKeys: /* @__PURE__ */ new Set()
|
|
122
|
-
}));
|
|
123
|
-
};
|
|
124
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
125
|
-
document.addEventListener("keyup", handleKeyUp);
|
|
126
|
-
window.addEventListener("blur", handleBlur);
|
|
127
|
-
window.addEventListener("contextmenu", handleContextmenu);
|
|
128
|
-
return () => {
|
|
129
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
130
|
-
document.removeEventListener("keyup", handleKeyUp);
|
|
131
|
-
window.removeEventListener("blur", handleBlur);
|
|
132
|
-
window.removeEventListener("contextmenu", handleContextmenu);
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
var isKeyPressed = (key) => {
|
|
136
|
-
const { pressedKeys } = libStore.getState();
|
|
137
|
-
if (key.length === 1) {
|
|
138
|
-
return pressedKeys.has(key.toLowerCase()) || pressedKeys.has(key.toUpperCase());
|
|
139
|
-
}
|
|
140
|
-
return pressedKeys.has(key);
|
|
141
|
-
};
|
|
142
|
-
var watchKeyHeldFor = (key, duration, onHeld) => {
|
|
143
|
-
let timeoutId = null;
|
|
144
|
-
let unsubscribe = null;
|
|
145
|
-
const watchStartTime = Date.now();
|
|
146
|
-
const cleanup = () => {
|
|
147
|
-
if (timeoutId !== null) {
|
|
148
|
-
clearTimeout(timeoutId);
|
|
149
|
-
timeoutId = null;
|
|
150
|
-
}
|
|
151
|
-
if (unsubscribe !== null) {
|
|
152
|
-
unsubscribe();
|
|
153
|
-
unsubscribe = null;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
const checkSingleKeyPressed = (keyToCheck, pressedKeys) => {
|
|
157
|
-
if (keyToCheck.length === 1) {
|
|
158
|
-
return pressedKeys.has(keyToCheck.toLowerCase()) || pressedKeys.has(keyToCheck.toUpperCase());
|
|
159
|
-
}
|
|
160
|
-
return pressedKeys.has(keyToCheck);
|
|
161
|
-
};
|
|
162
|
-
const checkAllKeysPressed = (pressedKeys) => {
|
|
163
|
-
if (Array.isArray(key)) {
|
|
164
|
-
return key.every(
|
|
165
|
-
(keyFromCombo) => checkSingleKeyPressed(keyFromCombo, pressedKeys)
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
return checkSingleKeyPressed(key, pressedKeys);
|
|
169
|
-
};
|
|
170
|
-
const scheduleCallback = () => {
|
|
171
|
-
const state = libStore.getState();
|
|
172
|
-
const { pressedKeys } = state;
|
|
173
|
-
if (!checkAllKeysPressed(pressedKeys)) {
|
|
174
|
-
if (timeoutId !== null) {
|
|
175
|
-
clearTimeout(timeoutId);
|
|
176
|
-
timeoutId = null;
|
|
177
|
-
}
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
const elapsed = Date.now() - watchStartTime;
|
|
181
|
-
const remaining = duration - elapsed;
|
|
182
|
-
if (remaining <= 0) {
|
|
183
|
-
onHeld();
|
|
184
|
-
cleanup();
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
if (timeoutId !== null) {
|
|
188
|
-
clearTimeout(timeoutId);
|
|
189
|
-
}
|
|
190
|
-
timeoutId = setTimeout(() => {
|
|
191
|
-
onHeld();
|
|
192
|
-
cleanup();
|
|
193
|
-
}, remaining);
|
|
194
|
-
};
|
|
195
|
-
unsubscribe = libStore.subscribe(
|
|
196
|
-
() => {
|
|
197
|
-
scheduleCallback();
|
|
198
|
-
},
|
|
199
|
-
(state) => state.pressedKeys
|
|
200
|
-
);
|
|
201
|
-
scheduleCallback();
|
|
202
|
-
return cleanup;
|
|
203
|
-
};
|
|
204
|
-
var fiberRoots = bippy._fiberRoots;
|
|
205
|
-
bippy.instrument({
|
|
206
|
-
onCommitFiberRoot(_, fiberRoot) {
|
|
207
|
-
fiberRoots.add(fiberRoot);
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
var dedupeFileName = (fileName) => {
|
|
211
|
-
if (!fileName) return fileName;
|
|
212
|
-
const parts = fileName.split("/");
|
|
213
|
-
for (let start = 0; start < parts.length / 2; start++) {
|
|
214
|
-
for (let len = 1; len <= parts.length - start; len++) {
|
|
215
|
-
const firstSeq = parts.slice(start, start + len);
|
|
216
|
-
const secondStart = start + len;
|
|
217
|
-
const secondSeq = parts.slice(secondStart, secondStart + len);
|
|
218
|
-
if (firstSeq.length > 2 && firstSeq.length === secondSeq.length && firstSeq.every((part, i) => part === secondSeq[i])) {
|
|
219
|
-
return parts.slice(secondStart).join("/");
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return fileName;
|
|
224
|
-
};
|
|
225
|
-
var getStack = async (element) => {
|
|
226
|
-
const fiber = bippy.getFiberFromHostInstance(element);
|
|
227
|
-
if (!fiber) return null;
|
|
228
|
-
const stackTrace = source.getFiberStackTrace(fiber);
|
|
229
|
-
const rawOwnerStack = await source.getOwnerStack(stackTrace);
|
|
230
|
-
const stack = rawOwnerStack.map((item) => ({
|
|
231
|
-
componentName: item.name,
|
|
232
|
-
fileName: dedupeFileName(item.source?.fileName)
|
|
233
|
-
}));
|
|
234
|
-
if (stack.length > 0 && fiber) {
|
|
235
|
-
const fiberSource = await source.getFiberSource(fiber);
|
|
236
|
-
if (fiberSource) {
|
|
237
|
-
const fiberType = fiber.type;
|
|
238
|
-
const displayName = fiberType?.displayName ?? fiberType?.name ?? stack[0].componentName;
|
|
239
|
-
stack[0].displayName = displayName;
|
|
240
|
-
const dedupedFileName = dedupeFileName(fiberSource.fileName);
|
|
241
|
-
stack[0].source = `${dedupedFileName}:${fiberSource.lineNumber}:${fiberSource.columnNumber}`;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return stack;
|
|
245
|
-
};
|
|
246
|
-
var filterStack = (stack) => {
|
|
247
|
-
return stack.filter(
|
|
248
|
-
(item) => item.fileName && !item.fileName.includes("node_modules") && item.componentName.length > 1 && !item.fileName.startsWith("_")
|
|
249
|
-
);
|
|
250
|
-
};
|
|
251
|
-
var serializeStack = (stack) => {
|
|
252
|
-
return stack.map((item, index) => {
|
|
253
|
-
const fileName = item.fileName;
|
|
254
|
-
const componentName = item.displayName || item.componentName;
|
|
255
|
-
let result = `${componentName}${fileName ? ` (${fileName})` : ""}`;
|
|
256
|
-
if (index === 0 && item.source) {
|
|
257
|
-
result += `
|
|
258
|
-
${item.source}`;
|
|
259
|
-
}
|
|
260
|
-
return result;
|
|
261
|
-
}).join("\n");
|
|
262
|
-
};
|
|
263
|
-
var getHTMLSnippet = (element) => {
|
|
264
|
-
const semanticTags = /* @__PURE__ */ new Set([
|
|
265
|
-
"article",
|
|
266
|
-
"aside",
|
|
267
|
-
"footer",
|
|
268
|
-
"form",
|
|
269
|
-
"header",
|
|
270
|
-
"main",
|
|
271
|
-
"nav",
|
|
272
|
-
"section"
|
|
273
|
-
]);
|
|
274
|
-
const hasDistinguishingFeatures = (el) => {
|
|
275
|
-
const tagName = el.tagName.toLowerCase();
|
|
276
|
-
if (semanticTags.has(tagName)) return true;
|
|
277
|
-
if (el.id) return true;
|
|
278
|
-
if (el.className && typeof el.className === "string") {
|
|
279
|
-
const classes = el.className.trim();
|
|
280
|
-
if (classes && classes.length > 0) return true;
|
|
281
|
-
}
|
|
282
|
-
return Array.from(el.attributes).some(
|
|
283
|
-
(attr) => attr.name.startsWith("data-")
|
|
284
|
-
);
|
|
285
|
-
};
|
|
286
|
-
const getAncestorChain = (el, maxDepth = 10) => {
|
|
287
|
-
const ancestors2 = [];
|
|
288
|
-
let current = el.parentElement;
|
|
289
|
-
let depth = 0;
|
|
290
|
-
while (current && depth < maxDepth && current.tagName !== "BODY") {
|
|
291
|
-
if (hasDistinguishingFeatures(current)) {
|
|
292
|
-
ancestors2.push(current);
|
|
293
|
-
if (ancestors2.length >= 3) break;
|
|
294
|
-
}
|
|
295
|
-
current = current.parentElement;
|
|
296
|
-
depth++;
|
|
297
|
-
}
|
|
298
|
-
return ancestors2.reverse();
|
|
299
|
-
};
|
|
300
|
-
const getCSSPath = (el) => {
|
|
301
|
-
const parts = [];
|
|
302
|
-
let current = el;
|
|
303
|
-
let depth = 0;
|
|
304
|
-
const maxDepth = 5;
|
|
305
|
-
while (current && depth < maxDepth && current.tagName !== "BODY") {
|
|
306
|
-
let selector = current.tagName.toLowerCase();
|
|
307
|
-
if (current.id) {
|
|
308
|
-
selector += `#${current.id}`;
|
|
309
|
-
parts.unshift(selector);
|
|
310
|
-
break;
|
|
311
|
-
} else if (current.className && typeof current.className === "string" && current.className.trim()) {
|
|
312
|
-
const classes = current.className.trim().split(/\s+/).slice(0, 2);
|
|
313
|
-
selector += `.${classes.join(".")}`;
|
|
314
|
-
}
|
|
315
|
-
if (!current.id && (!current.className || !current.className.trim()) && current.parentElement) {
|
|
316
|
-
const siblings = Array.from(current.parentElement.children);
|
|
317
|
-
const index = siblings.indexOf(current);
|
|
318
|
-
if (index >= 0 && siblings.length > 1) {
|
|
319
|
-
selector += `:nth-child(${index + 1})`;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
parts.unshift(selector);
|
|
323
|
-
current = current.parentElement;
|
|
324
|
-
depth++;
|
|
325
|
-
}
|
|
326
|
-
return parts.join(" > ");
|
|
327
|
-
};
|
|
328
|
-
const getElementTag = (el, compact = false) => {
|
|
329
|
-
const tagName = el.tagName.toLowerCase();
|
|
330
|
-
const attrs = [];
|
|
331
|
-
if (el.id) {
|
|
332
|
-
attrs.push(`id="${el.id}"`);
|
|
333
|
-
}
|
|
334
|
-
if (el.className && typeof el.className === "string") {
|
|
335
|
-
const classes = el.className.trim().split(/\s+/);
|
|
336
|
-
if (classes.length > 0 && classes[0]) {
|
|
337
|
-
const displayClasses = compact ? classes.slice(0, 3) : classes;
|
|
338
|
-
let classStr = displayClasses.join(" ");
|
|
339
|
-
if (classStr.length > 30) {
|
|
340
|
-
classStr = classStr.substring(0, 30) + "...";
|
|
341
|
-
}
|
|
342
|
-
attrs.push(`class="${classStr}"`);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
const dataAttrs = Array.from(el.attributes).filter(
|
|
346
|
-
(attr) => attr.name.startsWith("data-")
|
|
347
|
-
);
|
|
348
|
-
const displayDataAttrs = compact ? dataAttrs.slice(0, 1) : dataAttrs;
|
|
349
|
-
for (const attr of displayDataAttrs) {
|
|
350
|
-
let value = attr.value;
|
|
351
|
-
if (value.length > 20) {
|
|
352
|
-
value = value.substring(0, 20) + "...";
|
|
353
|
-
}
|
|
354
|
-
attrs.push(`${attr.name}="${value}"`);
|
|
355
|
-
}
|
|
356
|
-
const ariaLabel = el.getAttribute("aria-label");
|
|
357
|
-
if (ariaLabel && !compact) {
|
|
358
|
-
let value = ariaLabel;
|
|
359
|
-
if (value.length > 20) {
|
|
360
|
-
value = value.substring(0, 20) + "...";
|
|
361
|
-
}
|
|
362
|
-
attrs.push(`aria-label="${value}"`);
|
|
363
|
-
}
|
|
364
|
-
return attrs.length > 0 ? `<${tagName} ${attrs.join(" ")}>` : `<${tagName}>`;
|
|
365
|
-
};
|
|
366
|
-
const getClosingTag = (el) => {
|
|
367
|
-
return `</${el.tagName.toLowerCase()}>`;
|
|
368
|
-
};
|
|
369
|
-
const getTextContent = (el) => {
|
|
370
|
-
let text = el.textContent || "";
|
|
371
|
-
text = text.trim().replace(/\s+/g, " ");
|
|
372
|
-
const maxLength = 60;
|
|
373
|
-
if (text.length > maxLength) {
|
|
374
|
-
text = text.substring(0, maxLength) + "...";
|
|
375
|
-
}
|
|
376
|
-
return text;
|
|
377
|
-
};
|
|
378
|
-
const getSiblingIdentifier = (el) => {
|
|
379
|
-
if (el.id) return `#${el.id}`;
|
|
380
|
-
if (el.className && typeof el.className === "string") {
|
|
381
|
-
const classes = el.className.trim().split(/\s+/);
|
|
382
|
-
if (classes.length > 0 && classes[0]) {
|
|
383
|
-
return `.${classes[0]}`;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
return null;
|
|
387
|
-
};
|
|
388
|
-
const lines = [];
|
|
389
|
-
lines.push(`Path: ${getCSSPath(element)}`);
|
|
390
|
-
lines.push("");
|
|
391
|
-
const ancestors = getAncestorChain(element);
|
|
392
|
-
for (let i = 0; i < ancestors.length; i++) {
|
|
393
|
-
const indent2 = " ".repeat(i);
|
|
394
|
-
lines.push(indent2 + getElementTag(ancestors[i], true));
|
|
395
|
-
}
|
|
396
|
-
const parent = element.parentElement;
|
|
397
|
-
let targetIndex = -1;
|
|
398
|
-
if (parent) {
|
|
399
|
-
const siblings = Array.from(parent.children);
|
|
400
|
-
targetIndex = siblings.indexOf(element);
|
|
401
|
-
if (targetIndex > 0) {
|
|
402
|
-
const prevSibling = siblings[targetIndex - 1];
|
|
403
|
-
const prevId = getSiblingIdentifier(prevSibling);
|
|
404
|
-
if (prevId && targetIndex <= 2) {
|
|
405
|
-
const indent2 = " ".repeat(ancestors.length);
|
|
406
|
-
lines.push(`${indent2} ${getElementTag(prevSibling, true)}`);
|
|
407
|
-
lines.push(`${indent2} </${prevSibling.tagName.toLowerCase()}>`);
|
|
408
|
-
} else if (targetIndex > 0) {
|
|
409
|
-
const indent2 = " ".repeat(ancestors.length);
|
|
410
|
-
lines.push(
|
|
411
|
-
`${indent2} ... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
const indent = " ".repeat(ancestors.length);
|
|
417
|
-
lines.push(indent + " <!-- SELECTED -->");
|
|
418
|
-
const textContent = getTextContent(element);
|
|
419
|
-
const childrenCount = element.children.length;
|
|
420
|
-
if (textContent && childrenCount === 0 && textContent.length < 40) {
|
|
421
|
-
lines.push(
|
|
422
|
-
`${indent} ${getElementTag(element)}${textContent}${getClosingTag(
|
|
423
|
-
element
|
|
424
|
-
)}`
|
|
63
|
+
|
|
64
|
+
// src/utils/mount-root.ts
|
|
65
|
+
var ATTRIBUTE_NAME = "data-react-grab";
|
|
66
|
+
var mountRoot = () => {
|
|
67
|
+
const mountedHost = document.querySelector(`[${ATTRIBUTE_NAME}]`);
|
|
68
|
+
if (mountedHost) {
|
|
69
|
+
const mountedRoot = mountedHost.shadowRoot?.querySelector(
|
|
70
|
+
`[${ATTRIBUTE_NAME}]`
|
|
425
71
|
);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (textContent) {
|
|
429
|
-
lines.push(`${indent} ${textContent}`);
|
|
430
|
-
}
|
|
431
|
-
if (childrenCount > 0) {
|
|
432
|
-
lines.push(
|
|
433
|
-
`${indent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
lines.push(indent + " " + getClosingTag(element));
|
|
437
|
-
}
|
|
438
|
-
if (parent && targetIndex >= 0) {
|
|
439
|
-
const siblings = Array.from(parent.children);
|
|
440
|
-
const siblingsAfter = siblings.length - targetIndex - 1;
|
|
441
|
-
if (siblingsAfter > 0) {
|
|
442
|
-
const nextSibling = siblings[targetIndex + 1];
|
|
443
|
-
const nextId = getSiblingIdentifier(nextSibling);
|
|
444
|
-
if (nextId && siblingsAfter <= 2) {
|
|
445
|
-
lines.push(`${indent} ${getElementTag(nextSibling, true)}`);
|
|
446
|
-
lines.push(`${indent} </${nextSibling.tagName.toLowerCase()}>`);
|
|
447
|
-
} else {
|
|
448
|
-
lines.push(
|
|
449
|
-
`${indent} ... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
|
|
450
|
-
);
|
|
451
|
-
}
|
|
72
|
+
if (mountedRoot instanceof HTMLDivElement && mountedHost.shadowRoot) {
|
|
73
|
+
return mountedRoot;
|
|
452
74
|
}
|
|
453
75
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
76
|
+
const host = document.createElement("div");
|
|
77
|
+
host.setAttribute(ATTRIBUTE_NAME, "true");
|
|
78
|
+
host.style.zIndex = "2147483646";
|
|
79
|
+
host.style.position = "fixed";
|
|
80
|
+
host.style.top = "0";
|
|
81
|
+
host.style.left = "0";
|
|
82
|
+
const shadowRoot = host.attachShadow({ mode: "open" });
|
|
83
|
+
const root = document.createElement("div");
|
|
84
|
+
root.setAttribute(ATTRIBUTE_NAME, "true");
|
|
85
|
+
shadowRoot.appendChild(root);
|
|
86
|
+
const doc = document.body ?? document.documentElement;
|
|
87
|
+
doc.appendChild(host);
|
|
88
|
+
return root;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/utils/is-element-visible.ts
|
|
92
|
+
var isElementVisible = (element, computedStyle = window.getComputedStyle(element)) => {
|
|
93
|
+
return computedStyle.display !== "none" && computedStyle.visibility !== "hidden" && computedStyle.opacity !== "0";
|
|
459
94
|
};
|
|
460
95
|
|
|
461
96
|
// src/overlay.ts
|
|
97
|
+
var templateCache = /* @__PURE__ */ new Map();
|
|
98
|
+
var html = (strings, ...values) => {
|
|
99
|
+
const key = strings.join("");
|
|
100
|
+
let template = templateCache.get(key);
|
|
101
|
+
if (!template) {
|
|
102
|
+
template = document.createElement("template");
|
|
103
|
+
template.innerHTML = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ""), "");
|
|
104
|
+
templateCache.set(key, template);
|
|
105
|
+
}
|
|
106
|
+
return template.content.firstElementChild.cloneNode(true);
|
|
107
|
+
};
|
|
462
108
|
var VIEWPORT_MARGIN_PX = 8;
|
|
463
109
|
var LABEL_OFFSET_PX = 6;
|
|
464
110
|
var INDICATOR_CLAMP_PADDING_PX = 4;
|
|
@@ -476,20 +122,23 @@ var createSelectionElement = ({
|
|
|
476
122
|
x,
|
|
477
123
|
y
|
|
478
124
|
}) => {
|
|
479
|
-
const overlay =
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
125
|
+
const overlay = html`
|
|
126
|
+
<div style="
|
|
127
|
+
position: fixed;
|
|
128
|
+
top: ${y}px;
|
|
129
|
+
left: ${x}px;
|
|
130
|
+
width: ${width}px;
|
|
131
|
+
height: ${height}px;
|
|
132
|
+
border-radius: ${borderRadius};
|
|
133
|
+
transform: ${transform};
|
|
134
|
+
pointer-events: auto;
|
|
135
|
+
border: 1px solid rgb(210, 57, 192);
|
|
136
|
+
background-color: rgba(210, 57, 192, 0.2);
|
|
137
|
+
z-index: 2147483646;
|
|
138
|
+
box-sizing: border-box;
|
|
139
|
+
display: none;
|
|
140
|
+
"></div>
|
|
141
|
+
`;
|
|
493
142
|
return overlay;
|
|
494
143
|
};
|
|
495
144
|
var updateSelectionElement = (element, { borderRadius, height, transform, width, x, y }) => {
|
|
@@ -520,7 +169,7 @@ var updateSelectionElement = (element, { borderRadius, height, transform, width,
|
|
|
520
169
|
element.style.transform = transform;
|
|
521
170
|
}
|
|
522
171
|
};
|
|
523
|
-
var createSelectionOverlay = (root) => {
|
|
172
|
+
var createSelectionOverlay = (root, onSelectionClick) => {
|
|
524
173
|
const element = createSelectionElement({
|
|
525
174
|
borderRadius: "0px",
|
|
526
175
|
height: 0,
|
|
@@ -531,22 +180,24 @@ var createSelectionOverlay = (root) => {
|
|
|
531
180
|
});
|
|
532
181
|
root.appendChild(element);
|
|
533
182
|
let visible = false;
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
event
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
183
|
+
let hasBeenShown = false;
|
|
184
|
+
element.addEventListener(
|
|
185
|
+
"mousedown",
|
|
186
|
+
(event) => {
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
event.stopPropagation();
|
|
189
|
+
event.stopImmediatePropagation();
|
|
190
|
+
if (onSelectionClick) {
|
|
191
|
+
onSelectionClick();
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
true
|
|
195
|
+
);
|
|
546
196
|
return {
|
|
547
197
|
element,
|
|
548
198
|
hide: () => {
|
|
549
199
|
visible = false;
|
|
200
|
+
hasBeenShown = false;
|
|
550
201
|
element.style.display = "none";
|
|
551
202
|
},
|
|
552
203
|
isVisible: () => visible,
|
|
@@ -555,7 +206,17 @@ var createSelectionOverlay = (root) => {
|
|
|
555
206
|
element.style.display = "block";
|
|
556
207
|
},
|
|
557
208
|
update: (selection) => {
|
|
558
|
-
|
|
209
|
+
if (!hasBeenShown) {
|
|
210
|
+
element.style.top = `${selection.y}px`;
|
|
211
|
+
element.style.left = `${selection.x}px`;
|
|
212
|
+
element.style.width = `${selection.width}px`;
|
|
213
|
+
element.style.height = `${selection.height}px`;
|
|
214
|
+
element.style.borderRadius = selection.borderRadius;
|
|
215
|
+
element.style.transform = selection.transform;
|
|
216
|
+
hasBeenShown = true;
|
|
217
|
+
} else {
|
|
218
|
+
updateSelectionElement(element, selection);
|
|
219
|
+
}
|
|
559
220
|
}
|
|
560
221
|
};
|
|
561
222
|
};
|
|
@@ -584,15 +245,18 @@ var createGrabbedOverlay = (root, selection) => {
|
|
|
584
245
|
}, 300);
|
|
585
246
|
};
|
|
586
247
|
var createSpinner = () => {
|
|
587
|
-
const spinner =
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
248
|
+
const spinner = html`
|
|
249
|
+
<span style="
|
|
250
|
+
display: inline-block;
|
|
251
|
+
width: 8px;
|
|
252
|
+
height: 8px;
|
|
253
|
+
border: 1.5px solid rgb(210, 57, 192);
|
|
254
|
+
border-top-color: transparent;
|
|
255
|
+
border-radius: 50%;
|
|
256
|
+
margin-right: 4px;
|
|
257
|
+
vertical-align: middle;
|
|
258
|
+
"></span>
|
|
259
|
+
`;
|
|
596
260
|
spinner.animate(
|
|
597
261
|
[{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }],
|
|
598
262
|
{
|
|
@@ -605,27 +269,30 @@ var createSpinner = () => {
|
|
|
605
269
|
};
|
|
606
270
|
var activeIndicator = null;
|
|
607
271
|
var createIndicator = () => {
|
|
608
|
-
const indicator =
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
272
|
+
const indicator = html`
|
|
273
|
+
<div style="
|
|
274
|
+
position: fixed;
|
|
275
|
+
top: calc(8px + env(safe-area-inset-top));
|
|
276
|
+
padding: 2px 6px;
|
|
277
|
+
background-color: #fde7f7;
|
|
278
|
+
color: #b21c8e;
|
|
279
|
+
border: 1px solid #f7c5ec;
|
|
280
|
+
border-radius: 4px;
|
|
281
|
+
font-size: 11px;
|
|
282
|
+
font-weight: 500;
|
|
283
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
284
|
+
z-index: 2147483647;
|
|
285
|
+
pointer-events: none;
|
|
286
|
+
opacity: 0;
|
|
287
|
+
transition: opacity 0.2s ease-in-out;
|
|
288
|
+
display: flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
max-width: calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));
|
|
291
|
+
overflow: hidden;
|
|
292
|
+
text-overflow: ellipsis;
|
|
293
|
+
white-space: nowrap;
|
|
294
|
+
"></div>
|
|
295
|
+
`;
|
|
629
296
|
return indicator;
|
|
630
297
|
};
|
|
631
298
|
var showLabel = (root, selectionLeftPx, selectionTopPx, tagName) => {
|
|
@@ -768,643 +435,496 @@ var cleanupGrabbedIndicators = () => {
|
|
|
768
435
|
}
|
|
769
436
|
activeGrabbedIndicators.clear();
|
|
770
437
|
};
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const progressBarFill = document.createElement("div");
|
|
787
|
-
progressBarFill.style.width = "0%";
|
|
788
|
-
progressBarFill.style.height = "100%";
|
|
789
|
-
progressBarFill.style.backgroundColor = "#b21c8e";
|
|
790
|
-
progressBarFill.style.borderRadius = "1px";
|
|
791
|
-
progressBarFill.style.transition = "width 0.05s linear";
|
|
792
|
-
progressBarFill.setAttribute("data-progress-fill", "true");
|
|
793
|
-
progressBarContainer.appendChild(progressBarFill);
|
|
794
|
-
container.appendChild(progressBarContainer);
|
|
795
|
-
return container;
|
|
438
|
+
bippy.instrument({
|
|
439
|
+
onCommitFiberRoot(_, fiberRoot) {
|
|
440
|
+
bippy._fiberRoots.add(fiberRoot);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
var getSourceTrace = async (element) => {
|
|
444
|
+
const fiber = bippy.getFiberFromHostInstance(element);
|
|
445
|
+
if (!fiber) return null;
|
|
446
|
+
const ownerStack = source.getOwnerStack(fiber);
|
|
447
|
+
const sources = await source.getSourcesFromStack(
|
|
448
|
+
ownerStack,
|
|
449
|
+
Number.MAX_SAFE_INTEGER
|
|
450
|
+
);
|
|
451
|
+
if (!sources) return null;
|
|
452
|
+
return sources;
|
|
796
453
|
};
|
|
797
|
-
var
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
454
|
+
var getHTMLSnippet = (element) => {
|
|
455
|
+
const semanticTags = /* @__PURE__ */ new Set([
|
|
456
|
+
"article",
|
|
457
|
+
"aside",
|
|
458
|
+
"footer",
|
|
459
|
+
"form",
|
|
460
|
+
"header",
|
|
461
|
+
"main",
|
|
462
|
+
"nav",
|
|
463
|
+
"section"
|
|
464
|
+
]);
|
|
465
|
+
const hasDistinguishingFeatures = (el) => {
|
|
466
|
+
const tagName = el.tagName.toLowerCase();
|
|
467
|
+
if (semanticTags.has(tagName)) return true;
|
|
468
|
+
if (el.id) return true;
|
|
469
|
+
if (el.className && typeof el.className === "string") {
|
|
470
|
+
const classes = el.className.trim();
|
|
471
|
+
if (classes && classes.length > 0) return true;
|
|
472
|
+
}
|
|
473
|
+
return Array.from(el.attributes).some(
|
|
474
|
+
(attr) => attr.name.startsWith("data-")
|
|
475
|
+
);
|
|
476
|
+
};
|
|
477
|
+
const getAncestorChain = (el, maxDepth = 10) => {
|
|
478
|
+
const ancestors2 = [];
|
|
479
|
+
let current = el.parentElement;
|
|
480
|
+
let depth = 0;
|
|
481
|
+
while (current && depth < maxDepth && current.tagName !== "BODY") {
|
|
482
|
+
if (hasDistinguishingFeatures(current)) {
|
|
483
|
+
ancestors2.push(current);
|
|
484
|
+
if (ancestors2.length >= 3) break;
|
|
485
|
+
}
|
|
486
|
+
current = current.parentElement;
|
|
487
|
+
depth++;
|
|
488
|
+
}
|
|
489
|
+
return ancestors2.reverse();
|
|
490
|
+
};
|
|
491
|
+
const getCSSPath = (el) => {
|
|
492
|
+
const parts = [];
|
|
493
|
+
let current = el;
|
|
494
|
+
let depth = 0;
|
|
495
|
+
const maxDepth = 5;
|
|
496
|
+
while (current && depth < maxDepth && current.tagName !== "BODY") {
|
|
497
|
+
let selector = current.tagName.toLowerCase();
|
|
498
|
+
if (current.id) {
|
|
499
|
+
selector += `#${current.id}`;
|
|
500
|
+
parts.unshift(selector);
|
|
501
|
+
break;
|
|
502
|
+
} else if (current.className && typeof current.className === "string" && current.className.trim()) {
|
|
503
|
+
const classes = current.className.trim().split(/\s+/).slice(0, 2);
|
|
504
|
+
selector += `.${classes.join(".")}`;
|
|
505
|
+
}
|
|
506
|
+
if (!current.id && (!current.className || !current.className.trim()) && current.parentElement) {
|
|
507
|
+
const siblings = Array.from(current.parentElement.children);
|
|
508
|
+
const index = siblings.indexOf(current);
|
|
509
|
+
if (index >= 0 && siblings.length > 1) {
|
|
510
|
+
selector += `:nth-child(${index + 1})`;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
parts.unshift(selector);
|
|
514
|
+
current = current.parentElement;
|
|
515
|
+
depth++;
|
|
516
|
+
}
|
|
517
|
+
return parts.join(" > ");
|
|
518
|
+
};
|
|
519
|
+
const getElementTag = (el, compact = false) => {
|
|
520
|
+
const tagName = el.tagName.toLowerCase();
|
|
521
|
+
const attrs = [];
|
|
522
|
+
if (el.id) {
|
|
523
|
+
attrs.push(`id="${el.id}"`);
|
|
524
|
+
}
|
|
525
|
+
if (el.className && typeof el.className === "string") {
|
|
526
|
+
const classes = el.className.trim().split(/\s+/);
|
|
527
|
+
if (classes.length > 0 && classes[0]) {
|
|
528
|
+
const displayClasses = compact ? classes.slice(0, 3) : classes;
|
|
529
|
+
let classStr = displayClasses.join(" ");
|
|
530
|
+
if (classStr.length > 30) {
|
|
531
|
+
classStr = classStr.substring(0, 30) + "...";
|
|
532
|
+
}
|
|
533
|
+
attrs.push(`class="${classStr}"`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
const dataAttrs = Array.from(el.attributes).filter(
|
|
537
|
+
(attr) => attr.name.startsWith("data-")
|
|
538
|
+
);
|
|
539
|
+
const displayDataAttrs = compact ? dataAttrs.slice(0, 1) : dataAttrs;
|
|
540
|
+
for (const attr of displayDataAttrs) {
|
|
541
|
+
let value = attr.value;
|
|
542
|
+
if (value.length > 20) {
|
|
543
|
+
value = value.substring(0, 20) + "...";
|
|
544
|
+
}
|
|
545
|
+
attrs.push(`${attr.name}="${value}"`);
|
|
546
|
+
}
|
|
547
|
+
const ariaLabel = el.getAttribute("aria-label");
|
|
548
|
+
if (ariaLabel && !compact) {
|
|
549
|
+
let value = ariaLabel;
|
|
550
|
+
if (value.length > 20) {
|
|
551
|
+
value = value.substring(0, 20) + "...";
|
|
552
|
+
}
|
|
553
|
+
attrs.push(`aria-label="${value}"`);
|
|
554
|
+
}
|
|
555
|
+
return attrs.length > 0 ? `<${tagName} ${attrs.join(" ")}>` : `<${tagName}>`;
|
|
556
|
+
};
|
|
557
|
+
const getClosingTag = (el) => {
|
|
558
|
+
return `</${el.tagName.toLowerCase()}>`;
|
|
559
|
+
};
|
|
560
|
+
const getTextContent = (el) => {
|
|
561
|
+
let text = el.textContent || "";
|
|
562
|
+
text = text.trim().replace(/\s+/g, " ");
|
|
563
|
+
const maxLength = 60;
|
|
564
|
+
if (text.length > maxLength) {
|
|
565
|
+
text = text.substring(0, maxLength) + "...";
|
|
566
|
+
}
|
|
567
|
+
return text;
|
|
568
|
+
};
|
|
569
|
+
const getSiblingIdentifier = (el) => {
|
|
570
|
+
if (el.id) return `#${el.id}`;
|
|
571
|
+
if (el.className && typeof el.className === "string") {
|
|
572
|
+
const classes = el.className.trim().split(/\s+/);
|
|
573
|
+
if (classes.length > 0 && classes[0]) {
|
|
574
|
+
return `.${classes[0]}`;
|
|
804
575
|
}
|
|
805
|
-
}
|
|
576
|
+
}
|
|
577
|
+
return null;
|
|
578
|
+
};
|
|
579
|
+
const lines = [];
|
|
580
|
+
lines.push(`Path: ${getCSSPath(element)}`);
|
|
581
|
+
lines.push("");
|
|
582
|
+
const ancestors = getAncestorChain(element);
|
|
583
|
+
for (let i = 0; i < ancestors.length; i++) {
|
|
584
|
+
const indent2 = " ".repeat(i);
|
|
585
|
+
lines.push(indent2 + getElementTag(ancestors[i], true));
|
|
806
586
|
}
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
587
|
+
const parent = element.parentElement;
|
|
588
|
+
let targetIndex = -1;
|
|
589
|
+
if (parent) {
|
|
590
|
+
const siblings = Array.from(parent.children);
|
|
591
|
+
targetIndex = siblings.indexOf(element);
|
|
592
|
+
if (targetIndex > 0) {
|
|
593
|
+
const prevSibling = siblings[targetIndex - 1];
|
|
594
|
+
const prevId = getSiblingIdentifier(prevSibling);
|
|
595
|
+
if (prevId && targetIndex <= 2) {
|
|
596
|
+
const indent2 = " ".repeat(ancestors.length);
|
|
597
|
+
lines.push(`${indent2} ${getElementTag(prevSibling, true)}`);
|
|
598
|
+
lines.push(`${indent2} </${prevSibling.tagName.toLowerCase()}>`);
|
|
599
|
+
} else if (targetIndex > 0) {
|
|
600
|
+
const indent2 = " ".repeat(ancestors.length);
|
|
601
|
+
lines.push(
|
|
602
|
+
`${indent2} ... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
817
606
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
"
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
607
|
+
const indent = " ".repeat(ancestors.length);
|
|
608
|
+
lines.push(indent + " <!-- SELECTED -->");
|
|
609
|
+
const textContent = getTextContent(element);
|
|
610
|
+
const childrenCount = element.children.length;
|
|
611
|
+
if (textContent && childrenCount === 0 && textContent.length < 40) {
|
|
612
|
+
lines.push(
|
|
613
|
+
`${indent} ${getElementTag(element)}${textContent}${getClosingTag(
|
|
614
|
+
element
|
|
615
|
+
)}`
|
|
616
|
+
);
|
|
617
|
+
} else {
|
|
618
|
+
lines.push(indent + " " + getElementTag(element));
|
|
619
|
+
if (textContent) {
|
|
620
|
+
lines.push(`${indent} ${textContent}`);
|
|
621
|
+
}
|
|
622
|
+
if (childrenCount > 0) {
|
|
623
|
+
lines.push(
|
|
624
|
+
`${indent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
lines.push(indent + " " + getClosingTag(element));
|
|
834
628
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
629
|
+
if (parent && targetIndex >= 0) {
|
|
630
|
+
const siblings = Array.from(parent.children);
|
|
631
|
+
const siblingsAfter = siblings.length - targetIndex - 1;
|
|
632
|
+
if (siblingsAfter > 0) {
|
|
633
|
+
const nextSibling = siblings[targetIndex + 1];
|
|
634
|
+
const nextId = getSiblingIdentifier(nextSibling);
|
|
635
|
+
if (nextId && siblingsAfter <= 2) {
|
|
636
|
+
lines.push(`${indent} ${getElementTag(nextSibling, true)}`);
|
|
637
|
+
lines.push(`${indent} </${nextSibling.tagName.toLowerCase()}>`);
|
|
638
|
+
} else {
|
|
639
|
+
lines.push(
|
|
640
|
+
`${indent} ... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
|
|
641
|
+
);
|
|
843
642
|
}
|
|
844
|
-
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
646
|
+
const indent2 = " ".repeat(i);
|
|
647
|
+
lines.push(indent2 + getClosingTag(ancestors[i]));
|
|
845
648
|
}
|
|
649
|
+
return lines.join("\n");
|
|
846
650
|
};
|
|
847
651
|
|
|
848
|
-
// src/utils/copy-
|
|
849
|
-
var
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
652
|
+
// src/utils/copy-content.ts
|
|
653
|
+
var copyContent = async (content) => {
|
|
654
|
+
try {
|
|
655
|
+
if (Array.isArray(content)) {
|
|
656
|
+
if (!navigator?.clipboard?.write) {
|
|
657
|
+
for (const contentPart of content) {
|
|
658
|
+
if (typeof contentPart === "string") {
|
|
659
|
+
const result = copyContentFallback(contentPart);
|
|
660
|
+
if (!result) return result;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return true;
|
|
664
|
+
}
|
|
665
|
+
await navigator.clipboard.write([
|
|
666
|
+
new ClipboardItem(
|
|
667
|
+
Object.fromEntries(
|
|
668
|
+
content.map((contentPart) => {
|
|
669
|
+
if (contentPart instanceof Blob) {
|
|
670
|
+
return [contentPart.type ?? "text/plain", contentPart];
|
|
671
|
+
} else {
|
|
672
|
+
return [
|
|
673
|
+
"text/plain",
|
|
674
|
+
new Blob([contentPart], { type: "text/plain" })
|
|
675
|
+
];
|
|
676
|
+
}
|
|
677
|
+
})
|
|
678
|
+
)
|
|
679
|
+
)
|
|
680
|
+
]);
|
|
854
681
|
return true;
|
|
855
|
-
}
|
|
682
|
+
} else if (content instanceof Blob) {
|
|
683
|
+
await navigator.clipboard.write([
|
|
684
|
+
new ClipboardItem({ [content.type]: content })
|
|
685
|
+
]);
|
|
686
|
+
return true;
|
|
687
|
+
} else {
|
|
688
|
+
try {
|
|
689
|
+
await navigator.clipboard.writeText(String(content));
|
|
690
|
+
return true;
|
|
691
|
+
} catch {
|
|
692
|
+
return copyContentFallback(content);
|
|
693
|
+
}
|
|
856
694
|
}
|
|
695
|
+
} catch {
|
|
696
|
+
return false;
|
|
857
697
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
698
|
+
};
|
|
699
|
+
var copyContentFallback = (content) => {
|
|
700
|
+
if (!document.execCommand) return false;
|
|
701
|
+
const el = document.createElement("textarea");
|
|
702
|
+
el.value = String(content);
|
|
703
|
+
el.style.clipPath = "inset(50%)";
|
|
704
|
+
el.ariaHidden = "true";
|
|
865
705
|
const doc = document.body || document.documentElement;
|
|
866
|
-
doc.
|
|
867
|
-
textareaElement.select();
|
|
868
|
-
textareaElement.setSelectionRange(0, textareaElement.value.length);
|
|
869
|
-
let didCopyToClipboard = false;
|
|
706
|
+
doc.append(el);
|
|
870
707
|
try {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
didCopyToClipboard = false;
|
|
708
|
+
el.select();
|
|
709
|
+
return document.execCommand("copy");
|
|
874
710
|
} finally {
|
|
875
|
-
|
|
876
|
-
}
|
|
877
|
-
return didCopyToClipboard;
|
|
878
|
-
};
|
|
879
|
-
|
|
880
|
-
// src/utils/is-element-visible.ts
|
|
881
|
-
var isElementVisible = (element, computedStyle = window.getComputedStyle(element)) => {
|
|
882
|
-
return computedStyle.display !== "none" && computedStyle.visibility !== "hidden" && computedStyle.opacity !== "0";
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
// src/utils/mount-root.ts
|
|
886
|
-
var ATTRIBUTE_NAME = "data-react-grab";
|
|
887
|
-
var mountRoot = () => {
|
|
888
|
-
const mountedHost = document.querySelector(`[${ATTRIBUTE_NAME}]`);
|
|
889
|
-
if (mountedHost) {
|
|
890
|
-
const mountedRoot = mountedHost.shadowRoot?.querySelector(
|
|
891
|
-
`[${ATTRIBUTE_NAME}]`
|
|
892
|
-
);
|
|
893
|
-
if (mountedRoot instanceof HTMLDivElement && mountedHost.shadowRoot) {
|
|
894
|
-
return mountedRoot;
|
|
895
|
-
}
|
|
711
|
+
el.remove();
|
|
896
712
|
}
|
|
897
|
-
const host = document.createElement("div");
|
|
898
|
-
host.setAttribute(ATTRIBUTE_NAME, "true");
|
|
899
|
-
host.style.zIndex = "2147483646";
|
|
900
|
-
host.style.position = "fixed";
|
|
901
|
-
host.style.top = "0";
|
|
902
|
-
host.style.left = "0";
|
|
903
|
-
const shadowRoot = host.attachShadow({ mode: "open" });
|
|
904
|
-
const root = document.createElement("div");
|
|
905
|
-
root.setAttribute(ATTRIBUTE_NAME, "true");
|
|
906
|
-
shadowRoot.appendChild(root);
|
|
907
|
-
const doc = document.body ?? document.documentElement;
|
|
908
|
-
doc.appendChild(host);
|
|
909
|
-
return root;
|
|
910
713
|
};
|
|
911
714
|
|
|
912
|
-
// src/
|
|
913
|
-
var
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
const prevState = currentState;
|
|
919
|
-
const resolvedState = typeof maybeStateOrReducer === "function" ? maybeStateOrReducer(prevState) : maybeStateOrReducer;
|
|
920
|
-
const nextState = {
|
|
921
|
-
...prevState,
|
|
922
|
-
...resolvedState
|
|
923
|
-
};
|
|
924
|
-
currentState = nextState;
|
|
925
|
-
for (const entry of subscriberMap.values()) {
|
|
926
|
-
if (entry.type === "selected" /* Selected */) {
|
|
927
|
-
const nextSelectedValue = entry.selector(nextState);
|
|
928
|
-
const prevSelectedValue = entry.prevSelectedValue;
|
|
929
|
-
if (!Object.is(prevSelectedValue, nextSelectedValue)) {
|
|
930
|
-
entry.prevSelectedValue = nextSelectedValue;
|
|
931
|
-
entry.listener(nextSelectedValue, prevSelectedValue);
|
|
932
|
-
}
|
|
933
|
-
} else {
|
|
934
|
-
entry.listener(nextState, prevState);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
return currentState;
|
|
938
|
-
};
|
|
939
|
-
const getState = () => {
|
|
940
|
-
return currentState;
|
|
941
|
-
};
|
|
942
|
-
const initialState = initializer(setState, getState);
|
|
943
|
-
currentState = initialState;
|
|
944
|
-
const subscribeWithSelector = (listener, selector) => {
|
|
945
|
-
const index = String(currentListenerIndex++);
|
|
946
|
-
const wrappedListener = (value, prevValue) => listener(value, prevValue);
|
|
947
|
-
const entry = {
|
|
948
|
-
listener: wrappedListener,
|
|
949
|
-
prevSelectedValue: selector(currentState),
|
|
950
|
-
selector,
|
|
951
|
-
type: "selected" /* Selected */
|
|
952
|
-
};
|
|
953
|
-
subscriberMap.set(index, entry);
|
|
954
|
-
return () => {
|
|
955
|
-
subscriberMap.delete(index);
|
|
956
|
-
};
|
|
957
|
-
};
|
|
958
|
-
const subscribeToFullState = (listener) => {
|
|
959
|
-
const index = String(currentListenerIndex++);
|
|
960
|
-
const entry = {
|
|
961
|
-
listener,
|
|
962
|
-
type: "full" /* Full */
|
|
963
|
-
};
|
|
964
|
-
subscriberMap.set(index, entry);
|
|
965
|
-
return () => {
|
|
966
|
-
subscriberMap.delete(index);
|
|
967
|
-
};
|
|
968
|
-
};
|
|
969
|
-
function subscribe(subscriber, selector) {
|
|
970
|
-
return selector ? subscribeWithSelector(subscriber, selector) : subscribeToFullState(subscriber);
|
|
971
|
-
}
|
|
972
|
-
const store = {
|
|
973
|
-
getInitialState() {
|
|
974
|
-
return initialState;
|
|
975
|
-
},
|
|
976
|
-
getState,
|
|
977
|
-
setState,
|
|
978
|
-
subscribe
|
|
715
|
+
// src/core.ts
|
|
716
|
+
var init = (rawOptions) => {
|
|
717
|
+
const options = {
|
|
718
|
+
enabled: true,
|
|
719
|
+
keyHoldDuration: 500,
|
|
720
|
+
...rawOptions
|
|
979
721
|
};
|
|
980
|
-
return store;
|
|
981
|
-
};
|
|
982
|
-
|
|
983
|
-
// src/index.ts
|
|
984
|
-
var libStore = createStore(() => ({
|
|
985
|
-
keyPressTimestamps: /* @__PURE__ */ new Map(),
|
|
986
|
-
mouseX: -1e3,
|
|
987
|
-
mouseY: -1e3,
|
|
988
|
-
overlayMode: "hidden",
|
|
989
|
-
pressedKeys: /* @__PURE__ */ new Set()
|
|
990
|
-
}));
|
|
991
|
-
var getDefaultHotkey = () => {
|
|
992
|
-
if (typeof navigator === "undefined") {
|
|
993
|
-
return ["Meta", "C"];
|
|
994
|
-
}
|
|
995
|
-
const isMac = navigator.platform.toLowerCase().includes("mac");
|
|
996
|
-
return isMac ? ["Meta", "C"] : ["Control", "C"];
|
|
997
|
-
};
|
|
998
|
-
var init = (options = {}) => {
|
|
999
722
|
if (options.enabled === false) {
|
|
1000
723
|
return;
|
|
1001
724
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
const root = mountRoot();
|
|
1010
|
-
const selectionOverlay = createSelectionOverlay(root);
|
|
725
|
+
let holdTimer = null;
|
|
726
|
+
let isHoldingKeys = false;
|
|
727
|
+
let overlayRoot = null;
|
|
728
|
+
let selectionOverlay = null;
|
|
729
|
+
let renderFrameId = null;
|
|
730
|
+
let isActive = false;
|
|
731
|
+
let isCopying = false;
|
|
1011
732
|
let hoveredElement = null;
|
|
1012
733
|
let lastGrabbedElement = null;
|
|
1013
|
-
let
|
|
1014
|
-
let
|
|
1015
|
-
|
|
1016
|
-
const checkIsActivationHotkeyPressed = () => {
|
|
1017
|
-
if (Array.isArray(resolvedOptions.hotkey)) {
|
|
1018
|
-
for (const key of resolvedOptions.hotkey) {
|
|
1019
|
-
if (!isKeyPressed(key)) {
|
|
1020
|
-
return false;
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
return true;
|
|
1024
|
-
}
|
|
1025
|
-
return isKeyPressed(resolvedOptions.hotkey);
|
|
1026
|
-
};
|
|
1027
|
-
const updateProgressIndicator = () => {
|
|
1028
|
-
if (progressStartTime === null) return;
|
|
1029
|
-
const elapsed = Date.now() - progressStartTime;
|
|
1030
|
-
const progress = Math.min(1, elapsed / resolvedOptions.keyHoldDuration);
|
|
1031
|
-
const { mouseX, mouseY } = libStore.getState();
|
|
1032
|
-
showProgressIndicator(root, progress, mouseX, mouseY);
|
|
1033
|
-
if (progress < 1) {
|
|
1034
|
-
progressAnimationFrame = requestAnimationFrame(updateProgressIndicator);
|
|
1035
|
-
}
|
|
1036
|
-
};
|
|
1037
|
-
const startProgressTracking = () => {
|
|
1038
|
-
if (progressAnimationFrame !== null) return;
|
|
1039
|
-
progressStartTime = Date.now();
|
|
1040
|
-
const { mouseX, mouseY } = libStore.getState();
|
|
1041
|
-
showProgressIndicator(root, 0, mouseX, mouseY);
|
|
1042
|
-
progressAnimationFrame = requestAnimationFrame(updateProgressIndicator);
|
|
1043
|
-
};
|
|
1044
|
-
const stopProgressTracking = () => {
|
|
1045
|
-
if (progressAnimationFrame !== null) {
|
|
1046
|
-
cancelAnimationFrame(progressAnimationFrame);
|
|
1047
|
-
progressAnimationFrame = null;
|
|
1048
|
-
}
|
|
1049
|
-
progressStartTime = null;
|
|
1050
|
-
hideProgressIndicator();
|
|
1051
|
-
};
|
|
1052
|
-
let cleanupActivationHotkeyWatcher = null;
|
|
1053
|
-
const handleKeyStateChange = (pressedKeys) => {
|
|
1054
|
-
const { overlayMode } = libStore.getState();
|
|
1055
|
-
if (pressedKeys.has("Escape") || pressedKeys.has("Esc")) {
|
|
1056
|
-
libStore.setState((state) => {
|
|
1057
|
-
const nextPressedKeys = new Set(state.pressedKeys);
|
|
1058
|
-
nextPressedKeys.delete("Escape");
|
|
1059
|
-
nextPressedKeys.delete("Esc");
|
|
1060
|
-
const nextTimestamps = new Map(state.keyPressTimestamps);
|
|
1061
|
-
nextTimestamps.delete("Escape");
|
|
1062
|
-
nextTimestamps.delete("Esc");
|
|
1063
|
-
const activationKeys = Array.isArray(resolvedOptions.hotkey) ? resolvedOptions.hotkey : [resolvedOptions.hotkey];
|
|
1064
|
-
for (const activationKey of activationKeys) {
|
|
1065
|
-
if (activationKey.length === 1) {
|
|
1066
|
-
nextPressedKeys.delete(activationKey.toLowerCase());
|
|
1067
|
-
nextPressedKeys.delete(activationKey.toUpperCase());
|
|
1068
|
-
nextTimestamps.delete(activationKey.toLowerCase());
|
|
1069
|
-
nextTimestamps.delete(activationKey.toUpperCase());
|
|
1070
|
-
} else {
|
|
1071
|
-
nextPressedKeys.delete(activationKey);
|
|
1072
|
-
nextTimestamps.delete(activationKey);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
return {
|
|
1076
|
-
...state,
|
|
1077
|
-
keyPressTimestamps: nextTimestamps,
|
|
1078
|
-
overlayMode: "hidden",
|
|
1079
|
-
pressedKeys: nextPressedKeys
|
|
1080
|
-
};
|
|
1081
|
-
});
|
|
1082
|
-
if (cleanupActivationHotkeyWatcher) {
|
|
1083
|
-
cleanupActivationHotkeyWatcher();
|
|
1084
|
-
cleanupActivationHotkeyWatcher = null;
|
|
1085
|
-
}
|
|
1086
|
-
stopProgressTracking();
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
const isActivationHotkeyPressed = checkIsActivationHotkeyPressed();
|
|
1090
|
-
if (!isActivationHotkeyPressed) {
|
|
1091
|
-
if (cleanupActivationHotkeyWatcher) {
|
|
1092
|
-
cleanupActivationHotkeyWatcher();
|
|
1093
|
-
cleanupActivationHotkeyWatcher = null;
|
|
1094
|
-
}
|
|
1095
|
-
if (overlayMode !== "hidden") {
|
|
1096
|
-
libStore.setState((state) => ({
|
|
1097
|
-
...state,
|
|
1098
|
-
overlayMode: "hidden"
|
|
1099
|
-
}));
|
|
1100
|
-
}
|
|
1101
|
-
stopProgressTracking();
|
|
1102
|
-
return;
|
|
1103
|
-
}
|
|
1104
|
-
if (overlayMode === "hidden" && !cleanupActivationHotkeyWatcher) {
|
|
1105
|
-
startProgressTracking();
|
|
1106
|
-
cleanupActivationHotkeyWatcher = watchKeyHeldFor(
|
|
1107
|
-
resolvedOptions.hotkey,
|
|
1108
|
-
resolvedOptions.keyHoldDuration,
|
|
1109
|
-
() => {
|
|
1110
|
-
libStore.setState((state) => ({
|
|
1111
|
-
...state,
|
|
1112
|
-
overlayMode: "visible"
|
|
1113
|
-
}));
|
|
1114
|
-
stopProgressTracking();
|
|
1115
|
-
cleanupActivationHotkeyWatcher = null;
|
|
1116
|
-
}
|
|
1117
|
-
);
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
const cleanupKeyStateChangeSubscription = libStore.subscribe(
|
|
1121
|
-
handleKeyStateChange,
|
|
1122
|
-
(state) => state.pressedKeys
|
|
1123
|
-
);
|
|
1124
|
-
let mouseMoveScheduled = false;
|
|
1125
|
-
let pendingMouseX = -1e3;
|
|
1126
|
-
let pendingMouseY = -1e3;
|
|
1127
|
-
const handleMouseMove = (event) => {
|
|
1128
|
-
pendingMouseX = event.clientX;
|
|
1129
|
-
pendingMouseY = event.clientY;
|
|
1130
|
-
if (mouseMoveScheduled) return;
|
|
1131
|
-
mouseMoveScheduled = true;
|
|
1132
|
-
requestAnimationFrame(() => {
|
|
1133
|
-
mouseMoveScheduled = false;
|
|
1134
|
-
libStore.setState((state) => ({
|
|
1135
|
-
...state,
|
|
1136
|
-
mouseX: pendingMouseX,
|
|
1137
|
-
mouseY: pendingMouseY
|
|
1138
|
-
}));
|
|
1139
|
-
});
|
|
1140
|
-
};
|
|
1141
|
-
const handleMouseDown = (event) => {
|
|
1142
|
-
if (event.button !== 0) {
|
|
1143
|
-
return;
|
|
1144
|
-
}
|
|
1145
|
-
const { overlayMode } = libStore.getState();
|
|
1146
|
-
if (overlayMode === "hidden") {
|
|
1147
|
-
return;
|
|
1148
|
-
}
|
|
1149
|
-
event.preventDefault();
|
|
1150
|
-
event.stopPropagation();
|
|
1151
|
-
event.stopImmediatePropagation();
|
|
1152
|
-
libStore.setState((state) => ({
|
|
1153
|
-
...state,
|
|
1154
|
-
overlayMode: "copying"
|
|
1155
|
-
}));
|
|
1156
|
-
};
|
|
1157
|
-
const handleClick = (event) => {
|
|
1158
|
-
const { overlayMode } = libStore.getState();
|
|
1159
|
-
if (overlayMode === "hidden") {
|
|
1160
|
-
return;
|
|
1161
|
-
}
|
|
1162
|
-
event.preventDefault();
|
|
1163
|
-
event.stopPropagation();
|
|
1164
|
-
event.stopImmediatePropagation();
|
|
1165
|
-
};
|
|
1166
|
-
const handleVisibilityChange = () => {
|
|
1167
|
-
if (document.hidden) {
|
|
1168
|
-
cleanupGrabbedIndicators();
|
|
1169
|
-
hideLabel();
|
|
1170
|
-
}
|
|
1171
|
-
};
|
|
1172
|
-
let scrollScheduled = false;
|
|
1173
|
-
const handleScroll = () => {
|
|
1174
|
-
if (scrollScheduled) return;
|
|
1175
|
-
scrollScheduled = true;
|
|
1176
|
-
requestAnimationFrame(() => {
|
|
1177
|
-
scrollScheduled = false;
|
|
1178
|
-
scheduleRender();
|
|
1179
|
-
});
|
|
1180
|
-
};
|
|
1181
|
-
let resizeScheduled = false;
|
|
1182
|
-
const handleResize = () => {
|
|
1183
|
-
if (resizeScheduled) return;
|
|
1184
|
-
resizeScheduled = true;
|
|
1185
|
-
requestAnimationFrame(() => {
|
|
1186
|
-
resizeScheduled = false;
|
|
1187
|
-
scheduleRender();
|
|
1188
|
-
});
|
|
1189
|
-
};
|
|
1190
|
-
window.addEventListener("mousemove", handleMouseMove);
|
|
1191
|
-
window.addEventListener("mousedown", handleMouseDown, true);
|
|
1192
|
-
window.addEventListener("click", handleClick, true);
|
|
1193
|
-
window.addEventListener("scroll", handleScroll, true);
|
|
1194
|
-
window.addEventListener("resize", handleResize);
|
|
1195
|
-
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1196
|
-
const cleanupTrackHotkeys = trackHotkeys();
|
|
734
|
+
let mouseX = -1e3;
|
|
735
|
+
let mouseY = -1e3;
|
|
736
|
+
const isTargetKeyCombination = (event) => (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "c";
|
|
1197
737
|
const getElementAtPosition = (x, y) => {
|
|
1198
|
-
const
|
|
1199
|
-
for (const
|
|
1200
|
-
if (
|
|
738
|
+
const elementsAtPoint = document.elementsFromPoint(x, y);
|
|
739
|
+
for (const candidateElement of elementsAtPoint) {
|
|
740
|
+
if (candidateElement.closest(`[${ATTRIBUTE_NAME}]`)) {
|
|
1201
741
|
continue;
|
|
1202
742
|
}
|
|
1203
|
-
const computedStyle = window.getComputedStyle(
|
|
1204
|
-
if (!isElementVisible(
|
|
743
|
+
const computedStyle = window.getComputedStyle(candidateElement);
|
|
744
|
+
if (!isElementVisible(candidateElement, computedStyle)) {
|
|
1205
745
|
continue;
|
|
1206
746
|
}
|
|
1207
|
-
return
|
|
747
|
+
return candidateElement;
|
|
1208
748
|
}
|
|
1209
749
|
return null;
|
|
1210
750
|
};
|
|
1211
|
-
const
|
|
1212
|
-
const tagName = (element.tagName || "").toLowerCase();
|
|
1213
|
-
const rect = element.getBoundingClientRect();
|
|
1214
|
-
const cleanupIndicator = updateLabelToProcessing(root, rect.left, rect.top);
|
|
1215
|
-
try {
|
|
1216
|
-
const htmlSnippet = getHTMLSnippet(element);
|
|
1217
|
-
await copyTextToClipboard(
|
|
1218
|
-
`
|
|
751
|
+
const wrapInReferencedElement = (content) => `
|
|
1219
752
|
|
|
1220
753
|
<referenced_element>
|
|
1221
|
-
${
|
|
1222
|
-
</referenced_element
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
754
|
+
${content}
|
|
755
|
+
</referenced_element>`;
|
|
756
|
+
const handleCopy = async (targetElement) => {
|
|
757
|
+
const tagName = (targetElement.tagName || "").toLowerCase();
|
|
758
|
+
const elementBounds = targetElement.getBoundingClientRect();
|
|
759
|
+
const showSuccessIndicator = updateLabelToProcessing(
|
|
760
|
+
overlayRoot,
|
|
761
|
+
elementBounds.left,
|
|
762
|
+
elementBounds.top
|
|
763
|
+
);
|
|
764
|
+
try {
|
|
765
|
+
const elementHtml = getHTMLSnippet(targetElement);
|
|
766
|
+
await copyContent(wrapInReferencedElement(elementHtml));
|
|
767
|
+
const componentStackTrace = await getSourceTrace(targetElement);
|
|
768
|
+
if (componentStackTrace?.length) {
|
|
769
|
+
const formattedStackTrace = componentStackTrace.map(
|
|
770
|
+
(source) => ` ${source.functionName} - ${source.fileName}:${source.lineNumber}:${source.columnNumber}`
|
|
771
|
+
).join("\n");
|
|
772
|
+
await copyContent(
|
|
773
|
+
wrapInReferencedElement(
|
|
774
|
+
`${elementHtml}
|
|
1230
775
|
|
|
1231
776
|
Component owner stack:
|
|
1232
|
-
${
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
<referenced_element>
|
|
1237
|
-
${fullText}
|
|
1238
|
-
</referenced_element>`
|
|
1239
|
-
).catch(() => {
|
|
1240
|
-
});
|
|
1241
|
-
if (resolvedOptions.adapter) {
|
|
1242
|
-
resolvedOptions.adapter.open(fullText);
|
|
1243
|
-
}
|
|
1244
|
-
} else if (resolvedOptions.adapter) {
|
|
1245
|
-
resolvedOptions.adapter.open(htmlSnippet);
|
|
1246
|
-
}
|
|
1247
|
-
} else if (resolvedOptions.adapter) {
|
|
1248
|
-
resolvedOptions.adapter.open(htmlSnippet);
|
|
777
|
+
${formattedStackTrace}`
|
|
778
|
+
)
|
|
779
|
+
);
|
|
1249
780
|
}
|
|
1250
|
-
|
|
781
|
+
showSuccessIndicator(tagName);
|
|
1251
782
|
} catch {
|
|
1252
|
-
|
|
783
|
+
showSuccessIndicator(tagName);
|
|
1253
784
|
}
|
|
1254
785
|
};
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
if (
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
hideLabel();
|
|
1263
|
-
}
|
|
786
|
+
const hideOverlayAndLabel = () => {
|
|
787
|
+
selectionOverlay?.hide();
|
|
788
|
+
if (!isCopying) hideLabel();
|
|
789
|
+
};
|
|
790
|
+
const handleRender = () => {
|
|
791
|
+
if (!isActive) {
|
|
792
|
+
hideOverlayAndLabel();
|
|
1264
793
|
hoveredElement = null;
|
|
1265
794
|
lastGrabbedElement = null;
|
|
1266
795
|
return;
|
|
1267
796
|
}
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
const computedStyle2 = window.getComputedStyle(hoveredElement);
|
|
1273
|
-
const rect2 = hoveredElement.getBoundingClientRect();
|
|
1274
|
-
createGrabbedOverlay(root, {
|
|
1275
|
-
borderRadius: computedStyle2.borderRadius || "0px",
|
|
1276
|
-
height: rect2.height,
|
|
1277
|
-
transform: computedStyle2.transform || "none",
|
|
1278
|
-
width: rect2.width,
|
|
1279
|
-
x: rect2.left,
|
|
1280
|
-
y: rect2.top
|
|
1281
|
-
});
|
|
1282
|
-
void handleCopy(hoveredElement).finally(() => {
|
|
1283
|
-
isCopying = false;
|
|
1284
|
-
});
|
|
1285
|
-
const isStillPressed = checkIsActivationHotkeyPressed();
|
|
1286
|
-
libStore.setState((state2) => ({
|
|
1287
|
-
...state2,
|
|
1288
|
-
overlayMode: isStillPressed ? "visible" : "hidden"
|
|
1289
|
-
}));
|
|
1290
|
-
}
|
|
1291
|
-
return;
|
|
1292
|
-
}
|
|
1293
|
-
const element = getElementAtPosition(mouseX, mouseY);
|
|
1294
|
-
if (!element) {
|
|
1295
|
-
if (selectionOverlay.isVisible()) {
|
|
1296
|
-
selectionOverlay.hide();
|
|
1297
|
-
}
|
|
1298
|
-
if (!isCopying) {
|
|
1299
|
-
hideLabel();
|
|
1300
|
-
}
|
|
797
|
+
if (isCopying) return;
|
|
798
|
+
const targetElement = getElementAtPosition(mouseX, mouseY);
|
|
799
|
+
if (!targetElement) {
|
|
800
|
+
hideOverlayAndLabel();
|
|
1301
801
|
hoveredElement = null;
|
|
1302
802
|
return;
|
|
1303
803
|
}
|
|
1304
|
-
if (lastGrabbedElement
|
|
1305
|
-
lastGrabbedElement
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
selectionOverlay.update({
|
|
1324
|
-
borderRadius,
|
|
1325
|
-
height: rect.height,
|
|
1326
|
-
transform,
|
|
1327
|
-
width: rect.width,
|
|
1328
|
-
x: rect.left,
|
|
1329
|
-
y: rect.top
|
|
804
|
+
if (lastGrabbedElement) {
|
|
805
|
+
if (targetElement !== lastGrabbedElement) {
|
|
806
|
+
lastGrabbedElement = null;
|
|
807
|
+
} else {
|
|
808
|
+
hideOverlayAndLabel();
|
|
809
|
+
hoveredElement = targetElement;
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
hoveredElement = targetElement;
|
|
814
|
+
const elementBounds = targetElement.getBoundingClientRect();
|
|
815
|
+
const computedStyle = window.getComputedStyle(targetElement);
|
|
816
|
+
selectionOverlay?.update({
|
|
817
|
+
borderRadius: computedStyle.borderRadius || "0px",
|
|
818
|
+
height: elementBounds.height,
|
|
819
|
+
transform: computedStyle.transform || "none",
|
|
820
|
+
width: elementBounds.width,
|
|
821
|
+
x: elementBounds.left,
|
|
822
|
+
y: elementBounds.top
|
|
1330
823
|
});
|
|
1331
|
-
if (!selectionOverlay
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
Boolean(element.disabled) || computedStyle.pointerEvents === "none"
|
|
824
|
+
if (!selectionOverlay?.isVisible()) selectionOverlay?.show();
|
|
825
|
+
showLabel(
|
|
826
|
+
overlayRoot,
|
|
827
|
+
elementBounds.left,
|
|
828
|
+
elementBounds.top,
|
|
829
|
+
(targetElement.tagName || "").toLowerCase()
|
|
1338
830
|
);
|
|
1339
|
-
if (isDisabled) {
|
|
1340
|
-
const overlayElement = selectionOverlay.element;
|
|
1341
|
-
if (overlayElement) {
|
|
1342
|
-
overlayElement.style.pointerEvents = "auto";
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
831
|
};
|
|
1346
|
-
let renderScheduled = false;
|
|
1347
832
|
const scheduleRender = () => {
|
|
1348
|
-
if (
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
handleRender(libStore.getState());
|
|
833
|
+
if (renderFrameId !== null) return;
|
|
834
|
+
renderFrameId = requestAnimationFrame(() => {
|
|
835
|
+
renderFrameId = null;
|
|
836
|
+
handleRender();
|
|
1353
837
|
});
|
|
1354
838
|
};
|
|
1355
|
-
const cleanupRenderSubscription = libStore.subscribe(() => {
|
|
1356
|
-
scheduleRender();
|
|
1357
|
-
});
|
|
1358
839
|
const continuousRender = () => {
|
|
1359
840
|
scheduleRender();
|
|
1360
841
|
requestAnimationFrame(continuousRender);
|
|
1361
842
|
};
|
|
1362
|
-
|
|
843
|
+
const handleMouseMove = (event) => {
|
|
844
|
+
mouseX = event.clientX;
|
|
845
|
+
mouseY = event.clientY;
|
|
846
|
+
scheduleRender();
|
|
847
|
+
};
|
|
848
|
+
const handleVisibilityChange = () => {
|
|
849
|
+
if (document.hidden) {
|
|
850
|
+
cleanupGrabbedIndicators();
|
|
851
|
+
hideLabel();
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
const handleSelectionClick = () => {
|
|
855
|
+
if (!hoveredElement || isCopying) return;
|
|
856
|
+
isCopying = true;
|
|
857
|
+
lastGrabbedElement = hoveredElement;
|
|
858
|
+
const targetElement = hoveredElement;
|
|
859
|
+
const computedStyle = window.getComputedStyle(targetElement);
|
|
860
|
+
const elementBounds = targetElement.getBoundingClientRect();
|
|
861
|
+
createGrabbedOverlay(overlayRoot, {
|
|
862
|
+
borderRadius: computedStyle.borderRadius || "0px",
|
|
863
|
+
height: elementBounds.height,
|
|
864
|
+
transform: computedStyle.transform || "none",
|
|
865
|
+
width: elementBounds.width,
|
|
866
|
+
x: elementBounds.left,
|
|
867
|
+
y: elementBounds.top
|
|
868
|
+
});
|
|
869
|
+
void handleCopy(targetElement).finally(() => {
|
|
870
|
+
isCopying = false;
|
|
871
|
+
isActive = isHoldingKeys;
|
|
872
|
+
});
|
|
873
|
+
};
|
|
874
|
+
const activateOverlay = () => {
|
|
875
|
+
if (!overlayRoot) {
|
|
876
|
+
overlayRoot = mountRoot();
|
|
877
|
+
selectionOverlay = createSelectionOverlay(
|
|
878
|
+
overlayRoot,
|
|
879
|
+
handleSelectionClick
|
|
880
|
+
);
|
|
881
|
+
continuousRender();
|
|
882
|
+
}
|
|
883
|
+
isActive = true;
|
|
884
|
+
handleRender();
|
|
885
|
+
};
|
|
886
|
+
const handleKeyDown = (event) => {
|
|
887
|
+
if (event.key === "Escape" && isHoldingKeys) {
|
|
888
|
+
isHoldingKeys = false;
|
|
889
|
+
if (holdTimer) window.clearTimeout(holdTimer);
|
|
890
|
+
isActive = false;
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
if (isKeyboardEventTriggeredByInput(event)) return;
|
|
894
|
+
if (isTargetKeyCombination(event) && !isHoldingKeys) {
|
|
895
|
+
isHoldingKeys = true;
|
|
896
|
+
holdTimer = window.setTimeout(() => {
|
|
897
|
+
activateOverlay();
|
|
898
|
+
options.onActivate?.();
|
|
899
|
+
}, options.keyHoldDuration);
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
const handleKeyUp = (event) => {
|
|
903
|
+
if (isHoldingKeys && (!isTargetKeyCombination(event) || event.key.toLowerCase() === "c")) {
|
|
904
|
+
isHoldingKeys = false;
|
|
905
|
+
if (holdTimer) window.clearTimeout(holdTimer);
|
|
906
|
+
isActive = false;
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
910
|
+
window.addEventListener("keyup", handleKeyUp);
|
|
911
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
912
|
+
window.addEventListener("scroll", scheduleRender, true);
|
|
913
|
+
window.addEventListener("resize", scheduleRender);
|
|
914
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1363
915
|
return () => {
|
|
916
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
917
|
+
window.removeEventListener("keyup", handleKeyUp);
|
|
1364
918
|
window.removeEventListener("mousemove", handleMouseMove);
|
|
1365
|
-
window.removeEventListener("
|
|
1366
|
-
window.removeEventListener("
|
|
1367
|
-
window.removeEventListener("scroll", handleScroll, true);
|
|
1368
|
-
window.removeEventListener("resize", handleResize);
|
|
919
|
+
window.removeEventListener("scroll", scheduleRender, true);
|
|
920
|
+
window.removeEventListener("resize", scheduleRender);
|
|
1369
921
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
cleanupKeyStateChangeSubscription();
|
|
1373
|
-
if (cleanupActivationHotkeyWatcher) {
|
|
1374
|
-
cleanupActivationHotkeyWatcher();
|
|
1375
|
-
}
|
|
1376
|
-
stopProgressTracking();
|
|
922
|
+
if (holdTimer) window.clearTimeout(holdTimer);
|
|
923
|
+
if (renderFrameId) cancelAnimationFrame(renderFrameId);
|
|
1377
924
|
cleanupGrabbedIndicators();
|
|
1378
925
|
hideLabel();
|
|
1379
926
|
};
|
|
1380
927
|
};
|
|
1381
|
-
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
1382
|
-
const currentScript = document.currentScript;
|
|
1383
|
-
const options = {};
|
|
1384
|
-
if (currentScript?.dataset) {
|
|
1385
|
-
const { adapter, enabled, hotkey, keyHoldDuration } = currentScript.dataset;
|
|
1386
|
-
if (adapter !== void 0) {
|
|
1387
|
-
if (adapter === "cursor") {
|
|
1388
|
-
options.adapter = cursorAdapter;
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
if (enabled !== void 0) {
|
|
1392
|
-
options.enabled = enabled === "true";
|
|
1393
|
-
}
|
|
1394
|
-
if (hotkey !== void 0) {
|
|
1395
|
-
const keys = hotkey.split(",").map((key) => key.trim());
|
|
1396
|
-
options.hotkey = keys.length === 1 ? keys[0] : keys;
|
|
1397
|
-
}
|
|
1398
|
-
if (keyHoldDuration !== void 0) {
|
|
1399
|
-
const duration = Number(keyHoldDuration);
|
|
1400
|
-
if (!Number.isNaN(duration)) {
|
|
1401
|
-
options.keyHoldDuration = duration;
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
init(options);
|
|
1406
|
-
}
|
|
1407
928
|
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
exports.libStore = libStore;
|
|
929
|
+
// src/index.ts
|
|
930
|
+
init();
|