astro-annotate 0.1.0 → 0.2.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.
package/dist/client.js CHANGED
@@ -501,8 +501,9 @@ function generateSelector(target) {
501
501
  }
502
502
  const parent = current.parentElement;
503
503
  if (parent) {
504
+ const currentTag = current.tagName;
504
505
  const siblings = Array.from(parent.children).filter(
505
- (s) => s.tagName === current.tagName
506
+ (s) => s.tagName === currentTag
506
507
  );
507
508
  if (siblings.length > 1) {
508
509
  const index = siblings.indexOf(current) + 1;
@@ -576,16 +577,19 @@ var Highlighter = class {
576
577
 
577
578
  // src/client/form.ts
578
579
  var AnnotationForm = class {
579
- constructor(shadowRoot, onSubmitted) {
580
+ constructor(shadowRoot, onSubmitted, onClosed, devMode) {
580
581
  this.shadowRoot = shadowRoot;
582
+ this.devMode = devMode;
581
583
  this.container = document.createElement("div");
582
584
  this.container.className = "aa-form-container";
583
585
  this.container.style.display = "none";
584
586
  this.shadowRoot.appendChild(this.container);
585
587
  this.onSubmitted = onSubmitted;
588
+ this.onClosed = onClosed;
586
589
  }
587
590
  container;
588
591
  onSubmitted;
592
+ onClosed;
589
593
  show(target) {
590
594
  const selector = generateSelector(target);
591
595
  const elementTag = target.tagName.toLowerCase();
@@ -613,7 +617,7 @@ var AnnotationForm = class {
613
617
  <button class="aa-form-close" data-action="close">&times;</button>
614
618
  </div>
615
619
  <div class="aa-form-body">
616
- <input class="aa-input" type="text" placeholder="Your name" data-field="author" value="" />
620
+ ${this.devMode ? "" : '<input class="aa-input" type="text" placeholder="Your name" data-field="author" value="" />'}
617
621
  <textarea class="aa-textarea" placeholder="What should be changed?" data-field="text"></textarea>
618
622
  <div class="aa-form-actions">
619
623
  <button class="aa-btn aa-btn-secondary" data-action="close">Cancel</button>
@@ -638,7 +642,7 @@ var AnnotationForm = class {
638
642
  }
639
643
  async submit(selector, elementTag, elementText) {
640
644
  const text = this.container.querySelector('[data-field="text"]')?.value?.trim();
641
- const author = this.container.querySelector('[data-field="author"]')?.value?.trim();
645
+ const author = this.devMode ? "Developer" : this.container.querySelector('[data-field="author"]')?.value?.trim();
642
646
  if (!text) return;
643
647
  const submitBtn = this.container.querySelector('[data-action="submit"]');
644
648
  submitBtn.disabled = true;
@@ -669,8 +673,10 @@ var AnnotationForm = class {
669
673
  }
670
674
  }
671
675
  hide() {
676
+ if (this.container.style.display === "none") return;
672
677
  this.container.style.display = "none";
673
678
  this.container.innerHTML = "";
679
+ this.onClosed();
674
680
  }
675
681
  isVisible() {
676
682
  return this.container.style.display !== "none";
@@ -813,6 +819,7 @@ var Overlay = class {
813
819
  form;
814
820
  pinManager;
815
821
  active = false;
822
+ devMode = !!window.__ASTRO_ANNOTATE_DEV__;
816
823
  annotations = [];
817
824
  constructor() {
818
825
  this.host = document.createElement("div");
@@ -824,7 +831,12 @@ var Overlay = class {
824
831
  this.shadowRoot.appendChild(style);
825
832
  this.toolbar = new Toolbar(this.shadowRoot, (active) => this.setActive(active));
826
833
  this.highlighter = new Highlighter(this.shadowRoot);
827
- this.form = new AnnotationForm(this.shadowRoot, () => this.onAnnotationCreated());
834
+ this.form = new AnnotationForm(
835
+ this.shadowRoot,
836
+ () => this.onAnnotationCreated(),
837
+ () => this.onFormClosed(),
838
+ this.devMode
839
+ );
828
840
  this.pinManager = new PinManager(this.shadowRoot, () => this.loadAnnotations());
829
841
  this.loadAnnotations();
830
842
  document.addEventListener("click", (e) => {
@@ -841,6 +853,7 @@ var Overlay = class {
841
853
  window.addEventListener("resize", () => {
842
854
  this.renderPins();
843
855
  }, { passive: true });
856
+ document.addEventListener("keydown", this.onKeyDown);
844
857
  }
845
858
  setActive(active) {
846
859
  this.active = active;
@@ -859,12 +872,33 @@ var Overlay = class {
859
872
  onElementClick = (e) => {
860
873
  const target = e.target;
861
874
  if (this.host.contains(target) || this.host === target) return;
875
+ if (this.form.isVisible()) return;
862
876
  e.preventDefault();
863
877
  e.stopPropagation();
864
878
  this.highlighter.hide();
865
879
  document.removeEventListener("mousemove", this.highlighter.onMouseMove);
866
880
  this.form.show(target);
867
881
  };
882
+ onKeyDown = (e) => {
883
+ const active = document.activeElement;
884
+ const isExternalInput = active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA" || active.isContentEditable) && active !== this.host && !this.host.contains(active);
885
+ if (e.altKey && e.code === "KeyC" && !isExternalInput) {
886
+ e.preventDefault();
887
+ this.toolbar.toggle();
888
+ return;
889
+ }
890
+ if (e.key === "Escape") {
891
+ if (this.form.isVisible()) {
892
+ this.form.hide();
893
+ return;
894
+ }
895
+ if (this.active) {
896
+ this.toolbar.deactivate();
897
+ this.setActive(false);
898
+ return;
899
+ }
900
+ }
901
+ };
868
902
  async loadAnnotations() {
869
903
  try {
870
904
  const page = window.location.pathname;
@@ -881,11 +915,15 @@ var Overlay = class {
881
915
  this.pinManager.render(this.annotations);
882
916
  }
883
917
  onAnnotationCreated() {
884
- this.toolbar.deactivate();
885
- this.setActive(false);
886
918
  this.loadAnnotations();
887
919
  }
920
+ onFormClosed() {
921
+ if (this.active) {
922
+ document.addEventListener("mousemove", this.highlighter.onMouseMove);
923
+ }
924
+ }
888
925
  destroy() {
926
+ document.removeEventListener("keydown", this.onKeyDown);
889
927
  this.setActive(false);
890
928
  this.toolbar.destroy();
891
929
  this.highlighter.destroy();
@@ -898,9 +936,8 @@ var Overlay = class {
898
936
  // src/client/index.ts
899
937
  var overlay = null;
900
938
  function init() {
901
- const existing = document.getElementById(SHADOW_ROOT_ID);
902
- if (existing) {
903
- existing.remove();
939
+ if (overlay) {
940
+ overlay.destroy();
904
941
  overlay = null;
905
942
  }
906
943
  overlay = new Overlay();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/styles.ts","../src/client/toolbar.ts","../src/client/selector.ts","../src/client/highlighter.ts","../src/client/form.ts","../src/client/pin.ts","../src/client/overlay.ts","../src/client/index.ts"],"sourcesContent":["export const OVERLAY_STYLES = `\n :host {\n all: initial;\n position: fixed;\n z-index: 2147483647;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #1a1a2e;\n }\n\n *, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n /* Toolbar */\n .aa-toolbar {\n position: fixed;\n bottom: 20px;\n right: 20px;\n display: flex;\n align-items: center;\n gap: 8px;\n background: #1a1a2e;\n color: #fff;\n padding: 8px 16px;\n border-radius: 50px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n border: 1.5px solid rgba(255, 255, 255, 0.2);\n cursor: pointer;\n user-select: none;\n transition: all 0.2s ease;\n }\n\n .aa-toolbar:hover {\n background: #16213e;\n transform: translateY(-1px);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);\n border-color: rgba(255, 255, 255, 0.35);\n }\n\n .aa-toolbar.aa-active {\n background: #e94560;\n }\n\n .aa-toolbar.aa-active:hover {\n background: #c73e54;\n }\n\n .aa-toolbar-icon {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n }\n\n .aa-toolbar-label {\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n }\n\n .aa-badge {\n background: #e94560;\n color: #fff;\n font-size: 11px;\n font-weight: 700;\n min-width: 20px;\n height: 20px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 6px;\n }\n\n .aa-toolbar.aa-active .aa-badge {\n background: rgba(255, 255, 255, 0.3);\n }\n\n /* Element Highlight */\n .aa-highlight {\n position: fixed;\n pointer-events: none;\n border: 2px solid #e94560;\n background: rgba(233, 69, 96, 0.08);\n border-radius: 3px;\n z-index: 2147483646;\n transition: all 0.1s ease;\n }\n\n .aa-highlight-label {\n position: absolute;\n top: -26px;\n left: -2px;\n background: #e94560;\n color: #fff;\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 3px 3px 0 0;\n white-space: nowrap;\n max-width: 300px;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* Annotation Form */\n .aa-form-container {\n position: fixed;\n z-index: 2147483647;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n width: 340px;\n overflow: hidden;\n }\n\n .aa-form-header {\n background: #1a1a2e;\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .aa-form-header-title {\n font-size: 13px;\n font-weight: 600;\n }\n\n .aa-form-header-selector {\n font-size: 11px;\n opacity: 0.7;\n font-family: 'SF Mono', Monaco, monospace;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .aa-form-close {\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n font-size: 18px;\n line-height: 1;\n opacity: 0.7;\n padding: 0 0 0 8px;\n }\n\n .aa-form-close:hover {\n opacity: 1;\n }\n\n .aa-form-body {\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .aa-input, .aa-textarea {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n transition: border-color 0.15s;\n }\n\n .aa-input:focus, .aa-textarea:focus {\n border-color: #e94560;\n }\n\n .aa-textarea {\n resize: vertical;\n min-height: 80px;\n }\n\n .aa-form-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n }\n\n .aa-btn {\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s;\n }\n\n .aa-btn-primary {\n background: #e94560;\n color: #fff;\n }\n\n .aa-btn-primary:hover {\n background: #c73e54;\n }\n\n .aa-btn-primary:disabled {\n background: #ccc;\n cursor: not-allowed;\n }\n\n .aa-btn-secondary {\n background: #f0f0f0;\n color: #333;\n }\n\n .aa-btn-secondary:hover {\n background: #e0e0e0;\n }\n\n /* Pins */\n .aa-pin {\n position: absolute;\n width: 28px;\n height: 28px;\n border-radius: 50% 50% 50% 0;\n background: #e94560;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n transform: rotate(-45deg);\n box-shadow: 0 2px 8px rgba(233, 69, 96, 0.4);\n transition: transform 0.15s, box-shadow 0.15s;\n z-index: 2147483645;\n }\n\n .aa-pin:hover {\n transform: rotate(-45deg) scale(1.15);\n box-shadow: 0 4px 12px rgba(233, 69, 96, 0.5);\n }\n\n .aa-pin.aa-resolved {\n background: #2ecc71;\n box-shadow: 0 2px 8px rgba(46, 204, 113, 0.4);\n }\n\n .aa-pin-number {\n transform: rotate(45deg);\n }\n\n /* Pin Detail Popup */\n .aa-pin-detail {\n position: fixed;\n z-index: 2147483647;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n width: 320px;\n overflow: hidden;\n }\n\n .aa-pin-detail-header {\n background: #1a1a2e;\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .aa-pin-detail-meta {\n font-size: 11px;\n opacity: 0.7;\n }\n\n .aa-pin-detail-body {\n padding: 16px;\n }\n\n .aa-pin-detail-text {\n font-size: 14px;\n margin-bottom: 12px;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .aa-pin-detail-info {\n font-size: 12px;\n color: #888;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .aa-pin-detail-selector {\n font-family: 'SF Mono', Monaco, monospace;\n font-size: 11px;\n color: #666;\n background: #f5f5f5;\n padding: 4px 8px;\n border-radius: 4px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .aa-pin-detail-actions {\n padding: 0 16px 16px;\n display: flex;\n gap: 8px;\n }\n\n .aa-status-btn {\n padding: 4px 12px;\n border-radius: 4px;\n font-size: 12px;\n cursor: pointer;\n border: 1px solid #e0e0e0;\n background: #fff;\n transition: all 0.15s;\n }\n\n .aa-status-btn:hover {\n background: #f5f5f5;\n }\n\n .aa-status-btn.aa-resolve {\n color: #2ecc71;\n border-color: #2ecc71;\n }\n\n .aa-status-btn.aa-resolve:hover {\n background: #2ecc71;\n color: #fff;\n }\n\n .aa-status-btn.aa-reopen {\n color: #e94560;\n border-color: #e94560;\n }\n\n .aa-status-btn.aa-reopen:hover {\n background: #e94560;\n color: #fff;\n }\n\n /* Dark mode */\n @media (prefers-color-scheme: dark) {\n .aa-form-container, .aa-pin-detail {\n background: #2d2d3f;\n color: #e0e0e0;\n }\n\n .aa-input, .aa-textarea {\n background: #1a1a2e;\n border-color: #404060;\n color: #e0e0e0;\n }\n\n .aa-btn-secondary {\n background: #404060;\n color: #e0e0e0;\n }\n\n .aa-btn-secondary:hover {\n background: #505070;\n }\n\n .aa-pin-detail-selector {\n background: #1a1a2e;\n color: #aaa;\n }\n\n .aa-status-btn {\n background: #2d2d3f;\n border-color: #404060;\n }\n\n .aa-status-btn:hover {\n background: #404060;\n }\n }\n`;\n","const ANNOTATE_ICON = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"aa-toolbar-icon\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"9\"/><line x1=\"12\" y1=\"6\" x2=\"12\" y2=\"12\"/></svg>`;\n\nexport class Toolbar {\n private el: HTMLElement;\n private label: HTMLElement;\n private badge: HTMLElement;\n private active = false;\n private onToggle: (active: boolean) => void;\n\n constructor(shadowRoot: ShadowRoot, onToggle: (active: boolean) => void) {\n this.onToggle = onToggle;\n\n this.el = document.createElement('div');\n this.el.className = 'aa-toolbar';\n\n this.el.innerHTML = ANNOTATE_ICON;\n\n this.label = document.createElement('span');\n this.label.className = 'aa-toolbar-label';\n this.label.textContent = 'Annotate';\n this.el.appendChild(this.label);\n\n this.badge = document.createElement('span');\n this.badge.className = 'aa-badge';\n this.badge.textContent = '0';\n this.badge.style.display = 'none';\n this.el.appendChild(this.badge);\n\n this.el.addEventListener('click', () => {\n this.toggle();\n });\n\n shadowRoot.appendChild(this.el);\n }\n\n private toggle(): void {\n this.active = !this.active;\n this.el.classList.toggle('aa-active', this.active);\n this.label.textContent = this.active ? 'Stop' : 'Annotate';\n this.onToggle(this.active);\n }\n\n deactivate(): void {\n if (this.active) {\n this.active = false;\n this.el.classList.remove('aa-active');\n this.label.textContent = 'Annotate';\n }\n }\n\n updateCount(count: number): void {\n this.badge.textContent = String(count);\n this.badge.style.display = count > 0 ? 'flex' : 'none';\n }\n\n destroy(): void {\n this.el.remove();\n }\n}\n","const ASTRO_CLASS_RE = /^astro-[a-zA-Z0-9]+$/;\nconst IGNORED_TAGS = new Set(['html', 'body', 'head']);\n\nfunction isAstroClass(cls: string): boolean {\n return ASTRO_CLASS_RE.test(cls);\n}\n\nfunction getStableClasses(el: Element): string[] {\n return Array.from(el.classList).filter((cls) => !isAstroClass(cls));\n}\n\nfunction isUnique(selector: string): boolean {\n try {\n return document.querySelectorAll(selector).length === 1;\n } catch {\n return false;\n }\n}\n\nfunction escapeSelector(value: string): string {\n return CSS.escape(value);\n}\n\nexport function generateSelector(target: Element): string {\n if (IGNORED_TAGS.has(target.tagName.toLowerCase())) {\n return target.tagName.toLowerCase();\n }\n\n // 1. ID (if stable — not auto-generated)\n if (target.id && !/^[0-9]/.test(target.id) && !target.id.includes(':')) {\n const sel = `#${escapeSelector(target.id)}`;\n if (isUnique(sel)) return sel;\n }\n\n // 2. data-testid\n const testId = target.getAttribute('data-testid');\n if (testId) {\n const sel = `[data-testid=\"${escapeSelector(testId)}\"]`;\n if (isUnique(sel)) return sel;\n }\n\n // 3. Tag + stable classes\n const tag = target.tagName.toLowerCase();\n const classes = getStableClasses(target);\n if (classes.length > 0) {\n const classSel = classes.map((c) => `.${escapeSelector(c)}`).join('');\n const sel = `${tag}${classSel}`;\n if (isUnique(sel)) return sel;\n }\n\n // 4. Build path upward with nth-child\n const path: string[] = [];\n let current: Element | null = target;\n\n while (current && !IGNORED_TAGS.has(current.tagName.toLowerCase())) {\n let segment = current.tagName.toLowerCase();\n\n // Try id first\n if (current.id && !/^[0-9]/.test(current.id) && !current.id.includes(':')) {\n segment = `#${escapeSelector(current.id)}`;\n path.unshift(segment);\n const sel = path.join(' > ');\n if (isUnique(sel)) return sel;\n // ID should be unique, break here\n break;\n }\n\n // Try tag + classes\n const cls = getStableClasses(current);\n if (cls.length > 0) {\n segment += cls.map((c) => `.${escapeSelector(c)}`).join('');\n }\n\n // Add nth-child if not unique enough\n const parent = current.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n (s) => s.tagName === current!.tagName,\n );\n if (siblings.length > 1) {\n const index = siblings.indexOf(current) + 1;\n segment += `:nth-child(${index})`;\n }\n }\n\n path.unshift(segment);\n const sel = path.join(' > ');\n if (isUnique(sel)) return sel;\n\n current = parent;\n }\n\n return path.join(' > ') || tag;\n}\n\nexport function getElementLabel(el: Element): string {\n const tag = el.tagName.toLowerCase();\n const classes = getStableClasses(el);\n const classStr = classes.length > 0 ? `.${classes.slice(0, 3).join('.')}` : '';\n return `<${tag}${classStr}>`;\n}\n\nexport function getElementText(el: Element): string {\n const text = el.textContent?.trim() || '';\n return text.slice(0, 200);\n}\n","import { SHADOW_ROOT_ID } from '../constants.js';\nimport { getElementLabel } from './selector.js';\n\nexport class Highlighter {\n private highlight: HTMLElement;\n private label: HTMLElement;\n private currentTarget: Element | null = null;\n\n constructor(private shadowRoot: ShadowRoot) {\n this.highlight = document.createElement('div');\n this.highlight.className = 'aa-highlight';\n this.highlight.style.display = 'none';\n\n this.label = document.createElement('div');\n this.label.className = 'aa-highlight-label';\n this.highlight.appendChild(this.label);\n\n this.shadowRoot.appendChild(this.highlight);\n }\n\n private isOwnElement(el: Element): boolean {\n const root = document.getElementById(SHADOW_ROOT_ID);\n return root !== null && (root === el || root.contains(el));\n }\n\n onMouseMove = (e: MouseEvent): void => {\n const target = e.target as Element;\n\n if (!target || this.isOwnElement(target)) {\n this.hide();\n return;\n }\n\n if (target === this.currentTarget) return;\n this.currentTarget = target;\n\n const rect = target.getBoundingClientRect();\n this.highlight.style.display = 'block';\n this.highlight.style.top = `${rect.top}px`;\n this.highlight.style.left = `${rect.left}px`;\n this.highlight.style.width = `${rect.width}px`;\n this.highlight.style.height = `${rect.height}px`;\n this.label.textContent = getElementLabel(target);\n };\n\n hide(): void {\n this.highlight.style.display = 'none';\n this.currentTarget = null;\n }\n\n getTarget(): Element | null {\n return this.currentTarget;\n }\n\n destroy(): void {\n this.highlight.remove();\n }\n}\n","import type { CreateAnnotationPayload } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\nimport { generateSelector, getElementText } from './selector.js';\n\nexport class AnnotationForm {\n private container: HTMLElement;\n private onSubmitted: () => void;\n\n constructor(\n private shadowRoot: ShadowRoot,\n onSubmitted: () => void,\n ) {\n this.container = document.createElement('div');\n this.container.className = 'aa-form-container';\n this.container.style.display = 'none';\n this.shadowRoot.appendChild(this.container);\n this.onSubmitted = onSubmitted;\n }\n\n show(target: Element): void {\n const selector = generateSelector(target);\n const elementTag = target.tagName.toLowerCase();\n const elementText = getElementText(target);\n const rect = target.getBoundingClientRect();\n\n // Position: below and to the right of the element, or above if not enough space\n let top = rect.bottom + 8;\n let left = rect.left;\n\n if (top + 300 > window.innerHeight) {\n top = rect.top - 308;\n }\n if (left + 340 > window.innerWidth) {\n left = window.innerWidth - 350;\n }\n if (top < 10) top = 10;\n if (left < 10) left = 10;\n\n this.container.style.top = `${top}px`;\n this.container.style.left = `${left}px`;\n this.container.style.display = 'block';\n\n this.container.innerHTML = `\n <div class=\"aa-form-header\">\n <div>\n <div class=\"aa-form-header-title\">New Annotation</div>\n <div class=\"aa-form-header-selector\">${this.escapeHtml(selector)}</div>\n </div>\n <button class=\"aa-form-close\" data-action=\"close\">&times;</button>\n </div>\n <div class=\"aa-form-body\">\n <input class=\"aa-input\" type=\"text\" placeholder=\"Your name\" data-field=\"author\" value=\"\" />\n <textarea class=\"aa-textarea\" placeholder=\"What should be changed?\" data-field=\"text\"></textarea>\n <div class=\"aa-form-actions\">\n <button class=\"aa-btn aa-btn-secondary\" data-action=\"close\">Cancel</button>\n <button class=\"aa-btn aa-btn-primary\" data-action=\"submit\">Submit</button>\n </div>\n </div>\n `;\n\n // Focus textarea\n const textarea = this.container.querySelector('[data-field=\"text\"]') as HTMLTextAreaElement;\n setTimeout(() => textarea?.focus(), 50);\n\n // Event listeners\n this.container.querySelectorAll('[data-action=\"close\"]').forEach((btn) => {\n btn.addEventListener('click', () => this.hide());\n });\n\n const submitBtn = this.container.querySelector('[data-action=\"submit\"]') as HTMLButtonElement;\n submitBtn.addEventListener('click', () => {\n this.submit(selector, elementTag, elementText);\n });\n\n // Submit on Ctrl+Enter\n textarea.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n this.submit(selector, elementTag, elementText);\n }\n });\n }\n\n private async submit(selector: string, elementTag: string, elementText: string): Promise<void> {\n const text = (this.container.querySelector('[data-field=\"text\"]') as HTMLTextAreaElement)?.value?.trim();\n const author = (this.container.querySelector('[data-field=\"author\"]') as HTMLInputElement)?.value?.trim();\n\n if (!text) return;\n\n const submitBtn = this.container.querySelector('[data-action=\"submit\"]') as HTMLButtonElement;\n submitBtn.disabled = true;\n submitBtn.textContent = 'Saving...';\n\n const payload: CreateAnnotationPayload = {\n page: window.location.pathname,\n selector,\n elementTag,\n elementText,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n device: window.innerWidth < 768 ? 'mobile' : window.innerWidth < 1024 ? 'tablet' : 'desktop',\n text,\n author: author || 'Anonymous',\n };\n\n try {\n const res = await fetch(API_ANNOTATIONS, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n\n if (!res.ok) throw new Error('Failed to save');\n\n this.hide();\n this.onSubmitted();\n } catch (err) {\n submitBtn.disabled = false;\n submitBtn.textContent = 'Submit';\n console.error('[astro-annotate] Failed to save annotation:', err);\n }\n }\n\n hide(): void {\n this.container.style.display = 'none';\n this.container.innerHTML = '';\n }\n\n isVisible(): boolean {\n return this.container.style.display !== 'none';\n }\n\n private escapeHtml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n }\n\n destroy(): void {\n this.container.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\n\nexport class PinManager {\n private pins: HTMLElement[] = [];\n private detailPopup: HTMLElement;\n private onChanged: () => void;\n\n constructor(\n private shadowRoot: ShadowRoot,\n onChanged: () => void,\n ) {\n this.detailPopup = document.createElement('div');\n this.detailPopup.className = 'aa-pin-detail';\n this.detailPopup.style.display = 'none';\n this.shadowRoot.appendChild(this.detailPopup);\n this.onChanged = onChanged;\n }\n\n render(annotations: Annotation[]): void {\n this.clearPins();\n\n annotations.forEach((annotation, index) => {\n const el = document.querySelector(annotation.selector);\n if (!el) return;\n\n const pin = document.createElement('div');\n pin.className = `aa-pin${annotation.status === 'resolved' ? ' aa-resolved' : ''}`;\n pin.innerHTML = `<span class=\"aa-pin-number\">${index + 1}</span>`;\n\n // Position pin at top-right corner of element\n const updatePosition = () => {\n const rect = el.getBoundingClientRect();\n pin.style.position = 'fixed';\n pin.style.top = `${Math.max(0, rect.top - 10)}px`;\n pin.style.left = `${Math.max(0, rect.right - 24)}px`;\n };\n updatePosition();\n\n pin.addEventListener('click', (e) => {\n e.stopPropagation();\n this.showDetail(annotation, index, el);\n });\n\n this.shadowRoot.appendChild(pin);\n this.pins.push(pin);\n\n // Update position on scroll/resize\n const observer = new IntersectionObserver(() => updatePosition(), { threshold: 0 });\n observer.observe(el);\n });\n }\n\n private showDetail(annotation: Annotation, index: number, el: Element): void {\n const rect = el.getBoundingClientRect();\n let top = rect.bottom + 8;\n let left = rect.left;\n\n if (top + 250 > window.innerHeight) {\n top = rect.top - 258;\n }\n if (left + 320 > window.innerWidth) {\n left = window.innerWidth - 330;\n }\n if (top < 10) top = 10;\n if (left < 10) left = 10;\n\n this.detailPopup.style.top = `${top}px`;\n this.detailPopup.style.left = `${left}px`;\n this.detailPopup.style.display = 'block';\n\n const date = new Date(annotation.timestamp).toLocaleString();\n\n this.detailPopup.innerHTML = `\n <div class=\"aa-pin-detail-header\">\n <div>\n <div class=\"aa-form-header-title\">#${index + 1} — ${this.escapeHtml(annotation.author)}</div>\n <div class=\"aa-pin-detail-meta\">${date} · ${annotation.device} · ${annotation.status}</div>\n </div>\n <button class=\"aa-form-close\" data-action=\"close-detail\">&times;</button>\n </div>\n <div class=\"aa-pin-detail-body\">\n <div class=\"aa-pin-detail-text\">${this.escapeHtml(annotation.text)}</div>\n <div class=\"aa-pin-detail-info\">\n <div class=\"aa-pin-detail-selector\">${this.escapeHtml(annotation.selector)}</div>\n </div>\n </div>\n <div class=\"aa-pin-detail-actions\">\n ${this.getStatusButtons(annotation)}\n </div>\n `;\n\n this.detailPopup.querySelector('[data-action=\"close-detail\"]')?.addEventListener('click', () => {\n this.hideDetail();\n });\n\n this.detailPopup.querySelectorAll('[data-status]').forEach((btn) => {\n btn.addEventListener('click', () => {\n const status = (btn as HTMLElement).dataset.status!;\n this.updateStatus(annotation.id, status as Annotation['status']);\n });\n });\n }\n\n private getStatusButtons(annotation: Annotation): string {\n if (annotation.status === 'open') {\n return `\n <button class=\"aa-status-btn aa-resolve\" data-status=\"resolved\">Done</button>\n `;\n }\n return `<button class=\"aa-status-btn aa-reopen\" data-status=\"open\">Reopen</button>`;\n }\n\n private async updateStatus(id: string, status: Annotation['status']): Promise<void> {\n try {\n const res = await fetch(`${API_ANNOTATIONS}/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status }),\n });\n\n if (!res.ok) throw new Error('Failed to update');\n\n this.hideDetail();\n this.onChanged();\n } catch (err) {\n console.error('[astro-annotate] Failed to update annotation:', err);\n }\n }\n\n hideDetail(): void {\n this.detailPopup.style.display = 'none';\n }\n\n private clearPins(): void {\n this.pins.forEach((pin) => pin.remove());\n this.pins = [];\n this.hideDetail();\n }\n\n private escapeHtml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n }\n\n destroy(): void {\n this.clearPins();\n this.detailPopup.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { SHADOW_ROOT_ID, API_ANNOTATIONS } from '../constants.js';\nimport { OVERLAY_STYLES } from './styles.js';\nimport { Toolbar } from './toolbar.js';\nimport { Highlighter } from './highlighter.js';\nimport { AnnotationForm } from './form.js';\nimport { PinManager } from './pin.js';\n\nexport class Overlay {\n private host: HTMLElement;\n private shadowRoot: ShadowRoot;\n private toolbar: Toolbar;\n private highlighter: Highlighter;\n private form: AnnotationForm;\n private pinManager: PinManager;\n private active = false;\n private annotations: Annotation[] = [];\n\n constructor() {\n // Create host element\n this.host = document.createElement('div');\n this.host.id = SHADOW_ROOT_ID;\n document.body.appendChild(this.host);\n\n // Attach Shadow DOM\n this.shadowRoot = this.host.attachShadow({ mode: 'open' });\n\n // Inject styles\n const style = document.createElement('style');\n style.textContent = OVERLAY_STYLES;\n this.shadowRoot.appendChild(style);\n\n // Create components\n this.toolbar = new Toolbar(this.shadowRoot, (active) => this.setActive(active));\n this.highlighter = new Highlighter(this.shadowRoot);\n this.form = new AnnotationForm(this.shadowRoot, () => this.onAnnotationCreated());\n this.pinManager = new PinManager(this.shadowRoot, () => this.loadAnnotations());\n\n // Load existing annotations\n this.loadAnnotations();\n\n // Close detail popup when clicking outside\n document.addEventListener('click', (e) => {\n const target = e.target as Element;\n if (!this.host.contains(target)) {\n this.pinManager.hideDetail();\n }\n });\n\n // Update pin positions on scroll\n let scrollTimeout: ReturnType<typeof setTimeout>;\n window.addEventListener('scroll', () => {\n clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => this.renderPins(), 50);\n }, { passive: true });\n\n window.addEventListener('resize', () => {\n this.renderPins();\n }, { passive: true });\n }\n\n private setActive(active: boolean): void {\n this.active = active;\n\n if (active) {\n this.form.hide();\n this.pinManager.hideDetail();\n document.addEventListener('mousemove', this.highlighter.onMouseMove);\n document.addEventListener('click', this.onElementClick);\n } else {\n document.removeEventListener('mousemove', this.highlighter.onMouseMove);\n document.removeEventListener('click', this.onElementClick);\n this.highlighter.hide();\n this.form.hide();\n }\n }\n\n private onElementClick = (e: MouseEvent): void => {\n const target = e.target as Element;\n\n // Ignore clicks on our own overlay\n if (this.host.contains(target) || this.host === target) return;\n\n e.preventDefault();\n e.stopPropagation();\n\n // Hide highlight and show form\n this.highlighter.hide();\n document.removeEventListener('mousemove', this.highlighter.onMouseMove);\n\n this.form.show(target);\n };\n\n private async loadAnnotations(): Promise<void> {\n try {\n const page = window.location.pathname;\n const res = await fetch(`${API_ANNOTATIONS}?page=${encodeURIComponent(page)}`);\n if (!res.ok) return;\n\n const data = await res.json();\n this.annotations = data.annotations || [];\n this.toolbar.updateCount(this.annotations.filter((a) => a.status === 'open').length);\n this.renderPins();\n } catch {\n // API not available yet, will retry\n }\n }\n\n private renderPins(): void {\n this.pinManager.render(this.annotations);\n }\n\n private onAnnotationCreated(): void {\n // Deactivate annotation mode, reload annotations\n this.toolbar.deactivate();\n this.setActive(false);\n this.loadAnnotations();\n }\n\n destroy(): void {\n this.setActive(false);\n this.toolbar.destroy();\n this.highlighter.destroy();\n this.form.destroy();\n this.pinManager.destroy();\n this.host.remove();\n }\n}\n","import { SHADOW_ROOT_ID } from '../constants.js';\nimport { Overlay } from './overlay.js';\n\nlet overlay: Overlay | null = null;\n\nfunction init(): void {\n // Clean up previous instance (View Transitions)\n const existing = document.getElementById(SHADOW_ROOT_ID);\n if (existing) {\n existing.remove();\n overlay = null;\n }\n\n overlay = new Overlay();\n}\n\n// Support View Transitions (SPA navigation)\ndocument.addEventListener('astro:page-load', init);\n\n// Fallback for non-View-Transitions pages\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n // Only init if astro:page-load hasn't fired yet\n if (!document.getElementById(SHADOW_ROOT_ID)) {\n init();\n }\n });\n} else {\n if (!document.getElementById(SHADOW_ROOT_ID)) {\n init();\n }\n}\n"],"mappings":";;;;;;AAAO,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["sel"]}
1
+ {"version":3,"sources":["../src/client/styles.ts","../src/client/toolbar.ts","../src/client/selector.ts","../src/client/highlighter.ts","../src/client/form.ts","../src/client/pin.ts","../src/client/overlay.ts","../src/client/index.ts"],"sourcesContent":["export const OVERLAY_STYLES = `\n :host {\n all: initial;\n position: fixed;\n z-index: 2147483647;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #1a1a2e;\n }\n\n *, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n /* Toolbar */\n .aa-toolbar {\n position: fixed;\n bottom: 20px;\n right: 20px;\n display: flex;\n align-items: center;\n gap: 8px;\n background: #1a1a2e;\n color: #fff;\n padding: 8px 16px;\n border-radius: 50px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n border: 1.5px solid rgba(255, 255, 255, 0.2);\n cursor: pointer;\n user-select: none;\n transition: all 0.2s ease;\n }\n\n .aa-toolbar:hover {\n background: #16213e;\n transform: translateY(-1px);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);\n border-color: rgba(255, 255, 255, 0.35);\n }\n\n .aa-toolbar.aa-active {\n background: #e94560;\n }\n\n .aa-toolbar.aa-active:hover {\n background: #c73e54;\n }\n\n .aa-toolbar-icon {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n }\n\n .aa-toolbar-label {\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n }\n\n .aa-badge {\n background: #e94560;\n color: #fff;\n font-size: 11px;\n font-weight: 700;\n min-width: 20px;\n height: 20px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 6px;\n }\n\n .aa-toolbar.aa-active .aa-badge {\n background: rgba(255, 255, 255, 0.3);\n }\n\n /* Element Highlight */\n .aa-highlight {\n position: fixed;\n pointer-events: none;\n border: 2px solid #e94560;\n background: rgba(233, 69, 96, 0.08);\n border-radius: 3px;\n z-index: 2147483646;\n transition: all 0.1s ease;\n }\n\n .aa-highlight-label {\n position: absolute;\n top: -26px;\n left: -2px;\n background: #e94560;\n color: #fff;\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 3px 3px 0 0;\n white-space: nowrap;\n max-width: 300px;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* Annotation Form */\n .aa-form-container {\n position: fixed;\n z-index: 2147483647;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n width: 340px;\n overflow: hidden;\n }\n\n .aa-form-header {\n background: #1a1a2e;\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .aa-form-header-title {\n font-size: 13px;\n font-weight: 600;\n }\n\n .aa-form-header-selector {\n font-size: 11px;\n opacity: 0.7;\n font-family: 'SF Mono', Monaco, monospace;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .aa-form-close {\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n font-size: 18px;\n line-height: 1;\n opacity: 0.7;\n padding: 0 0 0 8px;\n }\n\n .aa-form-close:hover {\n opacity: 1;\n }\n\n .aa-form-body {\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .aa-input, .aa-textarea {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n transition: border-color 0.15s;\n }\n\n .aa-input:focus, .aa-textarea:focus {\n border-color: #e94560;\n }\n\n .aa-textarea {\n resize: vertical;\n min-height: 80px;\n }\n\n .aa-form-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n }\n\n .aa-btn {\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s;\n }\n\n .aa-btn-primary {\n background: #e94560;\n color: #fff;\n }\n\n .aa-btn-primary:hover {\n background: #c73e54;\n }\n\n .aa-btn-primary:disabled {\n background: #ccc;\n cursor: not-allowed;\n }\n\n .aa-btn-secondary {\n background: #f0f0f0;\n color: #333;\n }\n\n .aa-btn-secondary:hover {\n background: #e0e0e0;\n }\n\n /* Pins */\n .aa-pin {\n position: absolute;\n width: 28px;\n height: 28px;\n border-radius: 50% 50% 50% 0;\n background: #e94560;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n transform: rotate(-45deg);\n box-shadow: 0 2px 8px rgba(233, 69, 96, 0.4);\n transition: transform 0.15s, box-shadow 0.15s;\n z-index: 2147483645;\n }\n\n .aa-pin:hover {\n transform: rotate(-45deg) scale(1.15);\n box-shadow: 0 4px 12px rgba(233, 69, 96, 0.5);\n }\n\n .aa-pin.aa-resolved {\n background: #2ecc71;\n box-shadow: 0 2px 8px rgba(46, 204, 113, 0.4);\n }\n\n .aa-pin-number {\n transform: rotate(45deg);\n }\n\n /* Pin Detail Popup */\n .aa-pin-detail {\n position: fixed;\n z-index: 2147483647;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n width: 320px;\n overflow: hidden;\n }\n\n .aa-pin-detail-header {\n background: #1a1a2e;\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .aa-pin-detail-meta {\n font-size: 11px;\n opacity: 0.7;\n }\n\n .aa-pin-detail-body {\n padding: 16px;\n }\n\n .aa-pin-detail-text {\n font-size: 14px;\n margin-bottom: 12px;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .aa-pin-detail-info {\n font-size: 12px;\n color: #888;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .aa-pin-detail-selector {\n font-family: 'SF Mono', Monaco, monospace;\n font-size: 11px;\n color: #666;\n background: #f5f5f5;\n padding: 4px 8px;\n border-radius: 4px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .aa-pin-detail-actions {\n padding: 0 16px 16px;\n display: flex;\n gap: 8px;\n }\n\n .aa-status-btn {\n padding: 4px 12px;\n border-radius: 4px;\n font-size: 12px;\n cursor: pointer;\n border: 1px solid #e0e0e0;\n background: #fff;\n transition: all 0.15s;\n }\n\n .aa-status-btn:hover {\n background: #f5f5f5;\n }\n\n .aa-status-btn.aa-resolve {\n color: #2ecc71;\n border-color: #2ecc71;\n }\n\n .aa-status-btn.aa-resolve:hover {\n background: #2ecc71;\n color: #fff;\n }\n\n .aa-status-btn.aa-reopen {\n color: #e94560;\n border-color: #e94560;\n }\n\n .aa-status-btn.aa-reopen:hover {\n background: #e94560;\n color: #fff;\n }\n\n /* Dark mode */\n @media (prefers-color-scheme: dark) {\n .aa-form-container, .aa-pin-detail {\n background: #2d2d3f;\n color: #e0e0e0;\n }\n\n .aa-input, .aa-textarea {\n background: #1a1a2e;\n border-color: #404060;\n color: #e0e0e0;\n }\n\n .aa-btn-secondary {\n background: #404060;\n color: #e0e0e0;\n }\n\n .aa-btn-secondary:hover {\n background: #505070;\n }\n\n .aa-pin-detail-selector {\n background: #1a1a2e;\n color: #aaa;\n }\n\n .aa-status-btn {\n background: #2d2d3f;\n border-color: #404060;\n }\n\n .aa-status-btn:hover {\n background: #404060;\n }\n }\n`;\n","const ANNOTATE_ICON = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"aa-toolbar-icon\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"9\"/><line x1=\"12\" y1=\"6\" x2=\"12\" y2=\"12\"/></svg>`;\n\nexport class Toolbar {\n private el: HTMLElement;\n private label: HTMLElement;\n private badge: HTMLElement;\n private active = false;\n private onToggle: (active: boolean) => void;\n\n constructor(shadowRoot: ShadowRoot, onToggle: (active: boolean) => void) {\n this.onToggle = onToggle;\n\n this.el = document.createElement('div');\n this.el.className = 'aa-toolbar';\n\n this.el.innerHTML = ANNOTATE_ICON;\n\n this.label = document.createElement('span');\n this.label.className = 'aa-toolbar-label';\n this.label.textContent = 'Annotate';\n this.el.appendChild(this.label);\n\n this.badge = document.createElement('span');\n this.badge.className = 'aa-badge';\n this.badge.textContent = '0';\n this.badge.style.display = 'none';\n this.el.appendChild(this.badge);\n\n this.el.addEventListener('click', () => {\n this.toggle();\n });\n\n shadowRoot.appendChild(this.el);\n }\n\n toggle(): void {\n this.active = !this.active;\n this.el.classList.toggle('aa-active', this.active);\n this.label.textContent = this.active ? 'Stop' : 'Annotate';\n this.onToggle(this.active);\n }\n\n deactivate(): void {\n if (this.active) {\n this.active = false;\n this.el.classList.remove('aa-active');\n this.label.textContent = 'Annotate';\n }\n }\n\n updateCount(count: number): void {\n this.badge.textContent = String(count);\n this.badge.style.display = count > 0 ? 'flex' : 'none';\n }\n\n destroy(): void {\n this.el.remove();\n }\n}\n","const ASTRO_CLASS_RE = /^astro-[a-zA-Z0-9]+$/;\nconst IGNORED_TAGS = new Set(['html', 'body', 'head']);\n\nfunction isAstroClass(cls: string): boolean {\n return ASTRO_CLASS_RE.test(cls);\n}\n\nfunction getStableClasses(el: Element): string[] {\n return Array.from(el.classList).filter((cls) => !isAstroClass(cls));\n}\n\nfunction isUnique(selector: string): boolean {\n try {\n return document.querySelectorAll(selector).length === 1;\n } catch {\n return false;\n }\n}\n\nfunction escapeSelector(value: string): string {\n return CSS.escape(value);\n}\n\nexport function generateSelector(target: Element): string {\n if (IGNORED_TAGS.has(target.tagName.toLowerCase())) {\n return target.tagName.toLowerCase();\n }\n\n // 1. ID (if stable — not auto-generated)\n if (target.id && !/^[0-9]/.test(target.id) && !target.id.includes(':')) {\n const sel = `#${escapeSelector(target.id)}`;\n if (isUnique(sel)) return sel;\n }\n\n // 2. data-testid\n const testId = target.getAttribute('data-testid');\n if (testId) {\n const sel = `[data-testid=\"${escapeSelector(testId)}\"]`;\n if (isUnique(sel)) return sel;\n }\n\n // 3. Tag + stable classes\n const tag = target.tagName.toLowerCase();\n const classes = getStableClasses(target);\n if (classes.length > 0) {\n const classSel = classes.map((c) => `.${escapeSelector(c)}`).join('');\n const sel = `${tag}${classSel}`;\n if (isUnique(sel)) return sel;\n }\n\n // 4. Build path upward with nth-child\n const path: string[] = [];\n let current: Element | null = target;\n\n while (current && !IGNORED_TAGS.has(current.tagName.toLowerCase())) {\n let segment = current.tagName.toLowerCase();\n\n // Try id first\n if (current.id && !/^[0-9]/.test(current.id) && !current.id.includes(':')) {\n segment = `#${escapeSelector(current.id)}`;\n path.unshift(segment);\n const sel = path.join(' > ');\n if (isUnique(sel)) return sel;\n // ID should be unique, break here\n break;\n }\n\n // Try tag + classes\n const cls = getStableClasses(current);\n if (cls.length > 0) {\n segment += cls.map((c) => `.${escapeSelector(c)}`).join('');\n }\n\n // Add nth-child if not unique enough\n const parent: Element | null = current.parentElement;\n if (parent) {\n const currentTag = current.tagName;\n const siblings = Array.from(parent.children).filter(\n (s: Element) => s.tagName === currentTag,\n );\n if (siblings.length > 1) {\n const index = siblings.indexOf(current) + 1;\n segment += `:nth-child(${index})`;\n }\n }\n\n path.unshift(segment);\n const sel = path.join(' > ');\n if (isUnique(sel)) return sel;\n\n current = parent;\n }\n\n return path.join(' > ') || tag;\n}\n\nexport function getElementLabel(el: Element): string {\n const tag = el.tagName.toLowerCase();\n const classes = getStableClasses(el);\n const classStr = classes.length > 0 ? `.${classes.slice(0, 3).join('.')}` : '';\n return `<${tag}${classStr}>`;\n}\n\nexport function getElementText(el: Element): string {\n const text = el.textContent?.trim() || '';\n return text.slice(0, 200);\n}\n","import { SHADOW_ROOT_ID } from '../constants.js';\nimport { getElementLabel } from './selector.js';\n\nexport class Highlighter {\n private highlight: HTMLElement;\n private label: HTMLElement;\n private currentTarget: Element | null = null;\n\n constructor(private shadowRoot: ShadowRoot) {\n this.highlight = document.createElement('div');\n this.highlight.className = 'aa-highlight';\n this.highlight.style.display = 'none';\n\n this.label = document.createElement('div');\n this.label.className = 'aa-highlight-label';\n this.highlight.appendChild(this.label);\n\n this.shadowRoot.appendChild(this.highlight);\n }\n\n private isOwnElement(el: Element): boolean {\n const root = document.getElementById(SHADOW_ROOT_ID);\n return root !== null && (root === el || root.contains(el));\n }\n\n onMouseMove = (e: MouseEvent): void => {\n const target = e.target as Element;\n\n if (!target || this.isOwnElement(target)) {\n this.hide();\n return;\n }\n\n if (target === this.currentTarget) return;\n this.currentTarget = target;\n\n const rect = target.getBoundingClientRect();\n this.highlight.style.display = 'block';\n this.highlight.style.top = `${rect.top}px`;\n this.highlight.style.left = `${rect.left}px`;\n this.highlight.style.width = `${rect.width}px`;\n this.highlight.style.height = `${rect.height}px`;\n this.label.textContent = getElementLabel(target);\n };\n\n hide(): void {\n this.highlight.style.display = 'none';\n this.currentTarget = null;\n }\n\n getTarget(): Element | null {\n return this.currentTarget;\n }\n\n destroy(): void {\n this.highlight.remove();\n }\n}\n","import type { CreateAnnotationPayload } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\nimport { generateSelector, getElementText } from './selector.js';\n\nexport class AnnotationForm {\n private container: HTMLElement;\n private onSubmitted: () => void;\n private onClosed: () => void;\n\n constructor(\n private shadowRoot: ShadowRoot,\n onSubmitted: () => void,\n onClosed: () => void,\n private devMode: boolean,\n ) {\n this.container = document.createElement('div');\n this.container.className = 'aa-form-container';\n this.container.style.display = 'none';\n this.shadowRoot.appendChild(this.container);\n this.onSubmitted = onSubmitted;\n this.onClosed = onClosed;\n }\n\n show(target: Element): void {\n const selector = generateSelector(target);\n const elementTag = target.tagName.toLowerCase();\n const elementText = getElementText(target);\n const rect = target.getBoundingClientRect();\n\n // Position: below and to the right of the element, or above if not enough space\n let top = rect.bottom + 8;\n let left = rect.left;\n\n if (top + 300 > window.innerHeight) {\n top = rect.top - 308;\n }\n if (left + 340 > window.innerWidth) {\n left = window.innerWidth - 350;\n }\n if (top < 10) top = 10;\n if (left < 10) left = 10;\n\n this.container.style.top = `${top}px`;\n this.container.style.left = `${left}px`;\n this.container.style.display = 'block';\n\n this.container.innerHTML = `\n <div class=\"aa-form-header\">\n <div>\n <div class=\"aa-form-header-title\">New Annotation</div>\n <div class=\"aa-form-header-selector\">${this.escapeHtml(selector)}</div>\n </div>\n <button class=\"aa-form-close\" data-action=\"close\">&times;</button>\n </div>\n <div class=\"aa-form-body\">\n ${this.devMode ? '' : '<input class=\"aa-input\" type=\"text\" placeholder=\"Your name\" data-field=\"author\" value=\"\" />'}\n <textarea class=\"aa-textarea\" placeholder=\"What should be changed?\" data-field=\"text\"></textarea>\n <div class=\"aa-form-actions\">\n <button class=\"aa-btn aa-btn-secondary\" data-action=\"close\">Cancel</button>\n <button class=\"aa-btn aa-btn-primary\" data-action=\"submit\">Submit</button>\n </div>\n </div>\n `;\n\n // Focus textarea\n const textarea = this.container.querySelector('[data-field=\"text\"]') as HTMLTextAreaElement;\n setTimeout(() => textarea?.focus(), 50);\n\n // Event listeners\n this.container.querySelectorAll('[data-action=\"close\"]').forEach((btn) => {\n btn.addEventListener('click', () => this.hide());\n });\n\n const submitBtn = this.container.querySelector('[data-action=\"submit\"]') as HTMLButtonElement;\n submitBtn.addEventListener('click', () => {\n this.submit(selector, elementTag, elementText);\n });\n\n // Submit on Ctrl+Enter\n textarea.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n this.submit(selector, elementTag, elementText);\n }\n });\n }\n\n private async submit(selector: string, elementTag: string, elementText: string): Promise<void> {\n const text = (this.container.querySelector('[data-field=\"text\"]') as HTMLTextAreaElement)?.value?.trim();\n const author = this.devMode\n ? 'Developer'\n : (this.container.querySelector('[data-field=\"author\"]') as HTMLInputElement)?.value?.trim();\n\n if (!text) return;\n\n const submitBtn = this.container.querySelector('[data-action=\"submit\"]') as HTMLButtonElement;\n submitBtn.disabled = true;\n submitBtn.textContent = 'Saving...';\n\n const payload: CreateAnnotationPayload = {\n page: window.location.pathname,\n selector,\n elementTag,\n elementText,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n device: window.innerWidth < 768 ? 'mobile' : window.innerWidth < 1024 ? 'tablet' : 'desktop',\n text,\n author: author || 'Anonymous',\n };\n\n try {\n const res = await fetch(API_ANNOTATIONS, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n\n if (!res.ok) throw new Error('Failed to save');\n\n this.hide();\n this.onSubmitted();\n } catch (err) {\n submitBtn.disabled = false;\n submitBtn.textContent = 'Submit';\n console.error('[astro-annotate] Failed to save annotation:', err);\n }\n }\n\n hide(): void {\n if (this.container.style.display === 'none') return;\n this.container.style.display = 'none';\n this.container.innerHTML = '';\n this.onClosed();\n }\n\n isVisible(): boolean {\n return this.container.style.display !== 'none';\n }\n\n private escapeHtml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n }\n\n destroy(): void {\n this.container.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\n\nexport class PinManager {\n private pins: HTMLElement[] = [];\n private detailPopup: HTMLElement;\n private onChanged: () => void;\n\n constructor(\n private shadowRoot: ShadowRoot,\n onChanged: () => void,\n ) {\n this.detailPopup = document.createElement('div');\n this.detailPopup.className = 'aa-pin-detail';\n this.detailPopup.style.display = 'none';\n this.shadowRoot.appendChild(this.detailPopup);\n this.onChanged = onChanged;\n }\n\n render(annotations: Annotation[]): void {\n this.clearPins();\n\n annotations.forEach((annotation, index) => {\n const el = document.querySelector(annotation.selector);\n if (!el) return;\n\n const pin = document.createElement('div');\n pin.className = `aa-pin${annotation.status === 'resolved' ? ' aa-resolved' : ''}`;\n pin.innerHTML = `<span class=\"aa-pin-number\">${index + 1}</span>`;\n\n // Position pin at top-right corner of element\n const updatePosition = () => {\n const rect = el.getBoundingClientRect();\n pin.style.position = 'fixed';\n pin.style.top = `${Math.max(0, rect.top - 10)}px`;\n pin.style.left = `${Math.max(0, rect.right - 24)}px`;\n };\n updatePosition();\n\n pin.addEventListener('click', (e) => {\n e.stopPropagation();\n this.showDetail(annotation, index, el);\n });\n\n this.shadowRoot.appendChild(pin);\n this.pins.push(pin);\n\n // Update position on scroll/resize\n const observer = new IntersectionObserver(() => updatePosition(), { threshold: 0 });\n observer.observe(el);\n });\n }\n\n private showDetail(annotation: Annotation, index: number, el: Element): void {\n const rect = el.getBoundingClientRect();\n let top = rect.bottom + 8;\n let left = rect.left;\n\n if (top + 250 > window.innerHeight) {\n top = rect.top - 258;\n }\n if (left + 320 > window.innerWidth) {\n left = window.innerWidth - 330;\n }\n if (top < 10) top = 10;\n if (left < 10) left = 10;\n\n this.detailPopup.style.top = `${top}px`;\n this.detailPopup.style.left = `${left}px`;\n this.detailPopup.style.display = 'block';\n\n const date = new Date(annotation.timestamp).toLocaleString();\n\n this.detailPopup.innerHTML = `\n <div class=\"aa-pin-detail-header\">\n <div>\n <div class=\"aa-form-header-title\">#${index + 1} — ${this.escapeHtml(annotation.author)}</div>\n <div class=\"aa-pin-detail-meta\">${date} · ${annotation.device} · ${annotation.status}</div>\n </div>\n <button class=\"aa-form-close\" data-action=\"close-detail\">&times;</button>\n </div>\n <div class=\"aa-pin-detail-body\">\n <div class=\"aa-pin-detail-text\">${this.escapeHtml(annotation.text)}</div>\n <div class=\"aa-pin-detail-info\">\n <div class=\"aa-pin-detail-selector\">${this.escapeHtml(annotation.selector)}</div>\n </div>\n </div>\n <div class=\"aa-pin-detail-actions\">\n ${this.getStatusButtons(annotation)}\n </div>\n `;\n\n this.detailPopup.querySelector('[data-action=\"close-detail\"]')?.addEventListener('click', () => {\n this.hideDetail();\n });\n\n this.detailPopup.querySelectorAll('[data-status]').forEach((btn) => {\n btn.addEventListener('click', () => {\n const status = (btn as HTMLElement).dataset.status!;\n this.updateStatus(annotation.id, status as Annotation['status']);\n });\n });\n }\n\n private getStatusButtons(annotation: Annotation): string {\n if (annotation.status === 'open') {\n return `\n <button class=\"aa-status-btn aa-resolve\" data-status=\"resolved\">Done</button>\n `;\n }\n return `<button class=\"aa-status-btn aa-reopen\" data-status=\"open\">Reopen</button>`;\n }\n\n private async updateStatus(id: string, status: Annotation['status']): Promise<void> {\n try {\n const res = await fetch(`${API_ANNOTATIONS}/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status }),\n });\n\n if (!res.ok) throw new Error('Failed to update');\n\n this.hideDetail();\n this.onChanged();\n } catch (err) {\n console.error('[astro-annotate] Failed to update annotation:', err);\n }\n }\n\n hideDetail(): void {\n this.detailPopup.style.display = 'none';\n }\n\n private clearPins(): void {\n this.pins.forEach((pin) => pin.remove());\n this.pins = [];\n this.hideDetail();\n }\n\n private escapeHtml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n }\n\n destroy(): void {\n this.clearPins();\n this.detailPopup.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { SHADOW_ROOT_ID, API_ANNOTATIONS } from '../constants.js';\nimport { OVERLAY_STYLES } from './styles.js';\nimport { Toolbar } from './toolbar.js';\nimport { Highlighter } from './highlighter.js';\nimport { AnnotationForm } from './form.js';\nimport { PinManager } from './pin.js';\n\nexport class Overlay {\n private host: HTMLElement;\n private shadowRoot: ShadowRoot;\n private toolbar: Toolbar;\n private highlighter: Highlighter;\n private form: AnnotationForm;\n private pinManager: PinManager;\n private active = false;\n private devMode = !!(window as any).__ASTRO_ANNOTATE_DEV__;\n private annotations: Annotation[] = [];\n\n constructor() {\n // Create host element\n this.host = document.createElement('div');\n this.host.id = SHADOW_ROOT_ID;\n document.body.appendChild(this.host);\n\n // Attach Shadow DOM\n this.shadowRoot = this.host.attachShadow({ mode: 'open' });\n\n // Inject styles\n const style = document.createElement('style');\n style.textContent = OVERLAY_STYLES;\n this.shadowRoot.appendChild(style);\n\n // Create components\n this.toolbar = new Toolbar(this.shadowRoot, (active) => this.setActive(active));\n this.highlighter = new Highlighter(this.shadowRoot);\n this.form = new AnnotationForm(\n this.shadowRoot,\n () => this.onAnnotationCreated(),\n () => this.onFormClosed(),\n this.devMode,\n );\n this.pinManager = new PinManager(this.shadowRoot, () => this.loadAnnotations());\n\n // Load existing annotations\n this.loadAnnotations();\n\n // Close detail popup when clicking outside\n document.addEventListener('click', (e) => {\n const target = e.target as Element;\n if (!this.host.contains(target)) {\n this.pinManager.hideDetail();\n }\n });\n\n // Update pin positions on scroll\n let scrollTimeout: ReturnType<typeof setTimeout>;\n window.addEventListener('scroll', () => {\n clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => this.renderPins(), 50);\n }, { passive: true });\n\n window.addEventListener('resize', () => {\n this.renderPins();\n }, { passive: true });\n\n // Keyboard shortcuts\n document.addEventListener('keydown', this.onKeyDown);\n }\n\n private setActive(active: boolean): void {\n this.active = active;\n\n if (active) {\n this.form.hide();\n this.pinManager.hideDetail();\n document.addEventListener('mousemove', this.highlighter.onMouseMove);\n document.addEventListener('click', this.onElementClick);\n } else {\n document.removeEventListener('mousemove', this.highlighter.onMouseMove);\n document.removeEventListener('click', this.onElementClick);\n this.highlighter.hide();\n this.form.hide();\n }\n }\n\n private onElementClick = (e: MouseEvent): void => {\n const target = e.target as Element;\n\n // Ignore clicks on our own overlay\n if (this.host.contains(target) || this.host === target) return;\n\n // Don't switch targets while form is open\n if (this.form.isVisible()) return;\n\n e.preventDefault();\n e.stopPropagation();\n\n // Hide highlight and show form\n this.highlighter.hide();\n document.removeEventListener('mousemove', this.highlighter.onMouseMove);\n\n this.form.show(target);\n };\n\n private onKeyDown = (e: KeyboardEvent): void => {\n // Don't intercept when user is typing in an external input\n const active = document.activeElement;\n const isExternalInput = active &&\n (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' ||\n (active as HTMLElement).isContentEditable) &&\n active !== this.host && !this.host.contains(active);\n\n // Alt+C: toggle annotation mode (Figma-inspired, e.code for layout-independence)\n if (e.altKey && e.code === 'KeyC' && !isExternalInput) {\n e.preventDefault();\n this.toolbar.toggle();\n return;\n }\n\n // Escape: close form or exit annotation mode\n if (e.key === 'Escape') {\n if (this.form.isVisible()) {\n this.form.hide();\n return;\n }\n if (this.active) {\n this.toolbar.deactivate();\n this.setActive(false);\n return;\n }\n }\n };\n\n private async loadAnnotations(): Promise<void> {\n try {\n const page = window.location.pathname;\n const res = await fetch(`${API_ANNOTATIONS}?page=${encodeURIComponent(page)}`);\n if (!res.ok) return;\n\n const data = await res.json();\n this.annotations = data.annotations || [];\n this.toolbar.updateCount(this.annotations.filter((a) => a.status === 'open').length);\n this.renderPins();\n } catch {\n // API not available yet, will retry\n }\n }\n\n private renderPins(): void {\n this.pinManager.render(this.annotations);\n }\n\n private onAnnotationCreated(): void {\n this.loadAnnotations();\n }\n\n private onFormClosed(): void {\n // Re-enable highlighting if still in annotation mode\n if (this.active) {\n document.addEventListener('mousemove', this.highlighter.onMouseMove);\n }\n }\n\n destroy(): void {\n document.removeEventListener('keydown', this.onKeyDown);\n this.setActive(false);\n this.toolbar.destroy();\n this.highlighter.destroy();\n this.form.destroy();\n this.pinManager.destroy();\n this.host.remove();\n }\n}\n","import { SHADOW_ROOT_ID } from '../constants.js';\nimport { Overlay } from './overlay.js';\n\nlet overlay: Overlay | null = null;\n\nfunction init(): void {\n // Clean up previous instance (View Transitions)\n if (overlay) {\n overlay.destroy();\n overlay = null;\n }\n\n overlay = new Overlay();\n}\n\n// Support View Transitions (SPA navigation)\ndocument.addEventListener('astro:page-load', init);\n\n// Fallback for non-View-Transitions pages\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n // Only init if astro:page-load hasn't fired yet\n if (!document.getElementById(SHADOW_ROOT_ID)) {\n init();\n }\n });\n} else {\n if (!document.getElementById(SHADOW_ROOT_ID)) {\n init();\n }\n}\n"],"mappings":";;;;;;AAAO,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["sel"]}
package/dist/index.js CHANGED
@@ -200,6 +200,7 @@ function createIntegration(userConfig = {}) {
200
200
  }
201
201
  resolvedConfig = {
202
202
  enabled,
203
+ mode: isDev ? "dev" : "deployed",
203
204
  storage: userConfig.storage ?? "local",
204
205
  annotationsPath: userConfig.annotationsPath ?? DEFAULT_ANNOTATIONS_PATH
205
206
  };
@@ -209,7 +210,7 @@ function createIntegration(userConfig = {}) {
209
210
  }
210
211
  });
211
212
  const clientPath = resolve2(dirname(fileURLToPath(import.meta.url)), "client.js");
212
- injectScript("page", `import "${clientPath}";`);
213
+ injectScript("page", `window.__ASTRO_ANNOTATE_DEV__=${isDev};import("${clientPath}");`);
213
214
  logger.info("Overlay enabled");
214
215
  },
215
216
  "astro:server:setup"({ server, logger }) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/integration/index.ts","../src/integration/vite-plugin.ts","../src/storage/local.ts","../src/server/api-handlers.ts","../src/server/dev-middleware.ts","../src/index.ts"],"sourcesContent":["import type { AstroIntegration } from 'astro';\nimport { fileURLToPath } from 'node:url';\nimport { resolve, dirname } from 'node:path';\nimport type { AstroAnnotateConfig, ResolvedConfig } from '../types.js';\nimport { DEFAULT_ANNOTATIONS_PATH } from '../constants.js';\nimport { astroAnnotateVitePlugin } from './vite-plugin.js';\nimport { LocalStorage } from '../storage/local.js';\nimport { registerDevMiddleware } from '../server/dev-middleware.js';\n\nexport function createIntegration(userConfig: AstroAnnotateConfig = {}): AstroIntegration {\n let resolvedConfig: ResolvedConfig;\n let projectRoot: string;\n\n return {\n name: 'astro-annotate',\n hooks: {\n 'astro:config:setup'({ config, command, updateConfig, injectScript, logger }) {\n projectRoot = fileURLToPath(config.root);\n\n const isDev = command === 'dev' || command === 'preview';\n const enabled = userConfig.enabled ?? isDev;\n\n if (!enabled) {\n logger.info('Disabled (set enabled: true to force)');\n return;\n }\n\n resolvedConfig = {\n enabled,\n storage: userConfig.storage ?? 'local',\n annotationsPath: userConfig.annotationsPath ?? DEFAULT_ANNOTATIONS_PATH,\n };\n\n // Add Vite plugin for virtual module\n updateConfig({\n vite: {\n plugins: [astroAnnotateVitePlugin(resolvedConfig)],\n },\n });\n\n // Inject client overlay script\n // At runtime, import.meta.url points to dist/index.js, client bundle is dist/client.js\n const clientPath = resolve(dirname(fileURLToPath(import.meta.url)), 'client.js');\n injectScript('page', `import \"${clientPath}\";`);\n\n logger.info('Overlay enabled');\n },\n\n 'astro:server:setup'({ server, logger }) {\n if (!resolvedConfig) return;\n\n const storage = new LocalStorage(resolvedConfig.annotationsPath, projectRoot);\n registerDevMiddleware(server, storage);\n logger.info('Dev middleware registered');\n },\n },\n };\n}\n","import type { Plugin } from 'vite';\nimport type { ResolvedConfig } from '../types.js';\nimport { VIRTUAL_MODULE_ID, RESOLVED_VIRTUAL_MODULE_ID } from '../constants.js';\n\nexport function astroAnnotateVitePlugin(config: ResolvedConfig): Plugin {\n return {\n name: 'astro-annotate-config',\n resolveId(id) {\n if (id === VIRTUAL_MODULE_ID) {\n return RESOLVED_VIRTUAL_MODULE_ID;\n }\n },\n load(id) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return `export default ${JSON.stringify(config)};`;\n }\n },\n };\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { randomBytes } from 'node:crypto';\nimport type { Annotation, AnnotationsFile, CreateAnnotationPayload, UpdateAnnotationPayload } from '../types.js';\nimport type { AnnotationStorage } from './interface.js';\nimport { ANNOTATIONS_VERSION } from '../constants.js';\n\nfunction generateId(): string {\n return 'a_' + randomBytes(4).toString('hex');\n}\n\nfunction detectDevice(width: number): 'mobile' | 'tablet' | 'desktop' {\n if (width < 768) return 'mobile';\n if (width < 1024) return 'tablet';\n return 'desktop';\n}\n\nexport class LocalStorage implements AnnotationStorage {\n private filePath: string;\n\n constructor(annotationsPath: string, projectRoot: string) {\n this.filePath = resolve(projectRoot, annotationsPath);\n }\n\n private read(): AnnotationsFile {\n if (!existsSync(this.filePath)) {\n return { version: ANNOTATIONS_VERSION, annotations: [] };\n }\n const raw = readFileSync(this.filePath, 'utf-8');\n return JSON.parse(raw) as AnnotationsFile;\n }\n\n private write(data: AnnotationsFile): void {\n writeFileSync(this.filePath, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n }\n\n async list(page?: string): Promise<Annotation[]> {\n const data = this.read();\n if (page) {\n return data.annotations.filter((a) => a.page === page);\n }\n return data.annotations;\n }\n\n async create(payload: CreateAnnotationPayload): Promise<Annotation> {\n const data = this.read();\n const annotation: Annotation = {\n id: generateId(),\n timestamp: new Date().toISOString(),\n page: payload.page,\n selector: payload.selector,\n elementTag: payload.elementTag,\n elementText: payload.elementText.slice(0, 200),\n viewport: payload.viewport,\n device: payload.device || detectDevice(payload.viewport.width),\n text: payload.text,\n author: payload.author || 'Anonymous',\n status: 'open',\n };\n data.annotations.push(annotation);\n this.write(data);\n return annotation;\n }\n\n async update(id: string, payload: UpdateAnnotationPayload): Promise<Annotation | null> {\n const data = this.read();\n const index = data.annotations.findIndex((a) => a.id === id);\n if (index === -1) return null;\n\n if (payload.status) data.annotations[index].status = payload.status;\n if (payload.text !== undefined) data.annotations[index].text = payload.text;\n\n this.write(data);\n return data.annotations[index];\n }\n\n async delete(id: string): Promise<boolean> {\n const data = this.read();\n const before = data.annotations.length;\n data.annotations = data.annotations.filter((a) => a.id !== id);\n if (data.annotations.length === before) return false;\n this.write(data);\n return true;\n }\n}\n","import type { AnnotationStorage } from '../storage/interface.js';\nimport type { CreateAnnotationPayload, UpdateAnnotationPayload } from '../types.js';\n\nfunction json(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\nexport async function handleListAnnotations(\n storage: AnnotationStorage,\n url: URL,\n): Promise<Response> {\n const page = url.searchParams.get('page') || undefined;\n const annotations = await storage.list(page);\n return json({ annotations });\n}\n\nexport async function handleCreateAnnotation(\n storage: AnnotationStorage,\n body: unknown,\n): Promise<Response> {\n const payload = body as CreateAnnotationPayload;\n\n if (!payload.selector || !payload.text || !payload.page) {\n return json({ error: 'Missing required fields: selector, text, page' }, 400);\n }\n\n const annotation = await storage.create(payload);\n return json({ annotation }, 201);\n}\n\nexport async function handleUpdateAnnotation(\n storage: AnnotationStorage,\n id: string,\n body: unknown,\n): Promise<Response> {\n const payload = body as UpdateAnnotationPayload;\n const annotation = await storage.update(id, payload);\n\n if (!annotation) {\n return json({ error: 'Annotation not found' }, 404);\n }\n\n return json({ annotation });\n}\n\nexport async function handleDeleteAnnotation(\n storage: AnnotationStorage,\n id: string,\n): Promise<Response> {\n const deleted = await storage.delete(id);\n\n if (!deleted) {\n return json({ error: 'Annotation not found' }, 404);\n }\n\n return json({ success: true });\n}\n","import type { ViteDevServer, Connect } from 'vite';\nimport type { AnnotationStorage } from '../storage/interface.js';\nimport { API_ANNOTATIONS } from '../constants.js';\nimport {\n handleListAnnotations,\n handleCreateAnnotation,\n handleUpdateAnnotation,\n handleDeleteAnnotation,\n} from './api-handlers.js';\n\nfunction collectBody(req: Connect.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n req.on('error', reject);\n });\n}\n\nexport function registerDevMiddleware(server: ViteDevServer, storage: AnnotationStorage): void {\n server.middlewares.use(async (req, res, next) => {\n const url = new URL(req.url || '/', `http://${req.headers.host}`);\n\n if (!url.pathname.startsWith(API_ANNOTATIONS)) {\n return next();\n }\n\n try {\n let response: Response;\n\n // POST /api/astro-annotate/annotations\n if (url.pathname === API_ANNOTATIONS && req.method === 'POST') {\n const raw = await collectBody(req);\n const body = JSON.parse(raw);\n response = await handleCreateAnnotation(storage, body);\n }\n // GET /api/astro-annotate/annotations\n else if (url.pathname === API_ANNOTATIONS && req.method === 'GET') {\n response = await handleListAnnotations(storage, url);\n }\n // PATCH /api/astro-annotate/annotations/:id\n else if (url.pathname.startsWith(API_ANNOTATIONS + '/') && req.method === 'PATCH') {\n const id = url.pathname.slice(API_ANNOTATIONS.length + 1);\n const raw = await collectBody(req);\n const body = JSON.parse(raw);\n response = await handleUpdateAnnotation(storage, id, body);\n }\n // DELETE /api/astro-annotate/annotations/:id\n else if (url.pathname.startsWith(API_ANNOTATIONS + '/') && req.method === 'DELETE') {\n const id = url.pathname.slice(API_ANNOTATIONS.length + 1);\n response = await handleDeleteAnnotation(storage, id);\n }\n // Unknown route\n else {\n return next();\n }\n\n res.statusCode = response.status;\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n const text = await response.text();\n res.end(text);\n } catch (err) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal server error' }));\n }\n });\n}\n","import type { AstroAnnotateConfig } from './types.js';\nimport { createIntegration } from './integration/index.js';\n\nexport default function astroAnnotate(config: AstroAnnotateConfig = {}) {\n return createIntegration(config);\n}\n\nexport type { AstroAnnotateConfig, Annotation, AnnotationsFile } from './types.js';\n"],"mappings":"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","names":["resolve","resolve","resolve"]}
1
+ {"version":3,"sources":["../src/integration/index.ts","../src/integration/vite-plugin.ts","../src/storage/local.ts","../src/server/api-handlers.ts","../src/server/dev-middleware.ts","../src/index.ts"],"sourcesContent":["import type { AstroIntegration } from 'astro';\nimport { fileURLToPath } from 'node:url';\nimport { resolve, dirname } from 'node:path';\nimport type { AstroAnnotateConfig, ResolvedConfig } from '../types.js';\nimport { DEFAULT_ANNOTATIONS_PATH } from '../constants.js';\nimport { astroAnnotateVitePlugin } from './vite-plugin.js';\nimport { LocalStorage } from '../storage/local.js';\nimport { registerDevMiddleware } from '../server/dev-middleware.js';\n\nexport function createIntegration(userConfig: AstroAnnotateConfig = {}): AstroIntegration {\n let resolvedConfig: ResolvedConfig;\n let projectRoot: string;\n\n return {\n name: 'astro-annotate',\n hooks: {\n 'astro:config:setup'({ config, command, updateConfig, injectScript, logger }) {\n projectRoot = fileURLToPath(config.root);\n\n const isDev = command === 'dev' || command === 'preview';\n const enabled = userConfig.enabled ?? isDev;\n\n if (!enabled) {\n logger.info('Disabled (set enabled: true to force)');\n return;\n }\n\n resolvedConfig = {\n enabled,\n mode: isDev ? 'dev' : 'deployed',\n storage: userConfig.storage ?? 'local',\n annotationsPath: userConfig.annotationsPath ?? DEFAULT_ANNOTATIONS_PATH,\n };\n\n // Add Vite plugin for virtual module\n updateConfig({\n vite: {\n plugins: [astroAnnotateVitePlugin(resolvedConfig)],\n },\n });\n\n // Inject client overlay script\n // At runtime, import.meta.url points to dist/index.js, client bundle is dist/client.js\n const clientPath = resolve(dirname(fileURLToPath(import.meta.url)), 'client.js');\n injectScript('page', `window.__ASTRO_ANNOTATE_DEV__=${isDev};import(\"${clientPath}\");`);\n\n logger.info('Overlay enabled');\n },\n\n 'astro:server:setup'({ server, logger }) {\n if (!resolvedConfig) return;\n\n const storage = new LocalStorage(resolvedConfig.annotationsPath, projectRoot);\n registerDevMiddleware(server, storage);\n logger.info('Dev middleware registered');\n },\n },\n };\n}\n","import type { Plugin } from 'vite';\nimport type { ResolvedConfig } from '../types.js';\nimport { VIRTUAL_MODULE_ID, RESOLVED_VIRTUAL_MODULE_ID } from '../constants.js';\n\nexport function astroAnnotateVitePlugin(config: ResolvedConfig): Plugin {\n return {\n name: 'astro-annotate-config',\n resolveId(id) {\n if (id === VIRTUAL_MODULE_ID) {\n return RESOLVED_VIRTUAL_MODULE_ID;\n }\n },\n load(id) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return `export default ${JSON.stringify(config)};`;\n }\n },\n };\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { randomBytes } from 'node:crypto';\nimport type { Annotation, AnnotationsFile, CreateAnnotationPayload, UpdateAnnotationPayload } from '../types.js';\nimport type { AnnotationStorage } from './interface.js';\nimport { ANNOTATIONS_VERSION } from '../constants.js';\n\nfunction generateId(): string {\n return 'a_' + randomBytes(4).toString('hex');\n}\n\nfunction detectDevice(width: number): 'mobile' | 'tablet' | 'desktop' {\n if (width < 768) return 'mobile';\n if (width < 1024) return 'tablet';\n return 'desktop';\n}\n\nexport class LocalStorage implements AnnotationStorage {\n private filePath: string;\n\n constructor(annotationsPath: string, projectRoot: string) {\n this.filePath = resolve(projectRoot, annotationsPath);\n }\n\n private read(): AnnotationsFile {\n if (!existsSync(this.filePath)) {\n return { version: ANNOTATIONS_VERSION, annotations: [] };\n }\n const raw = readFileSync(this.filePath, 'utf-8');\n return JSON.parse(raw) as AnnotationsFile;\n }\n\n private write(data: AnnotationsFile): void {\n writeFileSync(this.filePath, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n }\n\n async list(page?: string): Promise<Annotation[]> {\n const data = this.read();\n if (page) {\n return data.annotations.filter((a) => a.page === page);\n }\n return data.annotations;\n }\n\n async create(payload: CreateAnnotationPayload): Promise<Annotation> {\n const data = this.read();\n const annotation: Annotation = {\n id: generateId(),\n timestamp: new Date().toISOString(),\n page: payload.page,\n selector: payload.selector,\n elementTag: payload.elementTag,\n elementText: payload.elementText.slice(0, 200),\n viewport: payload.viewport,\n device: payload.device || detectDevice(payload.viewport.width),\n text: payload.text,\n author: payload.author || 'Anonymous',\n status: 'open',\n };\n data.annotations.push(annotation);\n this.write(data);\n return annotation;\n }\n\n async update(id: string, payload: UpdateAnnotationPayload): Promise<Annotation | null> {\n const data = this.read();\n const index = data.annotations.findIndex((a) => a.id === id);\n if (index === -1) return null;\n\n if (payload.status) data.annotations[index].status = payload.status;\n if (payload.text !== undefined) data.annotations[index].text = payload.text;\n\n this.write(data);\n return data.annotations[index];\n }\n\n async delete(id: string): Promise<boolean> {\n const data = this.read();\n const before = data.annotations.length;\n data.annotations = data.annotations.filter((a) => a.id !== id);\n if (data.annotations.length === before) return false;\n this.write(data);\n return true;\n }\n}\n","import type { AnnotationStorage } from '../storage/interface.js';\nimport type { CreateAnnotationPayload, UpdateAnnotationPayload } from '../types.js';\n\nfunction json(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\nexport async function handleListAnnotations(\n storage: AnnotationStorage,\n url: URL,\n): Promise<Response> {\n const page = url.searchParams.get('page') || undefined;\n const annotations = await storage.list(page);\n return json({ annotations });\n}\n\nexport async function handleCreateAnnotation(\n storage: AnnotationStorage,\n body: unknown,\n): Promise<Response> {\n const payload = body as CreateAnnotationPayload;\n\n if (!payload.selector || !payload.text || !payload.page) {\n return json({ error: 'Missing required fields: selector, text, page' }, 400);\n }\n\n const annotation = await storage.create(payload);\n return json({ annotation }, 201);\n}\n\nexport async function handleUpdateAnnotation(\n storage: AnnotationStorage,\n id: string,\n body: unknown,\n): Promise<Response> {\n const payload = body as UpdateAnnotationPayload;\n const annotation = await storage.update(id, payload);\n\n if (!annotation) {\n return json({ error: 'Annotation not found' }, 404);\n }\n\n return json({ annotation });\n}\n\nexport async function handleDeleteAnnotation(\n storage: AnnotationStorage,\n id: string,\n): Promise<Response> {\n const deleted = await storage.delete(id);\n\n if (!deleted) {\n return json({ error: 'Annotation not found' }, 404);\n }\n\n return json({ success: true });\n}\n","import type { ViteDevServer, Connect } from 'vite';\nimport type { AnnotationStorage } from '../storage/interface.js';\nimport { API_ANNOTATIONS } from '../constants.js';\nimport {\n handleListAnnotations,\n handleCreateAnnotation,\n handleUpdateAnnotation,\n handleDeleteAnnotation,\n} from './api-handlers.js';\n\nfunction collectBody(req: Connect.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n req.on('error', reject);\n });\n}\n\nexport function registerDevMiddleware(server: ViteDevServer, storage: AnnotationStorage): void {\n server.middlewares.use(async (req, res, next) => {\n const url = new URL(req.url || '/', `http://${req.headers.host}`);\n\n if (!url.pathname.startsWith(API_ANNOTATIONS)) {\n return next();\n }\n\n try {\n let response: Response;\n\n // POST /api/astro-annotate/annotations\n if (url.pathname === API_ANNOTATIONS && req.method === 'POST') {\n const raw = await collectBody(req);\n const body = JSON.parse(raw);\n response = await handleCreateAnnotation(storage, body);\n }\n // GET /api/astro-annotate/annotations\n else if (url.pathname === API_ANNOTATIONS && req.method === 'GET') {\n response = await handleListAnnotations(storage, url);\n }\n // PATCH /api/astro-annotate/annotations/:id\n else if (url.pathname.startsWith(API_ANNOTATIONS + '/') && req.method === 'PATCH') {\n const id = url.pathname.slice(API_ANNOTATIONS.length + 1);\n const raw = await collectBody(req);\n const body = JSON.parse(raw);\n response = await handleUpdateAnnotation(storage, id, body);\n }\n // DELETE /api/astro-annotate/annotations/:id\n else if (url.pathname.startsWith(API_ANNOTATIONS + '/') && req.method === 'DELETE') {\n const id = url.pathname.slice(API_ANNOTATIONS.length + 1);\n response = await handleDeleteAnnotation(storage, id);\n }\n // Unknown route\n else {\n return next();\n }\n\n res.statusCode = response.status;\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n const text = await response.text();\n res.end(text);\n } catch (err) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal server error' }));\n }\n });\n}\n","import type { AstroAnnotateConfig } from './types.js';\nimport { createIntegration } from './integration/index.js';\n\nexport default function astroAnnotate(config: AstroAnnotateConfig = {}) {\n return createIntegration(config);\n}\n\nexport type { AstroAnnotateConfig, Annotation, AnnotationsFile } from './types.js';\n"],"mappings":"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","names":["resolve","resolve","resolve"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-annotate",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Visual annotation overlay for Astro staging sites. Clients annotate HTML elements directly in the browser. Annotations stored as structured JSON, readable by developers and LLMs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,6 +33,7 @@
33
33
  "astro": "^4.0.0 || ^5.0.0 || ^6.0.0"
34
34
  },
35
35
  "devDependencies": {
36
+ "@types/node": "^25.3.0",
36
37
  "astro": "^5.0.0",
37
38
  "tsup": "^8.0.0",
38
39
  "typescript": "^5.5.0"