react-grab 0.0.24 → 0.0.26

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,5 +1,5 @@
1
- import { render, createComponent, mergeProps, memo, template, effect, style, insert, setStyleProperty, use } from 'solid-js/web';
2
- import { createRoot, createSignal, createMemo, createEffect, onCleanup, Show, For } from 'solid-js';
1
+ import { 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
3
  import { instrument, _fiberRoots, getFiberFromHostInstance } from 'bippy';
4
4
  import { getOwnerStack, getSourcesFromStack } from 'bippy/dist/source';
5
5
 
@@ -89,93 +89,101 @@ var mountRoot = () => {
89
89
  return root;
90
90
  };
91
91
 
92
- // src/utils/is-element-visible.ts
93
- var isElementVisible = (element, computedStyle = window.getComputedStyle(element)) => {
94
- return computedStyle.display !== "none" && computedStyle.visibility !== "hidden" && computedStyle.opacity !== "0";
95
- };
96
- var _tmpl$ = /* @__PURE__ */ template(`<div>`);
97
- 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">`);
98
- var _tmpl$3 = /* @__PURE__ */ template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
99
- 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">`);
100
- var _tmpl$5 = /* @__PURE__ */ template(`<div style="position:fixed;padding:2px 6px;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;display:flex;align-items:center;max-width:calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));overflow:hidden;text-overflow:ellipsis;white-space:nowrap"><span>`);
101
- var _tmpl$6 = /* @__PURE__ */ template(`<div style="position:fixed;z-index:2147483647;pointer-events:none;transition:opacity 0.1s ease-in-out"><div style="width:32px;height:2px;background-color:rgba(178, 28, 142, 0.2);border-radius:1px;overflow:hidden;position:relative"><div style="height:100%;background-color:#b21c8e;border-radius:1px;transition:width 0.05s cubic-bezier(0.165, 0.84, 0.44, 1)">`);
92
+ // src/constants.ts
102
93
  var VIEWPORT_MARGIN_PX = 8;
103
- var LABEL_OFFSET_PX = 6;
104
94
  var INDICATOR_CLAMP_PADDING_PX = 4;
105
- var INDICATOR_SUCCESS_VISIBLE_MS = 1500;
95
+ var CURSOR_OFFSET_PX = 14;
106
96
  var SELECTION_LERP_FACTOR = 0.95;
107
- var MARQUEE_LERP_FACTOR = 0.9;
97
+
98
+ // src/utils/lerp.ts
108
99
  var lerp = (start, end, factor) => {
109
100
  return start + (end - start) * factor;
110
101
  };
111
- var Overlay = (props) => {
102
+
103
+ // src/components/selection-box.tsx
104
+ var _tmpl$ = /* @__PURE__ */ template(`<div>`);
105
+ var SelectionBox = (props) => {
112
106
  const [currentX, setCurrentX] = createSignal(props.bounds.x);
113
107
  const [currentY, setCurrentY] = createSignal(props.bounds.y);
114
108
  const [currentWidth, setCurrentWidth] = createSignal(props.bounds.width);
115
109
  const [currentHeight, setCurrentHeight] = createSignal(props.bounds.height);
116
110
  const [opacity, setOpacity] = createSignal(1);
117
- let hasBeenShown = false;
111
+ let hasBeenRenderedOnce = false;
118
112
  let animationFrameId = null;
113
+ let fadeTimerId = null;
119
114
  let targetBounds = props.bounds;
120
- createEffect(() => {
121
- targetBounds = props.bounds;
122
- const factor = props.lerpFactor ?? SELECTION_LERP_FACTOR;
123
- if (!hasBeenShown) {
124
- setCurrentX(targetBounds.x);
125
- setCurrentY(targetBounds.y);
126
- setCurrentWidth(targetBounds.width);
127
- setCurrentHeight(targetBounds.height);
128
- hasBeenShown = true;
129
- return;
130
- }
131
- const EPSILON = 0.5;
115
+ let isAnimating = false;
116
+ const lerpFactor = () => {
117
+ if (props.lerpFactor !== void 0) return props.lerpFactor;
118
+ if (props.variant === "drag") return 0.7;
119
+ return SELECTION_LERP_FACTOR;
120
+ };
121
+ const startAnimation = () => {
122
+ if (isAnimating) return;
123
+ isAnimating = true;
132
124
  const animate = () => {
133
- const newX = lerp(currentX(), targetBounds.x, factor);
134
- const newY = lerp(currentY(), targetBounds.y, factor);
135
- const newWidth = lerp(currentWidth(), targetBounds.width, factor);
136
- const newHeight = lerp(currentHeight(), targetBounds.height, factor);
137
- setCurrentX(newX);
138
- setCurrentY(newY);
139
- setCurrentWidth(newWidth);
140
- setCurrentHeight(newHeight);
141
- const hasConverged = Math.abs(newX - targetBounds.x) < EPSILON && Math.abs(newY - targetBounds.y) < EPSILON && Math.abs(newWidth - targetBounds.width) < EPSILON && Math.abs(newHeight - targetBounds.height) < EPSILON;
142
- if (!hasConverged) {
125
+ const interpolatedX = lerp(currentX(), targetBounds.x, lerpFactor());
126
+ const interpolatedY = lerp(currentY(), targetBounds.y, lerpFactor());
127
+ const interpolatedWidth = lerp(currentWidth(), targetBounds.width, lerpFactor());
128
+ const interpolatedHeight = lerp(currentHeight(), targetBounds.height, lerpFactor());
129
+ setCurrentX(interpolatedX);
130
+ setCurrentY(interpolatedY);
131
+ setCurrentWidth(interpolatedWidth);
132
+ setCurrentHeight(interpolatedHeight);
133
+ 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;
134
+ if (!hasConvergedToTarget) {
143
135
  animationFrameId = requestAnimationFrame(animate);
144
136
  } else {
145
137
  animationFrameId = null;
138
+ isAnimating = false;
146
139
  }
147
140
  };
148
- if (animationFrameId !== null) {
149
- cancelAnimationFrame(animationFrameId);
150
- }
151
141
  animationFrameId = requestAnimationFrame(animate);
152
- onCleanup(() => {
153
- if (animationFrameId !== null) {
154
- cancelAnimationFrame(animationFrameId);
155
- animationFrameId = null;
156
- }
157
- });
158
- });
142
+ };
143
+ createEffect(on(() => props.bounds, (newBounds) => {
144
+ targetBounds = newBounds;
145
+ if (!hasBeenRenderedOnce) {
146
+ setCurrentX(targetBounds.x);
147
+ setCurrentY(targetBounds.y);
148
+ setCurrentWidth(targetBounds.width);
149
+ setCurrentHeight(targetBounds.height);
150
+ hasBeenRenderedOnce = true;
151
+ return;
152
+ }
153
+ startAnimation();
154
+ }));
159
155
  createEffect(() => {
160
- if (props.variant === "grabbed") {
161
- requestAnimationFrame(() => {
156
+ if (props.variant === "grabbed" && props.createdAt) {
157
+ fadeTimerId = window.setTimeout(() => {
162
158
  setOpacity(0);
163
- });
159
+ }, 1500);
160
+ }
161
+ });
162
+ onCleanup(() => {
163
+ if (animationFrameId !== null) {
164
+ cancelAnimationFrame(animationFrameId);
165
+ animationFrameId = null;
166
+ }
167
+ if (fadeTimerId !== null) {
168
+ window.clearTimeout(fadeTimerId);
169
+ fadeTimerId = null;
164
170
  }
171
+ isAnimating = false;
165
172
  });
166
173
  const baseStyle = {
167
174
  position: "fixed",
168
175
  "box-sizing": "border-box",
169
- "pointer-events": props.variant === "marquee" ? "none" : "auto",
176
+ "pointer-events": props.variant === "drag" ? "none" : "auto",
170
177
  "z-index": "2147483646"
171
178
  };
172
179
  const variantStyle = () => {
173
- if (props.variant === "marquee") {
180
+ if (props.variant === "drag") {
174
181
  return {
175
- border: "1px dashed rgb(210, 57, 192)",
176
- "background-color": "rgba(210, 57, 192, 0.1)",
182
+ border: "1px dashed rgba(210, 57, 192, 0.4)",
183
+ "background-color": "rgba(210, 57, 192, 0.05)",
177
184
  "will-change": "transform, width, height",
178
- contain: "layout paint size"
185
+ contain: "layout paint size",
186
+ cursor: "crosshair"
179
187
  };
180
188
  }
181
189
  return {
@@ -205,11 +213,12 @@ var Overlay = (props) => {
205
213
  }
206
214
  });
207
215
  };
216
+ 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">`);
208
217
  var Spinner = (props) => {
209
- let ref;
210
- createEffect(() => {
211
- if (ref) {
212
- ref.animate([{
218
+ let spinnerRef;
219
+ onMount(() => {
220
+ if (spinnerRef) {
221
+ spinnerRef.animate([{
213
222
  transform: "rotate(0deg)"
214
223
  }, {
215
224
  transform: "rotate(360deg)"
@@ -221,126 +230,213 @@ var Spinner = (props) => {
221
230
  }
222
231
  });
223
232
  return (() => {
224
- var _el$2 = _tmpl$2();
225
- var _ref$ = ref;
226
- typeof _ref$ === "function" ? use(_ref$, _el$2) : ref = _el$2;
227
- effect((_$p) => style(_el$2, {
233
+ var _el$ = _tmpl$2();
234
+ var _ref$ = spinnerRef;
235
+ typeof _ref$ === "function" ? use(_ref$, _el$) : spinnerRef = _el$;
236
+ effect((_$p) => style(_el$, {
228
237
  ...props.style
229
238
  }, _$p));
230
- return _el$2;
239
+ return _el$;
231
240
  })();
232
241
  };
242
+
243
+ // src/utils/get-clamped-element-position.ts
244
+ var getClampedElementPosition = (positionLeft, positionTop, elementWidth, elementHeight) => {
245
+ const viewportWidth = window.innerWidth;
246
+ const viewportHeight = window.innerHeight;
247
+ const minLeft = VIEWPORT_MARGIN_PX;
248
+ const minTop = VIEWPORT_MARGIN_PX;
249
+ const maxLeft = viewportWidth - elementWidth - VIEWPORT_MARGIN_PX;
250
+ const maxTop = viewportHeight - elementHeight - VIEWPORT_MARGIN_PX;
251
+ const clampedLeft = Math.max(minLeft, Math.min(positionLeft, maxLeft));
252
+ const clampedTop = Math.max(minTop, Math.min(positionTop, maxTop));
253
+ return { left: clampedLeft, top: clampedTop };
254
+ };
255
+
256
+ // src/components/label.tsx
257
+ var _tmpl$3 = /* @__PURE__ */ template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
258
+ var _tmpl$22 = /* @__PURE__ */ template(`<div style=margin-right:4px>Copied`);
259
+ var _tmpl$32 = /* @__PURE__ */ template(`<div style=margin-left:4px>to clipboard`);
260
+ var _tmpl$4 = /* @__PURE__ */ template(`<div style="position:fixed;padding:2px 6px;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;display:flex;align-items:center;max-width:calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));overflow:hidden;text-overflow:ellipsis;white-space:nowrap">`);
261
+ var _tmpl$5 = /* @__PURE__ */ template(`<span style="font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;font-variant-numeric:tabular-nums">`);
233
262
  var Label = (props) => {
234
263
  const [opacity, setOpacity] = createSignal(0);
235
- let ref;
236
- createEffect(() => {
237
- if (props.visible !== false) {
264
+ const [positionTick, setPositionTick] = createSignal(0);
265
+ let labelRef;
266
+ let currentX = props.x;
267
+ let currentY = props.y;
268
+ let targetX = props.x;
269
+ let targetY = props.y;
270
+ let animationFrameId = null;
271
+ let hasBeenRenderedOnce = false;
272
+ const animate = () => {
273
+ currentX = lerp(currentX, targetX, 0.3);
274
+ currentY = lerp(currentY, targetY, 0.3);
275
+ setPositionTick((tick) => tick + 1);
276
+ const hasConvergedToTarget = Math.abs(currentX - targetX) < 0.5 && Math.abs(currentY - targetY) < 0.5;
277
+ if (!hasConvergedToTarget) {
278
+ animationFrameId = requestAnimationFrame(animate);
279
+ } else {
280
+ animationFrameId = null;
281
+ }
282
+ };
283
+ const startAnimation = () => {
284
+ if (animationFrameId !== null) return;
285
+ animationFrameId = requestAnimationFrame(animate);
286
+ };
287
+ const updateTarget = () => {
288
+ targetX = props.x;
289
+ targetY = props.y;
290
+ if (!hasBeenRenderedOnce) {
291
+ currentX = targetX;
292
+ currentY = targetY;
293
+ hasBeenRenderedOnce = true;
294
+ setPositionTick((tick) => tick + 1);
295
+ return;
296
+ }
297
+ startAnimation();
298
+ };
299
+ createEffect(on(() => props.visible, (visible) => {
300
+ if (visible !== false) {
238
301
  requestAnimationFrame(() => {
239
302
  setOpacity(1);
240
303
  });
241
304
  } else {
242
305
  setOpacity(0);
306
+ return;
243
307
  }
244
- });
245
- createEffect(() => {
246
308
  if (props.variant === "success") {
247
309
  const fadeOutTimer = setTimeout(() => {
248
310
  setOpacity(0);
249
- }, INDICATOR_SUCCESS_VISIBLE_MS);
311
+ }, 1500);
250
312
  onCleanup(() => clearTimeout(fadeOutTimer));
251
313
  }
314
+ }));
315
+ createEffect(() => {
316
+ updateTarget();
317
+ });
318
+ onCleanup(() => {
319
+ if (animationFrameId !== null) {
320
+ cancelAnimationFrame(animationFrameId);
321
+ animationFrameId = null;
322
+ }
252
323
  });
253
- const indicatorRect = () => ref?.getBoundingClientRect();
324
+ const labelBoundingRect = () => labelRef?.getBoundingClientRect();
254
325
  const computedPosition = () => {
255
- const rect = indicatorRect();
256
- if (!rect) return {
257
- left: props.x,
258
- top: props.y
326
+ positionTick();
327
+ const boundingRect = labelBoundingRect();
328
+ if (!boundingRect) return {
329
+ left: currentX,
330
+ top: currentY
259
331
  };
260
- const viewportWidthPx = window.innerWidth;
261
- const viewportHeightPx = window.innerHeight;
262
- let indicatorLeftPx = Math.round(props.x);
263
- let indicatorTopPx = Math.round(props.y) - rect.height - LABEL_OFFSET_PX;
264
- const minLeft = VIEWPORT_MARGIN_PX;
265
- const minTop = VIEWPORT_MARGIN_PX;
266
- const maxLeft = viewportWidthPx - rect.width - VIEWPORT_MARGIN_PX;
267
- const maxTop = viewportHeightPx - rect.height - VIEWPORT_MARGIN_PX;
268
- const willClampLeft = indicatorLeftPx < minLeft;
269
- const willClampTop = indicatorTopPx < minTop;
270
- const isClamped = willClampLeft || willClampTop;
271
- indicatorLeftPx = Math.max(minLeft, Math.min(indicatorLeftPx, maxLeft));
272
- indicatorTopPx = Math.max(minTop, Math.min(indicatorTopPx, maxTop));
273
- if (isClamped) {
274
- indicatorLeftPx += INDICATOR_CLAMP_PADDING_PX;
275
- indicatorTopPx += INDICATOR_CLAMP_PADDING_PX;
332
+ if (props.variant === "success") {
333
+ const indicatorLeft = Math.round(currentX);
334
+ const indicatorTop = Math.round(currentY) - boundingRect.height - 6;
335
+ const willClampLeft = indicatorLeft < VIEWPORT_MARGIN_PX;
336
+ const willClampTop = indicatorTop < VIEWPORT_MARGIN_PX;
337
+ const isClamped = willClampLeft || willClampTop;
338
+ const clamped = getClampedElementPosition(indicatorLeft, indicatorTop, boundingRect.width, boundingRect.height);
339
+ if (isClamped) {
340
+ clamped.left += INDICATOR_CLAMP_PADDING_PX;
341
+ clamped.top += INDICATOR_CLAMP_PADDING_PX;
342
+ }
343
+ return clamped;
276
344
  }
277
- return {
278
- left: indicatorLeftPx,
279
- top: indicatorTopPx
280
- };
345
+ const CROSSHAIR_OFFSET = 12;
346
+ const viewportWidth = window.innerWidth;
347
+ const viewportHeight = window.innerHeight;
348
+ const quadrants = [{
349
+ left: Math.round(currentX) + CROSSHAIR_OFFSET,
350
+ top: Math.round(currentY) + CROSSHAIR_OFFSET
351
+ }, {
352
+ left: Math.round(currentX) - boundingRect.width - CROSSHAIR_OFFSET,
353
+ top: Math.round(currentY) + CROSSHAIR_OFFSET
354
+ }, {
355
+ left: Math.round(currentX) + CROSSHAIR_OFFSET,
356
+ top: Math.round(currentY) - boundingRect.height - CROSSHAIR_OFFSET
357
+ }, {
358
+ left: Math.round(currentX) - boundingRect.width - CROSSHAIR_OFFSET,
359
+ top: Math.round(currentY) - boundingRect.height - CROSSHAIR_OFFSET
360
+ }];
361
+ for (const position of quadrants) {
362
+ const fitsHorizontally = position.left >= VIEWPORT_MARGIN_PX && position.left + boundingRect.width <= viewportWidth - VIEWPORT_MARGIN_PX;
363
+ const fitsVertically = position.top >= VIEWPORT_MARGIN_PX && position.top + boundingRect.height <= viewportHeight - VIEWPORT_MARGIN_PX;
364
+ if (fitsHorizontally && fitsVertically) {
365
+ return position;
366
+ }
367
+ }
368
+ const fallback = getClampedElementPosition(quadrants[0].left, quadrants[0].top, boundingRect.width, boundingRect.height);
369
+ fallback.left += INDICATOR_CLAMP_PADDING_PX;
370
+ fallback.top += INDICATOR_CLAMP_PADDING_PX;
371
+ return fallback;
281
372
  };
282
373
  return createComponent(Show, {
283
374
  get when() {
284
375
  return props.visible !== false;
285
376
  },
286
377
  get children() {
287
- var _el$3 = _tmpl$5(), _el$5 = _el$3.firstChild;
288
- var _ref$2 = ref;
289
- typeof _ref$2 === "function" ? use(_ref$2, _el$3) : ref = _el$3;
290
- insert(_el$3, createComponent(Show, {
378
+ var _el$ = _tmpl$4();
379
+ var _ref$ = labelRef;
380
+ typeof _ref$ === "function" ? use(_ref$, _el$) : labelRef = _el$;
381
+ insert(_el$, createComponent(Show, {
291
382
  get when() {
292
383
  return props.variant === "processing";
293
384
  },
294
385
  get children() {
295
386
  return createComponent(Spinner, {});
296
387
  }
297
- }), _el$5);
298
- insert(_el$3, createComponent(Show, {
388
+ }), null);
389
+ insert(_el$, createComponent(Show, {
299
390
  get when() {
300
391
  return props.variant === "success";
301
392
  },
302
393
  get children() {
303
394
  return _tmpl$3();
304
395
  }
305
- }), _el$5);
306
- insert(_el$5, createComponent(Show, {
396
+ }), null);
397
+ insert(_el$, createComponent(Show, {
307
398
  get when() {
308
399
  return props.variant === "success";
309
400
  },
310
- children: "Grabbed "
401
+ get children() {
402
+ return _tmpl$22();
403
+ }
311
404
  }), null);
312
- insert(_el$5, createComponent(Show, {
405
+ insert(_el$, createComponent(Show, {
313
406
  get when() {
314
407
  return props.variant === "processing";
315
408
  },
316
409
  children: "Grabbing\u2026"
317
410
  }), null);
318
- insert(_el$5, createComponent(Show, {
411
+ insert(_el$, createComponent(Show, {
319
412
  get when() {
320
- return props.variant === "hover";
413
+ return props.text.startsWith("(");
414
+ },
415
+ get fallback() {
416
+ return (() => {
417
+ var _el$5 = _tmpl$5();
418
+ insert(_el$5, () => props.text);
419
+ return _el$5;
420
+ })();
321
421
  },
322
422
  get children() {
323
- var _el$6 = _tmpl$4();
324
- insert(_el$6, () => props.text);
325
- return _el$6;
423
+ return props.text;
326
424
  }
327
425
  }), null);
328
- insert(_el$5, createComponent(Show, {
426
+ insert(_el$, createComponent(Show, {
329
427
  get when() {
330
- return props.variant !== "hover";
428
+ return props.variant === "success";
331
429
  },
332
430
  get children() {
333
- var _el$7 = _tmpl$4();
334
- insert(_el$7, () => props.text);
335
- return _el$7;
431
+ return _tmpl$32();
336
432
  }
337
433
  }), null);
338
434
  effect((_p$) => {
339
435
  var _v$ = `${computedPosition().top}px`, _v$2 = `${computedPosition().left}px`, _v$3 = props.zIndex?.toString() ?? "2147483647", _v$4 = opacity();
340
- _v$ !== _p$.e && setStyleProperty(_el$3, "top", _p$.e = _v$);
341
- _v$2 !== _p$.t && setStyleProperty(_el$3, "left", _p$.t = _v$2);
342
- _v$3 !== _p$.a && setStyleProperty(_el$3, "z-index", _p$.a = _v$3);
343
- _v$4 !== _p$.o && setStyleProperty(_el$3, "opacity", _p$.o = _v$4);
436
+ _v$ !== _p$.e && setStyleProperty(_el$, "top", _p$.e = _v$);
437
+ _v$2 !== _p$.t && setStyleProperty(_el$, "left", _p$.t = _v$2);
438
+ _v$3 !== _p$.a && setStyleProperty(_el$, "z-index", _p$.a = _v$3);
439
+ _v$4 !== _p$.o && setStyleProperty(_el$, "opacity", _p$.o = _v$4);
344
440
  return _p$;
345
441
  }, {
346
442
  e: void 0,
@@ -348,58 +444,52 @@ var Label = (props) => {
348
444
  a: void 0,
349
445
  o: void 0
350
446
  });
351
- return _el$3;
447
+ return _el$;
352
448
  }
353
449
  });
354
450
  };
355
- var ProgressIndicator = (props) => {
451
+ var _tmpl$6 = /* @__PURE__ */ template(`<div style="position:fixed;z-index:2147483647;pointer-events:none;transition:opacity 0.1s ease-in-out"><div style="width:32px;height:2px;background-color:rgba(178, 28, 142, 0.2);border-radius:1px;overflow:hidden;position:relative"><div style="height:100%;background-color:#b21c8e;border-radius:1px;transition:width 0.05s cubic-bezier(0.165, 0.84, 0.44, 1)">`);
452
+ var useFadeInOut = (visible) => {
356
453
  const [opacity, setOpacity] = createSignal(0);
357
- let ref;
358
- createEffect(() => {
359
- if (props.visible !== false) {
454
+ createEffect(on(() => visible, (isVisible) => {
455
+ if (isVisible !== false) {
360
456
  requestAnimationFrame(() => {
361
457
  setOpacity(1);
362
458
  });
363
459
  } else {
364
460
  setOpacity(0);
365
461
  }
366
- });
462
+ }));
463
+ return opacity;
464
+ };
465
+ var ProgressIndicator = (props) => {
466
+ const opacity = useFadeInOut(props.visible);
467
+ let progressIndicatorRef;
367
468
  const computedPosition = () => {
368
- const rect = ref?.getBoundingClientRect();
369
- if (!rect) return {
469
+ const boundingRect = progressIndicatorRef?.getBoundingClientRect();
470
+ if (!boundingRect) return {
370
471
  left: props.mouseX,
371
472
  top: props.mouseY
372
473
  };
373
- const viewportWidth = window.innerWidth;
374
474
  const viewportHeight = window.innerHeight;
375
- const CURSOR_OFFSET = 14;
376
- const VIEWPORT_MARGIN = 8;
377
- let indicatorLeft = props.mouseX - rect.width / 2;
378
- let indicatorTop = props.mouseY + CURSOR_OFFSET;
379
- if (indicatorTop + rect.height + VIEWPORT_MARGIN > viewportHeight) {
380
- indicatorTop = props.mouseY - rect.height - CURSOR_OFFSET;
381
- }
382
- indicatorTop = Math.max(VIEWPORT_MARGIN, Math.min(indicatorTop, viewportHeight - rect.height - VIEWPORT_MARGIN));
383
- indicatorLeft = Math.max(VIEWPORT_MARGIN, Math.min(indicatorLeft, viewportWidth - rect.width - VIEWPORT_MARGIN));
384
- return {
385
- left: indicatorLeft,
386
- top: indicatorTop
387
- };
475
+ const indicatorLeft = props.mouseX - boundingRect.width / 2;
476
+ const indicatorTop = props.mouseY + CURSOR_OFFSET_PX + boundingRect.height + VIEWPORT_MARGIN_PX > viewportHeight ? props.mouseY - boundingRect.height - CURSOR_OFFSET_PX : props.mouseY + CURSOR_OFFSET_PX;
477
+ return getClampedElementPosition(indicatorLeft, indicatorTop, boundingRect.width, boundingRect.height);
388
478
  };
389
479
  return createComponent(Show, {
390
480
  get when() {
391
481
  return props.visible !== false;
392
482
  },
393
483
  get children() {
394
- var _el$8 = _tmpl$6(), _el$9 = _el$8.firstChild, _el$0 = _el$9.firstChild;
395
- var _ref$3 = ref;
396
- typeof _ref$3 === "function" ? use(_ref$3, _el$8) : ref = _el$8;
484
+ var _el$ = _tmpl$6(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild;
485
+ var _ref$ = progressIndicatorRef;
486
+ typeof _ref$ === "function" ? use(_ref$, _el$) : progressIndicatorRef = _el$;
397
487
  effect((_p$) => {
398
- var _v$5 = `${computedPosition().top}px`, _v$6 = `${computedPosition().left}px`, _v$7 = opacity(), _v$8 = `${Math.min(100, Math.max(0, props.progress * 100))}%`;
399
- _v$5 !== _p$.e && setStyleProperty(_el$8, "top", _p$.e = _v$5);
400
- _v$6 !== _p$.t && setStyleProperty(_el$8, "left", _p$.t = _v$6);
401
- _v$7 !== _p$.a && setStyleProperty(_el$8, "opacity", _p$.a = _v$7);
402
- _v$8 !== _p$.o && setStyleProperty(_el$0, "width", _p$.o = _v$8);
488
+ var _v$ = `${computedPosition().top}px`, _v$2 = `${computedPosition().left}px`, _v$3 = opacity(), _v$4 = `${Math.min(100, Math.max(0, props.progress * 100))}%`;
489
+ _v$ !== _p$.e && setStyleProperty(_el$, "top", _p$.e = _v$);
490
+ _v$2 !== _p$.t && setStyleProperty(_el$, "left", _p$.t = _v$2);
491
+ _v$3 !== _p$.a && setStyleProperty(_el$, "opacity", _p$.a = _v$3);
492
+ _v$4 !== _p$.o && setStyleProperty(_el$3, "width", _p$.o = _v$4);
403
493
  return _p$;
404
494
  }, {
405
495
  e: void 0,
@@ -407,53 +497,167 @@ var ProgressIndicator = (props) => {
407
497
  a: void 0,
408
498
  o: void 0
409
499
  });
410
- return _el$8;
500
+ return _el$;
501
+ }
502
+ });
503
+ };
504
+ var _tmpl$7 = /* @__PURE__ */ template(`<canvas style=position:fixed;top:0;left:0;pointer-events:none;z-index:2147483645>`);
505
+ var Crosshair = (props) => {
506
+ let canvasRef;
507
+ let context = null;
508
+ let width = 0;
509
+ let height = 0;
510
+ let dpr = 1;
511
+ let currentX = props.mouseX;
512
+ let currentY = props.mouseY;
513
+ let targetX = props.mouseX;
514
+ let targetY = props.mouseY;
515
+ let animationFrameId = null;
516
+ let hasBeenRenderedOnce = false;
517
+ const setupCanvas = () => {
518
+ if (!canvasRef) return;
519
+ dpr = Math.max(window.devicePixelRatio || 1, 2);
520
+ width = window.innerWidth;
521
+ height = window.innerHeight;
522
+ canvasRef.width = width * dpr;
523
+ canvasRef.height = height * dpr;
524
+ canvasRef.style.width = `${width}px`;
525
+ canvasRef.style.height = `${height}px`;
526
+ context = canvasRef.getContext("2d");
527
+ if (context) {
528
+ context.scale(dpr, dpr);
529
+ }
530
+ };
531
+ const render2 = () => {
532
+ if (!context) return;
533
+ context.clearRect(0, 0, width, height);
534
+ context.strokeStyle = "rgba(210, 57, 192)";
535
+ context.lineWidth = 1;
536
+ context.beginPath();
537
+ context.moveTo(currentX, 0);
538
+ context.lineTo(currentX, height);
539
+ context.moveTo(0, currentY);
540
+ context.lineTo(width, currentY);
541
+ context.stroke();
542
+ };
543
+ const animate = () => {
544
+ currentX = lerp(currentX, targetX, 0.3);
545
+ currentY = lerp(currentY, targetY, 0.3);
546
+ render2();
547
+ const hasConvergedToTarget = Math.abs(currentX - targetX) < 0.5 && Math.abs(currentY - targetY) < 0.5;
548
+ if (!hasConvergedToTarget) {
549
+ animationFrameId = requestAnimationFrame(animate);
550
+ } else {
551
+ animationFrameId = null;
552
+ }
553
+ };
554
+ const startAnimation = () => {
555
+ if (animationFrameId !== null) return;
556
+ animationFrameId = requestAnimationFrame(animate);
557
+ };
558
+ const updateTarget = () => {
559
+ targetX = props.mouseX;
560
+ targetY = props.mouseY;
561
+ if (!hasBeenRenderedOnce) {
562
+ currentX = targetX;
563
+ currentY = targetY;
564
+ hasBeenRenderedOnce = true;
565
+ render2();
566
+ return;
567
+ }
568
+ startAnimation();
569
+ };
570
+ createEffect(() => {
571
+ setupCanvas();
572
+ render2();
573
+ const handleResize = () => {
574
+ setupCanvas();
575
+ render2();
576
+ };
577
+ window.addEventListener("resize", handleResize);
578
+ onCleanup(() => {
579
+ window.removeEventListener("resize", handleResize);
580
+ if (animationFrameId !== null) {
581
+ cancelAnimationFrame(animationFrameId);
582
+ animationFrameId = null;
583
+ }
584
+ });
585
+ });
586
+ createEffect(() => {
587
+ updateTarget();
588
+ });
589
+ return createComponent(Show, {
590
+ get when() {
591
+ return props.visible !== false;
592
+ },
593
+ get children() {
594
+ var _el$ = _tmpl$7();
595
+ var _ref$ = canvasRef;
596
+ typeof _ref$ === "function" ? use(_ref$, _el$) : canvasRef = _el$;
597
+ return _el$;
411
598
  }
412
599
  });
413
600
  };
414
- var ReactGrabOverlay = (props) => {
601
+
602
+ // src/components/renderer.tsx
603
+ var ReactGrabRenderer = (props) => {
415
604
  return [createComponent(Show, {
416
605
  get when() {
417
606
  return memo(() => !!props.selectionVisible)() && props.selectionBounds;
418
607
  },
419
608
  get children() {
420
- return createComponent(Overlay, {
609
+ return createComponent(SelectionBox, {
421
610
  variant: "selection",
422
611
  get bounds() {
423
612
  return props.selectionBounds;
424
613
  },
425
614
  get visible() {
426
615
  return props.selectionVisible;
616
+ }
617
+ });
618
+ }
619
+ }), createComponent(Show, {
620
+ get when() {
621
+ return memo(() => !!(props.crosshairVisible === true && props.mouseX !== void 0))() && props.mouseY !== void 0;
622
+ },
623
+ get children() {
624
+ return createComponent(Crosshair, {
625
+ get mouseX() {
626
+ return props.mouseX;
627
+ },
628
+ get mouseY() {
629
+ return props.mouseY;
427
630
  },
428
- lerpFactor: SELECTION_LERP_FACTOR
631
+ visible: true
429
632
  });
430
633
  }
431
634
  }), createComponent(Show, {
432
635
  get when() {
433
- return memo(() => !!props.marqueeVisible)() && props.marqueeBounds;
636
+ return memo(() => !!props.dragVisible)() && props.dragBounds;
434
637
  },
435
638
  get children() {
436
- return createComponent(Overlay, {
437
- variant: "marquee",
639
+ return createComponent(SelectionBox, {
640
+ variant: "drag",
438
641
  get bounds() {
439
- return props.marqueeBounds;
642
+ return props.dragBounds;
440
643
  },
441
644
  get visible() {
442
- return props.marqueeVisible;
443
- },
444
- lerpFactor: MARQUEE_LERP_FACTOR
645
+ return props.dragVisible;
646
+ }
445
647
  });
446
648
  }
447
649
  }), createComponent(For, {
448
650
  get each() {
449
- return props.grabbedOverlays ?? [];
651
+ return props.grabbedBoxes ?? [];
450
652
  },
451
- children: (overlay) => createComponent(Overlay, {
653
+ children: (box) => createComponent(SelectionBox, {
452
654
  variant: "grabbed",
453
655
  get bounds() {
454
- return overlay.bounds;
656
+ return box.bounds;
455
657
  },
456
- visible: true
658
+ get createdAt() {
659
+ return box.createdAt;
660
+ }
457
661
  })
458
662
  }), createComponent(For, {
459
663
  get each() {
@@ -469,9 +673,7 @@ var ReactGrabOverlay = (props) => {
469
673
  },
470
674
  get y() {
471
675
  return label.y;
472
- },
473
- visible: true,
474
- zIndex: 2147483648
676
+ }
475
677
  })
476
678
  }), createComponent(Show, {
477
679
  get when() {
@@ -526,15 +728,6 @@ instrument({
526
728
  _fiberRoots.add(fiberRoot);
527
729
  }
528
730
  });
529
- var isValidSource = (source) => {
530
- const fileName = source.fileName;
531
- if (fileName.includes("node_modules")) return false;
532
- if (fileName.includes("/dist/")) return false;
533
- if (fileName.includes("/.next/")) return false;
534
- if (fileName.includes("/build/")) return false;
535
- if (fileName.includes("webpack-internal:")) return false;
536
- return true;
537
- };
538
731
  var getSourceTrace = async (element) => {
539
732
  const fiber = getFiberFromHostInstance(element);
540
733
  if (!fiber) return null;
@@ -544,7 +737,7 @@ var getSourceTrace = async (element) => {
544
737
  Number.MAX_SAFE_INTEGER
545
738
  );
546
739
  if (!sources) return null;
547
- return sources.filter(isValidSource);
740
+ return sources;
548
741
  };
549
742
  var getHTMLSnippet = (element) => {
550
743
  const semanticTags = /* @__PURE__ */ new Set([
@@ -819,7 +1012,159 @@ var copyContentFallback = (content) => {
819
1012
  }
820
1013
  };
821
1014
 
1015
+ // src/utils/is-element-visible.ts
1016
+ var isElementVisible = (element, computedStyle = window.getComputedStyle(element)) => {
1017
+ return computedStyle.display !== "none" && computedStyle.visibility !== "hidden" && computedStyle.opacity !== "0";
1018
+ };
1019
+
1020
+ // src/utils/is-valid-grabbable-element.ts
1021
+ var isValidGrabbableElement = (element) => {
1022
+ if (element.closest(`[${ATTRIBUTE_NAME}]`)) {
1023
+ return false;
1024
+ }
1025
+ const computedStyle = window.getComputedStyle(element);
1026
+ if (!isElementVisible(element, computedStyle)) {
1027
+ return false;
1028
+ }
1029
+ if (computedStyle.pointerEvents === "none") {
1030
+ return false;
1031
+ }
1032
+ return true;
1033
+ };
1034
+
1035
+ // src/utils/get-element-at-position.ts
1036
+ var getElementAtPosition = (clientX, clientY) => {
1037
+ const elementsAtPoint = document.elementsFromPoint(clientX, clientY);
1038
+ for (const candidateElement of elementsAtPoint) {
1039
+ if (isValidGrabbableElement(candidateElement)) {
1040
+ return candidateElement;
1041
+ }
1042
+ }
1043
+ return null;
1044
+ };
1045
+
1046
+ // src/utils/get-elements-in-drag.ts
1047
+ var DRAG_COVERAGE_THRESHOLD = 0.75;
1048
+ var filterElementsInDrag = (dragRect, isValidGrabbableElement2, shouldCheckCoverage) => {
1049
+ const elements = [];
1050
+ const allElements = Array.from(document.querySelectorAll("*"));
1051
+ const dragLeft = dragRect.x;
1052
+ const dragTop = dragRect.y;
1053
+ const dragRight = dragRect.x + dragRect.width;
1054
+ const dragBottom = dragRect.y + dragRect.height;
1055
+ for (const candidateElement of allElements) {
1056
+ if (!shouldCheckCoverage) {
1057
+ const tagName = (candidateElement.tagName || "").toUpperCase();
1058
+ if (tagName === "HTML" || tagName === "BODY") continue;
1059
+ }
1060
+ if (!isValidGrabbableElement2(candidateElement)) {
1061
+ continue;
1062
+ }
1063
+ const elementRect = candidateElement.getBoundingClientRect();
1064
+ const elementLeft = elementRect.left;
1065
+ const elementTop = elementRect.top;
1066
+ const elementRight = elementRect.left + elementRect.width;
1067
+ const elementBottom = elementRect.top + elementRect.height;
1068
+ if (shouldCheckCoverage) {
1069
+ const intersectionLeft = Math.max(dragLeft, elementLeft);
1070
+ const intersectionTop = Math.max(dragTop, elementTop);
1071
+ const intersectionRight = Math.min(dragRight, elementRight);
1072
+ const intersectionBottom = Math.min(dragBottom, elementBottom);
1073
+ const intersectionWidth = Math.max(0, intersectionRight - intersectionLeft);
1074
+ const intersectionHeight = Math.max(0, intersectionBottom - intersectionTop);
1075
+ const intersectionArea = intersectionWidth * intersectionHeight;
1076
+ const elementArea = Math.max(0, elementRect.width * elementRect.height);
1077
+ const hasMajorityCoverage = elementArea > 0 && intersectionArea / elementArea >= DRAG_COVERAGE_THRESHOLD;
1078
+ if (hasMajorityCoverage) {
1079
+ elements.push(candidateElement);
1080
+ }
1081
+ } else {
1082
+ const hasIntersection = elementLeft < dragRight && elementRight > dragLeft && elementTop < dragBottom && elementBottom > dragTop;
1083
+ if (hasIntersection) {
1084
+ elements.push(candidateElement);
1085
+ }
1086
+ }
1087
+ }
1088
+ return elements;
1089
+ };
1090
+ var removeNestedElements = (elements) => {
1091
+ return elements.filter((element) => {
1092
+ return !elements.some(
1093
+ (otherElement) => otherElement !== element && otherElement.contains(element)
1094
+ );
1095
+ });
1096
+ };
1097
+ var findBestParentElement = (elements, dragRect, isValidGrabbableElement2) => {
1098
+ if (elements.length <= 1) return null;
1099
+ const dragLeft = dragRect.x;
1100
+ const dragTop = dragRect.y;
1101
+ const dragRight = dragRect.x + dragRect.width;
1102
+ const dragBottom = dragRect.y + dragRect.height;
1103
+ let currentParent = elements[0];
1104
+ while (currentParent) {
1105
+ const parent = currentParent.parentElement;
1106
+ if (!parent) break;
1107
+ const parentRect = parent.getBoundingClientRect();
1108
+ const intersectionLeft = Math.max(dragLeft, parentRect.left);
1109
+ const intersectionTop = Math.max(dragTop, parentRect.top);
1110
+ const intersectionRight = Math.min(dragRight, parentRect.left + parentRect.width);
1111
+ const intersectionBottom = Math.min(dragBottom, parentRect.top + parentRect.height);
1112
+ const intersectionWidth = Math.max(0, intersectionRight - intersectionLeft);
1113
+ const intersectionHeight = Math.max(0, intersectionBottom - intersectionTop);
1114
+ const intersectionArea = intersectionWidth * intersectionHeight;
1115
+ const parentArea = Math.max(0, parentRect.width * parentRect.height);
1116
+ const hasMajorityCoverage = parentArea > 0 && intersectionArea / parentArea >= DRAG_COVERAGE_THRESHOLD;
1117
+ if (!hasMajorityCoverage) break;
1118
+ if (!isValidGrabbableElement2(parent)) {
1119
+ currentParent = parent;
1120
+ continue;
1121
+ }
1122
+ const allChildrenInParent = elements.every(
1123
+ (element) => parent.contains(element)
1124
+ );
1125
+ if (allChildrenInParent) {
1126
+ return parent;
1127
+ }
1128
+ currentParent = parent;
1129
+ }
1130
+ return null;
1131
+ };
1132
+ var getElementsInDrag = (dragRect, isValidGrabbableElement2) => {
1133
+ const elements = filterElementsInDrag(dragRect, isValidGrabbableElement2, true);
1134
+ const uniqueElements = removeNestedElements(elements);
1135
+ const bestParent = findBestParentElement(uniqueElements, dragRect, isValidGrabbableElement2);
1136
+ if (bestParent) {
1137
+ return [bestParent];
1138
+ }
1139
+ return uniqueElements;
1140
+ };
1141
+ var getElementsInDragLoose = (dragRect, isValidGrabbableElement2) => {
1142
+ const elements = filterElementsInDrag(dragRect, isValidGrabbableElement2, false);
1143
+ const uniqueElements = removeNestedElements(elements);
1144
+ const bestParent = findBestParentElement(uniqueElements, dragRect, isValidGrabbableElement2);
1145
+ if (bestParent) {
1146
+ return [bestParent];
1147
+ }
1148
+ return uniqueElements;
1149
+ };
1150
+
1151
+ // src/utils/create-element-bounds.ts
1152
+ var createElementBounds = (element) => {
1153
+ const boundingRect = element.getBoundingClientRect();
1154
+ const computedStyle = window.getComputedStyle(element);
1155
+ return {
1156
+ borderRadius: computedStyle.borderRadius || "0px",
1157
+ height: boundingRect.height,
1158
+ transform: computedStyle.transform || "none",
1159
+ width: boundingRect.width,
1160
+ x: boundingRect.left,
1161
+ y: boundingRect.top
1162
+ };
1163
+ };
1164
+
822
1165
  // src/core.tsx
1166
+ var SUCCESS_LABEL_DURATION_MS = 1700;
1167
+ var PROGRESS_INDICATOR_DELAY_MS = 150;
823
1168
  var init = (rawOptions) => {
824
1169
  const options = {
825
1170
  enabled: true,
@@ -830,215 +1175,114 @@ var init = (rawOptions) => {
830
1175
  return;
831
1176
  }
832
1177
  return createRoot((dispose) => {
1178
+ const OFFSCREEN_POSITION = -1e3;
833
1179
  const [isHoldingKeys, setIsHoldingKeys] = createSignal(false);
834
- const [mouseX, setMouseX] = createSignal(-1e3);
835
- const [mouseY, setMouseY] = createSignal(-1e3);
1180
+ const [mouseX, setMouseX] = createSignal(OFFSCREEN_POSITION);
1181
+ const [mouseY, setMouseY] = createSignal(OFFSCREEN_POSITION);
836
1182
  const [isDragging, setIsDragging] = createSignal(false);
837
- const [dragStartX, setDragStartX] = createSignal(-1e3);
838
- const [dragStartY, setDragStartY] = createSignal(-1e3);
1183
+ const [dragStartX, setDragStartX] = createSignal(OFFSCREEN_POSITION);
1184
+ const [dragStartY, setDragStartY] = createSignal(OFFSCREEN_POSITION);
839
1185
  const [isCopying, setIsCopying] = createSignal(false);
840
1186
  const [lastGrabbedElement, setLastGrabbedElement] = createSignal(null);
841
1187
  const [progressStartTime, setProgressStartTime] = createSignal(null);
842
1188
  const [progressTick, setProgressTick] = createSignal(0);
843
- const [grabbedOverlays, setGrabbedOverlays] = createSignal([]);
1189
+ const [grabbedBoxes, setGrabbedBoxes] = createSignal([]);
844
1190
  const [successLabels, setSuccessLabels] = createSignal([]);
845
1191
  const [isActivated, setIsActivated] = createSignal(false);
846
1192
  const [showProgressIndicator, setShowProgressIndicator] = createSignal(false);
847
1193
  let holdTimerId = null;
848
1194
  let progressAnimationId = null;
849
1195
  let progressDelayTimerId = null;
850
- const isOverlayActive = createMemo(() => isActivated() && !isCopying());
1196
+ let keydownSpamTimerId = null;
1197
+ const isRendererActive = createMemo(() => isActivated() && !isCopying());
1198
+ const hasValidMousePosition = createMemo(() => mouseX() > OFFSCREEN_POSITION && mouseY() > OFFSCREEN_POSITION);
851
1199
  const isTargetKeyCombination = (event) => (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "c";
852
- const getElementAtPosition = (x, y) => {
853
- const elementsAtPoint = document.elementsFromPoint(x, y);
854
- for (const candidateElement of elementsAtPoint) {
855
- if (candidateElement.closest(`[${ATTRIBUTE_NAME}]`)) {
856
- continue;
857
- }
858
- const computedStyle = window.getComputedStyle(candidateElement);
859
- if (!isElementVisible(candidateElement, computedStyle)) {
860
- continue;
861
- }
862
- if (computedStyle.pointerEvents === "none") {
863
- continue;
864
- }
865
- return candidateElement;
866
- }
867
- return null;
868
- };
869
- const getElementsInMarquee = (marqueeRect) => {
870
- const elements = [];
871
- const allElements = Array.from(document.querySelectorAll("*"));
872
- const marqueeLeft = marqueeRect.x;
873
- const marqueeTop = marqueeRect.y;
874
- const marqueeRight = marqueeRect.x + marqueeRect.width;
875
- const marqueeBottom = marqueeRect.y + marqueeRect.height;
876
- for (const candidateElement of allElements) {
877
- if (candidateElement.closest(`[${ATTRIBUTE_NAME}]`)) {
878
- continue;
879
- }
880
- const computedStyle = window.getComputedStyle(candidateElement);
881
- if (!isElementVisible(candidateElement, computedStyle)) {
882
- continue;
883
- }
884
- if (computedStyle.pointerEvents === "none") {
885
- continue;
886
- }
887
- const rect = candidateElement.getBoundingClientRect();
888
- const elementLeft = rect.left;
889
- const elementTop = rect.top;
890
- const elementRight = rect.left + rect.width;
891
- const elementBottom = rect.top + rect.height;
892
- const intersectionLeft = Math.max(marqueeLeft, elementLeft);
893
- const intersectionTop = Math.max(marqueeTop, elementTop);
894
- const intersectionRight = Math.min(marqueeRight, elementRight);
895
- const intersectionBottom = Math.min(marqueeBottom, elementBottom);
896
- const intersectionWidth = Math.max(0, intersectionRight - intersectionLeft);
897
- const intersectionHeight = Math.max(0, intersectionBottom - intersectionTop);
898
- const intersectionArea = intersectionWidth * intersectionHeight;
899
- const elementArea = Math.max(0, rect.width * rect.height);
900
- const COVERAGE_THRESHOLD = 0.75;
901
- const hasMajorityCoverage = elementArea > 0 && intersectionArea / elementArea >= COVERAGE_THRESHOLD;
902
- if (hasMajorityCoverage) {
903
- elements.push(candidateElement);
904
- }
905
- }
906
- return elements;
907
- };
908
- const getElementsInMarqueeLoose = (marqueeRect) => {
909
- const elements = [];
910
- const allElements = Array.from(document.querySelectorAll("*"));
911
- const marqueeLeft = marqueeRect.x;
912
- const marqueeTop = marqueeRect.y;
913
- const marqueeRight = marqueeRect.x + marqueeRect.width;
914
- const marqueeBottom = marqueeRect.y + marqueeRect.height;
915
- for (const candidateElement of allElements) {
916
- if (candidateElement.closest(`[${ATTRIBUTE_NAME}]`)) {
917
- continue;
918
- }
919
- const tag = (candidateElement.tagName || "").toUpperCase();
920
- if (tag === "HTML" || tag === "BODY") continue;
921
- const computedStyle = window.getComputedStyle(candidateElement);
922
- if (!isElementVisible(candidateElement, computedStyle)) {
923
- continue;
924
- }
925
- if (computedStyle.pointerEvents === "none") {
926
- continue;
927
- }
928
- const rect = candidateElement.getBoundingClientRect();
929
- const elementLeft = rect.left;
930
- const elementTop = rect.top;
931
- const elementRight = rect.left + rect.width;
932
- const elementBottom = rect.top + rect.height;
933
- const intersects = elementLeft < marqueeRight && elementRight > marqueeLeft && elementTop < marqueeBottom && elementBottom > marqueeTop;
934
- if (intersects) {
935
- elements.push(candidateElement);
936
- }
937
- }
938
- return elements;
939
- };
940
- const wrapInReferencedElement = (content) => `
941
-
942
- <referenced_element>
943
- ${content}
944
- </referenced_element>`;
945
- const wrapInReferencedElements = (content) => `
946
-
947
- <referenced_elements>
948
- ${content}
949
- </referenced_elements>`;
950
- const addGrabbedOverlay = (bounds) => {
951
- const id = `grabbed-${Date.now()}-${Math.random()}`;
952
- setGrabbedOverlays((prev) => [...prev, {
953
- id,
954
- bounds
955
- }]);
1200
+ const addGrabbedBox = (bounds) => {
1201
+ const boxId = `grabbed-${Date.now()}-${Math.random()}`;
1202
+ const createdAt = Date.now();
1203
+ const newBox = {
1204
+ id: boxId,
1205
+ bounds,
1206
+ createdAt
1207
+ };
1208
+ const currentBoxes = grabbedBoxes();
1209
+ setGrabbedBoxes([...currentBoxes, newBox]);
956
1210
  setTimeout(() => {
957
- setGrabbedOverlays((prev) => prev.filter((overlay) => overlay.id !== id));
958
- }, 300);
1211
+ setGrabbedBoxes((previousBoxes) => previousBoxes.filter((box) => box.id !== boxId));
1212
+ }, SUCCESS_LABEL_DURATION_MS);
959
1213
  };
960
- const addSuccessLabel = (text, x, y) => {
961
- const id = `success-${Date.now()}-${Math.random()}`;
962
- setSuccessLabels((prev) => [...prev, {
963
- id,
1214
+ const addSuccessLabel = (text, positionX, positionY) => {
1215
+ const labelId = `success-${Date.now()}-${Math.random()}`;
1216
+ setSuccessLabels((previousLabels) => [...previousLabels, {
1217
+ id: labelId,
964
1218
  text,
965
- x,
966
- y
1219
+ x: positionX,
1220
+ y: positionY
967
1221
  }]);
968
1222
  setTimeout(() => {
969
- setSuccessLabels((prev) => prev.filter((label) => label.id !== id));
970
- }, 1700);
1223
+ setSuccessLabels((previousLabels) => previousLabels.filter((label) => label.id !== labelId));
1224
+ }, SUCCESS_LABEL_DURATION_MS);
1225
+ };
1226
+ const formatStackTrace = (stackTrace) => {
1227
+ return stackTrace.map((source) => {
1228
+ const functionName = source.functionName ?? "anonymous";
1229
+ const fileName = source.fileName ?? "unknown";
1230
+ const lineNumber = source.lineNumber ?? 0;
1231
+ const columnNumber = source.columnNumber ?? 0;
1232
+ return ` ${functionName} - ${fileName}:${lineNumber}:${columnNumber}`;
1233
+ }).join("\n");
1234
+ };
1235
+ const wrapContextInXmlTags = (context) => {
1236
+ return `<selected_element>${context}</selected_element>`;
971
1237
  };
1238
+ const getElementContentWithTrace = async (element) => {
1239
+ const elementHtml = getHTMLSnippet(element);
1240
+ const componentStackTrace = await getSourceTrace(element);
1241
+ if (componentStackTrace?.length) {
1242
+ const formattedStackTrace = formatStackTrace(componentStackTrace);
1243
+ return `${elementHtml}
1244
+
1245
+ Component owner stack:
1246
+ ${formattedStackTrace}`;
1247
+ }
1248
+ return elementHtml;
1249
+ };
1250
+ const getElementTagName = (element) => (element.tagName || "").toLowerCase();
972
1251
  const handleCopy = async (targetElement2) => {
973
1252
  const elementBounds = targetElement2.getBoundingClientRect();
974
- const tagName = (targetElement2.tagName || "").toLowerCase();
975
- addGrabbedOverlay({
976
- borderRadius: window.getComputedStyle(targetElement2).borderRadius || "0px",
977
- height: elementBounds.height,
978
- transform: window.getComputedStyle(targetElement2).transform || "none",
979
- width: elementBounds.width,
980
- x: elementBounds.left,
981
- y: elementBounds.top
982
- });
1253
+ const tagName = getElementTagName(targetElement2);
1254
+ addGrabbedBox(createElementBounds(targetElement2));
983
1255
  try {
984
- const elementHtml = getHTMLSnippet(targetElement2);
985
- await copyContent(wrapInReferencedElement(elementHtml));
986
- const componentStackTrace = await getSourceTrace(targetElement2);
987
- if (componentStackTrace?.length) {
988
- const formattedStackTrace = componentStackTrace.map((source) => ` ${source.functionName} - ${source.fileName}:${source.lineNumber}:${source.columnNumber}`).join("\n");
989
- await copyContent(wrapInReferencedElement(`${elementHtml}
990
-
991
- Component owner stack:
992
- ${formattedStackTrace}`));
993
- }
1256
+ const content = await getElementContentWithTrace(targetElement2);
1257
+ await copyContent(wrapContextInXmlTags(content));
994
1258
  } catch {
995
1259
  }
996
1260
  addSuccessLabel(tagName ? `<${tagName}>` : "<element>", elementBounds.left, elementBounds.top);
997
1261
  };
998
1262
  const handleMultipleCopy = async (targetElements) => {
999
1263
  if (targetElements.length === 0) return;
1000
- let minX = Infinity;
1001
- let minY = Infinity;
1264
+ let minPositionX = Infinity;
1265
+ let minPositionY = Infinity;
1002
1266
  for (const element of targetElements) {
1003
1267
  const elementBounds = element.getBoundingClientRect();
1004
- minX = Math.min(minX, elementBounds.left);
1005
- minY = Math.min(minY, elementBounds.top);
1006
- addGrabbedOverlay({
1007
- borderRadius: window.getComputedStyle(element).borderRadius || "0px",
1008
- height: elementBounds.height,
1009
- transform: window.getComputedStyle(element).transform || "none",
1010
- width: elementBounds.width,
1011
- x: elementBounds.left,
1012
- y: elementBounds.top
1013
- });
1268
+ minPositionX = Math.min(minPositionX, elementBounds.left);
1269
+ minPositionY = Math.min(minPositionY, elementBounds.top);
1270
+ addGrabbedBox(createElementBounds(element));
1014
1271
  }
1015
1272
  try {
1016
- const elementSnippets = [];
1017
- for (const element of targetElements) {
1018
- const elementHtml = getHTMLSnippet(element);
1019
- const componentStackTrace = await getSourceTrace(element);
1020
- if (componentStackTrace?.length) {
1021
- const formattedStackTrace = componentStackTrace.map((source) => ` ${source.functionName} - ${source.fileName}:${source.lineNumber}:${source.columnNumber}`).join("\n");
1022
- elementSnippets.push(`${elementHtml}
1023
-
1024
- Component owner stack:
1025
- ${formattedStackTrace}`);
1026
- } else {
1027
- elementSnippets.push(elementHtml);
1028
- }
1029
- }
1273
+ const elementSnippets = await Promise.all(targetElements.map((element) => getElementContentWithTrace(element)));
1030
1274
  const combinedContent = elementSnippets.join("\n\n---\n\n");
1031
- await copyContent(wrapInReferencedElements(combinedContent));
1275
+ await copyContent(wrapContextInXmlTags(combinedContent));
1032
1276
  } catch {
1033
1277
  }
1034
- addSuccessLabel(`${targetElements.length} elements`, minX, minY);
1278
+ addSuccessLabel(`${targetElements.length} elements`, minPositionX, minPositionY);
1035
1279
  };
1036
1280
  const targetElement = createMemo(() => {
1037
- if (!isOverlayActive() || isDragging()) return null;
1281
+ if (!isRendererActive() || isDragging()) return null;
1038
1282
  return getElementAtPosition(mouseX(), mouseY());
1039
1283
  });
1040
1284
  const selectionBounds = createMemo(() => {
1041
- const element = targetElement() ?? lastGrabbedElement();
1285
+ const element = targetElement();
1042
1286
  if (!element) return void 0;
1043
1287
  const elementBounds = element.getBoundingClientRect();
1044
1288
  const computedStyle = window.getComputedStyle(element);
@@ -1051,54 +1295,68 @@ ${formattedStackTrace}`);
1051
1295
  y: elementBounds.top
1052
1296
  };
1053
1297
  });
1054
- const marqueeBounds = createMemo(() => {
1055
- if (!isDragging()) return void 0;
1056
- const marqueeX = Math.min(dragStartX(), mouseX());
1057
- const marqueeY = Math.min(dragStartY(), mouseY());
1058
- const marqueeWidth = Math.abs(mouseX() - dragStartX());
1059
- const marqueeHeight = Math.abs(mouseY() - dragStartY());
1298
+ const DRAG_THRESHOLD_PX = 2;
1299
+ const getDragDistance = (endX, endY) => ({
1300
+ x: Math.abs(endX - dragStartX()),
1301
+ y: Math.abs(endY - dragStartY())
1302
+ });
1303
+ const isDraggingBeyondThreshold = createMemo(() => {
1304
+ if (!isDragging()) return false;
1305
+ const dragDistance = getDragDistance(mouseX(), mouseY());
1306
+ return dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
1307
+ });
1308
+ const getDragRect = (endX, endY) => {
1309
+ const dragX = Math.min(dragStartX(), endX);
1310
+ const dragY = Math.min(dragStartY(), endY);
1311
+ const dragWidth = Math.abs(endX - dragStartX());
1312
+ const dragHeight = Math.abs(endY - dragStartY());
1313
+ return {
1314
+ x: dragX,
1315
+ y: dragY,
1316
+ width: dragWidth,
1317
+ height: dragHeight
1318
+ };
1319
+ };
1320
+ const dragBounds = createMemo(() => {
1321
+ if (!isDraggingBeyondThreshold()) return void 0;
1322
+ const drag = getDragRect(mouseX(), mouseY());
1060
1323
  return {
1061
1324
  borderRadius: "0px",
1062
- height: marqueeHeight,
1325
+ height: drag.height,
1063
1326
  transform: "none",
1064
- width: marqueeWidth,
1065
- x: marqueeX,
1066
- y: marqueeY
1327
+ width: drag.width,
1328
+ x: drag.x,
1329
+ y: drag.y
1067
1330
  };
1068
1331
  });
1069
1332
  const labelText = createMemo(() => {
1070
1333
  const element = targetElement();
1071
- if (!element) return "";
1072
- const tagName = (element.tagName || "").toLowerCase();
1334
+ if (!element) return "(click or drag to select element(s))";
1335
+ const tagName = getElementTagName(element);
1073
1336
  return tagName ? `<${tagName}>` : "<element>";
1074
1337
  });
1075
1338
  const labelPosition = createMemo(() => {
1076
- const element = targetElement() ?? lastGrabbedElement();
1077
- if (element) {
1078
- const rect = element.getBoundingClientRect();
1079
- return {
1080
- x: rect.left,
1081
- y: rect.top
1082
- };
1083
- }
1084
1339
  return {
1085
1340
  x: mouseX(),
1086
1341
  y: mouseY()
1087
1342
  };
1088
1343
  });
1089
- createEffect(() => {
1090
- const current = targetElement();
1091
- const last = lastGrabbedElement();
1092
- if (last && current && last !== current) {
1344
+ const isSameAsLast = createMemo(() => {
1345
+ const currentElement = targetElement();
1346
+ const lastElement = lastGrabbedElement();
1347
+ return !!currentElement && currentElement === lastElement;
1348
+ });
1349
+ createEffect(on(() => [targetElement(), lastGrabbedElement()], ([currentElement, lastElement]) => {
1350
+ if (lastElement && currentElement && lastElement !== currentElement) {
1093
1351
  setLastGrabbedElement(null);
1094
1352
  }
1095
- });
1353
+ }));
1096
1354
  const progress = createMemo(() => {
1097
1355
  const startTime = progressStartTime();
1098
1356
  progressTick();
1099
1357
  if (startTime === null) return 0;
1100
- const elapsed = Date.now() - startTime;
1101
- return Math.min(elapsed / options.keyHoldDuration, 1);
1358
+ const elapsedTime = Date.now() - startTime;
1359
+ return Math.min(elapsedTime / options.keyHoldDuration, 1);
1102
1360
  });
1103
1361
  const startProgressAnimation = () => {
1104
1362
  setProgressStartTime(Date.now());
@@ -1106,10 +1364,10 @@ ${formattedStackTrace}`);
1106
1364
  progressDelayTimerId = window.setTimeout(() => {
1107
1365
  setShowProgressIndicator(true);
1108
1366
  progressDelayTimerId = null;
1109
- }, 150);
1367
+ }, PROGRESS_INDICATOR_DELAY_MS);
1110
1368
  const animateProgress = () => {
1111
1369
  if (progressStartTime() === null) return;
1112
- setProgressTick((t) => t + 1);
1370
+ setProgressTick((tick) => tick + 1);
1113
1371
  const currentProgress = progress();
1114
1372
  if (currentProgress < 1) {
1115
1373
  progressAnimationId = requestAnimationFrame(animateProgress);
@@ -1129,86 +1387,93 @@ ${formattedStackTrace}`);
1129
1387
  setProgressStartTime(null);
1130
1388
  setShowProgressIndicator(false);
1131
1389
  };
1132
- const activateOverlay = () => {
1390
+ const activateRenderer = () => {
1133
1391
  stopProgressAnimation();
1134
1392
  setIsActivated(true);
1393
+ document.body.style.cursor = "crosshair";
1394
+ };
1395
+ const deactivateRenderer = () => {
1396
+ setIsHoldingKeys(false);
1397
+ setIsActivated(false);
1398
+ document.body.style.cursor = "";
1399
+ if (isDragging()) {
1400
+ setIsDragging(false);
1401
+ document.body.style.userSelect = "";
1402
+ }
1403
+ if (holdTimerId) window.clearTimeout(holdTimerId);
1404
+ if (keydownSpamTimerId) window.clearTimeout(keydownSpamTimerId);
1405
+ stopProgressAnimation();
1135
1406
  };
1136
1407
  const abortController = new AbortController();
1137
- const signal = abortController.signal;
1408
+ const eventListenerSignal = abortController.signal;
1138
1409
  window.addEventListener("keydown", (event) => {
1139
1410
  if (event.key === "Escape" && isHoldingKeys()) {
1140
- setIsHoldingKeys(false);
1141
- setIsActivated(false);
1142
- if (holdTimerId) window.clearTimeout(holdTimerId);
1143
- stopProgressAnimation();
1411
+ deactivateRenderer();
1144
1412
  return;
1145
1413
  }
1146
1414
  if (isKeyboardEventTriggeredByInput(event)) return;
1147
- if (isTargetKeyCombination(event) && !isHoldingKeys()) {
1148
- setIsHoldingKeys(true);
1149
- startProgressAnimation();
1150
- holdTimerId = window.setTimeout(() => {
1151
- activateOverlay();
1152
- options.onActivate?.();
1153
- }, options.keyHoldDuration);
1415
+ if (isTargetKeyCombination(event)) {
1416
+ if (!isHoldingKeys()) {
1417
+ setIsHoldingKeys(true);
1418
+ startProgressAnimation();
1419
+ holdTimerId = window.setTimeout(() => {
1420
+ activateRenderer();
1421
+ options.onActivate?.();
1422
+ }, options.keyHoldDuration);
1423
+ }
1424
+ if (isActivated()) {
1425
+ if (keydownSpamTimerId) window.clearTimeout(keydownSpamTimerId);
1426
+ keydownSpamTimerId = window.setTimeout(() => {
1427
+ deactivateRenderer();
1428
+ }, 200);
1429
+ }
1154
1430
  }
1155
1431
  }, {
1156
- signal
1432
+ signal: eventListenerSignal
1157
1433
  });
1158
1434
  window.addEventListener("keyup", (event) => {
1159
- if (isHoldingKeys() && (!isTargetKeyCombination(event) || event.key.toLowerCase() === "c")) {
1160
- setIsHoldingKeys(false);
1161
- setIsActivated(false);
1162
- if (holdTimerId) window.clearTimeout(holdTimerId);
1163
- stopProgressAnimation();
1435
+ if (!isHoldingKeys() && !isActivated()) return;
1436
+ const isReleasingC = event.key.toLowerCase() === "c";
1437
+ const isReleasingModifier = !event.metaKey && !event.ctrlKey;
1438
+ if (isReleasingC || isReleasingModifier) {
1439
+ deactivateRenderer();
1164
1440
  }
1165
1441
  }, {
1166
- signal
1442
+ signal: eventListenerSignal,
1443
+ capture: true
1167
1444
  });
1168
1445
  window.addEventListener("mousemove", (event) => {
1169
1446
  setMouseX(event.clientX);
1170
1447
  setMouseY(event.clientY);
1171
1448
  }, {
1172
- signal
1449
+ signal: eventListenerSignal
1173
1450
  });
1174
1451
  window.addEventListener("mousedown", (event) => {
1175
- if (!isOverlayActive() || isCopying()) return;
1452
+ if (!isRendererActive() || isCopying()) return;
1453
+ event.preventDefault();
1176
1454
  setIsDragging(true);
1177
1455
  setDragStartX(event.clientX);
1178
1456
  setDragStartY(event.clientY);
1457
+ document.body.style.userSelect = "none";
1179
1458
  }, {
1180
- signal
1459
+ signal: eventListenerSignal
1181
1460
  });
1182
1461
  window.addEventListener("mouseup", (event) => {
1183
1462
  if (!isDragging()) return;
1184
- const dragDistanceX = Math.abs(event.clientX - dragStartX());
1185
- const dragDistanceY = Math.abs(event.clientY - dragStartY());
1186
- const DRAG_THRESHOLD = 5;
1187
- const wasDrag = dragDistanceX > DRAG_THRESHOLD || dragDistanceY > DRAG_THRESHOLD;
1463
+ const dragDistance = getDragDistance(event.clientX, event.clientY);
1464
+ const wasDragGesture = dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
1188
1465
  setIsDragging(false);
1189
- if (wasDrag) {
1190
- const marqueeX = Math.min(dragStartX(), event.clientX);
1191
- const marqueeY = Math.min(dragStartY(), event.clientY);
1192
- const marqueeWidth = Math.abs(event.clientX - dragStartX());
1193
- const marqueeHeight = Math.abs(event.clientY - dragStartY());
1194
- const elements = getElementsInMarquee({
1195
- x: marqueeX,
1196
- y: marqueeY,
1197
- width: marqueeWidth,
1198
- height: marqueeHeight
1199
- });
1466
+ document.body.style.userSelect = "";
1467
+ if (wasDragGesture) {
1468
+ const dragRect = getDragRect(event.clientX, event.clientY);
1469
+ const elements = getElementsInDrag(dragRect, isValidGrabbableElement);
1200
1470
  if (elements.length > 0) {
1201
1471
  setIsCopying(true);
1202
1472
  void handleMultipleCopy(elements).finally(() => {
1203
1473
  setIsCopying(false);
1204
1474
  });
1205
1475
  } else {
1206
- const fallbackElements = getElementsInMarqueeLoose({
1207
- x: marqueeX,
1208
- y: marqueeY,
1209
- width: marqueeWidth,
1210
- height: marqueeHeight
1211
- });
1476
+ const fallbackElements = getElementsInDragLoose(dragRect, isValidGrabbableElement);
1212
1477
  if (fallbackElements.length > 0) {
1213
1478
  setIsCopying(true);
1214
1479
  void handleMultipleCopy(fallbackElements).finally(() => {
@@ -1226,51 +1491,85 @@ ${formattedStackTrace}`);
1226
1491
  });
1227
1492
  }
1228
1493
  }, {
1229
- signal
1230
- });
1231
- window.addEventListener("scroll", () => {
1232
- }, {
1233
- signal,
1234
- capture: true
1235
- });
1236
- window.addEventListener("resize", () => {
1237
- }, {
1238
- signal
1494
+ signal: eventListenerSignal
1239
1495
  });
1240
1496
  document.addEventListener("visibilitychange", () => {
1241
1497
  if (document.hidden) {
1242
- setGrabbedOverlays([]);
1498
+ setGrabbedBoxes([]);
1243
1499
  }
1244
1500
  }, {
1245
- signal
1501
+ signal: eventListenerSignal
1246
1502
  });
1247
1503
  onCleanup(() => {
1248
1504
  abortController.abort();
1249
1505
  if (holdTimerId) window.clearTimeout(holdTimerId);
1506
+ if (keydownSpamTimerId) window.clearTimeout(keydownSpamTimerId);
1250
1507
  stopProgressAnimation();
1508
+ document.body.style.userSelect = "";
1509
+ document.body.style.cursor = "";
1251
1510
  });
1252
- const overlayRoot = mountRoot();
1253
- const overlayProps = createMemo(() => ({
1254
- selectionVisible: isOverlayActive() && !isDragging() && !!selectionBounds(),
1255
- selectionBounds: selectionBounds(),
1256
- marqueeVisible: isOverlayActive() && isDragging(),
1257
- marqueeBounds: marqueeBounds(),
1258
- grabbedOverlays: grabbedOverlays(),
1259
- successLabels: successLabels(),
1260
- labelVariant: isCopying() ? "processing" : "hover",
1261
- labelText: labelText(),
1262
- labelX: labelPosition().x,
1263
- labelY: labelPosition().y,
1264
- labelVisible: isOverlayActive() && !isDragging() && !!targetElement() || isCopying(),
1265
- progressVisible: isHoldingKeys() && showProgressIndicator(),
1266
- progress: progress(),
1267
- mouseX: mouseX(),
1268
- mouseY: mouseY()
1269
- }));
1270
- render(() => createComponent(ReactGrabOverlay, mergeProps(overlayProps)), overlayRoot);
1511
+ const rendererRoot = mountRoot();
1512
+ const selectionVisible = createMemo(() => false);
1513
+ const dragVisible = createMemo(() => isRendererActive() && isDraggingBeyondThreshold());
1514
+ const labelVariant = createMemo(() => isCopying() ? "processing" : "hover");
1515
+ const labelVisible = createMemo(() => isRendererActive() && !isDragging() && (!!targetElement() && !isSameAsLast() || !targetElement()) || isCopying());
1516
+ const progressVisible = createMemo(() => isHoldingKeys() && showProgressIndicator() && hasValidMousePosition());
1517
+ const crosshairVisible = createMemo(() => isRendererActive() && !isDragging());
1518
+ render(() => createComponent(ReactGrabRenderer, {
1519
+ get selectionVisible() {
1520
+ return selectionVisible();
1521
+ },
1522
+ get selectionBounds() {
1523
+ return selectionBounds();
1524
+ },
1525
+ get dragVisible() {
1526
+ return dragVisible();
1527
+ },
1528
+ get dragBounds() {
1529
+ return dragBounds();
1530
+ },
1531
+ get grabbedBoxes() {
1532
+ return grabbedBoxes();
1533
+ },
1534
+ get successLabels() {
1535
+ return successLabels();
1536
+ },
1537
+ get labelVariant() {
1538
+ return labelVariant();
1539
+ },
1540
+ get labelText() {
1541
+ return labelText();
1542
+ },
1543
+ get labelX() {
1544
+ return labelPosition().x;
1545
+ },
1546
+ get labelY() {
1547
+ return labelPosition().y;
1548
+ },
1549
+ get labelVisible() {
1550
+ return labelVisible();
1551
+ },
1552
+ get progressVisible() {
1553
+ return progressVisible();
1554
+ },
1555
+ get progress() {
1556
+ return progress();
1557
+ },
1558
+ get mouseX() {
1559
+ return mouseX();
1560
+ },
1561
+ get mouseY() {
1562
+ return mouseY();
1563
+ },
1564
+ get crosshairVisible() {
1565
+ return crosshairVisible();
1566
+ }
1567
+ }), rendererRoot);
1271
1568
  return dispose;
1272
1569
  });
1273
1570
  };
1274
1571
 
1275
1572
  // src/index.ts
1276
1573
  init();
1574
+
1575
+ export { init };