react-grab 0.0.36 → 0.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +264 -80
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.global.js +20 -16
- package/dist/index.js +264 -81
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -260,9 +260,10 @@ var getClampedElementPosition = (positionLeft, positionTop, elementWidth, elemen
|
|
|
260
260
|
// src/components/label.tsx
|
|
261
261
|
var _tmpl$3 = /* @__PURE__ */ web.template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
|
|
262
262
|
var _tmpl$22 = /* @__PURE__ */ web.template(`<div style=margin-right:4px>Copied`);
|
|
263
|
-
var _tmpl$32 = /* @__PURE__ */ web.template(`<span style="font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;font-variant-numeric:tabular-nums">`);
|
|
264
|
-
var _tmpl$4 = /* @__PURE__ */ web.template(`<
|
|
265
|
-
var _tmpl$5 = /* @__PURE__ */ web.template(`<div style=
|
|
263
|
+
var _tmpl$32 = /* @__PURE__ */ web.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">`);
|
|
264
|
+
var _tmpl$4 = /* @__PURE__ */ web.template(`<span style=font-variant-numeric:tabular-nums;font-size:10px;margin-left:4px;vertical-align:middle>`);
|
|
265
|
+
var _tmpl$5 = /* @__PURE__ */ web.template(`<div style=margin-left:4px>to clipboard`);
|
|
266
|
+
var _tmpl$6 = /* @__PURE__ */ web.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">`);
|
|
266
267
|
var Label = (props) => {
|
|
267
268
|
const [opacity, setOpacity] = solidJs.createSignal(0);
|
|
268
269
|
const [positionTick, setPositionTick] = solidJs.createSignal(0);
|
|
@@ -360,12 +361,26 @@ var Label = (props) => {
|
|
|
360
361
|
fallback.top += INDICATOR_CLAMP_PADDING_PX;
|
|
361
362
|
return fallback;
|
|
362
363
|
};
|
|
364
|
+
const labelSegments = () => {
|
|
365
|
+
const separator = " in ";
|
|
366
|
+
const separatorIndex = props.text.indexOf(separator);
|
|
367
|
+
if (separatorIndex === -1) {
|
|
368
|
+
return {
|
|
369
|
+
primary: props.text,
|
|
370
|
+
secondary: ""
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
primary: props.text.slice(0, separatorIndex),
|
|
375
|
+
secondary: props.text.slice(separatorIndex)
|
|
376
|
+
};
|
|
377
|
+
};
|
|
363
378
|
return web.createComponent(solidJs.Show, {
|
|
364
379
|
get when() {
|
|
365
380
|
return props.visible !== false;
|
|
366
381
|
},
|
|
367
382
|
get children() {
|
|
368
|
-
var _el$ = _tmpl$
|
|
383
|
+
var _el$ = _tmpl$6();
|
|
369
384
|
var _ref$ = labelRef;
|
|
370
385
|
typeof _ref$ === "function" ? web.use(_ref$, _el$) : labelRef = _el$;
|
|
371
386
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
@@ -403,9 +418,20 @@ var Label = (props) => {
|
|
|
403
418
|
return props.variant !== "processing";
|
|
404
419
|
},
|
|
405
420
|
get children() {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
421
|
+
return [(() => {
|
|
422
|
+
var _el$4 = _tmpl$32();
|
|
423
|
+
web.insert(_el$4, () => labelSegments().primary);
|
|
424
|
+
return _el$4;
|
|
425
|
+
})(), web.createComponent(solidJs.Show, {
|
|
426
|
+
get when() {
|
|
427
|
+
return web.memo(() => props.variant === "hover")() && labelSegments().secondary !== "";
|
|
428
|
+
},
|
|
429
|
+
get children() {
|
|
430
|
+
var _el$5 = _tmpl$4();
|
|
431
|
+
web.insert(_el$5, () => labelSegments().secondary);
|
|
432
|
+
return _el$5;
|
|
433
|
+
}
|
|
434
|
+
})];
|
|
409
435
|
}
|
|
410
436
|
}), null);
|
|
411
437
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
@@ -413,7 +439,7 @@ var Label = (props) => {
|
|
|
413
439
|
return props.variant === "success";
|
|
414
440
|
},
|
|
415
441
|
get children() {
|
|
416
|
-
return _tmpl$
|
|
442
|
+
return _tmpl$5();
|
|
417
443
|
}
|
|
418
444
|
}), null);
|
|
419
445
|
web.effect((_p$) => {
|
|
@@ -433,7 +459,7 @@ var Label = (props) => {
|
|
|
433
459
|
}
|
|
434
460
|
});
|
|
435
461
|
};
|
|
436
|
-
var _tmpl$
|
|
462
|
+
var _tmpl$7 = /* @__PURE__ */ web.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)">`);
|
|
437
463
|
var useFadeInOut = (visible) => {
|
|
438
464
|
const [opacity, setOpacity] = solidJs.createSignal(0);
|
|
439
465
|
solidJs.createEffect(solidJs.on(() => visible, (isVisible) => {
|
|
@@ -466,7 +492,7 @@ var ProgressIndicator = (props) => {
|
|
|
466
492
|
return props.visible !== false;
|
|
467
493
|
},
|
|
468
494
|
get children() {
|
|
469
|
-
var _el$ = _tmpl$
|
|
495
|
+
var _el$ = _tmpl$7(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild;
|
|
470
496
|
var _ref$ = progressIndicatorRef;
|
|
471
497
|
typeof _ref$ === "function" ? web.use(_ref$, _el$) : progressIndicatorRef = _el$;
|
|
472
498
|
web.effect((_p$) => {
|
|
@@ -486,7 +512,7 @@ var ProgressIndicator = (props) => {
|
|
|
486
512
|
}
|
|
487
513
|
});
|
|
488
514
|
};
|
|
489
|
-
var _tmpl$
|
|
515
|
+
var _tmpl$8 = /* @__PURE__ */ web.template(`<canvas style=position:fixed;top:0;left:0;pointer-events:none;z-index:2147483645>`);
|
|
490
516
|
var Crosshair = (props) => {
|
|
491
517
|
let canvasRef;
|
|
492
518
|
let context = null;
|
|
@@ -576,7 +602,7 @@ var Crosshair = (props) => {
|
|
|
576
602
|
return props.visible !== false;
|
|
577
603
|
},
|
|
578
604
|
get children() {
|
|
579
|
-
var _el$ = _tmpl$
|
|
605
|
+
var _el$ = _tmpl$8();
|
|
580
606
|
var _ref$ = canvasRef;
|
|
581
607
|
typeof _ref$ === "function" ? web.use(_ref$, _el$) : canvasRef = _el$;
|
|
582
608
|
return _el$;
|
|
@@ -708,6 +734,11 @@ var ReactGrabRenderer = (props) => {
|
|
|
708
734
|
}
|
|
709
735
|
})];
|
|
710
736
|
};
|
|
737
|
+
|
|
738
|
+
// src/utils/is-capitalized.ts
|
|
739
|
+
var isCapitalized = (value) => value.length > 0 && /^[A-Z]/.test(value);
|
|
740
|
+
|
|
741
|
+
// src/instrumentation.ts
|
|
711
742
|
bippy.instrument({
|
|
712
743
|
onCommitFiberRoot(_, fiberRoot) {
|
|
713
744
|
bippy._fiberRoots.add(fiberRoot);
|
|
@@ -716,40 +747,36 @@ bippy.instrument({
|
|
|
716
747
|
var generateCSSSelector = (element) => {
|
|
717
748
|
return finder.finder(element);
|
|
718
749
|
};
|
|
719
|
-
var
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
if (bippy.isCompositeFiber(currentFiber)) {
|
|
734
|
-
const displayName = bippy.getDisplayName(currentFiber);
|
|
735
|
-
if (displayName && !isInternalComponent(displayName)) {
|
|
736
|
-
componentName = displayName;
|
|
737
|
-
return true;
|
|
738
|
-
}
|
|
750
|
+
var truncateString = (string, maxLength) => string.length > maxLength ? `${string.substring(0, maxLength)}...` : string;
|
|
751
|
+
var isInternalComponent = (name) => !isCapitalized(name) || name.startsWith("_") || name.includes("Provider") && name.includes("Context");
|
|
752
|
+
var getNearestComponentDisplayName = (element) => {
|
|
753
|
+
const fiber = bippy.getFiberFromHostInstance(element);
|
|
754
|
+
if (!fiber) return null;
|
|
755
|
+
let componentName = null;
|
|
756
|
+
bippy.traverseFiber(
|
|
757
|
+
fiber,
|
|
758
|
+
(currentFiber) => {
|
|
759
|
+
if (bippy.isCompositeFiber(currentFiber)) {
|
|
760
|
+
const displayName = bippy.getDisplayName(currentFiber);
|
|
761
|
+
if (displayName && !isInternalComponent(displayName)) {
|
|
762
|
+
componentName = displayName;
|
|
763
|
+
return true;
|
|
739
764
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
}
|
|
765
|
+
}
|
|
766
|
+
return false;
|
|
767
|
+
},
|
|
768
|
+
true
|
|
769
|
+
);
|
|
770
|
+
return componentName;
|
|
771
|
+
};
|
|
772
|
+
var formatComponentSourceLocation = async (el) => {
|
|
773
|
+
const source$1 = await source.getSourceFromHostInstance(el);
|
|
774
|
+
if (!source$1) return null;
|
|
775
|
+
const fileName = source.normalizeFileName(source$1.fileName);
|
|
776
|
+
if (!source.isSourceFile(fileName)) return null;
|
|
777
|
+
return `${fileName}:${source$1.lineNumber}:${source$1.columnNumber}`;
|
|
778
|
+
};
|
|
779
|
+
var getHTMLSnippet = async (element) => {
|
|
753
780
|
const semanticTags = /* @__PURE__ */ new Set([
|
|
754
781
|
"article",
|
|
755
782
|
"aside",
|
|
@@ -838,9 +865,9 @@ var getHTMLSnippet = async (element) => {
|
|
|
838
865
|
lines.push("```html");
|
|
839
866
|
const ancestors = collectDistinguishingAncestors(element);
|
|
840
867
|
const ancestorComponents = ancestors.map(
|
|
841
|
-
(ancestor) =>
|
|
868
|
+
(ancestor) => getNearestComponentDisplayName(ancestor)
|
|
842
869
|
);
|
|
843
|
-
const elementComponent =
|
|
870
|
+
const elementComponent = getNearestComponentDisplayName(element);
|
|
844
871
|
const ancestorSources = await Promise.all(
|
|
845
872
|
ancestors.map((ancestor) => formatComponentSourceLocation(ancestor))
|
|
846
873
|
);
|
|
@@ -948,17 +975,18 @@ var waitForFocus = () => {
|
|
|
948
975
|
window.focus();
|
|
949
976
|
});
|
|
950
977
|
};
|
|
951
|
-
var copyContent = async (content) => {
|
|
978
|
+
var copyContent = async (content, onSuccess) => {
|
|
952
979
|
await waitForFocus();
|
|
953
980
|
try {
|
|
954
981
|
if (Array.isArray(content)) {
|
|
955
982
|
if (!navigator?.clipboard?.write) {
|
|
956
983
|
for (const contentPart of content) {
|
|
957
984
|
if (typeof contentPart === "string") {
|
|
958
|
-
const result = copyContentFallback(contentPart);
|
|
985
|
+
const result = copyContentFallback(contentPart, onSuccess);
|
|
959
986
|
if (!result) return result;
|
|
960
987
|
}
|
|
961
988
|
}
|
|
989
|
+
onSuccess?.();
|
|
962
990
|
return true;
|
|
963
991
|
}
|
|
964
992
|
const mimeTypeMap = /* @__PURE__ */ new Map();
|
|
@@ -977,28 +1005,52 @@ var copyContent = async (content) => {
|
|
|
977
1005
|
}
|
|
978
1006
|
}
|
|
979
1007
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1008
|
+
if (mimeTypeMap.size === 0) {
|
|
1009
|
+
const plainTextFallback = content.find(
|
|
1010
|
+
(contentPart) => typeof contentPart === "string"
|
|
1011
|
+
);
|
|
1012
|
+
if (typeof plainTextFallback === "string") {
|
|
1013
|
+
return copyContentFallback(plainTextFallback, onSuccess);
|
|
1014
|
+
}
|
|
1015
|
+
return false;
|
|
1016
|
+
}
|
|
1017
|
+
try {
|
|
1018
|
+
await navigator.clipboard.write([
|
|
1019
|
+
new ClipboardItem(Object.fromEntries(mimeTypeMap))
|
|
1020
|
+
]);
|
|
1021
|
+
onSuccess?.();
|
|
1022
|
+
return true;
|
|
1023
|
+
} catch {
|
|
1024
|
+
const plainTextParts = content.filter(
|
|
1025
|
+
(contentPart) => typeof contentPart === "string"
|
|
1026
|
+
);
|
|
1027
|
+
if (plainTextParts.length > 0) {
|
|
1028
|
+
const combinedText = plainTextParts.join("\n\n");
|
|
1029
|
+
return copyContentFallback(combinedText, onSuccess);
|
|
1030
|
+
}
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
984
1033
|
} else if (content instanceof Blob) {
|
|
985
1034
|
await navigator.clipboard.write([
|
|
986
1035
|
new ClipboardItem({ [content.type]: content })
|
|
987
1036
|
]);
|
|
1037
|
+
onSuccess?.();
|
|
988
1038
|
return true;
|
|
989
1039
|
} else {
|
|
990
1040
|
try {
|
|
991
1041
|
await navigator.clipboard.writeText(String(content));
|
|
1042
|
+
onSuccess?.();
|
|
992
1043
|
return true;
|
|
993
1044
|
} catch {
|
|
994
|
-
|
|
1045
|
+
const result = copyContentFallback(content, onSuccess);
|
|
1046
|
+
return result;
|
|
995
1047
|
}
|
|
996
1048
|
}
|
|
997
1049
|
} catch {
|
|
998
1050
|
return false;
|
|
999
1051
|
}
|
|
1000
1052
|
};
|
|
1001
|
-
var copyContentFallback = (content) => {
|
|
1053
|
+
var copyContentFallback = (content, onSuccess) => {
|
|
1002
1054
|
if (!document.execCommand) return false;
|
|
1003
1055
|
const el = document.createElement("textarea");
|
|
1004
1056
|
el.value = String(content);
|
|
@@ -1008,12 +1060,46 @@ var copyContentFallback = (content) => {
|
|
|
1008
1060
|
doc.append(el);
|
|
1009
1061
|
try {
|
|
1010
1062
|
el.select();
|
|
1011
|
-
|
|
1063
|
+
const result = document.execCommand("copy");
|
|
1064
|
+
if (result) onSuccess?.();
|
|
1065
|
+
return result;
|
|
1012
1066
|
} finally {
|
|
1013
1067
|
el.remove();
|
|
1014
1068
|
}
|
|
1015
1069
|
};
|
|
1016
1070
|
|
|
1071
|
+
// src/utils/play-copy-sound.ts
|
|
1072
|
+
var playCopySound = () => {
|
|
1073
|
+
try {
|
|
1074
|
+
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
|
|
1075
|
+
window.webkitAudioContext)();
|
|
1076
|
+
const masterGain = audioContext.createGain();
|
|
1077
|
+
masterGain.connect(audioContext.destination);
|
|
1078
|
+
const notes = [
|
|
1079
|
+
{ freq: 523.25, start: 0, duration: 0.1 },
|
|
1080
|
+
{ freq: 659.25, start: 0.05, duration: 0.1 },
|
|
1081
|
+
{ freq: 783.99, start: 0.1, duration: 0.15 }
|
|
1082
|
+
];
|
|
1083
|
+
notes.forEach((note) => {
|
|
1084
|
+
const oscillator = audioContext.createOscillator();
|
|
1085
|
+
const gainNode = audioContext.createGain();
|
|
1086
|
+
oscillator.connect(gainNode);
|
|
1087
|
+
gainNode.connect(masterGain);
|
|
1088
|
+
oscillator.frequency.value = note.freq;
|
|
1089
|
+
oscillator.type = "triangle";
|
|
1090
|
+
const startTime = audioContext.currentTime + note.start;
|
|
1091
|
+
const peakTime = startTime + 0.01;
|
|
1092
|
+
const endTime = startTime + note.duration;
|
|
1093
|
+
gainNode.gain.setValueAtTime(0, startTime);
|
|
1094
|
+
gainNode.gain.linearRampToValueAtTime(0.15, peakTime);
|
|
1095
|
+
gainNode.gain.exponentialRampToValueAtTime(0.01, endTime);
|
|
1096
|
+
oscillator.start(startTime);
|
|
1097
|
+
oscillator.stop(endTime);
|
|
1098
|
+
});
|
|
1099
|
+
} catch {
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1017
1103
|
// src/utils/is-element-visible.ts
|
|
1018
1104
|
var isElementVisible = (element, computedStyle = window.getComputedStyle(element)) => {
|
|
1019
1105
|
return computedStyle.display !== "none" && computedStyle.visibility !== "hidden" && computedStyle.opacity !== "0";
|
|
@@ -1121,6 +1207,12 @@ var createElementBounds = (element) => {
|
|
|
1121
1207
|
};
|
|
1122
1208
|
};
|
|
1123
1209
|
|
|
1210
|
+
// src/utils/is-localhost.ts
|
|
1211
|
+
var isLocalhost = () => {
|
|
1212
|
+
const hostname = window.location.hostname;
|
|
1213
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1124
1216
|
// src/core.tsx
|
|
1125
1217
|
var PROGRESS_INDICATOR_DELAY_MS = 150;
|
|
1126
1218
|
var init = (rawOptions) => {
|
|
@@ -1128,6 +1220,7 @@ var init = (rawOptions) => {
|
|
|
1128
1220
|
enabled: true,
|
|
1129
1221
|
keyHoldDuration: 300,
|
|
1130
1222
|
allowActivationInsideInput: true,
|
|
1223
|
+
playCopySound: false,
|
|
1131
1224
|
...rawOptions
|
|
1132
1225
|
};
|
|
1133
1226
|
if (options.enabled === false) {
|
|
@@ -1224,13 +1317,41 @@ ${context}
|
|
|
1224
1317
|
computedStyles: element.computedStyles
|
|
1225
1318
|
}))
|
|
1226
1319
|
};
|
|
1227
|
-
const
|
|
1320
|
+
const jsonString = JSON.stringify(structuredData);
|
|
1321
|
+
const base64Data = btoa(encodeURIComponent(jsonString).replace(/%([0-9A-F]{2})/g, (_match, p1) => String.fromCharCode(parseInt(p1, 16))));
|
|
1228
1322
|
const htmlContent = `<div data-react-grab="${base64Data}"></div>`;
|
|
1229
1323
|
return new Blob([htmlContent], {
|
|
1230
1324
|
type: "text/html"
|
|
1231
1325
|
});
|
|
1232
1326
|
};
|
|
1233
1327
|
const extractElementTagName = (element) => (element.tagName || "").toLowerCase();
|
|
1328
|
+
const extractNearestComponentName = (element) => {
|
|
1329
|
+
const fiber = bippy.getFiberFromHostInstance(element);
|
|
1330
|
+
if (!fiber) return null;
|
|
1331
|
+
let componentName = null;
|
|
1332
|
+
bippy.traverseFiber(fiber, (currentFiber) => {
|
|
1333
|
+
if (bippy.isCompositeFiber(currentFiber)) {
|
|
1334
|
+
const displayName = bippy.getDisplayName(currentFiber);
|
|
1335
|
+
if (displayName && isCapitalized(displayName) && !displayName.startsWith("_")) {
|
|
1336
|
+
componentName = displayName;
|
|
1337
|
+
return true;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
return false;
|
|
1341
|
+
}, true);
|
|
1342
|
+
return componentName;
|
|
1343
|
+
};
|
|
1344
|
+
const extractElementLabelText = (element) => {
|
|
1345
|
+
const tagName = extractElementTagName(element);
|
|
1346
|
+
const componentName = extractNearestComponentName(element);
|
|
1347
|
+
if (tagName && componentName) {
|
|
1348
|
+
return `<${tagName}> in ${componentName}`;
|
|
1349
|
+
}
|
|
1350
|
+
if (tagName) {
|
|
1351
|
+
return `<${tagName}>`;
|
|
1352
|
+
}
|
|
1353
|
+
return "<element>";
|
|
1354
|
+
};
|
|
1234
1355
|
const notifyElementsSelected = (elements) => {
|
|
1235
1356
|
try {
|
|
1236
1357
|
const elementsPayload = elements.map((element) => ({
|
|
@@ -1244,6 +1365,8 @@ ${context}
|
|
|
1244
1365
|
} catch {
|
|
1245
1366
|
}
|
|
1246
1367
|
};
|
|
1368
|
+
const isExtensionEnvironment = () => window.__REACT_GRAB_EXTENSION_ACTIVE__ === true || options.isExtension === true;
|
|
1369
|
+
const isExtensionTextOnlyMode = () => isExtensionEnvironment() && !isLocalhost();
|
|
1247
1370
|
const executeCopyOperation = async (positionX, positionY, operation) => {
|
|
1248
1371
|
setCopyStartX(positionX);
|
|
1249
1372
|
setCopyStartY(positionY);
|
|
@@ -1254,22 +1377,49 @@ ${context}
|
|
|
1254
1377
|
stopProgressAnimation();
|
|
1255
1378
|
});
|
|
1256
1379
|
};
|
|
1380
|
+
const hasInnerText = (element) => "innerText" in element;
|
|
1381
|
+
const extractElementTextContent = (element) => {
|
|
1382
|
+
if (hasInnerText(element)) {
|
|
1383
|
+
return element.innerText;
|
|
1384
|
+
}
|
|
1385
|
+
return element.textContent ?? "";
|
|
1386
|
+
};
|
|
1387
|
+
const createCombinedTextContent = (elements) => elements.map((element) => extractElementTextContent(element).trim()).filter((textContent) => textContent.length > 0).join("\n\n");
|
|
1257
1388
|
const copySingleElementToClipboard = async (targetElement2) => {
|
|
1258
|
-
const tagName = extractElementTagName(targetElement2);
|
|
1259
1389
|
showTemporaryGrabbedBox(createElementBounds(targetElement2));
|
|
1260
1390
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
1391
|
+
let didCopy = false;
|
|
1261
1392
|
try {
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1393
|
+
if (isExtensionTextOnlyMode()) {
|
|
1394
|
+
const plainTextContent = createCombinedTextContent([targetElement2]);
|
|
1395
|
+
if (plainTextContent.length > 0) {
|
|
1396
|
+
didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
|
|
1397
|
+
}
|
|
1398
|
+
} else {
|
|
1399
|
+
const content = await getHTMLSnippet(targetElement2);
|
|
1400
|
+
const plainTextContent = wrapInSelectedElementTags(content);
|
|
1401
|
+
const htmlContent = createStructuredClipboardHtmlBlob([{
|
|
1402
|
+
tagName: extractElementTagName(targetElement2),
|
|
1403
|
+
content,
|
|
1404
|
+
computedStyles: extractRelevantComputedStyles(targetElement2)
|
|
1405
|
+
}]);
|
|
1406
|
+
didCopy = await copyContent([plainTextContent, htmlContent], options.playCopySound ? playCopySound : void 0);
|
|
1407
|
+
if (!didCopy) {
|
|
1408
|
+
const plainTextContentOnly = createCombinedTextContent([targetElement2]);
|
|
1409
|
+
if (plainTextContentOnly.length > 0) {
|
|
1410
|
+
didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1270
1414
|
} catch {
|
|
1415
|
+
const plainTextContentOnly = createCombinedTextContent([targetElement2]);
|
|
1416
|
+
if (plainTextContentOnly.length > 0) {
|
|
1417
|
+
didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
if (didCopy) {
|
|
1421
|
+
showTemporarySuccessLabel(extractElementLabelText(targetElement2), copyStartX(), copyStartY());
|
|
1271
1422
|
}
|
|
1272
|
-
showTemporarySuccessLabel(tagName ? `<${tagName}>` : "<element>", copyStartX(), copyStartY());
|
|
1273
1423
|
notifyElementsSelected([targetElement2]);
|
|
1274
1424
|
};
|
|
1275
1425
|
const copyMultipleElementsToClipboard = async (targetElements) => {
|
|
@@ -1278,19 +1428,43 @@ ${context}
|
|
|
1278
1428
|
showTemporaryGrabbedBox(createElementBounds(element));
|
|
1279
1429
|
}
|
|
1280
1430
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
1431
|
+
let didCopy = false;
|
|
1281
1432
|
try {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1433
|
+
if (isExtensionTextOnlyMode()) {
|
|
1434
|
+
const plainTextContent = createCombinedTextContent(targetElements);
|
|
1435
|
+
if (plainTextContent.length > 0) {
|
|
1436
|
+
didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
|
|
1437
|
+
}
|
|
1438
|
+
} else {
|
|
1439
|
+
const elementSnippetResults = await Promise.allSettled(targetElements.map((element) => getHTMLSnippet(element)));
|
|
1440
|
+
const elementSnippets = elementSnippetResults.map((result) => result.status === "fulfilled" ? result.value : "").filter((snippet) => snippet.trim());
|
|
1441
|
+
if (elementSnippets.length > 0) {
|
|
1442
|
+
const plainTextContent = elementSnippets.map((snippet) => wrapInSelectedElementTags(snippet)).join("\n\n");
|
|
1443
|
+
const structuredElements = elementSnippets.map((content, elementIndex) => ({
|
|
1444
|
+
tagName: extractElementTagName(targetElements[elementIndex]),
|
|
1445
|
+
content,
|
|
1446
|
+
computedStyles: extractRelevantComputedStyles(targetElements[elementIndex])
|
|
1447
|
+
}));
|
|
1448
|
+
const htmlContent = createStructuredClipboardHtmlBlob(structuredElements);
|
|
1449
|
+
didCopy = await copyContent([plainTextContent, htmlContent], options.playCopySound ? playCopySound : void 0);
|
|
1450
|
+
if (!didCopy) {
|
|
1451
|
+
const plainTextContentOnly = createCombinedTextContent(targetElements);
|
|
1452
|
+
if (plainTextContentOnly.length > 0) {
|
|
1453
|
+
didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
} else {
|
|
1457
|
+
const plainTextContentOnly = createCombinedTextContent(targetElements);
|
|
1458
|
+
if (plainTextContentOnly.length > 0) {
|
|
1459
|
+
didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1291
1463
|
} catch {
|
|
1292
1464
|
}
|
|
1293
|
-
|
|
1465
|
+
if (didCopy) {
|
|
1466
|
+
showTemporarySuccessLabel(`${targetElements.length} elements`, copyStartX(), copyStartY());
|
|
1467
|
+
}
|
|
1294
1468
|
notifyElementsSelected(targetElements);
|
|
1295
1469
|
};
|
|
1296
1470
|
const targetElement = solidJs.createMemo(() => {
|
|
@@ -1347,7 +1521,7 @@ ${context}
|
|
|
1347
1521
|
});
|
|
1348
1522
|
const labelText = solidJs.createMemo(() => {
|
|
1349
1523
|
const element = targetElement();
|
|
1350
|
-
return element ?
|
|
1524
|
+
return element ? extractElementLabelText(element) : "<element>";
|
|
1351
1525
|
});
|
|
1352
1526
|
const labelPosition = solidJs.createMemo(() => isCopying() ? {
|
|
1353
1527
|
x: copyStartX(),
|
|
@@ -1374,7 +1548,13 @@ ${context}
|
|
|
1374
1548
|
progressTick();
|
|
1375
1549
|
if (startTime === null) return 0;
|
|
1376
1550
|
const elapsedTime = Date.now() - startTime;
|
|
1377
|
-
|
|
1551
|
+
const normalizedTime = elapsedTime / options.keyHoldDuration;
|
|
1552
|
+
const easedProgress = 1 - Math.exp(-normalizedTime);
|
|
1553
|
+
const maxProgressBeforeCompletion = 0.95;
|
|
1554
|
+
if (isCopying()) {
|
|
1555
|
+
return Math.min(easedProgress, maxProgressBeforeCompletion);
|
|
1556
|
+
}
|
|
1557
|
+
return 1;
|
|
1378
1558
|
});
|
|
1379
1559
|
const startProgressAnimation = () => {
|
|
1380
1560
|
setProgressStartTime(Date.now());
|
|
@@ -1659,7 +1839,11 @@ ${context}
|
|
|
1659
1839
|
// src/index.ts
|
|
1660
1840
|
var globalApi = null;
|
|
1661
1841
|
var getGlobalApi = () => globalApi;
|
|
1662
|
-
|
|
1842
|
+
var EXTENSION_MARKER = "__REACT_GRAB_EXTENSION_ACTIVE__";
|
|
1843
|
+
if (!window[EXTENSION_MARKER]) {
|
|
1844
|
+
globalApi = init();
|
|
1845
|
+
}
|
|
1663
1846
|
|
|
1664
1847
|
exports.getGlobalApi = getGlobalApi;
|
|
1665
1848
|
exports.init = init;
|
|
1849
|
+
exports.playCopySound = playCopySound;
|
package/dist/index.d.cts
CHANGED
|
@@ -3,6 +3,8 @@ interface Options {
|
|
|
3
3
|
keyHoldDuration?: number;
|
|
4
4
|
allowActivationInsideInput?: boolean;
|
|
5
5
|
onActivate?: () => void;
|
|
6
|
+
playCopySound?: boolean;
|
|
7
|
+
isExtension?: boolean;
|
|
6
8
|
}
|
|
7
9
|
interface ReactGrabAPI {
|
|
8
10
|
activate: () => void;
|
|
@@ -48,8 +50,20 @@ interface ReactGrabRendererProps {
|
|
|
48
50
|
crosshairVisible?: boolean;
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
declare global {
|
|
54
|
+
interface Window {
|
|
55
|
+
__REACT_GRAB_EXTENSION_ACTIVE__?: boolean;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
51
58
|
declare const init: (rawOptions?: Options) => ReactGrabAPI;
|
|
52
59
|
|
|
60
|
+
declare const playCopySound: () => void;
|
|
61
|
+
|
|
62
|
+
declare global {
|
|
63
|
+
interface Window {
|
|
64
|
+
__REACT_GRAB_EXTENSION_ACTIVE__?: boolean;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
53
67
|
declare const getGlobalApi: () => ReactGrabAPI | null;
|
|
54
68
|
|
|
55
|
-
export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, getGlobalApi, init };
|
|
69
|
+
export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, getGlobalApi, init, playCopySound };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ interface Options {
|
|
|
3
3
|
keyHoldDuration?: number;
|
|
4
4
|
allowActivationInsideInput?: boolean;
|
|
5
5
|
onActivate?: () => void;
|
|
6
|
+
playCopySound?: boolean;
|
|
7
|
+
isExtension?: boolean;
|
|
6
8
|
}
|
|
7
9
|
interface ReactGrabAPI {
|
|
8
10
|
activate: () => void;
|
|
@@ -48,8 +50,20 @@ interface ReactGrabRendererProps {
|
|
|
48
50
|
crosshairVisible?: boolean;
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
declare global {
|
|
54
|
+
interface Window {
|
|
55
|
+
__REACT_GRAB_EXTENSION_ACTIVE__?: boolean;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
51
58
|
declare const init: (rawOptions?: Options) => ReactGrabAPI;
|
|
52
59
|
|
|
60
|
+
declare const playCopySound: () => void;
|
|
61
|
+
|
|
62
|
+
declare global {
|
|
63
|
+
interface Window {
|
|
64
|
+
__REACT_GRAB_EXTENSION_ACTIVE__?: boolean;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
53
67
|
declare const getGlobalApi: () => ReactGrabAPI | null;
|
|
54
68
|
|
|
55
|
-
export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, getGlobalApi, init };
|
|
69
|
+
export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, getGlobalApi, init, playCopySound };
|