astro-annotate 0.2.1 → 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 +788 -132
- package/dist/client.js.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/toolbar-app.js +25 -0
- package/dist/toolbar-app.js.map +1 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -21,70 +21,6 @@ var OVERLAY_STYLES = `
|
|
|
21
21
|
padding: 0;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/* Toolbar */
|
|
25
|
-
.aa-toolbar {
|
|
26
|
-
position: fixed;
|
|
27
|
-
bottom: 20px;
|
|
28
|
-
right: 20px;
|
|
29
|
-
display: flex;
|
|
30
|
-
align-items: center;
|
|
31
|
-
gap: 8px;
|
|
32
|
-
background: #1a1a2e;
|
|
33
|
-
color: #fff;
|
|
34
|
-
padding: 8px 16px;
|
|
35
|
-
border-radius: 50px;
|
|
36
|
-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
37
|
-
border: 1.5px solid rgba(255, 255, 255, 0.2);
|
|
38
|
-
cursor: pointer;
|
|
39
|
-
user-select: none;
|
|
40
|
-
transition: all 0.2s ease;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.aa-toolbar:hover {
|
|
44
|
-
background: #16213e;
|
|
45
|
-
transform: translateY(-1px);
|
|
46
|
-
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
|
|
47
|
-
border-color: rgba(255, 255, 255, 0.35);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.aa-toolbar.aa-active {
|
|
51
|
-
background: #e94560;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.aa-toolbar.aa-active:hover {
|
|
55
|
-
background: #c73e54;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.aa-toolbar-icon {
|
|
59
|
-
width: 20px;
|
|
60
|
-
height: 20px;
|
|
61
|
-
flex-shrink: 0;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.aa-toolbar-label {
|
|
65
|
-
font-size: 13px;
|
|
66
|
-
font-weight: 500;
|
|
67
|
-
white-space: nowrap;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.aa-badge {
|
|
71
|
-
background: #e94560;
|
|
72
|
-
color: #fff;
|
|
73
|
-
font-size: 11px;
|
|
74
|
-
font-weight: 700;
|
|
75
|
-
min-width: 20px;
|
|
76
|
-
height: 20px;
|
|
77
|
-
border-radius: 10px;
|
|
78
|
-
display: flex;
|
|
79
|
-
align-items: center;
|
|
80
|
-
justify-content: center;
|
|
81
|
-
padding: 0 6px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.aa-toolbar.aa-active .aa-badge {
|
|
85
|
-
background: rgba(255, 255, 255, 0.3);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
24
|
/* Element Highlight */
|
|
89
25
|
.aa-highlight {
|
|
90
26
|
position: fixed;
|
|
@@ -119,6 +55,7 @@ var OVERLAY_STYLES = `
|
|
|
119
55
|
background: #fff;
|
|
120
56
|
border-radius: 12px;
|
|
121
57
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
58
|
+
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
122
59
|
width: 340px;
|
|
123
60
|
overflow: hidden;
|
|
124
61
|
}
|
|
@@ -269,6 +206,7 @@ var OVERLAY_STYLES = `
|
|
|
269
206
|
background: #fff;
|
|
270
207
|
border-radius: 12px;
|
|
271
208
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
209
|
+
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
272
210
|
width: 320px;
|
|
273
211
|
overflow: hidden;
|
|
274
212
|
}
|
|
@@ -358,11 +296,288 @@ var OVERLAY_STYLES = `
|
|
|
358
296
|
color: #fff;
|
|
359
297
|
}
|
|
360
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
|
+
|
|
361
575
|
/* Dark mode */
|
|
362
576
|
@media (prefers-color-scheme: dark) {
|
|
363
577
|
.aa-form-container, .aa-pin-detail {
|
|
364
578
|
background: #2d2d3f;
|
|
365
579
|
color: #e0e0e0;
|
|
580
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
366
581
|
}
|
|
367
582
|
|
|
368
583
|
.aa-input, .aa-textarea {
|
|
@@ -393,57 +608,98 @@ var OVERLAY_STYLES = `
|
|
|
393
608
|
.aa-status-btn:hover {
|
|
394
609
|
background: #404060;
|
|
395
610
|
}
|
|
396
|
-
}
|
|
397
|
-
`;
|
|
398
611
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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;
|
|
437
700
|
}
|
|
438
701
|
}
|
|
439
|
-
|
|
440
|
-
this.badge.textContent = String(count);
|
|
441
|
-
this.badge.style.display = count > 0 ? "flex" : "none";
|
|
442
|
-
}
|
|
443
|
-
destroy() {
|
|
444
|
-
this.el.remove();
|
|
445
|
-
}
|
|
446
|
-
};
|
|
702
|
+
`;
|
|
447
703
|
|
|
448
704
|
// src/client/selector.ts
|
|
449
705
|
var ASTRO_CLASS_RE = /^astro-[a-zA-Z0-9]+$/;
|
|
@@ -575,6 +831,11 @@ var Highlighter = class {
|
|
|
575
831
|
}
|
|
576
832
|
};
|
|
577
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
|
+
|
|
578
839
|
// src/client/form.ts
|
|
579
840
|
var AnnotationForm = class {
|
|
580
841
|
constructor(shadowRoot, onSubmitted, onClosed, devMode) {
|
|
@@ -612,7 +873,7 @@ var AnnotationForm = class {
|
|
|
612
873
|
<div class="aa-form-header">
|
|
613
874
|
<div>
|
|
614
875
|
<div class="aa-form-header-title">New Annotation</div>
|
|
615
|
-
<div class="aa-form-header-selector">${
|
|
876
|
+
<div class="aa-form-header-selector">${escapeHtml(selector)}</div>
|
|
616
877
|
</div>
|
|
617
878
|
<button class="aa-form-close" data-action="close">×</button>
|
|
618
879
|
</div>
|
|
@@ -681,9 +942,6 @@ var AnnotationForm = class {
|
|
|
681
942
|
isVisible() {
|
|
682
943
|
return this.container.style.display !== "none";
|
|
683
944
|
}
|
|
684
|
-
escapeHtml(str) {
|
|
685
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
686
|
-
}
|
|
687
945
|
destroy() {
|
|
688
946
|
this.container.remove();
|
|
689
947
|
}
|
|
@@ -702,7 +960,9 @@ var PinManager = class {
|
|
|
702
960
|
pins = [];
|
|
703
961
|
detailPopup;
|
|
704
962
|
onChanged;
|
|
705
|
-
|
|
963
|
+
panelSide = null;
|
|
964
|
+
render(annotations, panelSide = null) {
|
|
965
|
+
this.panelSide = panelSide;
|
|
706
966
|
this.clearPins();
|
|
707
967
|
annotations.forEach((annotation, index) => {
|
|
708
968
|
const el = document.querySelector(annotation.selector);
|
|
@@ -713,8 +973,22 @@ var PinManager = class {
|
|
|
713
973
|
const updatePosition = () => {
|
|
714
974
|
const rect = el.getBoundingClientRect();
|
|
715
975
|
pin.style.position = "fixed";
|
|
716
|
-
|
|
717
|
-
|
|
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`;
|
|
718
992
|
};
|
|
719
993
|
updatePosition();
|
|
720
994
|
pin.addEventListener("click", (e) => {
|
|
@@ -746,15 +1020,15 @@ var PinManager = class {
|
|
|
746
1020
|
this.detailPopup.innerHTML = `
|
|
747
1021
|
<div class="aa-pin-detail-header">
|
|
748
1022
|
<div>
|
|
749
|
-
<div class="aa-form-header-title">#${index + 1} \u2014 ${
|
|
1023
|
+
<div class="aa-form-header-title">#${index + 1} \u2014 ${escapeHtml(annotation.author)}</div>
|
|
750
1024
|
<div class="aa-pin-detail-meta">${date} \xB7 ${annotation.device} \xB7 ${annotation.status}</div>
|
|
751
1025
|
</div>
|
|
752
1026
|
<button class="aa-form-close" data-action="close-detail">×</button>
|
|
753
1027
|
</div>
|
|
754
1028
|
<div class="aa-pin-detail-body">
|
|
755
|
-
<div class="aa-pin-detail-text">${
|
|
1029
|
+
<div class="aa-pin-detail-text">${escapeHtml(annotation.text)}</div>
|
|
756
1030
|
<div class="aa-pin-detail-info">
|
|
757
|
-
<div class="aa-pin-detail-selector">${
|
|
1031
|
+
<div class="aa-pin-detail-selector">${escapeHtml(annotation.selector)}</div>
|
|
758
1032
|
</div>
|
|
759
1033
|
</div>
|
|
760
1034
|
<div class="aa-pin-detail-actions">
|
|
@@ -801,26 +1075,346 @@ var PinManager = class {
|
|
|
801
1075
|
this.pins = [];
|
|
802
1076
|
this.hideDetail();
|
|
803
1077
|
}
|
|
804
|
-
escapeHtml(str) {
|
|
805
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
806
|
-
}
|
|
807
1078
|
destroy() {
|
|
808
1079
|
this.clearPins();
|
|
809
1080
|
this.detailPopup.remove();
|
|
810
1081
|
}
|
|
811
1082
|
};
|
|
812
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
|
+
|
|
813
1406
|
// src/client/overlay.ts
|
|
814
1407
|
var Overlay = class {
|
|
815
1408
|
host;
|
|
816
1409
|
shadowRoot;
|
|
817
|
-
toolbar;
|
|
818
1410
|
highlighter;
|
|
819
1411
|
form;
|
|
820
1412
|
pinManager;
|
|
1413
|
+
panel;
|
|
821
1414
|
active = false;
|
|
822
1415
|
devMode = !!window.__ASTRO_ANNOTATE_DEV__;
|
|
823
1416
|
annotations = [];
|
|
1417
|
+
lastOpenedUI = null;
|
|
824
1418
|
constructor() {
|
|
825
1419
|
this.host = document.createElement("div");
|
|
826
1420
|
this.host.id = SHADOW_ROOT_ID;
|
|
@@ -829,7 +1423,6 @@ var Overlay = class {
|
|
|
829
1423
|
const style = document.createElement("style");
|
|
830
1424
|
style.textContent = OVERLAY_STYLES;
|
|
831
1425
|
this.shadowRoot.appendChild(style);
|
|
832
|
-
this.toolbar = new Toolbar(this.shadowRoot, (active) => this.setActive(active));
|
|
833
1426
|
this.highlighter = new Highlighter(this.shadowRoot);
|
|
834
1427
|
this.form = new AnnotationForm(
|
|
835
1428
|
this.shadowRoot,
|
|
@@ -838,6 +1431,12 @@ var Overlay = class {
|
|
|
838
1431
|
this.devMode
|
|
839
1432
|
);
|
|
840
1433
|
this.pinManager = new PinManager(this.shadowRoot, () => this.loadAnnotations());
|
|
1434
|
+
this.panel = new AnnotationPanel(
|
|
1435
|
+
this.shadowRoot,
|
|
1436
|
+
() => this.loadAnnotations(),
|
|
1437
|
+
() => this.renderPins()
|
|
1438
|
+
);
|
|
1439
|
+
window.addEventListener("aa:toggle", this.onToolbarToggle);
|
|
841
1440
|
this.loadAnnotations();
|
|
842
1441
|
document.addEventListener("click", (e) => {
|
|
843
1442
|
const target = e.target;
|
|
@@ -869,6 +1468,9 @@ var Overlay = class {
|
|
|
869
1468
|
this.form.hide();
|
|
870
1469
|
}
|
|
871
1470
|
}
|
|
1471
|
+
onToolbarToggle = ((e) => {
|
|
1472
|
+
this.setActive(e.detail.active);
|
|
1473
|
+
});
|
|
872
1474
|
onElementClick = (e) => {
|
|
873
1475
|
const target = e.target;
|
|
874
1476
|
if (this.host.contains(target) || this.host === target) return;
|
|
@@ -878,23 +1480,47 @@ var Overlay = class {
|
|
|
878
1480
|
this.highlighter.hide();
|
|
879
1481
|
document.removeEventListener("mousemove", this.highlighter.onMouseMove);
|
|
880
1482
|
this.form.show(target);
|
|
1483
|
+
this.lastOpenedUI = "form";
|
|
881
1484
|
};
|
|
882
1485
|
onKeyDown = (e) => {
|
|
883
1486
|
const active = document.activeElement;
|
|
884
1487
|
const isExternalInput = active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA" || active.isContentEditable) && active !== this.host && !this.host.contains(active);
|
|
885
1488
|
if (e.altKey && e.code === "KeyC" && !isExternalInput) {
|
|
886
1489
|
e.preventDefault();
|
|
887
|
-
this.
|
|
1490
|
+
const newActive = !this.active;
|
|
1491
|
+
this.setActive(newActive);
|
|
1492
|
+
window.dispatchEvent(new CustomEvent("aa:state-changed", { detail: { active: newActive } }));
|
|
1493
|
+
return;
|
|
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;
|
|
888
1500
|
return;
|
|
889
1501
|
}
|
|
890
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
|
+
}
|
|
891
1517
|
if (this.form.isVisible()) {
|
|
892
1518
|
this.form.hide();
|
|
893
1519
|
return;
|
|
894
1520
|
}
|
|
895
1521
|
if (this.active) {
|
|
896
|
-
this.toolbar.deactivate();
|
|
897
1522
|
this.setActive(false);
|
|
1523
|
+
window.dispatchEvent(new CustomEvent("aa:state-changed", { detail: { active: false } }));
|
|
898
1524
|
return;
|
|
899
1525
|
}
|
|
900
1526
|
}
|
|
@@ -906,13 +1532,16 @@ var Overlay = class {
|
|
|
906
1532
|
if (!res.ok) return;
|
|
907
1533
|
const data = await res.json();
|
|
908
1534
|
this.annotations = data.annotations || [];
|
|
909
|
-
this.
|
|
1535
|
+
const openCount = this.annotations.filter((a) => a.status === "open").length;
|
|
1536
|
+
window.dispatchEvent(new CustomEvent("aa:count", { detail: { count: openCount } }));
|
|
910
1537
|
this.renderPins();
|
|
1538
|
+
this.panel.update(this.annotations);
|
|
911
1539
|
} catch {
|
|
912
1540
|
}
|
|
913
1541
|
}
|
|
914
1542
|
renderPins() {
|
|
915
|
-
this.
|
|
1543
|
+
const panelSide = this.panel.isVisible() ? this.panel.getSide() : null;
|
|
1544
|
+
this.pinManager.render(this.annotations, panelSide);
|
|
916
1545
|
}
|
|
917
1546
|
onAnnotationCreated() {
|
|
918
1547
|
this.loadAnnotations();
|
|
@@ -922,26 +1551,53 @@ var Overlay = class {
|
|
|
922
1551
|
document.addEventListener("mousemove", this.highlighter.onMouseMove);
|
|
923
1552
|
}
|
|
924
1553
|
}
|
|
1554
|
+
getPanelState() {
|
|
1555
|
+
return this.panel.getState();
|
|
1556
|
+
}
|
|
1557
|
+
restorePanelState(state) {
|
|
1558
|
+
this.panel.restoreState(state);
|
|
1559
|
+
}
|
|
925
1560
|
destroy() {
|
|
926
1561
|
document.removeEventListener("keydown", this.onKeyDown);
|
|
1562
|
+
window.removeEventListener("aa:toggle", this.onToolbarToggle);
|
|
1563
|
+
if (this.active) {
|
|
1564
|
+
window.dispatchEvent(new CustomEvent("aa:state-changed", { detail: { active: false } }));
|
|
1565
|
+
}
|
|
927
1566
|
this.setActive(false);
|
|
928
|
-
this.toolbar.destroy();
|
|
929
1567
|
this.highlighter.destroy();
|
|
930
1568
|
this.form.destroy();
|
|
931
1569
|
this.pinManager.destroy();
|
|
1570
|
+
this.panel.destroy();
|
|
932
1571
|
this.host.remove();
|
|
933
1572
|
}
|
|
934
1573
|
};
|
|
935
1574
|
|
|
936
1575
|
// src/client/index.ts
|
|
937
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
|
+
}
|
|
938
1581
|
function init() {
|
|
939
1582
|
if (overlay) {
|
|
1583
|
+
savePanelState(overlay.getPanelState());
|
|
940
1584
|
overlay.destroy();
|
|
941
1585
|
overlay = null;
|
|
942
1586
|
}
|
|
943
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
|
+
}
|
|
944
1595
|
}
|
|
1596
|
+
window.addEventListener("beforeunload", () => {
|
|
1597
|
+
if (overlay) {
|
|
1598
|
+
savePanelState(overlay.getPanelState());
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
945
1601
|
document.addEventListener("astro:page-load", init);
|
|
946
1602
|
if (document.readyState === "loading") {
|
|
947
1603
|
document.addEventListener("DOMContentLoaded", () => {
|