astro-annotate 0.3.0 → 0.4.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
@@ -55,6 +55,7 @@ var OVERLAY_STYLES = `
55
55
  background: #fff;
56
56
  border-radius: 12px;
57
57
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
58
+ border: 1px solid rgba(0, 0, 0, 0.08);
58
59
  width: 340px;
59
60
  overflow: hidden;
60
61
  }
@@ -205,6 +206,7 @@ var OVERLAY_STYLES = `
205
206
  background: #fff;
206
207
  border-radius: 12px;
207
208
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
209
+ border: 1px solid rgba(0, 0, 0, 0.08);
208
210
  width: 320px;
209
211
  overflow: hidden;
210
212
  }
@@ -294,11 +296,288 @@ var OVERLAY_STYLES = `
294
296
  color: #fff;
295
297
  }
296
298
 
299
+ /* Annotation Panel */
300
+ .aa-panel {
301
+ position: fixed;
302
+ top: 16px;
303
+ right: 16px;
304
+ width: 360px;
305
+ height: calc(100vh - 32px);
306
+ background: #fff;
307
+ box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);
308
+ border: 1px solid rgba(0, 0, 0, 0.08);
309
+ border-radius: 12px;
310
+ overflow: hidden;
311
+ display: flex;
312
+ flex-direction: column;
313
+ z-index: 2147483647;
314
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
315
+ font-size: 14px;
316
+ color: #1a1a2e;
317
+ }
318
+
319
+ .aa-panel.aa-panel-left {
320
+ right: auto;
321
+ left: 16px;
322
+ box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15);
323
+ }
324
+
325
+ .aa-panel-header {
326
+ background: #1a1a2e;
327
+ color: #fff;
328
+ padding: 14px 16px;
329
+ display: flex;
330
+ justify-content: space-between;
331
+ align-items: center;
332
+ flex-shrink: 0;
333
+ }
334
+
335
+ .aa-panel-title {
336
+ font-size: 14px;
337
+ font-weight: 600;
338
+ }
339
+
340
+ .aa-panel-header-actions {
341
+ display: flex;
342
+ gap: 4px;
343
+ }
344
+
345
+ .aa-panel-header-actions button {
346
+ background: none;
347
+ border: none;
348
+ color: #fff;
349
+ cursor: pointer;
350
+ font-size: 16px;
351
+ width: 28px;
352
+ height: 28px;
353
+ display: flex;
354
+ align-items: center;
355
+ justify-content: center;
356
+ border-radius: 4px;
357
+ opacity: 0.7;
358
+ }
359
+
360
+ .aa-panel-header-actions button:hover {
361
+ opacity: 1;
362
+ background: rgba(255, 255, 255, 0.1);
363
+ }
364
+
365
+ .aa-panel-filters {
366
+ display: flex;
367
+ background: #f8f8fa;
368
+ border-bottom: 1px solid #e0e0e0;
369
+ flex-shrink: 0;
370
+ }
371
+
372
+ .aa-panel-filters button {
373
+ flex: 1;
374
+ padding: 10px 8px;
375
+ background: none;
376
+ border: none;
377
+ border-bottom: 2px solid transparent;
378
+ cursor: pointer;
379
+ font-size: 12px;
380
+ font-weight: 500;
381
+ color: #666;
382
+ transition: color 0.15s, border-color 0.15s;
383
+ }
384
+
385
+ .aa-panel-filters button:hover {
386
+ color: #1a1a2e;
387
+ background: #f0f0f2;
388
+ }
389
+
390
+ .aa-panel-filters button.aa-active {
391
+ color: #e94560;
392
+ border-bottom-color: #e94560;
393
+ }
394
+
395
+ .aa-panel-bulk {
396
+ padding: 10px 16px;
397
+ border-bottom: 1px solid #e0e0e0;
398
+ flex-shrink: 0;
399
+ }
400
+
401
+ .aa-panel-bulk-btn {
402
+ width: 100%;
403
+ padding: 7px 12px;
404
+ border: 1px solid #2ecc71;
405
+ border-radius: 6px;
406
+ background: transparent;
407
+ color: #2ecc71;
408
+ font-size: 12px;
409
+ font-weight: 500;
410
+ cursor: pointer;
411
+ transition: all 0.15s;
412
+ }
413
+
414
+ .aa-panel-bulk-btn:hover {
415
+ background: #2ecc71;
416
+ color: #fff;
417
+ }
418
+
419
+ .aa-panel-bulk-btn[disabled] {
420
+ opacity: 0.6;
421
+ cursor: not-allowed;
422
+ }
423
+
424
+ .aa-panel-list {
425
+ flex: 1;
426
+ overflow-y: auto;
427
+ }
428
+
429
+ .aa-panel-item {
430
+ padding: 12px 16px;
431
+ border-bottom: 1px solid #f0f0f0;
432
+ transition: background 0.1s;
433
+ }
434
+
435
+ .aa-panel-item:hover {
436
+ background: #fafafa;
437
+ }
438
+
439
+ .aa-panel-item.aa-panel-item-resolved {
440
+ opacity: 0.6;
441
+ }
442
+
443
+ .aa-panel-item-header {
444
+ display: flex;
445
+ align-items: center;
446
+ gap: 8px;
447
+ margin-bottom: 4px;
448
+ font-size: 12px;
449
+ }
450
+
451
+ .aa-panel-item-number {
452
+ font-weight: 700;
453
+ color: #e94560;
454
+ }
455
+
456
+ .aa-panel-item-number.aa-panel-item-number-resolved {
457
+ color: #2ecc71;
458
+ }
459
+
460
+ .aa-panel-item-author {
461
+ font-weight: 500;
462
+ }
463
+
464
+ .aa-panel-item-time {
465
+ color: #999;
466
+ margin-left: auto;
467
+ font-size: 11px;
468
+ }
469
+
470
+ .aa-panel-item-selector {
471
+ font-family: 'SF Mono', Monaco, monospace;
472
+ font-size: 11px;
473
+ color: #666;
474
+ background: #f5f5f5;
475
+ padding: 3px 6px;
476
+ border-radius: 3px;
477
+ margin-bottom: 6px;
478
+ overflow: hidden;
479
+ text-overflow: ellipsis;
480
+ white-space: nowrap;
481
+ }
482
+
483
+ .aa-panel-item-text {
484
+ font-size: 13px;
485
+ white-space: pre-wrap;
486
+ word-break: break-word;
487
+ margin-bottom: 8px;
488
+ color: #333;
489
+ }
490
+
491
+ .aa-panel-item-actions {
492
+ display: flex;
493
+ gap: 6px;
494
+ }
495
+
496
+ .aa-panel-item-actions button {
497
+ padding: 3px 10px;
498
+ border: 1px solid #e0e0e0;
499
+ border-radius: 4px;
500
+ background: #fff;
501
+ font-size: 11px;
502
+ cursor: pointer;
503
+ transition: all 0.15s;
504
+ color: #555;
505
+ }
506
+
507
+ .aa-panel-item-actions button:hover {
508
+ background: #f5f5f5;
509
+ border-color: #ccc;
510
+ }
511
+
512
+ .aa-panel-edit-textarea {
513
+ width: 100%;
514
+ min-height: 60px;
515
+ max-height: 40vh;
516
+ padding: 8px;
517
+ border: 1px solid #e94560;
518
+ border-radius: 4px;
519
+ font-size: 13px;
520
+ font-family: inherit;
521
+ resize: vertical;
522
+ outline: none;
523
+ overflow-y: auto;
524
+ margin-bottom: 6px;
525
+ }
526
+
527
+ .aa-panel-empty {
528
+ padding: 40px 16px;
529
+ text-align: center;
530
+ color: #999;
531
+ font-size: 13px;
532
+ }
533
+
534
+ /* Floating Action Button */
535
+ .aa-panel-fab {
536
+ position: fixed;
537
+ bottom: 72px;
538
+ right: 16px;
539
+ width: 32px;
540
+ height: 32px;
541
+ border-radius: 50%;
542
+ background: #1a1a2e;
543
+ color: #fff;
544
+ cursor: pointer;
545
+ border: 1px solid rgba(255, 255, 255, 0.15);
546
+ display: flex;
547
+ align-items: center;
548
+ justify-content: center;
549
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
550
+ z-index: 2147483647;
551
+ transition: background 0.15s;
552
+ }
553
+
554
+ .aa-panel-fab:hover {
555
+ background: #2a2a40;
556
+ }
557
+
558
+ .aa-panel-fab-badge {
559
+ position: absolute;
560
+ top: -4px;
561
+ right: -4px;
562
+ background: #e94560;
563
+ color: #fff;
564
+ font-size: 9px;
565
+ font-weight: 700;
566
+ width: 16px;
567
+ height: 16px;
568
+ border-radius: 50%;
569
+ display: flex;
570
+ align-items: center;
571
+ justify-content: center;
572
+ line-height: 1;
573
+ }
574
+
297
575
  /* Dark mode */
298
576
  @media (prefers-color-scheme: dark) {
299
577
  .aa-form-container, .aa-pin-detail {
300
578
  background: #2d2d3f;
301
579
  color: #e0e0e0;
580
+ border-color: rgba(255, 255, 255, 0.1);
302
581
  }
303
582
 
304
583
  .aa-input, .aa-textarea {
@@ -329,6 +608,96 @@ var OVERLAY_STYLES = `
329
608
  .aa-status-btn:hover {
330
609
  background: #404060;
331
610
  }
