astro-annotate 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +47 -10
- package/dist/client.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
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 ===
|
|
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">×</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(
|
|
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
|
-
|
|
902
|
-
|
|
903
|
-
existing.remove();
|
|
939
|
+
if (overlay) {
|
|
940
|
+
overlay.destroy();
|
|
904
941
|
overlay = null;
|
|
905
942
|
}
|
|
906
943
|
overlay = new Overlay();
|
package/dist/client.js.map
CHANGED
|
@@ -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\">×</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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\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\">×</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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\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\">×</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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\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\">×</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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\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
|
|
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
|
|
3
|
+
"version": "0.2.1",
|
|
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",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"keywords": [
|
|
19
19
|
"astro-integration",
|
|
20
20
|
"withastro",
|
|
21
|
+
"dev-toolbar",
|
|
22
|
+
"devtools",
|
|
21
23
|
"annotations",
|
|
22
24
|
"visual-feedback",
|
|
23
25
|
"llm",
|
|
@@ -33,6 +35,7 @@
|
|
|
33
35
|
"astro": "^4.0.0 || ^5.0.0 || ^6.0.0"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.3.0",
|
|
36
39
|
"astro": "^5.0.0",
|
|
37
40
|
"tsup": "^8.0.0",
|
|
38
41
|
"typescript": "^5.5.0"
|