vite-plugin-ai-annotator 1.3.4 → 1.14.1

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.
@@ -2,6 +2,7 @@
2
2
  * Shared constants for annotator components
3
3
  */
4
4
  export declare const Z_INDEX: {
5
+ readonly INSPECTION_OVERLAY: 999995;
5
6
  readonly HIGHLIGHT_OVERLAY: 999996;
6
7
  readonly HOVER_OVERLAY: 999997;
7
8
  readonly BADGE: 999998;
@@ -13,3 +14,17 @@ export declare const CONSOLE_LIMITS: {
13
14
  readonly BUFFER_TRIM_TO: 500;
14
15
  };
15
16
  export declare const SCREENSHOT_TIMEOUT_MS = 10000;
17
+ export declare const SELECTION_COLORS: readonly ["#FF00FF", "#00FFFF", "#FFFF00"];
18
+ export declare const COLORS: {
19
+ readonly INSPECTION: "#A855F7";
20
+ readonly BADGE_TEXT: "#050505";
21
+ readonly BADGE_BG: "#050505";
22
+ };
23
+ export declare const FONTS: {
24
+ readonly MONO: "'JetBrains Mono', monospace";
25
+ readonly GOOGLE_FONTS_URL: "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap";
26
+ };
27
+ export declare const TEXT_SELECTION: {
28
+ readonly MAX_LENGTH: 10000;
29
+ readonly HIGHLIGHT_OPACITY: 0.3;
30
+ };
@@ -11,6 +11,7 @@ export interface InspectionManager {
11
11
  export interface InspectionCallbacks {
12
12
  onElementSelect?: (element: Element) => void;
13
13
  onMultiSelect?: (elements: Element[]) => void;
14
+ onTextSelect?: (range: Range, commonAncestor: Element) => void;
14
15
  shouldIgnoreElement?: (element: Element) => boolean;
15
16
  isElementSelected?: (element: Element) => boolean;
16
17
  onEscape?: () => void;
@@ -3,14 +3,30 @@
3
3
  */
4
4
  import type { ElementData } from '../rpc/define';
5
5
  import type { ComponentInfo } from './detectors';
6
+ /**
7
+ * Metadata for a text selection annotation.
8
+ * Created when user selects text (not an element) for annotation.
9
+ */
10
+ export interface TextSelectionInfo {
11
+ /** The actual text content that was selected by the user */
12
+ selectedText: string;
13
+ /** The nearest common ancestor element containing the entire selection */
14
+ containerElement: Element;
15
+ }
6
16
  export interface SelectedElementInfo {
7
17
  color: string;
8
18
  index: number;
9
19
  displayText: string;
20
+ textSelection?: TextSelectionInfo;
10
21
  }
11
22
  type ComponentFinder = (el: Element) => ComponentInfo | null;
23
+ export interface WrapTextRangeResult {
24
+ wrapper: Element;
25
+ textSelection: TextSelectionInfo;
26
+ }
12
27
  export interface ElementSelectionManager {
13
- selectElement(element: Element, componentFinder?: ComponentFinder): void;
28
+ selectElement(element: Element, componentFinder?: ComponentFinder, textSelection?: TextSelectionInfo): void;
29
+ wrapTextRange(range: Range, containerElement: Element): WrapTextRangeResult | null;
14
30
  deselectElement(element: Element): void;
15
31
  clearAllSelections(): void;
16
32
  hasElement(element: Element): boolean;
@@ -40,6 +40,7 @@ export declare class AnnotatorToolbar extends LitElement {
40
40
  private reportPageContext;
41
41
  private getPageContext;
42
42
  private getSelectedElements;
43
+ private enrichElementsWithComments;
43
44
  private triggerSelection;
44
45
  private captureScreenshot;
45
46
  private clearSelection;
@@ -48,6 +49,7 @@ export declare class AnnotatorToolbar extends LitElement {
48
49
  private getConsoleLogs;
49
50
  private handleElementSelected;
50
51
  private handleMultiSelect;
52
+ private handleTextSelected;
51
53
  private removeSelectedElement;
52
54
  private showCommentPopoverForElement;
53
55
  private handlePopoverKeydown;
@@ -6388,6 +6388,7 @@
6388
6388
 
6389
6389
  // src/annotator/constants.ts
6390
6390
  var Z_INDEX = {
6391
+ INSPECTION_OVERLAY: 999995,
6391
6392
  HIGHLIGHT_OVERLAY: 999996,
6392
6393
  HOVER_OVERLAY: 999997,
6393
6394
  BADGE: 999998,
@@ -6402,6 +6403,25 @@
6402
6403
  // Keep last N entries after trim
6403
6404
  };
6404
6405
  var SCREENSHOT_TIMEOUT_MS = 1e4;
6406
+ var SELECTION_COLORS = ["#FF00FF", "#00FFFF", "#FFFF00"];
6407
+ var COLORS = {
6408
+ INSPECTION: "#A855F7",
6409
+ // Purple for inspection
6410
+ BADGE_TEXT: "#050505",
6411
+ // Black for badge text
6412
+ BADGE_BG: "#050505"
6413
+ // Black for badge background
6414
+ };
6415
+ var FONTS = {
6416
+ MONO: "'JetBrains Mono', monospace",
6417
+ GOOGLE_FONTS_URL: "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap"
6418
+ };
6419
+ var TEXT_SELECTION = {
6420
+ MAX_LENGTH: 1e4,
6421
+ // Maximum characters allowed in a text selection
6422
+ HIGHLIGHT_OPACITY: 0.3
6423
+ // Background opacity for text highlight
6424
+ };
6405
6425
 
6406
6426
  // src/annotator/selection.ts
6407
6427
  function createElementSelectionManager() {
@@ -6410,8 +6430,23 @@
6410
6430
  let colorIndex = 0;
6411
6431
  let onEditClickCallback = null;
6412
6432
  let keyframesStyleElement = null;
6413
- const colors = ["#FF00FF", "#00FFFF", "#FFFF00"];
6414
- function getDisplayText(index, element, componentFinder) {
6433
+ function unwrapTextSelection(element) {
6434
+ const parent = element.parentNode;
6435
+ if (!parent) {
6436
+ console.warn("[AI Annotator] Cannot unwrap text selection: element has no parent");
6437
+ return;
6438
+ }
6439
+ while (element.firstChild) {
6440
+ parent.insertBefore(element.firstChild, element);
6441
+ }
6442
+ element.remove();
6443
+ }
6444
+ function getDisplayText(index, element, componentFinder, textSelection) {
6445
+ if (textSelection) {
6446
+ const preview = textSelection.selectedText.substring(0, 15);
6447
+ const ellipsis = textSelection.selectedText.length > 15 ? "..." : "";
6448
+ return `#${index} "${preview}${ellipsis}"`;
6449
+ }
6415
6450
  const component = componentFinder?.(element);
6416
6451
  if (component && component.componentLocation) {
6417
6452
  const componentPath = component.componentLocation.split("@")[0];
@@ -6434,14 +6469,14 @@
6434
6469
  }
6435
6470
  }
6436
6471
  function createSelectionGroup(element, color, displayText) {
6437
- const textColor = "#050505";
6472
+ const textColor = COLORS.BADGE_TEXT;
6438
6473
  const glowColor = color.toLowerCase();
6439
6474
  const badge = document.createElement("div");
6440
6475
  badge.classList.add("annotator-badge");
6441
6476
  const shadow = badge.attachShadow({ mode: "open" });
6442
6477
  const style = document.createElement("style");
6443
6478
  style.textContent = `
6444
- @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
6479
+ @import url('${FONTS.GOOGLE_FONTS_URL}');
6445
6480
 
6446
6481
  @keyframes badge-glow {
6447
6482
  0%, 100% { box-shadow: 2px 2px 0px ${color}44, 0 0 8px ${glowColor}80; }
@@ -6452,7 +6487,7 @@
6452
6487
  display: flex;
6453
6488
  align-items: center;
6454
6489
  border: 1px solid ${color};
6455
- background: #050505;
6490
+ background: ${COLORS.BADGE_BG};
6456
6491
  box-shadow: 1px 1px 0px ${color}44, 0 0 6px ${glowColor}60;
6457
6492
  animation: badge-glow 2s ease-in-out infinite;
6458
6493
  cursor: pointer;
@@ -6471,7 +6506,7 @@
6471
6506
  justify-content: center;
6472
6507
  font-size: 9px;
6473
6508
  font-weight: 700;
6474
- font-family: 'JetBrains Mono', monospace;
6509
+ font-family: ${FONTS.MONO};
6475
6510
  white-space: nowrap;
6476
6511
  text-transform: uppercase;
6477
6512
  }
@@ -6580,14 +6615,42 @@
6580
6615
  return children;
6581
6616
  }
6582
6617
  return {
6583
- selectElement(element, componentFinder) {
6584
- const color = colors[colorIndex % colors.length];
6618
+ selectElement(element, componentFinder, textSelection) {
6619
+ const color = SELECTION_COLORS[colorIndex % SELECTION_COLORS.length];
6585
6620
  const index = selectedElements.size + 1;
6586
6621
  colorIndex++;
6587
- const displayText = getDisplayText(index, element, componentFinder);
6622
+ const displayText = getDisplayText(index, element, componentFinder, textSelection);
6588
6623
  const group = createSelectionGroup(element, color, displayText);
6589
6624
  selectionGroups.set(element, group);
6590
- selectedElements.set(element, { color, index, displayText });
6625
+ selectedElements.set(element, { color, index, displayText, textSelection });
6626
+ },
6627
+ wrapTextRange(range, containerElement) {
6628
+ const selectedText = range.toString().trim();
6629
+ if (!selectedText) return null;
6630
+ if (selectedText.length > TEXT_SELECTION.MAX_LENGTH) {
6631
+ console.warn(`[AI Annotator] Text selection exceeds maximum length of ${TEXT_SELECTION.MAX_LENGTH} characters`);
6632
+ return null;
6633
+ }
6634
+ const highlightColor = SELECTION_COLORS[colorIndex % SELECTION_COLORS.length];
6635
+ const wrapper = document.createElement("span");
6636
+ wrapper.className = "annotator-text-selection annotator-ignore";
6637
+ wrapper.style.cssText = `
6638
+ background: ${highlightColor}4D;
6639
+ border-radius: 2px;
6640
+ `;
6641
+ try {
6642
+ range.surroundContents(wrapper);
6643
+ } catch (error) {
6644
+ console.warn("[AI Annotator] Cannot wrap cross-element text selection. Please select text within a single paragraph.");
6645
+ return null;
6646
+ }
6647
+ return {
6648
+ wrapper,
6649
+ textSelection: {
6650
+ selectedText,
6651
+ containerElement
6652
+ }
6653
+ };
6591
6654
  },
6592
6655
  deselectElement(element) {
6593
6656
  const elementData = selectedElements.get(element);
@@ -6599,15 +6662,22 @@
6599
6662
  group.overlay.remove();
6600
6663
  selectionGroups.delete(element);
6601
6664
  }
6665
+ if (elementData.textSelection && element.classList.contains("annotator-text-selection")) {
6666
+ unwrapTextSelection(element);
6667
+ }
6602
6668
  selectedElements.delete(element);
6603
6669
  reindexElements();
6604
6670
  }
6605
6671
  },
6606
6672
  clearAllSelections() {
6607
- selectionGroups.forEach((group) => {
6673
+ selectionGroups.forEach((group, element) => {
6608
6674
  group.cleanup();
6609
6675
  group.badge.remove();
6610
6676
  group.overlay.remove();
6677
+ const elementData = selectedElements.get(element);
6678
+ if (elementData?.textSelection && element.classList.contains("annotator-text-selection")) {
6679
+ unwrapTextSelection(element);
6680
+ }
6611
6681
  });
6612
6682
  selectionGroups.clear();
6613
6683
  selectedElements.clear();
@@ -6646,13 +6716,14 @@
6646
6716
  const data = selectedElements.get(element);
6647
6717
  const children = findSelectedChildren(element);
6648
6718
  const componentData = componentFinder?.(element);
6719
+ const targetElement = data.textSelection?.containerElement || element;
6649
6720
  const elementInfo = {
6650
6721
  index: data.index,
6651
- tagName: element.tagName,
6652
- xpath: XPathUtils.generateXPath(element),
6653
- cssSelector: XPathUtils.generateEnhancedCSSSelector(element),
6722
+ tagName: targetElement.tagName,
6723
+ xpath: XPathUtils.generateXPath(targetElement),
6724
+ cssSelector: XPathUtils.generateEnhancedCSSSelector(targetElement),
6654
6725
  textContent: element.textContent?.substring(0, 100) || "",
6655
- attributes: Array.from(element.attributes).reduce((acc, attr) => {
6726
+ attributes: Array.from(targetElement.attributes).reduce((acc, attr) => {
6656
6727
  if (attr.name !== "style") {
6657
6728
  acc[attr.name] = attr.value;
6658
6729
  }
@@ -6660,11 +6731,24 @@
6660
6731
  }, {}),
6661
6732
  children: []
6662
6733
  };
6734
+ if (data.textSelection && data.textSelection.containerElement.isConnected) {
6735
+ elementInfo.textSelection = {
6736
+ selectedText: data.textSelection.selectedText,
6737
+ containerXPath: XPathUtils.generateXPath(data.textSelection.containerElement),
6738
+ containerCssSelector: XPathUtils.generateEnhancedCSSSelector(data.textSelection.containerElement)
6739
+ };
6740
+ } else if (data.textSelection) {
6741
+ elementInfo.textSelection = {
6742
+ selectedText: data.textSelection.selectedText,
6743
+ containerXPath: "",
6744
+ containerCssSelector: ""
6745
+ };
6746
+ }
6663
6747
  if (imagePaths && imagePaths.has(element)) {
6664
6748
  elementInfo.imagePath = imagePaths.get(element);
6665
6749
  }
6666
6750
  try {
6667
- const htmlElement = element;
6751
+ const htmlElement = targetElement;
6668
6752
  const computedStyle = window.getComputedStyle(htmlElement);
6669
6753
  elementInfo.computedStyles = {
6670
6754
  width: htmlElement.offsetWidth,
@@ -6699,38 +6783,43 @@
6699
6783
  var DRAG_THRESHOLD = 5;
6700
6784
  var INITIAL_DRAG_STATE = { isDragging: false, startX: 0, startY: 0, currentX: 0, currentY: 0 };
6701
6785
  function createInspectionManager(callbacks = {}) {
6702
- const { onElementSelect, onMultiSelect, shouldIgnoreElement, isElementSelected, onEscape, onCopy } = callbacks;
6786
+ const { onElementSelect, onMultiSelect, onTextSelect, shouldIgnoreElement, isElementSelected, onEscape, onCopy } = callbacks;
6703
6787
  let isInspecting = false;
6704
6788
  let currentHoveredElement = null;
6705
6789
  let hoverOverlay = null;
6706
6790
  let hoverKeyframesStyleElement = null;
6707
- let inspectionStyleElement = null;
6708
6791
  let selectionOverlay = null;
6792
+ let glassPane = null;
6709
6793
  let dragState = { ...INITIAL_DRAG_STATE };
6710
6794
  let mouseDownTime = 0;
6711
- function addInspectionStyles() {
6712
- inspectionStyleElement = document.createElement("style");
6713
- inspectionStyleElement.id = "annotator-toolbar-styles";
6714
- inspectionStyleElement.textContent = `
6715
- * {
6716
- pointer-events: none !important;
6717
- cursor: crosshair !important;
6718
- user-select: none !important;
6719
- -webkit-user-select: none !important;
6720
- }
6721
- annotator-toolbar, annotator-toolbar *,
6722
- .annotator-badge, .annotator-badge *,
6723
- .annotator-ignore {
6724
- pointer-events: auto !important;
6725
- cursor: default !important;
6726
- }
6795
+ function createGlassPane() {
6796
+ const pane = document.createElement("div");
6797
+ pane.id = "annotator-glass-pane";
6798
+ pane.className = "annotator-ignore";
6799
+ pane.style.cssText = `
6800
+ position: fixed;
6801
+ top: 0;
6802
+ left: 0;
6803
+ width: 100vw;
6804
+ height: 100vh;
6805
+ background: transparent;
6806
+ cursor: crosshair;
6807
+ z-index: ${Z_INDEX.INSPECTION_OVERLAY};
6808
+ pointer-events: auto;
6727
6809
  `;
6728
- document.head.appendChild(inspectionStyleElement);
6810
+ pane.addEventListener("mousedown", handleMouseDown, true);
6811
+ pane.addEventListener("mousemove", handleMouseMove, true);
6812
+ pane.addEventListener("mouseup", handleMouseUp, true);
6813
+ pane.addEventListener("click", preventClick, true);
6814
+ pane.addEventListener("dblclick", preventMouseEvents, true);
6815
+ pane.addEventListener("contextmenu", preventMouseEvents, true);
6816
+ document.body.appendChild(pane);
6817
+ return pane;
6729
6818
  }
6730
- function removeInspectionStyles() {
6731
- if (inspectionStyleElement) {
6732
- inspectionStyleElement.remove();
6733
- inspectionStyleElement = null;
6819
+ function removeGlassPane() {
6820
+ if (glassPane) {
6821
+ glassPane.remove();
6822
+ glassPane = null;
6734
6823
  }
6735
6824
  }
6736
6825
  function removeHoverHighlight() {
@@ -6752,7 +6841,7 @@
6752
6841
  `;
6753
6842
  document.head.appendChild(hoverKeyframesStyleElement);
6754
6843
  }
6755
- const color = "#A855F7";
6844
+ const color = COLORS.INSPECTION;
6756
6845
  const overlay = document.createElement("div");
6757
6846
  overlay.className = "annotator-hover-overlay";
6758
6847
  const rect = element.getBoundingClientRect();
@@ -6785,15 +6874,13 @@
6785
6874
  hoverOverlay.style.height = `${rect.height}px`;
6786
6875
  }
6787
6876
  function getElementAtPoint(x2, y3) {
6788
- if (inspectionStyleElement) {
6789
- inspectionStyleElement.disabled = true;
6790
- }
6877
+ if (!glassPane) return null;
6878
+ const originalPointerEvents = glassPane.style.pointerEvents;
6879
+ glassPane.style.pointerEvents = "none";
6791
6880
  try {
6792
6881
  return document.elementFromPoint(x2, y3);
6793
6882
  } finally {
6794
- if (inspectionStyleElement) {
6795
- inspectionStyleElement.disabled = false;
6796
- }
6883
+ glassPane.style.pointerEvents = originalPointerEvents;
6797
6884
  }
6798
6885
  }
6799
6886
  function createSelectionOverlay() {
@@ -6801,7 +6888,7 @@
6801
6888
  overlay.className = "annotator-ignore";
6802
6889
  overlay.style.cssText = `
6803
6890
  position: fixed;
6804
- border: 2px dashed #A855F7;
6891
+ border: 2px dashed ${COLORS.INSPECTION};
6805
6892
  background: rgba(168, 85, 247, 0.1);
6806
6893
  pointer-events: none;
6807
6894
  z-index: ${Z_INDEX.HOVER_OVERLAY};
@@ -6839,26 +6926,28 @@
6839
6926
  return inner.left >= outer.left && inner.right <= outer.right && inner.top >= outer.top && inner.bottom <= outer.bottom;
6840
6927
  }
6841
6928
  function findElementsFullyInRect(rect) {
6842
- if (inspectionStyleElement) {
6843
- inspectionStyleElement.disabled = true;
6844
- }
6929
+ if (glassPane) glassPane.style.display = "none";
6845
6930
  try {
6846
- const elements = [];
6847
- const allElements = document.body.querySelectorAll("*");
6848
- for (const element of allElements) {
6849
- if (shouldIgnoreElement?.(element)) continue;
6850
- if (element.tagName === "SCRIPT" || element.tagName === "STYLE" || element.tagName === "NOSCRIPT") continue;
6851
- const elementRect = element.getBoundingClientRect();
6852
- if (elementRect.width === 0 || elementRect.height === 0) continue;
6853
- if (isFullyContained(elementRect, rect)) {
6854
- elements.push(element);
6931
+ let traverse2 = function(node) {
6932
+ if (shouldIgnoreElement?.(node)) return;
6933
+ if (node.tagName === "SCRIPT" || node.tagName === "STYLE" || node.tagName === "NOSCRIPT") return;
6934
+ const elementRect = node.getBoundingClientRect();
6935
+ if (elementRect.width > 0 && elementRect.height > 0) {
6936
+ if (isFullyContained(elementRect, rect)) {
6937
+ elements.push(node);
6938
+ }
6855
6939
  }
6856
- }
6940
+ const children = node.children;
6941
+ for (let i5 = 0; i5 < children.length; i5++) {
6942
+ traverse2(children[i5]);
6943
+ }
6944
+ };
6945
+ var traverse = traverse2;
6946
+ const elements = [];
6947
+ traverse2(document.body);
6857
6948
  return elements;
6858
6949
  } finally {
6859
- if (inspectionStyleElement) {
6860
- inspectionStyleElement.disabled = false;
6861
- }
6950
+ if (glassPane) glassPane.style.display = "block";
6862
6951
  }
6863
6952
  }
6864
6953
  function filterLeafElements(elements) {
@@ -6868,7 +6957,7 @@
6868
6957
  }
6869
6958
  function handleMouseDown(e5) {
6870
6959
  const clickedElement = e5.target;
6871
- if (shouldIgnoreElement?.(clickedElement)) return;
6960
+ if (shouldIgnoreElement?.(clickedElement) && clickedElement !== glassPane) return;
6872
6961
  mouseDownTime = Date.now();
6873
6962
  dragState = {
6874
6963
  isDragging: false,
@@ -6911,38 +7000,55 @@
6911
7000
  hoverOverlay = createHoverOverlay(target);
6912
7001
  currentHoveredElement = target;
6913
7002
  }
7003
+ function detectTextSelection() {
7004
+ const selection = window.getSelection();
7005
+ if (!selection || selection.isCollapsed || selection.rangeCount === 0) return null;
7006
+ const selectedText = selection.toString().trim();
7007
+ if (selectedText.length === 0) return null;
7008
+ const range = selection.getRangeAt(0);
7009
+ const ancestor = range.commonAncestorContainer;
7010
+ const commonAncestor = ancestor.nodeType === Node.ELEMENT_NODE ? ancestor : ancestor.parentElement;
7011
+ if (!commonAncestor || shouldIgnoreElement?.(commonAncestor)) return null;
7012
+ return { range, commonAncestor };
7013
+ }
6914
7014
  function handleMouseUp(e5) {
6915
7015
  const wasDragging = dragState.isDragging;
6916
- if (wasDragging) {
6917
- removeSelectionOverlay();
6918
- const selectionRect = getSelectionRect();
6919
- if (selectionRect.width > 10 && selectionRect.height > 10) {
6920
- const elementsInRect = findElementsFullyInRect(selectionRect);
6921
- const leafElements = filterLeafElements(elementsInRect);
6922
- if (leafElements.length > 0) {
6923
- onMultiSelect?.(leafElements);
7016
+ try {
7017
+ if (wasDragging) {
7018
+ removeSelectionOverlay();
7019
+ const selectionRect = getSelectionRect();
7020
+ if (selectionRect.width > 10 && selectionRect.height > 10) {
7021
+ const elementsInRect = findElementsFullyInRect(selectionRect);
7022
+ const leafElements = filterLeafElements(elementsInRect);
7023
+ if (leafElements.length > 0) {
7024
+ onMultiSelect?.(leafElements);
7025
+ }
7026
+ }
7027
+ } else if (mouseDownTime > 0) {
7028
+ const textSelection = detectTextSelection();
7029
+ if (textSelection) {
7030
+ const range = textSelection.range.cloneRange();
7031
+ const commonAncestor = textSelection.commonAncestor;
7032
+ window.getSelection()?.removeAllRanges();
7033
+ onTextSelect?.(range, commonAncestor);
7034
+ return;
7035
+ }
7036
+ const target = getElementAtPoint(e5.clientX, e5.clientY);
7037
+ if (target && !shouldIgnoreElement?.(target)) {
7038
+ onElementSelect?.(target);
6924
7039
  }
6925
7040
  }
6926
- } else if (mouseDownTime > 0) {
6927
- const target = getElementAtPoint(e5.clientX, e5.clientY);
6928
- if (target && !shouldIgnoreElement?.(target)) {
6929
- onElementSelect?.(target);
6930
- }
7041
+ } finally {
7042
+ mouseDownTime = 0;
7043
+ dragState = { ...INITIAL_DRAG_STATE };
6931
7044
  }
6932
- mouseDownTime = 0;
6933
- dragState = { ...INITIAL_DRAG_STATE };
6934
7045
  }
6935
7046
  function preventClick(e5) {
6936
- const target = e5.target;
6937
- if (shouldIgnoreElement?.(target)) return;
6938
7047
  e5.preventDefault();
6939
7048
  e5.stopPropagation();
6940
7049
  e5.stopImmediatePropagation();
6941
7050
  }
6942
7051
  function preventMouseEvents(e5) {
6943
- const target = e5.target;
6944
- if (shouldIgnoreElement?.(target)) return;
6945
- if (dragState.isDragging) return;
6946
7052
  e5.preventDefault();
6947
7053
  e5.stopPropagation();
6948
7054
  e5.stopImmediatePropagation();
@@ -6967,16 +7073,11 @@
6967
7073
  removeSelectionOverlay();
6968
7074
  }
6969
7075
  function cleanup() {
6970
- removeInspectionStyles();
6971
- document.removeEventListener("mousedown", handleMouseDown, true);
6972
- document.removeEventListener("mousemove", handleMouseMove, true);
6973
- document.removeEventListener("mouseup", handleMouseUp, true);
6974
- document.removeEventListener("click", preventClick, true);
6975
- document.removeEventListener("dblclick", preventMouseEvents, true);
6976
- document.removeEventListener("contextmenu", preventMouseEvents, true);
7076
+ removeGlassPane();
6977
7077
  document.removeEventListener("keydown", handleKeyDown);
6978
7078
  removeHoverHighlight();
6979
7079
  resetDragState();
7080
+ window.getSelection()?.removeAllRanges();
6980
7081
  }
6981
7082
  function cleanupKeyframesStyle() {
6982
7083
  if (hoverKeyframesStyleElement) {
@@ -6988,13 +7089,7 @@
6988
7089
  enterInspectionMode() {
6989
7090
  if (isInspecting) return;
6990
7091
  isInspecting = true;
6991
- addInspectionStyles();
6992
- document.addEventListener("mousedown", handleMouseDown, true);
6993
- document.addEventListener("mousemove", handleMouseMove, true);
6994
- document.addEventListener("mouseup", handleMouseUp, true);
6995
- document.addEventListener("click", preventClick, true);
6996
- document.addEventListener("dblclick", preventMouseEvents, true);
6997
- document.addEventListener("contextmenu", preventMouseEvents, true);
7092
+ glassPane = createGlassPane();
6998
7093
  document.addEventListener("keydown", handleKeyDown);
6999
7094
  },
7000
7095
  exitInspectionMode() {
@@ -7589,6 +7684,7 @@
7589
7684
  this.inspectionManager = createInspectionManager({
7590
7685
  onElementSelect: (element) => this.handleElementSelected(element),
7591
7686
  onMultiSelect: (elements) => this.handleMultiSelect(elements),
7687
+ onTextSelect: (range, commonAncestor) => this.handleTextSelected(range, commonAncestor),
7592
7688
  shouldIgnoreElement: (element) => this.shouldIgnoreElement(element),
7593
7689
  isElementSelected: (element) => this.selectionManager?.hasElement(element) || false,
7594
7690
  onEscape: () => this.exitInspectingMode(),
@@ -7695,24 +7791,24 @@
7695
7791
  const elements = this.selectionManager.buildHierarchicalStructure(
7696
7792
  (el) => findNearestComponent(el, this.verbose)
7697
7793
  );
7698
- const addComments = (items, selectedElements) => {
7699
- for (const item of items) {
7700
- for (const [element, info] of selectedElements) {
7701
- if (info.index === item.index) {
7702
- const comment = this.elementComments.get(element);
7703
- if (comment) {
7704
- item.comment = comment;
7705
- }
7706
- break;
7794
+ this.enrichElementsWithComments(elements, this.selectionManager.getSelectedElements());
7795
+ return elements;
7796
+ }
7797
+ enrichElementsWithComments(items, selectedElements) {
7798
+ for (const item of items) {
7799
+ for (const [element, info] of selectedElements) {
7800
+ if (info.index === item.index) {
7801
+ const comment = this.elementComments.get(element);
7802
+ if (comment) {
7803
+ item.comment = comment;
7707
7804
  }
7708
- }
7709
- if (item.children.length > 0) {
7710
- addComments(item.children, selectedElements);
7805
+ break;
7711
7806
  }
7712
7807
  }
7713
- };
7714
- addComments(elements, this.selectionManager.getSelectedElements());
7715
- return elements;
7808
+ if (item.children.length > 0) {
7809
+ this.enrichElementsWithComments(item.children, selectedElements);
7810
+ }
7811
+ }
7716
7812
  }
7717
7813
  triggerSelection(mode, selector, selectorType) {
7718
7814
  try {
@@ -7878,6 +7974,29 @@
7878
7974
  }
7879
7975
  this.emitSelectionChanged();
7880
7976
  }
7977
+ handleTextSelected(range, commonAncestor) {
7978
+ if (!this.selectionManager) return;
7979
+ const result = this.selectionManager.wrapTextRange(range, commonAncestor);
7980
+ if (!result) {
7981
+ this.showToast("Select text within one element");
7982
+ return;
7983
+ }
7984
+ try {
7985
+ this.selectionManager.selectElement(
7986
+ result.wrapper,
7987
+ (el) => findNearestComponent(el, this.verbose),
7988
+ result.textSelection
7989
+ );
7990
+ this.showCommentPopoverForElement(result.wrapper);
7991
+ this.selectionCount = this.selectionManager.getSelectedCount();
7992
+ this.emitSelectionChanged();
7993
+ } catch (error) {
7994
+ result.wrapper.remove();
7995
+ const message = error instanceof Error ? error.message : "Unknown error";
7996
+ this.log("Text selection error:", message);
7997
+ this.showToast("Selection error");
7998
+ }
7999
+ }
7881
8000
  removeSelectedElement() {
7882
8001
  if (!this.commentPopover.element || !this.selectionManager) return;
7883
8002
  const element = this.commentPopover.element;
@@ -10,6 +10,26 @@ export interface PageContext {
10
10
  selectionCount: number;
11
11
  isInspecting: boolean;
12
12
  }
13
+ export interface ElementTextSelection {
14
+ selectedText: string;
15
+ containerXPath: string;
16
+ containerCssSelector: string;
17
+ }
18
+ export interface ElementComputedStyles {
19
+ width: number;
20
+ height: number;
21
+ fontSize: string;
22
+ fontFamily: string;
23
+ color?: string;
24
+ backgroundColor?: string;
25
+ display?: string;
26
+ position?: string;
27
+ }
28
+ export interface ElementComponentData {
29
+ componentLocation: string;
30
+ componentName?: string;
31
+ framework?: 'vue' | 'react' | 'angular' | 'svelte' | 'vanilla';
32
+ }
13
33
  export interface ElementData {
14
34
  index: number;
15
35
  tagName: string;
@@ -19,21 +39,9 @@ export interface ElementData {
19
39
  attributes: Record<string, string>;
20
40
  imagePath?: string;
21
41
  comment?: string;
22
- computedStyles?: {
23
- width: number;
24
- height: number;
25
- fontSize: string;
26
- fontFamily: string;
27
- color?: string;
28
- backgroundColor?: string;
29
- display?: string;
30
- position?: string;
31
- };
32
- componentData?: {
33
- componentLocation: string;
34
- componentName?: string;
35
- framework?: 'vue' | 'react' | 'angular' | 'svelte' | 'vanilla';
36
- };
42
+ textSelection?: ElementTextSelection;
43
+ computedStyles?: ElementComputedStyles;
44
+ componentData?: ElementComponentData;
37
45
  children: ElementData[];
38
46
  }
39
47
  export interface ScreenshotResult {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-ai-annotator",
3
- "version": "1.3.4",
3
+ "version": "1.14.1",
4
4
  "description": "AI-powered element annotator for Vite - Pick elements and get instant AI code modifications",
5
5
  "type": "module",
6
6
  "main": "dist/vite-plugin.js",