Repository navigation
Expand file tree
/
Copy pathannotate.min.js
More file actions
867 lines (792 loc) · 81.8 KB
/
Copy pathannotate.min.js
File metadata and controls
867 lines (792 loc) · 81.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
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
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
(()=>{var En=Object.freeze({p:0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,n:0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n,h:1n,a:0n,b:7n,Gx:0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,Gy:0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n}),{p:Pt,n:oe,Gx:Vo,Gy:Jo,b:Tn}=En,ct=32,Yt=64,Me={publicKey:ct+1,publicKeyUncompressed:Yt+1,signature:Yt,seed:ct+ct/2},W=(n="",a=Error)=>{let s=new a(n),{captureStackTrace:l}=Error;throw typeof l=="function"&&l(s,W),s},Yo=n=>n instanceof Uint8Array||ArrayBuffer.isView(n)&&n.constructor.name==="Uint8Array"&&n.BYTES_PER_ELEMENT===1,lt=(n,a,s="")=>{let l=Yo(n),c=n?.length,f=a!==void 0;if(!l||f&&c!==a){let k=s&&`"${s}" `,E=f?` of length ${a}`:"",b=l?`length=${c}`:`type=${typeof n}`,I=k+"expected Uint8Array"+E+", got "+b;return l?W(I,RangeError):W(I,TypeError)}return n},ne=n=>new Uint8Array(n),_n=(n,a)=>n.toString(16).padStart(a,"0"),In=n=>{let a="";for(let s of lt(n))a+=_n(s,2);return a},kt={_0:48,_9:57,A:65,F:70,a:97,f:102},gn=n=>n>=kt._0&&n<=kt._9?n-kt._0:n>=kt.A&&n<=kt.F?n-(kt.A-10):n>=kt.a&&n<=kt.f?n-(kt.a-10):void 0,Pn=n=>{let a="hex invalid";if(typeof n!="string")return W(a);let s=n.length,l=s/2;if(s%2)return W(a);let c=ne(l);for(let f=0,k=0;f<l;f++,k+=2){let E=gn(n.charCodeAt(k)),b=gn(n.charCodeAt(k+1));if(E===void 0||b===void 0)return W(a);c[f]=E*16+b}return c},vn=()=>globalThis?.crypto?.subtle??W("crypto.subtle must be defined, consider polyfill"),Ft=(...n)=>{let a=0;for(let c of n)a+=lt(c).length;let s=ne(a),l=0;for(let c of n)s.set(c,l),l+=c.length;return s},He=(n=ct)=>(globalThis?.crypto).getRandomValues(ne(n)),De=BigInt,Mt=(n,a,s,l="bad number: out of range")=>typeof n!="bigint"?W(l,TypeError):a<=n&&n<s?n:W(l,RangeError),S=(n,a=Pt)=>{let s=n%a;return s>=0n?s:a+s},ge=n=>S(n,oe),Fo=(n,a)=>{(n===0n||a<=0n)&&W("no inverse n="+n+" mod="+a);let s=S(n,a),l=a,c=0n,f=1n,k=1n,E=0n;for(;s!==0n;){let b=l/s,I=l%s,T=c-k*b,p=f-E*b;l=s,s=I,c=k,f=E,k=T,E=p}return l===1n?S(c,a):W("no inverse")},Rn=n=>{let a=Qo[n];return typeof a!="function"&&W("hashes."+n+" not set"),a},bn=(n,a,s)=>lt(Rn(n)(a,s),ct,"digest"),xn=(n,a,s)=>Promise.resolve(Rn(n)(a,s)).then(l=>lt(l,ct,"digest"));var Be=n=>n instanceof Rt?n:W("Point expected"),Cn=n=>S(S(n*n)*n+Tn),wn=n=>Mt(n,0n,Pt),he=n=>Mt(n,1n,Pt),Ko=n=>Mt(n,1n,oe),ve=n=>!(n&1n),On=n=>Uint8Array.of(n),Go=n=>On(ve(n)?2:3),Ln=n=>{let a=Cn(he(n)),s=1n;for(let l=a,c=(Pt+1n)/4n;c>0n;c>>=1n)c&1n&&(s=s*l%Pt),l=l*l%Pt;return S(s*s)!==a&&W("sqrt invalid"),ve(s)?s:S(-s)};var Rt=class n{static BASE;static ZERO;X;Y;Z;constructor(a,s,l){this.X=wn(a),this.Y=he(s),this.Z=wn(l),Object.freeze(this)}static CURVE(){return En}static fromAffine(a){let{x:s,y:l}=a;return s===0n&&l===0n?Bt:new n(s,l,1n)}static fromBytes(a){lt(a);let{publicKey:s,publicKeyUncompressed:l}=Me,c,f=a.length,k=a[0],E=a.subarray(1),b=ye(E,0,ct);if(f===s&&(k===2||k===3)){let I=Ln(b);k===3&&(I=S(-I)),c=new n(b,I,1n)}return f===l&&k===4&&(c=new n(b,ye(E,ct,Yt),1n)),c?c.assertValidity():W("bad point: not on curve")}static fromHex(a){return n.fromBytes(Pn(a))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}equals(a){let{X:s,Y:l,Z:c}=this,{X:f,Y:k,Z:E}=Be(a),b=S(s*E),I=S(f*c),T=S(l*E),p=S(k*c);return b===I&&T===p}is0(){return this.equals(Bt)}negate(){return new n(this.X,S(-this.Y),this.Z)}double(){return this.add(this)}add(a){let{X:s,Y:l,Z:c}=this,{X:f,Y:k,Z:E}=Be(a),b=0n,I=Tn,T=0n,p=0n,w=0n,C=S(I*3n),$=S(s*f),X=S(l*k),Y=S(c*E),V=S(s+l),L=S(f+k);V=S(V*L),L=S($+X),V=S(V-L),L=S(s+c);let B=S(f+E);return L=S(L*B),B=S($+Y),L=S(L-B),B=S(l+c),T=S(k+E),B=S(B*T),T=S(X+Y),B=S(B-T),w=S(b*L),T=S(C*Y),w=S(T+w),T=S(X-w),w=S(X+w),p=S(T*w),X=S($+$),X=S(X+$),Y=S(b*Y),L=S(C*L),X=S(X+Y),Y=S($-Y),Y=S(b*Y),L=S(L+Y),$=S(X*L),p=S(p+$),$=S(B*L),T=S(V*T),T=S(T-$),$=S(V*X),w=S(B*w),w=S(w+$),new n(T,p,w)}subtract(a){return this.add(Be(a).negate())}multiply(a,s=!0){if(!s&&a===0n)return Bt;if(Ko(a),a===1n)return this;if(this.equals(Dt))return ca(a).p;let l=Bt,c=Dt;for(let f=this;a>0n;f=f.double(),a>>=1n)a&1n?l=l.add(f):s&&(c=c.add(f));return l}multiplyUnsafe(a){return this.multiply(a,!1)}toAffine(){let{X:a,Y:s,Z:l}=this;if(this.equals(Bt))return{x:0n,y:0n};if(l===1n)return{x:a,y:s};let c=Fo(l,Pt);return S(l*c)!==1n&&W("inverse invalid"),{x:S(a*c),y:S(s*c)}}assertValidity(){let{x:a,y:s}=this.toAffine();return he(a),he(s),S(s*s)===Cn(a)?this:W("bad point: not on curve")}toBytes(a=!0){let{x:s,y:l}=this.assertValidity().toAffine(),c=vt(s);return a?Ft(Go(l),c):Ft(On(4),c,vt(l))}toHex(a){return In(this.toBytes(a))}},Dt=new Rt(Vo,Jo,1n),Bt=new Rt(0n,1n,0n);Rt.BASE=Dt;Rt.ZERO=Bt;var Xo=(n,a,s)=>Dt.multiply(a,!1).add(n.multiply(s,!1)).assertValidity(),jt=n=>De("0x"+(In(n)||"0")),ye=(n,a,s)=>jt(n.subarray(a,s)),Wo=2n**256n,vt=n=>Pn(_n(Mt(n,0n,Wo),Yt)),Zo=n=>{let a=jt(lt(n,ct,"secret key"));return Mt(a,1n,oe,"invalid secret key: outside of range")};var Sn="SHA-256",Qo={hmacSha256Async:async(n,a)=>{let s=vn(),l="HMAC",c=await s.importKey("raw",n,{name:l,hash:{name:Sn}},!1,["sign"]);return ne(await s.sign(l,c,a))},hmacSha256:void 0,sha256Async:async n=>ne(await vn().digest(Sn,n)),sha256:void 0};var ta=n=>{if(n=n===void 0?He(Me.seed):n,lt(n),n.length<Me.seed||n.length>1024)return W("expected 48-1024b",RangeError);let a=S(jt(n),oe-1n);return vt(a+1n)},ea=n=>a=>{let s=ta(a);return{secretKey:s,publicKey:n(s)}};var Nn=n=>Uint8Array.from("BIP0340/"+n,a=>a.charCodeAt(0)),Bn="aux",Mn="nonce",Dn="challenge",je=(n,...a)=>{let s=bn("sha256",Nn(n));return bn("sha256",Ft(s,s,...a))},Ue=(n,...a)=>xn("sha256Async",Nn(n)).then(s=>xn("sha256Async",Ft(s,s,...a))),ze=n=>{let a=Zo(n),s=Dt.multiply(a),{x:l,y:c}=s.assertValidity().toAffine(),f=ve(c)?a:ge(-a),k=vt(l);return{d:f,px:k}},$e=n=>ge(jt(n)),jn=(...n)=>$e(je(Dn,...n)),Un=async(...n)=>$e(await Ue(Dn,...n)),qn=n=>ze(n).px,na=ea(qn),Hn=(n,a,s)=>{let{px:l,d:c}=ze(a);return{m:lt(n),px:l,d:c,a:lt(s,ct)}},zn=n=>{let a=$e(n);a===0n&&W("sign failed: k is zero");let{px:s,d:l}=ze(vt(a));return{rx:s,k:l}},$n=(n,a,s,l)=>Ft(a,vt(ge(n+s*l))),Vn="invalid signature produced",oa=(n,a,s=He(ct))=>{let{m:l,px:c,d:f,a:k}=Hn(n,a,s),E=je(Bn,k),b=vt(f^jt(E)),I=je(Mn,b,c,l),{rx:T,k:p}=zn(I),w=jn(T,c,l),C=$n(p,T,w,f);return Yn(C,l,c)||W(Vn),C},aa=async(n,a,s=He(ct))=>{let{m:l,px:c,d:f,a:k}=Hn(n,a,s),E=await Ue(Bn,k),b=vt(f^jt(E)),I=await Ue(Mn,b,c,l),{rx:T,k:p}=zn(I),w=await Un(T,c,l),C=$n(p,T,w,f);return await Fn(C,l,c)||W(Vn),C},ra=(n,a)=>n instanceof Promise?n.then(a):a(n),Jn=(n,a,s,l)=>{let c=lt(n,Yt,"signature"),f=lt(a,void 0,"message"),k=lt(s,ct,"publicKey");try{let E=jt(k),b=Ln(E),I=new Rt(E,b,1n).assertValidity(),T=vt(I.toAffine().x),p=ye(c,0,ct);Mt(p,1n,Pt);let w=ye(c,ct,Yt);Mt(w,1n,oe);let C=Ft(vt(p),T,f);return ra(l(C),$=>{let{x:X,y:Y}=Xo(I,w,ge(-$)).toAffine();return!(!ve(Y)||X!==p)})}catch{return!1}},Yn=(n,a,s)=>Jn(n,a,s,jn),Fn=async(n,a,s)=>Jn(n,a,s,Un),Ve=Object.freeze({keygen:na,getPublicKey:qn,sign:oa,verify:Yn,signAsync:aa,verifyAsync:Fn}),me=8,sa=256,Kn=Math.ceil(sa/me)+1,qe=2**(me-1),ia=()=>{let n=[],a=Dt,s=a;for(let l=0;l<Kn;l++){s=a,n.push(s);for(let c=1;c<qe;c++)s=s.add(a),n.push(s);a=s.double()}return n},An,kn=(n,a)=>{let s=a.negate();return n?s:a},ca=n=>{let a=An||(An=ia()),s=Bt,l=Dt,c=2**me,f=c,k=De(c-1),E=De(me);for(let b=0;b<Kn;b++){let I=Number(n&k);n>>=E,I>qe&&(I-=f,n+=1n);let T=b*qe,p=T,w=T+Math.abs(I)-1,C=b%2!==0,$=I<0;I===0?l=l.add(kn(C,a[p])):s=s.add(kn($,a[w]))}return n!==0n&&W("invalid wnaf"),{p:s,f:l}};var{floor:Je,random:da,sin:la}=Math,Ct="Trystero",Ot=(n,a)=>Array(n).fill().map(a),Gn="0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",xe=n=>Ot(n,()=>Gn[Je(da()*Gn.length)]).join(""),Et=xe(20),bt=Promise.all.bind(Promise),Wn=typeof window<"u",{entries:we,fromEntries:Ye,keys:Zn}=Object,Tt=()=>{},ht=n=>new Error(`${Ct}: ${n}`),ua=new TextEncoder,fa=new TextDecoder,Lt=n=>ua.encode(n),Kt=n=>fa.decode(n),Se=n=>n.reduce((a,s)=>a+s.toString(16).padStart(2,"0"),""),ae=(...n)=>n.join("@"),pa=(n,a)=>{let s=[...n],l=()=>{let f=la(a++)*1e4;return f-Je(f)},c=s.length;for(;c;){let f=Je(l()*c--);[s[c],s[f]]=[s[f],s[c]]}return s},Qn=(n,a,s,l)=>(n.relayUrls||(l?pa(a,Fe(n.appId)):a)).slice(0,n.relayUrls?n.relayUrls.length:n.relayRedundancy||s),ft=JSON.stringify,Ut=JSON.parse,Fe=(n,a=Number.MAX_SAFE_INTEGER)=>n.split("").reduce((s,l)=>s+l.charCodeAt(0),0)%a,Xn=3333,be={},to=(n,a)=>{let s={},l=()=>{let c=new WebSocket(n);c.onclose=()=>{be[n]??=Xn,setTimeout(l,be[n]),be[n]*=2},c.onmessage=f=>a(f.data),s.socket=c,s.url=c.url,s.ready=new Promise(f=>c.onopen=()=>{f(s),be[n]=Xn}),s.send=f=>{c.readyState===1&&c.send(f)}};return l(),s},eo=n=>()=>Ye(we(n).map(([a,s])=>[a,s.socket]));var Ke="AES-GCM",ha={},ya=n=>btoa(String.fromCharCode.apply(null,new Uint8Array(n))),ma=n=>{let a=atob(n);return new Uint8Array(a.length).map((s,l)=>a.charCodeAt(l)).buffer},Ge=async(n,a)=>new Uint8Array(await crypto.subtle.digest(n,Lt(a))),re=async n=>ha[n]||=Array.from(await Ge("SHA-1",n)).map(a=>a.toString(36)).join(""),no=async(n,a,s)=>crypto.subtle.importKey("raw",await crypto.subtle.digest({name:"SHA-256"},Lt(`${n}:${a}:${s}`)),{name:Ke},!1,["encrypt","decrypt"]),oo="$",ao=",",ro=async(n,a)=>{let s=crypto.getRandomValues(new Uint8Array(16));return s.join(ao)+oo+ya(await crypto.subtle.encrypt({name:Ke,iv:s},await n,Lt(a)))},so=async(n,a)=>{let[s,l]=a.split(oo);return Kt(await crypto.subtle.decrypt({name:Ke,iv:new Uint8Array(s.split(ao))},await n,ma(l)))};var ga=5e3,io="icegatheringstatechange",co="offer",va="answer",Xe=(n,{rtcConfig:a,rtcPolyfill:s,turnConfig:l})=>{let c=new(s||RTCPeerConnection)({iceServers:ba.concat(l||[]),...a}),f={},k=!1,E=!1,b=null,I=p=>{p.binaryType="arraybuffer",p.bufferedAmountLowThreshold=65535,p.onmessage=w=>f.data?.(w.data),p.onopen=()=>f.connect?.(),p.onclose=()=>f.close?.(),p.onerror=w=>f.error?.(w)},T=p=>Promise.race([new Promise(w=>{let C=()=>{p.iceGatheringState==="complete"&&(p.removeEventListener(io,C),w())};p.addEventListener(io,C),C()}),new Promise(w=>setTimeout(w,ga))]).then(()=>({type:p.localDescription.type,sdp:p.localDescription.sdp.replace(/a=ice-options:trickle\s\n/g,"")}));return n?(b=c.createDataChannel("data"),I(b)):c.ondatachannel=({channel:p})=>{b=p,I(p)},c.onnegotiationneeded=async()=>{try{k=!0,await c.setLocalDescription();let p=await T(c);f.signal?.(p)}catch(p){f.error?.(p)}finally{k=!1}},c.onconnectionstatechange=()=>{["disconnected","failed","closed"].includes(c.connectionState)&&f.close?.()},c.ontrack=p=>{f.track?.(p.track,p.streams[0]),f.stream?.(p.streams[0])},c.onremovestream=p=>f.stream?.(p.stream),n&&(c.canTrickleIceCandidates||c.onnegotiationneeded()),{created:Date.now(),connection:c,get channel(){return b},get isDead(){return c.connectionState==="closed"},async signal(p){if(!(b?.readyState==="open"&&!p.sdp?.includes("a=rtpmap")))try{if(p.type===co){if(k||c.signalingState!=="stable"&&!E){if(n)return;await bt([c.setLocalDescription({type:"rollback"}),c.setRemoteDescription(p)])}else await c.setRemoteDescription(p);await c.setLocalDescription();let w=await T(c);return f.signal?.(w),w}else if(p.type===va){E=!0;try{await c.setRemoteDescription(p)}finally{E=!1}}}catch(w){f.error?.(w)}},sendData:p=>b.send(p),destroy:()=>{b?.close(),c.close(),k=!1,E=!1},setHandlers:p=>Object.assign(f,p),offerPromise:n?new Promise(p=>f.signal=w=>{w.type===co&&p(w)}):Promise.resolve(),addStream:p=>p.getTracks().forEach(w=>c.addTrack(w,p)),removeStream:p=>c.getSenders().filter(w=>p.getTracks().includes(w.track)).forEach(w=>c.removeTrack(w)),addTrack:(p,w)=>c.addTrack(p,w),removeTrack:p=>{let w=c.getSenders().find(C=>C.track===p);w&&c.removeTrack(w)},replaceTrack:(p,w)=>{let C=c.getSenders().find($=>$.track===p);if(C)return C.replaceTrack(w)}}},ba=[...Ot(3,(n,a)=>`stun:stun${a||""}.l.google.com:19302`),"stun:stun.cloudflare.com:3478"].map(n=>({urls:n}));var xa=Object.getPrototypeOf(Uint8Array),ke=12,uo=0,Ee=uo+ke,Te=Ee+1,se=Te+1,ie=se+1,qt=16*2**10-ie,Ae=255,lo="bufferedamountlow",Gt=n=>"@_"+n,fo=(n,a,s)=>{let l={},c={},f={},k={},E={},b={},I={},T={onPeerJoin:Tt,onPeerLeave:Tt,onPeerStream:Tt,onPeerTrack:Tt},p=(h,A)=>(h?Array.isArray(h)?h:[h]:Zn(l)).flatMap(N=>{let j=l[N];return j?A(N,j):(console.warn(`${Ct}: no peer with id ${N} found`),[])}),w=h=>{l[h]&&(l[h].destroy(),delete l[h],delete k[h],delete E[h],T.onPeerLeave(h),a(h))},C=h=>{if(c[h])return f[h];if(!h)throw ht("action type argument is required");let A=Lt(h);if(A.byteLength>ke)throw ht(`action type string "${h}" (${A.byteLength}b) exceeds byte limit (${ke}). Hint: choose a shorter name.`);let N=new Uint8Array(ke);N.set(A);let j=0;return c[h]={onComplete:Tt,onProgress:Tt,setOnComplete:x=>c[h]={...c[h],onComplete:x},setOnProgress:x=>c[h]={...c[h],onProgress:x},send:async(x,P,U,J)=>{if(U&&typeof U!="object")throw ht("action meta argument must be an object");let Z=typeof x;if(Z==="undefined")throw ht("action data cannot be undefined");let it=Z!=="string",z=x instanceof Blob,et=z||x instanceof ArrayBuffer||x instanceof xa;if(U&&!et)throw ht("action meta argument can only be used with binary data");let nt=et?new Uint8Array(z?await x.arrayBuffer():x):Lt(it?ft(x):x),G=U?Lt(ft(U)):null,M=Math.ceil(nt.byteLength/qt)+(U?1:0)||1,H=Ot(M,(dt,ot)=>{let Q=ot===M-1,at=U&&ot===0,st=new Uint8Array(ie+(at?G.byteLength:Q?nt.byteLength-qt*(M-(U?2:1)):qt));return st.set(N),st.set([j],Ee),st.set([Q|at<<1|et<<2|it<<3],Te),st.set([Math.round((ot+1)/M*Ae)],se),st.set(U?at?G:nt.subarray((ot-1)*qt,ot*qt):nt.subarray(ot*qt,(ot+1)*qt),ie),st});return j=j+1&Ae,bt(p(P,async(dt,ot)=>{let{channel:Q}=ot,at=0;for(;at<M;){let st=H[at];if(Q.bufferedAmount>Q.bufferedAmountLowThreshold&&await new Promise(de=>{let le=()=>{Q.removeEventListener(lo,le),de()};Q.addEventListener(lo,le)}),!l[dt])break;ot.sendData(st),at++,J?.(st[se]/Ae,dt,U)}}))}},f[h]||=[c[h].send,c[h].setOnComplete,c[h].setOnProgress]},$=(h,A)=>{let N=new Uint8Array(A),j=Kt(N.subarray(uo,Ee)).replaceAll("\0",""),[x]=N.subarray(Ee,Te),[P]=N.subarray(Te,se),[U]=N.subarray(se,ie),J=N.subarray(ie),Z=!!(P&1),it=!!(P&2),z=!!(P&4),et=!!(P&8);if(!c[j]){console.warn(`${Ct}: received message with unregistered type (${j})`);return}k[h]||={},k[h][j]||={};let nt=k[h][j][x]||={chunks:[]};if(it?nt.meta=Ut(Kt(J)):nt.chunks.push(J),c[j].onProgress(U/Ae,h,nt.meta),!Z)return;let G=new Uint8Array(nt.chunks.reduce((M,H)=>M+H.byteLength,0));if(nt.chunks.reduce((M,H)=>(G.set(H,M),M+H.byteLength),0),delete k[h][j][x],z)c[j].onComplete(G,h,nt.meta);else{let M=Kt(G);c[j].onComplete(et?Ut(M):M,h)}},X=async()=>{await Ht(""),await new Promise(h=>setTimeout(h,99)),we(l).forEach(([h,A])=>{A.destroy(),delete l[h]}),s()},[Y,V]=C(Gt("ping")),[L,B]=C(Gt("pong")),[v,yt]=C(Gt("signal")),[mt,pt]=C(Gt("stream")),[K,tt]=C(Gt("track")),[Ht,xt]=C(Gt("leave"));return n((h,A)=>{l[A]||(l[A]=h,h.setHandlers({data:N=>$(A,N),stream:N=>{T.onPeerStream(N,A,b[A]),delete b[A]},track:(N,j)=>{T.onPeerTrack(N,j,A,I[A]),delete I[A]},signal:N=>v(N,A),close:()=>w(A),error:N=>{console.error(N),w(A)}}),T.onPeerJoin(A))}),V((h,A)=>L("",A)),B((h,A)=>{E[A]?.(),delete E[A]}),yt((h,A)=>l[A]?.signal(h)),pt((h,A)=>b[A]=h),tt((h,A)=>I[A]=h),xt((h,A)=>w(A)),Wn&&addEventListener("beforeunload",X),{makeAction:C,leave:X,ping:async h=>{if(!h)throw ht("ping() must be called with target peer ID");let A=Date.now();return Y("",h),await new Promise(N=>E[h]=N),Date.now()-A},getPeers:()=>Ye(we(l).map(([h,A])=>[h,A.connection])),addStream:(h,A,N)=>p(A,async(j,x)=>{N&&await mt(N,j),x.addStream(h)}),removeStream:(h,A)=>p(A,(N,j)=>j.removeStream(h)),addTrack:(h,A,N,j)=>p(N,async(x,P)=>{j&&await K(j,x),P.addTrack(h,A)}),removeTrack:(h,A)=>p(A,(N,j)=>j.removeTrack(h)),replaceTrack:(h,A,N,j)=>p(N,async(x,P)=>{j&&await K(j,x),P.replaceTrack(h,A)}),onPeerJoin:h=>T.onPeerJoin=h,onPeerLeave:h=>T.onPeerLeave=h,onPeerStream:h=>T.onPeerStream=h,onPeerTrack:h=>T.onPeerTrack=h}};var wa=20,Sa=5333,po=57333,ho=({init:n,subscribe:a,announce:s})=>{let l={},c=!1,f,k,E;return(b,I,T)=>{let{appId:p}=b;if(l[p]?.[I])return l[p][I];let w={},C={},$=ae(Ct,p,I),X=re($),Y=re(ae($,Et)),V=no(b.password||"",p,I),L=x=>async P=>({type:P.type,sdp:await x(V,P.sdp)}),B=L(so),v=L(ro),yt=()=>Xe(!0,b),mt=(x,P,U)=>{if(C[P]){C[P]!==x&&x.destroy();return}C[P]=x,j(x,P),w[P]?.forEach((J,Z)=>{Z!==U&&J.destroy()}),delete w[P]},pt=(x,P)=>{C[P]===x&&delete C[P]},K=(x,P)=>{if(C[x])return;let U=w[x]?.[P];U&&(delete w[x][P],U.destroy())},tt=x=>(k.push(...Ot(x,yt)),bt(k.splice(0,x).map(P=>P.offerPromise.then(v).then(U=>({peer:P,offer:U}))))),Ht=(x,P)=>T?.({error:`incorrect password (${b.password}) when decrypting ${P}`,appId:p,peerId:x,roomId:I}),xt=x=>async(P,U,J)=>{let[Z,it]=await bt([X,Y]);if(P!==Z&&P!==it)return;let{peerId:z,offer:et,answer:nt,peer:G}=typeof U=="string"?Ut(U):U;if(!(z===Et||C[z])){if(z&&!et&&!nt){if(w[z]?.[x])return;let[[{peer:M,offer:H}],dt]=await bt([tt(1),re(ae($,z))]);w[z]||=[],w[z][x]=M,setTimeout(()=>K(z,x),h[x]*.9),M.setHandlers({connect:()=>mt(M,z,x),close:()=>pt(M,z)}),J(dt,ft({peerId:Et,offer:H}))}else if(et){if(w[z]?.[x]&&Et>z)return;let H=Xe(!1,b);H.setHandlers({connect:()=>mt(H,z,x),close:()=>pt(H,z)});let dt;try{dt=await B(et)}catch{Ht(z,"offer");return}if(H.isDead)return;let[ot,Q]=await bt([re(ae($,z)),H.signal(dt)]);J(ot,ft({peerId:Et,answer:await v(Q)}))}else if(nt){let M;try{M=await B(nt)}catch{Ht(z,"answer");return}if(G)G.setHandlers({connect:()=>mt(G,z,x),close:()=>pt(G,z)}),G.signal(M);else{let H=w[z]?.[x];H&&!H.isDead&&H.signal(M)}}}};if(!b)throw ht("requires a config map as the first argument");if(!p&&!b.firebaseApp)throw ht("config map is missing appId field");if(!I)throw ht("roomId argument required");if(!c){let x=n(b);k=Ot(wa,yt),f=Array.isArray(x)?x:[x],c=!0,E=setInterval(()=>k=k.filter(P=>{let U=Date.now()-P.created<po;return U||P.destroy(),U}),po*1.03)}let h=f.map(()=>Sa),A=[],N=f.map(async(x,P)=>a(await x,await X,await Y,xt(P),tt));bt([X,Y]).then(([x,P])=>{let U=async(J,Z)=>{let it=await s(J,x,P);typeof it=="number"&&(h[Z]=it),A[Z]=setTimeout(()=>U(J,Z),h[Z])};N.forEach(async(J,Z)=>{await J,U(await f[Z],Z)})});let j=Tt;return l[p]||={},l[p][I]=fo(x=>j=x,x=>delete C[x],()=>{delete l[p][I],A.forEach(clearTimeout),N.forEach(async x=>(await x)()),clearInterval(E)})}};var vo={},Aa=5,bo="x",xo="EVENT",{secretKey:ka,publicKey:Ea}=Ve.keygen(),Ta=Se(Ea),We={},ce={},_a={},wo=()=>Math.floor(Date.now()/1e3),So=n=>_a[n]??=Fe(n,1e4)+2e4,yo=async(n,a)=>{let s={kind:So(n),tags:[[bo,n]],created_at:wo(),content:a,pubkey:Ta},l=await Ge("SHA-256",ft([0,s.pubkey,s.created_at,s.kind,s.tags,s.content]));return ft([xo,{...s,id:Se(l),sig:Se(await Ve.signAsync(l,ka))}])},mo=(n,a)=>(We[n]=a,ft(["REQ",n,{kinds:[So(a)],since:wo(),["#"+bo]:[a]}])),go=n=>(delete We[n],ft(["CLOSE",n])),ut=ho({init:n=>Qn(n,Ia,Aa,!0).map(a=>{let s=to(a,l=>{let[c,f,k,E]=Ut(l);if(c!==xo){let b=`${Ct}: relay failure from ${s.url} - `;c==="NOTICE"?console.warn(b+f):c==="OK"&&!k&&console.warn(b+E);return}ce[f]?.(We[f],k.content)});return vo[a]=s,s.ready}),subscribe:(n,a,s,l)=>{let c=xe(64),f=xe(64);return ce[c]=ce[f]=(k,E)=>l(k,E,async(b,I)=>n.send(await yo(b,I))),n.send(mo(c,a)),n.send(mo(f,s)),()=>{n.send(go(c)),n.send(go(f)),delete ce[c],delete ce[f]}},announce:async(n,a)=>n.send(await yo(a,ft({peerId:Et})))}),Qa=eo(vo),Ia=["black.nostrcity.club","ftp.halifax.rwth-aachen.de/nostr","nos.lol","nostr.cool110.xyz","nostr.data.haus","nostr.sathoarder.com","nostr.vulpem.com","relay.agorist.space","relay.binaryrobot.com","relay.damus.io","relay.fountain.fm","relay.mostro.network","relay.nostraddress.com","relay.nostrdice.com","relay.nostromo.social","relay.oldenburg.cool","relay.verified-nostr.com","yabu.me/v2"].map(n=>"wss://"+n);(function(){"use strict";var n="0.5.15";function a(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(t){let e=Math.random()*16|0;return(t==="x"?e:e&3|8).toString(16)})}function s(t){try{let e=new URL(t);return e.hash="",e.toString()}catch{return t}}function l(t){return new Promise(function(e,o){if(!t){o(new Error("Annotate.js: siteId is required"));return}let i=indexedDB.open("annotate-"+t,1);i.onupgradeneeded=function(r){let d=r.target.result;if(!d.objectStoreNames.contains("threads")){let u=d.createObjectStore("threads",{keyPath:"id"});u.createIndex("pageUrl","pageUrl",{unique:!1}),u.createIndex("createdAt","createdAt",{unique:!1})}if(!d.objectStoreNames.contains("activity")){let u=d.createObjectStore("activity",{keyPath:"id"});u.createIndex("pageUrl","pageUrl",{unique:!1}),u.createIndex("timestamp","timestamp",{unique:!1})}},i.onsuccess=function(r){e(r.target.result)},i.onerror=function(r){console.error("Annotate.js: could not open IndexedDB",r.target.error),o(r.target.error)}})}function c(t,e){return new Promise(function(o,i){let r=t.transaction("threads","readonly").objectStore("threads").get(e);r.onsuccess=function(){o(r.result||null)},r.onerror=function(){i(r.error)}})}function f(t,e){return new Promise(function(o,i){let r=t.transaction("threads","readonly").objectStore("threads").index("pageUrl").getAll(e);r.onsuccess=function(){o((r.result||[]).sort(function(d,u){return d.createdAt.localeCompare(u.createdAt)}))},r.onerror=function(){i(r.error)}})}function k(t,e){return f(t,e).then(function(o){return o.filter(function(i){return!i.resolved&&!i.deletedAt})})}function E(t,e){return f(t,e).then(function(o){return o.filter(function(i){return i.resolved&&!i.deletedAt})})}function b(t,e){return new Promise(function(o,i){let r=t.transaction("threads","readwrite").objectStore("threads").put(e);r.onsuccess=function(){o()},r.onerror=function(){i(r.error)}})}function I(t,e){return c(t,e).then(function(o){if(o)return o.deletedAt=new Date().toISOString(),o.updatedAt=new Date().toISOString(),o.dirty=!0,b(t,o)})}function T(t,e){return new Promise(function(o,i){let r=t.transaction("activity","readwrite").objectStore("activity").put(e);r.onsuccess=function(){o()},r.onerror=function(){i(r.error)}})}function p(t,e){return new Promise(function(o,i){let r=t.transaction("activity","readonly").objectStore("activity").index("pageUrl").getAll(e);r.onsuccess=function(){o((r.result||[]).sort(function(d,u){return u.timestamp.localeCompare(d.timestamp)}))},r.onerror=function(){i(r.error)}})}function w(t,e,o){return new Promise(function(i,r){var d=t.transaction(["threads","activity"],"readwrite"),u=d.objectStore("threads"),y=u.getAll();y.onsuccess=function(){var m=(y.result||[]).filter(function(g){return g.siteId===e&&g.authorId===o});m.forEach(function(g){u.delete(g.id)}),d.objectStore("activity").clear()},d.oncomplete=i,d.onerror=function(){r(d.error)}})}function C(t,e){return e?new Promise(function(o,i){var r=e.transaction(["threads","activity"],"readwrite");r.objectStore("threads").clear(),r.objectStore("activity").clear(),r.oncomplete=o,r.onerror=function(){i(r.error)}}):new Promise(function(o,i){var r=indexedDB.deleteDatabase("annotate-"+t);r.onsuccess=function(){o()},r.onerror=function(){i(r.error)},r.onblocked=function(){console.warn("Annotate.js: dbClearAll blocked \u2014 deletion will complete on reload"),o()}})}function $(t){let e=[],o=t;for(;o&&o.nodeType!==Node.DOCUMENT_NODE;){if(o.nodeType===Node.TEXT_NODE){let i=1,r=o.previousSibling;for(;r;)r.nodeType===Node.TEXT_NODE&&i++,r=r.previousSibling;e.unshift("text()["+i+"]")}else if(o.nodeType===Node.ELEMENT_NODE){let i=o.tagName.toLowerCase(),r=1,d=o.previousSibling;for(;d;)d.nodeType===Node.ELEMENT_NODE&&d.tagName.toLowerCase()===i&&r++,d=d.previousSibling;e.unshift(i+"["+r+"]")}o=o.parentNode}return"/"+e.join("/")}function X(t){return{xpath:$(t.startContainer),startOffset:t.startOffset,endXpath:$(t.endContainer),endOffset:t.endOffset}}function Y(t){try{var e=document.evaluate(t.xpath,document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;if(!e)return null;var o=t.endXpath||t.xpath,i=document.evaluate(o,document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;if(!i)return null;var r=e.nodeValue?e.nodeValue.length:0,d=i.nodeValue?i.nodeValue.length:0;if(t.startOffset>r||t.endOffset>d)return null;var u=document.createRange();return u.setStart(e,t.startOffset),u.setEnd(i,t.endOffset),u}catch(y){return console.warn("Annotate.js: restoreRange failed",t,y),null}}let V=document.currentScript&&document.currentScript.dataset.siteId||"default",L=s(window.location.href),B=document.currentScript&&document.currentScript.dataset.syncUrl||null,v=null,yt=null,mt=null,pt="annotate_author_id",K=localStorage.getItem(pt);K||(K=a(),localStorage.setItem(pt,K));let tt=document.currentScript&&document.currentScript.dataset.roomId||null,Ht=document.currentScript&&document.currentScript.dataset.relayUrl||"wss://relay.annotate-js.workers.dev",xt=document.currentScript&&document.currentScript.dataset.adminId||null,h=!1,A=document.currentScript&&document.currentScript.dataset.syncMs,N=A?parseInt(A,10):0,j=N>0?N:3e4;A&&!(N>0)&&console.warn('Annotate.js: data-sync-ms="'+A+'" is invalid \u2014 using default 30 s poll interval.'),B&&tt&&console.warn("Annotate.js: data-sync-url and data-room-id are mutually exclusive \u2014 data-room-id will be ignored; server sync is active.");let x=function(){},P=function(){},U=function(){};var J=0,Z=null,it={},z=null;console.log("Annotate.js loaded \u2014 site:",V);var et=typeof BroadcastChannel<"u"&&V?new BroadcastChannel("annotate-"+V):null;et&&(et.onmessage=function(t){var e=t.data;v&&(e.type==="THREAD_UPDATE"&&c(v,e.thread.id).then(function(o){(!o||e.thread.updatedAt>=o.updatedAt)&&b(v,e.thread).then(function(){yn(e.thread)})}),e.type==="ACTIVITY_UPDATE"&&T(v,e.entry))});let nt=document.createElement("style");nt.textContent=`
/* ===================== SIDEBAR ===================== */
#annotate-sidebar {
position: fixed;
top: 0;
right: 0;
width: 360px;
height: 100vh;
background: #f4f5f7;
border-left: 1px solid #e0e4ea;
box-shadow: -2px 0 16px rgba(0,0,0,0.07);
z-index: 9999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 14px;
transition: transform 0.25s ease;
}
#annotate-sidebar.collapsed {
transform: translateX(360px);
}
/* ===================== HEADER ===================== */
#annotate-sidebar-header {
position: sticky;
top: 0;
background: #fff;
border-bottom: 1px solid #e8eaed;
z-index: 1;
}
.annotate-header-top {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 12px 10px 16px;
}
.annotate-header-title {
font-size: 16px;
font-weight: 700;
color: #111;
letter-spacing: -0.015em;
text-transform: uppercase;
}
.annotate-header-icons {
display: flex;
align-items: center;
gap: 2px;
}
.annotate-header-icon-btn {
background: none;
border: none;
cursor: pointer;
color: #6b7280;
padding: 5px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s, color 0.15s;
}
.annotate-header-icon-btn:hover {
background: #f3f4f6;
color: #111;
}
/* ===================== TABS ===================== */
.annotate-tabs {
display: flex;
padding: 0 6px;
}
.annotate-tab {
padding: 8px 10px 7px;
font-size: 12.5px;
font-weight: 500;
color: #9ca3af;
border: none;
background: none;
cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
transition: color 0.15s;
}
.annotate-tab.active {
color: #111;
border-bottom-color: #111;
font-weight: 600;
}
.annotate-tab:hover:not(.active) {
color: #374151;
}
/* ===================== BODY ===================== */
#annotate-sidebar-body {
position: relative;
padding-top: 10px;
}
#annotate-empty {
position: absolute;
top: 24px;
left: 0;
right: 0;
text-align: center;
color: #9ca3af;
font-size: 13px;
}
/* ===================== CARDS ===================== */
.annotate-card {
position: absolute;
left: 10px;
right: 10px;
border: 1px solid #e4e7eb;
border-radius: 10px;
background: #fff;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
@keyframes annotate-focus-ring {
0% { box-shadow: 0 0 0 0 rgba(245,158,11,0.55), 0 1px 4px rgba(0,0,0,0.06); }
40% { box-shadow: 0 0 0 5px rgba(245,158,11,0.18), 0 1px 4px rgba(0,0,0,0.06); }
100% { box-shadow: 0 0 0 0 rgba(245,158,11,0), 0 1px 4px rgba(0,0,0,0.06); }
}
.annotate-card-focused {
border-color: #f59e0b;
animation: annotate-focus-ring 1.1s ease forwards;
}
/* ===================== QUOTE ===================== */
.annotate-card-quote {
background: #fdf8f0;
border-left: 3px solid #d4a843;
border-radius: 10px 10px 0 0;
padding: 8px 12px;
font-size: 12px;
color: #6b5c3e;
font-style: italic;
line-height: 1.5;
}
/* ===================== CARD BODY ===================== */
.annotate-card-body {
padding: 12px;
}
/* ===================== META ROW ===================== */
.annotate-meta {
display: flex;
align-items: flex-start;
gap: 9px;
margin-bottom: 0;
}
.annotate-avatar {
width: 34px;
height: 34px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
color: #fff;
flex-shrink: 0;
background: #1a1a1a;
}
.annotate-avatar-sm {
width: 26px;
height: 26px;
font-size: 10px;
margin-top: 1px;
}
.annotate-meta-right {
flex: 1;
min-width: 0;
}
.annotate-author-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
margin-bottom: 4px;
}
.annotate-author {
font-size: 13px;
font-weight: 600;
color: #111;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.annotate-author-row-right {
display: flex;
align-items: center;
gap: 2px;
flex-shrink: 0;
position: relative;
}
.annotate-timestamp {
font-size: 10.5px;
color: #9ca3af;
font-weight: 500;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap;
}
/* ===================== NOTE TEXT ===================== */
.annotate-note-text {
font-size: 13px;
color: #374151;
line-height: 1.6;
margin: 0 0 9px;
}
/* ===================== INLINE ACTION ROW ===================== */
.annotate-action-row {
display: flex;
align-items: center;
gap: 14px;
}
.annotate-action-btn {
background: none;
border: none;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.07em;
color: #9ca3af;
cursor: pointer;
padding: 0;
text-transform: uppercase;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
transition: color 0.15s;
}
.annotate-action-btn:hover {
color: #374151;
}
/* ===================== ICON BUTTONS ===================== */
.annotate-icon-btn {
background: none;
border: none;
cursor: pointer;
color: #d1d5db;
padding: 3px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
transition: background 0.15s, color 0.15s;
}
.annotate-icon-btn:hover {
background: #f3f4f6;
color: #6b7280;
}
/* ===================== DROPDOWN ===================== */
.annotate-dropdown {
position: absolute;
top: 100%;
right: 0;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
z-index: 10002;
min-width: 140px;
padding: 4px 0;
}
.annotate-dropdown.hidden {
display: none;
}
.annotate-dropdown-item {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 12px;
font-size: 13px;
color: #1a1a1a;
cursor: pointer;
background: none;
border: none;
width: 100%;
text-align: left;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.annotate-dropdown-item:hover {
background: #f5f5f5;
}
.annotate-dropdown-item.danger {
color: #dc2626;
}
/* ===================== COMPOSER ===================== */
.annotate-card-composer {
width: 100%;
box-sizing: border-box;
border: 1px solid #e5e7eb;
border-radius: 7px;
padding: 8px 10px;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
resize: none;
outline: none;
color: #111;
min-height: 64px;
background: #fafafa;
}
.annotate-card-composer:focus {
border-color: #1a1a1a;
background: #fff;
}
.annotate-card-actions {
display: flex;
justify-content: flex-end;
gap: 6px;
margin-top: 8px;
}
.annotate-btn-cancel {
background: none;
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 5px 14px;
font-size: 12px;
cursor: pointer;
color: #555;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
transition: background 0.15s;
}
.annotate-btn-save {
background: #1a1a1a;
border: none;
border-radius: 6px;
padding: 5px 14px;
font-size: 12px;
cursor: pointer;
color: #fff;
font-weight: 600;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
transition: background 0.15s;
}
.annotate-btn-save:hover { background: #333; }
.annotate-btn-cancel:hover { background: #f5f5f5; }
/* ===================== HIGHLIGHT ===================== */
.annotate-highlight {
background: #fde68a;
border-radius: 2px;
}
/* ===================== REPLIES ===================== */
.annotate-replies {
border-top: 1px solid #f3f4f6;
}
.annotate-reply {
padding: 10px 12px;
border-top: 1px solid #f3f4f6;
}
.annotate-reply-action {
padding: 8px 12px;
border-top: 1px solid #f3f4f6;
}
.annotate-reply-composer {
width: 100%;
box-sizing: border-box;
border: 1px solid #e5e7eb;
border-radius: 7px;
padding: 6px 10px;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
resize: none;
outline: none;
color: #111;
min-height: 52px;
margin-bottom: 6px;
background: #fafafa;
}
.annotate-reply-composer:focus {
border-color: #1a1a1a;
background: #fff;
}
/* ===================== TOGGLE BUTTON ===================== */
#annotate-toggle {
position: fixed;
top: 50%;
right: 0;
transform: translateY(-50%);
background: #1a1a1a;
color: #fff;
border: none;
border-radius: 6px 0 0 6px;
padding: 10px 8px;
cursor: pointer;
z-index: 10000;
writing-mode: vertical-rl;
letter-spacing: 0.05em;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 10px;
font-weight: 600;
transition: right 0.25s ease;
text-transform: uppercase;
}
#annotate-toggle.sidebar-open {
right: 360px;
}
/* ===================== FLOATING COMMENT BUTTON ===================== */
#annotate-comment-btn {
position: fixed;
background: #1a1a1a;
color: #fff;
border: none;
border-radius: 6px;
padding: 4px;
cursor: pointer;
z-index: 10001;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
transition: background 0.15s;
}
#annotate-comment-btn.hidden {
display: none;
}
#annotate-comment-btn:hover {
background: #333;
}
@media (pointer: coarse) {
#annotate-comment-btn {
padding: 10px;
border-radius: 8px;
}
}
/* ===================== PANELS (Resolved / Activity / Settings) ===================== */
.annotate-panel {
position: sticky;
top: var(--annotate-header-h, 80px);
height: calc(100vh - var(--annotate-header-h, 80px));
box-sizing: border-box;
padding: 10px;
overflow-y: auto;
}
/* Resolved tab \u2014 flow-layout cards (not absolutely positioned) */
.annotate-resolved-card {
border: 1px solid #e4e7eb;
border-radius: 10px;
background: #fff;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
margin-bottom: 8px;
}
/* Activity tab */
.annotate-activity-list { padding: 4px 0; }
.annotate-activity-entry {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 10px 4px;
border-bottom: 1px solid #f3f4f6;
}
.annotate-activity-entry:last-child { border-bottom: none; }
.annotate-activity-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
margin-top: 4px;
}
.annotate-activity-content { flex: 1; min-width: 0; }
.annotate-activity-actor {
font-size: 12px;
font-weight: 600;
color: #111;
}
.annotate-activity-snapshot {
font-size: 12px;
color: #6b7280;
line-height: 1.4;
margin-top: 1px;
}
.annotate-activity-time {
font-size: 10px;
color: #9ca3af;
white-space: nowrap;
text-transform: uppercase;
letter-spacing: 0.04em;
flex-shrink: 0;
}
/* Settings tab */
.annotate-settings-group {
padding: 14px 4px;
border-bottom: 1px solid #f3f4f6;
}
.annotate-settings-group:last-child { border-bottom: none; }
.annotate-settings-label {
display: block;
font-size: 11px;
font-weight: 600;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.06em;
margin-bottom: 6px;
}
.annotate-settings-input {
width: 100%;
box-sizing: border-box;
border: 1px solid #e5e7eb;
border-radius: 7px;
padding: 7px 10px;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
outline: none;
color: #111;
background: #fafafa;
}
.annotate-settings-input:focus { border-color: #1a1a1a; background: #fff; }
.annotate-settings-hint {
font-size: 11px;
color: #9ca3af;
margin-top: 5px;
}
.annotate-settings-btn {
width: 100%;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 6px;
background: #f9fafb;
color: #374151;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
cursor: pointer;
text-align: left;
margin-bottom: 6px;
transition: background 0.15s;
}
.annotate-settings-btn:hover { background: #f3f4f6; }
.annotate-settings-btn-danger {
background: none;
border: 1px solid #dc2626;
color: #dc2626;
border-radius: 6px;
padding: 6px 14px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
transition: background 0.15s, color 0.15s;
}
.annotate-settings-btn-danger:hover { background: #dc2626; color: #fff; }
/* Shared empty state */
.annotate-empty-state {
text-align: center;
color: #9ca3af;
font-size: 13px;
padding: 32px 0;
}
/* Resolved highlight \u2014 slightly muted so it reads as "done" */
.annotate-highlight.is-resolved {
background: #d1fae5;
opacity: 0.7;
}
/* ===================== ABOUT (Settings \u2192 About) ===================== */
.annotate-about-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.annotate-about-name {
font-weight: 600;
color: #1a1a1a;
}
.annotate-about-version {
font-size: 12px;
color: #888;
font-variant-numeric: tabular-nums;
}
.annotate-mode-chip-row {
margin-bottom: 6px;
}
.annotate-mode-chip {
font-size: 11px;
font-weight: 500;
padding: 2px 8px;
border-radius: 10px;
background: #f0f0f0;
color: #555;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.annotate-privacy-note {
margin: 4px 0 8px 0;
}
.annotate-about-link {
font-size: 12px;
color: #2563eb;
text-decoration: none;
}
.annotate-about-link:hover { text-decoration: underline; }
.annotate-about-id-row {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
flex-wrap: wrap;
}
.annotate-about-id {
font-family: monospace;
font-size: 11px;
color: #374151;
background: #e5e7eb;
border-radius: 4px;
padding: 2px 6px;
cursor: pointer;
word-break: break-all;
user-select: all;
}
.annotate-about-id:hover { background: #d1d5db; }
.annotate-p2p-info {
display: flex;
flex-direction: column;
gap: 4px;
margin-top: 4px;
}
.annotate-p2p-row {
display: flex;
align-items: center;
gap: 8px;
}
.annotate-p2p-key {
font-size: 11px;
color: #9ca3af;
width: 44px;
flex-shrink: 0;
}
.annotate-p2p-val {
font-size: 12px;
color: #374151;
}
.annotate-p2p-room {
font-family: monospace;
background: #e5e7eb;
border-radius: 4px;
padding: 1px 5px;
font-size: 11px;
}
.annotate-p2p-live { color: #16a34a; font-weight: 500; }
`,document.head.appendChild(nt),document.documentElement.style.position="relative";let G=document.createElement("div");G.id="annotate-sidebar",G.innerHTML=`
<div id="annotate-sidebar-header">
<div class="annotate-header-top">
<span class="annotate-header-title">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-3px;margin-right:6px;"><path d="M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/><path d="M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1"/></svg>Discussion
</span>
<div class="annotate-header-icons">
<button class="annotate-header-icon-btn" title="Search">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
</button>
<button class="annotate-header-icon-btn" title="Filter">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="6" x2="16" y2="6"/><line x1="8" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg>
</button>
<button id="annotate-close" class="annotate-header-icon-btn" title="Close">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
</div>
<div class="annotate-tabs">
<button class="annotate-tab active">Threads</button>
<button class="annotate-tab">Resolved</button>
<button class="annotate-tab">Activity</button>
<button class="annotate-tab">Settings</button>
</div>
</div>
<div id="annotate-sidebar-body">
<span id="annotate-empty">No threads yet.</span>
</div>
<div id="annotate-panel-resolved" class="annotate-panel" style="display:none;"></div>
<div id="annotate-panel-activity" class="annotate-panel" style="display:none;"></div>
<div id="annotate-panel-settings" class="annotate-panel" style="display:none;"></div>
`,document.documentElement.appendChild(G),G.style.setProperty("--annotate-header-h",document.getElementById("annotate-sidebar-header").offsetHeight+"px");let M=document.getElementById("annotate-sidebar-body"),H=document.getElementById("annotate-empty"),dt=document.getElementById("annotate-panel-resolved"),ot=document.getElementById("annotate-panel-activity"),Q=document.getElementById("annotate-panel-settings"),at={},st=new Set;function de(){M.style.transform="translateY("+-window.scrollY+"px)"}window.addEventListener("scroll",de,{passive:!0}),de();let le=[M,dt,ot,Q],Ao={Threads:M,Resolved:dt,Activity:ot,Settings:Q};function Nt(t){G.querySelectorAll(".annotate-tab").forEach(function(o){o.classList.toggle("active",o.textContent.trim()===t)}),le.forEach(function(o){o.style.display="none"});let e=Ao[t];e&&(e.style.display=""),t==="Resolved"&&v&&Po(v),t==="Activity"&&v&&Co(v),t==="Settings"&&dn()}G.querySelectorAll(".annotate-tab").forEach(function(t){t.addEventListener("click",function(){Nt(t.textContent.trim())})});let zt=document.createElement("button");zt.id="annotate-toggle",zt.textContent="Annotations",document.documentElement.appendChild(zt);function $t(){G.classList.remove("collapsed"),zt.classList.add("sidebar-open")}function _e(){G.classList.add("collapsed"),zt.classList.remove("sidebar-open")}zt.addEventListener("click",function(){G.classList.contains("collapsed")?$t():_e()}),document.getElementById("annotate-close").addEventListener("click",_e),_e();function Ie(t){let e=Math.floor((Date.now()-t)/1e3);return e<60?"JUST NOW":e<3600?Math.floor(e/60)+"M AGO":e<86400?Math.floor(e/3600)+"H AGO":Math.floor(e/86400)+"D AGO"}let Xt="annotate_display_name";function wt(){return localStorage.getItem(Xt)||""}function Ze(){let t=wt();return t||(t=(window.prompt("Enter your display name for annotations:")||"").trim(),t||(t="Anonymous"),localStorage.setItem(Xt,t)),t}function ko(t,e,o,i,r){return{id:a(),siteId:V,pageUrl:L,type:t,threadId:e,replyId:o||null,actor:i,timestamp:new Date().toISOString(),snapshot:r}}function St(t,e,o,i,r){var d=ko(t,e,o,i,r);return T(v,d).then(function(){No(d)})}let Qe='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="12" cy="19" r="1"/></svg>';function tn(t){return(t||"A").charAt(0).toUpperCase()}function Pa(t,e){let i=t,r=Array.from(M.querySelectorAll(".annotate-card")).filter(function(u){return u!==e}).sort(function(u,y){return(parseInt(u.style.top)||0)-(parseInt(y.style.top)||0)}),d=!0;for(;d;){d=!1;for(let u of r){let y=parseInt(u.style.top,10)||0,m=y+u.offsetHeight;i<m+8&&i+e.offsetHeight>y&&(i=m+8,d=!0)}}return i}function en(t){var e=at[t];if(e&&e.classList.contains("is-resolved")){Nt("Resolved");return}Nt("Threads");var o=M.querySelector('[data-thread-id="'+t+'"]');e&&e.scrollIntoView({behavior:"smooth",block:"center"}),o&&(o.classList.remove("annotate-card-focused"),o.offsetWidth,o.classList.add("annotate-card-focused"),setTimeout(function(){o.classList.remove("annotate-card-focused")},1200))}function _t(){let e=Array.from(M.querySelectorAll(".annotate-card")).sort(function(i,r){return(i._anchorTop||0)-(r._anchorTop||0)}),o=0;for(let i of e){let r=i._anchorTop||0,d=Math.max(r,o);i.style.top=d+"px",o=d+i.offsetHeight+8}if(e.length>0){let i=e[e.length-1];M.style.minHeight=(parseInt(i.style.top)||0)+i.offsetHeight+16+"px",Eo(i)}else document.body.style.paddingBottom=""}function Eo(t){let e=(parseInt(t.style.top)||0)+t.offsetHeight,o=document.getElementById("annotate-sidebar-header").offsetHeight,i=e+o+32,r=parseInt(document.body.style.paddingBottom||0,10),d=document.documentElement.scrollHeight-r,u=Math.max(0,i-d);document.body.style.paddingBottom=u>0?u+"px":""}function nn(t){if(typeof ResizeObserver>"u")return;new ResizeObserver(function(){requestAnimationFrame(_t)}).observe(t)}function Wt(t){var e=document.createElement("mark");e.className="annotate-highlight";try{return t.surroundContents(e),e._allMarks=[e],e}catch(i){var o=To(t);return o.length?(o[0]._allMarks=o,o[0]):(console.warn("Annotate.js: could not highlight range",i),null)}}function To(t){for(var e=t.startContainer,o=t.endContainer,i=t.commonAncestorContainer,r=[],d=document.createNodeIterator(i,NodeFilter.SHOW_TEXT,null),u,y=!1;(u=d.nextNode())&&(u===e&&(y=!0),y&&r.push(u),u!==o););var m=[];return r.forEach(function(g){var _=document.createRange();if(g===e&&g===o?(_.setStart(g,t.startOffset),_.setEnd(g,t.endOffset)):g===e?(_.setStart(g,t.startOffset),_.setEnd(g,g.nodeValue.length)):g===o?(_.setStart(g,0),_.setEnd(g,t.endOffset)):(_.setStart(g,0),_.setEnd(g,g.nodeValue.length)),!_.collapsed){var R=document.createElement("mark");R.className="annotate-highlight";try{_.surroundContents(R),m.push(R)}catch{}}}),m}function It(t){return t?t._allMarks||[t]:[]}function on(t,e){t.addEventListener("click",function(o){o.stopPropagation(),e.classList.toggle("hidden")}),document.addEventListener("click",function(){e.classList.add("hidden")})}function _o(){return tt?"P2P":B?"Server sync":et?"Offline + BroadcastChannel":"Offline"}function Io(){return tt?"End-to-end encrypted between peers. No server sees the content.":B?"Synced to your own server. No third party involved.":"Stored locally in your browser. Never leaves this device."}function Pe(t){return!B&&!tt?!0:t.authorId?t.authorId===K:!1}function an(t){if(!B&&!tt){h=!0;return}if(xt){h=K===xt;return}var e=(t||[]).filter(function(o){return o.authorId&&!o.deletedAt});if(!e.length){h=!0;return}e.sort(function(o,i){return o.createdAt<i.createdAt?-1:1}),h=e[0].authorId===K}function rn(t){let e=document.createElement("div");e.className="annotate-reply";let o=Pe(t)?`<button class="annotate-icon-btn annotate-menu-btn" title="More">${Qe}</button>
<div class="annotate-dropdown hidden">
<button class="annotate-dropdown-item annotate-edit-btn">Edit</button>
<button class="annotate-dropdown-item danger annotate-delete-btn">Delete</button>
</div>`:"";return e.innerHTML=`
<div class="annotate-meta">
<div class="annotate-avatar annotate-avatar-sm">${tn(t.author)}</div>
<div class="annotate-meta-right">
<div class="annotate-author-row">
<span class="annotate-author">${t.author}</span>
<div class="annotate-author-row-right">
<span class="annotate-timestamp">${Ie(new Date(t.createdAt))}</span>
${o}
</div>
</div>
<p class="annotate-note-text" style="margin-bottom:0;">${t.body}</p>
</div>
</div>
`,e}function sn(t,e,o,i){if(t._replyId=e,!Pe(i))return;let r=t.querySelector(".annotate-dropdown");on(t.querySelector(".annotate-menu-btn"),r),t.querySelector(".annotate-edit-btn").addEventListener("click",function(){r.classList.add("hidden");let d=t.querySelector(".annotate-note-text"),u=d.textContent,y=document.createElement("div");y.innerHTML=`
<textarea class="annotate-reply-composer" style="margin-top:6px;">${u}</textarea>
<div class="annotate-card-actions">
<button class="annotate-btn-cancel">Cancel</button>
<button class="annotate-btn-save">Save</button>
</div>
`,d.replaceWith(y);let m=y.querySelector("textarea");m.focus(),m.setSelectionRange(m.value.length,m.value.length),y.querySelector(".annotate-btn-save").addEventListener("click",function(){let g=m.value.trim();if(!g)return;v&&c(v,o._threadId).then(function(R){if(!R)return;let O=R.replies.find(function(q){return q.id===e});return O&&(O.body=g,O.updatedAt=new Date().toISOString(),R.updatedAt=new Date().toISOString(),R.dirty=!0),b(v,R).then(function(){return At(R),St("reply_edited",R.id,e,O?O.author:"","edited reply")})}).catch(function(R){console.warn("Annotate.js: reply edit persist failed",R)});let _=document.createElement("p");_.className="annotate-note-text",_.style.marginBottom="0",_.textContent=g,y.replaceWith(_)}),y.querySelector(".annotate-btn-cancel").addEventListener("click",function(){let g=document.createElement("p");g.className="annotate-note-text",g.style.marginBottom="0",g.textContent=u,y.replaceWith(g)})}),t.querySelector(".annotate-delete-btn").addEventListener("click",function(){v&&c(v,o._threadId).then(function(d){if(!d)return;let u=d.replies.find(function(y){return y.id===e});return u&&(u.deleted=!0,d.updatedAt=new Date().toISOString(),d.dirty=!0),b(v,d).then(function(){return At(d),St("reply_deleted",d.id,e,u?u.author:"","deleted reply")})}).catch(function(d){console.warn("Annotate.js: reply delete persist failed",d)}),t.remove()})}function Zt(t,e){t._lastRenderedAt=e.updatedAt;let o=t.querySelector(".annotate-card-body"),r=Pe(e)&&!e.resolved,d=r?`<button class="annotate-icon-btn annotate-menu-btn" title="More">${Qe}</button>
<div class="annotate-dropdown hidden">
<button class="annotate-dropdown-item annotate-edit-btn">Edit</button>
<button class="annotate-dropdown-item danger annotate-delete-btn">Delete</button>
</div>`:"";if(o.innerHTML=`
<div class="annotate-meta">
<div class="annotate-avatar">${tn(e.author)}</div>
<div class="annotate-meta-right">
<div class="annotate-author-row">
<span class="annotate-author">${e.author}</span>
<div class="annotate-author-row-right">
<span class="annotate-timestamp">${Ie(new Date(e.createdAt))}</span>
${d}
</div>
</div>
<p class="annotate-note-text">${e.body}</p>
<div class="annotate-action-row">
${e.resolved?"":'<button class="annotate-action-btn annotate-reply-trigger">Reply</button>'}
<button class="annotate-action-btn annotate-resolve-btn">${e.resolved?"Un-Resolve":"Resolve"}</button>
</div>
</div>
</div>
`,o.querySelector(".annotate-resolve-btn").addEventListener("click",function(){v&&c(v,e.id).then(function(g){if(!g)return;let _=wt(),R=!!g.resolved;return R?(g.resolved=!1,g.resolvedAt=null,g.resolvedBy=null):(g.resolved=!0,g.resolvedAt=new Date().toISOString(),g.resolvedBy=_),g.updatedAt=new Date().toISOString(),g.dirty=!0,b(v,g).then(function(){if(Bo(g),R){var O=at[g.id]||null;It(O).forEach(function(F){F.classList.remove("is-resolved")});var q=M.querySelector('[data-thread-id="'+g.id+'"]');q?(q.style.opacity="",q.style.pointerEvents="",Zt(q,g),_t()):Re(g,O)}return St(R?"thread_unresolved":"thread_resolved",g.id,null,_,R?"un-resolved thread":"resolved thread")})}).catch(function(g){console.warn("Annotate.js: resolve toggle persist failed",g)}),t.style.opacity="0.4",t.style.pointerEvents="none"}),r){let g=o.querySelector(".annotate-dropdown");on(o.querySelector(".annotate-menu-btn"),g),o.querySelector(".annotate-edit-btn").addEventListener("click",function(){g.classList.add("hidden");let _=o.querySelector(".annotate-note-text"),R=_.textContent,O=document.createElement("div");O.innerHTML=`
<textarea class="annotate-card-composer" style="margin-top:8px;">${R}</textarea>
<div class="annotate-card-actions">
<button class="annotate-btn-cancel">Cancel</button>
<button class="annotate-btn-save">Save</button>
</div>
`,_.replaceWith(O);let q=O.querySelector("textarea");q.focus(),q.setSelectionRange(q.value.length,q.value.length),O.querySelector(".annotate-btn-save").addEventListener("click",function(){let F=q.value.trim();if(!F)return;v&&c(v,e.id).then(function(D){if(D)return D.body=F,D.updatedAt=new Date().toISOString(),D.dirty=!0,b(v,D).then(function(){return At(D),St("thread_edited",D.id,null,D.author,"edited: '"+F.slice(0,40)+"'")})}).catch(function(D){console.warn("Annotate.js: edit persist failed",D)});let gt=document.createElement("p");gt.className="annotate-note-text",gt.textContent=F,O.replaceWith(gt)}),O.querySelector(".annotate-btn-cancel").addEventListener("click",function(){let F=document.createElement("p");F.className="annotate-note-text",F.textContent=R,O.replaceWith(F)})}),o.querySelector(".annotate-delete-btn").addEventListener("click",function(){if(v&&I(v,e.id).then(function(){return c(v,e.id).then(function(_){_&&At(_)}),St("thread_deleted",e.id,null,wt(),"deleted thread")}).catch(function(_){console.warn("Annotate.js: delete persist failed",_)}),t._annotationMark){let _=t._annotationMark,R=_.parentNode;if(R){for(;_.firstChild;)R.insertBefore(_.firstChild,_);R.removeChild(_)}}t.remove(),M.querySelector(".annotate-card")||(H.style.display="")})}let u=t.querySelector(".annotate-replies");u&&u.remove();let y=document.createElement("div");y.className="annotate-replies",(e.replies||[]).filter(function(g){return!g.deleted}).forEach(function(g){let _=rn(g);sn(_,g.id,t,g),y.appendChild(_)}),t.appendChild(y);let m=o.querySelector(".annotate-reply-trigger");m&&m.addEventListener("click",function(){$o(y,t)})}function Re(t,e){H&&(H.style.display="none");let o=document.createElement("div");o.className="annotate-card",o._threadId=t.id,o.dataset.threadId=t.id,st.add(t.id);let i=e?"":' <span style="font-size:10px;color:#aaa;font-style:normal;">(highlight unavailable)</span>';o.innerHTML=`
<div class="annotate-card-quote">${t.quote}${i}</div>
<div class="annotate-card-body"></div>
`,e&&(o._annotationMark=e,at[t.id]=e,It(e).forEach(function(d){d._threadId=t.id,d.addEventListener("click",function(){$t(),en(t.id)})}));let r=document.getElementById("annotate-sidebar-header").offsetHeight;if(e){let d=e.getBoundingClientRect();o._anchorTop=Math.max(0,d.top+window.scrollY-r)}else o._anchorTop=8;return M.appendChild(o),Zt(o,t),t.resolved&&(o.style.opacity="0.4",o.style.pointerEvents="none"),_t(),nn(o),o}function Po(t){dt.innerHTML="",E(t,L).then(function(e){if(e.length===0){dt.innerHTML='<div class="annotate-empty-state">No resolved threads yet.</div>';return}e.forEach(function(o){let i=document.createElement("div");i.className="annotate-resolved-card",i._threadId=o.id,i._annotationMark=at[o.id]||null,i.innerHTML=`
<div class="annotate-card-quote">${o.quote}</div>
<div class="annotate-card-body"></div>
`,dt.appendChild(i),Zt(i,o)})}).catch(function(e){console.warn("Annotate.js: _renderResolvedTab failed",e)})}function Ro(t){return t.startsWith("data_")?"#2563eb":t.includes("deleted")?"#dc2626":t.includes("edited")?"#d97706":t.includes("resolved")?"#6b7280":"#1a1a1a"}function Co(t){ot.innerHTML="",p(t,L).then(function(e){if(e.length===0){ot.innerHTML='<div class="annotate-empty-state">No activity yet.</div>';return}let o=document.createElement("div");o.className="annotate-activity-list",e.forEach(function(i){let r=document.createElement("div");r.className="annotate-activity-entry",r.innerHTML=`
<div class="annotate-activity-dot" style="background:${Ro(i.type)};"></div>
<div class="annotate-activity-content">
<span class="annotate-activity-actor">${i.actor}</span>
<div class="annotate-activity-snapshot">${i.snapshot}</div>
</div>
<div class="annotate-activity-time">${Ie(new Date(i.timestamp))}</div>
`,o.appendChild(r)}),ot.appendChild(o)}).catch(function(e){console.warn("Annotate.js: _renderActivityTab failed",e)})}function cn(t){if(v){var e=v.transaction(["threads"],"readwrite"),o=e.objectStore("threads"),i=o.getAll();i.onsuccess=function(){var r=[];(i.result||[]).forEach(function(d){var u=!1;d.authorId===K&&d.author!==t&&(d.author=t,u=!0),d.replies&&d.replies.forEach(function(y){y.authorId===K&&y.author!==t&&(y.author=t,u=!0)}),u&&(d.updatedAt=new Date().toISOString(),d.dirty=!0,o.put(d),r.push(d))}),e.oncomplete=function(){r.forEach(function(d){At(d)}),r.length&&Vt(r)}},e.onerror=function(){console.warn("Annotate.js: _renameAuthorEverywhere failed",e.error)}}}function Oo(){v&&Promise.all([f(v,L),p(v,L)]).then(function(t){var e={version:"1",exportedAt:new Date().toISOString(),siteId:V,pageUrl:L,settings:{displayName:wt(),authorId:K},threads:t[0],activity:t[1]},o=new Blob([JSON.stringify(e,null,2)],{type:"application/json"}),i=URL.createObjectURL(o),r=document.createElement("a");r.href=i,r.download="annotate-"+V+"-"+new Date().toISOString().slice(0,10)+".json",document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(i);var d=t[0].length;St("data_exported",null,null,wt(),"exported "+d+(d===1?" thread":" threads"))}).catch(function(t){console.warn("Annotate.js: export failed",t)})}function Lo(t){if(v){var e=new FileReader;e.onload=function(o){var i;try{i=JSON.parse(o.target.result)}catch{alert("Annotate.js: could not parse the file \u2014 is it a valid JSON export?");return}if(!i.threads||!Array.isArray(i.threads)){alert("Annotate.js: unrecognised export format.");return}if(!(i.pageUrl&&i.pageUrl!==L&&!window.confirm(`This export is from a different page:
`+i.pageUrl+`
Highlights may not resolve on this page. Import anyway?`))){var r=[],d=0,u=new Date().toISOString(),y=v.transaction(["threads","activity"],"readwrite"),m=y.objectStore("threads"),g=y.objectStore("activity");y.oncomplete=function(){r.forEach(function(D){At(D)});var R=i.settings;if(R){var O=wt();R.displayName&&localStorage.setItem(Xt,R.displayName),R.authorId&&(localStorage.setItem(pt,R.authorId),K=R.authorId),R.displayName&&R.displayName!==O&&cn(R.displayName)}if(r.length===0)alert("Import complete \u2014 all threads are already up to date.");else{Nt("Threads");var q=r.filter(function(D){return!D.deletedAt});q.sort(function(D,Jt){return D.createdAt<Jt.createdAt?-1:1}),q.length&&Vt(q),requestAnimationFrame(_t);var F=r.length,gt="imported "+F+(F===1?" thread":" threads");d>0&&(gt+=" ("+d+" restored)"),St("data_imported",null,null,wt(),gt)}},y.onerror=function(){console.warn("Annotate.js: import transaction failed",y.error)};var _=m.getAll();_.onsuccess=function(){var R={};(_.result||[]).forEach(function(O){R[O.id]=O}),i.threads.forEach(function(O){var q=R[O.id],F=!!(q&&q.deletedAt&&!O.deletedAt),gt=!q||O.updatedAt>q.updatedAt||F;gt&&(F&&d++,O=Object.assign({},O,{updatedAt:u}),O.dirty=!!B,m.put(O),r.push(O))}),(i.activity||[]).forEach(function(O){g.put(O)})}}},e.readAsText(t)}}function ue(){var t=document.getElementById("annotate-p2p-peers-val");if(t){var e=J>0?J+" connected":"Searching for peers\u2026";t.textContent=e,t.className="annotate-p2p-val"+(J>0?" annotate-p2p-live":"")}}function dn(){var t=h,e=t?`
<div class="annotate-settings-group">
<label class="annotate-settings-label">Data</label>
<button class="annotate-settings-btn-danger" id="annotate-clear-btn">Clear all annotations</button>
<p class="annotate-settings-hint">Permanently removes all threads and activity for this page. Cannot be undone.</p>
<button class="annotate-settings-btn-danger" id="annotate-reset-identity-btn" style="margin-top:8px">Reset identity</button>
<p class="annotate-settings-hint">Clears your display name and browser ID from this device. You will appear as a new anonymous user and lose edit access to your existing annotations.</p>
</div>`:"",o=`
<div class="annotate-settings-group annotate-about">
<label class="annotate-settings-label">About</label>
<div class="annotate-about-row">
<span class="annotate-about-name">Annotate.js</span>
<span class="annotate-about-version">${n==="dev"?"dev":"v"+n}</span>
</div>
<div class="annotate-mode-chip-row">
<span class="annotate-mode-chip" title="Active sync mode">${_o()}</span>
</div>
<p class="annotate-settings-hint annotate-privacy-note">${Io()}</p>
<a class="annotate-about-link" href="https://github.com/kasunben/Annotate.js"
target="_blank" rel="noopener">View on GitHub</a>
</div>`,i=`
<div class="annotate-settings-group">
<label class="annotate-settings-label">Export / Import</label>
<button class="annotate-settings-btn" id="annotate-export-btn">Download annotations</button>
<p class="annotate-settings-hint">Saves all threads, activity, and settings for this page as a JSON file.</p>
<input type="file" id="annotate-import-file" accept=".json" style="display:none">
<button class="annotate-settings-btn" id="annotate-import-btn">Import from file\u2026</button>
<p class="annotate-settings-hint">Merges threads and activity from a JSON export. Settings in the file override current settings. Importing another user's export grants you edit access to their threads.</p>
</div>`,r=tt?(function(){var g=tt.slice(0,8)+"\u2026"+tt.slice(-4),_=J>0?J+" connected":"Searching for peers\u2026",R=Z==="relay"?"Relay":Z==="nostr"?"NOSTR":"Connecting\u2026";return'<div class="annotate-settings-group"><label class="annotate-settings-label">P2P Session</label><div class="annotate-p2p-info"><div class="annotate-p2p-row"><span class="annotate-p2p-key">Room</span><code class="annotate-p2p-val annotate-p2p-room">'+g+'</code></div><div class="annotate-p2p-row"><span class="annotate-p2p-key">Peers</span><span id="annotate-p2p-peers-val" class="annotate-p2p-val '+(J>0?"annotate-p2p-live":"")+'">'+_+'</span></div><div class="annotate-p2p-row"><span class="annotate-p2p-key">Via</span><span class="annotate-p2p-val">'+R+"</span></div></div></div>"})():"";Q.innerHTML=`
${o}
<div class="annotate-settings-group">
<label class="annotate-settings-label">Display name</label>
<input class="annotate-settings-input" id="annotate-name-input" type="text"
value="${wt()}" placeholder="Enter your name\u2026" />
<p class="annotate-settings-hint">Shown on all your annotations and replies.</p>
<div class="annotate-about-id-row">
<span class="annotate-settings-hint" style="margin:0">Browser ID</span>
<code class="annotate-about-id" id="annotate-about-id-val"
title="Click to copy \u2014 use as data-admin-id to lock in admin access">${K}</code>
</div>
</div>
${r}
${i}
${e}
`;let d=Q.querySelector("#annotate-name-input");function u(){let g=d.value.trim();if(!g)return;let _=wt();localStorage.setItem(Xt,g),g!==_&&cn(g)}d.addEventListener("blur",u),d.addEventListener("keydown",function(g){g.key==="Enter"&&(u(),d.blur())});var y=Q.querySelector("#annotate-about-id-val");y&&y.addEventListener("click",function(){navigator.clipboard&&navigator.clipboard.writeText&&navigator.clipboard.writeText(K).then(function(){y.textContent="Copied!",setTimeout(function(){y.textContent=K},1500)}).catch(function(){})}),Q.querySelector("#annotate-export-btn").addEventListener("click",Oo);let m=Q.querySelector("#annotate-import-file");Q.querySelector("#annotate-import-btn").addEventListener("click",function(){m.click()}),m.addEventListener("change",function(){m.files[0]&&(Lo(m.files[0]),m.value="")}),t&&(Q.querySelector("#annotate-clear-btn").addEventListener("click",function(){window.confirm("Delete ALL annotations and activity for this page? This cannot be undone.")&&(B?Promise.all([fetch(B+"/threads?siteId="+encodeURIComponent(V),{method:"DELETE"}),fetch(B+"/activity?siteId="+encodeURIComponent(V),{method:"DELETE"})]).then(function(){return C(V,v)}).then(function(){window.location.reload()}).catch(function(g){console.warn("Annotate.js: clearAll (server) failed",g)}):tt?(U({adminId:xt||K}),C(V,v).then(function(){window.location.reload()}).catch(function(g){console.warn("Annotate.js: clearAll (P2P) failed",g)})):C(V,v).then(function(){window.location.reload()}).catch(function(g){console.warn("Annotate.js: clearAll failed",g)}))}),Q.querySelector("#annotate-reset-identity-btn").addEventListener("click",function(){window.confirm(`Reset your browser identity?
Your display name and browser ID will be cleared. You will lose edit access to any annotations you have made on this device.`)&&(localStorage.removeItem(Xt),localStorage.removeItem(pt),K=a(),h=!1,localStorage.setItem(pt,K),dn())}))}function ln(t){It(t).forEach(function(e){var o=e.parentNode;if(o){for(;e.firstChild;)o.insertBefore(e.firstChild,e);o.removeChild(e)}})}function No(t){et&&et.postMessage({type:"ACTIVITY_UPDATE",entry:t}),P(t),B&&fetch(B+"/activity",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).catch(function(){})}function Ce(){if(!B||!v)return Promise.resolve();var t=B+"/activity?siteId="+encodeURIComponent(V)+"&pageUrl="+encodeURIComponent(L)+(mt?"&since="+encodeURIComponent(mt):"");return fetch(t).then(function(e){return e.json()}).then(function(e){if(e.length){var o=mt||"";return e.reduce(function(i,r){return i.then(function(){return r.timestamp>o&&(o=r.timestamp),T(v,r)})},Promise.resolve()).then(function(){mt=o})}}).catch(function(){})}function At(t){if(et&&et.postMessage({type:"THREAD_UPDATE",thread:t}),x(t),!(!B||!v)){var e=Object.assign({},t);delete e.dirty,fetch(B+"/threads",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(o){if(!o.ok)throw new Error("sync failed: "+o.status);return c(v,t.id)}).then(function(o){if(o&&o.dirty)return o.dirty=!1,b(v,o)}).catch(function(o){console.warn("Annotate.js: syncThread failed",o)})}}function Bo(t){et&&et.postMessage({type:"THREAD_UPDATE",thread:t}),x(t),!(!B||!v)&&fetch(B+"/threads/"+t.id+"/resolve",{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({resolved:t.resolved,resolvedBy:t.resolvedBy||null,resolvedAt:t.resolvedAt||null})}).then(function(e){if(!e.ok)throw new Error("syncResolve failed: "+e.status);return c(v,t.id)}).then(function(e){if(e&&e.dirty)return e.dirty=!1,b(v,e)}).catch(function(e){console.warn("Annotate.js: syncResolve failed",e)})}function un(t){v&&c(v,t.id).then(function(e){t.deletedAt&&e&&!e.deletedAt&&e.authorId&&t.authorId!==e.authorId||(!e||t.updatedAt>e.updatedAt)&&b(v,t).then(function(){yn(t)})})}function fn(t){v&&T(v,t)}function pn(t){if(!v)return;var e=t&&t.adminId;if(!e)return;function o(){C(V,v).then(function(){window.location.reload()})}xt?e===xt&&o():f(v,L).then(function(i){var r=i.filter(function(d){return d.authorId&&!d.deletedAt});r.length&&(r.sort(function(d,u){return d.createdAt<u.createdAt?-1:1}),r[0].authorId===e&&o())})}function hn(t){t.onopen=function(){J++,ue()},t.onclose=function(){J=Math.max(0,J-1),ue()},t.onmessage=function(e){var o;try{o=JSON.parse(e.data)}catch{return}if(o.type==="THREAD"){un(o.thread);return}if(o.type==="ACTIVITY"){fn(o.entry);return}if(o.type==="ADMIN_CLEAR"){pn(o);return}if(o.type==="REQUEST_STATE"){f(v,L).then(function(d){t.readyState==="open"&&t.send(JSON.stringify({type:"STATE_SNAPSHOT",threads:d}))});return}if(o.type==="STATE_SNAPSHOT"){var i=o.threads||[];i.sort(function(d,u){return d.createdAt<u.createdAt?-1:1});var r=Promise.resolve();i.forEach(function(d){r=r.then(function(){return c(v,d.id).then(function(u){if(!u||d.updatedAt>u.updatedAt)return b(v,d)})})}),r.then(function(){Vt(i),requestAnimationFrame(_t)})}}}function Mo(t,e,o,i){var r=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]}),d=r.createDataChannel("annotate");i[t]={pc:r,dc:d},hn(d),r.onicecandidate=function(u){u.candidate&&e.send(JSON.stringify({type:"signal",to:t,from:o,data:{candidate:u.candidate}}))},r.createOffer().then(function(u){return r.setLocalDescription(u)}).then(function(){e.send(JSON.stringify({type:"signal",to:t,from:o,data:{sdp:r.localDescription}}))}).catch(function(u){console.warn("Annotate.js P2P: offer failed",u)})}function Do(t,e,o,i){var r=t.from,d=t.data;if(!i[r]){var u=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]});i[r]={pc:u,dc:null},u.ondatachannel=function(m){i[r].dc=m.channel,hn(m.channel),m.channel.onopen=function(){m.channel.send(JSON.stringify({type:"REQUEST_STATE",pageUrl:L}))}},u.onicecandidate=function(m){m.candidate&&e.send(JSON.stringify({type:"signal",to:r,from:o,data:{candidate:m.candidate}}))}}var y=i[r].pc;d.sdp&&(d.sdp.type==="offer"?y.setRemoteDescription(d.sdp).then(function(){return y.createAnswer()}).then(function(m){return y.setLocalDescription(m)}).then(function(){e.send(JSON.stringify({type:"signal",to:r,from:o,data:{sdp:y.localDescription}}))}).catch(function(m){console.warn("Annotate.js P2P: answer failed",m)}):d.sdp.type==="answer"&&y.setRemoteDescription(d.sdp).catch(function(){})),d.candidate&&y.addIceCandidate(d.candidate).catch(function(){})}function jo(t,e,o){var i=a(),r={},d;try{d=new WebSocket(t+"/room/"+encodeURIComponent(tt))}catch(u){o(u);return}d.onopen=function(){e(),Z="relay",d.send(JSON.stringify({type:"join",room:tt,peerId:i}))},d.onmessage=function(u){var y;try{y=JSON.parse(u.data)}catch{return}y.type==="peer-joined"&&Mo(y.peerId,d,i,r),y.type==="signal"&&Do(y,d,i,r),y.type==="peer-left"&&r[y.peerId]&&(r[y.peerId].pc.close(),delete r[y.peerId])},d.onerror=function(u){o(u)},d.onclose=function(u){u.wasClean||(console.warn("Annotate.js P2P: relay disconnected \u2014 falling back to NOSTR"),Oe())},x=function(u){Object.keys(r).forEach(function(y){var m=r[y];m.dc&&m.dc.readyState==="open"&&m.dc.send(JSON.stringify({type:"THREAD",thread:u}))})},P=function(u){Object.keys(r).forEach(function(y){var m=r[y];m.dc&&m.dc.readyState==="open"&&m.dc.send(JSON.stringify({type:"ACTIVITY",entry:u}))})},U=function(u){Object.keys(r).forEach(function(y){var m=r[y];m.dc&&m.dc.readyState==="open"&&m.dc.send(JSON.stringify({type:"ADMIN_CLEAR",adminId:u.adminId}))})}}function Oe(){if(typeof ut!="function"){console.warn("Annotate.js P2P: NOSTR fallback requires the bundled build (annotate.min.js). Raw annotate.js does not include Trystero.");return}var t;try{t=ut({appId:V},tt)}catch(D){console.warn("Annotate.js P2P: NOSTR init failed",D);return}Z="nostr";var e=t.makeAction("thread"),o=t.makeAction("activity"),i=t.makeAction("request"),r=t.makeAction("snapshot"),d=t.makeAction("admin"),u=e[0],y=e[1],m=o[0],g=o[1],_=i[0],R=i[1],O=r[0],q=r[1],F=d[0],gt=d[1];y(function(D){un(D)}),g(function(D){fn(D)}),gt(function(D){pn(D)}),R(function(D,Jt){f(v,L).then(function(ee){O({threads:ee},[Jt])})}),q(function(D){var Jt=D.threads||[],ee=Promise.resolve();Jt.forEach(function(Ne){ee=ee.then(function(){return c(v,Ne.id).then(function(mn){if(!mn||Ne.updatedAt>mn.updatedAt)return b(v,Ne)})})}),ee.then(function(){Vt(Jt)})}),t.onPeerJoin(function(D){J++,ue(),_({pageUrl:L},[D])}),t.onPeerLeave(function(){J=Math.max(0,J-1),ue()}),x=function(D){u(D)},P=function(D){m(D)},U=function(D){F(D)}}function Uo(){if(!(!tt||!v)){typeof ut!="function"&&console.warn("Annotate.js P2P: data-room-id is set but Trystero is not available. P2P mode requires the bundled build (annotate.min.js). The raw source (annotate.js) does not include Trystero \u2014 all signaling tiers will fail. Annotations will save locally only and will not reach other peers.");var t=!1,e=setTimeout(function(){t||(t=!0,Oe())},5e3);jo(Ht,function(){t||(t=!0,clearTimeout(e))},function(){t||(t=!0,clearTimeout(e),Oe())})}}function Vt(t){t.forEach(function(e){var o=M.querySelector('[data-thread-id="'+e.id+'"]'),i=at[e.id];if(e.deletedAt){o?(o._annotationMark&&ln(o._annotationMark),o.remove(),st.delete(e.id)):i&&ln(i),delete at[e.id];return}if(e.resolved){if(o&&(o.style.opacity="0.4",o.style.pointerEvents="none",st.delete(e.id)),i)It(i).forEach(function(m){m.classList.add("is-resolved")});else if(e.anchor){var r=Y(e.anchor);if(r){var d=Wt(r);d&&(It(d).forEach(function(m){m.classList.add("is-resolved"),m.addEventListener("click",function(){$t(),Nt("Resolved")})}),at[e.id]=d)}}return}if(!o){if(st.has(e.id))return;var u=e.anchor?Y(e.anchor):null,y=u?Wt(u):null;Re(e,y);return}e.updatedAt!==o._lastRenderedAt&&!o.querySelector(".annotate-card-composer")&&!o.querySelector(".annotate-reply-composer")&&(o.style.opacity="",o.style.pointerEvents="",st.add(e.id),It(i).forEach(function(m){m.classList.remove("is-resolved")}),Zt(o,e))}),!M.querySelector(".annotate-card")&&H&&(H.style.display="")}function yn(t){var e=it[t.id];(!e||t.updatedAt>=e.updatedAt)&&(it[t.id]=t),!z&&(z=setTimeout(function(){var o=Object.keys(it).map(function(i){return it[i]});it={},z=null,o.sort(function(i,r){return i.createdAt<r.createdAt?-1:1}),Vt(o),requestAnimationFrame(_t)},50))}function Le(){if(!(!B||!v)){var t=yt?"&since="+encodeURIComponent(yt):"",e=B+"/threads?siteId="+encodeURIComponent(V)+"&pageUrl="+encodeURIComponent(L)+t;fetch(e).then(function(o){return o.ok?o.json():Promise.reject("pull failed: "+o.status)}).then(function(o){o.forEach(function(r){(!yt||r.updatedAt>yt)&&(yt=r.updatedAt)});var i=new Set;return Promise.all(o.map(function(r){return c(v,r.id).then(function(d){if(d&&d.dirty){i.add(r.id);return}return r.dirty=!1,b(v,r)})})).then(function(){Vt(o.filter(function(r){return!i.has(r.id)})),an(o)})}).catch(function(o){console.warn("Annotate.js: pullThreads failed",o)})}}function qo(){!B||!v||f(v,L).then(function(t){t.filter(function(e){return e.dirty}).forEach(At)})}function Ho(t){return Promise.all([k(t,L),E(t,L)]).then(function(e){let o=e[0],i=e[1];o.length>0&&H&&(H.style.display="none"),o.forEach(function(r){let d=r.anchor?Y(r.anchor):null,u=d?Wt(d):null;Re(r,u)}),i.forEach(function(r){let d=r.anchor?Y(r.anchor):null,u=d?Wt(d):null;u&&(It(u).forEach(function(y){y.classList.add("is-resolved"),y._threadId=r.id,y.addEventListener("click",function(){$t(),Nt("Resolved")})}),at[r.id]=u)})}).catch(function(e){console.warn("Annotate.js: loadThreads failed",e)})}function zo(t,e){H&&(H.style.display="none");let o=document.createElement("div");o.className="annotate-card",o.innerHTML=`
<div class="annotate-card-quote">${t}</div>
<div class="annotate-card-body">
<textarea class="annotate-card-composer" placeholder="Add a note\u2026"></textarea>
<div class="annotate-card-actions">
<button class="annotate-btn-cancel">Cancel</button>
<button class="annotate-btn-save">Save</button>
</div>
</div>
`;let i=document.getElementById("annotate-sidebar-header").offsetHeight,r=8;if(e){let m=e.getBoundingClientRect();r=Math.max(0,m.top+window.scrollY-i)}M.appendChild(o),o._anchorTop=r,_t(),nn(o);let d=o.querySelector(".annotate-card-composer"),u=o.querySelector(".annotate-btn-save"),y=o.querySelector(".annotate-btn-cancel");setTimeout(function(){d.focus()},50),u.addEventListener("click",function(){let m=d.value.trim();if(!m)return;let g=Ze(),_=new Date().toISOString(),R=e?X(e):null,O=e?Wt(e):null;if(O){o._annotationMark=O;let F=Math.max(0,O.getBoundingClientRect().top+window.scrollY-i);o._anchorTop=F,_t()}let q={id:a(),siteId:V,pageUrl:L,quote:t,anchor:R,body:m,author:g,authorId:K,createdAt:_,updatedAt:_,resolved:!1,resolvedAt:null,resolvedBy:null,replies:[],dirty:!0,deletedAt:null};o._threadId=q.id,o.dataset.threadId=q.id,st.add(q.id),O&&(It(O).forEach(function(F){F._threadId=q.id}),at[q.id]=O),v&&b(v,q).then(function(){return At(q),St("thread_created",q.id,null,g,"created: '"+m.slice(0,40)+"'")}).catch(function(F){console.warn("Annotate.js: save failed",F)}),Zt(o,q)}),y.addEventListener("click",function(){o.remove(),M.querySelector(".annotate-card")||(H.style.display="")})}function $o(t,e){if(t.querySelector(".annotate-reply-composer"))return;let o=document.createElement("div");o.className="annotate-reply-action",o.innerHTML=`
<textarea class="annotate-reply-composer" placeholder="Reply\u2026"></textarea>
<div class="annotate-card-actions">
<button class="annotate-btn-cancel">Cancel</button>
<button class="annotate-btn-save">Reply</button>
</div>
`,t.appendChild(o);let i=o.querySelector(".annotate-reply-composer");setTimeout(function(){i.focus()},50),o.querySelector(".annotate-btn-save").addEventListener("click",function(){let r=i.value.trim();if(!r)return;let d=Ze(),u={id:a(),body:r,author:d,authorId:K,createdAt:new Date().toISOString(),updatedAt:null,deleted:!1};v&&c(v,e._threadId).then(function(m){if(m)return m.replies.push(u),m.updatedAt=new Date().toISOString(),m.dirty=!0,b(v,m).then(function(){return At(m),St("reply_added",m.id,u.id,d,"replied: '"+r.slice(0,40)+"'")})}).catch(function(m){console.warn("Annotate.js: reply persist failed",m)});let y=rn(u);sn(y,u.id,e,u),t.insertBefore(y,o),o.remove()}),o.querySelector(".annotate-btn-cancel").addEventListener("click",function(){o.remove()})}let rt=document.createElement("button");rt.id="annotate-comment-btn",rt.title="Add a comment",rt.classList.add("hidden"),rt.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"/><path d="M7 11h10"/><path d="M7 15h6"/><path d="M7 7h8"/></svg>',document.documentElement.appendChild(rt);let Qt="",fe=null;function te(){rt.classList.add("hidden"),Qt="",fe=null}document.addEventListener("mouseup",function(t){if(G.contains(t.target)||rt.contains(t.target))return;if(t.target.closest(".annotate-highlight")){$t();var e=t.target.closest(".annotate-highlight"),o=e._threadId;o&&en(o);return}let i=window.getSelection(),r=i?i.toString().trim():"";if(!r)return;Qt=r,fe=i.getRangeAt(0).cloneRange();let u=i.getRangeAt(0).getBoundingClientRect(),y=28,m=u.left+u.width/2-y/2,g=u.top-y-6;m<8&&(m=8),m+y>window.innerWidth-8&&(m=window.innerWidth-y-8),rt.style.left=m+"px",rt.style.top=g+"px",rt.classList.remove("hidden")}),rt.addEventListener("click",function(){let t=Qt,e=fe;te(),$t(),Nt("Threads"),zo(t,e)}),document.addEventListener("mousedown",function(t){rt.contains(t.target)||te()});var pe=null;document.addEventListener("selectionchange",function(){pe&&clearTimeout(pe),pe=setTimeout(function(){pe=null;var t=window.getSelection(),e=t?t.toString().trim():"";if(!e||!t.rangeCount){te();return}if(!(Qt===e&&!rt.classList.contains("hidden"))){Qt=e,fe=t.getRangeAt(0).cloneRange();var o=t.getRangeAt(0).getBoundingClientRect(),i=36,r=o.left+o.width/2-i/2,d=o.top-i-6;r<8&&(r=8),r+i>window.innerWidth-8&&(r=window.innerWidth-i-8),rt.style.left=r+"px",rt.style.top=d+"px",rt.classList.remove("hidden")}},300)}),document.addEventListener("touchstart",function(t){rt.contains(t.target)||te()},{passive:!0}),document.addEventListener("keydown",function(t){t.key==="Escape"&&te()}),l(V).then(function(t){return v=t,Ho(t)}).then(function(){return f(v,L).then(an)}).then(function(){B&&(Le(),Ce(),qo(),setInterval(function(){Le(),Ce()},j),document.addEventListener("visibilitychange",function(){document.visibilityState==="visible"&&(Le(),Ce())})),tt&&!B&&Uo()}).catch(function(t){console.warn("Annotate.js: IndexedDB unavailable \u2014 running in memory-only mode",t)})})();})();
/*! Bundled license information:
@noble/secp256k1/index.js:
(*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
*/