611
+
612
+ .aa-panel {
613
+ background: #2d2d3f;
614
+ color: #e0e0e0;
615
+ box-shadow: -4px 0 24px rgba(0, 0, 0, 0.4);
616
+ border-color: rgba(255, 255, 255, 0.1);
617
+ }
618
+
619
+ .aa-panel.aa-panel-left {
620
+ box-shadow: 4px 0 24px rgba(0, 0, 0, 0.4);
621
+ }
622
+
623
+ .aa-panel-filters {
624
+ background: #252538;
625
+ border-bottom-color: #404060;
626
+ }
627
+
628
+ .aa-panel-filters button {
629
+ color: #aaa;
630
+ }
631
+
632
+ .aa-panel-filters button:hover {
633
+ color: #e0e0e0;
634
+ background: #353550;
635
+ }
636
+
637
+ .aa-panel-bulk {
638
+ border-bottom-color: #404060;
639
+ }
640
+
641
+ .aa-panel-bulk-btn {
642
+ color: #2ecc71;
643
+ border-color: #2ecc71;
644
+ background: transparent;
645
+ }
646
+
647
+ .aa-panel-bulk-btn:hover {
648
+ background: #2ecc71;
649
+ color: #fff;
650
+ }
651
+
652
+ .aa-panel-item {
653
+ border-bottom-color: #404060;
654
+ }
655
+
656
+ .aa-panel-item:hover {
657
+ background: #353550;
658
+ }
659
+
660
+ .aa-panel-item-time {
661
+ color: #888;
662
+ }
663
+
664
+ .aa-panel-item-selector {
665
+ background: #1a1a2e;
666
+ color: #aaa;
667
+ }
668
+
669
+ .aa-panel-item-text {
670
+ color: #ddd;
671
+ }
672
+
673
+ .aa-panel-item-actions button {
674
+ border-color: #404060;
675
+ color: #ccc;
676
+ background: #2d2d3f;
677
+ }
678
+
679
+ .aa-panel-item-actions button:hover {
680
+ background: #404060;
681
+ }
682
+
683
+ .aa-panel-edit-textarea {
684
+ background: #1a1a2e;
685
+ color: #e0e0e0;
686
+ border-color: #e94560;
687
+ }
688
+
689
+ .aa-panel-empty {
690
+ color: #888;
691
+ }
692
+
693
+ .aa-panel-fab {
694
+ background: #2d2d3f;
695
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
696
+ }
697
+
698
+ .aa-panel-fab:hover {
699
+ background: #404060;
700
+ }
332
701
  }
333
702
  `;
334
703
 
@@ -462,6 +831,11 @@ var Highlighter = class {
462
831
  }
463
832
  };
464
833
 
834
+ // src/client/utils.ts
835
+ function escapeHtml(str) {
836
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
837
+ }
838
+
465
839
  // src/client/form.ts
466
840
  var AnnotationForm = class {
467
841
  constructor(shadowRoot, onSubmitted, onClosed, devMode) {
@@ -499,7 +873,7 @@ var AnnotationForm = class {
499
873
  <div class="aa-form-header">
500
874
  <div>
501
875
  <div class="aa-form-header-title">New Annotation</div>
502
- <div class="aa-form-header-selector">${this.escapeHtml(selector)}</div>
876
+ <div class="aa-form-header-selector">${escapeHtml(selector)}</div>
503
877
  </div>
504
878
  <button class="aa-form-close" data-action="close">&times;</button>
505
879
  </div>
@@ -568,9 +942,6 @@ var AnnotationForm = class {
568
942
  isVisible() {
569
943
  return this.container.style.display !== "none";
570
944
  }
571
- escapeHtml(str) {
572
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
573
- }
574
945
  destroy() {
575
946
  this.container.remove();
576
947
  }
@@ -589,7 +960,9 @@ var PinManager = class {
589
960
  pins = [];
590
961
  detailPopup;
591
962
  onChanged;
592
- render(annotations) {
963
+ panelSide = null;
964
+ render(annotations, panelSide = null) {
965
+ this.panelSide = panelSide;
593
966
  this.clearPins();
594
967
  annotations.forEach((annotation, index) => {
595
968
  const el = document.querySelector(annotation.selector);
@@ -600,8 +973,22 @@ var PinManager = class {
600
973
  const updatePosition = () => {
601
974
  const rect = el.getBoundingClientRect();
602
975
  pin.style.position = "fixed";
603
- pin.style.top = `${Math.max(0, rect.top - 10)}px`;
604
- pin.style.left = `${Math.max(0, rect.right - 24)}px`;
976
+ const pinTop = Math.max(0, rect.top - 10);
977
+ let pinLeft;
978
+ if (this.panelSide === "right") {
979
+ pinLeft = Math.max(10, rect.left - 32);
980
+ } else {
981
+ pinLeft = Math.max(10, rect.right - 24);
982
+ }
983
+ const fabLeft = window.innerWidth - 48;
984
+ const fabTop = window.innerHeight - 104;
985
+ const fabRight = window.innerWidth - 16;
986
+ const fabBottom = window.innerHeight - 72;
987
+ if (pinTop + 28 > fabTop && pinTop < fabBottom && pinLeft + 28 > fabLeft && pinLeft < fabRight) {
988
+ pinLeft = fabLeft - 32;
989
+ }
990
+ pin.style.top = `${pinTop}px`;
991
+ pin.style.left = `${pinLeft}px`;
605
992
  };
606
993
  updatePosition();
607
994
  pin.addEventListener("click", (e) => {
@@ -633,15 +1020,15 @@ var PinManager = class {
633
1020
  this.detailPopup.innerHTML = `
634
1021
  <div class="aa-pin-detail-header">
635
1022
  <div>
636
- <div class="aa-form-header-title">#${index + 1} \u2014 ${this.escapeHtml(annotation.author)}</div>
1023
+ <div class="aa-form-header-title">#${index + 1} \u2014 ${escapeHtml(annotation.author)}</div>
637
1024
  <div class="aa-pin-detail-meta">${date} \xB7 ${annotation.device} \xB7 ${annotation.status}</div>
638
1025
  </div>
639
1026
  <button class="aa-form-close" data-action="close-detail">&times;</button>
640
1027
  </div>
641
1028
  <div class="aa-pin-detail-body">
642
- <div class="aa-pin-detail-text">${this.escapeHtml(annotation.text)}</div>
1029
+ <div class="aa-pin-detail-text">${escapeHtml(annotation.text)}</div>
643
1030
  <div class="aa-pin-detail-info">
644
- <div class="aa-pin-detail-selector">${this.escapeHtml(annotation.selector)}</div>
1031
+ <div class="aa-pin-detail-selector">${escapeHtml(annotation.selector)}</div>
645
1032
  </div>
