react-grab 0.1.15 → 0.1.16

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/react.js CHANGED
@@ -2140,7 +2140,7 @@ var VERSION, VIEWPORT_MARGIN_PX, OFFSCREEN_POSITION, SELECTION_LERP_FACTOR, FEED
2140
2140
  var init_constants = __esm({
2141
2141
  "src/constants.ts"() {
2142
2142
  "use strict";
2143
- VERSION = "0.1.15";
2143
+ VERSION = "0.1.16";
2144
2144
  VIEWPORT_MARGIN_PX = 8;
2145
2145
  OFFSCREEN_POSITION = -1e3;
2146
2146
  SELECTION_LERP_FACTOR = 0.95;
@@ -10189,7 +10189,7 @@ var init_icon_copy = __esm({
10189
10189
  });
10190
10190
 
10191
10191
  // src/components/history-dropdown.tsx
10192
- var _tmpl$37, _tmpl$211, _tmpl$38, _tmpl$45, DEFAULT_OFFSCREEN_POSITION2, ITEM_ACTION_CLASS, EDGE_TO_TRANSFORM_ORIGIN, formatRelativeTime, HistoryDropdown;
10192
+ var _tmpl$37, _tmpl$211, _tmpl$38, _tmpl$45, DEFAULT_OFFSCREEN_POSITION2, ITEM_ACTION_CLASS, EDGE_TO_TRANSFORM_ORIGIN, formatRelativeTime, getHistoryItemDisplayName, HistoryDropdown;
10193
10193
  var init_history_dropdown = __esm({
10194
10194
  "src/components/history-dropdown.tsx"() {
10195
10195
  "use strict";
@@ -10234,6 +10234,12 @@ var init_history_dropdown = __esm({
10234
10234
  if (elapsedHours < 24) return `${elapsedHours}h`;
10235
10235
  return `${Math.floor(elapsedHours / 24)}d`;
10236
10236
  };
10237
+ getHistoryItemDisplayName = (item) => {
10238
+ if (item.elementsCount && item.elementsCount > 1) {
10239
+ return `${item.elementsCount} elements`;
10240
+ }
10241
+ return item.componentName ?? item.tagName;
10242
+ };
10237
10243
  HistoryDropdown = (props) => {
10238
10244
  let containerRef;
10239
10245
  const [measuredWidth, setMeasuredWidth] = createSignal(0);
@@ -10452,7 +10458,7 @@ var init_history_dropdown = __esm({
10452
10458
  }, FEEDBACK_DURATION_MS);
10453
10459
  };
10454
10460
  _el$10.$$pointerdown = (event) => event.stopPropagation();
10455
- insert(_el$12, () => item.componentName ?? item.tagName);
10461
+ insert(_el$12, () => getHistoryItemDisplayName(item));
10456
10462
  insert(_el$11, createComponent(Show, {
10457
10463
  get when() {
10458
10464
  return item.commentText;
@@ -10558,6 +10564,7 @@ var init_renderer = __esm({
10558
10564
  init_solid();
10559
10565
  init_constants();
10560
10566
  init_build_open_file_url();
10567
+ init_is_element_connected();
10561
10568
  init_overlay_canvas();
10562
10569
  init_selection_label();
10563
10570
  init_toolbar();
@@ -10777,6 +10784,9 @@ var init_renderer = __esm({
10777
10784
  get componentName() {
10778
10785
  return instance().componentName;
10779
10786
  },
10787
+ get elementsCount() {
10788
+ return instance().elementsCount;
10789
+ },
10780
10790
  get selectionBounds() {
10781
10791
  return instance().bounds;
10782
10792
  },
@@ -10806,7 +10816,12 @@ var init_renderer = __esm({
10806
10816
  return instance().hideArrow;
10807
10817
  },
10808
10818
  get onShowContextMenu() {
10809
- return (instance().status === "copied" || instance().status === "fading") && instance().element && (document.body ?? document.documentElement).contains(instance().element) ? () => props.onShowContextMenuInstance?.(instance().id) : void 0;
10819
+ const currentInstance = instance();
10820
+ const hasCompletedStatus = currentInstance.status === "copied" || currentInstance.status === "fading";
10821
+ if (!hasCompletedStatus || !isElementConnected(currentInstance.element)) {
10822
+ return void 0;
10823
+ }
10824
+ return () => props.onShowContextMenuInstance?.(currentInstance.id);
10810
10825
  },
10811
10826
  onHoverChange: (isHovered) => props.onLabelInstanceHoverChange?.(instance().id, isHovered)
10812
10827
  })
@@ -13957,7 +13972,7 @@ var init_log_intro = __esm({
13957
13972
  init_is_extension_context();
13958
13973
  logIntro = () => {
13959
13974
  try {
13960
- const version = "0.1.15";
13975
+ const version = "0.1.16";
13961
13976
  const logoDataUri = `data:image/svg+xml;base64,${btoa(LOGO_SVG)}`;
13962
13977
  console.log(
13963
13978
  `%cReact Grab${version ? ` v${version}` : ""}%c
@@ -14113,6 +14128,7 @@ var copyHtmlPlugin;
14113
14128
  var init_copy_html = __esm({
14114
14129
  "src/core/plugins/copy-html.ts"() {
14115
14130
  "use strict";
14131
+ init_copy_content();
14116
14132
  copyHtmlPlugin = {
14117
14133
  name: "copy-html",
14118
14134
  actions: [
@@ -14130,8 +14146,7 @@ var init_copy_html = __esm({
14130
14146
  context.elements
14131
14147
  );
14132
14148
  if (!transformedHtml) return false;
14133
- await navigator.clipboard.writeText(transformedHtml);
14134
- return true;
14149
+ return copyContent(transformedHtml);
14135
14150
  });
14136
14151
  }
14137
14152
  }
@@ -14210,7 +14225,12 @@ var init_history_storage = __esm({
14210
14225
  try {
14211
14226
  const serializedHistoryItems = sessionStorage.getItem(SESSION_STORAGE_KEY);
14212
14227
  if (!serializedHistoryItems) return [];
14213
- return JSON.parse(serializedHistoryItems);
14228
+ const parsedHistoryItems = JSON.parse(serializedHistoryItems);
14229
+ return parsedHistoryItems.map((historyItem) => ({
14230
+ ...historyItem,
14231
+ elementsCount: Math.max(1, historyItem.elementsCount ?? 1),
14232
+ previewBounds: historyItem.previewBounds ?? []
14233
+ }));
14214
14234
  } catch {
14215
14235
  return [];
14216
14236
  }
@@ -14401,12 +14421,21 @@ var init_core = __esm({
14401
14421
  const [hasUnreadHistoryItems, setHasUnreadHistoryItems] = createSignal(false);
14402
14422
  const [isHistoryHoverOpen, setIsHistoryHoverOpen] = createSignal(false);
14403
14423
  let historyHoverPreviews = [];
14424
+ const getMappedHistoryElements = (historyItemId) => historyElementMap.get(historyItemId) ?? [];
14425
+ const getConnectedHistoryElements = (historyItemId) => getMappedHistoryElements(historyItemId).filter((mappedElement) => isElementConnected(mappedElement));
14426
+ const getFirstConnectedHistoryElement = (historyItemId) => getConnectedHistoryElements(historyItemId)[0];
14427
+ const getHistoryPreviewBounds = (historyItem) => {
14428
+ const connectedElements = getConnectedHistoryElements(historyItem.id);
14429
+ if (connectedElements.length > 0) {
14430
+ return connectedElements.map((element) => createElementBounds(element));
14431
+ }
14432
+ return historyItem.previewBounds ?? [];
14433
+ };
14404
14434
  const historyDisconnectedItemIds = createMemo(() => {
14405
14435
  void historyDropdownPosition();
14406
14436
  const disconnectedIds = /* @__PURE__ */ new Set();
14407
14437
  for (const item of historyItems2()) {
14408
- const element = historyElementMap.get(item.id);
14409
- if (!element || !isElementConnected(element)) {
14438
+ if (getConnectedHistoryElements(item.id).length === 0) {
14410
14439
  disconnectedIds.add(item.id);
14411
14440
  }
14412
14441
  }
@@ -14694,12 +14723,13 @@ var init_core = __esm({
14694
14723
  onAfterCopy: pluginRegistry.hooks.onAfterCopy,
14695
14724
  onCopySuccess: (copiedElements, content) => {
14696
14725
  pluginRegistry.hooks.onCopySuccess(copiedElements, content);
14697
- const primaryElement = copiedElements[0];
14726
+ const hasCopiedElements = copiedElements.length > 0;
14698
14727
  const isComment = Boolean(extraPrompt);
14699
- if (primaryElement) {
14728
+ if (hasCopiedElements) {
14700
14729
  const currentItems = historyItems2();
14701
- for (const [existingItemId, mappedElement] of historyElementMap.entries()) {
14702
- if (mappedElement !== primaryElement) continue;
14730
+ for (const [existingItemId, mappedElements] of historyElementMap.entries()) {
14731
+ const isSameSelection = mappedElements.length === copiedElements.length && mappedElements.every((element, index) => element === copiedElements[index]);
14732
+ if (!isSameSelection) continue;
14703
14733
  const existingItem = currentItems.find((item) => item.id === existingItemId);
14704
14734
  if (!existingItem) continue;
14705
14735
  const shouldDedup = isComment ? existingItem.isComment && existingItem.commentText === extraPrompt : !existingItem.isComment;
@@ -14715,6 +14745,8 @@ var init_core = __esm({
14715
14745
  elementName: elementName ?? "element",
14716
14746
  tagName: tagName ?? "div",
14717
14747
  componentName: componentName ?? void 0,
14748
+ elementsCount: copiedElements.length,
14749
+ previewBounds: copiedElements.map((element) => createElementBounds(element)),
14718
14750
  isComment,
14719
14751
  commentText: extraPrompt ?? void 0,
14720
14752
  timestamp: Date.now()
@@ -14722,8 +14754,8 @@ var init_core = __esm({
14722
14754
  setHistoryItems(updatedHistoryItems);
14723
14755
  setHasUnreadHistoryItems(true);
14724
14756
  const newestHistoryItem = updatedHistoryItems[0];
14725
- if (newestHistoryItem && primaryElement) {
14726
- historyElementMap.set(newestHistoryItem.id, primaryElement);
14757
+ if (newestHistoryItem && hasCopiedElements) {
14758
+ historyElementMap.set(newestHistoryItem.id, [...copiedElements]);
14727
14759
  }
14728
14760
  const currentItemIds = new Set(updatedHistoryItems.map((item) => item.id));
14729
14761
  for (const mapItemId of historyElementMap.keys()) {
@@ -16474,33 +16506,45 @@ var init_core = __esm({
16474
16506
  }
16475
16507
  historyHoverPreviews = [];
16476
16508
  };
16477
- const addHistoryItemPreview = (item, element, idPrefix) => {
16478
- const bounds = createElementBounds(element);
16479
- const boxId = `${idPrefix}-${item.id}`;
16480
- actions.addGrabbedBox({
16481
- id: boxId,
16482
- bounds,
16483
- createdAt: 0,
16484
- element
16485
- });
16509
+ const addHistoryItemPreview = (item, previewBounds, previewElements, idPrefix) => {
16510
+ if (previewBounds.length === 0) return;
16486
16511
  const hasCommentText = item.isComment && item.commentText;
16487
- const labelId = `${idPrefix}-label-${item.id}`;
16488
- actions.addLabelInstance({
16489
- id: labelId,
16490
- bounds,
16491
- tagName: item.tagName,
16492
- componentName: item.componentName,
16493
- status: "idle",
16494
- isPromptMode: Boolean(hasCommentText),
16495
- inputValue: hasCommentText ? item.commentText : void 0,
16496
- createdAt: 0,
16497
- element,
16498
- mouseX: bounds.x + bounds.width / 2
16499
- });
16500
- historyHoverPreviews.push({
16501
- boxId,
16502
- labelId
16503
- });
16512
+ for (const [index, bounds] of previewBounds.entries()) {
16513
+ const previewElement = previewElements[index];
16514
+ const boxId = `${idPrefix}-${item.id}-${index}`;
16515
+ actions.addGrabbedBox({
16516
+ id: boxId,
16517
+ bounds,
16518
+ createdAt: 0,
16519
+ element: previewElement
16520
+ });
16521
+ let labelId = null;
16522
+ if (index === 0) {
16523
+ labelId = `${idPrefix}-label-${item.id}`;
16524
+ actions.addLabelInstance({
16525
+ id: labelId,
16526
+ bounds,
16527
+ tagName: item.tagName,
16528
+ componentName: item.componentName,
16529
+ elementsCount: item.elementsCount,
16530
+ status: "idle",
16531
+ isPromptMode: Boolean(hasCommentText),
16532
+ inputValue: hasCommentText ? item.commentText : void 0,
16533
+ createdAt: 0,
16534
+ element: previewElement,
16535
+ mouseX: bounds.x + bounds.width / 2
16536
+ });
16537
+ }
16538
+ historyHoverPreviews.push({
16539
+ boxId,
16540
+ labelId
16541
+ });
16542
+ }
16543
+ };
16544
+ const showHistoryItemPreview = (item, idPrefix) => {
16545
+ const previewBounds = getHistoryPreviewBounds(item);
16546
+ const connectedElements = getConnectedHistoryElements(item.id);
16547
+ addHistoryItemPreview(item, previewBounds, connectedElements, idPrefix);
16504
16548
  };
16505
16549
  const stopTrackingToolbarPosition = () => {
16506
16550
  if (historyPositionFrameId !== null) {
@@ -16596,8 +16640,8 @@ var init_core = __esm({
16596
16640
  componentName: item.componentName ?? item.elementName,
16597
16641
  commentText: item.commentText
16598
16642
  });
16599
- const element = historyElementMap.get(item.id);
16600
- if (!element || !isElementConnected(element)) return;
16643
+ const element = getFirstConnectedHistoryElement(item.id);
16644
+ if (!element) return;
16601
16645
  actions.clearLabelInstances();
16602
16646
  requestAnimationFrame(() => {
16603
16647
  if (!isElementConnected(element)) return;
@@ -16615,8 +16659,8 @@ var init_core = __esm({
16615
16659
  actions.exitPromptMode();
16616
16660
  actions.clearInputText();
16617
16661
  }
16618
- const element = historyElementMap.get(item.id);
16619
- if (item.isComment && item.commentText && element && isElementConnected(element)) {
16662
+ const element = getFirstConnectedHistoryElement(item.id);
16663
+ if (item.isComment && item.commentText && element) {
16620
16664
  const bounds = createElementBounds(element);
16621
16665
  const centerX = bounds.x + bounds.width / 2;
16622
16666
  const centerY = bounds.y + bounds.height / 2;
@@ -16658,34 +16702,32 @@ var init_core = __esm({
16658
16702
  requestAnimationFrame(() => {
16659
16703
  batch(() => {
16660
16704
  for (const historyItem of currentHistoryItems) {
16661
- const element = historyElementMap.get(historyItem.id);
16662
- if (!element || !isElementConnected(element)) continue;
16663
- const bounds = createElementBounds(element);
16664
- const labelId = `label-${Date.now()}-${Math.random().toString(36).slice(2)}`;
16665
- actions.addLabelInstance({
16666
- id: labelId,
16667
- bounds,
16668
- tagName: historyItem.tagName,
16669
- componentName: historyItem.componentName,
16670
- status: "copied",
16671
- createdAt: Date.now(),
16672
- element,
16673
- mouseX: bounds.x + bounds.width / 2
16674
- });
16675
- scheduleLabelFade(labelId);
16705
+ const connectedElements = getConnectedHistoryElements(historyItem.id);
16706
+ for (const element of connectedElements) {
16707
+ const bounds = createElementBounds(element);
16708
+ const labelId = `label-${Date.now()}-${Math.random().toString(36).slice(2)}`;
16709
+ actions.addLabelInstance({
16710
+ id: labelId,
16711
+ bounds,
16712
+ tagName: historyItem.tagName,
16713
+ componentName: historyItem.componentName,
16714
+ status: "copied",
16715
+ createdAt: Date.now(),
16716
+ element,
16717
+ mouseX: bounds.x + bounds.width / 2
16718
+ });
16719
+ scheduleLabelFade(labelId);
16720
+ }
16676
16721
  }
16677
16722
  });
16678
16723
  });
16679
16724
  };
16680
16725
  const handleHistoryItemHover = (historyItemId) => {
16681
16726
  clearHistoryHoverPreviews();
16682
- if (historyItemId) {
16683
- const item = historyItems2().find((innerItem) => innerItem.id === historyItemId);
16684
- const element = historyElementMap.get(historyItemId);
16685
- if (item && element && isElementConnected(element)) {
16686
- addHistoryItemPreview(item, element, "history-hover");
16687
- }
16688
- }
16727
+ if (!historyItemId) return;
16728
+ const item = historyItems2().find((innerItem) => innerItem.id === historyItemId);
16729
+ if (!item) return;
16730
+ showHistoryItemPreview(item, "history-hover");
16689
16731
  };
16690
16732
  const handleHistoryButtonHover = (isHovered) => {
16691
16733
  cancelHistoryHoverOpenTimeout();
@@ -16725,10 +16767,7 @@ var init_core = __esm({
16725
16767
  };
16726
16768
  const showAllHistoryItemPreviews = () => {
16727
16769
  for (const item of historyItems2()) {
16728
- const element = historyElementMap.get(item.id);
16729
- if (element && isElementConnected(element)) {
16730
- addHistoryItemPreview(item, element, "history-all-hover");
16731
- }
16770
+ showHistoryItemPreview(item, "history-all-hover");
16732
16771
  }
16733
16772
  };
16734
16773
  const handleHistoryClear = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-grab",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Select context for coding agents directly from your website",
5
5
  "keywords": [
6
6
  "agent",