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