646
1033
  </div>
647
1034
  <div class="aa-pin-detail-actions">
@@ -688,15 +1075,334 @@ var PinManager = class {
688
1075
  this.pins = [];
689
1076
  this.hideDetail();
690
1077
  }
691
- escapeHtml(str) {
692
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
693
- }
694
1078
  destroy() {
695
1079
  this.clearPins();
696
1080
  this.detailPopup.remove();
697
1081
  }
698
1082
  };
699
1083
 
1084
+ // src/client/panel.ts
1085
+ var AnnotationPanel = class {
1086
+ constructor(shadowRoot, onChanged, onVisibilityChanged = () => {
1087
+ }) {
1088
+ this.shadowRoot = shadowRoot;
1089
+ this.onChanged = onChanged;
1090
+ this.onVisibilityChanged = onVisibilityChanged;
1091
+ this.container = document.createElement("div");
1092
+ this.container.className = "aa-panel";
1093
+ this.container.style.display = "none";
1094
+ this.container.addEventListener("click", this.onClick);
1095
+ this.container.addEventListener("keydown", this.onKeyDown);
1096
+ this.shadowRoot.appendChild(this.container);
1097
+ this.fab = document.createElement("button");
1098
+ this.fab.className = "aa-panel-fab";
1099
+ this.fab.addEventListener("click", () => this.toggle());
1100
+ this.shadowRoot.appendChild(this.fab);
1101
+ this.renderFab();
1102
+ }
1103
+ container;
1104
+ fab;
1105
+ visible = false;
1106
+ filter = "open";
1107
+ side = "right";
1108
+ editingId = null;
1109
+ annotations = [];
1110
+ indexMap = /* @__PURE__ */ new Map();
1111
+ onChanged;
1112
+ onVisibilityChanged;
1113
+ show() {
1114
+ this.visible = true;
1115
+ this.container.style.display = "flex";
1116
+ this.render();
1117
+ this.onVisibilityChanged();
1118
+ }
1119
+ hide() {
1120
+ this.visible = false;
1121
+ this.editingId = null;
1122
+ this.container.style.display = "none";
1123
+ this.onVisibilityChanged();
1124
+ }
1125
+ toggle() {
1126
+ if (this.visible) {
1127
+ this.hide();
1128
+ } else {
1129
+ this.show();
1130
+ }
1131
+ }
1132
+ isVisible() {
1133
+ return this.visible;
1134
+ }
1135
+ isEditing() {
1136
+ return this.editingId !== null;
1137
+ }
1138
+ getSide() {
1139
+ return this.side;
1140
+ }
1141
+ getState() {
1142
+ return { visible: this.visible, filter: this.filter, side: this.side };
1143
+ }
1144
+ restoreState(state) {
1145
+ this.filter = state.filter || "open";
1146
+ this.side = state.side || "right";
1147
+ if (state.visible) {
1148
+ this.show();
1149
+ }
1150
+ }
1151
+ update(annotations) {
1152
+ this.annotations = annotations;
1153
+ this.rebuildIndexMap();
1154
+ this.renderFab();
1155
+ if (this.visible) {
1156
+ this.render();
1157
+ }
1158
+ }
1159
+ destroy() {
1160
+ this.container.removeEventListener("click", this.onClick);
1161
+ this.container.removeEventListener("keydown", this.onKeyDown);
1162
+ this.container.remove();
1163
+ this.fab.remove();
1164
+ }
1165
+ // --- Private ---
1166
+ rebuildIndexMap() {
1167
+ this.indexMap.clear();
1168
+ this.annotations.forEach((a, i) => {
1169
+ this.indexMap.set(a.id, i + 1);
1170
+ });
1171
+ }
1172
+ getFiltered() {
1173
+ if (this.filter === "all") return this.annotations;
1174
+ return this.annotations.filter((a) => a.status === this.filter);
1175
+ }
1176
+ countByStatus(status) {
1177
+ return this.annotations.filter((a) => a.status === status).length;
1178
+ }
1179
+ render() {
1180
+ const filtered = this.getFiltered();
1181
+ const openCount = this.countByStatus("open");
1182
+ const resolvedCount = this.countByStatus("resolved");
1183
+ const totalCount = this.annotations.length;
1184
+ this.container.className = `aa-panel${this.side === "left" ? " aa-panel-left" : ""}`;
1185
+ const sideIcon = this.side === "right" ? '<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="3" y1="3" x2="3" y2="13"/><polyline points="12,5 8,8 12,11"/></svg>' : '<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="13" y1="3" x2="13" y2="13"/><polyline points="4,5 8,8 4,11"/></svg>';
1186
+ this.container.innerHTML = `
1187
+ <div class="aa-panel-header">
1188
+ <span class="aa-panel-title">Annotations (${totalCount})</span>
1189
+ <div class="aa-panel-header-actions">
1190
+ <button data-action="toggle-side" title="Move panel">${sideIcon}</button>
1191
+ <button data-action="close-panel" title="Close panel">&times;</button>
1192
+ </div>
1193
+ </div>
1194
+ <div class="aa-panel-filters">
1195
+ <button data-action="filter" data-filter="open" class="${this.filter === "open" ? "aa-active" : ""}">Open (${openCount})</button>
1196
+ <button data-action="filter" data-filter="resolved" class="${this.filter === "resolved" ? "aa-active" : ""}">Resolved (${resolvedCount})</button>
1197
+ <button data-action="filter" data-filter="all" class="${this.filter === "all" ? "aa-active" : ""}">All (${totalCount})</button>
1198
+ </div>
1199
+ ${this.filter === "open" && openCount > 0 ? `
1200
+ <div class="aa-panel-bulk">
1201
+ <button class="aa-panel-bulk-btn" data-action="bulk-resolve">Mark all as done</button>
1202
+ </div>
1203
+ ` : ""}
1204
+ <div class="aa-panel-list">
1205
+ ${filtered.length > 0 ? filtered.map((a) => this.renderItem(a)).join("") : '<div class="aa-panel-empty">No annotations match this filter.</div>'}
1206
+ </div>
1207
+ `;
1208
+ if (this.editingId) {
1209
+ const textarea = this.container.querySelector(".aa-panel-edit-textarea");
1210
+ if (textarea) {
1211
+ setTimeout(() => {
1212
+ textarea.focus();
1213
+ textarea.style.height = "auto";
1214
+ textarea.style.height = Math.min(textarea.scrollHeight, window.innerHeight * 0.4) + "px";
1215
+ }, 30);
1216
+ }
1217
+ }
1218
+ }
1219
+ renderItem(annotation) {
1220
+ const num = this.indexMap.get(annotation.id) ?? 0;
1221
+ const isResolved = annotation.status === "resolved";
1222
+ const isEditing = this.editingId === annotation.id;
1223
+ const numberClass = isResolved ? "aa-panel-item-number aa-panel-item-number-resolved" : "aa-panel-item-number";
1224
+ const textBlock = isEditing ? `<textarea class="aa-panel-edit-textarea" data-id="${annotation.id}">${escapeHtml(annotation.text)}</textarea>
1225
+ <div class="aa-panel-item-actions">
1226
+ <button data-action="save-edit" data-id="${annotation.id}">Save</button>
1227
+ <button data-action="cancel-edit" data-id="${annotation.id}">Cancel</button>
1228
+ </div>` : `<div class="aa-panel-item-text">${escapeHtml(annotation.text)}</div>
1229
+ <div class="aa-panel-item-actions">
1230
+ <button data-action="locate" data-id="${annotation.id}" data-selector="${escapeHtml(annotation.selector)}">Locate</button>
1231
+ <button data-action="edit-inline" data-id="${annotation.id}">Edit</button>
1232
+ ${isResolved ? `<button data-action="reopen" data-id="${annotation.id}">Reopen</button>` : `<button data-action="resolve" data-id="${annotation.id}">Done</button>`}
1233
+ </div>`;
1234
+ return `
1235
+ <div class="aa-panel-item${isResolved ? " aa-panel-item-resolved" : ""}" data-id="${annotation.id}">
1236
+ <div class="aa-panel-item-header">
1237
+ <span class="${numberClass}">#${num}</span>
1238
+ <span class="aa-panel-item-author">${escapeHtml(annotation.author)}</span>
1239
+ <span class="aa-panel-item-time">${this.formatTimeAgo(annotation.timestamp)}</span>
1240
+ </div>
1241
+ <div class="aa-panel-item-selector">${escapeHtml(annotation.selector)}</div>
1242
+ ${textBlock}
1243
+ </div>
1244
+ `;
1245
+ }
1246
+ renderFab() {
1247
+ const openCount = this.countByStatus("open");
1248
+ const badge = openCount > 0 ? `<span class="aa-panel-fab-badge">${openCount}</span>` : "";
1249
+ this.fab.innerHTML = `
1250
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
1251
+ <line x1="2" y1="4" x2="14" y2="4"/><line x1="2" y1="8" x2="14" y2="8"/><line x1="2" y1="12" x2="10" y2="12"/>
1252
+ </svg>
1253
+ ${badge}
1254
+ `;
1255
+ }
1256
+ // --- Event delegation ---
1257
+ onClick = (e) => {
1258
+ const target = e.target;
1259
+ const actionEl = target.closest("[data-action]");
1260
+ if (!actionEl) return;
1261
+ const action = actionEl.dataset.action;
1262
+ const id = actionEl.dataset.id;
1263
+ switch (action) {
1264
+ case "close-panel":
1265
+ this.hide();
1266
+ break;
1267
+ case "toggle-side":
1268
+ this.side = this.side === "right" ? "left" : "right";
1269
+ this.render();
1270
+ this.onVisibilityChanged();
1271
+ break;
1272
+ case "filter":
1273
+ this.filter = actionEl.dataset.filter || "all";
1274
+ this.editingId = null;
1275
+ this.render();
1276
+ break;
1277
+ case "bulk-resolve":
1278
+ this.bulkResolve(actionEl);
1279
+ break;
1280
+ case "locate":
1281
+ if (actionEl.dataset.selector) this.locateElement(actionEl.dataset.selector);
1282
+ break;
1283
+ case "edit-inline":
1284
+ if (id) {
1285
+ this.editingId = id;
1286
+ this.render();
1287
+ }
1288
+ break;
1289
+ case "cancel-edit":
1290
+ this.editingId = null;
1291
+ this.render();
1292
+ break;
1293
+ case "save-edit":
1294
+ if (id) this.saveEdit(id);
1295
+ break;
1296
+ case "resolve":
1297
+ if (id) this.updateStatus(id, "resolved");
1298
+ break;
1299
+ case "reopen":
1300
+ if (id) this.updateStatus(id, "open");
1301
+ break;
1302
+ }
1303
+ };
1304
+ onKeyDown = (e) => {
1305
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && this.editingId) {
1306
+ e.preventDefault();
1307
+ this.saveEdit(this.editingId);
1308
+ }
1309
+ };
1310
+ // --- Actions ---
1311
+ async updateStatus(id, status) {
1312
+ try {
1313
+ const res = await fetch(`${API_ANNOTATIONS}/${id}`, {
1314
+ method: "PATCH",
1315
+ headers: { "Content-Type": "application/json" },
1316
+ body: JSON.stringify({ status })
1317
+ });
1318
+ if (!res.ok) throw new Error("Failed to update");
1319
+ this.onChanged();
1320
+ } catch (err) {
1321
+ console.error("[astro-annotate] Failed to update annotation:", err);
1322
+ }
1323
+ }
1324
+ async saveEdit(id) {
1325
+ const textarea = this.container.querySelector(`.aa-panel-edit-textarea[data-id="${id}"]`);
1326
+ if (!textarea) return;
1327
+ const text = textarea.value.trim();
1328
+ if (!text) return;
1329
+ try {
1330
+ const res = await fetch(`${API_ANNOTATIONS}/${id}`, {
1331
+ method: "PATCH",
1332
+ headers: { "Content-Type": "application/json" },
1333
+ body: JSON.stringify({ text })
1334
+ });
1335
+ if (!res.ok) throw new Error("Failed to update");
1336
+ this.editingId = null;
1337
+ this.onChanged();
1338
+ } catch (err) {
1339
+ console.error("[astro-annotate] Failed to update annotation text:", err);
1340
+ }
1341
+ }
1342
+ async bulkResolve(btn) {
1343
+ const openAnnotations = this.annotations.filter((a) => a.status === "open");
1344
+ const total = openAnnotations.length;
1345
+ if (total === 0) return;
1346
+ btn.setAttribute("disabled", "");
1347
+ for (let i = 0; i < openAnnotations.length; i++) {
1348
+ btn.textContent = `Resolving ${i + 1}/${total}...`;
1349
+ try {
1350
+ const res = await fetch(`${API_ANNOTATIONS}/${openAnnotations[i].id}`, {
1351
+ method: "PATCH",
1352
+ headers: { "Content-Type": "application/json" },
1353
+ body: JSON.stringify({ status: "resolved" })
1354
+ });
1355
+ if (!res.ok) throw new Error("Failed to update");
1356
+ } catch (err) {
1357
+ console.error("[astro-annotate] Failed to resolve annotation:", err);
1358
+ }
1359
+ }
1360
+ this.onChanged();
1361
+ }
1362
+ locateElement(selector) {
1363
+ const el = document.querySelector(selector);
1364
+ if (!el) return;
1365
+ el.scrollIntoView({ behavior: "instant", block: "center" });
1366
+ const flash = document.createElement("div");
1367
+ const rect = el.getBoundingClientRect();
1368
+ Object.assign(flash.style, {
1369
+ position: "fixed",
1370
+ top: `${rect.top}px`,
1371
+ left: `${rect.left}px`,
1372
+ width: `${rect.width}px`,
1373
+ height: `${rect.height}px`,
1374
+ border: "2px solid #e94560",
1375
+ background: "rgba(233, 69, 96, 0.12)",
1376
+ borderRadius: "3px",
1377
+ pointerEvents: "none",
1378
+ zIndex: "2147483646",
1379
+ transition: "opacity 0.6s ease"
1380
+ });
1381
+ this.shadowRoot.appendChild(flash);
1382
+ setTimeout(() => {
1383
+ flash.style.opacity = "0";
1384
+ }, 700);
1385
+ setTimeout(() => {
1386
+ flash.remove();
1387
+ }, 1300);
1388
+ }
1389
+ // --- Helpers ---
1390
+ formatTimeAgo(timestamp) {
1391
+ const now = Date.now();
1392
+ const then = new Date(timestamp).getTime();
1393
+ if (isNaN(then)) return "unknown";
1394
+ const diffSec = Math.floor((now - then) / 1e3);
1395
+ if (diffSec < 60) return "just now";
1396
+ const diffMin = Math.floor(diffSec / 60);
1397
+ if (diffMin < 60) return `${diffMin}m ago`;
1398
+ const diffHr = Math.floor(diffMin / 60);
1399
+ if (diffHr < 24) return `${diffHr}h ago`;
1400
+ const diffDay = Math.floor(diffHr / 24);
1401
+ if (diffDay < 30) return `${diffDay}d ago`;
1402
+ return new Date(timestamp).toLocaleDateString();
1403
+ }
1404
+ };
1405
+
700
1406
  // src/client/overlay.ts
