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 +775 -14
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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">${
|
|
876
|
+
<div class="aa-form-header-selector">${escapeHtml(selector)}</div>
|
|
503
877
|
</div>
|
|
504
878
|
<button class="aa-form-close" data-action="close">×</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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
-
|
|
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
|
-
|
|
604
|
-
|
|
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 ${
|
|
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">×</button>
|
|
640
1027
|
</div>
|
|
641
1028
|
<div class="aa-pin-detail-body">
|
|
642
|
-
<div class="aa-pin-detail-text">${
|
|
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">${
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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">×</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.
|
|
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", () => {
|
package/dist/client.js.map
CHANGED
|
@@ -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\">×</button>\n </div>\n <div class=\"aa-form-body\">\n ${this.devMode ? '' : '<input class=\"aa-input\" type=\"text\" placeholder=\"Your name\" data-field=\"author\" value=\"\" />'}\n <textarea class=\"aa-textarea\" placeholder=\"What should be changed?\" data-field=\"text\"></textarea>\n <div class=\"aa-form-actions\">\n <button class=\"aa-btn aa-btn-secondary\" data-action=\"close\">Cancel</button>\n <button class=\"aa-btn aa-btn-primary\" data-action=\"submit\">Submit</button>\n </div>\n </div>\n `;\n\n // Focus textarea\n const textarea = this.container.querySelector('[data-field=\"text\"]') as HTMLTextAreaElement;\n setTimeout(() => textarea?.focus(), 50);\n\n // Event listeners\n this.container.querySelectorAll('[data-action=\"close\"]').forEach((btn) => {\n btn.addEventListener('click', () => this.hide());\n });\n\n const submitBtn = this.container.querySelector('[data-action=\"submit\"]') as HTMLButtonElement;\n submitBtn.addEventListener('click', () => {\n this.submit(selector, elementTag, elementText);\n });\n\n // Submit on Ctrl+Enter\n textarea.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n this.submit(selector, elementTag, elementText);\n }\n });\n }\n\n private async submit(selector: string, elementTag: string, elementText: string): Promise<void> {\n const text = (this.container.querySelector('[data-field=\"text\"]') as HTMLTextAreaElement)?.value?.trim();\n const author = this.devMode\n ? 'Developer'\n : (this.container.querySelector('[data-field=\"author\"]') as HTMLInputElement)?.value?.trim();\n\n if (!text) return;\n\n const submitBtn = this.container.querySelector('[data-action=\"submit\"]') as HTMLButtonElement;\n submitBtn.disabled = true;\n submitBtn.textContent = 'Saving...';\n\n const payload: CreateAnnotationPayload = {\n page: window.location.pathname,\n selector,\n elementTag,\n elementText,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n device: window.innerWidth < 768 ? 'mobile' : window.innerWidth < 1024 ? 'tablet' : 'desktop',\n text,\n author: author || 'Anonymous',\n };\n\n try {\n const res = await fetch(API_ANNOTATIONS, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n\n if (!res.ok) throw new Error('Failed to save');\n\n this.hide();\n this.onSubmitted();\n } catch (err) {\n submitBtn.disabled = false;\n submitBtn.textContent = 'Submit';\n console.error('[astro-annotate] Failed to save annotation:', err);\n }\n }\n\n hide(): void {\n if (this.container.style.display === 'none') return;\n this.container.style.display = 'none';\n this.container.innerHTML = '';\n this.onClosed();\n }\n\n isVisible(): boolean {\n return this.container.style.display !== 'none';\n }\n\n private escapeHtml(str: string): string {\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n destroy(): void {\n this.container.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { API_ANNOTATIONS } from '../constants.js';\n\nexport class PinManager {\n private pins: HTMLElement[] = [];\n private detailPopup: HTMLElement;\n private onChanged: () => void;\n\n constructor(\n private shadowRoot: ShadowRoot,\n onChanged: () => void,\n ) {\n this.detailPopup = document.createElement('div');\n this.detailPopup.className = 'aa-pin-detail';\n this.detailPopup.style.display = 'none';\n this.shadowRoot.appendChild(this.detailPopup);\n this.onChanged = onChanged;\n }\n\n render(annotations: Annotation[]): void {\n this.clearPins();\n\n annotations.forEach((annotation, index) => {\n const el = document.querySelector(annotation.selector);\n if (!el) return;\n\n const pin = document.createElement('div');\n pin.className = `aa-pin${annotation.status === 'resolved' ? ' aa-resolved' : ''}`;\n pin.innerHTML = `<span class=\"aa-pin-number\">${index + 1}</span>`;\n\n // Position pin at top-right corner of element\n const updatePosition = () => {\n const rect = el.getBoundingClientRect();\n pin.style.position = 'fixed';\n pin.style.top = `${Math.max(0, rect.top - 10)}px`;\n pin.style.left = `${Math.max(0, rect.right - 24)}px`;\n };\n updatePosition();\n\n pin.addEventListener('click', (e) => {\n e.stopPropagation();\n this.showDetail(annotation, index, el);\n });\n\n this.shadowRoot.appendChild(pin);\n this.pins.push(pin);\n\n // Update position on scroll/resize\n const observer = new IntersectionObserver(() => updatePosition(), { threshold: 0 });\n observer.observe(el);\n });\n }\n\n private showDetail(annotation: Annotation, index: number, el: Element): void {\n const rect = el.getBoundingClientRect();\n let top = rect.bottom + 8;\n let left = rect.left;\n\n if (top + 250 > window.innerHeight) {\n top = rect.top - 258;\n }\n if (left + 320 > window.innerWidth) {\n left = window.innerWidth - 330;\n }\n if (top < 10) top = 10;\n if (left < 10) left = 10;\n\n this.detailPopup.style.top = `${top}px`;\n this.detailPopup.style.left = `${left}px`;\n this.detailPopup.style.display = 'block';\n\n const date = new Date(annotation.timestamp).toLocaleString();\n\n this.detailPopup.innerHTML = `\n <div class=\"aa-pin-detail-header\">\n <div>\n <div class=\"aa-form-header-title\">#${index + 1} — ${this.escapeHtml(annotation.author)}</div>\n <div class=\"aa-pin-detail-meta\">${date} · ${annotation.device} · ${annotation.status}</div>\n </div>\n <button class=\"aa-form-close\" data-action=\"close-detail\">×</button>\n </div>\n <div class=\"aa-pin-detail-body\">\n <div class=\"aa-pin-detail-text\">${this.escapeHtml(annotation.text)}</div>\n <div class=\"aa-pin-detail-info\">\n <div class=\"aa-pin-detail-selector\">${this.escapeHtml(annotation.selector)}</div>\n </div>\n </div>\n <div class=\"aa-pin-detail-actions\">\n ${this.getStatusButtons(annotation)}\n </div>\n `;\n\n this.detailPopup.querySelector('[data-action=\"close-detail\"]')?.addEventListener('click', () => {\n this.hideDetail();\n });\n\n this.detailPopup.querySelectorAll('[data-status]').forEach((btn) => {\n btn.addEventListener('click', () => {\n const status = (btn as HTMLElement).dataset.status!;\n this.updateStatus(annotation.id, status as Annotation['status']);\n });\n });\n }\n\n private getStatusButtons(annotation: Annotation): string {\n if (annotation.status === 'open') {\n return `\n <button class=\"aa-status-btn aa-resolve\" data-status=\"resolved\">Done</button>\n `;\n }\n return `<button class=\"aa-status-btn aa-reopen\" data-status=\"open\">Reopen</button>`;\n }\n\n private async updateStatus(id: string, status: Annotation['status']): Promise<void> {\n try {\n const res = await fetch(`${API_ANNOTATIONS}/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status }),\n });\n\n if (!res.ok) throw new Error('Failed to update');\n\n this.hideDetail();\n this.onChanged();\n } catch (err) {\n console.error('[astro-annotate] Failed to update annotation:', err);\n }\n }\n\n hideDetail(): void {\n this.detailPopup.style.display = 'none';\n }\n\n private clearPins(): void {\n this.pins.forEach((pin) => pin.remove());\n this.pins = [];\n this.hideDetail();\n }\n\n private escapeHtml(str: string): string {\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n destroy(): void {\n this.clearPins();\n this.detailPopup.remove();\n }\n}\n","import type { Annotation } from '../types.js';\nimport { SHADOW_ROOT_ID, API_ANNOTATIONS } from '../constants.js';\nimport { OVERLAY_STYLES } from './styles.js';\nimport { 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, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\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\">×</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\">×</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\">×</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
|
+
"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",
|