vite-plugin-ai-annotator 1.0.2 → 1.1.0

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.
@@ -6508,6 +6508,7 @@
6508
6508
  document.body.appendChild(badge);
6509
6509
  const cleanup = autoUpdate(element, badge, () => {
6510
6510
  computePosition2(element, badge, {
6511
+ strategy: "fixed",
6511
6512
  placement: "top-start",
6512
6513
  middleware: [
6513
6514
  offset2({ mainAxis: -5, crossAxis: 7 }),
@@ -6569,14 +6570,17 @@
6569
6570
  const color = colors[colorIndex % colors.length];
6570
6571
  const index = selectedElements.size + 1;
6571
6572
  colorIndex++;
6572
- element.style.outline = `3px solid ${color}`;
6573
- element.style.outlineOffset = "-1px";
6573
+ const el = element;
6574
+ const originalOutline = el.style.outline;
6575
+ const originalOutlineOffset = el.style.outlineOffset;
6576
+ el.style.outline = `3px solid ${color}`;
6577
+ el.style.outlineOffset = "-1px";
6574
6578
  const badge = createBadge(index, color, element, componentFinder);
6575
6579
  badges.set(element, badge);
6576
6580
  selectedElements.set(element, {
6577
6581
  color,
6578
- originalOutline: element.style.outline,
6579
- originalOutlineOffset: element.style.outlineOffset,
6582
+ originalOutline,
6583
+ originalOutlineOffset,
6580
6584
  index
6581
6585
  });
6582
6586
  },
@@ -6584,11 +6588,10 @@
6584
6588
  const elementData = selectedElements.get(element);
6585
6589
  if (elementData) {
6586
6590
  ;
6587
- element.style.outline = "";
6588
- element.style.outlineOffset = "";
6591
+ element.style.outline = elementData.originalOutline;
6592
+ element.style.outlineOffset = elementData.originalOutlineOffset;
6589
6593
  const badge = badges.get(element);
6590
6594
  if (badge) {
6591
- ;
6592
6595
  badge._cleanup?.();
6593
6596
  badge.remove();
6594
6597
  badges.delete(element);
@@ -6598,13 +6601,12 @@
6598
6601
  }
6599
6602
  },
6600
6603
  clearAllSelections() {
6601
- selectedElements.forEach((_2, element) => {
6604
+ selectedElements.forEach((data, element) => {
6602
6605
  ;
6603
- element.style.outline = "";
6604
- element.style.outlineOffset = "";
6606
+ element.style.outline = data.originalOutline;
6607
+ element.style.outlineOffset = data.originalOutlineOffset;
6605
6608
  });
6606
6609
  badges.forEach((badge) => {
6607
- ;
6608
6610
  badge._cleanup?.();
6609
6611
  badge.remove();
6610
6612
  });
@@ -6700,7 +6702,8 @@
6700
6702
  }
6701
6703
 
6702
6704
  // src/annotator/inspection.ts
6703
- function createInspectionManager(onElementSelect, shouldIgnoreElement, isElementSelected) {
6705
+ function createInspectionManager(callbacks = {}) {
6706
+ const { onElementSelect, shouldIgnoreElement, isElementSelected, onEscape, onCopy } = callbacks;
6704
6707
  let isInspecting = false;
6705
6708
  let currentHoveredElement = null;
6706
6709
  let inspectionStyleElement = null;
@@ -6709,8 +6712,15 @@
6709
6712
  inspectionStyleElement.id = "annotator-toolbar-styles";
6710
6713
  inspectionStyleElement.textContent = `
6711
6714
  * {
6715
+ pointer-events: none !important;
6712
6716
  cursor: crosshair !important;
6713
6717
  }
6718
+ annotator-toolbar, annotator-toolbar *,
6719
+ .annotator-badge, .annotator-badge *,
6720
+ .annotator-ignore {
6721
+ pointer-events: auto !important;
6722
+ cursor: default !important;
6723
+ }
6714
6724
  `;
6715
6725
  document.head.appendChild(inspectionStyleElement);
6716
6726
  }
@@ -6730,29 +6740,36 @@
6730
6740
  currentHoveredElement = null;
6731
6741
  }
6732
6742
  }
6733
- function handleMouseOver(e5) {
6734
- const target = e5.target;
6735
- if (shouldIgnoreElement?.(target)) return;
6743
+ function getElementAtPoint(x2, y3) {
6744
+ if (inspectionStyleElement) {
6745
+ inspectionStyleElement.disabled = true;
6746
+ }
6747
+ const element = document.elementFromPoint(x2, y3);
6748
+ if (inspectionStyleElement) {
6749
+ inspectionStyleElement.disabled = false;
6750
+ }
6751
+ return element;
6752
+ }
6753
+ function handleMouseMove(e5) {
6754
+ const target = getElementAtPoint(e5.clientX, e5.clientY);
6755
+ if (!target || shouldIgnoreElement?.(target)) {
6756
+ removeHoverHighlight();
6757
+ return;
6758
+ }
6759
+ if (target === currentHoveredElement) return;
6736
6760
  removeHoverHighlight();
6737
6761
  target.style.outline = "3px solid #3B82F6";
6738
6762
  target.style.outlineOffset = "-1px";
6739
6763
  currentHoveredElement = target;
6740
6764
  }
6741
- function handleMouseOut(e5) {
6742
- const target = e5.target;
6743
- if (shouldIgnoreElement?.(target)) return;
6744
- if (!isElementSelected?.(target)) {
6745
- ;
6746
- target.style.outline = "";
6747
- target.style.outlineOffset = "";
6748
- }
6749
- }
6750
- function handleElementClick(e5) {
6751
- const target = e5.target;
6752
- if (shouldIgnoreElement?.(target)) return;
6765
+ function handleClick(e5) {
6766
+ const clickedElement = e5.target;
6767
+ if (shouldIgnoreElement?.(clickedElement)) return;
6753
6768
  e5.preventDefault();
6754
6769
  e5.stopPropagation();
6755
6770
  e5.stopImmediatePropagation();
6771
+ const target = getElementAtPoint(e5.clientX, e5.clientY);
6772
+ if (!target || shouldIgnoreElement?.(target)) return;
6756
6773
  onElementSelect?.(target);
6757
6774
  }
6758
6775
  function preventMouseEvents(e5) {
@@ -6762,30 +6779,44 @@
6762
6779
  e5.stopPropagation();
6763
6780
  e5.stopImmediatePropagation();
6764
6781
  }
6782
+ function handleKeyDown(e5) {
6783
+ if (e5.key === "Escape") {
6784
+ e5.preventDefault();
6785
+ onEscape?.();
6786
+ return;
6787
+ }
6788
+ if ((e5.metaKey || e5.ctrlKey) && e5.key === "c") {
6789
+ const selection = window.getSelection();
6790
+ if (!selection || selection.isCollapsed) {
6791
+ e5.preventDefault();
6792
+ onCopy?.();
6793
+ }
6794
+ }
6795
+ }
6765
6796
  return {
6766
6797
  enterInspectionMode() {
6767
6798
  if (isInspecting) return;
6768
6799
  isInspecting = true;
6769
6800
  addInspectionStyles();
6770
- document.addEventListener("mouseover", handleMouseOver, true);
6771
- document.addEventListener("mouseout", handleMouseOut, true);
6772
- document.addEventListener("click", handleElementClick, true);
6801
+ document.addEventListener("mousemove", handleMouseMove, true);
6802
+ document.addEventListener("click", handleClick, true);
6773
6803
  document.addEventListener("mousedown", preventMouseEvents, true);
6774
6804
  document.addEventListener("mouseup", preventMouseEvents, true);
6775
6805
  document.addEventListener("dblclick", preventMouseEvents, true);
6776
6806
  document.addEventListener("contextmenu", preventMouseEvents, true);
6807
+ document.addEventListener("keydown", handleKeyDown);
6777
6808
  },
6778
6809
  exitInspectionMode() {
6779
6810
  if (!isInspecting) return;
6780
6811
  isInspecting = false;
6781
6812
  removeInspectionStyles();
6782
- document.removeEventListener("mouseover", handleMouseOver, true);
6783
- document.removeEventListener("mouseout", handleMouseOut, true);
6784
- document.removeEventListener("click", handleElementClick, true);
6813
+ document.removeEventListener("mousemove", handleMouseMove, true);
6814
+ document.removeEventListener("click", handleClick, true);
6785
6815
  document.removeEventListener("mousedown", preventMouseEvents, true);
6786
6816
  document.removeEventListener("mouseup", preventMouseEvents, true);
6787
6817
  document.removeEventListener("dblclick", preventMouseEvents, true);
6788
6818
  document.removeEventListener("contextmenu", preventMouseEvents, true);
6819
+ document.removeEventListener("keydown", handleKeyDown);
6789
6820
  removeHoverHighlight();
6790
6821
  },
6791
6822
  isInInspectionMode() {
@@ -6795,13 +6826,13 @@
6795
6826
  if (isInspecting) {
6796
6827
  isInspecting = false;
6797
6828
  removeInspectionStyles();
6798
- document.removeEventListener("mouseover", handleMouseOver, true);
6799
- document.removeEventListener("mouseout", handleMouseOut, true);
6800
- document.removeEventListener("click", handleElementClick, true);
6829
+ document.removeEventListener("mousemove", handleMouseMove, true);
6830
+ document.removeEventListener("click", handleClick, true);
6801
6831
  document.removeEventListener("mousedown", preventMouseEvents, true);
6802
6832
  document.removeEventListener("mouseup", preventMouseEvents, true);
6803
6833
  document.removeEventListener("dblclick", preventMouseEvents, true);
6804
6834
  document.removeEventListener("contextmenu", preventMouseEvents, true);
6835
+ document.removeEventListener("keydown", handleKeyDown);
6805
6836
  removeHoverHighlight();
6806
6837
  }
6807
6838
  }
@@ -6828,14 +6859,15 @@
6828
6859
  const logger = createLogger(verbose);
6829
6860
  try {
6830
6861
  let componentInfo = getVueComponentInfo(element);
6862
+ const el = element;
6831
6863
  if (componentInfo) {
6832
6864
  logger.log("\u{1F7E2} Vue component found:", componentInfo);
6833
6865
  } else {
6834
6866
  logger.log("\u{1F50D} No Vue component found for element:", element.tagName, "Checking properties:", {
6835
- __vnode: !!element.__vnode,
6836
- __vueParentComponent: !!element.__vueParentComponent,
6837
- __vue__: !!element.__vue__,
6838
- __v_inspector: !!element.__v_inspector
6867
+ __vnode: !!el.__vnode,
6868
+ __vueParentComponent: !!el.__vueParentComponent,
6869
+ __vue__: !!el.__vue__,
6870
+ __v_inspector: !!el.__v_inspector
6839
6871
  });
6840
6872
  }
6841
6873
  if (!componentInfo) {
@@ -6877,14 +6909,14 @@
6877
6909
  }
6878
6910
  function getReactComponentInfo(element) {
6879
6911
  if (!element) return null;
6880
- const elementAny = element;
6881
- const fiberKey = Object.keys(elementAny).find(
6912
+ const el = element;
6913
+ const fiberKey = Object.keys(el).find(
6882
6914
  (key) => key.startsWith("__reactFiber") || key.startsWith("__reactInternalInstance") || key.startsWith("_reactInternalFiber")
6883
6915
  );
6884
6916
  if (!fiberKey) {
6885
6917
  return null;
6886
6918
  }
6887
- const fiber = elementAny[fiberKey];
6919
+ const fiber = el[fiberKey];
6888
6920
  if (!fiber) {
6889
6921
  return null;
6890
6922
  }
@@ -6902,18 +6934,18 @@
6902
6934
  let componentName = "";
6903
6935
  let componentFile = "";
6904
6936
  while (currentFiber) {
6905
- if (currentFiber.type && typeof currentFiber.type === "function") {
6906
- componentName = currentFiber.type.name || currentFiber.type.displayName || "Anonymous";
6907
- if (currentFiber.type.__source) {
6908
- componentFile = currentFiber.type.__source.fileName || "";
6937
+ const fiberType = currentFiber.type;
6938
+ if (fiberType && typeof fiberType === "object") {
6939
+ componentName = fiberType.name || fiberType.displayName || "Anonymous";
6940
+ if (fiberType.__source) {
6941
+ componentFile = fiberType.__source.fileName || "";
6942
+ }
6943
+ if (fiberType.prototype?.render) {
6944
+ componentName = fiberType.name || "Component";
6909
6945
  }
6910
6946
  break;
6911
6947
  }
6912
- if (currentFiber.type && currentFiber.type.prototype && currentFiber.type.prototype.render) {
6913
- componentName = currentFiber.type.name || "Component";
6914
- break;
6915
- }
6916
- currentFiber = currentFiber.return || currentFiber._debugOwner;
6948
+ currentFiber = currentFiber.return ?? currentFiber._debugOwner;
6917
6949
  }
6918
6950
  if (!componentName && !componentFile) {
6919
6951
  return null;
@@ -6958,13 +6990,14 @@
6958
6990
  const parts2 = [];
6959
6991
  let currentFiber = fiber;
6960
6992
  while (currentFiber && parts2.length < 3) {
6961
- if (currentFiber.type && typeof currentFiber.type === "function") {
6962
- const name = currentFiber.type.name || currentFiber.type.displayName;
6993
+ const fiberType = currentFiber.type;
6994
+ if (fiberType && typeof fiberType === "object") {
6995
+ const name = fiberType.name || fiberType.displayName;
6963
6996
  if (name && name !== "Fragment") {
6964
6997
  parts2.unshift(name);
6965
6998
  }
6966
- } else if (currentFiber.type && typeof currentFiber.type === "string") {
6967
- parts2.push(currentFiber.type);
6999
+ } else if (fiberType && typeof fiberType === "string") {
7000
+ parts2.push(fiberType);
6968
7001
  }
6969
7002
  currentFiber = currentFiber.return;
6970
7003
  }
@@ -6982,11 +7015,13 @@
6982
7015
  function extractReactSourceMap(fiber) {
6983
7016
  try {
6984
7017
  if (fiber._debugSource) {
7018
+ const fiberType = fiber.type;
7019
+ const typeName = fiberType && typeof fiberType === "object" ? fiberType.name || fiberType.displayName : void 0;
6985
7020
  return {
6986
7021
  originalLine: fiber._debugSource.lineNumber || 0,
6987
7022
  originalColumn: fiber._debugSource.columnNumber || 0,
6988
7023
  originalSource: fiber._debugSource.fileName || "",
6989
- originalName: fiber.type?.name || fiber.type?.displayName
7024
+ originalName: typeName
6990
7025
  };
6991
7026
  }
6992
7027
  return null;
@@ -6995,6 +7030,28 @@
6995
7030
  }
6996
7031
  }
6997
7032
  function getVanillaComponentInfo(element) {
7033
+ const sourceLoc = element.getAttribute("data-source-loc");
7034
+ if (sourceLoc) {
7035
+ const match = sourceLoc.match(/^(.+):(\d+):(\d+)$/);
7036
+ if (match) {
7037
+ const [, file, line, column] = match;
7038
+ return {
7039
+ componentLocation: file,
7040
+ componentName: element.tagName.toLowerCase(),
7041
+ framework: "vanilla",
7042
+ elementLocation: {
7043
+ file,
7044
+ line: parseInt(line, 10),
7045
+ column: parseInt(column, 10)
7046
+ },
7047
+ sourceMap: {
7048
+ originalLine: parseInt(line, 10),
7049
+ originalColumn: parseInt(column, 10),
7050
+ originalSource: file
7051
+ }
7052
+ };
7053
+ }
7054
+ }
6998
7055
  const componentName = element.getAttribute("data-component-name");
6999
7056
  const componentFile = element.getAttribute("data-component-file");
7000
7057
  if (!componentName && !componentFile) {
@@ -7007,26 +7064,26 @@
7007
7064
  }
7008
7065
  function getVueComponentInfo(element) {
7009
7066
  if (!element) return null;
7010
- const elementAny = element;
7011
- let codeLocation = elementAny.__vnode?.props?.__v_inspector;
7067
+ const el = element;
7068
+ let codeLocation = el.__vnode?.props?.__v_inspector;
7012
7069
  let vueInstance = null;
7013
7070
  let vnode = null;
7014
7071
  if (!codeLocation) {
7015
- codeLocation = elementAny.__vueParentComponent?.vnode?.props?.__v_inspector;
7016
- vueInstance = elementAny.__vueParentComponent;
7017
- vnode = elementAny.__vueParentComponent?.vnode;
7072
+ codeLocation = el.__vueParentComponent?.vnode?.props?.__v_inspector;
7073
+ vueInstance = el.__vueParentComponent ?? null;
7074
+ vnode = el.__vueParentComponent?.vnode ?? null;
7018
7075
  }
7019
7076
  if (!codeLocation) {
7020
- codeLocation = elementAny.__v_inspector;
7077
+ codeLocation = el.__v_inspector;
7021
7078
  }
7022
7079
  if (!codeLocation) {
7023
- vueInstance = elementAny.__vue__ || elementAny.__vueParentComponent;
7080
+ vueInstance = el.__vue__ ?? el.__vueParentComponent ?? null;
7024
7081
  if (vueInstance) {
7025
7082
  codeLocation = vueInstance.__v_inspector || vueInstance.$options?.__v_inspector || vueInstance.type?.__v_inspector;
7026
7083
  }
7027
7084
  }
7028
7085
  if (!codeLocation && !vnode) {
7029
- vnode = elementAny.__vnode || elementAny.$vnode;
7086
+ vnode = el.__vnode ?? el.$vnode ?? null;
7030
7087
  if (vnode) {
7031
7088
  codeLocation = vnode.__v_inspector || vnode.props?.__v_inspector || vnode.componentOptions?.__v_inspector;
7032
7089
  }
@@ -7287,6 +7344,7 @@
7287
7344
  }
7288
7345
 
7289
7346
  // src/annotator-toolbar.ts
7347
+ var CONSOLE_METHODS = ["log", "info", "warn", "error", "debug"];
7290
7348
  var AnnotatorToolbar = class extends i4 {
7291
7349
  constructor() {
7292
7350
  super(...arguments);
@@ -7297,7 +7355,9 @@
7297
7355
  this.selectionCount = 0;
7298
7356
  this.isInspecting = false;
7299
7357
  this.commentPopover = { visible: false, element: null, comment: "" };
7358
+ this.toastMessage = "";
7300
7359
  this.popoverCleanup = null;
7360
+ this.toastTimeout = null;
7301
7361
  this.socket = null;
7302
7362
  this.rpc = null;
7303
7363
  this.selectionManager = null;
@@ -7329,24 +7389,27 @@
7329
7389
  this.selectionManager.setOnEditClick((element) => {
7330
7390
  this.showCommentPopoverForElement(element);
7331
7391
  });
7332
- this.inspectionManager = createInspectionManager(
7333
- (element) => this.handleElementSelected(element),
7334
- (element) => this.shouldIgnoreElement(element),
7335
- (element) => this.selectionManager?.hasElement(element) || false
7336
- );
7392
+ this.inspectionManager = createInspectionManager({
7393
+ onElementSelect: (element) => this.handleElementSelected(element),
7394
+ shouldIgnoreElement: (element) => this.shouldIgnoreElement(element),
7395
+ isElementSelected: (element) => this.selectionManager?.hasElement(element) || false,
7396
+ onEscape: () => this.exitInspectingMode(),
7397
+ onCopy: () => this.copySelectedElements()
7398
+ });
7337
7399
  }
7338
7400
  initializeConsoleCapture() {
7339
- const methods = ["log", "info", "warn", "error", "debug"];
7340
- methods.forEach((method) => {
7401
+ CONSOLE_METHODS.forEach((method) => {
7341
7402
  this.originalConsoleMethods[method] = console[method].bind(console);
7342
7403
  console[method] = (...args) => {
7404
+ const MAX_ARG_LENGTH = 1e4;
7343
7405
  this.consoleBuffer.push({
7344
7406
  type: method,
7345
7407
  args: args.map((arg) => {
7346
7408
  try {
7347
- return typeof arg === "object" ? JSON.stringify(arg) : String(arg);
7409
+ const str = typeof arg === "object" ? JSON.stringify(arg) : String(arg);
7410
+ return str.length > MAX_ARG_LENGTH ? str.slice(0, MAX_ARG_LENGTH) + "...[truncated]" : str;
7348
7411
  } catch {
7349
- return String(arg);
7412
+ return "[circular or unserializable]";
7350
7413
  }
7351
7414
  }),
7352
7415
  timestamp: Date.now()
@@ -7359,8 +7422,7 @@
7359
7422
  });
7360
7423
  }
7361
7424
  restoreConsoleMethods() {
7362
- const methods = ["log", "info", "warn", "error", "debug"];
7363
- methods.forEach((method) => {
7425
+ CONSOLE_METHODS.forEach((method) => {
7364
7426
  if (this.originalConsoleMethods[method]) {
7365
7427
  console[method] = this.originalConsoleMethods[method];
7366
7428
  }
@@ -7431,9 +7493,8 @@
7431
7493
  );
7432
7494
  const addComments = (items, selectedElements) => {
7433
7495
  for (const item of items) {
7434
- for (const [element] of selectedElements) {
7435
- const info = selectedElements.get(element);
7436
- if (info?.index === item.index) {
7496
+ for (const [element, info] of selectedElements) {
7497
+ if (info.index === item.index) {
7437
7498
  const comment = this.elementComments.get(element);
7438
7499
  if (comment) {
7439
7500
  item.comment = comment;
@@ -7536,7 +7597,7 @@
7536
7597
  injectCSS(css) {
7537
7598
  try {
7538
7599
  const style = document.createElement("style");
7539
- style.setAttribute("data-injected-by", "instantcode");
7600
+ style.setAttribute("data-injected-by", "ai-annotator");
7540
7601
  style.textContent = css;
7541
7602
  document.head.appendChild(style);
7542
7603
  return { success: true };
@@ -7613,14 +7674,17 @@
7613
7674
  document.addEventListener("keydown", this.handlePopoverKeydown);
7614
7675
  this.updateComplete.then(() => {
7615
7676
  const popoverEl = this.shadowRoot?.querySelector(".popover");
7677
+ const inputEl = this.shadowRoot?.querySelector(".popover-input");
7616
7678
  if (!popoverEl || !element) return;
7679
+ inputEl?.focus();
7617
7680
  this.popoverCleanup = autoUpdate(element, popoverEl, () => {
7618
7681
  computePosition2(element, popoverEl, {
7619
- placement: "bottom",
7682
+ strategy: "fixed",
7683
+ placement: "bottom-start",
7620
7684
  middleware: [
7621
- offset2(10),
7622
- flip2({ fallbackPlacements: ["top", "right", "left"] }),
7623
- shift2({ padding: 10 })
7685
+ offset2(8),
7686
+ flip2({ fallbackPlacements: ["top-start", "bottom-end", "top-end", "right", "left"] }),
7687
+ shift2({ padding: 8 })
7624
7688
  ]
7625
7689
  }).then(({ x: x2, y: y3 }) => {
7626
7690
  Object.assign(popoverEl.style, {
@@ -7631,6 +7695,16 @@
7631
7695
  });
7632
7696
  });
7633
7697
  }
7698
+ handlePopoverInputKeydown(e5) {
7699
+ if (e5.key === "Enter") {
7700
+ e5.preventDefault();
7701
+ this.hideCommentPopover();
7702
+ } else if (e5.key === "Escape") {
7703
+ e5.preventDefault();
7704
+ e5.stopPropagation();
7705
+ this.hideCommentPopover();
7706
+ }
7707
+ }
7634
7708
  hideCommentPopover() {
7635
7709
  document.removeEventListener("keydown", this.handlePopoverKeydown);
7636
7710
  if (this.popoverCleanup) {
@@ -7656,9 +7730,20 @@
7656
7730
  }
7657
7731
  }
7658
7732
  shouldIgnoreElement(element) {
7659
- if (element.closest("annotator-toolbar")) return true;
7660
- if (element.classList.contains("annotator-badge")) return true;
7661
- if (element.classList.contains("annotator-ignore")) return true;
7733
+ let current = element;
7734
+ while (current) {
7735
+ if (current instanceof Element) {
7736
+ if (current.tagName.toLowerCase() === "annotator-toolbar") return true;
7737
+ if (current.classList.contains("annotator-badge")) return true;
7738
+ if (current.classList.contains("annotator-ignore")) return true;
7739
+ }
7740
+ const parent = current.parentNode;
7741
+ if (parent instanceof ShadowRoot) {
7742
+ current = parent.host;
7743
+ } else {
7744
+ current = parent;
7745
+ }
7746
+ }
7662
7747
  return false;
7663
7748
  }
7664
7749
  cleanup() {
@@ -7674,22 +7759,43 @@
7674
7759
  }
7675
7760
  log(...args) {
7676
7761
  if (this.verbose) {
7677
- console.log("[InstantCode]", ...args);
7762
+ console.log("[AI Annotator]", ...args);
7678
7763
  }
7679
7764
  }
7680
- toggleInspect() {
7765
+ exitInspectingMode() {
7681
7766
  if (this.isInspecting) {
7682
7767
  this.inspectionManager?.exitInspectionMode();
7683
7768
  this.isInspecting = false;
7684
7769
  if (this.commentPopover.visible) {
7685
7770
  this.hideCommentPopover();
7686
7771
  }
7772
+ }
7773
+ }
7774
+ async copySelectedElements() {
7775
+ const elements = this.getSelectedElements();
7776
+ if (elements.length === 0) {
7777
+ this.showToast("No elements selected");
7778
+ return;
7779
+ }
7780
+ const text = JSON.stringify(elements, null, 2);
7781
+ try {
7782
+ await navigator.clipboard.writeText(text);
7783
+ this.showToast(`Copied ${elements.length} element(s)`);
7784
+ } catch (error) {
7785
+ this.showToast("Failed to copy");
7786
+ this.log("Failed to copy:", error);
7787
+ }
7788
+ }
7789
+ toggleInspect() {
7790
+ if (this.isInspecting) {
7791
+ this.exitInspectingMode();
7687
7792
  } else {
7688
7793
  this.inspectionManager?.enterInspectionMode();
7689
7794
  this.isInspecting = true;
7690
7795
  }
7691
7796
  }
7692
7797
  handleClearClick() {
7798
+ this.exitInspectingMode();
7693
7799
  this.clearSelection();
7694
7800
  if (this.socket?.connected) {
7695
7801
  this.socket.emit("selectionChanged", {
@@ -7719,13 +7825,44 @@
7719
7825
  <path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z" />
7720
7826
  </svg>`;
7721
7827
  }
7828
+ renderClipboardIcon() {
7829
+ return x`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
7830
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9.75a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" />
7831
+ </svg>`;
7832
+ }
7833
+ showToast(message) {
7834
+ if (this.toastTimeout) {
7835
+ clearTimeout(this.toastTimeout);
7836
+ }
7837
+ this.toastMessage = message;
7838
+ this.toastTimeout = setTimeout(() => {
7839
+ this.toastMessage = "";
7840
+ }, 2e3);
7841
+ }
7842
+ async copySessionId() {
7843
+ this.exitInspectingMode();
7844
+ if (!this.sessionId) {
7845
+ this.showToast("No session ID");
7846
+ return;
7847
+ }
7848
+ const text = `use annotator tool (session: ${this.sessionId}) to read my live feedback`;
7849
+ try {
7850
+ await navigator.clipboard.writeText(text);
7851
+ this.showToast("Copied!");
7852
+ this.log("Copied to clipboard:", text);
7853
+ } catch (error) {
7854
+ this.showToast("Failed to copy");
7855
+ this.log("Failed to copy:", error);
7856
+ }
7857
+ }
7722
7858
  renderErrorIcon() {
7723
7859
  return x`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
7724
7860
  <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
7725
7861
  </svg>`;
7726
7862
  }
7727
7863
  openHelpPage() {
7728
- window.open("https://instantcode.dev", "_blank");
7864
+ this.exitInspectingMode();
7865
+ window.open("https://ai-annotator.dev", "_blank");
7729
7866
  }
7730
7867
  render() {
7731
7868
  if (!this.connected) {
@@ -7733,18 +7870,13 @@
7733
7870
  <div class="toolbar">
7734
7871
  <div class="error-message">
7735
7872
  ${this.renderErrorIcon()}
7736
- <span>Cannot connect to InstantCode server</span>
7873
+ <span>Cannot connect to AI Annotator server</span>
7737
7874
  </div>
7738
7875
  </div>
7739
7876
  `;
7740
7877
  }
7741
7878
  return x`
7742
7879
  <div class="toolbar">
7743
- ${this.selectionCount > 0 ? x`
7744
- <span class="selection-badge">${this.selectionCount}</span>
7745
- <div class="divider"></div>
7746
- ` : ""}
7747
-
7748
7880
  <button
7749
7881
  class="toolbar-btn ${this.isInspecting ? "active" : ""}"
7750
7882
  @click=${this.toggleInspect}
@@ -7753,17 +7885,28 @@
7753
7885
  ${this.renderCursorIcon()}
7754
7886
  </button>
7755
7887
 
7888
+ <div class="btn-with-badge">
7889
+ <button
7890
+ class="toolbar-btn"
7891
+ @click=${this.handleClearClick}
7892
+ title="Clear all selections"
7893
+ ?disabled=${this.selectionCount === 0}
7894
+ >
7895
+ ${this.renderTrashIcon()}
7896
+ </button>
7897
+ ${this.selectionCount > 0 ? x`<span class="badge">${this.selectionCount}</span>` : ""}
7898
+ </div>
7899
+
7900
+ <div class="divider"></div>
7901
+
7756
7902
  <button
7757
7903
  class="toolbar-btn"
7758
- @click=${this.handleClearClick}
7759
- title="Clear all selections"
7760
- ?disabled=${this.selectionCount === 0}
7904
+ @click=${this.copySessionId}
7905
+ title="Copy session ID"
7761
7906
  >
7762
- ${this.renderTrashIcon()}
7907
+ ${this.renderClipboardIcon()}
7763
7908
  </button>
7764
7909
 
7765
- <div class="divider"></div>
7766
-
7767
7910
  <button
7768
7911
  class="toolbar-btn"
7769
7912
  @click=${this.openHelpPage}
@@ -7771,27 +7914,27 @@
7771
7914
  >
7772
7915
  ${this.renderHelpIcon()}
7773
7916
  </button>
7917
+
7918
+ ${this.toastMessage ? x`<div class="toast">${this.toastMessage}</div>` : ""}
7774
7919
  </div>
7775
7920
 
7776
7921
  ${this.commentPopover.visible ? x`
7777
7922
  <div class="popover">
7778
- <div class="popover-header">
7779
- <div class="popover-title">
7780
- ${this.elementComments.has(this.commentPopover.element) ? "Edit Comment" : "Add Comment"}
7781
- <span class="tag">${this.commentPopover.element?.tagName?.toLowerCase() || "element"}</span>
7782
- </div>
7783
- <button class="popover-close" @click=${this.hideCommentPopover}>
7784
- ${this.renderCloseIcon()}
7785
- </button>
7786
- </div>
7787
- <textarea
7788
- class="popover-textarea"
7789
- placeholder="Describe what you want to change about this element..."
7923
+ <input
7924
+ type="text"
7925
+ class="popover-input"
7926
+ placeholder="Add a note..."
7790
7927
  .value=${this.commentPopover.comment}
7791
7928
  @input=${this.handlePopoverInput}
7792
- ></textarea>
7929
+ @keydown=${this.handlePopoverInputKeydown}
7930
+ />
7793
7931
  <div class="popover-actions">
7794
- <button class="popover-btn popover-btn-danger" @click=${this.removeSelectedElement}>Remove</button>
7932
+ <button class="popover-btn danger" @click=${this.removeSelectedElement} title="Remove selection">
7933
+ ${this.renderTrashIcon()}
7934
+ </button>
7935
+ <button class="popover-btn" @click=${this.hideCommentPopover} title="Close (Esc)">
7936
+ ${this.renderCloseIcon()}
7937
+ </button>
7795
7938
  </div>
7796
7939
  </div>
7797
7940
  ` : ""}
@@ -7808,6 +7951,7 @@
7808
7951
  }
7809
7952
 
7810
7953
  .toolbar {
7954
+ position: relative;
7811
7955
  display: flex;
7812
7956
  align-items: center;
7813
7957
  gap: 4px;
@@ -7862,13 +8006,26 @@
7862
8006
  margin: 0 4px;
7863
8007
  }
7864
8008
 
7865
- .selection-badge {
8009
+ .btn-with-badge {
8010
+ position: relative;
8011
+ }
8012
+
8013
+ .badge {
8014
+ position: absolute;
8015
+ top: -4px;
8016
+ right: -4px;
8017
+ min-width: 16px;
8018
+ height: 16px;
8019
+ padding: 0 4px;
7866
8020
  background: #3b82f6;
7867
- padding: 2px 8px;
7868
- border-radius: 10px;
7869
- font-size: 11px;
8021
+ border-radius: 8px;
8022
+ font-size: 10px;
7870
8023
  font-weight: 600;
7871
8024
  color: white;
8025
+ display: flex;
8026
+ align-items: center;
8027
+ justify-content: center;
8028
+ pointer-events: none;
7872
8029
  }
7873
8030
 
7874
8031
  .error-message {
@@ -7886,117 +8043,113 @@
7886
8043
  flex-shrink: 0;
7887
8044
  }
7888
8045
 
7889
- /* Popover styles */
8046
+ /* Minimal Popover */
7890
8047
  .popover {
7891
8048
  position: fixed;
7892
8049
  top: 0;
7893
8050
  left: 0;
7894
8051
  z-index: 1000000;
7895
- background: white;
7896
- border-radius: 10px;
7897
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
7898
- padding: 12px;
7899
- min-width: 280px;
7900
- max-width: 350px;
7901
- }
7902
-
7903
- .popover-header {
7904
8052
  display: flex;
7905
- align-items: center;
7906
- justify-content: space-between;
7907
- margin-bottom: 10px;
7908
- padding-bottom: 8px;
7909
- border-bottom: 1px solid #eee;
8053
+ align-items: stretch;
8054
+ background: #1a1a1a;
8055
+ border-radius: 8px;
8056
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.08);
8057
+ overflow: hidden;
8058
+ animation: popover-in 0.15s ease-out;
7910
8059
  }
7911
8060
 
7912
- .popover-title {
7913
- font-size: 13px;
7914
- font-weight: 600;
7915
- color: #333;
7916
- display: flex;
7917
- align-items: center;
7918
- gap: 6px;
7919
- }
7920
-
7921
- .popover-title .tag {
7922
- background: #f0f0f0;
7923
- padding: 2px 6px;
7924
- border-radius: 4px;
7925
- font-size: 11px;
7926
- color: #666;
7927
- font-weight: 500;
8061
+ @keyframes popover-in {
8062
+ from {
8063
+ opacity: 0;
8064
+ transform: scale(0.95) translateY(-4px);
8065
+ }
8066
+ to {
8067
+ opacity: 1;
8068
+ transform: scale(1) translateY(0);
8069
+ }
7928
8070
  }
7929
8071
 
7930
- .popover-close {
7931
- background: none;
8072
+ .popover-input {
8073
+ flex: 1;
8074
+ min-width: 180px;
8075
+ max-width: 260px;
8076
+ padding: 10px 12px;
7932
8077
  border: none;
7933
- color: #999;
7934
- cursor: pointer;
7935
- padding: 4px;
7936
- border-radius: 4px;
7937
- display: flex;
7938
- align-items: center;
7939
- justify-content: center;
7940
- }
7941
-
7942
- .popover-close:hover {
7943
- background: #f0f0f0;
7944
- color: #333;
7945
- }
7946
-
7947
- .popover-close svg {
7948
- width: 16px;
7949
- height: 16px;
7950
- }
7951
-
7952
- .popover-textarea {
7953
- width: 100%;
7954
- min-height: 80px;
7955
- border: 1px solid #ddd;
7956
- border-radius: 6px;
7957
- padding: 10px;
8078
+ background: transparent;
8079
+ color: #fff;
7958
8080
  font-size: 13px;
7959
8081
  font-family: inherit;
7960
- resize: vertical;
7961
- box-sizing: border-box;
8082
+ outline: none;
7962
8083
  }
7963
8084
 
7964
- .popover-textarea:focus {
7965
- outline: none;
7966
- border-color: #3b82f6;
7967
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
8085
+ .popover-input::placeholder {
8086
+ color: #555;
7968
8087
  }
7969
8088
 
7970
8089
  .popover-actions {
7971
8090
  display: flex;
7972
8091
  align-items: center;
7973
- gap: 8px;
7974
- margin-top: 10px;
8092
+ border-left: 1px solid rgba(255, 255, 255, 0.06);
7975
8093
  }
7976
8094
 
7977
8095
  .popover-btn {
7978
- padding: 6px 14px;
7979
- border-radius: 6px;
7980
- font-size: 12px;
7981
- font-weight: 500;
8096
+ display: flex;
8097
+ align-items: center;
8098
+ justify-content: center;
8099
+ width: 36px;
8100
+ height: 100%;
8101
+ border: none;
8102
+ background: transparent;
8103
+ color: #666;
7982
8104
  cursor: pointer;
7983
- transition: all 0.15s ease;
8105
+ transition: all 0.12s ease;
7984
8106
  }
7985
8107
 
7986
- .popover-btn-danger {
7987
- background: transparent;
7988
- border: 1px solid #ef4444;
7989
- color: #ef4444;
8108
+ .popover-btn:hover {
8109
+ background: rgba(255, 255, 255, 0.06);
8110
+ color: #fff;
7990
8111
  }
7991
8112
 
7992
- .popover-btn-danger:hover {
7993
- background: #ef4444;
7994
- color: white;
8113
+ .popover-btn.danger:hover {
8114
+ background: rgba(239, 68, 68, 0.15);
8115
+ color: #f87171;
8116
+ }
8117
+
8118
+ .popover-btn svg {
8119
+ width: 14px;
8120
+ height: 14px;
7995
8121
  }
7996
8122
 
7997
8123
  .hidden {
7998
8124
  display: none;
7999
8125
  }
8126
+
8127
+ /* Toast */
8128
+ .toast {
8129
+ position: absolute;
8130
+ bottom: 100%;
8131
+ right: 0;
8132
+ margin-bottom: 8px;
8133
+ padding: 8px 12px;
8134
+ background: rgba(0, 0, 0, 0.9);
8135
+ border-radius: 6px;
8136
+ font-size: 12px;
8137
+ color: #10b981;
8138
+ white-space: nowrap;
8139
+ animation: toast-in 0.2s ease-out;
8140
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
8141
+ }
8142
+
8143
+ @keyframes toast-in {
8144
+ from {
8145
+ opacity: 0;
8146
+ transform: translateY(4px);
8147
+ }
8148
+ to {
8149
+ opacity: 1;
8150
+ transform: translateY(0);
8151
+ }
8152
+ }
8000
8153
  `;
8001
8154
  __decorateClass([
8002
8155
  n4({ attribute: "ws-endpoint" })
@@ -8019,6 +8172,9 @@
8019
8172
  __decorateClass([
8020
8173
  r5()
8021
8174
  ], AnnotatorToolbar.prototype, "commentPopover", 2);
8175
+ __decorateClass([
8176
+ r5()
8177
+ ], AnnotatorToolbar.prototype, "toastMessage", 2);
8022
8178
  AnnotatorToolbar = __decorateClass([
8023
8179
  t3("annotator-toolbar")
8024
8180
  ], AnnotatorToolbar);