701
1407
  var Overlay = class {
702
1408
  host;
@@ -704,9 +1410,11 @@ var Overlay = class {
704
1410
  highlighter;
705
1411
  form;
706
1412
  pinManager;
1413
+ panel;
707
1414
  active = false;
708
1415
  devMode = !!window.__ASTRO_ANNOTATE_DEV__;
709
1416
  annotations = [];
1417
+ lastOpenedUI = null;
710
1418
  constructor() {
711
1419
  this.host = document.createElement("div");
712
1420
  this.host.id = SHADOW_ROOT_ID;
@@ -723,6 +1431,11 @@ var Overlay = class {
723
1431
  this.devMode
724
1432
  );
725
1433
  this.pinManager = new PinManager(this.shadowRoot, () => this.loadAnnotations());
1434
+ this.panel = new AnnotationPanel(
1435
+ this.shadowRoot,
1436
+ () => this.loadAnnotations(),
1437
+ () => this.renderPins()
1438
+ );
726
1439
  window.addEventListener("aa:toggle", this.onToolbarToggle);
727
1440
  this.loadAnnotations();
728
1441
  document.addEventListener("click", (e) => {
@@ -767,6 +1480,7 @@ var Overlay = class {
767
1480
  this.highlighter.hide();
768
1481
  document.removeEventListener("mousemove", this.highlighter.onMouseMove);
769
1482
  this.form.show(target);
1483
+ this.lastOpenedUI = "form";
770
1484
  };
771
1485
  onKeyDown = (e) => {
772
1486
  const active = document.activeElement;
@@ -778,7 +1492,28 @@ var Overlay = class {
778
1492
  window.dispatchEvent(new CustomEvent("aa:state-changed", { detail: { active: newActive } }));
779
1493
  return;
780
1494
  }
1495
+ if (e.altKey && e.code === "KeyL" && !isExternalInput) {
1496
+ if (this.panel.isEditing()) return;
1497
+ e.preventDefault();
1498
+ this.panel.toggle();
1499
+ this.lastOpenedUI = this.panel.isVisible() ? "panel" : null;
1500
+ return;
1501
+ }
781
1502
  if (e.key === "Escape") {
1503
+ if (this.lastOpenedUI === "form" && this.form.isVisible()) {
1504
+ this.form.hide();
1505
+ this.lastOpenedUI = this.panel.isVisible() ? "panel" : null;
1506
+ return;
1507
+ }
1508
+ if (this.lastOpenedUI === "panel" && this.panel.isVisible()) {
1509
+ this.panel.hide();
1510
+ this.lastOpenedUI = this.form.isVisible() ? "form" : null;
1511
+ return;
1512
+ }
1513
+ if (this.panel.isVisible()) {
1514
+ this.panel.hide();
1515
+ return;
1516
+ }
782
1517
  if (this.form.isVisible()) {
783
1518
  this.form.hide();
784
1519
  return;
@@ -800,11 +1535,13 @@ var Overlay = class {
800
1535
  const openCount = this.annotations.filter((a) => a.status === "open").length;
801
1536
  window.dispatchEvent(new CustomEvent("aa:count", { detail: { count: openCount } }));
802
1537
  this.renderPins();
1538
+ this.panel.update(this.annotations);
803
1539
  } catch {
804
1540
  }
805
1541
  }
806
1542
  renderPins() {
807
- this.pinManager.render(this.annotations);
1543
+ const panelSide = this.panel.isVisible() ? this.panel.getSide() : null;
1544
+ this.pinManager.render(this.annotations, panelSide);
808
1545
  }
809
1546
  onAnnotationCreated() {
810
1547
  this.loadAnnotations();
@@ -814,6 +1551,12 @@ var Overlay = class {
814
1551
  document.addEventListener("mousemove", this.highlighter.onMouseMove);
815
1552
  }
816
1553
  }
1554
+ getPanelState() {
1555
+ return this.panel.getState();
1556
+ }
1557
+ restorePanelState(state) {
1558
+ this.panel.restoreState(state);
1559
+ }
817
1560
  destroy() {
818
1561
  document.removeEventListener("keydown", this.onKeyDown);
819
1562
  window.removeEventListener("aa:toggle", this.onToolbarToggle);
@@ -824,19 +1567,37 @@ var Overlay = class {
824
1567
  this.highlighter.destroy();
825
1568
  this.form.destroy();
826
1569
  this.pinManager.destroy();
1570
+ this.panel.destroy();
827
1571
  this.host.remove();
828
1572
  }
829
1573
  };
830
1574
 
831
1575
  // src/client/index.ts
832
1576
  var overlay = null;
1577
+ var PANEL_STATE_KEY = "aa-panel-state";
1578
+ function savePanelState(state) {
1579
+ sessionStorage.setItem(PANEL_STATE_KEY, JSON.stringify(state));
1580
+ }
833
1581
  function init() {
834
1582
  if (overlay) {
1583
+ savePanelState(overlay.getPanelState());
835
1584
  overlay.destroy();
836
1585
  overlay = null;
837
1586
  }
838
1587
  overlay = new Overlay();
1588
+ const saved = sessionStorage.getItem(PANEL_STATE_KEY);
1589
+ if (saved) {
1590
+ try {
1591
+ overlay.restorePanelState(JSON.parse(saved));
1592
+ } catch {
1593
+ }
1594
+ }
839
1595
  }
1596
+ window.addEventListener("beforeunload", () => {
1597
+ if (overlay) {
1598
+ savePanelState(overlay.getPanelState());
1599
+ }
1600
+ });
840
1601
  document.addEventListener("astro:page-load", init);
841
1602
  if (document.readyState === "loading") {
842
1603
  document.addEventListener("DOMContentLoaded", () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/styles.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 /* 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 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 { 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 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.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 // Listen for toggle from Dev Toolbar\n window.addEventListener('aa:toggle', this.onToolbarToggle);\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 onToolbarToggle = ((e: CustomEvent) => {\n this.setActive(e.detail.active);\n }) as EventListener;\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 const newActive = !this.active;\n this.setActive(newActive);\n window.dispatchEvent(new CustomEvent('aa:state-changed', { detail: { active: newActive } }));\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.setActive(false);\n window.dispatchEvent(new CustomEvent('aa:state-changed', { detail: { active: 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 const openCount = this.annotations.filter((a) => a.status === 'open').length;\n window.dispatchEvent(new CustomEvent('aa:count', { detail: { count: openCount } }));\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 window.removeEventListener('aa:toggle', this.onToolbarToggle);\n if (this.active) {\n window.dispatchEvent(new CustomEvent('aa:state-changed', { detail: { active: false } }));\n }\n this.setActive(false);\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"]}
1
+ {"version":3,"sources":["../src/client/styles.ts","../src/client/selector.ts","../src/client/highlighter.ts","../src/client/utils.ts","../src/client/form.ts","../src/client/pin.ts","../src/client/panel.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 /* 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 border: 1px solid rgba(0, 0, 0, 0.08);\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 border: 1px solid rgba(0, 0, 0, 0.08);\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 /* Annotation Panel */\n .aa-panel {\n position: fixed;\n top: 16px;\n right: 16px;\n width: 360px;\n height: calc(100vh - 32px);\n background: #fff;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);\n border: 1px solid rgba(0, 0, 0, 0.08);\n border-radius: 12px;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n z-index: 2147483647;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #1a1a2e;\n }\n\n .aa-panel.aa-panel-left {\n right: auto;\n left: 16px;\n box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15);\n }\n\n .aa-panel-header {\n background: #1a1a2e;\n color: #fff;\n padding: 14px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-shrink: 0;\n }\n\n .aa-panel-title {\n font-size: 14px;\n font-weight: 600;\n }\n\n .aa-panel-header-actions {\n display: flex;\n gap: 4px;\n }\n\n .aa-panel-header-actions button {\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n font-size: 16px;\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n opacity: 0.7;\n }\n\n .aa-panel-header-actions button:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n }\n\n .aa-panel-filters {\n display: flex;\n background: #f8f8fa;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n }\n\n .aa-panel-filters button {\n flex: 1;\n padding: 10px 8px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: #666;\n transition: color 0.15s, border-color 0.15s;\n }\n\n .aa-panel-filters button:hover {\n color: #1a1a2e;\n background: #f0f0f2;\n }\n\n .aa-panel-filters button.aa-active {\n color: #e94560;\n border-bottom-color: #e94560;\n }\n\n .aa-panel-bulk {\n padding: 10px 16px;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n }\n\n .aa-panel-bulk-btn {\n width: 100%;\n padding: 7px 12px;\n border: 1px solid #2ecc71;\n border-radius: 6px;\n background: transparent;\n color: #2ecc71;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n }\n\n .aa-panel-bulk-btn:hover {\n background: #2ecc71;\n color: #fff;\n }\n\n .aa-panel-bulk-btn[disabled] {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .aa-panel-list {\n flex: 1;\n overflow-y: auto;\n }\n\n .aa-panel-item {\n padding: 12px 16px;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.1s;\n }\n\n .aa-panel-item:hover {\n background: #fafafa;\n }\n\n .aa-panel-item.aa-panel-item-resolved {\n opacity: 0.6;\n }\n\n .aa-panel-item-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 4px;\n font-size: 12px;\n }\n\n .aa-panel-item-number {\n font-weight: 700;\n color: #e94560;\n }\n\n .aa-panel-item-number.aa-panel-item-number-resolved {\n color: #2ecc71;\n }\n\n .aa-panel-item-author {\n font-weight: 500;\n }\n\n .aa-panel-item-time {\n color: #999;\n margin-left: auto;\n font-size: 11px;\n }\n\n .aa-panel-item-selector {\n font-family: 'SF Mono', Monaco, monospace;\n font-size: 11px;\n color: #666;\n background: #f5f5f5;\n padding: 3px 6px;\n border-radius: 3px;\n margin-bottom: 6px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .aa-panel-item-text {\n font-size: 13px;\n white-space: pre-wrap;\n word-break: break-word;\n margin-bottom: 8px;\n color: #333;\n }\n\n .aa-panel-item-actions {\n display: flex;\n gap: 6px;\n }\n\n .aa-panel-item-actions button {\n padding: 3px 10px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n background: #fff;\n font-size: 11px;\n cursor: pointer;\n transition: all 0.15s;\n color: #555;\n }\n\n .aa-panel-item-actions button:hover {\n background: #f5f5f5;\n border-color: #ccc;\n }\n\n .aa-panel-edit-textarea {\n width: 100%;\n min-height: 60px;\n max-height: 40vh;\n padding: 8px;\n border: 1px solid #e94560;\n border-radius: 4px;\n font-size: 13px;\n font-family: inherit;\n resize: vertical;\n outline: none;\n overflow-y: auto;\n margin-bottom: 6px;\n }\n\n .aa-panel-empty {\n padding: 40px 16px;\n text-align: center;\n color: #999;\n font-size: 13px;\n }\n\n /* Floating Action Button */\n .aa-panel-fab {\n position: fixed;\n bottom: 72px;\n right: 16px;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: #1a1a2e;\n color: #fff;\n cursor: pointer;\n border: 1px solid rgba(255, 255, 255, 0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n z-index: 2147483647;\n transition: background 0.15s;\n }\n\n .aa-panel-fab:hover {\n background: #2a2a40;\n }\n\n .aa-panel-fab-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: #e94560;\n color: #fff;\n font-size: 9px;\n font-weight: 700;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\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 border-color: rgba(255, 255, 255, 0.1);\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 .aa-panel {\n background: #2d2d3f;\n color: #e0e0e0;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.4);\n border-color: rgba(255, 255, 255, 0.1);\n }\n\n .aa-panel.aa-panel-left {\n box-shadow: 4px 0 24px rgba(0, 0, 0, 0.4);\n }\n\n .aa-panel-filters {\n background: #252538;\n border-bottom-color: #404060;\n }\n\n .aa-panel-filters button {\n color: #aaa;\n }\n\n .aa-panel-filters button:hover {\n color: #e0e0e0;\n background: #353550;\n }\n\n .aa-panel-bulk {\n border-bottom-color: #404060;\n }\n\n .aa-panel-bulk-btn {\n color: #2ecc71;\n border-color: #2ecc71;\n background: transparent;\n }\n\n .aa-panel-bulk-btn:hover {\n background: #2ecc71;\n color: #fff;\n }\n\n .aa-panel-item {\n border-bottom-color: #404060;\n }\n\n .aa-panel-item:hover {\n background: #353550;\n }\n\n .aa-panel-item-time {\n color: #888;\n }\n\n .aa-panel-item-selector {\n background: #1a1a2e;\n color: #aaa;\n }\n\n .aa-panel-item-text {\n color: #ddd;\n }\n\n .aa-panel-item-actions button {\n border-color: #404060;\n color: #ccc;\n background: #2d2d3f;\n }\n\n .aa-panel-item-actions button:hover {\n background: #404060;\n }\n\n .aa-panel-edit-textarea {\n background: #1a1a2e;\n color: #e0e0e0;\n border-color: #e94560;\n }\n\n .aa-panel-empty {\n color: #888;\n }\n\n .aa-panel-fab {\n background: #2d2d3f;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);\n }\n\n .aa-panel-fab:hover {\n background: #404060;\n }\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","export function escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n","import type { CreateAnnotationPayload } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\nimport { generateSelector, getElementText } from './selector.js';\nimport { escapeHtml } from './utils.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\">${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\n destroy(): void {\n this.container.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\nimport { escapeHtml } from './utils.js';\n\nexport class PinManager {\n private pins: HTMLElement[] = [];\n private detailPopup: HTMLElement;\n private onChanged: () => void;\n private panelSide: 'left' | 'right' | null = null;\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[], panelSide: 'left' | 'right' | null = null): void {\n this.panelSide = panelSide;\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 — shift away from panel side and FAB\n const updatePosition = () => {\n const rect = el.getBoundingClientRect();\n pin.style.position = 'fixed';\n const pinTop = Math.max(0, rect.top - 10);\n let pinLeft: number;\n if (this.panelSide === 'right') {\n pinLeft = Math.max(10, rect.left - 32);\n } else {\n pinLeft = Math.max(10, rect.right - 24);\n }\n\n // FAB collision check (FAB: bottom: 72px, right: 16px, 32×32)\n const fabLeft = window.innerWidth - 48;\n const fabTop = window.innerHeight - 104;\n const fabRight = window.innerWidth - 16;\n const fabBottom = window.innerHeight - 72;\n\n if (pinTop + 28 > fabTop && pinTop < fabBottom &&\n pinLeft + 28 > fabLeft && pinLeft < fabRight) {\n pinLeft = fabLeft - 32;\n }\n\n pin.style.top = `${pinTop}px`;\n pin.style.left = `${pinLeft}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} — ${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\">${escapeHtml(annotation.text)}</div>\n <div class=\"aa-pin-detail-info\">\n <div class=\"aa-pin-detail-selector\">${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 destroy(): void {\n this.clearPins();\n this.detailPopup.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\nimport { escapeHtml } from './utils.js';\n\ntype FilterValue = 'all' | 'open' | 'resolved';\ntype SideValue = 'right' | 'left';\n\nexport class AnnotationPanel {\n private container: HTMLElement;\n private fab: HTMLElement;\n private visible = false;\n private filter: FilterValue = 'open';\n private side: SideValue = 'right';\n private editingId: string | null = null;\n private annotations: Annotation[] = [];\n private indexMap: Map<string, number> = new Map();\n private onChanged: () => void;\n private onVisibilityChanged: () => void;\n\n constructor(\n private shadowRoot: ShadowRoot,\n onChanged: () => void,\n onVisibilityChanged: () => void = () => {},\n ) {\n this.onChanged = onChanged;\n this.onVisibilityChanged = onVisibilityChanged;\n\n // Panel container\n this.container = document.createElement('div');\n this.container.className = 'aa-panel';\n this.container.style.display = 'none';\n this.container.addEventListener('click', this.onClick);\n this.container.addEventListener('keydown', this.onKeyDown);\n this.shadowRoot.appendChild(this.container);\n\n // Floating action button\n this.fab = document.createElement('button');\n this.fab.className = 'aa-panel-fab';\n this.fab.addEventListener('click', () => this.toggle());\n this.shadowRoot.appendChild(this.fab);\n\n this.renderFab();\n }\n\n show(): void {\n this.visible = true;\n this.container.style.display = 'flex';\n this.render();\n this.onVisibilityChanged();\n }\n\n hide(): void {\n this.visible = false;\n this.editingId = null;\n this.container.style.display = 'none';\n this.onVisibilityChanged();\n }\n\n toggle(): void {\n if (this.visible) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n isVisible(): boolean {\n return this.visible;\n }\n\n isEditing(): boolean {\n return this.editingId !== null;\n }\n\n getSide(): SideValue {\n return this.side;\n }\n\n getState(): { visible: boolean; filter: FilterValue; side: SideValue } {\n return { visible: this.visible, filter: this.filter, side: this.side };\n }\n\n restoreState(state: { visible: boolean; filter: string; side: string }): void {\n this.filter = (state.filter as FilterValue) || 'open';\n this.side = (state.side as SideValue) || 'right';\n if (state.visible) {\n this.show();\n }\n }\n\n update(annotations: Annotation[]): void {\n this.annotations = annotations;\n this.rebuildIndexMap();\n this.renderFab();\n if (this.visible) {\n this.render();\n }\n }\n\n destroy(): void {\n this.container.removeEventListener('click', this.onClick);\n this.container.removeEventListener('keydown', this.onKeyDown);\n this.container.remove();\n this.fab.remove();\n }\n\n // --- Private ---\n\n private rebuildIndexMap(): void {\n this.indexMap.clear();\n this.annotations.forEach((a, i) => {\n this.indexMap.set(a.id, i + 1);\n });\n }\n\n private getFiltered(): Annotation[] {\n if (this.filter === 'all') return this.annotations;\n return this.annotations.filter((a) => a.status === this.filter);\n }\n\n private countByStatus(status: 'open' | 'resolved'): number {\n return this.annotations.filter((a) => a.status === status).length;\n }\n\n private render(): void {\n const filtered = this.getFiltered();\n const openCount = this.countByStatus('open');\n const resolvedCount = this.countByStatus('resolved');\n const totalCount = this.annotations.length;\n\n this.container.className = `aa-panel${this.side === 'left' ? ' aa-panel-left' : ''}`;\n\n const sideIcon = this.side === 'right'\n ? '<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"><line x1=\"3\" y1=\"3\" x2=\"3\" y2=\"13\"/><polyline points=\"12,5 8,8 12,11\"/></svg>'\n : '<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"><line x1=\"13\" y1=\"3\" x2=\"13\" y2=\"13\"/><polyline points=\"4,5 8,8 4,11\"/></svg>';\n\n this.container.innerHTML = `\n <div class=\"aa-panel-header\">\n <span class=\"aa-panel-title\">Annotations (${totalCount})</span>\n <div class=\"aa-panel-header-actions\">\n <button data-action=\"toggle-side\" title=\"Move panel\">${sideIcon}</button>\n <button data-action=\"close-panel\" title=\"Close panel\">&times;</button>\n </div>\n </div>\n <div class=\"aa-panel-filters\">\n <button data-action=\"filter\" data-filter=\"open\" class=\"${this.filter === 'open' ? 'aa-active' : ''}\">Open (${openCount})</button>\n <button data-action=\"filter\" data-filter=\"resolved\" class=\"${this.filter === 'resolved' ? 'aa-active' : ''}\">Resolved (${resolvedCount})</button>\n <button data-action=\"filter\" data-filter=\"all\" class=\"${this.filter === 'all' ? 'aa-active' : ''}\">All (${totalCount})</button>\n </div>\n ${this.filter === 'open' && openCount > 0 ? `\n <div class=\"aa-panel-bulk\">\n <button class=\"aa-panel-bulk-btn\" data-action=\"bulk-resolve\">Mark all as done</button>\n </div>\n ` : ''}\n <div class=\"aa-panel-list\">\n ${filtered.length > 0\n ? filtered.map((a) => this.renderItem(a)).join('')\n : '<div class=\"aa-panel-empty\">No annotations match this filter.</div>'\n }\n </div>\n `;\n\n // Focus textarea if editing + auto-grow\n if (this.editingId) {\n const textarea = this.container.querySelector('.aa-panel-edit-textarea') as HTMLTextAreaElement | null;\n if (textarea) {\n setTimeout(() => {\n textarea.focus();\n textarea.style.height = 'auto';\n textarea.style.height = Math.min(textarea.scrollHeight, window.innerHeight * 0.4) + 'px';\n }, 30);\n }\n }\n }\n\n private renderItem(annotation: Annotation): string {\n const num = this.indexMap.get(annotation.id) ?? 0;\n const isResolved = annotation.status === 'resolved';\n const isEditing = this.editingId === annotation.id;\n\n const numberClass = isResolved ? 'aa-panel-item-number aa-panel-item-number-resolved' : 'aa-panel-item-number';\n\n const textBlock = isEditing\n ? `<textarea class=\"aa-panel-edit-textarea\" data-id=\"${annotation.id}\">${escapeHtml(annotation.text)}</textarea>\n <div class=\"aa-panel-item-actions\">\n <button data-action=\"save-edit\" data-id=\"${annotation.id}\">Save</button>\n <button data-action=\"cancel-edit\" data-id=\"${annotation.id}\">Cancel</button>\n </div>`\n : `<div class=\"aa-panel-item-text\">${escapeHtml(annotation.text)}</div>\n <div class=\"aa-panel-item-actions\">\n <button data-action=\"locate\" data-id=\"${annotation.id}\" data-selector=\"${escapeHtml(annotation.selector)}\">Locate</button>\n <button data-action=\"edit-inline\" data-id=\"${annotation.id}\">Edit</button>\n ${isResolved\n ? `<button data-action=\"reopen\" data-id=\"${annotation.id}\">Reopen</button>`\n : `<button data-action=\"resolve\" data-id=\"${annotation.id}\">Done</button>`\n }\n </div>`;\n\n return `\n <div class=\"aa-panel-item${isResolved ? ' aa-panel-item-resolved' : ''}\" data-id=\"${annotation.id}\">\n <div class=\"aa-panel-item-header\">\n <span class=\"${numberClass}\">#${num}</span>\n <span class=\"aa-panel-item-author\">${escapeHtml(annotation.author)}</span>\n <span class=\"aa-panel-item-time\">${this.formatTimeAgo(annotation.timestamp)}</span>\n </div>\n <div class=\"aa-panel-item-selector\">${escapeHtml(annotation.selector)}</div>\n ${textBlock}\n </div>\n `;\n }\n\n private renderFab(): void {\n const openCount = this.countByStatus('open');\n const badge = openCount > 0\n ? `<span class=\"aa-panel-fab-badge\">${openCount}</span>`\n : '';\n this.fab.innerHTML = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\">\n <line x1=\"2\" y1=\"4\" x2=\"14\" y2=\"4\"/><line x1=\"2\" y1=\"8\" x2=\"14\" y2=\"8\"/><line x1=\"2\" y1=\"12\" x2=\"10\" y2=\"12\"/>\n </svg>\n ${badge}\n `;\n }\n\n // --- Event delegation ---\n\n private onClick = (e: MouseEvent): void => {\n const target = e.target as HTMLElement;\n const actionEl = target.closest<HTMLElement>('[data-action]');\n if (!actionEl) return;\n\n const action = actionEl.dataset.action;\n const id = actionEl.dataset.id;\n\n switch (action) {\n case 'close-panel':\n this.hide();\n break;\n case 'toggle-side':\n this.side = this.side === 'right' ? 'left' : 'right';\n this.render();\n this.onVisibilityChanged();\n break;\n case 'filter':\n this.filter = (actionEl.dataset.filter as FilterValue) || 'all';\n this.editingId = null;\n this.render();\n break;\n case 'bulk-resolve':\n this.bulkResolve(actionEl);\n break;\n case 'locate':\n if (actionEl.dataset.selector) this.locateElement(actionEl.dataset.selector);\n break;\n case 'edit-inline':\n if (id) {\n this.editingId = id;\n this.render();\n }\n break;\n case 'cancel-edit':\n this.editingId = null;\n this.render();\n break;\n case 'save-edit':\n if (id) this.saveEdit(id);\n break;\n case 'resolve':\n if (id) this.updateStatus(id, 'resolved');\n break;\n case 'reopen':\n if (id) this.updateStatus(id, 'open');\n break;\n }\n };\n\n private onKeyDown = (e: KeyboardEvent): void => {\n // Ctrl+Enter inside edit textarea -> save\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey) && this.editingId) {\n e.preventDefault();\n this.saveEdit(this.editingId);\n }\n };\n\n // --- Actions ---\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 if (!res.ok) throw new Error('Failed to update');\n this.onChanged();\n } catch (err) {\n console.error('[astro-annotate] Failed to update annotation:', err);\n }\n }\n\n private async saveEdit(id: string): Promise<void> {\n const textarea = this.container.querySelector(`.aa-panel-edit-textarea[data-id=\"${id}\"]`) as HTMLTextAreaElement | null;\n if (!textarea) return;\n\n const text = textarea.value.trim();\n if (!text) return;\n\n try {\n const res = await fetch(`${API_ANNOTATIONS}/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text }),\n });\n if (!res.ok) throw new Error('Failed to update');\n this.editingId = null;\n this.onChanged();\n } catch (err) {\n console.error('[astro-annotate] Failed to update annotation text:', err);\n }\n }\n\n private async bulkResolve(btn: HTMLElement): Promise<void> {\n const openAnnotations = this.annotations.filter((a) => a.status === 'open');\n const total = openAnnotations.length;\n if (total === 0) return;\n\n btn.setAttribute('disabled', '');\n\n // Sequential PATCH: intentional — local JSON storage has no write locking,\n // so parallel writes would cause data loss via read-modify-write races.\n for (let i = 0; i < openAnnotations.length; i++) {\n btn.textContent = `Resolving ${i + 1}/${total}...`;\n try {\n const res = await fetch(`${API_ANNOTATIONS}/${openAnnotations[i].id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'resolved' }),\n });\n if (!res.ok) throw new Error('Failed to update');\n } catch (err) {\n console.error('[astro-annotate] Failed to resolve annotation:', err);\n }\n }\n\n this.onChanged();\n }\n\n private locateElement(selector: string): void {\n const el = document.querySelector(selector);\n if (!el) return;\n\n el.scrollIntoView({ behavior: 'instant', block: 'center' });\n\n // Flash highlight — rendered in Shadow DOM to avoid host CSS interference\n // and ensure cleanup on destroy/view transitions\n const flash = document.createElement('div');\n const rect = el.getBoundingClientRect();\n Object.assign(flash.style, {\n position: 'fixed',\n top: `${rect.top}px`,\n left: `${rect.left}px`,\n width: `${rect.width}px`,\n height: `${rect.height}px`,\n border: '2px solid #e94560',\n background: 'rgba(233, 69, 96, 0.12)',\n borderRadius: '3px',\n pointerEvents: 'none',\n zIndex: '2147483646',\n transition: 'opacity 0.6s ease',\n });\n this.shadowRoot.appendChild(flash);\n\n setTimeout(() => {\n flash.style.opacity = '0';\n }, 700);\n setTimeout(() => {\n flash.remove();\n }, 1300);\n }\n\n // --- Helpers ---\n\n private formatTimeAgo(timestamp: string): string {\n const now = Date.now();\n const then = new Date(timestamp).getTime();\n if (isNaN(then)) return 'unknown';\n const diffSec = Math.floor((now - then) / 1000);\n\n if (diffSec < 60) return 'just now';\n const diffMin = Math.floor(diffSec / 60);\n if (diffMin < 60) return `${diffMin}m ago`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24) return `${diffHr}h ago`;\n const diffDay = Math.floor(diffHr / 24);\n if (diffDay < 30) return `${diffDay}d ago`;\n return new Date(timestamp).toLocaleDateString();\n }\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 { Highlighter } from './highlighter.js';\nimport { AnnotationForm } from './form.js';\nimport { PinManager } from './pin.js';\nimport { AnnotationPanel } from './panel.js';\n\nexport class Overlay {\n private host: HTMLElement;\n private shadowRoot: ShadowRoot;\n private highlighter: Highlighter;\n private form: AnnotationForm;\n private pinManager: PinManager;\n private panel: AnnotationPanel;\n private active = false;\n private devMode = !!(window as any).__ASTRO_ANNOTATE_DEV__;\n private annotations: Annotation[] = [];\n private lastOpenedUI: 'panel' | 'form' | null = null;\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.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 this.panel = new AnnotationPanel(\n this.shadowRoot,\n () => this.loadAnnotations(),\n () => this.renderPins(),\n );\n\n // Listen for toggle from Dev Toolbar\n window.addEventListener('aa:toggle', this.onToolbarToggle);\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 onToolbarToggle = ((e: CustomEvent) => {\n this.setActive(e.detail.active);\n }) as EventListener;\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 this.lastOpenedUI = 'form';\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 const newActive = !this.active;\n this.setActive(newActive);\n window.dispatchEvent(new CustomEvent('aa:state-changed', { detail: { active: newActive } }));\n return;\n }\n\n // Alt+L: toggle annotation panel\n if (e.altKey && e.code === 'KeyL' && !isExternalInput) {\n if (this.panel.isEditing()) return;\n e.preventDefault();\n this.panel.toggle();\n this.lastOpenedUI = this.panel.isVisible() ? 'panel' : null;\n return;\n }\n\n // Escape: close most recently opened UI first\n if (e.key === 'Escape') {\n if (this.lastOpenedUI === 'form' && this.form.isVisible()) {\n this.form.hide();\n this.lastOpenedUI = this.panel.isVisible() ? 'panel' : null;\n return;\n }\n if (this.lastOpenedUI === 'panel' && this.panel.isVisible()) {\n this.panel.hide();\n this.lastOpenedUI = this.form.isVisible() ? 'form' : null;\n return;\n }\n // Fallback: close whatever is visible\n if (this.panel.isVisible()) { this.panel.hide(); return; }\n if (this.form.isVisible()) { this.form.hide(); return; }\n if (this.active) {\n this.setActive(false);\n window.dispatchEvent(new CustomEvent('aa:state-changed', { detail: { active: 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 const openCount = this.annotations.filter((a) => a.status === 'open').length;\n window.dispatchEvent(new CustomEvent('aa:count', { detail: { count: openCount } }));\n this.renderPins();\n this.panel.update(this.annotations);\n } catch {\n // API not available yet, will retry\n }\n }\n\n private renderPins(): void {\n const panelSide = this.panel.isVisible() ? this.panel.getSide() : null;\n this.pinManager.render(this.annotations, panelSide);\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 getPanelState(): { visible: boolean; filter: string; side: string } {\n return this.panel.getState();\n }\n\n restorePanelState(state: { visible: boolean; filter: string; side: string }): void {\n this.panel.restoreState(state);\n }\n\n destroy(): void {\n document.removeEventListener('keydown', this.onKeyDown);\n window.removeEventListener('aa:toggle', this.onToolbarToggle);\n if (this.active) {\n window.dispatchEvent(new CustomEvent('aa:state-changed', { detail: { active: false } }));\n }\n this.setActive(false);\n this.highlighter.destroy();\n this.form.destroy();\n this.pinManager.destroy();\n this.panel.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;\nconst PANEL_STATE_KEY = 'aa-panel-state';\n\nfunction savePanelState(state: { visible: boolean; filter: string; side: string }): void {\n sessionStorage.setItem(PANEL_STATE_KEY, JSON.stringify(state));\n}\n\nfunction init(): void {\n // Preserve panel state before destroying (View Transitions)\n if (overlay) {\n savePanelState(overlay.getPanelState());\n overlay.destroy();\n overlay = null;\n }\n\n overlay = new Overlay();\n\n // Restore from sessionStorage (works for both VT and full reload)\n const saved = sessionStorage.getItem(PANEL_STATE_KEY);\n if (saved) {\n try {\n overlay.restorePanelState(JSON.parse(saved));\n } catch { /* ignore corrupt data */ }\n }\n}\n\n// Save panel state before full page reload (F5, tab close)\nwindow.addEventListener('beforeunload', () => {\n if (overlay) {\n savePanelState(overlay.getPanelState());\n }\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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-annotate",
3
- "version": "0.3.0",
3
+ "version": "0.4.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",