react-grab 0.0.39 → 0.0.40

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 CHANGED
@@ -5,6 +5,11 @@ var solidJs = require('solid-js');
5
5
  var bippy = require('bippy');
6
6
  var source = require('bippy/dist/source');
7
7
  var finder = require('@medv/finder');
8
+ var TurndownService = require('turndown');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var TurndownService__default = /*#__PURE__*/_interopDefault(TurndownService);
8
13
 
9
14
  /**
10
15
  * @license MIT
@@ -178,7 +183,7 @@ var SelectionBox = (props) => {
178
183
  position: "fixed",
179
184
  "box-sizing": "border-box",
180
185
  "pointer-events": props.variant === "drag" ? "none" : "auto",
181
- "z-index": "2147483646"
186
+ "z-index": props.variant === "grabbed" ? "2147483645" : "2147483646"
182
187
  };
183
188
  const variantStyle = () => {
184
189
  if (props.variant === "drag") {
@@ -190,10 +195,16 @@ var SelectionBox = (props) => {
190
195
  cursor: "crosshair"
191
196
  };
192
197
  }
198
+ if (props.variant === "selection") {
199
+ return {
200
+ border: "1px dashed rgba(210, 57, 192, 0.5)",
201
+ "background-color": "rgba(210, 57, 192, 0.08)"
202
+ };
203
+ }
193
204
  return {
194
205
  border: "1px solid rgb(210, 57, 192)",
195
- "background-color": "rgba(210, 57, 192, 0.2)",
196
- transition: props.variant === "grabbed" ? "opacity 0.3s ease-out" : void 0
206
+ "background-color": "rgba(210, 57, 192, 0.08)",
207
+ transition: "opacity 0.3s ease-out"
197
208
  };
198
209
  };
199
210
  return web.createComponent(solidJs.Show, {
@@ -748,7 +759,7 @@ var generateCSSSelector = (element) => {
748
759
  return finder.finder(element);
749
760
  };
750
761
  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");
762
+ var isInternalComponent = (name) => !isCapitalized(name) || name.startsWith("_") || name.startsWith("Primitive.") || name.includes("Provider") && name.includes("Context");
752
763
  var getNearestComponentDisplayName = (element) => {
753
764
  const fiber = bippy.getFiberFromHostInstance(element);
754
765
  if (!fiber) return null;
@@ -880,14 +891,26 @@ var getHTMLSnippet = async (element) => {
880
891
  ancestors.map((ancestor) => formatComponentSourceLocation(ancestor))
881
892
  );
882
893
  const elementSource = await formatComponentSourceLocation(element);
894
+ const ancestorElementIndents = [];
895
+ const ancestorComponentIndents = [];
896
+ let currentIndentLevel = 0;
897
+ const getIndent = (level) => " ".repeat(level);
883
898
  for (let i = 0; i < ancestors.length; i++) {
884
- const indent2 = " ".repeat(i);
885
899
  const componentName = ancestorComponents[i];
886
900
  const source = ancestorSources[i];
887
- if (componentName && source && (i === 0 || ancestorComponents[i - 1] !== componentName)) {
888
- lines.push(`${indent2}<${componentName} source="${source}">`);
901
+ const isNewComponent = componentName && source && (i === 0 || ancestorComponents[i - 1] !== componentName);
902
+ if (isNewComponent) {
903
+ ancestorComponentIndents[i] = currentIndentLevel;
904
+ lines.push(
905
+ `${getIndent(currentIndentLevel)}<${componentName} used-at="${source}">`
906
+ );
907
+ currentIndentLevel++;
889
908
  }
890
- lines.push(`${indent2}${formatElementOpeningTag(ancestors[i], true)}`);
909
+ ancestorElementIndents[i] = currentIndentLevel;
910
+ lines.push(
911
+ `${getIndent(currentIndentLevel)}${formatElementOpeningTag(ancestors[i], true)}`
912
+ );
913
+ currentIndentLevel++;
891
914
  }
892
915
  const parent = element.parentElement;
893
916
  let targetIndex = -1;
@@ -895,33 +918,37 @@ var getHTMLSnippet = async (element) => {
895
918
  const siblings = Array.from(parent.children);
896
919
  targetIndex = siblings.indexOf(element);
897
920
  if (targetIndex > 0) {
898
- const indent2 = " ".repeat(ancestors.length);
921
+ const indent = getIndent(currentIndentLevel);
899
922
  if (targetIndex <= 2) {
900
923
  for (let i = 0; i < targetIndex; i++) {
901
924
  const sibling = siblings[i];
902
925
  const siblingId = extractSiblingIdentifier(sibling);
903
926
  if (siblingId) {
904
- lines.push(`${indent2} ${formatElementOpeningTag(sibling, true)}`);
905
- lines.push(`${indent2} </${sibling.tagName.toLowerCase()}>`);
927
+ lines.push(`${indent}${formatElementOpeningTag(sibling, true)}`);
928
+ lines.push(`${indent}</${sibling.tagName.toLowerCase()}>`);
906
929
  }
907
930
  }
908
931
  } else {
909
932
  lines.push(
910
- `${indent2} ... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
933
+ `${indent}... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
911
934
  );
912
935
  }
913
936
  }
914
937
  }
915
- const indent = " ".repeat(ancestors.length);
916
938
  const lastAncestorComponent = ancestors.length > 0 ? ancestorComponents[ancestorComponents.length - 1] : null;
917
939
  const showElementComponent = elementComponent && elementSource && elementComponent !== lastAncestorComponent;
940
+ let elementIndentLevel = currentIndentLevel;
941
+ const elementComponentIndentLevel = currentIndentLevel;
918
942
  if (showElementComponent) {
919
- lines.push(`${indent} <${elementComponent} used-at="${elementSource}">`);
943
+ lines.push(
944
+ `${getIndent(elementIndentLevel)}<${elementComponent} used-at="${elementSource}">`
945
+ );
946
+ elementIndentLevel++;
920
947
  }
921
- lines.push(`${indent} <!-- IMPORTANT: selected element -->`);
948
+ const elementIndent = getIndent(elementIndentLevel);
949
+ lines.push(`${elementIndent}<!-- IMPORTANT: selected element -->`);
922
950
  const textContent = extractTruncatedTextContent(element);
923
951
  const childrenCount = element.children.length;
924
- const elementIndent = `${indent}${showElementComponent ? " " : " "}`;
925
952
  if (textContent && childrenCount === 0 && textContent.length < 40) {
926
953
  lines.push(
927
954
  `${elementIndent}${formatElementOpeningTag(element)}${textContent}${formatElementClosingTag(
@@ -941,35 +968,37 @@ var getHTMLSnippet = async (element) => {
941
968
  lines.push(`${elementIndent}${formatElementClosingTag(element)}`);
942
969
  }
943
970
  if (showElementComponent) {
944
- lines.push(`${indent} </${elementComponent}>`);
971
+ lines.push(
972
+ `${getIndent(elementComponentIndentLevel)}</${elementComponent}>`
973
+ );
945
974
  }
946
975
  if (parent && targetIndex >= 0) {
947
976
  const siblings = Array.from(parent.children);
948
977
  const siblingsAfter = siblings.length - targetIndex - 1;
949
978
  if (siblingsAfter > 0) {
979
+ const indent = getIndent(currentIndentLevel);
950
980
  if (siblingsAfter <= 2) {
951
981
  for (let i = targetIndex + 1; i < siblings.length; i++) {
952
982
  const sibling = siblings[i];
953
983
  const siblingId = extractSiblingIdentifier(sibling);
954
984
  if (siblingId) {
955
- lines.push(`${indent} ${formatElementOpeningTag(sibling, true)}`);
956
- lines.push(`${indent} </${sibling.tagName.toLowerCase()}>`);
985
+ lines.push(`${indent}${formatElementOpeningTag(sibling, true)}`);
986
+ lines.push(`${indent}</${sibling.tagName.toLowerCase()}>`);
957
987
  }
958
988
  }
959
989
  } else {
960
990
  lines.push(
961
- `${indent} ... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
991
+ `${indent}... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
962
992
  );
963
993
  }
964
994
  }
965
995
  }
966
996
  for (let i = ancestors.length - 1; i >= 0; i--) {
967
- const indent2 = " ".repeat(i);
968
- lines.push(`${indent2}${formatElementClosingTag(ancestors[i])}`);
969
- const componentName = ancestorComponents[i];
970
- const source = ancestorSources[i];
971
- if (componentName && source && (i === ancestors.length - 1 || ancestorComponents[i + 1] !== componentName)) {
972
- lines.push(`${indent2}</${componentName}>`);
997
+ const elementIndent2 = getIndent(ancestorElementIndents[i]);
998
+ lines.push(`${elementIndent2}${formatElementClosingTag(ancestors[i])}`);
999
+ if (ancestorComponentIndents[i] !== void 0) {
1000
+ const compIndent = getIndent(ancestorComponentIndents[i]);
1001
+ lines.push(`${compIndent}</${ancestorComponents[i]}>`);
973
1002
  }
974
1003
  }
975
1004
  lines.push("```");
@@ -1227,6 +1256,62 @@ var isLocalhost = () => {
1227
1256
  const hostname = window.location.hostname;
1228
1257
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
1229
1258
  };
1259
+ var turndownService = null;
1260
+ var getTurndownService = () => {
1261
+ if (!turndownService) {
1262
+ turndownService = new TurndownService__default.default({
1263
+ headingStyle: "atx",
1264
+ codeBlockStyle: "fenced",
1265
+ emDelimiter: "_",
1266
+ bulletListMarker: "-",
1267
+ linkStyle: "inlined",
1268
+ linkReferenceStyle: "full"
1269
+ });
1270
+ turndownService.addRule("strikethrough", {
1271
+ filter: ["del", "s"],
1272
+ replacement: (content) => `~~${content}~~`
1273
+ });
1274
+ turndownService.addRule("removeHidden", {
1275
+ filter: (node) => {
1276
+ if (node instanceof HTMLElement) {
1277
+ const style = window.getComputedStyle(node);
1278
+ return style.display === "none" || style.visibility === "hidden" || style.opacity === "0";
1279
+ }
1280
+ return false;
1281
+ },
1282
+ replacement: () => ""
1283
+ });
1284
+ turndownService.addRule("preserveButtons", {
1285
+ filter: ["button"],
1286
+ replacement: (content) => content ? `[${content}]` : ""
1287
+ });
1288
+ turndownService.addRule("preserveInputs", {
1289
+ filter: (node) => {
1290
+ if (node.nodeName === "INPUT" && node instanceof HTMLInputElement) {
1291
+ return true;
1292
+ }
1293
+ return false;
1294
+ },
1295
+ replacement: (_, node) => {
1296
+ if (node instanceof HTMLInputElement) {
1297
+ const value = node.value || node.placeholder;
1298
+ return value ? `[${value}]` : "";
1299
+ }
1300
+ return "";
1301
+ }
1302
+ });
1303
+ }
1304
+ return turndownService;
1305
+ };
1306
+ var htmlToMarkdown = (html) => {
1307
+ const service = getTurndownService();
1308
+ return service.turndown(html).trim();
1309
+ };
1310
+ var elementToMarkdown = (element) => {
1311
+ const clonedElement = element.cloneNode(true);
1312
+ const service = getTurndownService();
1313
+ return service.turndown(clonedElement).trim();
1314
+ };
1230
1315
 
1231
1316
  // src/core.tsx
1232
1317
  var PROGRESS_INDICATOR_DELAY_MS = 150;
@@ -1266,6 +1351,7 @@ var init = (rawOptions) => {
1266
1351
  const [grabbedBoxes, setGrabbedBoxes] = solidJs.createSignal([]);
1267
1352
  const [successLabels, setSuccessLabels] = solidJs.createSignal([]);
1268
1353
  const [isActivated, setIsActivated] = solidJs.createSignal(false);
1354
+ const [isToggleMode, setIsToggleMode] = solidJs.createSignal(false);
1269
1355
  const [showProgressIndicator, setShowProgressIndicator] = solidJs.createSignal(false);
1270
1356
  const [didJustDrag, setDidJustDrag] = solidJs.createSignal(false);
1271
1357
  const [copyStartX, setCopyStartX] = solidJs.createSignal(OFFSCREEN_POSITION);
@@ -1345,7 +1431,7 @@ ${context}
1345
1431
  bippy.traverseFiber(fiber, (currentFiber) => {
1346
1432
  if (bippy.isCompositeFiber(currentFiber)) {
1347
1433
  const displayName = bippy.getDisplayName(currentFiber);
1348
- if (displayName && isCapitalized(displayName) && !displayName.startsWith("_")) {
1434
+ if (displayName && isCapitalized(displayName) && !displayName.startsWith("_") && !displayName.startsWith("Primitive.")) {
1349
1435
  componentName = displayName;
1350
1436
  return true;
1351
1437
  }
@@ -1388,6 +1474,13 @@ ${context}
1388
1474
  await operation().finally(() => {
1389
1475
  setIsCopying(false);
1390
1476
  stopProgressAnimation();
1477
+ if (isToggleMode()) {
1478
+ if (!isHoldingKeys()) {
1479
+ deactivateRenderer();
1480
+ } else {
1481
+ setIsToggleMode(false);
1482
+ }
1483
+ }
1391
1484
  });
1392
1485
  };
1393
1486
  const hasInnerText = (element) => "innerText" in element;
@@ -1398,15 +1491,16 @@ ${context}
1398
1491
  return element.textContent ?? "";
1399
1492
  };
1400
1493
  const createCombinedTextContent = (elements) => elements.map((element) => extractElementTextContent(element).trim()).filter((textContent) => textContent.length > 0).join("\n\n");
1494
+ const createCombinedMarkdownContent = (elements) => elements.map((element) => elementToMarkdown(element).trim()).filter((markdownContent) => markdownContent.length > 0).join("\n\n");
1401
1495
  const copySingleElementToClipboard = async (targetElement2) => {
1402
1496
  showTemporaryGrabbedBox(createElementBounds(targetElement2));
1403
1497
  await new Promise((resolve) => requestAnimationFrame(resolve));
1404
1498
  let didCopy = false;
1405
1499
  try {
1406
1500
  if (isExtensionTextOnlyMode()) {
1407
- const plainTextContent = createCombinedTextContent([targetElement2]);
1408
- if (plainTextContent.length > 0) {
1409
- didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
1501
+ const markdownContent = createCombinedMarkdownContent([targetElement2]);
1502
+ if (markdownContent.length > 0) {
1503
+ didCopy = await copyContent(markdownContent, options.playCopySound ? playCopySound : void 0);
1410
1504
  }
1411
1505
  } else {
1412
1506
  const content = await getHTMLSnippet(targetElement2);
@@ -1444,9 +1538,9 @@ ${context}
1444
1538
  let didCopy = false;
1445
1539
  try {
1446
1540
  if (isExtensionTextOnlyMode()) {
1447
- const plainTextContent = createCombinedTextContent(targetElements);
1448
- if (plainTextContent.length > 0) {
1449
- didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
1541
+ const markdownContent = createCombinedMarkdownContent(targetElements);
1542
+ if (markdownContent.length > 0) {
1543
+ didCopy = await copyContent(markdownContent, options.playCopySound ? playCopySound : void 0);
1450
1544
  }
1451
1545
  } else {
1452
1546
  const elementSnippetResults = await Promise.allSettled(targetElements.map((element) => getHTMLSnippet(element)));
@@ -1604,6 +1698,7 @@ ${context}
1604
1698
  document.body.style.cursor = "crosshair";
1605
1699
  };
1606
1700
  const deactivateRenderer = () => {
1701
+ setIsToggleMode(false);
1607
1702
  setIsHoldingKeys(false);
1608
1703
  setIsActivated(false);
1609
1704
  document.body.style.cursor = "";
@@ -1629,11 +1724,25 @@ ${context}
1629
1724
  deactivateRenderer();
1630
1725
  return;
1631
1726
  }
1727
+ if (event.key === "Enter" && isHoldingKeys()) {
1728
+ setIsToggleMode(true);
1729
+ if (keydownSpamTimerId !== null) {
1730
+ window.clearTimeout(keydownSpamTimerId);
1731
+ keydownSpamTimerId = null;
1732
+ }
1733
+ if (!isActivated()) {
1734
+ if (holdTimerId) window.clearTimeout(holdTimerId);
1735
+ activateRenderer();
1736
+ options.onActivate?.();
1737
+ }
1738
+ return;
1739
+ }
1632
1740
  if (!options.allowActivationInsideInput && isKeyboardEventTriggeredByInput(event)) {
1633
1741
  return;
1634
1742
  }
1635
1743
  if (!isTargetKeyCombination(event)) return;
1636
1744
  if (isActivated()) {
1745
+ if (isToggleMode()) return;
1637
1746
  if (keydownSpamTimerId !== null) {
1638
1747
  window.clearTimeout(keydownSpamTimerId);
1639
1748
  }
@@ -1654,13 +1763,15 @@ ${context}
1654
1763
  options.onActivate?.();
1655
1764
  }, options.keyHoldDuration);
1656
1765
  }, {
1657
- signal: eventListenerSignal
1766
+ signal: eventListenerSignal,
1767
+ capture: true
1658
1768
  });
1659
1769
  window.addEventListener("keyup", (event) => {
1660
1770
  if (!isHoldingKeys() && !isActivated()) return;
1661
1771
  const isReleasingModifier = !event.metaKey && !event.ctrlKey;
1662
1772
  const isReleasingC = event.key.toLowerCase() === "c";
1663
1773
  if (isReleasingC || isReleasingModifier) {
1774
+ if (isToggleMode()) return;
1664
1775
  deactivateRenderer();
1665
1776
  }
1666
1777
  }, {
@@ -1742,9 +1853,17 @@ ${context}
1742
1853
  if (isRendererActive() || isCopying() || didJustDrag()) {
1743
1854
  event.preventDefault();
1744
1855
  event.stopPropagation();
1745
- if (didJustDrag()) {
1856
+ const hadDrag = didJustDrag();
1857
+ if (hadDrag) {
1746
1858
  setDidJustDrag(false);
1747
1859
  }
1860
+ if (isToggleMode() && !isCopying()) {
1861
+ if (!isHoldingKeys()) {
1862
+ deactivateRenderer();
1863
+ } else {
1864
+ setIsToggleMode(false);
1865
+ }
1866
+ }
1748
1867
  }
1749
1868
  }, {
1750
1869
  signal: eventListenerSignal,
@@ -1767,10 +1886,14 @@ ${context}
1767
1886
  document.body.style.cursor = "";
1768
1887
  });
1769
1888
  const rendererRoot = mountRoot();
1770
- const selectionVisible = solidJs.createMemo(() => false);
1889
+ const selectionVisible = solidJs.createMemo(() => isRendererActive() && !isDragging() && Boolean(targetElement()));
1771
1890
  const dragVisible = solidJs.createMemo(() => isRendererActive() && isDraggingBeyondThreshold());
1772
1891
  const labelVariant = solidJs.createMemo(() => isCopying() ? "processing" : "hover");
1773
- const labelVisible = solidJs.createMemo(() => isRendererActive() && !isDragging() && mouseHasSettled() && (Boolean(targetElement()) && !isSameAsLast() || !targetElement()) || isCopying());
1892
+ const labelVisible = solidJs.createMemo(() => {
1893
+ if (isCopying()) return true;
1894
+ if (successLabels().length > 0) return false;
1895
+ return isRendererActive() && !isDragging() && mouseHasSettled() && (Boolean(targetElement()) && !isSameAsLast() || !targetElement());
1896
+ });
1774
1897
  const progressVisible = solidJs.createMemo(() => isCopying() && showProgressIndicator() && hasValidMousePosition());
1775
1898
  const crosshairVisible = solidJs.createMemo(() => isRendererActive() && !isDragging());
1776
1899
  web.render(() => web.createComponent(ReactGrabRenderer, {
@@ -1807,6 +1930,7 @@ ${context}
1807
1930
  get labelVisible() {
1808
1931
  return labelVisible();
1809
1932
  },
1933
+ labelZIndex: 2147483646,
1810
1934
  get progressVisible() {
1811
1935
  return progressVisible();
1812
1936
  },
@@ -1857,6 +1981,8 @@ if (!window[EXTENSION_MARKER]) {
1857
1981
  globalApi = init();
1858
1982
  }
1859
1983
 
1984
+ exports.elementToMarkdown = elementToMarkdown;
1860
1985
  exports.getGlobalApi = getGlobalApi;
1986
+ exports.htmlToMarkdown = htmlToMarkdown;
1861
1987
  exports.init = init;
1862
1988
  exports.playCopySound = playCopySound;
package/dist/index.d.cts CHANGED
@@ -57,6 +57,9 @@ declare const init: (rawOptions?: Options) => ReactGrabAPI;
57
57
 
58
58
  declare const playCopySound: () => void;
59
59
 
60
+ declare const htmlToMarkdown: (html: string) => string;
61
+ declare const elementToMarkdown: (element: Element) => string;
62
+
60
63
  declare global {
61
64
  interface Window {
62
65
  __REACT_GRAB_EXTENSION_ACTIVE__?: boolean;
@@ -64,4 +67,4 @@ declare global {
64
67
  }
65
68
  declare const getGlobalApi: () => ReactGrabAPI | null;
66
69
 
67
- export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, getGlobalApi, init, playCopySound };
70
+ export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, elementToMarkdown, getGlobalApi, htmlToMarkdown, init, playCopySound };
package/dist/index.d.ts CHANGED
@@ -57,6 +57,9 @@ declare const init: (rawOptions?: Options) => ReactGrabAPI;
57
57
 
58
58
  declare const playCopySound: () => void;
59
59
 
60
+ declare const htmlToMarkdown: (html: string) => string;
61
+ declare const elementToMarkdown: (element: Element) => string;
62
+
60
63
  declare global {
61
64
  interface Window {
62
65
  __REACT_GRAB_EXTENSION_ACTIVE__?: boolean;
@@ -64,4 +67,4 @@ declare global {
64
67
  }
65
68
  declare const getGlobalApi: () => ReactGrabAPI | null;
66
69
 
67
- export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, getGlobalApi, init, playCopySound };
70
+ export { type Options, type OverlayBounds, type ReactGrabAPI, type ReactGrabRendererProps, elementToMarkdown, getGlobalApi, htmlToMarkdown, init, playCopySound };