react-grab 0.0.48 → 0.0.50

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.js CHANGED
@@ -1,7 +1,5 @@
1
- import { delegateEvents, render, createComponent, memo, template, effect, style, insert, setStyleProperty, use } from 'solid-js/web';
2
- import { createRoot, createSignal, createMemo, createEffect, on, onCleanup, Show, For, onMount } from 'solid-js';
3
- import { isInstrumentationActive, getFiberFromHostInstance, traverseFiber, isCompositeFiber, getDisplayName, isFiber, getLatestFiber, isHostFiber } from 'bippy';
4
- import { getSource, isSourceFile, normalizeFileName } from 'bippy/source';
1
+ import { init } from './chunk-73DWLENH.js';
2
+ export { DEFAULT_THEME, formatStack, getHTMLPreview, getNearestComponentName, getStack, init, Ee as isInstrumentationActive } from './chunk-73DWLENH.js';
5
3
 
6
4
  /**
7
5
  * @license MIT
@@ -12,1900 +10,7 @@ import { getSource, isSourceFile, normalizeFileName } from 'bippy/source';
12
10
  * LICENSE file in the root directory of this source tree.
13
11
  */
14
12
 
15
-
16
- // src/utils/is-keyboard-event-triggered-by-input.ts
17
- var FORM_TAGS_AND_ROLES = [
18
- "input",
19
- "textarea",
20
- "select",
21
- "searchbox",
22
- "slider",
23
- "spinbutton",
24
- "menuitem",
25
- "menuitemcheckbox",
26
- "menuitemradio",
27
- "option",
28
- "radio",
29
- "textbox"
30
- ];
31
- var isCustomElement = (element) => {
32
- return Boolean(element.tagName) && !element.tagName.startsWith("-") && element.tagName.includes("-");
33
- };
34
- var isReadonlyArray = (value) => {
35
- return Array.isArray(value);
36
- };
37
- var isHotkeyEnabledOnTagName = (event, enabledOnTags = false) => {
38
- const { composed, target } = event;
39
- let targetTagName;
40
- let targetRole;
41
- if (target instanceof HTMLElement && isCustomElement(target) && composed) {
42
- const composedPath = event.composedPath();
43
- const targetElement = composedPath[0];
44
- if (targetElement instanceof HTMLElement) {
45
- targetTagName = targetElement.tagName;
46
- targetRole = targetElement.role;
47
- }
48
- } else if (target instanceof HTMLElement) {
49
- targetTagName = target.tagName;
50
- targetRole = target.role;
51
- }
52
- if (isReadonlyArray(enabledOnTags)) {
53
- return Boolean(
54
- targetTagName && enabledOnTags && enabledOnTags.some(
55
- (tag) => typeof targetTagName === "string" && tag.toLowerCase() === targetTagName.toLowerCase() || tag === targetRole
56
- )
57
- );
58
- }
59
- return Boolean(targetTagName && enabledOnTags && enabledOnTags);
60
- };
61
- var isKeyboardEventTriggeredByInput = (event) => {
62
- return isHotkeyEnabledOnTagName(event, FORM_TAGS_AND_ROLES);
63
- };
64
-
65
- // src/utils/mount-root.ts
66
- var ATTRIBUTE_NAME = "data-react-grab";
67
- var mountRoot = () => {
68
- const mountedHost = document.querySelector(`[${ATTRIBUTE_NAME}]`);
69
- if (mountedHost) {
70
- const mountedRoot = mountedHost.shadowRoot?.querySelector(
71
- `[${ATTRIBUTE_NAME}]`
72
- );
73
- if (mountedRoot instanceof HTMLDivElement && mountedHost.shadowRoot) {
74
- return mountedRoot;
75
- }
76
- }
77
- const host = document.createElement("div");
78
- host.setAttribute(ATTRIBUTE_NAME, "true");
79
- host.style.zIndex = "2147483646";
80
- host.style.position = "fixed";
81
- host.style.top = "0";
82
- host.style.left = "0";
83
- const shadowRoot = host.attachShadow({ mode: "open" });
84
- const root = document.createElement("div");
85
- root.setAttribute(ATTRIBUTE_NAME, "true");
86
- shadowRoot.appendChild(root);
87
- const doc = document.body ?? document.documentElement;
88
- doc.appendChild(host);
89
- return root;
90
- };
91
-
92
- // src/constants.ts
93
- var VIEWPORT_MARGIN_PX = 8;
94
- var INDICATOR_CLAMP_PADDING_PX = 4;
95
- var CURSOR_OFFSET_PX = 14;
96
- var OFFSCREEN_POSITION = -1e3;
97
- var SELECTION_LERP_FACTOR = 0.95;
98
- var SUCCESS_LABEL_DURATION_MS = 1700;
99
- var PROGRESS_INDICATOR_DELAY_MS = 150;
100
- var DRAG_THRESHOLD_PX = 2;
101
- var AUTO_SCROLL_EDGE_THRESHOLD_PX = 25;
102
- var AUTO_SCROLL_SPEED_PX = 10;
103
- var Z_INDEX_LABEL = 2147483647;
104
-
105
- // src/utils/lerp.ts
106
- var lerp = (start, end, factor) => {
107
- return start + (end - start) * factor;
108
- };
109
-
110
- // src/components/selection-box.tsx
111
- var _tmpl$ = /* @__PURE__ */ template(`<div>`);
112
- var SelectionBox = (props) => {
113
- const [currentX, setCurrentX] = createSignal(props.bounds.x);
114
- const [currentY, setCurrentY] = createSignal(props.bounds.y);
115
- const [currentWidth, setCurrentWidth] = createSignal(props.bounds.width);
116
- const [currentHeight, setCurrentHeight] = createSignal(props.bounds.height);
117
- const [opacity, setOpacity] = createSignal(1);
118
- let hasBeenRenderedOnce = false;
119
- let animationFrameId = null;
120
- let fadeTimerId = null;
121
- let targetBounds = props.bounds;
122
- let isAnimating = false;
123
- const lerpFactor = () => {
124
- if (props.lerpFactor !== void 0) return props.lerpFactor;
125
- if (props.variant === "drag") return 0.7;
126
- return SELECTION_LERP_FACTOR;
127
- };
128
- const startAnimation = () => {
129
- if (isAnimating) return;
130
- isAnimating = true;
131
- const animate = () => {
132
- const interpolatedX = lerp(currentX(), targetBounds.x, lerpFactor());
133
- const interpolatedY = lerp(currentY(), targetBounds.y, lerpFactor());
134
- const interpolatedWidth = lerp(currentWidth(), targetBounds.width, lerpFactor());
135
- const interpolatedHeight = lerp(currentHeight(), targetBounds.height, lerpFactor());
136
- setCurrentX(interpolatedX);
137
- setCurrentY(interpolatedY);
138
- setCurrentWidth(interpolatedWidth);
139
- setCurrentHeight(interpolatedHeight);
140
- const hasConvergedToTarget = Math.abs(interpolatedX - targetBounds.x) < 0.5 && Math.abs(interpolatedY - targetBounds.y) < 0.5 && Math.abs(interpolatedWidth - targetBounds.width) < 0.5 && Math.abs(interpolatedHeight - targetBounds.height) < 0.5;
141
- if (!hasConvergedToTarget) {
142
- animationFrameId = requestAnimationFrame(animate);
143
- } else {
144
- animationFrameId = null;
145
- isAnimating = false;
146
- }
147
- };
148
- animationFrameId = requestAnimationFrame(animate);
149
- };
150
- createEffect(on(() => props.bounds, (newBounds) => {
151
- targetBounds = newBounds;
152
- if (!hasBeenRenderedOnce) {
153
- setCurrentX(targetBounds.x);
154
- setCurrentY(targetBounds.y);
155
- setCurrentWidth(targetBounds.width);
156
- setCurrentHeight(targetBounds.height);
157
- hasBeenRenderedOnce = true;
158
- return;
159
- }
160
- startAnimation();
161
- }));
162
- createEffect(() => {
163
- if (props.variant === "grabbed" && props.createdAt) {
164
- fadeTimerId = window.setTimeout(() => {
165
- setOpacity(0);
166
- }, 1500);
167
- }
168
- });
169
- onCleanup(() => {
170
- if (animationFrameId !== null) {
171
- cancelAnimationFrame(animationFrameId);
172
- animationFrameId = null;
173
- }
174
- if (fadeTimerId !== null) {
175
- window.clearTimeout(fadeTimerId);
176
- fadeTimerId = null;
177
- }
178
- isAnimating = false;
179
- });
180
- const baseStyle = {
181
- position: "fixed",
182
- "box-sizing": "border-box",
183
- "pointer-events": props.variant === "drag" ? "none" : "auto",
184
- "z-index": props.variant === "grabbed" ? "2147483645" : "2147483646"
185
- };
186
- const variantStyle = () => {
187
- if (props.variant === "drag") {
188
- return {
189
- border: "1px dashed rgba(210, 57, 192, 0.4)",
190
- "background-color": "rgba(210, 57, 192, 0.05)",
191
- "will-change": "transform, width, height",
192
- contain: "layout paint size",
193
- cursor: "crosshair"
194
- };
195
- }
196
- if (props.variant === "selection") {
197
- return {
198
- border: "1px dashed rgba(210, 57, 192, 0.5)",
199
- "background-color": "rgba(210, 57, 192, 0.08)"
200
- };
201
- }
202
- return {
203
- border: "1px solid rgb(210, 57, 192)",
204
- "background-color": "rgba(210, 57, 192, 0.08)",
205
- transition: "opacity 0.3s ease-out"
206
- };
207
- };
208
- return createComponent(Show, {
209
- get when() {
210
- return props.visible !== false;
211
- },
212
- get children() {
213
- var _el$ = _tmpl$();
214
- effect((_$p) => style(_el$, {
215
- ...baseStyle,
216
- ...variantStyle(),
217
- top: `${currentY()}px`,
218
- left: `${currentX()}px`,
219
- width: `${currentWidth()}px`,
220
- height: `${currentHeight()}px`,
221
- "border-radius": props.bounds.borderRadius,
222
- transform: props.bounds.transform,
223
- opacity: opacity()
224
- }, _$p));
225
- return _el$;
226
- }
227
- });
228
- };
229
- var _tmpl$2 = /* @__PURE__ */ template(`<span style="display:inline-block;width:8px;height:8px;border:1.5px solid rgb(210, 57, 192);border-top-color:transparent;border-radius:50%;margin-right:4px;vertical-align:middle">`);
230
- var Spinner = (props) => {
231
- let spinnerRef;
232
- onMount(() => {
233
- if (spinnerRef) {
234
- spinnerRef.animate([{
235
- transform: "rotate(0deg)"
236
- }, {
237
- transform: "rotate(360deg)"
238
- }], {
239
- duration: 600,
240
- easing: "linear",
241
- iterations: Infinity
242
- });
243
- }
244
- });
245
- return (() => {
246
- var _el$ = _tmpl$2();
247
- var _ref$ = spinnerRef;
248
- typeof _ref$ === "function" ? use(_ref$, _el$) : spinnerRef = _el$;
249
- effect((_$p) => style(_el$, {
250
- ...props.style
251
- }, _$p));
252
- return _el$;
253
- })();
254
- };
255
-
256
- // src/utils/get-clamped-element-position.ts
257
- var getClampedElementPosition = (positionLeft, positionTop, elementWidth, elementHeight) => {
258
- const viewportWidth = window.innerWidth;
259
- const viewportHeight = window.innerHeight;
260
- const minLeft = VIEWPORT_MARGIN_PX;
261
- const minTop = VIEWPORT_MARGIN_PX;
262
- const maxLeft = viewportWidth - elementWidth - VIEWPORT_MARGIN_PX;
263
- const maxTop = viewportHeight - elementHeight - VIEWPORT_MARGIN_PX;
264
- const clampedLeft = Math.max(minLeft, Math.min(positionLeft, maxLeft));
265
- const clampedTop = Math.max(minTop, Math.min(positionTop, maxTop));
266
- return { left: clampedLeft, top: clampedTop };
267
- };
268
- var useAnimatedPosition = (options) => {
269
- const lerpFactor = options.lerpFactor ?? 0.3;
270
- const convergenceThreshold = options.convergenceThreshold ?? 0.5;
271
- const [x, setX] = createSignal(options.x());
272
- const [y, setY] = createSignal(options.y());
273
- let targetX = options.x();
274
- let targetY = options.y();
275
- let animationFrameId = null;
276
- let hasBeenRenderedOnce = false;
277
- const animate = () => {
278
- const currentX = lerp(x(), targetX, lerpFactor);
279
- const currentY = lerp(y(), targetY, lerpFactor);
280
- setX(currentX);
281
- setY(currentY);
282
- const hasConverged = Math.abs(currentX - targetX) < convergenceThreshold && Math.abs(currentY - targetY) < convergenceThreshold;
283
- if (!hasConverged) {
284
- animationFrameId = requestAnimationFrame(animate);
285
- } else {
286
- animationFrameId = null;
287
- }
288
- };
289
- const startAnimation = () => {
290
- if (animationFrameId !== null) return;
291
- animationFrameId = requestAnimationFrame(animate);
292
- };
293
- createEffect(() => {
294
- targetX = options.x();
295
- targetY = options.y();
296
- if (!hasBeenRenderedOnce) {
297
- setX(targetX);
298
- setY(targetY);
299
- hasBeenRenderedOnce = true;
300
- return;
301
- }
302
- startAnimation();
303
- });
304
- onCleanup(() => {
305
- if (animationFrameId !== null) {
306
- cancelAnimationFrame(animationFrameId);
307
- animationFrameId = null;
308
- }
309
- });
310
- return { x, y };
311
- };
312
- var useFadeInOut = (options) => {
313
- const [opacity, setOpacity] = createSignal(0);
314
- createEffect(
315
- on(
316
- () => options.visible,
317
- (isVisible) => {
318
- if (isVisible !== false) {
319
- requestAnimationFrame(() => {
320
- setOpacity(1);
321
- });
322
- } else {
323
- setOpacity(0);
324
- return;
325
- }
326
- if (options.autoFadeOutAfter !== void 0) {
327
- const fadeOutTimer = setTimeout(() => {
328
- setOpacity(0);
329
- }, options.autoFadeOutAfter);
330
- onCleanup(() => clearTimeout(fadeOutTimer));
331
- }
332
- }
333
- )
334
- );
335
- return opacity;
336
- };
337
-
338
- // src/utils/get-cursor-quadrants.ts
339
- var getCursorQuadrants = (cursorX, cursorY, elementWidth, elementHeight, offset) => {
340
- return [
341
- {
342
- left: Math.round(cursorX) + offset,
343
- top: Math.round(cursorY) + offset
344
- },
345
- {
346
- left: Math.round(cursorX) - elementWidth - offset,
347
- top: Math.round(cursorY) + offset
348
- },
349
- {
350
- left: Math.round(cursorX) + offset,
351
- top: Math.round(cursorY) - elementHeight - offset
352
- },
353
- {
354
- left: Math.round(cursorX) - elementWidth - offset,
355
- top: Math.round(cursorY) - elementHeight - offset
356
- }
357
- ];
358
- };
359
-
360
- // src/components/label.tsx
361
- var _tmpl$3 = /* @__PURE__ */ template(`<div style="position:absolute;top:0;left:0;bottom:0;background-color:rgba(178, 28, 142, 0.2);border-radius:3px;transition:width 0.1s ease-out;pointer-events:none">`);
362
- var _tmpl$22 = /* @__PURE__ */ template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
363
- var _tmpl$32 = /* @__PURE__ */ template(`<div style=margin-right:4px>Copied`);
364
- var _tmpl$4 = /* @__PURE__ */ template(`<span style="font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;font-variant-numeric:tabular-nums;vertical-align:middle">`);
365
- var _tmpl$5 = /* @__PURE__ */ template(`<span style=font-variant-numeric:tabular-nums;font-size:10px;margin-left:4px;vertical-align:middle>`);
366
- var _tmpl$6 = /* @__PURE__ */ template(`<div style=margin-left:4px>to clipboard`);
367
- var _tmpl$7 = /* @__PURE__ */ template(`<div style=font-size:9px;opacity:0.6;text-align:center;margin-top:2px>Click or drag to select`);
368
- var _tmpl$8 = /* @__PURE__ */ template(`<div style="position:fixed;background-color:#fde7f7;color:#b21c8e;border:1px solid #f7c5ec;border-radius:4px;font-size:11px;font-weight:500;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;pointer-events:none;transition:opacity 0.2s ease-in-out;max-width:calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));overflow:hidden"><div style="position:relative;padding:2px 6px;display:flex;flex-direction:column"><div style=display:flex;align-items:center;text-overflow:ellipsis;white-space:nowrap>`);
369
- var Label = (props) => {
370
- let labelRef;
371
- const position = useAnimatedPosition({
372
- x: () => props.x,
373
- y: () => props.y,
374
- lerpFactor: 0.3
375
- });
376
- const opacity = useFadeInOut({
377
- visible: props.visible,
378
- autoFadeOutAfter: props.variant === "success" ? SUCCESS_LABEL_DURATION_MS : void 0
379
- });
380
- const labelBoundingRect = () => labelRef?.getBoundingClientRect();
381
- const computedPosition = () => {
382
- const boundingRect = labelBoundingRect();
383
- if (!boundingRect) return {
384
- left: position.x(),
385
- top: position.y()
386
- };
387
- const viewportWidth = window.innerWidth;
388
- const viewportHeight = window.innerHeight;
389
- const quadrants = getCursorQuadrants(position.x(), position.y(), boundingRect.width, boundingRect.height, CURSOR_OFFSET_PX);
390
- for (const position2 of quadrants) {
391
- const fitsHorizontally = position2.left >= VIEWPORT_MARGIN_PX && position2.left + boundingRect.width <= viewportWidth - VIEWPORT_MARGIN_PX;
392
- const fitsVertically = position2.top >= VIEWPORT_MARGIN_PX && position2.top + boundingRect.height <= viewportHeight - VIEWPORT_MARGIN_PX;
393
- if (fitsHorizontally && fitsVertically) {
394
- return position2;
395
- }
396
- }
397
- const fallback = getClampedElementPosition(quadrants[0].left, quadrants[0].top, boundingRect.width, boundingRect.height);
398
- fallback.left += INDICATOR_CLAMP_PADDING_PX;
399
- fallback.top += INDICATOR_CLAMP_PADDING_PX;
400
- return fallback;
401
- };
402
- const labelSegments = () => {
403
- const separator = " in ";
404
- const separatorIndex = props.text.indexOf(separator);
405
- if (separatorIndex === -1) {
406
- return {
407
- primary: props.text,
408
- secondary: ""
409
- };
410
- }
411
- return {
412
- primary: props.text.slice(0, separatorIndex),
413
- secondary: props.text.slice(separatorIndex)
414
- };
415
- };
416
- return createComponent(Show, {
417
- get when() {
418
- return props.visible !== false;
419
- },
420
- get children() {
421
- var _el$ = _tmpl$8(), _el$3 = _el$.firstChild, _el$4 = _el$3.firstChild;
422
- var _ref$ = labelRef;
423
- typeof _ref$ === "function" ? use(_ref$, _el$) : labelRef = _el$;
424
- insert(_el$, createComponent(Show, {
425
- get when() {
426
- return memo(() => props.variant === "processing")() && props.progress !== void 0;
427
- },
428
- get children() {
429
- var _el$2 = _tmpl$3();
430
- effect((_$p) => setStyleProperty(_el$2, "width", `${Math.min(100, Math.max(0, (props.progress ?? 0) * 100))}%`));
431
- return _el$2;
432
- }
433
- }), _el$3);
434
- insert(_el$4, createComponent(Show, {
435
- get when() {
436
- return props.variant === "processing";
437
- },
438
- get children() {
439
- return createComponent(Spinner, {});
440
- }
441
- }), null);
442
- insert(_el$4, createComponent(Show, {
443
- get when() {
444
- return props.variant === "success";
445
- },
446
- get children() {
447
- return _tmpl$22();
448
- }
449
- }), null);
450
- insert(_el$4, createComponent(Show, {
451
- get when() {
452
- return props.variant === "success";
453
- },
454
- get children() {
455
- return _tmpl$32();
456
- }
457
- }), null);
458
- insert(_el$4, createComponent(Show, {
459
- get when() {
460
- return props.variant === "processing";
461
- },
462
- children: "Please wait\u2026"
463
- }), null);
464
- insert(_el$4, createComponent(Show, {
465
- get when() {
466
- return props.variant !== "processing";
467
- },
468
- get children() {
469
- return [(() => {
470
- var _el$7 = _tmpl$4();
471
- insert(_el$7, () => labelSegments().primary);
472
- return _el$7;
473
- })(), createComponent(Show, {
474
- get when() {
475
- return memo(() => props.variant === "hover")() && labelSegments().secondary !== "";
476
- },
477
- get children() {
478
- var _el$8 = _tmpl$5();
479
- insert(_el$8, () => labelSegments().secondary);
480
- return _el$8;
481
- }
482
- })];
483
- }
484
- }), null);
485
- insert(_el$4, createComponent(Show, {
486
- get when() {
487
- return props.variant === "success";
488
- },
489
- get children() {
490
- return _tmpl$6();
491
- }
492
- }), null);
493
- insert(_el$3, createComponent(Show, {
494
- get when() {
495
- return memo(() => props.variant === "hover")() && props.showHint;
496
- },
497
- get children() {
498
- return _tmpl$7();
499
- }
500
- }), null);
501
- effect((_p$) => {
502
- var _v$ = `${computedPosition().top}px`, _v$2 = `${computedPosition().left}px`, _v$3 = props.zIndex?.toString() ?? "2147483647", _v$4 = opacity();
503
- _v$ !== _p$.e && setStyleProperty(_el$, "top", _p$.e = _v$);
504
- _v$2 !== _p$.t && setStyleProperty(_el$, "left", _p$.t = _v$2);
505
- _v$3 !== _p$.a && setStyleProperty(_el$, "z-index", _p$.a = _v$3);
506
- _v$4 !== _p$.o && setStyleProperty(_el$, "opacity", _p$.o = _v$4);
507
- return _p$;
508
- }, {
509
- e: void 0,
510
- t: void 0,
511
- a: void 0,
512
- o: void 0
513
- });
514
- return _el$;
515
- }
516
- });
517
- };
518
- var _tmpl$9 = /* @__PURE__ */ template(`<canvas style=position:fixed;top:0;left:0;pointer-events:none;z-index:2147483645>`);
519
- var Crosshair = (props) => {
520
- let canvasRef;
521
- let context = null;
522
- let width = 0;
523
- let height = 0;
524
- let dpr = 1;
525
- const position = useAnimatedPosition({
526
- x: () => props.mouseX,
527
- y: () => props.mouseY,
528
- lerpFactor: 0.3
529
- });
530
- const setupCanvas = () => {
531
- if (!canvasRef) return;
532
- dpr = Math.max(window.devicePixelRatio || 1, 2);
533
- width = window.innerWidth;
534
- height = window.innerHeight;
535
- canvasRef.width = width * dpr;
536
- canvasRef.height = height * dpr;
537
- canvasRef.style.width = `${width}px`;
538
- canvasRef.style.height = `${height}px`;
539
- context = canvasRef.getContext("2d");
540
- if (context) {
541
- context.scale(dpr, dpr);
542
- }
543
- };
544
- const render2 = () => {
545
- if (!context) return;
546
- context.clearRect(0, 0, width, height);
547
- context.strokeStyle = "rgba(210, 57, 192)";
548
- context.lineWidth = 1;
549
- context.beginPath();
550
- context.moveTo(position.x(), 0);
551
- context.lineTo(position.x(), height);
552
- context.moveTo(0, position.y());
553
- context.lineTo(width, position.y());
554
- context.stroke();
555
- };
556
- createEffect(() => {
557
- setupCanvas();
558
- render2();
559
- const handleResize = () => {
560
- setupCanvas();
561
- render2();
562
- };
563
- window.addEventListener("resize", handleResize);
564
- onCleanup(() => {
565
- window.removeEventListener("resize", handleResize);
566
- });
567
- });
568
- createEffect(() => {
569
- position.x();
570
- position.y();
571
- render2();
572
- });
573
- return createComponent(Show, {
574
- get when() {
575
- return props.visible !== false;
576
- },
577
- get children() {
578
- var _el$ = _tmpl$9();
579
- var _ref$ = canvasRef;
580
- typeof _ref$ === "function" ? use(_ref$, _el$) : canvasRef = _el$;
581
- return _el$;
582
- }
583
- });
584
- };
585
- var _tmpl$10 = /* @__PURE__ */ template(`<div data-react-grab-input style="position:fixed;background-color:#fde7f7;color:#b21c8e;border:1px solid #f7c5ec;border-radius:4px;font-size:11px;font-weight:500;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;max-width:calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));overflow:hidden"><div style="position:relative;padding:2px 3px;display:flex;flex-direction:column;gap:2px"><textarea placeholder="e.g., Make this button larger"rows=1 style="width:240px;padding:2px 4px;background-color:#ffffff;color:#b21c8e;border:1px solid #f7c5ec;border-radius:3px;font-size:11px;line-height:1.2;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;outline:none;resize:vertical;min-height:18px"></textarea><div style=font-size:9px;opacity:0.6;text-align:center>Enter \u23CE to submit, Escape to cancel`);
586
- var InputOverlay = (props) => {
587
- let containerRef;
588
- let inputRef;
589
- const position = useAnimatedPosition({
590
- x: () => props.x,
591
- y: () => props.y,
592
- lerpFactor: 0.3
593
- });
594
- const containerBoundingRect = () => containerRef?.getBoundingClientRect();
595
- const computedPosition = () => {
596
- const boundingRect = containerBoundingRect();
597
- if (!boundingRect) return {
598
- left: position.x(),
599
- top: position.y()
600
- };
601
- const viewportWidth = window.innerWidth;
602
- const viewportHeight = window.innerHeight;
603
- const quadrants = getCursorQuadrants(position.x(), position.y(), boundingRect.width, boundingRect.height, CURSOR_OFFSET_PX);
604
- for (const position2 of quadrants) {
605
- const fitsHorizontally = position2.left >= VIEWPORT_MARGIN_PX && position2.left + boundingRect.width <= viewportWidth - VIEWPORT_MARGIN_PX;
606
- const fitsVertically = position2.top >= VIEWPORT_MARGIN_PX && position2.top + boundingRect.height <= viewportHeight - VIEWPORT_MARGIN_PX;
607
- if (fitsHorizontally && fitsVertically) {
608
- return position2;
609
- }
610
- }
611
- const fallback = getClampedElementPosition(quadrants[0].left, quadrants[0].top, boundingRect.width, boundingRect.height);
612
- fallback.left += INDICATOR_CLAMP_PADDING_PX;
613
- fallback.top += INDICATOR_CLAMP_PADDING_PX;
614
- return fallback;
615
- };
616
- const handleKeyDown = (event) => {
617
- if (event.code === "Enter" && !event.shiftKey) {
618
- event.preventDefault();
619
- event.stopPropagation();
620
- props.onSubmit();
621
- } else if (event.code === "Escape") {
622
- event.preventDefault();
623
- event.stopPropagation();
624
- props.onCancel();
625
- }
626
- };
627
- const handleInput = (event) => {
628
- const target = event.target;
629
- props.onInput(target.value);
630
- };
631
- createEffect(() => {
632
- if (props.visible && inputRef) {
633
- inputRef.focus();
634
- } else if (!props.visible && inputRef) {
635
- inputRef.blur();
636
- }
637
- });
638
- return (() => {
639
- var _el$ = _tmpl$10(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild; _el$3.nextSibling;
640
- var _ref$ = containerRef;
641
- typeof _ref$ === "function" ? use(_ref$, _el$) : containerRef = _el$;
642
- _el$3.$$keydown = handleKeyDown;
643
- _el$3.$$input = handleInput;
644
- var _ref$2 = inputRef;
645
- typeof _ref$2 === "function" ? use(_ref$2, _el$3) : inputRef = _el$3;
646
- effect((_p$) => {
647
- var _v$ = props.visible ? "block" : "none", _v$2 = `${computedPosition().top}px`, _v$3 = `${computedPosition().left}px`, _v$4 = props.zIndex?.toString() ?? "2147483647", _v$5 = props.visible ? "auto" : "none";
648
- _v$ !== _p$.e && setStyleProperty(_el$, "display", _p$.e = _v$);
649
- _v$2 !== _p$.t && setStyleProperty(_el$, "top", _p$.t = _v$2);
650
- _v$3 !== _p$.a && setStyleProperty(_el$, "left", _p$.a = _v$3);
651
- _v$4 !== _p$.o && setStyleProperty(_el$, "z-index", _p$.o = _v$4);
652
- _v$5 !== _p$.i && setStyleProperty(_el$, "pointer-events", _p$.i = _v$5);
653
- return _p$;
654
- }, {
655
- e: void 0,
656
- t: void 0,
657
- a: void 0,
658
- o: void 0,
659
- i: void 0
660
- });
661
- effect(() => _el$3.value = props.value);
662
- return _el$;
663
- })();
664
- };
665
- delegateEvents(["input", "keydown"]);
666
-
667
- // src/components/renderer.tsx
668
- var ReactGrabRenderer = (props) => {
669
- return [createComponent(Show, {
670
- get when() {
671
- return memo(() => !!props.selectionVisible)() && props.selectionBounds;
672
- },
673
- get children() {
674
- return createComponent(SelectionBox, {
675
- variant: "selection",
676
- get bounds() {
677
- return props.selectionBounds;
678
- },
679
- get visible() {
680
- return props.selectionVisible;
681
- }
682
- });
683
- }
684
- }), createComponent(Show, {
685
- get when() {
686
- return memo(() => !!(props.crosshairVisible === true && props.mouseX !== void 0))() && props.mouseY !== void 0;
687
- },
688
- get children() {
689
- return createComponent(Crosshair, {
690
- get mouseX() {
691
- return props.mouseX;
692
- },
693
- get mouseY() {
694
- return props.mouseY;
695
- },
696
- visible: true
697
- });
698
- }
699
- }), createComponent(Show, {
700
- get when() {
701
- return memo(() => !!props.dragVisible)() && props.dragBounds;
702
- },
703
- get children() {
704
- return createComponent(SelectionBox, {
705
- variant: "drag",
706
- get bounds() {
707
- return props.dragBounds;
708
- },
709
- get visible() {
710
- return props.dragVisible;
711
- }
712
- });
713
- }
714
- }), createComponent(For, {
715
- get each() {
716
- return props.grabbedBoxes ?? [];
717
- },
718
- children: (box) => createComponent(SelectionBox, {
719
- variant: "grabbed",
720
- get bounds() {
721
- return box.bounds;
722
- },
723
- get createdAt() {
724
- return box.createdAt;
725
- }
726
- })
727
- }), createComponent(Show, {
728
- get when() {
729
- return props.labelVariant !== "processing";
730
- },
731
- get children() {
732
- return createComponent(For, {
733
- get each() {
734
- return props.successLabels ?? [];
735
- },
736
- children: (label) => createComponent(Label, {
737
- variant: "success",
738
- get text() {
739
- return label.text;
740
- },
741
- get x() {
742
- return props.mouseX ?? 0;
743
- },
744
- get y() {
745
- return props.mouseY ?? 0;
746
- }
747
- })
748
- });
749
- }
750
- }), createComponent(Show, {
751
- get when() {
752
- return memo(() => !!(props.labelVisible && props.labelVariant && props.labelText && props.labelX !== void 0))() && props.labelY !== void 0;
753
- },
754
- get children() {
755
- return createComponent(Label, {
756
- get variant() {
757
- return props.labelVariant;
758
- },
759
- get text() {
760
- return props.labelText;
761
- },
762
- get x() {
763
- return props.labelX;
764
- },
765
- get y() {
766
- return props.labelY;
767
- },
768
- get visible() {
769
- return props.labelVisible;
770
- },
771
- get zIndex() {
772
- return props.labelZIndex;
773
- },
774
- get progress() {
775
- return props.progress;
776
- },
777
- get showHint() {
778
- return props.labelShowHint;
779
- }
780
- });
781
- }
782
- }), createComponent(InputOverlay, {
783
- get x() {
784
- return props.inputX ?? 0;
785
- },
786
- get y() {
787
- return props.inputY ?? 0;
788
- },
789
- get zIndex() {
790
- return props.labelZIndex;
791
- },
792
- get value() {
793
- return props.inputValue ?? "";
794
- },
795
- get visible() {
796
- return props.inputVisible ?? false;
797
- },
798
- get onInput() {
799
- return props.onInputChange;
800
- },
801
- get onSubmit() {
802
- return props.onInputSubmit;
803
- },
804
- get onCancel() {
805
- return props.onInputCancel;
806
- }
807
- })];
808
- };
809
-
810
- // src/utils/is-capitalized.ts
811
- var isCapitalized = (value) => value.length > 0 && /^[A-Z]/.test(value);
812
-
813
- // src/instrumentation.ts
814
- var NEXT_INTERNAL_COMPONENT_NAMES = [
815
- "InnerLayoutRouter",
816
- "RedirectErrorBoundary",
817
- "RedirectBoundary",
818
- "HTTPAccessFallbackErrorBoundary",
819
- "HTTPAccessFallbackBoundary",
820
- "LoadingBoundary",
821
- "ErrorBoundary",
822
- "InnerScrollAndFocusHandler",
823
- "ScrollAndFocusHandler",
824
- "RenderFromTemplateContext",
825
- "OuterLayoutRouter",
826
- "body",
827
- "html",
828
- "RedirectErrorBoundary",
829
- "RedirectBoundary",
830
- "HTTPAccessFallbackErrorBoundary",
831
- "HTTPAccessFallbackBoundary",
832
- "DevRootHTTPAccessFallbackBoundary",
833
- "AppDevOverlayErrorBoundary",
834
- "AppDevOverlay",
835
- "HotReload",
836
- "Router",
837
- "ErrorBoundaryHandler",
838
- "ErrorBoundary",
839
- "AppRouter",
840
- "ServerRoot",
841
- "SegmentStateProvider",
842
- "RootErrorBoundary"
843
- ];
844
- var checkIsNextProject = () => {
845
- return Boolean(document.getElementById("__NEXT_DATA__"));
846
- };
847
- var checkIsInternalComponentName = (name) => {
848
- if (name.startsWith("_")) return true;
849
- if (NEXT_INTERNAL_COMPONENT_NAMES.includes(name)) return true;
850
- return false;
851
- };
852
- var checkIsSourceComponentName = (name) => {
853
- if (checkIsInternalComponentName(name)) return false;
854
- if (!isCapitalized(name)) return false;
855
- if (name.startsWith("Primitive.")) return false;
856
- if (name.includes("Provider") && name.includes("Context")) return false;
857
- return true;
858
- };
859
- var getNearestComponentName = (element) => {
860
- if (!isInstrumentationActive()) return null;
861
- try {
862
- const fiber = getFiberFromHostInstance(element);
863
- if (!fiber) return null;
864
- let foundComponentName = null;
865
- traverseFiber(
866
- fiber,
867
- (currentFiber) => {
868
- if (isCompositeFiber(currentFiber)) {
869
- const displayName = getDisplayName(currentFiber);
870
- if (displayName && checkIsSourceComponentName(displayName)) {
871
- foundComponentName = displayName;
872
- return true;
873
- }
874
- }
875
- return false;
876
- },
877
- true
878
- );
879
- return foundComponentName;
880
- } catch {
881
- return null;
882
- }
883
- };
884
- var getStack = async (element) => {
885
- if (!isInstrumentationActive()) return [];
886
- try {
887
- const maybeFiber = getFiberFromHostInstance(element);
888
- if (!maybeFiber || !isFiber(maybeFiber)) return [];
889
- const fiber = getLatestFiber(maybeFiber);
890
- const unresolvedStack = [];
891
- traverseFiber(
892
- fiber,
893
- (currentFiber) => {
894
- const displayName = isHostFiber(currentFiber) ? typeof currentFiber.type === "string" ? currentFiber.type : null : getDisplayName(currentFiber);
895
- if (displayName && !checkIsInternalComponentName(displayName)) {
896
- unresolvedStack.push({
897
- name: displayName,
898
- sourcePromise: getSource(currentFiber)
899
- });
900
- }
901
- },
902
- true
903
- );
904
- const resolvedStack = await Promise.all(
905
- unresolvedStack.map(async (frame) => ({
906
- name: frame.name,
907
- source: await frame.sourcePromise
908
- }))
909
- );
910
- return resolvedStack.filter((frame) => frame.source !== null);
911
- } catch {
912
- return [];
913
- }
914
- };
915
- var formatStack = (stack) => {
916
- const isNextProject = checkIsNextProject();
917
- return stack.map(({ name, source }) => {
918
- if (!source) return ` at ${name}`;
919
- if (source.fileName.startsWith("about://React/Server")) {
920
- return ` at ${name} (Server)`;
921
- }
922
- if (!isSourceFile(source.fileName)) return ` at ${name}`;
923
- const framePart = ` at ${name} in ${normalizeFileName(source.fileName)}`;
924
- if (isNextProject) {
925
- return `${framePart}:${source.lineNumber}:${source.columnNumber}`;
926
- }
927
- return framePart;
928
- }).join("\n");
929
- };
930
- var getHTMLPreview = (element) => {
931
- const tagName = element.tagName.toLowerCase();
932
- if (!(element instanceof HTMLElement)) {
933
- return `<${tagName} />`;
934
- }
935
- const text = element.innerText?.trim() ?? element.textContent?.trim() ?? "";
936
- let attrsText = "";
937
- const attributes = Array.from(element.attributes);
938
- for (const attribute of attributes) {
939
- const name = attribute.name;
940
- let value = attribute.value;
941
- if (value.length > 20) {
942
- value = `${value.slice(0, 20)}...`;
943
- }
944
- attrsText += ` ${name}="${value}"`;
945
- }
946
- const topElements = [];
947
- const bottomElements = [];
948
- let foundFirstText = false;
949
- const childNodes = Array.from(element.childNodes);
950
- for (const node of childNodes) {
951
- if (node.nodeType === Node.COMMENT_NODE) continue;
952
- if (node.nodeType === Node.TEXT_NODE) {
953
- if (node.textContent && node.textContent.trim().length > 0) {
954
- foundFirstText = true;
955
- }
956
- } else if (node instanceof Element) {
957
- if (!foundFirstText) {
958
- topElements.push(node);
959
- } else {
960
- bottomElements.push(node);
961
- }
962
- }
963
- }
964
- const formatElements = (elements) => {
965
- if (elements.length === 0) return "";
966
- if (elements.length <= 2) {
967
- return elements.map((el) => `<${el.tagName.toLowerCase()} ...>`).join("\n ");
968
- }
969
- return `(${elements.length} elements)`;
970
- };
971
- let content = "";
972
- const topElementsStr = formatElements(topElements);
973
- if (topElementsStr) content += `
974
- ${topElementsStr}`;
975
- if (text.length > 0) {
976
- const truncatedText = text.length > 100 ? `${text.slice(0, 100)}...` : text;
977
- content += `
978
- ${truncatedText}`;
979
- }
980
- const bottomElementsStr = formatElements(bottomElements);
981
- if (bottomElementsStr) content += `
982
- ${bottomElementsStr}`;
983
- if (content.length > 0) {
984
- return `<${tagName}${attrsText}>${content}
985
- </${tagName}>`;
986
- }
987
- return `<${tagName}${attrsText} />`;
988
- };
989
-
990
- // src/utils/copy-content.ts
991
- var waitForFocus = () => {
992
- if (document.hasFocus()) {
993
- return new Promise((resolve) => setTimeout(resolve, 50));
994
- }
995
- return new Promise((resolve) => {
996
- const onFocus = () => {
997
- window.removeEventListener("focus", onFocus);
998
- setTimeout(resolve, 50);
999
- };
1000
- window.addEventListener("focus", onFocus);
1001
- window.focus();
1002
- });
1003
- };
1004
- var copyContent = async (content, onSuccess) => {
1005
- await waitForFocus();
1006
- try {
1007
- try {
1008
- await navigator.clipboard.writeText(content);
1009
- onSuccess?.();
1010
- return true;
1011
- } catch {
1012
- const result = copyContentFallback(content, onSuccess);
1013
- return result;
1014
- }
1015
- } catch {
1016
- return false;
1017
- }
1018
- };
1019
- var copyContentFallback = (content, onSuccess) => {
1020
- if (!document.execCommand) return false;
1021
- const el = document.createElement("textarea");
1022
- el.value = String(content);
1023
- el.style.clipPath = "inset(50%)";
1024
- el.ariaHidden = "true";
1025
- const doc = document.body || document.documentElement;
1026
- doc.append(el);
1027
- try {
1028
- el.select();
1029
- const result = document.execCommand("copy");
1030
- if (result) onSuccess?.();
1031
- return result;
1032
- } finally {
1033
- el.remove();
1034
- }
1035
- };
1036
-
1037
- // src/utils/play-copy-sound.ts
1038
- var playCopySound = () => {
1039
- try {
1040
- const audioContext = new (window.AudioContext || // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- window.webkitAudioContext is not typed
1041
- window.webkitAudioContext)();
1042
- const masterGain = audioContext.createGain();
1043
- masterGain.connect(audioContext.destination);
1044
- const notes = [
1045
- { freq: 523.25, start: 0, duration: 0.1 },
1046
- { freq: 659.25, start: 0.05, duration: 0.1 },
1047
- { freq: 783.99, start: 0.1, duration: 0.15 }
1048
- ];
1049
- notes.forEach((note) => {
1050
- const oscillator = audioContext.createOscillator();
1051
- const gainNode = audioContext.createGain();
1052
- oscillator.connect(gainNode);
1053
- gainNode.connect(masterGain);
1054
- oscillator.frequency.value = note.freq;
1055
- oscillator.type = "triangle";
1056
- const startTime = audioContext.currentTime + note.start;
1057
- const peakTime = startTime + 0.01;
1058
- const endTime = startTime + note.duration;
1059
- gainNode.gain.setValueAtTime(0, startTime);
1060
- gainNode.gain.linearRampToValueAtTime(0.15, peakTime);
1061
- gainNode.gain.exponentialRampToValueAtTime(0.01, endTime);
1062
- oscillator.start(startTime);
1063
- oscillator.stop(endTime);
1064
- });
1065
- } catch {
1066
- }
1067
- };
1068
-
1069
- // src/utils/is-element-visible.ts
1070
- var isElementVisible = (element, computedStyle = window.getComputedStyle(element)) => {
1071
- return computedStyle.display !== "none" && computedStyle.visibility !== "hidden" && computedStyle.opacity !== "0";
1072
- };
1073
-
1074
- // src/utils/is-valid-grabbable-element.ts
1075
- var isValidGrabbableElement = (element) => {
1076
- if (element.closest(`[${ATTRIBUTE_NAME}]`)) {
1077
- return false;
1078
- }
1079
- const computedStyle = window.getComputedStyle(element);
1080
- if (!isElementVisible(element, computedStyle)) {
1081
- return false;
1082
- }
1083
- if (computedStyle.pointerEvents === "none") {
1084
- return false;
1085
- }
1086
- return true;
1087
- };
1088
-
1089
- // src/utils/get-element-at-position.ts
1090
- var getElementAtPosition = (clientX, clientY) => {
1091
- const elementsAtPoint = document.elementsFromPoint(clientX, clientY);
1092
- for (const candidateElement of elementsAtPoint) {
1093
- if (isValidGrabbableElement(candidateElement)) {
1094
- return candidateElement;
1095
- }
1096
- }
1097
- return null;
1098
- };
1099
-
1100
- // src/utils/get-elements-in-drag.ts
1101
- var DRAG_COVERAGE_THRESHOLD = 0.75;
1102
- var calculateIntersectionArea = (rect1, rect2) => {
1103
- const intersectionLeft = Math.max(rect1.left, rect2.left);
1104
- const intersectionTop = Math.max(rect1.top, rect2.top);
1105
- const intersectionRight = Math.min(rect1.right, rect2.right);
1106
- const intersectionBottom = Math.min(rect1.bottom, rect2.bottom);
1107
- const intersectionWidth = Math.max(0, intersectionRight - intersectionLeft);
1108
- const intersectionHeight = Math.max(0, intersectionBottom - intersectionTop);
1109
- return intersectionWidth * intersectionHeight;
1110
- };
1111
- var hasIntersection = (rect1, rect2) => {
1112
- return rect1.left < rect2.right && rect1.right > rect2.left && rect1.top < rect2.bottom && rect1.bottom > rect2.top;
1113
- };
1114
- var filterElementsInDrag = (dragRect, isValidGrabbableElement2, shouldCheckCoverage) => {
1115
- const elements = [];
1116
- const allElements = Array.from(document.querySelectorAll("*"));
1117
- const dragBounds = {
1118
- left: dragRect.x,
1119
- top: dragRect.y,
1120
- right: dragRect.x + dragRect.width,
1121
- bottom: dragRect.y + dragRect.height
1122
- };
1123
- for (const candidateElement of allElements) {
1124
- if (!shouldCheckCoverage) {
1125
- const tagName = (candidateElement.tagName || "").toUpperCase();
1126
- if (tagName === "HTML" || tagName === "BODY") continue;
1127
- }
1128
- if (!isValidGrabbableElement2(candidateElement)) {
1129
- continue;
1130
- }
1131
- const elementRect = candidateElement.getBoundingClientRect();
1132
- const elementBounds = {
1133
- left: elementRect.left,
1134
- top: elementRect.top,
1135
- right: elementRect.left + elementRect.width,
1136
- bottom: elementRect.top + elementRect.height
1137
- };
1138
- if (shouldCheckCoverage) {
1139
- const intersectionArea = calculateIntersectionArea(dragBounds, elementBounds);
1140
- const elementArea = Math.max(0, elementRect.width * elementRect.height);
1141
- const hasMajorityCoverage = elementArea > 0 && intersectionArea / elementArea >= DRAG_COVERAGE_THRESHOLD;
1142
- if (hasMajorityCoverage) {
1143
- elements.push(candidateElement);
1144
- }
1145
- } else {
1146
- if (hasIntersection(elementBounds, dragBounds)) {
1147
- elements.push(candidateElement);
1148
- }
1149
- }
1150
- }
1151
- return elements;
1152
- };
1153
- var removeNestedElements = (elements) => {
1154
- return elements.filter((element) => {
1155
- return !elements.some(
1156
- (otherElement) => otherElement !== element && otherElement.contains(element)
1157
- );
1158
- });
1159
- };
1160
- var getElementsInDrag = (dragRect, isValidGrabbableElement2) => {
1161
- const elements = filterElementsInDrag(dragRect, isValidGrabbableElement2, true);
1162
- const uniqueElements = removeNestedElements(elements);
1163
- return uniqueElements;
1164
- };
1165
- var getElementsInDragLoose = (dragRect, isValidGrabbableElement2) => {
1166
- const elements = filterElementsInDrag(dragRect, isValidGrabbableElement2, false);
1167
- const uniqueElements = removeNestedElements(elements);
1168
- return uniqueElements;
1169
- };
1170
-
1171
- // src/utils/create-element-bounds.ts
1172
- var createElementBounds = (element) => {
1173
- const boundingRect = element.getBoundingClientRect();
1174
- const computedStyle = window.getComputedStyle(element);
1175
- return {
1176
- borderRadius: computedStyle.borderRadius || "0px",
1177
- height: boundingRect.height,
1178
- transform: computedStyle.transform || "none",
1179
- width: boundingRect.width,
1180
- x: boundingRect.left,
1181
- y: boundingRect.top
1182
- };
1183
- };
1184
-
1185
- // src/core.tsx
1186
- var hasInited = false;
1187
- var init = (rawOptions) => {
1188
- const options = {
1189
- enabled: true,
1190
- keyHoldDuration: 300,
1191
- allowActivationInsideInput: true,
1192
- playCopySound: false,
1193
- ...rawOptions
1194
- };
1195
- if (options.enabled === false || hasInited) {
1196
- return {
1197
- activate: () => {
1198
- },
1199
- deactivate: () => {
1200
- },
1201
- toggle: () => {
1202
- },
1203
- isActive: () => false,
1204
- dispose: () => {
1205
- }
1206
- };
1207
- }
1208
- hasInited = true;
1209
- const logIntro = () => {
1210
- try {
1211
- const version = "0.0.48";
1212
- const logoSvg = `<svg width="294" height="294" viewBox="0 0 294 294" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_0_3)"><mask id="mask0_0_3" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="294" height="294"><path d="M294 0H0V294H294V0Z" fill="white"/></mask><g mask="url(#mask0_0_3)"><path d="M144.599 47.4924C169.712 27.3959 194.548 20.0265 212.132 30.1797C227.847 39.2555 234.881 60.3243 231.926 89.516C231.677 92.0069 231.328 94.5423 230.94 97.1058L228.526 110.14C228.517 110.136 228.505 110.132 228.495 110.127C228.486 110.165 228.479 110.203 228.468 110.24L216.255 105.741C216.256 105.736 216.248 105.728 216.248 105.723C207.915 103.125 199.421 101.075 190.82 99.5888L190.696 99.5588L173.526 97.2648L173.511 97.2631C173.492 97.236 173.467 97.2176 173.447 97.1905C163.862 96.2064 154.233 95.7166 144.599 95.7223C134.943 95.7162 125.295 96.219 115.693 97.2286C110.075 105.033 104.859 113.118 100.063 121.453C95.2426 129.798 90.8624 138.391 86.939 147.193C90.8624 155.996 95.2426 164.588 100.063 172.933C104.866 181.302 110.099 189.417 115.741 197.245C115.749 197.245 115.758 197.246 115.766 197.247L115.752 197.27L115.745 197.283L115.754 197.296L126.501 211.013L126.574 211.089C132.136 217.767 138.126 224.075 144.507 229.974L144.609 230.082L154.572 238.287C154.539 238.319 154.506 238.35 154.472 238.38C154.485 238.392 154.499 238.402 154.513 238.412L143.846 247.482L143.827 247.497C126.56 261.128 109.472 268.745 94.8019 268.745C88.5916 268.837 82.4687 267.272 77.0657 264.208C61.3496 255.132 54.3164 234.062 57.2707 204.871C57.528 202.307 57.8806 199.694 58.2904 197.054C28.3363 185.327 9.52301 167.51 9.52301 147.193C9.52301 129.042 24.2476 112.396 50.9901 100.375C53.3443 99.3163 55.7938 98.3058 58.2904 97.3526C57.8806 94.7023 57.528 92.0803 57.2707 89.516C54.3164 60.3243 61.3496 39.2555 77.0657 30.1797C94.6494 20.0265 119.486 27.3959 144.599 47.4924ZM70.6423 201.315C70.423 202.955 70.2229 204.566 70.0704 206.168C67.6686 229.567 72.5478 246.628 83.3615 252.988L83.5176 253.062C95.0399 259.717 114.015 254.426 134.782 238.38C125.298 229.45 116.594 219.725 108.764 209.314C95.8516 207.742 83.0977 205.066 70.6423 201.315ZM80.3534 163.438C77.34 171.677 74.8666 180.104 72.9484 188.664C81.1787 191.224 89.5657 193.247 98.0572 194.724L98.4618 194.813C95.2115 189.865 92.0191 184.66 88.9311 179.378C85.8433 174.097 83.003 168.768 80.3534 163.438ZM60.759 110.203C59.234 110.839 57.7378 111.475 56.27 112.11C34.7788 121.806 22.3891 134.591 22.3891 147.193C22.3891 160.493 36.4657 174.297 60.7494 184.26C63.7439 171.581 67.8124 159.182 72.9104 147.193C67.822 135.23 63.7566 122.855 60.759 110.203ZM98.4137 99.6404C89.8078 101.145 81.3075 103.206 72.9676 105.809C74.854 114.203 77.2741 122.468 80.2132 130.554L80.3059 130.939C82.9938 125.6 85.8049 120.338 88.8834 115.008C91.9618 109.679 95.1544 104.569 98.4137 99.6404ZM94.9258 38.5215C90.9331 38.4284 86.9866 39.3955 83.4891 41.3243C72.6291 47.6015 67.6975 64.5954 70.0424 87.9446L70.0416 88.2194C70.194 89.8208 70.3941 91.4325 70.6134 93.0624C83.0737 89.3364 95.8263 86.6703 108.736 85.0924C116.57 74.6779 125.28 64.9532 134.773 56.0249C119.877 44.5087 105.895 38.5215 94.9258 38.5215ZM205.737 41.3148C202.268 39.398 198.355 38.4308 194.394 38.5099L194.29 38.512C183.321 38.512 169.34 44.4991 154.444 56.0153C163.93 64.9374 172.634 74.6557 180.462 85.064C193.375 86.6345 206.128 89.3102 218.584 93.0624C218.812 91.4325 219.003 89.8118 219.165 88.2098C221.548 64.7099 216.65 47.6164 205.737 41.3148ZM144.552 64.3097C138.104 70.2614 132.054 76.6306 126.443 83.3765C132.39 82.995 138.426 82.8046 144.552 82.8046C150.727 82.8046 156.778 83.0143 162.707 83.3765C157.08 76.6293 151.015 70.2596 144.552 64.3097Z" fill="white"/><path d="M144.598 47.4924C169.712 27.3959 194.547 20.0265 212.131 30.1797C227.847 39.2555 234.88 60.3243 231.926 89.516C231.677 92.0069 231.327 94.5423 230.941 97.1058L228.526 110.14L228.496 110.127C228.487 110.165 228.478 110.203 228.469 110.24L216.255 105.741L216.249 105.723C207.916 103.125 199.42 101.075 190.82 99.5888L190.696 99.5588L173.525 97.2648L173.511 97.263C173.492 97.236 173.468 97.2176 173.447 97.1905C163.863 96.2064 154.234 95.7166 144.598 95.7223C134.943 95.7162 125.295 96.219 115.693 97.2286C110.075 105.033 104.859 113.118 100.063 121.453C95.2426 129.798 90.8622 138.391 86.939 147.193C90.8622 155.996 95.2426 164.588 100.063 172.933C104.866 181.302 110.099 189.417 115.741 197.245L115.766 197.247L115.752 197.27L115.745 197.283L115.754 197.296L126.501 211.013L126.574 211.089C132.136 217.767 138.126 224.075 144.506 229.974L144.61 230.082L154.572 238.287C154.539 238.319 154.506 238.35 154.473 238.38L154.512 238.412L143.847 247.482L143.827 247.497C126.56 261.13 109.472 268.745 94.8018 268.745C88.5915 268.837 82.4687 267.272 77.0657 264.208C61.3496 255.132 54.3162 234.062 57.2707 204.871C57.528 202.307 57.8806 199.694 58.2904 197.054C28.3362 185.327 9.52298 167.51 9.52298 147.193C9.52298 129.042 24.2476 112.396 50.9901 100.375C53.3443 99.3163 55.7938 98.3058 58.2904 97.3526C57.8806 94.7023 57.528 92.0803 57.2707 89.516C54.3162 60.3243 61.3496 39.2555 77.0657 30.1797C94.6493 20.0265 119.486 27.3959 144.598 47.4924ZM70.6422 201.315C70.423 202.955 70.2229 204.566 70.0704 206.168C67.6686 229.567 72.5478 246.628 83.3615 252.988L83.5175 253.062C95.0399 259.717 114.015 254.426 134.782 238.38C125.298 229.45 116.594 219.725 108.764 209.314C95.8515 207.742 83.0977 205.066 70.6422 201.315ZM80.3534 163.438C77.34 171.677 74.8666 180.104 72.9484 188.664C81.1786 191.224 89.5657 193.247 98.0572 194.724L98.4618 194.813C95.2115 189.865 92.0191 184.66 88.931 179.378C85.8433 174.097 83.003 168.768 80.3534 163.438ZM60.7589 110.203C59.234 110.839 57.7378 111.475 56.2699 112.11C34.7788 121.806 22.3891 134.591 22.3891 147.193C22.3891 160.493 36.4657 174.297 60.7494 184.26C63.7439 171.581 67.8124 159.182 72.9103 147.193C67.822 135.23 63.7566 122.855 60.7589 110.203ZM98.4137 99.6404C89.8078 101.145 81.3075 103.206 72.9676 105.809C74.8539 114.203 77.2741 122.468 80.2132 130.554L80.3059 130.939C82.9938 125.6 85.8049 120.338 88.8834 115.008C91.9618 109.679 95.1544 104.569 98.4137 99.6404ZM94.9258 38.5215C90.9331 38.4284 86.9866 39.3955 83.4891 41.3243C72.629 47.6015 67.6975 64.5954 70.0424 87.9446L70.0415 88.2194C70.194 89.8208 70.3941 91.4325 70.6134 93.0624C83.0737 89.3364 95.8262 86.6703 108.736 85.0924C116.57 74.6779 125.28 64.9532 134.772 56.0249C119.877 44.5087 105.895 38.5215 94.9258 38.5215ZM205.737 41.3148C202.268 39.398 198.355 38.4308 194.394 38.5099L194.291 38.512C183.321 38.512 169.34 44.4991 154.443 56.0153C163.929 64.9374 172.634 74.6557 180.462 85.064C193.374 86.6345 206.129 89.3102 218.584 93.0624C218.813 91.4325 219.003 89.8118 219.166 88.2098C221.548 64.7099 216.65 47.6164 205.737 41.3148ZM144.551 64.3097C138.103 70.2614 132.055 76.6306 126.443 83.3765C132.389 82.995 138.427 82.8046 144.551 82.8046C150.727 82.8046 156.779 83.0143 162.707 83.3765C157.079 76.6293 151.015 70.2596 144.551 64.3097Z" fill="#FF40E0"/></g><mask id="mask1_0_3" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="102" y="84" width="161" height="162"><path d="M235.282 84.827L102.261 112.259L129.693 245.28L262.714 217.848L235.282 84.827Z" fill="white"/></mask><g mask="url(#mask1_0_3)"><path d="M136.863 129.916L213.258 141.224C220.669 142.322 222.495 152.179 215.967 155.856L187.592 171.843L184.135 204.227C183.339 211.678 173.564 213.901 169.624 207.526L129.021 141.831C125.503 136.14 130.245 128.936 136.863 129.916Z" fill="#FF40E0" stroke="#FF40E0" stroke-width="0.817337" stroke-linecap="round" stroke-linejoin="round"/></g></g><defs><clipPath id="clip0_0_3"><rect width="294" height="294" fill="white"/></clipPath></defs></svg>`;
1213
- const logoDataUri = `data:image/svg+xml;base64,${btoa(logoSvg)}`;
1214
- console.log(`%cReact Grab${version ? ` v${version}` : ""}%c
1215
- https://react-grab.com`, `background: #330039; color: #ffffff; border: 1px solid #d75fcb; padding: 4px 4px 4px 24px; border-radius: 4px; background-image: url("${logoDataUri}"); background-size: 16px 16px; background-repeat: no-repeat; background-position: 4px center; display: inline-block; margin-bottom: 4px;`, "");
1216
- if (navigator.onLine) {
1217
- fetch("https://www.react-grab.com/api/version", {
1218
- referrerPolicy: "origin",
1219
- keepalive: true,
1220
- priority: "low",
1221
- cache: "no-store"
1222
- }).then((res) => res.text()).catch(() => null);
1223
- }
1224
- } catch {
1225
- }
1226
- };
1227
- logIntro();
1228
- return createRoot((dispose) => {
1229
- const [isHoldingKeys, setIsHoldingKeys] = createSignal(false);
1230
- const [mouseX, setMouseX] = createSignal(OFFSCREEN_POSITION);
1231
- const [mouseY, setMouseY] = createSignal(OFFSCREEN_POSITION);
1232
- const [isDragging, setIsDragging] = createSignal(false);
1233
- const [dragStartX, setDragStartX] = createSignal(OFFSCREEN_POSITION);
1234
- const [dragStartY, setDragStartY] = createSignal(OFFSCREEN_POSITION);
1235
- const [isCopying, setIsCopying] = createSignal(false);
1236
- const [lastGrabbedElement, setLastGrabbedElement] = createSignal(null);
1237
- const [progressStartTime, setProgressStartTime] = createSignal(null);
1238
- const [progress, setProgress] = createSignal(0);
1239
- const [grabbedBoxes, setGrabbedBoxes] = createSignal([]);
1240
- const [successLabels, setSuccessLabels] = createSignal([]);
1241
- const [isActivated, setIsActivated] = createSignal(false);
1242
- const [isToggleMode, setIsToggleMode] = createSignal(false);
1243
- const [showProgressIndicator, setShowProgressIndicator] = createSignal(false);
1244
- const [didJustDrag, setDidJustDrag] = createSignal(false);
1245
- const [copyStartX, setCopyStartX] = createSignal(OFFSCREEN_POSITION);
1246
- const [copyStartY, setCopyStartY] = createSignal(OFFSCREEN_POSITION);
1247
- const [mouseHasSettled, setMouseHasSettled] = createSignal(false);
1248
- const [viewportVersion, setViewportVersion] = createSignal(0);
1249
- const [isInputMode, setIsInputMode] = createSignal(false);
1250
- const [inputText, setInputText] = createSignal("");
1251
- let holdTimerId = null;
1252
- let progressAnimationId = null;
1253
- let progressDelayTimerId = null;
1254
- let keydownSpamTimerId = null;
1255
- let mouseSettleTimerId = null;
1256
- let autoScrollAnimationId = null;
1257
- const isRendererActive = createMemo(() => isActivated() && !isCopying());
1258
- const hasValidMousePosition = createMemo(() => mouseX() > OFFSCREEN_POSITION && mouseY() > OFFSCREEN_POSITION);
1259
- const isTargetKeyCombination = (event) => (
1260
- // NOTE: we use event.code instead of event.key for keyboard layout compatibility (e.g., AZERTY, QWERTZ)
1261
- (event.metaKey || event.ctrlKey) && event.code === "KeyC"
1262
- );
1263
- const getAutoScrollDirection = (clientX, clientY) => {
1264
- return {
1265
- top: clientY < AUTO_SCROLL_EDGE_THRESHOLD_PX,
1266
- bottom: clientY > window.innerHeight - AUTO_SCROLL_EDGE_THRESHOLD_PX,
1267
- left: clientX < AUTO_SCROLL_EDGE_THRESHOLD_PX,
1268
- right: clientX > window.innerWidth - AUTO_SCROLL_EDGE_THRESHOLD_PX
1269
- };
1270
- };
1271
- const showTemporaryGrabbedBox = (bounds, element) => {
1272
- const boxId = `grabbed-${Date.now()}-${Math.random()}`;
1273
- const createdAt = Date.now();
1274
- const newBox = {
1275
- id: boxId,
1276
- bounds,
1277
- createdAt,
1278
- element
1279
- };
1280
- const currentBoxes = grabbedBoxes();
1281
- setGrabbedBoxes([...currentBoxes, newBox]);
1282
- setTimeout(() => {
1283
- setGrabbedBoxes((previousBoxes) => previousBoxes.filter((box) => box.id !== boxId));
1284
- }, SUCCESS_LABEL_DURATION_MS);
1285
- };
1286
- const showTemporarySuccessLabel = (text) => {
1287
- const labelId = `success-${Date.now()}-${Math.random()}`;
1288
- setSuccessLabels((previousLabels) => [...previousLabels, {
1289
- id: labelId,
1290
- text
1291
- }]);
1292
- setTimeout(() => {
1293
- setSuccessLabels((previousLabels) => previousLabels.filter((label) => label.id !== labelId));
1294
- }, SUCCESS_LABEL_DURATION_MS);
1295
- };
1296
- const wrapInSelectedElementTags = (context) => `<selected_element>
1297
- ${context}
1298
- </selected_element>`;
1299
- const extractElementTagName = (element) => (element.tagName || "").toLowerCase();
1300
- const extractElementLabelText = (element) => {
1301
- const tagName = extractElementTagName(element);
1302
- const componentName = getNearestComponentName(element);
1303
- if (tagName && componentName) {
1304
- return `<${tagName}> in ${componentName}`;
1305
- }
1306
- if (tagName) {
1307
- return `<${tagName}>`;
1308
- }
1309
- return "<element>";
1310
- };
1311
- const notifyElementsSelected = (elements) => {
1312
- try {
1313
- const elementsPayload = elements.map((element) => ({
1314
- tagName: extractElementTagName(element)
1315
- }));
1316
- window.dispatchEvent(new CustomEvent("react-grab:element-selected", {
1317
- detail: {
1318
- elements: elementsPayload
1319
- }
1320
- }));
1321
- } catch {
1322
- }
1323
- };
1324
- const executeCopyOperation = async (positionX, positionY, operation) => {
1325
- setCopyStartX(positionX);
1326
- setCopyStartY(positionY);
1327
- setIsCopying(true);
1328
- startProgressAnimation();
1329
- await operation().finally(() => {
1330
- setIsCopying(false);
1331
- stopProgressAnimation();
1332
- if (isToggleMode()) {
1333
- deactivateRenderer();
1334
- }
1335
- });
1336
- };
1337
- const hasInnerText = (element) => "innerText" in element;
1338
- const extractElementTextContent = (element) => {
1339
- if (hasInnerText(element)) {
1340
- return element.innerText;
1341
- }
1342
- return element.textContent ?? "";
1343
- };
1344
- const createCombinedTextContent = (elements) => elements.map((element) => extractElementTextContent(element).trim()).filter((textContent) => textContent.length > 0).join("\n\n");
1345
- const tryCopyWithFallback = async (elements, extraPrompt) => {
1346
- let didCopy = false;
1347
- const isReactProject = isInstrumentationActive();
1348
- try {
1349
- const elementSnippetResults = await Promise.allSettled(elements.map(async (element) => {
1350
- const htmlPreview = getHTMLPreview(element);
1351
- if (!isReactProject) {
1352
- return `## HTML Frame:
1353
- ${htmlPreview}`;
1354
- }
1355
- const stack = await getStack(element);
1356
- const formattedStack = formatStack(stack);
1357
- if (formattedStack) {
1358
- return `## HTML Frame:
1359
- ${htmlPreview}
1360
-
1361
- ## Code Location:
1362
- ${formattedStack}`;
1363
- }
1364
- return `## HTML Frame:
1365
- ${htmlPreview}`;
1366
- }));
1367
- const elementSnippets = elementSnippetResults.map((result) => result.status === "fulfilled" ? result.value : "").filter((snippet) => snippet.trim());
1368
- if (elementSnippets.length > 0) {
1369
- const wrappedSnippets = elementSnippets.map((snippet) => wrapInSelectedElementTags(snippet)).join("\n\n");
1370
- const plainTextContent = extraPrompt ? `${extraPrompt}
1371
-
1372
- ${wrappedSnippets}` : wrappedSnippets;
1373
- didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
1374
- }
1375
- if (!didCopy) {
1376
- const plainTextContentOnly = createCombinedTextContent(elements);
1377
- if (plainTextContentOnly.length > 0) {
1378
- const contentWithPrompt = extraPrompt ? `${extraPrompt}
1379
-
1380
- ${plainTextContentOnly}` : plainTextContentOnly;
1381
- didCopy = await copyContent(contentWithPrompt, options.playCopySound ? playCopySound : void 0);
1382
- }
1383
- }
1384
- } catch {
1385
- const plainTextContentOnly = createCombinedTextContent(elements);
1386
- if (plainTextContentOnly.length > 0) {
1387
- const contentWithPrompt = extraPrompt ? `${extraPrompt}
1388
-
1389
- ${plainTextContentOnly}` : plainTextContentOnly;
1390
- didCopy = await copyContent(contentWithPrompt, options.playCopySound ? playCopySound : void 0);
1391
- }
1392
- }
1393
- return didCopy;
1394
- };
1395
- const copySingleElementToClipboard = async (targetElement2, extraPrompt) => {
1396
- showTemporaryGrabbedBox(createElementBounds(targetElement2), targetElement2);
1397
- await new Promise((resolve) => requestAnimationFrame(resolve));
1398
- const didCopy = await tryCopyWithFallback([targetElement2], extraPrompt);
1399
- if (didCopy) {
1400
- showTemporarySuccessLabel(extractElementLabelText(targetElement2));
1401
- }
1402
- notifyElementsSelected([targetElement2]);
1403
- };
1404
- const copyMultipleElementsToClipboard = async (targetElements) => {
1405
- if (targetElements.length === 0) return;
1406
- for (const element of targetElements) {
1407
- showTemporaryGrabbedBox(createElementBounds(element), element);
1408
- }
1409
- await new Promise((resolve) => requestAnimationFrame(resolve));
1410
- const didCopy = await tryCopyWithFallback(targetElements);
1411
- if (didCopy) {
1412
- showTemporarySuccessLabel(`${targetElements.length} elements`);
1413
- }
1414
- notifyElementsSelected(targetElements);
1415
- };
1416
- const targetElement = createMemo(() => {
1417
- if (!isRendererActive() || isDragging()) return null;
1418
- return getElementAtPosition(mouseX(), mouseY());
1419
- });
1420
- const selectionBounds = createMemo(() => {
1421
- viewportVersion();
1422
- const element = targetElement();
1423
- if (!element) return void 0;
1424
- const elementBounds = element.getBoundingClientRect();
1425
- const computedStyle = window.getComputedStyle(element);
1426
- return {
1427
- borderRadius: computedStyle.borderRadius || "0px",
1428
- height: elementBounds.height,
1429
- transform: computedStyle.transform || "none",
1430
- width: elementBounds.width,
1431
- x: elementBounds.left,
1432
- y: elementBounds.top
1433
- };
1434
- });
1435
- const calculateDragDistance = (endX, endY) => {
1436
- const endPageX = endX + window.scrollX;
1437
- const endPageY = endY + window.scrollY;
1438
- return {
1439
- x: Math.abs(endPageX - dragStartX()),
1440
- y: Math.abs(endPageY - dragStartY())
1441
- };
1442
- };
1443
- const isDraggingBeyondThreshold = createMemo(() => {
1444
- if (!isDragging()) return false;
1445
- const dragDistance = calculateDragDistance(mouseX(), mouseY());
1446
- return dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
1447
- });
1448
- const calculateDragRectangle = (endX, endY) => {
1449
- const endPageX = endX + window.scrollX;
1450
- const endPageY = endY + window.scrollY;
1451
- const dragPageX = Math.min(dragStartX(), endPageX);
1452
- const dragPageY = Math.min(dragStartY(), endPageY);
1453
- const dragWidth = Math.abs(endPageX - dragStartX());
1454
- const dragHeight = Math.abs(endPageY - dragStartY());
1455
- return {
1456
- x: dragPageX - window.scrollX,
1457
- y: dragPageY - window.scrollY,
1458
- width: dragWidth,
1459
- height: dragHeight
1460
- };
1461
- };
1462
- const dragBounds = createMemo(() => {
1463
- if (!isDraggingBeyondThreshold()) return void 0;
1464
- const drag = calculateDragRectangle(mouseX(), mouseY());
1465
- return {
1466
- borderRadius: "0px",
1467
- height: drag.height,
1468
- transform: "none",
1469
- width: drag.width,
1470
- x: drag.x,
1471
- y: drag.y
1472
- };
1473
- });
1474
- const labelText = createMemo(() => {
1475
- const element = targetElement();
1476
- return element ? extractElementLabelText(element) : "<element>";
1477
- });
1478
- const labelPosition = createMemo(() => isCopying() ? {
1479
- x: copyStartX(),
1480
- y: copyStartY()
1481
- } : {
1482
- x: mouseX(),
1483
- y: mouseY()
1484
- });
1485
- const progressPosition = createMemo(() => isCopying() ? {
1486
- x: copyStartX(),
1487
- y: copyStartY()
1488
- } : {
1489
- x: mouseX(),
1490
- y: mouseY()
1491
- });
1492
- createEffect(on(() => [targetElement(), lastGrabbedElement()], ([currentElement, lastElement]) => {
1493
- if (lastElement && currentElement && lastElement !== currentElement) {
1494
- setLastGrabbedElement(null);
1495
- }
1496
- }));
1497
- createEffect(on(() => viewportVersion(), () => {
1498
- const currentBoxes = grabbedBoxes();
1499
- if (currentBoxes.length === 0) return;
1500
- const updatedBoxes = currentBoxes.map((box) => ({
1501
- ...box,
1502
- bounds: createElementBounds(box.element)
1503
- }));
1504
- setGrabbedBoxes(updatedBoxes);
1505
- }));
1506
- const startProgressAnimation = () => {
1507
- const startTime = Date.now();
1508
- setProgressStartTime(startTime);
1509
- setShowProgressIndicator(false);
1510
- progressDelayTimerId = window.setTimeout(() => {
1511
- setShowProgressIndicator(true);
1512
- progressDelayTimerId = null;
1513
- }, PROGRESS_INDICATOR_DELAY_MS);
1514
- const animateProgress = () => {
1515
- const currentStartTime = progressStartTime();
1516
- if (currentStartTime === null) return;
1517
- const elapsedTime = Date.now() - currentStartTime;
1518
- const normalizedTime = elapsedTime / options.keyHoldDuration;
1519
- const easedProgress = 1 - Math.exp(-normalizedTime);
1520
- const maxProgressBeforeCompletion = 0.95;
1521
- const currentProgress = isCopying() ? Math.min(easedProgress, maxProgressBeforeCompletion) : 1;
1522
- setProgress(currentProgress);
1523
- if (currentProgress < 1) {
1524
- progressAnimationId = requestAnimationFrame(animateProgress);
1525
- }
1526
- };
1527
- animateProgress();
1528
- };
1529
- const stopProgressAnimation = () => {
1530
- if (progressAnimationId !== null) {
1531
- cancelAnimationFrame(progressAnimationId);
1532
- progressAnimationId = null;
1533
- }
1534
- if (progressDelayTimerId !== null) {
1535
- window.clearTimeout(progressDelayTimerId);
1536
- progressDelayTimerId = null;
1537
- }
1538
- setProgressStartTime(null);
1539
- setProgress(1);
1540
- setShowProgressIndicator(false);
1541
- };
1542
- const startAutoScroll = () => {
1543
- const scroll = () => {
1544
- if (!isDragging()) {
1545
- stopAutoScroll();
1546
- return;
1547
- }
1548
- const direction = getAutoScrollDirection(mouseX(), mouseY());
1549
- if (direction.top) window.scrollBy(0, -AUTO_SCROLL_SPEED_PX);
1550
- if (direction.bottom) window.scrollBy(0, AUTO_SCROLL_SPEED_PX);
1551
- if (direction.left) window.scrollBy(-AUTO_SCROLL_SPEED_PX, 0);
1552
- if (direction.right) window.scrollBy(AUTO_SCROLL_SPEED_PX, 0);
1553
- if (direction.top || direction.bottom || direction.left || direction.right) {
1554
- autoScrollAnimationId = requestAnimationFrame(scroll);
1555
- } else {
1556
- autoScrollAnimationId = null;
1557
- }
1558
- };
1559
- scroll();
1560
- };
1561
- const stopAutoScroll = () => {
1562
- if (autoScrollAnimationId !== null) {
1563
- cancelAnimationFrame(autoScrollAnimationId);
1564
- autoScrollAnimationId = null;
1565
- }
1566
- };
1567
- const activateRenderer = () => {
1568
- stopProgressAnimation();
1569
- setIsActivated(true);
1570
- document.body.style.cursor = "crosshair";
1571
- options.onActivate?.();
1572
- };
1573
- const deactivateRenderer = () => {
1574
- setIsToggleMode(false);
1575
- setIsHoldingKeys(false);
1576
- setIsActivated(false);
1577
- setIsInputMode(false);
1578
- setInputText("");
1579
- document.body.style.cursor = "";
1580
- if (isDragging()) {
1581
- setIsDragging(false);
1582
- document.body.style.userSelect = "";
1583
- }
1584
- if (holdTimerId) window.clearTimeout(holdTimerId);
1585
- if (keydownSpamTimerId) window.clearTimeout(keydownSpamTimerId);
1586
- if (mouseSettleTimerId) {
1587
- window.clearTimeout(mouseSettleTimerId);
1588
- mouseSettleTimerId = null;
1589
- }
1590
- setMouseHasSettled(false);
1591
- stopAutoScroll();
1592
- stopProgressAnimation();
1593
- options.onDeactivate?.();
1594
- };
1595
- const handleInputChange = (value) => {
1596
- setInputText(value);
1597
- };
1598
- const handleInputSubmit = () => {
1599
- if (!isInputMode()) return;
1600
- const element = targetElement();
1601
- const prompt = inputText().trim();
1602
- const currentX = mouseX();
1603
- const currentY = mouseY();
1604
- setIsInputMode(false);
1605
- setInputText("");
1606
- if (element) {
1607
- void executeCopyOperation(currentX, currentY, () => copySingleElementToClipboard(element, prompt || void 0)).then(() => {
1608
- deactivateRenderer();
1609
- });
1610
- } else {
1611
- deactivateRenderer();
1612
- }
1613
- };
1614
- const handleInputCancel = () => {
1615
- if (!isInputMode()) return;
1616
- deactivateRenderer();
1617
- };
1618
- const abortController = new AbortController();
1619
- const eventListenerSignal = abortController.signal;
1620
- window.addEventListener("keydown", (event) => {
1621
- if (event.code === "Escape" && isHoldingKeys()) {
1622
- if (isInputMode()) {
1623
- return;
1624
- }
1625
- deactivateRenderer();
1626
- return;
1627
- }
1628
- if (event.code === "Enter" && isHoldingKeys() && !isInputMode()) {
1629
- event.preventDefault();
1630
- event.stopPropagation();
1631
- setIsToggleMode(true);
1632
- if (keydownSpamTimerId !== null) {
1633
- window.clearTimeout(keydownSpamTimerId);
1634
- keydownSpamTimerId = null;
1635
- }
1636
- if (!isActivated()) {
1637
- if (holdTimerId) window.clearTimeout(holdTimerId);
1638
- activateRenderer();
1639
- }
1640
- setIsInputMode(true);
1641
- return;
1642
- }
1643
- if (!options.allowActivationInsideInput && isKeyboardEventTriggeredByInput(event)) {
1644
- return;
1645
- }
1646
- if (!isTargetKeyCombination(event)) return;
1647
- if (isActivated()) {
1648
- if (isToggleMode()) return;
1649
- if (keydownSpamTimerId !== null) {
1650
- window.clearTimeout(keydownSpamTimerId);
1651
- }
1652
- keydownSpamTimerId = window.setTimeout(() => {
1653
- deactivateRenderer();
1654
- }, 200);
1655
- return;
1656
- }
1657
- if (event.repeat) return;
1658
- if (holdTimerId !== null) {
1659
- window.clearTimeout(holdTimerId);
1660
- }
1661
- if (!isHoldingKeys()) {
1662
- setIsHoldingKeys(true);
1663
- }
1664
- holdTimerId = window.setTimeout(() => {
1665
- activateRenderer();
1666
- }, options.keyHoldDuration);
1667
- }, {
1668
- signal: eventListenerSignal,
1669
- capture: true
1670
- });
1671
- window.addEventListener("keyup", (event) => {
1672
- if (!isHoldingKeys() && !isActivated()) return;
1673
- const isReleasingModifier = !event.metaKey && !event.ctrlKey;
1674
- const isReleasingC = event.code === "KeyC";
1675
- if (isReleasingC || isReleasingModifier) {
1676
- if (isToggleMode()) return;
1677
- deactivateRenderer();
1678
- }
1679
- }, {
1680
- signal: eventListenerSignal,
1681
- capture: true
1682
- });
1683
- window.addEventListener("mousemove", (event) => {
1684
- setMouseX(event.clientX);
1685
- setMouseY(event.clientY);
1686
- if (mouseSettleTimerId !== null) {
1687
- window.clearTimeout(mouseSettleTimerId);
1688
- }
1689
- setMouseHasSettled(false);
1690
- mouseSettleTimerId = window.setTimeout(() => {
1691
- setMouseHasSettled(true);
1692
- mouseSettleTimerId = null;
1693
- }, 300);
1694
- if (isDragging()) {
1695
- const direction = getAutoScrollDirection(event.clientX, event.clientY);
1696
- const isNearEdge = direction.top || direction.bottom || direction.left || direction.right;
1697
- if (isNearEdge && autoScrollAnimationId === null) {
1698
- startAutoScroll();
1699
- } else if (!isNearEdge && autoScrollAnimationId !== null) {
1700
- stopAutoScroll();
1701
- }
1702
- }
1703
- }, {
1704
- signal: eventListenerSignal
1705
- });
1706
- window.addEventListener("mousedown", (event) => {
1707
- if (isInputMode()) {
1708
- const target = event.target;
1709
- const isClickingInput = target.closest("[data-react-grab-input]");
1710
- if (!isClickingInput) {
1711
- handleInputCancel();
1712
- }
1713
- return;
1714
- }
1715
- if (!isRendererActive() || isCopying()) return;
1716
- event.preventDefault();
1717
- setIsDragging(true);
1718
- setDragStartX(event.clientX + window.scrollX);
1719
- setDragStartY(event.clientY + window.scrollY);
1720
- document.body.style.userSelect = "none";
1721
- }, {
1722
- signal: eventListenerSignal
1723
- });
1724
- window.addEventListener("mouseup", (event) => {
1725
- if (!isDragging()) return;
1726
- const dragDistance = calculateDragDistance(event.clientX, event.clientY);
1727
- const wasDragGesture = dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
1728
- setIsDragging(false);
1729
- stopAutoScroll();
1730
- document.body.style.userSelect = "";
1731
- if (wasDragGesture) {
1732
- setDidJustDrag(true);
1733
- const dragRect = calculateDragRectangle(event.clientX, event.clientY);
1734
- const elements = getElementsInDrag(dragRect, isValidGrabbableElement);
1735
- if (elements.length > 0) {
1736
- void executeCopyOperation(event.clientX, event.clientY, () => copyMultipleElementsToClipboard(elements));
1737
- } else {
1738
- const fallbackElements = getElementsInDragLoose(dragRect, isValidGrabbableElement);
1739
- if (fallbackElements.length > 0) {
1740
- void executeCopyOperation(event.clientX, event.clientY, () => copyMultipleElementsToClipboard(fallbackElements));
1741
- }
1742
- }
1743
- } else {
1744
- const element = getElementAtPosition(event.clientX, event.clientY);
1745
- if (!element) return;
1746
- setLastGrabbedElement(element);
1747
- void executeCopyOperation(event.clientX, event.clientY, () => copySingleElementToClipboard(element));
1748
- }
1749
- }, {
1750
- signal: eventListenerSignal
1751
- });
1752
- window.addEventListener("click", (event) => {
1753
- if (isRendererActive() || isCopying() || didJustDrag()) {
1754
- event.preventDefault();
1755
- event.stopPropagation();
1756
- const hadDrag = didJustDrag();
1757
- if (hadDrag) {
1758
- setDidJustDrag(false);
1759
- }
1760
- if (isToggleMode() && !isCopying()) {
1761
- if (!isHoldingKeys()) {
1762
- deactivateRenderer();
1763
- } else {
1764
- setIsToggleMode(false);
1765
- }
1766
- }
1767
- }
1768
- }, {
1769
- signal: eventListenerSignal,
1770
- capture: true
1771
- });
1772
- document.addEventListener("visibilitychange", () => {
1773
- if (document.hidden) {
1774
- setGrabbedBoxes([]);
1775
- }
1776
- }, {
1777
- signal: eventListenerSignal
1778
- });
1779
- window.addEventListener("scroll", () => {
1780
- setViewportVersion((version) => version + 1);
1781
- }, {
1782
- signal: eventListenerSignal,
1783
- capture: true
1784
- });
1785
- window.addEventListener("resize", () => {
1786
- setViewportVersion((version) => version + 1);
1787
- }, {
1788
- signal: eventListenerSignal
1789
- });
1790
- onCleanup(() => {
1791
- abortController.abort();
1792
- if (holdTimerId) window.clearTimeout(holdTimerId);
1793
- if (keydownSpamTimerId) window.clearTimeout(keydownSpamTimerId);
1794
- if (mouseSettleTimerId) window.clearTimeout(mouseSettleTimerId);
1795
- stopAutoScroll();
1796
- stopProgressAnimation();
1797
- document.body.style.userSelect = "";
1798
- document.body.style.cursor = "";
1799
- });
1800
- const rendererRoot = mountRoot();
1801
- const selectionVisible = createMemo(() => isRendererActive() && !isDragging() && Boolean(targetElement()));
1802
- const dragVisible = createMemo(() => isRendererActive() && isDraggingBeyondThreshold());
1803
- const labelVariant = createMemo(() => isCopying() ? "processing" : "hover");
1804
- const labelVisible = createMemo(() => {
1805
- if (isInputMode()) return false;
1806
- if (isCopying()) return true;
1807
- if (successLabels().length > 0) return false;
1808
- return isRendererActive() && !isDragging() && Boolean(targetElement());
1809
- });
1810
- const progressVisible = createMemo(() => isCopying() && showProgressIndicator() && hasValidMousePosition());
1811
- const crosshairVisible = createMemo(() => isRendererActive() && !isDragging());
1812
- const inputVisible = createMemo(() => isInputMode());
1813
- render(() => createComponent(ReactGrabRenderer, {
1814
- get selectionVisible() {
1815
- return selectionVisible();
1816
- },
1817
- get selectionBounds() {
1818
- return selectionBounds();
1819
- },
1820
- get dragVisible() {
1821
- return dragVisible();
1822
- },
1823
- get dragBounds() {
1824
- return dragBounds();
1825
- },
1826
- get grabbedBoxes() {
1827
- return grabbedBoxes();
1828
- },
1829
- get successLabels() {
1830
- return successLabels();
1831
- },
1832
- get labelVariant() {
1833
- return labelVariant();
1834
- },
1835
- get labelText() {
1836
- return labelText();
1837
- },
1838
- get labelX() {
1839
- return labelPosition().x;
1840
- },
1841
- get labelY() {
1842
- return labelPosition().y;
1843
- },
1844
- get labelVisible() {
1845
- return labelVisible();
1846
- },
1847
- labelZIndex: Z_INDEX_LABEL,
1848
- get labelShowHint() {
1849
- return mouseHasSettled();
1850
- },
1851
- get progressVisible() {
1852
- return progressVisible();
1853
- },
1854
- get progress() {
1855
- return progress();
1856
- },
1857
- get mouseX() {
1858
- return progressPosition().x;
1859
- },
1860
- get mouseY() {
1861
- return progressPosition().y;
1862
- },
1863
- get crosshairVisible() {
1864
- return crosshairVisible();
1865
- },
1866
- get inputVisible() {
1867
- return inputVisible();
1868
- },
1869
- get inputX() {
1870
- return mouseX();
1871
- },
1872
- get inputY() {
1873
- return mouseY();
1874
- },
1875
- get inputValue() {
1876
- return inputText();
1877
- },
1878
- onInputChange: handleInputChange,
1879
- onInputSubmit: handleInputSubmit,
1880
- onInputCancel: handleInputCancel
1881
- }), rendererRoot);
1882
- return {
1883
- activate: () => {
1884
- if (!isActivated()) {
1885
- activateRenderer();
1886
- }
1887
- },
1888
- deactivate: () => {
1889
- if (isActivated()) {
1890
- deactivateRenderer();
1891
- }
1892
- },
1893
- toggle: () => {
1894
- if (isActivated()) {
1895
- deactivateRenderer();
1896
- } else {
1897
- activateRenderer();
1898
- }
1899
- },
1900
- isActive: () => isActivated(),
1901
- dispose
1902
- };
1903
- });
1904
- };
1905
-
1906
13
  // src/index.ts
1907
- var globalApi = null;
1908
- var getGlobalApi = () => globalApi;
1909
- globalApi = init();
1910
-
1911
- export { getGlobalApi, init, playCopySound };
14
+ if (typeof window !== "undefined") {
15
+ init();
16
+ }