react-grab 0.0.38 → 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.js CHANGED
@@ -3,6 +3,7 @@ import { createRoot, createSignal, createMemo, createEffect, on, onCleanup, Show
3
3
  import { instrument, _fiberRoots, getFiberFromHostInstance, traverseFiber, isCompositeFiber, getDisplayName } from 'bippy';
4
4
  import { getSourceFromHostInstance, normalizeFileName, isSourceFile } from 'bippy/dist/source';
5
5
  import { finder } from '@medv/finder';
6
+ import TurndownService from 'turndown';
6
7
 
7
8
  /**
8
9
  * @license MIT
@@ -176,7 +177,7 @@ var SelectionBox = (props) => {
176
177
  position: "fixed",
177
178
  "box-sizing": "border-box",
178
179
  "pointer-events": props.variant === "drag" ? "none" : "auto",
179
- "z-index": "2147483646"
180
+ "z-index": props.variant === "grabbed" ? "2147483645" : "2147483646"
180
181
  };
181
182
  const variantStyle = () => {
182
183
  if (props.variant === "drag") {
@@ -188,10 +189,16 @@ var SelectionBox = (props) => {
188
189
  cursor: "crosshair"
189
190
  };
190
191
  }
192
+ if (props.variant === "selection") {
193
+ return {
194
+ border: "1px dashed rgba(210, 57, 192, 0.5)",
195
+ "background-color": "rgba(210, 57, 192, 0.08)"
196
+ };
197
+ }
191
198
  return {
192
199
  border: "1px solid rgb(210, 57, 192)",
193
- "background-color": "rgba(210, 57, 192, 0.2)",
194
- transition: props.variant === "grabbed" ? "opacity 0.3s ease-out" : void 0
200
+ "background-color": "rgba(210, 57, 192, 0.08)",
201
+ transition: "opacity 0.3s ease-out"
195
202
  };
196
203
  };
197
204
  return createComponent(Show, {
@@ -678,10 +685,10 @@ var ReactGrabRenderer = (props) => {
678
685
  return label.text;
679
686
  },
680
687
  get x() {
681
- return label.x;
688
+ return props.mouseX ?? 0;
682
689
  },
683
690
  get y() {
684
- return label.y;
691
+ return props.mouseY ?? 0;
685
692
  }
686
693
  })
687
694
  }), createComponent(Show, {
@@ -746,7 +753,7 @@ var generateCSSSelector = (element) => {
746
753
  return finder(element);
747
754
  };
748
755
  var truncateString = (string, maxLength) => string.length > maxLength ? `${string.substring(0, maxLength)}...` : string;
749
- var isInternalComponent = (name) => !isCapitalized(name) || name.startsWith("_") || name.includes("Provider") && name.includes("Context");
756
+ var isInternalComponent = (name) => !isCapitalized(name) || name.startsWith("_") || name.startsWith("Primitive.") || name.includes("Provider") && name.includes("Context");
750
757
  var getNearestComponentDisplayName = (element) => {
751
758
  const fiber = getFiberFromHostInstance(element);
752
759
  if (!fiber) return null;
@@ -771,8 +778,16 @@ var formatComponentSourceLocation = async (el) => {
771
778
  const source = await getSourceFromHostInstance(el);
772
779
  if (!source) return null;
773
780
  const fileName = normalizeFileName(source.fileName);
774
- if (!isSourceFile(fileName)) return null;
775
- return `${fileName}:${source.lineNumber}:${source.columnNumber}`;
781
+ if (isSourceFile(fileName)) {
782
+ return `${fileName}:${source.lineNumber}:${source.columnNumber}`;
783
+ }
784
+ if (fileName && (fileName.includes(".tsx") || fileName.includes(".ts") || fileName.includes(".jsx") || fileName.includes(".js"))) {
785
+ const cleanedFileName = fileName.replace(/^webpack:\/\/_N_E\//, "").replace(/^webpack:\/\/\//, "").replace(/^webpack:\/\//, "").replace(/^\.\//, "");
786
+ if (cleanedFileName && !cleanedFileName.startsWith("node_modules") && !cleanedFileName.includes(".next") && !cleanedFileName.startsWith("webpack")) {
787
+ return `${cleanedFileName}:${source.lineNumber}:${source.columnNumber}`;
788
+ }
789
+ }
790
+ return null;
776
791
  };
777
792
  var getHTMLSnippet = async (element) => {
778
793
  const semanticTags = /* @__PURE__ */ new Set([
@@ -870,14 +885,26 @@ var getHTMLSnippet = async (element) => {
870
885
  ancestors.map((ancestor) => formatComponentSourceLocation(ancestor))
871
886
  );
872
887
  const elementSource = await formatComponentSourceLocation(element);
888
+ const ancestorElementIndents = [];
889
+ const ancestorComponentIndents = [];
890
+ let currentIndentLevel = 0;
891
+ const getIndent = (level) => " ".repeat(level);
873
892
  for (let i = 0; i < ancestors.length; i++) {
874
- const indent2 = " ".repeat(i);
875
893
  const componentName = ancestorComponents[i];
876
894
  const source = ancestorSources[i];
877
- if (componentName && source && (i === 0 || ancestorComponents[i - 1] !== componentName)) {
878
- lines.push(`${indent2}<${componentName} source="${source}">`);
895
+ const isNewComponent = componentName && source && (i === 0 || ancestorComponents[i - 1] !== componentName);
896
+ if (isNewComponent) {
897
+ ancestorComponentIndents[i] = currentIndentLevel;
898
+ lines.push(
899
+ `${getIndent(currentIndentLevel)}<${componentName} used-at="${source}">`
900
+ );
901
+ currentIndentLevel++;
879
902
  }
880
- lines.push(`${indent2}${formatElementOpeningTag(ancestors[i], true)}`);
903
+ ancestorElementIndents[i] = currentIndentLevel;
904
+ lines.push(
905
+ `${getIndent(currentIndentLevel)}${formatElementOpeningTag(ancestors[i], true)}`
906
+ );
907
+ currentIndentLevel++;
881
908
  }
882
909
  const parent = element.parentElement;
883
910
  let targetIndex = -1;
@@ -885,30 +912,37 @@ var getHTMLSnippet = async (element) => {
885
912
  const siblings = Array.from(parent.children);
886
913
  targetIndex = siblings.indexOf(element);
887
914
  if (targetIndex > 0) {
888
- const prevSibling = siblings[targetIndex - 1];
889
- const prevId = extractSiblingIdentifier(prevSibling);
890
- if (prevId && targetIndex <= 2) {
891
- const indent2 = " ".repeat(ancestors.length);
892
- lines.push(`${indent2} ${formatElementOpeningTag(prevSibling, true)}`);
893
- lines.push(`${indent2} </${prevSibling.tagName.toLowerCase()}>`);
894
- } else if (targetIndex > 0) {
895
- const indent2 = " ".repeat(ancestors.length);
915
+ const indent = getIndent(currentIndentLevel);
916
+ if (targetIndex <= 2) {
917
+ for (let i = 0; i < targetIndex; i++) {
918
+ const sibling = siblings[i];
919
+ const siblingId = extractSiblingIdentifier(sibling);
920
+ if (siblingId) {
921
+ lines.push(`${indent}${formatElementOpeningTag(sibling, true)}`);
922
+ lines.push(`${indent}</${sibling.tagName.toLowerCase()}>`);
923
+ }
924
+ }
925
+ } else {
896
926
  lines.push(
897
- `${indent2} ... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
927
+ `${indent}... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
898
928
  );
899
929
  }
900
930
  }
901
931
  }
902
- const indent = " ".repeat(ancestors.length);
903
932
  const lastAncestorComponent = ancestors.length > 0 ? ancestorComponents[ancestorComponents.length - 1] : null;
904
933
  const showElementComponent = elementComponent && elementSource && elementComponent !== lastAncestorComponent;
934
+ let elementIndentLevel = currentIndentLevel;
935
+ const elementComponentIndentLevel = currentIndentLevel;
905
936
  if (showElementComponent) {
906
- lines.push(`${indent} <${elementComponent} used-at="${elementSource}">`);
937
+ lines.push(
938
+ `${getIndent(elementIndentLevel)}<${elementComponent} used-at="${elementSource}">`
939
+ );
940
+ elementIndentLevel++;
907
941
  }
908
- lines.push(`${indent} <!-- IMPORTANT: selected element -->`);
942
+ const elementIndent = getIndent(elementIndentLevel);
943
+ lines.push(`${elementIndent}<!-- IMPORTANT: selected element -->`);
909
944
  const textContent = extractTruncatedTextContent(element);
910
945
  const childrenCount = element.children.length;
911
- const elementIndent = `${indent}${showElementComponent ? " " : " "}`;
912
946
  if (textContent && childrenCount === 0 && textContent.length < 40) {
913
947
  lines.push(
914
948
  `${elementIndent}${formatElementOpeningTag(element)}${textContent}${formatElementClosingTag(
@@ -928,31 +962,37 @@ var getHTMLSnippet = async (element) => {
928
962
  lines.push(`${elementIndent}${formatElementClosingTag(element)}`);
929
963
  }
930
964
  if (showElementComponent) {
931
- lines.push(`${indent} </${elementComponent}>`);
965
+ lines.push(
966
+ `${getIndent(elementComponentIndentLevel)}</${elementComponent}>`
967
+ );
932
968
  }
933
969
  if (parent && targetIndex >= 0) {
934
970
  const siblings = Array.from(parent.children);
935
971
  const siblingsAfter = siblings.length - targetIndex - 1;
936
972
  if (siblingsAfter > 0) {
937
- const nextSibling = siblings[targetIndex + 1];
938
- const nextId = extractSiblingIdentifier(nextSibling);
939
- if (nextId && siblingsAfter <= 2) {
940
- lines.push(`${indent} ${formatElementOpeningTag(nextSibling, true)}`);
941
- lines.push(`${indent} </${nextSibling.tagName.toLowerCase()}>`);
973
+ const indent = getIndent(currentIndentLevel);
974
+ if (siblingsAfter <= 2) {
975
+ for (let i = targetIndex + 1; i < siblings.length; i++) {
976
+ const sibling = siblings[i];
977
+ const siblingId = extractSiblingIdentifier(sibling);
978
+ if (siblingId) {
979
+ lines.push(`${indent}${formatElementOpeningTag(sibling, true)}`);
980
+ lines.push(`${indent}</${sibling.tagName.toLowerCase()}>`);
981
+ }
982
+ }
942
983
  } else {
943
984
  lines.push(
944
- `${indent} ... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
985
+ `${indent}... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
945
986
  );
946
987
  }
947
988
  }
948
989
  }
949
990
  for (let i = ancestors.length - 1; i >= 0; i--) {
950
- const indent2 = " ".repeat(i);
951
- lines.push(`${indent2}${formatElementClosingTag(ancestors[i])}`);
952
- const componentName = ancestorComponents[i];
953
- const source = ancestorSources[i];
954
- if (componentName && source && (i === ancestors.length - 1 || ancestorComponents[i + 1] !== componentName)) {
955
- lines.push(`${indent2}</${componentName}>`);
991
+ const elementIndent2 = getIndent(ancestorElementIndents[i]);
992
+ lines.push(`${elementIndent2}${formatElementClosingTag(ancestors[i])}`);
993
+ if (ancestorComponentIndents[i] !== void 0) {
994
+ const compIndent = getIndent(ancestorComponentIndents[i]);
995
+ lines.push(`${compIndent}</${ancestorComponents[i]}>`);
956
996
  }
957
997
  }
958
998
  lines.push("```");
@@ -1210,6 +1250,62 @@ var isLocalhost = () => {
1210
1250
  const hostname = window.location.hostname;
1211
1251
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
1212
1252
  };
1253
+ var turndownService = null;
1254
+ var getTurndownService = () => {
1255
+ if (!turndownService) {
1256
+ turndownService = new TurndownService({
1257
+ headingStyle: "atx",
1258
+ codeBlockStyle: "fenced",
1259
+ emDelimiter: "_",
1260
+ bulletListMarker: "-",
1261
+ linkStyle: "inlined",
1262
+ linkReferenceStyle: "full"
1263
+ });
1264
+ turndownService.addRule("strikethrough", {
1265
+ filter: ["del", "s"],
1266
+ replacement: (content) => `~~${content}~~`
1267
+ });
1268
+ turndownService.addRule("removeHidden", {
1269
+ filter: (node) => {
1270
+ if (node instanceof HTMLElement) {
1271
+ const style = window.getComputedStyle(node);
1272
+ return style.display === "none" || style.visibility === "hidden" || style.opacity === "0";
1273
+ }
1274
+ return false;
1275
+ },
1276
+ replacement: () => ""
1277
+ });
1278
+ turndownService.addRule("preserveButtons", {
1279
+ filter: ["button"],
1280
+ replacement: (content) => content ? `[${content}]` : ""
1281
+ });
1282
+ turndownService.addRule("preserveInputs", {
1283
+ filter: (node) => {
1284
+ if (node.nodeName === "INPUT" && node instanceof HTMLInputElement) {
1285
+ return true;
1286
+ }
1287
+ return false;
1288
+ },
1289
+ replacement: (_, node) => {
1290
+ if (node instanceof HTMLInputElement) {
1291
+ const value = node.value || node.placeholder;
1292
+ return value ? `[${value}]` : "";
1293
+ }
1294
+ return "";
1295
+ }
1296
+ });
1297
+ }
1298
+ return turndownService;
1299
+ };
1300
+ var htmlToMarkdown = (html) => {
1301
+ const service = getTurndownService();
1302
+ return service.turndown(html).trim();
1303
+ };
1304
+ var elementToMarkdown = (element) => {
1305
+ const clonedElement = element.cloneNode(true);
1306
+ const service = getTurndownService();
1307
+ return service.turndown(clonedElement).trim();
1308
+ };
1213
1309
 
1214
1310
  // src/core.tsx
1215
1311
  var PROGRESS_INDICATOR_DELAY_MS = 150;
@@ -1249,6 +1345,7 @@ var init = (rawOptions) => {
1249
1345
  const [grabbedBoxes, setGrabbedBoxes] = createSignal([]);
1250
1346
  const [successLabels, setSuccessLabels] = createSignal([]);
1251
1347
  const [isActivated, setIsActivated] = createSignal(false);
1348
+ const [isToggleMode, setIsToggleMode] = createSignal(false);
1252
1349
  const [showProgressIndicator, setShowProgressIndicator] = createSignal(false);
1253
1350
  const [didJustDrag, setDidJustDrag] = createSignal(false);
1254
1351
  const [copyStartX, setCopyStartX] = createSignal(OFFSCREEN_POSITION);
@@ -1278,13 +1375,11 @@ var init = (rawOptions) => {
1278
1375
  setGrabbedBoxes((previousBoxes) => previousBoxes.filter((box) => box.id !== boxId));
1279
1376
  }, SUCCESS_LABEL_DURATION_MS);
1280
1377
  };
1281
- const showTemporarySuccessLabel = (text, positionX, positionY) => {
1378
+ const showTemporarySuccessLabel = (text) => {
1282
1379
  const labelId = `success-${Date.now()}-${Math.random()}`;
1283
1380
  setSuccessLabels((previousLabels) => [...previousLabels, {
1284
1381
  id: labelId,
1285
- text,
1286
- x: positionX,
1287
- y: positionY
1382
+ text
1288
1383
  }]);
1289
1384
  setTimeout(() => {
1290
1385
  setSuccessLabels((previousLabels) => previousLabels.filter((label) => label.id !== labelId));
@@ -1330,7 +1425,7 @@ ${context}
1330
1425
  traverseFiber(fiber, (currentFiber) => {
1331
1426
  if (isCompositeFiber(currentFiber)) {
1332
1427
  const displayName = getDisplayName(currentFiber);
1333
- if (displayName && isCapitalized(displayName) && !displayName.startsWith("_")) {
1428
+ if (displayName && isCapitalized(displayName) && !displayName.startsWith("_") && !displayName.startsWith("Primitive.")) {
1334
1429
  componentName = displayName;
1335
1430
  return true;
1336
1431
  }
@@ -1373,6 +1468,13 @@ ${context}
1373
1468
  await operation().finally(() => {
1374
1469
  setIsCopying(false);
1375
1470
  stopProgressAnimation();
1471
+ if (isToggleMode()) {
1472
+ if (!isHoldingKeys()) {
1473
+ deactivateRenderer();
1474
+ } else {
1475
+ setIsToggleMode(false);
1476
+ }
1477
+ }
1376
1478
  });
1377
1479
  };
1378
1480
  const hasInnerText = (element) => "innerText" in element;
@@ -1383,15 +1485,16 @@ ${context}
1383
1485
  return element.textContent ?? "";
1384
1486
  };
1385
1487
  const createCombinedTextContent = (elements) => elements.map((element) => extractElementTextContent(element).trim()).filter((textContent) => textContent.length > 0).join("\n\n");
1488
+ const createCombinedMarkdownContent = (elements) => elements.map((element) => elementToMarkdown(element).trim()).filter((markdownContent) => markdownContent.length > 0).join("\n\n");
1386
1489
  const copySingleElementToClipboard = async (targetElement2) => {
1387
1490
  showTemporaryGrabbedBox(createElementBounds(targetElement2));
1388
1491
  await new Promise((resolve) => requestAnimationFrame(resolve));
1389
1492
  let didCopy = false;
1390
1493
  try {
1391
1494
  if (isExtensionTextOnlyMode()) {
1392
- const plainTextContent = createCombinedTextContent([targetElement2]);
1393
- if (plainTextContent.length > 0) {
1394
- didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
1495
+ const markdownContent = createCombinedMarkdownContent([targetElement2]);
1496
+ if (markdownContent.length > 0) {
1497
+ didCopy = await copyContent(markdownContent, options.playCopySound ? playCopySound : void 0);
1395
1498
  }
1396
1499
  } else {
1397
1500
  const content = await getHTMLSnippet(targetElement2);
@@ -1416,7 +1519,7 @@ ${context}
1416
1519
  }
1417
1520
  }
1418
1521
  if (didCopy) {
1419
- showTemporarySuccessLabel(extractElementLabelText(targetElement2), copyStartX(), copyStartY());
1522
+ showTemporarySuccessLabel(extractElementLabelText(targetElement2));
1420
1523
  }
1421
1524
  notifyElementsSelected([targetElement2]);
1422
1525
  };
@@ -1429,9 +1532,9 @@ ${context}
1429
1532
  let didCopy = false;
1430
1533
  try {
1431
1534
  if (isExtensionTextOnlyMode()) {
1432
- const plainTextContent = createCombinedTextContent(targetElements);
1433
- if (plainTextContent.length > 0) {
1434
- didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
1535
+ const markdownContent = createCombinedMarkdownContent(targetElements);
1536
+ if (markdownContent.length > 0) {
1537
+ didCopy = await copyContent(markdownContent, options.playCopySound ? playCopySound : void 0);
1435
1538
  }
1436
1539
  } else {
1437
1540
  const elementSnippetResults = await Promise.allSettled(targetElements.map((element) => getHTMLSnippet(element)));
@@ -1461,7 +1564,7 @@ ${context}
1461
1564
  } catch {
1462
1565
  }
1463
1566
  if (didCopy) {
1464
- showTemporarySuccessLabel(`${targetElements.length} elements`, copyStartX(), copyStartY());
1567
+ showTemporarySuccessLabel(`${targetElements.length} elements`);
1465
1568
  }
1466
1569
  notifyElementsSelected(targetElements);
1467
1570
  };
@@ -1589,6 +1692,7 @@ ${context}
1589
1692
  document.body.style.cursor = "crosshair";
1590
1693
  };
1591
1694
  const deactivateRenderer = () => {
1695
+ setIsToggleMode(false);
1592
1696
  setIsHoldingKeys(false);
1593
1697
  setIsActivated(false);
1594
1698
  document.body.style.cursor = "";
@@ -1614,11 +1718,25 @@ ${context}
1614
1718
  deactivateRenderer();
1615
1719
  return;
1616
1720
  }
1721
+ if (event.key === "Enter" && isHoldingKeys()) {
1722
+ setIsToggleMode(true);
1723
+ if (keydownSpamTimerId !== null) {
1724
+ window.clearTimeout(keydownSpamTimerId);
1725
+ keydownSpamTimerId = null;
1726
+ }
1727
+ if (!isActivated()) {
1728
+ if (holdTimerId) window.clearTimeout(holdTimerId);
1729
+ activateRenderer();
1730
+ options.onActivate?.();
1731
+ }
1732
+ return;
1733
+ }
1617
1734
  if (!options.allowActivationInsideInput && isKeyboardEventTriggeredByInput(event)) {
1618
1735
  return;
1619
1736
  }
1620
1737
  if (!isTargetKeyCombination(event)) return;
1621
1738
  if (isActivated()) {
1739
+ if (isToggleMode()) return;
1622
1740
  if (keydownSpamTimerId !== null) {
1623
1741
  window.clearTimeout(keydownSpamTimerId);
1624
1742
  }
@@ -1639,13 +1757,15 @@ ${context}
1639
1757
  options.onActivate?.();
1640
1758
  }, options.keyHoldDuration);
1641
1759
  }, {
1642
- signal: eventListenerSignal
1760
+ signal: eventListenerSignal,
1761
+ capture: true
1643
1762
  });
1644
1763
  window.addEventListener("keyup", (event) => {
1645
1764
  if (!isHoldingKeys() && !isActivated()) return;
1646
1765
  const isReleasingModifier = !event.metaKey && !event.ctrlKey;
1647
1766
  const isReleasingC = event.key.toLowerCase() === "c";
1648
1767
  if (isReleasingC || isReleasingModifier) {
1768
+ if (isToggleMode()) return;
1649
1769
  deactivateRenderer();
1650
1770
  }
1651
1771
  }, {
@@ -1727,9 +1847,17 @@ ${context}
1727
1847
  if (isRendererActive() || isCopying() || didJustDrag()) {
1728
1848
  event.preventDefault();
1729
1849
  event.stopPropagation();
1730
- if (didJustDrag()) {
1850
+ const hadDrag = didJustDrag();
1851
+ if (hadDrag) {
1731
1852
  setDidJustDrag(false);
1732
1853
  }
1854
+ if (isToggleMode() && !isCopying()) {
1855
+ if (!isHoldingKeys()) {
1856
+ deactivateRenderer();
1857
+ } else {
1858
+ setIsToggleMode(false);
1859
+ }
1860
+ }
1733
1861
  }
1734
1862
  }, {
1735
1863
  signal: eventListenerSignal,
@@ -1752,10 +1880,14 @@ ${context}
1752
1880
  document.body.style.cursor = "";
1753
1881
  });
1754
1882
  const rendererRoot = mountRoot();
1755
- const selectionVisible = createMemo(() => false);
1883
+ const selectionVisible = createMemo(() => isRendererActive() && !isDragging() && Boolean(targetElement()));
1756
1884
  const dragVisible = createMemo(() => isRendererActive() && isDraggingBeyondThreshold());
1757
1885
  const labelVariant = createMemo(() => isCopying() ? "processing" : "hover");
1758
- const labelVisible = createMemo(() => isRendererActive() && !isDragging() && mouseHasSettled() && (Boolean(targetElement()) && !isSameAsLast() || !targetElement()) || isCopying());
1886
+ const labelVisible = createMemo(() => {
1887
+ if (isCopying()) return true;
1888
+ if (successLabels().length > 0) return false;
1889
+ return isRendererActive() && !isDragging() && mouseHasSettled() && (Boolean(targetElement()) && !isSameAsLast() || !targetElement());
1890
+ });
1759
1891
  const progressVisible = createMemo(() => isCopying() && showProgressIndicator() && hasValidMousePosition());
1760
1892
  const crosshairVisible = createMemo(() => isRendererActive() && !isDragging());
1761
1893
  render(() => createComponent(ReactGrabRenderer, {
@@ -1792,6 +1924,7 @@ ${context}
1792
1924
  get labelVisible() {
1793
1925
  return labelVisible();
1794
1926
  },
1927
+ labelZIndex: 2147483646,
1795
1928
  get progressVisible() {
1796
1929
  return progressVisible();
1797
1930
  },
@@ -1842,4 +1975,4 @@ if (!window[EXTENSION_MARKER]) {
1842
1975
  globalApi = init();
1843
1976
  }
1844
1977
 
1845
- export { getGlobalApi, init, playCopySound };
1978
+ export { elementToMarkdown, getGlobalApi, htmlToMarkdown, init, playCopySound };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-grab",
3
- "version": "0.0.38",
4
- "description": "",
3
+ "version": "0.0.40",
4
+ "description": "Grab any element in your app and give it to Cursor, Claude Code, or other AI coding agents.",
5
5
  "keywords": [
6
6
  "react",
7
7
  "grab",
@@ -54,6 +54,7 @@
54
54
  "devDependencies": {
55
55
  "@babel/core": "^7.28.5",
56
56
  "@babel/preset-typescript": "^7.28.5",
57
+ "@types/turndown": "^5.0.6",
57
58
  "babel-preset-solid": "^1.9.10",
58
59
  "esbuild-plugin-babel": "^0.2.3",
59
60
  "eslint": "^9.37.0",
@@ -70,7 +71,8 @@
70
71
  "bippy": "^0.5.14",
71
72
  "modern-screenshot": "^4.6.6",
72
73
  "solid-js": "^1.9.10",
73
- "solid-sonner": "^0.2.8"
74
+ "solid-sonner": "^0.2.8",
75
+ "turndown": "^7.2.2"
74
76
  },
75
77
  "scripts": {
76
78
  "build": "NODE_ENV=production tsup",