react-grab 0.0.18 → 0.0.20
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 +1410 -0
- package/dist/index.d.cts +47 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.global.js +19 -19
- package/dist/index.js +1406 -0
- package/package.json +2 -2
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1410 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var bippy = require('bippy');
|
|
4
|
+
var source = require('bippy/dist/source');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @license MIT
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2025 Aiden Bai
|
|
10
|
+
*
|
|
11
|
+
* This source code is licensed under the MIT license found in the
|
|
12
|
+
* LICENSE file in the root directory of this source tree.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// src/adapters.ts
|
|
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
|
|
27
|
+
var FORM_TAGS_AND_ROLES = [
|
|
28
|
+
"input",
|
|
29
|
+
"textarea",
|
|
30
|
+
"select",
|
|
31
|
+
"searchbox",
|
|
32
|
+
"slider",
|
|
33
|
+
"spinbutton",
|
|
34
|
+
"menuitem",
|
|
35
|
+
"menuitemcheckbox",
|
|
36
|
+
"menuitemradio",
|
|
37
|
+
"option",
|
|
38
|
+
"radio",
|
|
39
|
+
"textbox"
|
|
40
|
+
];
|
|
41
|
+
var isCustomElement = (element) => {
|
|
42
|
+
return Boolean(element.tagName) && !element.tagName.startsWith("-") && element.tagName.includes("-");
|
|
43
|
+
};
|
|
44
|
+
var isReadonlyArray = (value) => {
|
|
45
|
+
return Array.isArray(value);
|
|
46
|
+
};
|
|
47
|
+
var isHotkeyEnabledOnTagName = (event, enabledOnTags = false) => {
|
|
48
|
+
const { composed, target } = event;
|
|
49
|
+
let targetTagName;
|
|
50
|
+
let targetRole;
|
|
51
|
+
if (target instanceof HTMLElement && isCustomElement(target) && composed) {
|
|
52
|
+
const composedPath = event.composedPath();
|
|
53
|
+
const targetElement = composedPath[0];
|
|
54
|
+
if (targetElement instanceof HTMLElement) {
|
|
55
|
+
targetTagName = targetElement.tagName;
|
|
56
|
+
targetRole = targetElement.role;
|
|
57
|
+
}
|
|
58
|
+
} else if (target instanceof HTMLElement) {
|
|
59
|
+
targetTagName = target.tagName;
|
|
60
|
+
targetRole = target.role;
|
|
61
|
+
}
|
|
62
|
+
if (isReadonlyArray(enabledOnTags)) {
|
|
63
|
+
return Boolean(
|
|
64
|
+
targetTagName && enabledOnTags && enabledOnTags.some(
|
|
65
|
+
(tag) => typeof targetTagName === "string" && tag.toLowerCase() === targetTagName.toLowerCase() || tag === targetRole
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return Boolean(targetTagName && enabledOnTags && enabledOnTags);
|
|
70
|
+
};
|
|
71
|
+
var isKeyboardEventTriggeredByInput = (event) => {
|
|
72
|
+
return isHotkeyEnabledOnTagName(event, FORM_TAGS_AND_ROLES);
|
|
73
|
+
};
|
|
74
|
+
var trackHotkeys = () => {
|
|
75
|
+
const handleKeyDown = (event) => {
|
|
76
|
+
if (isKeyboardEventTriggeredByInput(event)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (event.code === void 0) {
|
|
80
|
+
return;
|
|
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
|
+
)}`
|
|
425
|
+
);
|
|
426
|
+
} else {
|
|
427
|
+
lines.push(indent + " " + getElementTag(element));
|
|
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
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
455
|
+
const indent2 = " ".repeat(i);
|
|
456
|
+
lines.push(indent2 + getClosingTag(ancestors[i]));
|
|
457
|
+
}
|
|
458
|
+
return lines.join("\n");
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
// src/overlay.ts
|
|
462
|
+
var VIEWPORT_MARGIN_PX = 8;
|
|
463
|
+
var LABEL_OFFSET_PX = 6;
|
|
464
|
+
var INDICATOR_CLAMP_PADDING_PX = 4;
|
|
465
|
+
var INDICATOR_SUCCESS_VISIBLE_MS = 1500;
|
|
466
|
+
var INDICATOR_FADE_MS = 200;
|
|
467
|
+
var lerp = (start, end, factor) => {
|
|
468
|
+
return start + (end - start) * factor;
|
|
469
|
+
};
|
|
470
|
+
var SELECTION_LERP_FACTOR = 0.95;
|
|
471
|
+
var createSelectionElement = ({
|
|
472
|
+
borderRadius,
|
|
473
|
+
height,
|
|
474
|
+
transform,
|
|
475
|
+
width,
|
|
476
|
+
x,
|
|
477
|
+
y
|
|
478
|
+
}) => {
|
|
479
|
+
const overlay = document.createElement("div");
|
|
480
|
+
overlay.style.position = "fixed";
|
|
481
|
+
overlay.style.top = `${y}px`;
|
|
482
|
+
overlay.style.left = `${x}px`;
|
|
483
|
+
overlay.style.width = `${width}px`;
|
|
484
|
+
overlay.style.height = `${height}px`;
|
|
485
|
+
overlay.style.borderRadius = borderRadius;
|
|
486
|
+
overlay.style.transform = transform;
|
|
487
|
+
overlay.style.pointerEvents = "none";
|
|
488
|
+
overlay.style.border = "1px solid rgb(210, 57, 192)";
|
|
489
|
+
overlay.style.backgroundColor = "rgba(210, 57, 192, 0.2)";
|
|
490
|
+
overlay.style.zIndex = "2147483646";
|
|
491
|
+
overlay.style.boxSizing = "border-box";
|
|
492
|
+
overlay.style.display = "none";
|
|
493
|
+
return overlay;
|
|
494
|
+
};
|
|
495
|
+
var updateSelectionElement = (element, { borderRadius, height, transform, width, x, y }) => {
|
|
496
|
+
const currentTop = parseFloat(element.style.top) || 0;
|
|
497
|
+
const currentLeft = parseFloat(element.style.left) || 0;
|
|
498
|
+
const currentWidth = parseFloat(element.style.width) || 0;
|
|
499
|
+
const currentHeight = parseFloat(element.style.height) || 0;
|
|
500
|
+
const topValue = `${lerp(currentTop, y, SELECTION_LERP_FACTOR)}px`;
|
|
501
|
+
const leftValue = `${lerp(currentLeft, x, SELECTION_LERP_FACTOR)}px`;
|
|
502
|
+
const widthValue = `${lerp(currentWidth, width, SELECTION_LERP_FACTOR)}px`;
|
|
503
|
+
const heightValue = `${lerp(currentHeight, height, SELECTION_LERP_FACTOR)}px`;
|
|
504
|
+
if (element.style.top !== topValue) {
|
|
505
|
+
element.style.top = topValue;
|
|
506
|
+
}
|
|
507
|
+
if (element.style.left !== leftValue) {
|
|
508
|
+
element.style.left = leftValue;
|
|
509
|
+
}
|
|
510
|
+
if (element.style.width !== widthValue) {
|
|
511
|
+
element.style.width = widthValue;
|
|
512
|
+
}
|
|
513
|
+
if (element.style.height !== heightValue) {
|
|
514
|
+
element.style.height = heightValue;
|
|
515
|
+
}
|
|
516
|
+
if (element.style.borderRadius !== borderRadius) {
|
|
517
|
+
element.style.borderRadius = borderRadius;
|
|
518
|
+
}
|
|
519
|
+
if (element.style.transform !== transform) {
|
|
520
|
+
element.style.transform = transform;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
var createSelectionOverlay = (root) => {
|
|
524
|
+
const element = createSelectionElement({
|
|
525
|
+
borderRadius: "0px",
|
|
526
|
+
height: 0,
|
|
527
|
+
transform: "none",
|
|
528
|
+
width: 0,
|
|
529
|
+
x: -1e3,
|
|
530
|
+
y: -1e3
|
|
531
|
+
});
|
|
532
|
+
root.appendChild(element);
|
|
533
|
+
let visible = false;
|
|
534
|
+
element.addEventListener("mousedown", (event) => {
|
|
535
|
+
event.preventDefault();
|
|
536
|
+
event.stopPropagation();
|
|
537
|
+
event.stopImmediatePropagation();
|
|
538
|
+
const { overlayMode } = libStore.getState();
|
|
539
|
+
if (overlayMode === "visible") {
|
|
540
|
+
libStore.setState((state) => ({
|
|
541
|
+
...state,
|
|
542
|
+
overlayMode: "copying"
|
|
543
|
+
}));
|
|
544
|
+
}
|
|
545
|
+
}, true);
|
|
546
|
+
return {
|
|
547
|
+
element,
|
|
548
|
+
hide: () => {
|
|
549
|
+
visible = false;
|
|
550
|
+
element.style.display = "none";
|
|
551
|
+
},
|
|
552
|
+
isVisible: () => visible,
|
|
553
|
+
show: () => {
|
|
554
|
+
visible = true;
|
|
555
|
+
element.style.display = "block";
|
|
556
|
+
},
|
|
557
|
+
update: (selection) => {
|
|
558
|
+
updateSelectionElement(element, selection);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
};
|
|
562
|
+
var createGrabbedOverlay = (root, selection) => {
|
|
563
|
+
const element = document.createElement("div");
|
|
564
|
+
element.style.position = "fixed";
|
|
565
|
+
element.style.top = `${selection.y}px`;
|
|
566
|
+
element.style.left = `${selection.x}px`;
|
|
567
|
+
element.style.width = `${selection.width}px`;
|
|
568
|
+
element.style.height = `${selection.height}px`;
|
|
569
|
+
element.style.borderRadius = selection.borderRadius;
|
|
570
|
+
element.style.transform = selection.transform;
|
|
571
|
+
element.style.pointerEvents = "none";
|
|
572
|
+
element.style.border = "1px solid rgb(210, 57, 192)";
|
|
573
|
+
element.style.backgroundColor = "rgba(210, 57, 192, 0.2)";
|
|
574
|
+
element.style.zIndex = "2147483646";
|
|
575
|
+
element.style.boxSizing = "border-box";
|
|
576
|
+
element.style.transition = "opacity 0.3s ease-out";
|
|
577
|
+
element.style.opacity = "1";
|
|
578
|
+
root.appendChild(element);
|
|
579
|
+
requestAnimationFrame(() => {
|
|
580
|
+
element.style.opacity = "0";
|
|
581
|
+
});
|
|
582
|
+
setTimeout(() => {
|
|
583
|
+
element.remove();
|
|
584
|
+
}, 300);
|
|
585
|
+
};
|
|
586
|
+
var createSpinner = () => {
|
|
587
|
+
const spinner = document.createElement("span");
|
|
588
|
+
spinner.style.display = "inline-block";
|
|
589
|
+
spinner.style.width = "8px";
|
|
590
|
+
spinner.style.height = "8px";
|
|
591
|
+
spinner.style.border = "1.5px solid rgb(210, 57, 192)";
|
|
592
|
+
spinner.style.borderTopColor = "transparent";
|
|
593
|
+
spinner.style.borderRadius = "50%";
|
|
594
|
+
spinner.style.marginRight = "4px";
|
|
595
|
+
spinner.style.verticalAlign = "middle";
|
|
596
|
+
spinner.animate(
|
|
597
|
+
[{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }],
|
|
598
|
+
{
|
|
599
|
+
duration: 600,
|
|
600
|
+
easing: "linear",
|
|
601
|
+
iterations: Infinity
|
|
602
|
+
}
|
|
603
|
+
);
|
|
604
|
+
return spinner;
|
|
605
|
+
};
|
|
606
|
+
var activeIndicator = null;
|
|
607
|
+
var createIndicator = () => {
|
|
608
|
+
const indicator = document.createElement("div");
|
|
609
|
+
indicator.style.position = "fixed";
|
|
610
|
+
indicator.style.top = "calc(8px + env(safe-area-inset-top))";
|
|
611
|
+
indicator.style.padding = "2px 6px";
|
|
612
|
+
indicator.style.backgroundColor = "#fde7f7";
|
|
613
|
+
indicator.style.color = "#b21c8e";
|
|
614
|
+
indicator.style.border = "1px solid #f7c5ec";
|
|
615
|
+
indicator.style.borderRadius = "4px";
|
|
616
|
+
indicator.style.fontSize = "11px";
|
|
617
|
+
indicator.style.fontWeight = "500";
|
|
618
|
+
indicator.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
|
|
619
|
+
indicator.style.zIndex = "2147483647";
|
|
620
|
+
indicator.style.pointerEvents = "none";
|
|
621
|
+
indicator.style.opacity = "0";
|
|
622
|
+
indicator.style.transition = "opacity 0.2s ease-in-out";
|
|
623
|
+
indicator.style.display = "flex";
|
|
624
|
+
indicator.style.alignItems = "center";
|
|
625
|
+
indicator.style.maxWidth = "calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)))";
|
|
626
|
+
indicator.style.overflow = "hidden";
|
|
627
|
+
indicator.style.textOverflow = "ellipsis";
|
|
628
|
+
indicator.style.whiteSpace = "nowrap";
|
|
629
|
+
return indicator;
|
|
630
|
+
};
|
|
631
|
+
var showLabel = (root, selectionLeftPx, selectionTopPx, tagName) => {
|
|
632
|
+
let indicator = activeIndicator;
|
|
633
|
+
let isNewIndicator = false;
|
|
634
|
+
if (!indicator) {
|
|
635
|
+
indicator = createIndicator();
|
|
636
|
+
root.appendChild(indicator);
|
|
637
|
+
activeIndicator = indicator;
|
|
638
|
+
isNewIndicator = true;
|
|
639
|
+
isProcessing = false;
|
|
640
|
+
}
|
|
641
|
+
if (!isProcessing) {
|
|
642
|
+
const labelText = indicator.querySelector("span");
|
|
643
|
+
if (labelText) {
|
|
644
|
+
const tagNameMonospace = document.createElement("span");
|
|
645
|
+
tagNameMonospace.textContent = tagName ? `<${tagName}>` : "<element>";
|
|
646
|
+
tagNameMonospace.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
|
|
647
|
+
tagNameMonospace.style.fontVariantNumeric = "tabular-nums";
|
|
648
|
+
labelText.replaceChildren(tagNameMonospace);
|
|
649
|
+
} else {
|
|
650
|
+
const newLabelText = document.createElement("span");
|
|
651
|
+
const tagNameMonospace = document.createElement("span");
|
|
652
|
+
tagNameMonospace.textContent = tagName ? `<${tagName}>` : "<element>";
|
|
653
|
+
tagNameMonospace.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
|
|
654
|
+
tagNameMonospace.style.fontVariantNumeric = "tabular-nums";
|
|
655
|
+
newLabelText.appendChild(tagNameMonospace);
|
|
656
|
+
indicator.appendChild(newLabelText);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
const indicatorRect = indicator.getBoundingClientRect();
|
|
660
|
+
const viewportWidthPx = window.innerWidth;
|
|
661
|
+
const viewportHeightPx = window.innerHeight;
|
|
662
|
+
let indicatorLeftPx = Math.round(selectionLeftPx);
|
|
663
|
+
let indicatorTopPx = Math.round(selectionTopPx) - indicatorRect.height - LABEL_OFFSET_PX;
|
|
664
|
+
const CLAMPED_PADDING = INDICATOR_CLAMP_PADDING_PX;
|
|
665
|
+
const minLeft = VIEWPORT_MARGIN_PX;
|
|
666
|
+
const minTop = VIEWPORT_MARGIN_PX;
|
|
667
|
+
const maxLeft = viewportWidthPx - indicatorRect.width - VIEWPORT_MARGIN_PX;
|
|
668
|
+
const maxTop = viewportHeightPx - indicatorRect.height - VIEWPORT_MARGIN_PX;
|
|
669
|
+
const willClampLeft = indicatorLeftPx < minLeft;
|
|
670
|
+
const willClampTop = indicatorTopPx < minTop;
|
|
671
|
+
const isClamped = willClampLeft || willClampTop;
|
|
672
|
+
indicatorLeftPx = Math.max(minLeft, Math.min(indicatorLeftPx, maxLeft));
|
|
673
|
+
indicatorTopPx = Math.max(minTop, Math.min(indicatorTopPx, maxTop));
|
|
674
|
+
if (isClamped) {
|
|
675
|
+
indicatorLeftPx += CLAMPED_PADDING;
|
|
676
|
+
indicatorTopPx += CLAMPED_PADDING;
|
|
677
|
+
}
|
|
678
|
+
indicator.style.left = `${indicatorLeftPx}px`;
|
|
679
|
+
indicator.style.top = `${indicatorTopPx}px`;
|
|
680
|
+
indicator.style.right = "auto";
|
|
681
|
+
if (isNewIndicator) {
|
|
682
|
+
requestAnimationFrame(() => {
|
|
683
|
+
indicator.style.opacity = "1";
|
|
684
|
+
});
|
|
685
|
+
} else if (indicator.style.opacity !== "1") {
|
|
686
|
+
indicator.style.opacity = "1";
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
var isProcessing = false;
|
|
690
|
+
var activeGrabbedIndicators = /* @__PURE__ */ new Set();
|
|
691
|
+
var updateLabelToProcessing = (root, selectionLeftPx, selectionTopPx) => {
|
|
692
|
+
const indicator = createIndicator();
|
|
693
|
+
indicator.style.zIndex = "2147483648";
|
|
694
|
+
root.appendChild(indicator);
|
|
695
|
+
activeGrabbedIndicators.add(indicator);
|
|
696
|
+
const positionIndicator = () => {
|
|
697
|
+
if (selectionLeftPx === void 0 || selectionTopPx === void 0) return;
|
|
698
|
+
const indicatorRect = indicator.getBoundingClientRect();
|
|
699
|
+
const viewportWidthPx = window.innerWidth;
|
|
700
|
+
const viewportHeightPx = window.innerHeight;
|
|
701
|
+
let indicatorLeftPx = Math.round(selectionLeftPx);
|
|
702
|
+
let indicatorTopPx = Math.round(selectionTopPx) - indicatorRect.height - LABEL_OFFSET_PX;
|
|
703
|
+
const CLAMPED_PADDING = INDICATOR_CLAMP_PADDING_PX;
|
|
704
|
+
const minLeft = VIEWPORT_MARGIN_PX;
|
|
705
|
+
const minTop = VIEWPORT_MARGIN_PX;
|
|
706
|
+
const maxLeft = viewportWidthPx - indicatorRect.width - VIEWPORT_MARGIN_PX;
|
|
707
|
+
const maxTop = viewportHeightPx - indicatorRect.height - VIEWPORT_MARGIN_PX;
|
|
708
|
+
const willClampLeft = indicatorLeftPx < minLeft;
|
|
709
|
+
const willClampTop = indicatorTopPx < minTop;
|
|
710
|
+
const isClamped = willClampLeft || willClampTop;
|
|
711
|
+
indicatorLeftPx = Math.max(minLeft, Math.min(indicatorLeftPx, maxLeft));
|
|
712
|
+
indicatorTopPx = Math.max(minTop, Math.min(indicatorTopPx, maxTop));
|
|
713
|
+
if (isClamped) {
|
|
714
|
+
indicatorLeftPx += CLAMPED_PADDING;
|
|
715
|
+
indicatorTopPx += CLAMPED_PADDING;
|
|
716
|
+
}
|
|
717
|
+
indicator.style.left = `${indicatorLeftPx}px`;
|
|
718
|
+
indicator.style.top = `${indicatorTopPx}px`;
|
|
719
|
+
indicator.style.right = "auto";
|
|
720
|
+
};
|
|
721
|
+
const loadingSpinner = createSpinner();
|
|
722
|
+
const labelText = document.createElement("span");
|
|
723
|
+
labelText.textContent = "Grabbing\u2026";
|
|
724
|
+
indicator.appendChild(loadingSpinner);
|
|
725
|
+
indicator.appendChild(labelText);
|
|
726
|
+
positionIndicator();
|
|
727
|
+
requestAnimationFrame(() => {
|
|
728
|
+
indicator.style.opacity = "1";
|
|
729
|
+
});
|
|
730
|
+
return (tagName) => {
|
|
731
|
+
indicator.textContent = "";
|
|
732
|
+
const checkmarkIcon = document.createElement("span");
|
|
733
|
+
checkmarkIcon.textContent = "\u2713";
|
|
734
|
+
checkmarkIcon.style.display = "inline-block";
|
|
735
|
+
checkmarkIcon.style.marginRight = "4px";
|
|
736
|
+
checkmarkIcon.style.fontWeight = "600";
|
|
737
|
+
const newLabelText = document.createElement("span");
|
|
738
|
+
const tagNameMonospace = document.createElement("span");
|
|
739
|
+
tagNameMonospace.textContent = tagName ? `<${tagName}>` : "<element>";
|
|
740
|
+
tagNameMonospace.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
|
|
741
|
+
tagNameMonospace.style.fontVariantNumeric = "tabular-nums";
|
|
742
|
+
newLabelText.appendChild(document.createTextNode("Grabbed "));
|
|
743
|
+
newLabelText.appendChild(tagNameMonospace);
|
|
744
|
+
indicator.appendChild(checkmarkIcon);
|
|
745
|
+
indicator.appendChild(newLabelText);
|
|
746
|
+
requestAnimationFrame(() => {
|
|
747
|
+
positionIndicator();
|
|
748
|
+
});
|
|
749
|
+
setTimeout(() => {
|
|
750
|
+
indicator.style.opacity = "0";
|
|
751
|
+
setTimeout(() => {
|
|
752
|
+
indicator.remove();
|
|
753
|
+
activeGrabbedIndicators.delete(indicator);
|
|
754
|
+
}, INDICATOR_FADE_MS);
|
|
755
|
+
}, INDICATOR_SUCCESS_VISIBLE_MS);
|
|
756
|
+
};
|
|
757
|
+
};
|
|
758
|
+
var hideLabel = () => {
|
|
759
|
+
if (activeIndicator) {
|
|
760
|
+
activeIndicator.remove();
|
|
761
|
+
activeIndicator = null;
|
|
762
|
+
}
|
|
763
|
+
isProcessing = false;
|
|
764
|
+
};
|
|
765
|
+
var cleanupGrabbedIndicators = () => {
|
|
766
|
+
for (const indicator of activeGrabbedIndicators) {
|
|
767
|
+
indicator.remove();
|
|
768
|
+
}
|
|
769
|
+
activeGrabbedIndicators.clear();
|
|
770
|
+
};
|
|
771
|
+
var activeProgressIndicator = null;
|
|
772
|
+
var createProgressIndicatorElement = () => {
|
|
773
|
+
const container = document.createElement("div");
|
|
774
|
+
container.style.position = "fixed";
|
|
775
|
+
container.style.zIndex = "2147483647";
|
|
776
|
+
container.style.pointerEvents = "none";
|
|
777
|
+
container.style.opacity = "0";
|
|
778
|
+
container.style.transition = "opacity 0.1s ease-in-out";
|
|
779
|
+
const progressBarContainer = document.createElement("div");
|
|
780
|
+
progressBarContainer.style.width = "32px";
|
|
781
|
+
progressBarContainer.style.height = "2px";
|
|
782
|
+
progressBarContainer.style.backgroundColor = "rgba(178, 28, 142, 0.2)";
|
|
783
|
+
progressBarContainer.style.borderRadius = "1px";
|
|
784
|
+
progressBarContainer.style.overflow = "hidden";
|
|
785
|
+
progressBarContainer.style.position = "relative";
|
|
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;
|
|
796
|
+
};
|
|
797
|
+
var showProgressIndicator = (root, progress, mouseX, mouseY) => {
|
|
798
|
+
if (!activeProgressIndicator) {
|
|
799
|
+
activeProgressIndicator = createProgressIndicatorElement();
|
|
800
|
+
root.appendChild(activeProgressIndicator);
|
|
801
|
+
requestAnimationFrame(() => {
|
|
802
|
+
if (activeProgressIndicator) {
|
|
803
|
+
activeProgressIndicator.style.opacity = "1";
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
const indicator = activeProgressIndicator;
|
|
808
|
+
const indicatorRect = indicator.getBoundingClientRect();
|
|
809
|
+
const viewportWidth = window.innerWidth;
|
|
810
|
+
const viewportHeight = window.innerHeight;
|
|
811
|
+
const CURSOR_OFFSET = 14;
|
|
812
|
+
const VIEWPORT_MARGIN = 8;
|
|
813
|
+
let indicatorLeft = mouseX - indicatorRect.width / 2;
|
|
814
|
+
let indicatorTop = mouseY + CURSOR_OFFSET;
|
|
815
|
+
if (indicatorTop + indicatorRect.height + VIEWPORT_MARGIN > viewportHeight) {
|
|
816
|
+
indicatorTop = mouseY - indicatorRect.height - CURSOR_OFFSET;
|
|
817
|
+
}
|
|
818
|
+
indicatorTop = Math.max(
|
|
819
|
+
VIEWPORT_MARGIN,
|
|
820
|
+
Math.min(indicatorTop, viewportHeight - indicatorRect.height - VIEWPORT_MARGIN)
|
|
821
|
+
);
|
|
822
|
+
indicatorLeft = Math.max(
|
|
823
|
+
VIEWPORT_MARGIN,
|
|
824
|
+
Math.min(indicatorLeft, viewportWidth - indicatorRect.width - VIEWPORT_MARGIN)
|
|
825
|
+
);
|
|
826
|
+
indicator.style.top = `${indicatorTop}px`;
|
|
827
|
+
indicator.style.left = `${indicatorLeft}px`;
|
|
828
|
+
const progressFill = indicator.querySelector(
|
|
829
|
+
"[data-progress-fill]"
|
|
830
|
+
);
|
|
831
|
+
if (progressFill) {
|
|
832
|
+
const percentage = Math.min(100, Math.max(0, progress * 100));
|
|
833
|
+
progressFill.style.width = `${percentage}%`;
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
var hideProgressIndicator = () => {
|
|
837
|
+
if (activeProgressIndicator) {
|
|
838
|
+
activeProgressIndicator.style.opacity = "0";
|
|
839
|
+
setTimeout(() => {
|
|
840
|
+
if (activeProgressIndicator) {
|
|
841
|
+
activeProgressIndicator.remove();
|
|
842
|
+
activeProgressIndicator = null;
|
|
843
|
+
}
|
|
844
|
+
}, 100);
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// src/utils/copy-text.ts
|
|
849
|
+
var IS_NAVIGATOR_CLIPBOARD_AVAILABLE = typeof window !== "undefined" && window.navigator.clipboard && window.isSecureContext;
|
|
850
|
+
var copyTextToClipboard = async (text) => {
|
|
851
|
+
if (IS_NAVIGATOR_CLIPBOARD_AVAILABLE) {
|
|
852
|
+
try {
|
|
853
|
+
await navigator.clipboard.writeText(text);
|
|
854
|
+
return true;
|
|
855
|
+
} catch {
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
const textareaElement = document.createElement("textarea");
|
|
859
|
+
textareaElement.value = text;
|
|
860
|
+
textareaElement.setAttribute("readonly", "");
|
|
861
|
+
textareaElement.style.position = "fixed";
|
|
862
|
+
textareaElement.style.top = "-9999px";
|
|
863
|
+
textareaElement.style.opacity = "0";
|
|
864
|
+
textareaElement.style.pointerEvents = "none";
|
|
865
|
+
const doc = document.body || document.documentElement;
|
|
866
|
+
doc.appendChild(textareaElement);
|
|
867
|
+
textareaElement.select();
|
|
868
|
+
textareaElement.setSelectionRange(0, textareaElement.value.length);
|
|
869
|
+
let didCopyToClipboard = false;
|
|
870
|
+
try {
|
|
871
|
+
didCopyToClipboard = document.execCommand("copy");
|
|
872
|
+
} catch {
|
|
873
|
+
didCopyToClipboard = false;
|
|
874
|
+
} finally {
|
|
875
|
+
doc.removeChild(textareaElement);
|
|
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
|
+
}
|
|
896
|
+
}
|
|
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
|
+
};
|
|
911
|
+
|
|
912
|
+
// src/utils/store.ts
|
|
913
|
+
var createStore = (initializer) => {
|
|
914
|
+
const subscriberMap = /* @__PURE__ */ new Map();
|
|
915
|
+
let currentListenerIndex = 0;
|
|
916
|
+
let currentState;
|
|
917
|
+
const setState = (maybeStateOrReducer) => {
|
|
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
|
|
979
|
+
};
|
|
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
|
+
if (options.enabled === false) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
const resolvedOptions = {
|
|
1003
|
+
adapter: void 0,
|
|
1004
|
+
enabled: true,
|
|
1005
|
+
hotkey: options.hotkey ?? getDefaultHotkey(),
|
|
1006
|
+
keyHoldDuration: 500,
|
|
1007
|
+
...options
|
|
1008
|
+
};
|
|
1009
|
+
const root = mountRoot();
|
|
1010
|
+
const selectionOverlay = createSelectionOverlay(root);
|
|
1011
|
+
let hoveredElement = null;
|
|
1012
|
+
let lastGrabbedElement = null;
|
|
1013
|
+
let isCopying = false;
|
|
1014
|
+
let progressAnimationFrame = null;
|
|
1015
|
+
let progressStartTime = null;
|
|
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();
|
|
1197
|
+
const getElementAtPosition = (x, y) => {
|
|
1198
|
+
const elements = document.elementsFromPoint(x, y);
|
|
1199
|
+
for (const element of elements) {
|
|
1200
|
+
if (element.closest(`[${ATTRIBUTE_NAME}]`)) {
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
const computedStyle = window.getComputedStyle(element);
|
|
1204
|
+
if (!isElementVisible(element, computedStyle)) {
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
return element;
|
|
1208
|
+
}
|
|
1209
|
+
return null;
|
|
1210
|
+
};
|
|
1211
|
+
const handleCopy = async (element) => {
|
|
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
|
+
`
|
|
1219
|
+
|
|
1220
|
+
<referenced_element>
|
|
1221
|
+
${htmlSnippet}
|
|
1222
|
+
</referenced_element>`
|
|
1223
|
+
);
|
|
1224
|
+
const stack = await getStack(element);
|
|
1225
|
+
if (stack) {
|
|
1226
|
+
const filteredStack = filterStack(stack);
|
|
1227
|
+
if (filteredStack.length > 0) {
|
|
1228
|
+
const serializedStack = serializeStack(filteredStack);
|
|
1229
|
+
const fullText = `${htmlSnippet}
|
|
1230
|
+
|
|
1231
|
+
Component owner stack:
|
|
1232
|
+
${serializedStack}`;
|
|
1233
|
+
await copyTextToClipboard(
|
|
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);
|
|
1249
|
+
}
|
|
1250
|
+
cleanupIndicator(tagName);
|
|
1251
|
+
} catch {
|
|
1252
|
+
cleanupIndicator(tagName);
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
const handleRender = (state) => {
|
|
1256
|
+
const { mouseX, mouseY, overlayMode } = state;
|
|
1257
|
+
if (overlayMode === "hidden") {
|
|
1258
|
+
if (selectionOverlay.isVisible()) {
|
|
1259
|
+
selectionOverlay.hide();
|
|
1260
|
+
}
|
|
1261
|
+
if (!isCopying) {
|
|
1262
|
+
hideLabel();
|
|
1263
|
+
}
|
|
1264
|
+
hoveredElement = null;
|
|
1265
|
+
lastGrabbedElement = null;
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
if (overlayMode === "copying" && hoveredElement) {
|
|
1269
|
+
if (!isCopying) {
|
|
1270
|
+
isCopying = true;
|
|
1271
|
+
lastGrabbedElement = hoveredElement;
|
|
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
|
+
}
|
|
1301
|
+
hoveredElement = null;
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
if (lastGrabbedElement && element !== lastGrabbedElement) {
|
|
1305
|
+
lastGrabbedElement = null;
|
|
1306
|
+
}
|
|
1307
|
+
if (element === lastGrabbedElement) {
|
|
1308
|
+
if (selectionOverlay.isVisible()) {
|
|
1309
|
+
selectionOverlay.hide();
|
|
1310
|
+
}
|
|
1311
|
+
if (!isCopying) {
|
|
1312
|
+
hideLabel();
|
|
1313
|
+
}
|
|
1314
|
+
hoveredElement = element;
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
const tagName = (element.tagName || "").toLowerCase();
|
|
1318
|
+
hoveredElement = element;
|
|
1319
|
+
const rect = element.getBoundingClientRect();
|
|
1320
|
+
const computedStyle = window.getComputedStyle(element);
|
|
1321
|
+
const borderRadius = computedStyle.borderRadius || "0px";
|
|
1322
|
+
const transform = computedStyle.transform || "none";
|
|
1323
|
+
selectionOverlay.update({
|
|
1324
|
+
borderRadius,
|
|
1325
|
+
height: rect.height,
|
|
1326
|
+
transform,
|
|
1327
|
+
width: rect.width,
|
|
1328
|
+
x: rect.left,
|
|
1329
|
+
y: rect.top
|
|
1330
|
+
});
|
|
1331
|
+
if (!selectionOverlay.isVisible()) {
|
|
1332
|
+
selectionOverlay.show();
|
|
1333
|
+
}
|
|
1334
|
+
showLabel(root, rect.left, rect.top, tagName);
|
|
1335
|
+
const isDisabled = (
|
|
1336
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- we do any because typing this will be a massive fucking headache
|
|
1337
|
+
Boolean(element.disabled) || computedStyle.pointerEvents === "none"
|
|
1338
|
+
);
|
|
1339
|
+
if (isDisabled) {
|
|
1340
|
+
const overlayElement = selectionOverlay.element;
|
|
1341
|
+
if (overlayElement) {
|
|
1342
|
+
overlayElement.style.pointerEvents = "auto";
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
let renderScheduled = false;
|
|
1347
|
+
const scheduleRender = () => {
|
|
1348
|
+
if (renderScheduled) return;
|
|
1349
|
+
renderScheduled = true;
|
|
1350
|
+
requestAnimationFrame(() => {
|
|
1351
|
+
renderScheduled = false;
|
|
1352
|
+
handleRender(libStore.getState());
|
|
1353
|
+
});
|
|
1354
|
+
};
|
|
1355
|
+
const cleanupRenderSubscription = libStore.subscribe(() => {
|
|
1356
|
+
scheduleRender();
|
|
1357
|
+
});
|
|
1358
|
+
const continuousRender = () => {
|
|
1359
|
+
scheduleRender();
|
|
1360
|
+
requestAnimationFrame(continuousRender);
|
|
1361
|
+
};
|
|
1362
|
+
continuousRender();
|
|
1363
|
+
return () => {
|
|
1364
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
1365
|
+
window.removeEventListener("mousedown", handleMouseDown, true);
|
|
1366
|
+
window.removeEventListener("click", handleClick, true);
|
|
1367
|
+
window.removeEventListener("scroll", handleScroll, true);
|
|
1368
|
+
window.removeEventListener("resize", handleResize);
|
|
1369
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1370
|
+
cleanupTrackHotkeys();
|
|
1371
|
+
cleanupRenderSubscription();
|
|
1372
|
+
cleanupKeyStateChangeSubscription();
|
|
1373
|
+
if (cleanupActivationHotkeyWatcher) {
|
|
1374
|
+
cleanupActivationHotkeyWatcher();
|
|
1375
|
+
}
|
|
1376
|
+
stopProgressTracking();
|
|
1377
|
+
cleanupGrabbedIndicators();
|
|
1378
|
+
hideLabel();
|
|
1379
|
+
};
|
|
1380
|
+
};
|
|
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
|
+
|
|
1408
|
+
exports.cursorAdapter = cursorAdapter;
|
|
1409
|
+
exports.init = init;
|
|
1410
|
+
exports.libStore = libStore;
|