@tacktext/widget 0.1.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/index.mjs ADDED
@@ -0,0 +1,1909 @@
1
+ var k=class{constructor(e,t){this.authToken=null;this.onUnauthorized=null;this.baseUrl=e,this.projectId=t}setAuthToken(e){this.authToken=e}setOnUnauthorized(e){this.onUnauthorized=e}getHeaders(){let e={"Content-Type":"application/json"};return this.authToken&&(e.Authorization=`Bearer ${this.authToken}`),e}async handleResponse(e){return e.status===401&&this.onUnauthorized?.(),e}async getComments(){let e=await fetch(`${this.baseUrl}/projects/${this.projectId}/comments?path=${encodeURIComponent(window.location.pathname)}`,{headers:this.getHeaders()});if(await this.handleResponse(e),!e.ok)throw new Error("Failed to fetch comments");return e.json()}async createComment(e){let t=await fetch(`${this.baseUrl}/comments`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({project_id:this.projectId,...e})});if(await this.handleResponse(t),!t.ok)throw new Error("Failed to create comment");return t.json()}async updateComment(e,t){let i=await fetch(`${this.baseUrl}/comments/${e}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(t)});if(await this.handleResponse(i),!i.ok)throw new Error("Failed to update comment");return i.json()}async deleteComment(e){let t=await fetch(`${this.baseUrl}/comments/${e}`,{method:"DELETE",headers:this.getHeaders()});if(await this.handleResponse(t),!t.ok)throw new Error("Failed to delete comment")}async uploadScreenshot(e){let t=await fetch(`${this.baseUrl}/upload`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({image:e,project_id:this.projectId})});if(await this.handleResponse(t),!t.ok)throw new Error("Failed to upload screenshot");let{url:i}=await t.json();return i}};var b=class{constructor(e){this.sessionToken=null;this.user=null;this.onAuthChange=null;this.apiUrl=e}async init(){let e=localStorage.getItem("tack_session");if(e)try{let t=await fetch(`${this.apiUrl}/auth/widget-session`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok){localStorage.removeItem("tack_session");return}let{user:i}=await t.json();this.sessionToken=e,this.user={id:i.user_id,name:i.user_name,email:i.user_email,avatar_url:i.user_avatar_url},this.onAuthChange?.(this.user)}catch{localStorage.removeItem("tack_session")}}signIn(){return new Promise((e,t)=>{let i=this.apiUrl.replace(/\/api$/,""),n=window.open(`${i}/auth/widget`,"tack-auth","width=500,height=600,popup=yes");if(!n){t(new Error("Popup blocked"));return}let r=new URL(this.apiUrl).origin,a=o=>{if(o.data?.type!=="tack:auth"||o.origin!==r)return;window.removeEventListener("message",a),clearInterval(s);let{token:c,user:d}=o.data;this.sessionToken=c,this.user={id:d.user_id,name:d.user_name,email:d.user_email,avatar_url:d.user_avatar_url},localStorage.setItem("tack_session",c),this.onAuthChange?.(this.user),e(this.user)};window.addEventListener("message",a);let s=setInterval(()=>{n.closed&&(clearInterval(s),window.removeEventListener("message",a),this.user||t(new Error("Auth popup closed")))},500)})}signOut(){this.sessionToken&&fetch(`${this.apiUrl}/auth/widget-session`,{method:"DELETE",headers:{Authorization:`Bearer ${this.sessionToken}`}}).catch(()=>{}),this.sessionToken=null,this.user=null,localStorage.removeItem("tack_session"),this.onAuthChange?.(null)}getUser(){return this.user}getSessionToken(){return this.sessionToken}isAuthenticated(){return this.user!==null}setOnAuthChange(e){this.onAuthChange=e}};function H(){return`
2
+ * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ .tack-wrapper {
7
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
8
+ font-size: 14px;
9
+ line-height: 1.5;
10
+ color: #18181b;
11
+ }
12
+
13
+ /* Toggle Button - Teal gradient */
14
+ .tack-toggle {
15
+ position: fixed;
16
+ bottom: 20px;
17
+ right: 20px;
18
+ width: 52px;
19
+ height: 52px;
20
+ border-radius: 16px;
21
+ background: linear-gradient(135deg, #14B8A6 0%, #0D9488 100%);
22
+ color: white;
23
+ border: none;
24
+ cursor: pointer;
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ box-shadow:
29
+ 0 4px 14px rgba(20, 184, 166, 0.4),
30
+ 0 2px 4px rgba(0, 0, 0, 0.1);
31
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
32
+ z-index: 10000;
33
+ }
34
+
35
+ .tack-toggle:hover {
36
+ transform: translateY(-2px) scale(1.02);
37
+ box-shadow:
38
+ 0 8px 20px rgba(20, 184, 166, 0.5),
39
+ 0 4px 8px rgba(0, 0, 0, 0.1);
40
+ }
41
+
42
+ .tack-toggle:active {
43
+ transform: scale(0.98);
44
+ }
45
+
46
+ .tack-active .tack-toggle {
47
+ opacity: 0;
48
+ pointer-events: none;
49
+ }
50
+
51
+ /* Pins container - hidden by default */
52
+ .tack-pins-container {
53
+ display: none;
54
+ }
55
+
56
+ .tack-pins-container.visible {
57
+ display: block;
58
+ }
59
+
60
+ /* Comment Pin \u2014 unified white shell */
61
+ .tack-pin {
62
+ position: absolute;
63
+ cursor: pointer;
64
+ z-index: 9999;
65
+ transition: transform 150ms ease, filter 150ms ease;
66
+ filter: drop-shadow(0 2px 6px rgba(0,0,0,0.15)) drop-shadow(0 1px 2px rgba(0,0,0,0.10));
67
+ }
68
+
69
+ .tack-pin:hover {
70
+ filter: drop-shadow(0 4px 12px rgba(0,0,0,0.18)) drop-shadow(0 2px 4px rgba(0,0,0,0.12));
71
+ z-index: 10000;
72
+ }
73
+
74
+ /* White shell \u2014 the unified container (circle + ::after tail)
75
+ Uses CSS Grid so avatar and preview stack in the same cell.
76
+ The shell's width/max-height control which content is visible
77
+ via overflow:hidden, enabling smooth morph animations. */
78
+ .tack-pin-shell {
79
+ position: relative;
80
+ background: #FFFFFF;
81
+ border-radius: 19px;
82
+ padding: 3px;
83
+ display: grid;
84
+ align-items: start;
85
+ width: 38px;
86
+ max-height: 38px;
87
+ will-change: width, max-height, border-radius;
88
+ }
89
+
90
+ /* All direct children stack in the same grid cell */
91
+ .tack-pin-shell > * {
92
+ grid-area: 1 / 1;
93
+ }
94
+
95
+ /* Multi-author shell becomes pill-shaped */
96
+ .tack-pin.multi-author .tack-pin-shell {
97
+ border-radius: 17px;
98
+ padding: 3px;
99
+ width: auto;
100
+ max-height: 34px;
101
+ }
102
+
103
+ /* White tail as ::after pseudo-element \u2014 seamless with shell.
104
+ Height is 13px (not 6px) so the top of the triangle extends behind
105
+ the circle where it curves away from the bounding box. The opaque
106
+ circle paints over the hidden overlap, eliminating the gap. */
107
+ .tack-pin-shell::after {
108
+ content: '';
109
+ position: absolute;
110
+ bottom: -7px;
111
+ left: 2px;
112
+ width: 18px;
113
+ height: 16px;
114
+ background: #FFFFFF;
115
+ clip-path: polygon(25% 100%, 100% 0%, 0% 0%);
116
+ z-index: -1;
117
+ }
118
+
119
+ /* Multi-author: tail under first avatar */
120
+ .tack-pin.multi-author .tack-pin-shell::after {
121
+ left: 10px;
122
+ }
123
+
124
+ /* Avatar inset \u2014 sits inside the shell with white padding visible */
125
+ .tack-pin-avatar-img {
126
+ border-radius: 50%;
127
+ object-fit: cover;
128
+ display: block;
129
+ }
130
+
131
+ .tack-pin-avatar-fallback {
132
+ border-radius: 50%;
133
+ color: white;
134
+ font-weight: 600;
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ line-height: 1;
139
+ }
140
+
141
+ /* Multi-author avatar stack inside the shell */
142
+ .tack-pin-avatars {
143
+ display: flex;
144
+ align-items: center;
145
+ z-index: 1;
146
+ transition: opacity 120ms ease;
147
+ }
148
+
149
+ .tack-pin-avatar-stacked {
150
+ width: 28px;
151
+ height: 28px;
152
+ border-radius: 50%;
153
+ margin-left: -8px;
154
+ position: relative;
155
+ flex-shrink: 0;
156
+ overflow: hidden;
157
+ line-height: 1;
158
+ border: 2px solid #FFFFFF;
159
+ }
160
+
161
+ .tack-pin-avatar-stacked:first-child {
162
+ margin-left: 0;
163
+ }
164
+
165
+ .tack-pin-avatar-stacked img,
166
+ .tack-pin-avatar-stacked div {
167
+ display: block;
168
+ }
169
+
170
+ /* Overflow "+N" chip */
171
+ .tack-pin-avatar-overflow {
172
+ background: #E4E4E7;
173
+ color: #52525B;
174
+ font-size: 9px;
175
+ font-weight: 600;
176
+ display: flex;
177
+ align-items: center;
178
+ justify-content: center;
179
+ }
180
+
181
+ /* \u2500\u2500 Hover Preview Card \u2500\u2500 */
182
+
183
+ /* Shell transitions \u2014 collapse timing by default (faster close) */
184
+ .tack-pin-shell {
185
+ overflow: hidden;
186
+ transition:
187
+ width 160ms cubic-bezier(0.4, 0, 0.2, 1),
188
+ max-height 160ms cubic-bezier(0.4, 0, 0.2, 1),
189
+ border-radius 160ms cubic-bezier(0.4, 0, 0.2, 1),
190
+ padding 160ms cubic-bezier(0.4, 0, 0.2, 1);
191
+ }
192
+
193
+ /* Avatar: fade out on expand instead of display:none */
194
+ .tack-pin-shell > .tack-pin-avatar-img,
195
+ .tack-pin-shell > .tack-pin-avatar-fallback {
196
+ z-index: 1;
197
+ transition: opacity 120ms ease;
198
+ }
199
+
200
+ /* Expanded shell \u2014 smooth open */
201
+ .tack-pin.expanded .tack-pin-shell {
202
+ width: 256px;
203
+ max-height: 130px;
204
+ border-radius: 14px;
205
+ padding: 3px;
206
+ transition:
207
+ width 240ms cubic-bezier(0.16, 1, 0.3, 1),
208
+ max-height 240ms cubic-bezier(0.16, 1, 0.3, 1),
209
+ border-radius 240ms cubic-bezier(0.16, 1, 0.3, 1),
210
+ padding 240ms cubic-bezier(0.16, 1, 0.3, 1);
211
+ }
212
+
213
+ .tack-pin.expanded.multi-author .tack-pin-shell {
214
+ border-radius: 14px;
215
+ width: 256px;
216
+ max-height: 130px;
217
+ }
218
+
219
+ /* Fade avatar/stacked avatars when expanded (not display:none) */
220
+ .tack-pin.expanded .tack-pin-shell > .tack-pin-avatar-img,
221
+ .tack-pin.expanded .tack-pin-shell > .tack-pin-avatar-fallback {
222
+ opacity: 0;
223
+ pointer-events: none;
224
+ }
225
+
226
+ .tack-pin.expanded .tack-pin-avatars {
227
+ opacity: 0;
228
+ pointer-events: none;
229
+ }
230
+
231
+ /* Preview card: always rendered, clipped by shell overflow when collapsed */
232
+ .tack-pin-preview {
233
+ display: flex;
234
+ width: 240px;
235
+ flex-direction: column;
236
+ padding: 6px 8px 8px 8px;
237
+ opacity: 0;
238
+ pointer-events: none;
239
+ align-self: start;
240
+ justify-self: start;
241
+ }
242
+
243
+ .tack-pin.expanded .tack-pin-preview {
244
+ opacity: 1;
245
+ pointer-events: auto;
246
+ transition:
247
+ opacity 120ms cubic-bezier(0.4, 0, 0.2, 1) 100ms;
248
+ }
249
+
250
+ /* Top row: avatar + meta (name + time) */
251
+ .tack-pin-preview-header {
252
+ display: flex;
253
+ align-items: center;
254
+ gap: 8px;
255
+ }
256
+
257
+ .tack-pin-preview-avatar {
258
+ width: 28px;
259
+ height: 28px;
260
+ border-radius: 50%;
261
+ flex-shrink: 0;
262
+ overflow: hidden;
263
+ }
264
+
265
+ .tack-pin-preview-avatar img,
266
+ .tack-pin-preview-avatar div {
267
+ display: block;
268
+ }
269
+
270
+ .tack-pin-preview-meta {
271
+ flex: 1;
272
+ min-width: 0;
273
+ display: flex;
274
+ align-items: baseline;
275
+ gap: 4px;
276
+ }
277
+
278
+ .tack-pin-preview-name {
279
+ font-size: 12.5px;
280
+ font-weight: 600;
281
+ color: #18181b;
282
+ white-space: nowrap;
283
+ overflow: hidden;
284
+ text-overflow: ellipsis;
285
+ max-width: 120px;
286
+ }
287
+
288
+ .tack-pin-preview-time {
289
+ font-size: 11px;
290
+ color: #a1a1aa;
291
+ white-space: nowrap;
292
+ flex-shrink: 0;
293
+ }
294
+
295
+ /* Comment text preview \u2014 staggered fade */
296
+ .tack-pin-preview-text {
297
+ font-size: 12.5px;
298
+ line-height: 1.4;
299
+ color: #3f3f46;
300
+ display: -webkit-box;
301
+ -webkit-line-clamp: 2;
302
+ -webkit-box-orient: vertical;
303
+ overflow: hidden;
304
+ text-overflow: ellipsis;
305
+ word-break: break-word;
306
+ margin: 4px 0 0 0;
307
+ opacity: 0;
308
+ transform: translateY(4px);
309
+ }
310
+
311
+ .tack-pin.expanded .tack-pin-preview-text {
312
+ opacity: 1;
313
+ transform: translateY(0);
314
+ transition:
315
+ opacity 120ms cubic-bezier(0.4, 0, 0.2, 1) 140ms,
316
+ transform 120ms cubic-bezier(0.4, 0, 0.2, 1) 140ms;
317
+ }
318
+
319
+ /* Reply count badge */
320
+ .tack-pin-preview-replies {
321
+ font-size: 11px;
322
+ color: #a1a1aa;
323
+ margin-top: 4px;
324
+ opacity: 0;
325
+ }
326
+
327
+ .tack-pin.expanded .tack-pin-preview-replies {
328
+ opacity: 1;
329
+ transition: opacity 100ms ease 180ms;
330
+ }
331
+
332
+ /* Expanded pin gets higher z-index */
333
+ .tack-pin.expanded {
334
+ z-index: 10000;
335
+ }
336
+
337
+ /* Expand-left variant (near right viewport edge) \u2014
338
+ shift shell leftward so the card doesn't overflow viewport */
339
+ .tack-pin.expand-left.expanded .tack-pin-shell {
340
+ transform: translateX(-218px);
341
+ }
342
+
343
+ /* Active pins should not show the preview (card is already open) */
344
+ .tack-pin.active .tack-pin-preview {
345
+ opacity: 0 !important;
346
+ pointer-events: none !important;
347
+ }
348
+
349
+ .tack-pin.active.expanded .tack-pin-shell > .tack-pin-avatar-img,
350
+ .tack-pin.active.expanded .tack-pin-shell > .tack-pin-avatar-fallback {
351
+ opacity: 1;
352
+ pointer-events: auto;
353
+ }
354
+
355
+ .tack-pin.active.expanded .tack-pin-avatars {
356
+ opacity: 1;
357
+ pointer-events: auto;
358
+ }
359
+
360
+ .tack-pin.active.expanded .tack-pin-shell {
361
+ border-radius: 19px;
362
+ width: 38px;
363
+ max-height: 38px;
364
+ }
365
+
366
+ .tack-pin.active.expanded.multi-author .tack-pin-shell {
367
+ border-radius: 17px;
368
+ width: auto;
369
+ max-height: 34px;
370
+ }
371
+
372
+ /* On touch devices, suppress hover preview entirely */
373
+ @media (hover: none) {
374
+ .tack-pin.expanded .tack-pin-preview {
375
+ opacity: 0;
376
+ pointer-events: none;
377
+ }
378
+
379
+ .tack-pin.expanded .tack-pin-shell > .tack-pin-avatar-img,
380
+ .tack-pin.expanded .tack-pin-shell > .tack-pin-avatar-fallback {
381
+ opacity: 1;
382
+ pointer-events: auto;
383
+ }
384
+
385
+ .tack-pin.expanded .tack-pin-avatars {
386
+ opacity: 1;
387
+ pointer-events: auto;
388
+ }
389
+
390
+ .tack-pin.expanded .tack-pin-shell {
391
+ border-radius: 19px;
392
+ width: 38px;
393
+ max-height: 38px;
394
+ }
395
+
396
+ .tack-pin.expanded.multi-author .tack-pin-shell {
397
+ border-radius: 17px;
398
+ width: auto;
399
+ max-height: 34px;
400
+ }
401
+ }
402
+
403
+ @media (prefers-reduced-motion: reduce) {
404
+ .tack-pin-shell,
405
+ .tack-pin.expanded .tack-pin-shell,
406
+ .tack-pin-preview,
407
+ .tack-pin.expanded .tack-pin-preview,
408
+ .tack-pin-preview-text,
409
+ .tack-pin.expanded .tack-pin-preview-text {
410
+ transition-duration: 0ms !important;
411
+ transition-delay: 0ms !important;
412
+ }
413
+ }
414
+
415
+ /* Resolved pin: green check dot */
416
+ .tack-pin-check {
417
+ position: absolute;
418
+ bottom: 8px;
419
+ right: -2px;
420
+ width: 14px;
421
+ height: 14px;
422
+ border-radius: 50%;
423
+ background: #10B981;
424
+ border: 2px solid white;
425
+ z-index: 10;
426
+ }
427
+
428
+ /* Resolved: fade entire pin */
429
+ .tack-pin.resolved {
430
+ opacity: 0.5;
431
+ }
432
+
433
+ .tack-pin.resolved:hover {
434
+ opacity: 0.85;
435
+ }
436
+
437
+ /* Active / selected pin \u2014 solid blue ring on shell */
438
+ .tack-pin.active {
439
+ transform: scale(1.05);
440
+ }
441
+
442
+ .tack-pin.active .tack-pin-shell {
443
+ box-shadow: 0 0 0 2.5px #2563EB;
444
+ }
445
+
446
+ /* Highlighted \u2014 finite pulse, 2 cycles */
447
+ .tack-pin.highlighted {
448
+ animation: tack-pulse 1.5s ease-in-out 2;
449
+ }
450
+
451
+ @keyframes tack-pulse {
452
+ 0%, 100% {
453
+ filter: drop-shadow(0 2px 6px rgba(0,0,0,0.15)) drop-shadow(0 1px 2px rgba(0,0,0,0.10));
454
+ }
455
+ 50% {
456
+ filter: drop-shadow(0 0 6px rgba(37,99,235,0.3)) drop-shadow(0 2px 8px rgba(0,0,0,0.10));
457
+ }
458
+ }
459
+
460
+ @media (prefers-reduced-motion: reduce) {
461
+ .tack-pin.highlighted {
462
+ animation: none;
463
+ filter: drop-shadow(0 0 4px rgba(37,99,235,0.25)) drop-shadow(0 2px 8px rgba(0,0,0,0.10));
464
+ }
465
+ }
466
+
467
+ /* Panel Strip */
468
+ .tack-panel-strip {
469
+ position: fixed;
470
+ top: 0;
471
+ right: 0;
472
+ width: 360px;
473
+ height: 100vh;
474
+ background: transparent;
475
+ padding: 0;
476
+ transform: translateX(100%);
477
+ transition: transform 250ms cubic-bezier(0.16, 1, 0.3, 1);
478
+ z-index: 10001;
479
+ display: flex;
480
+ flex-direction: column;
481
+ }
482
+
483
+ .tack-panel-strip.open {
484
+ transform: translateX(0);
485
+ }
486
+
487
+ /* Panel Card */
488
+ .tack-panel {
489
+ flex: 1;
490
+ background: #FFFFFF;
491
+ border-radius: 0;
492
+ border: none;
493
+ box-shadow:
494
+ 0 0 0 1px rgba(0, 0, 0, 0.04),
495
+ 0 2px 4px rgba(0, 0, 0, 0.04),
496
+ 0 8px 24px rgba(0, 0, 0, 0.08);
497
+ display: flex;
498
+ flex-direction: column;
499
+ overflow: hidden;
500
+ }
501
+
502
+ .tack-panel-header {
503
+ padding: 12px 16px;
504
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
505
+ display: flex;
506
+ align-items: center;
507
+ gap: 10px;
508
+ }
509
+
510
+ .tack-panel-close {
511
+ display: flex;
512
+ align-items: center;
513
+ justify-content: center;
514
+ width: 28px;
515
+ height: 28px;
516
+ border-radius: 6px;
517
+ background: transparent;
518
+ border: none;
519
+ cursor: pointer;
520
+ color: #71717a;
521
+ flex-shrink: 0;
522
+ transition: all 0.15s ease;
523
+ }
524
+
525
+ .tack-panel-close:hover {
526
+ background: #f4f4f5;
527
+ color: #18181b;
528
+ }
529
+
530
+ /* Search bar */
531
+ .tack-panel-search-wrap {
532
+ flex: 1;
533
+ display: flex;
534
+ align-items: center;
535
+ gap: 6px;
536
+ background: #f4f4f5;
537
+ border-radius: 6px;
538
+ padding: 0 8px;
539
+ min-height: 32px;
540
+ min-width: 0;
541
+ }
542
+
543
+ .tack-panel-search-icon {
544
+ flex-shrink: 0;
545
+ color: #a1a1aa;
546
+ }
547
+
548
+ .tack-panel-search {
549
+ flex: 1;
550
+ border: none;
551
+ background: transparent;
552
+ font-size: 13px;
553
+ font-family: inherit;
554
+ color: #18181b;
555
+ outline: none;
556
+ min-width: 0;
557
+ padding: 0;
558
+ line-height: 32px;
559
+ }
560
+
561
+ .tack-panel-search::placeholder {
562
+ color: #a1a1aa;
563
+ }
564
+
565
+ .tack-panel-search-clear {
566
+ display: flex;
567
+ align-items: center;
568
+ justify-content: center;
569
+ width: 20px;
570
+ height: 20px;
571
+ border-radius: 4px;
572
+ border: none;
573
+ background: transparent;
574
+ cursor: pointer;
575
+ color: #a1a1aa;
576
+ flex-shrink: 0;
577
+ padding: 0;
578
+ transition: all 0.1s ease;
579
+ }
580
+
581
+ .tack-panel-search-clear:hover {
582
+ background: rgba(0, 0, 0, 0.08);
583
+ color: #71717a;
584
+ }
585
+
586
+ /* Icon buttons (filter + more) */
587
+ .tack-panel-filter-wrap,
588
+ .tack-panel-more-wrap {
589
+ position: relative;
590
+ flex-shrink: 0;
591
+ }
592
+
593
+ .tack-panel-filter-btn,
594
+ .tack-panel-more-btn {
595
+ display: flex;
596
+ align-items: center;
597
+ justify-content: center;
598
+ width: 32px;
599
+ height: 32px;
600
+ border-radius: 6px;
601
+ background: transparent;
602
+ border: none;
603
+ cursor: pointer;
604
+ color: #71717a;
605
+ position: relative;
606
+ transition: all 0.15s ease;
607
+ padding: 0;
608
+ }
609
+
610
+ .tack-panel-filter-btn:hover,
611
+ .tack-panel-more-btn:hover {
612
+ background: #f4f4f5;
613
+ color: #18181b;
614
+ }
615
+
616
+ .tack-panel-filter-btn.filter-active {
617
+ color: #2563EB;
618
+ }
619
+
620
+ .tack-panel-filter-btn.filter-active:hover {
621
+ color: #1d4ed8;
622
+ }
623
+
624
+ /* Filter badge (count) */
625
+ .tack-panel-filter-badge {
626
+ position: absolute;
627
+ top: 2px;
628
+ right: 2px;
629
+ min-width: 16px;
630
+ height: 16px;
631
+ border-radius: 8px;
632
+ background: #2563EB;
633
+ color: white;
634
+ font-size: 10px;
635
+ font-weight: 600;
636
+ display: flex;
637
+ align-items: center;
638
+ justify-content: center;
639
+ padding: 0 4px;
640
+ line-height: 1;
641
+ }
642
+
643
+ /* Dark dropdown menu */
644
+ .tack-panel-filter-dropdown,
645
+ .tack-panel-more-dropdown {
646
+ position: absolute;
647
+ top: calc(100% + 6px);
648
+ right: 0;
649
+ background: #1e1e1e;
650
+ border-radius: 10px;
651
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.06);
652
+ z-index: 100;
653
+ min-width: 220px;
654
+ padding: 4px;
655
+ overflow: hidden;
656
+ }
657
+
658
+ .tack-panel-filter-dropdown[data-state="closed"],
659
+ .tack-panel-more-dropdown[data-state="closed"] {
660
+ display: none;
661
+ }
662
+
663
+ .tack-panel-filter-dropdown[data-state="open"],
664
+ .tack-panel-more-dropdown[data-state="open"] {
665
+ display: block;
666
+ animation: tack-dropdown-in 0.12s ease;
667
+ }
668
+
669
+ @keyframes tack-dropdown-in {
670
+ from {
671
+ opacity: 0;
672
+ transform: translateY(-4px);
673
+ }
674
+ to {
675
+ opacity: 1;
676
+ transform: translateY(0);
677
+ }
678
+ }
679
+
680
+ .tack-dropdown-item {
681
+ display: flex;
682
+ align-items: center;
683
+ gap: 8px;
684
+ width: 100%;
685
+ padding: 8px 10px;
686
+ font-size: 13px;
687
+ font-family: inherit;
688
+ background: transparent;
689
+ border: none;
690
+ cursor: pointer;
691
+ text-align: left;
692
+ color: #e4e4e7;
693
+ border-radius: 6px;
694
+ transition: background 0.1s ease;
695
+ }
696
+
697
+ .tack-dropdown-item:hover {
698
+ background: rgba(255, 255, 255, 0.08);
699
+ }
700
+
701
+ .tack-dropdown-check {
702
+ flex-shrink: 0;
703
+ color: #a1a1aa;
704
+ }
705
+
706
+ .tack-dropdown-check-space {
707
+ display: inline-block;
708
+ width: 14px;
709
+ flex-shrink: 0;
710
+ }
711
+
712
+ .tack-dropdown-sep {
713
+ height: 1px;
714
+ background: rgba(255, 255, 255, 0.08);
715
+ margin: 4px 10px;
716
+ }
717
+
718
+ /* Toggle items */
719
+ .tack-dropdown-toggle {
720
+ justify-content: space-between;
721
+ }
722
+
723
+ .tack-dropdown-toggle-switch {
724
+ width: 32px;
725
+ height: 18px;
726
+ border-radius: 9px;
727
+ background: rgba(255, 255, 255, 0.15);
728
+ position: relative;
729
+ flex-shrink: 0;
730
+ transition: background 0.15s ease;
731
+ }
732
+
733
+ .tack-dropdown-toggle-switch.on {
734
+ background: #2563EB;
735
+ }
736
+
737
+ .tack-dropdown-toggle-knob {
738
+ position: absolute;
739
+ top: 2px;
740
+ left: 2px;
741
+ width: 14px;
742
+ height: 14px;
743
+ border-radius: 50%;
744
+ background: white;
745
+ transition: transform 0.15s ease;
746
+ }
747
+
748
+ .tack-dropdown-toggle-switch.on .tack-dropdown-toggle-knob {
749
+ transform: translateX(14px);
750
+ }
751
+
752
+ /* Unread dot */
753
+ .tack-unread-dot {
754
+ width: 8px;
755
+ height: 8px;
756
+ border-radius: 50%;
757
+ background: #2563EB;
758
+ flex-shrink: 0;
759
+ }
760
+
761
+ .tack-comment-row-top {
762
+ display: flex;
763
+ align-items: center;
764
+ gap: 8px;
765
+ margin-bottom: 6px;
766
+ }
767
+
768
+ /* Empty state reset button */
769
+ .tack-empty-reset {
770
+ display: inline-block;
771
+ margin-top: 12px;
772
+ padding: 6px 14px;
773
+ font-size: 12px;
774
+ font-weight: 500;
775
+ font-family: inherit;
776
+ border: 1px solid #e4e4e7;
777
+ border-radius: 6px;
778
+ background: white;
779
+ color: #71717a;
780
+ cursor: pointer;
781
+ transition: all 0.15s ease;
782
+ }
783
+
784
+ .tack-empty-reset:hover {
785
+ background: #f4f4f5;
786
+ color: #18181b;
787
+ border-color: #d4d4d8;
788
+ }
789
+
790
+ .tack-panel-content {
791
+ flex: 1;
792
+ overflow-y: auto;
793
+ padding: 6px 0;
794
+ mask-image: linear-gradient(
795
+ to bottom,
796
+ transparent 0%,
797
+ black 8px,
798
+ black calc(100% - 8px),
799
+ transparent 100%
800
+ );
801
+ }
802
+
803
+ /* Comment Row \u2014 ghost card on hover */
804
+ .tack-comment-row {
805
+ padding: 14px 16px;
806
+ margin: 0 6px;
807
+ border-radius: 8px;
808
+ cursor: pointer;
809
+ transition: background-color 150ms ease-out;
810
+ position: relative;
811
+ }
812
+
813
+ .tack-comment-row:hover {
814
+ background-color: rgba(0, 0, 0, 0.04);
815
+ }
816
+
817
+ /* Row action buttons (overflow + resolve) */
818
+ .tack-comment-row-actions {
819
+ position: absolute;
820
+ top: 10px;
821
+ right: 10px;
822
+ display: flex;
823
+ align-items: center;
824
+ gap: 2px;
825
+ opacity: 0;
826
+ transition: opacity 120ms ease;
827
+ }
828
+
829
+ .tack-comment-row:hover .tack-comment-row-actions {
830
+ opacity: 1;
831
+ }
832
+
833
+ .tack-comment-row-more,
834
+ .tack-comment-row-resolve {
835
+ width: 28px;
836
+ height: 28px;
837
+ border-radius: 6px;
838
+ border: none;
839
+ background: transparent;
840
+ cursor: pointer;
841
+ color: #a1a1aa;
842
+ display: flex;
843
+ align-items: center;
844
+ justify-content: center;
845
+ padding: 0;
846
+ transition: all 120ms ease;
847
+ }
848
+
849
+ .tack-comment-row-more:hover,
850
+ .tack-comment-row-resolve:hover {
851
+ background: rgba(0, 0, 0, 0.06);
852
+ color: #18181b;
853
+ }
854
+
855
+ .tack-comment-row-resolve.resolved {
856
+ color: #10B981;
857
+ }
858
+
859
+ .tack-comment-row-resolve.resolved:hover {
860
+ background: rgba(16, 185, 129, 0.1);
861
+ color: #059669;
862
+ }
863
+
864
+ .tack-comment-row:active {
865
+ background-color: rgba(0, 0, 0, 0.06);
866
+ transition-duration: 50ms;
867
+ }
868
+
869
+ .tack-comment-row.highlighted {
870
+ background-color: rgba(20, 184, 166, 0.06);
871
+ }
872
+
873
+ .tack-comment-row.highlighted::before {
874
+ content: '';
875
+ position: absolute;
876
+ left: 0;
877
+ top: 6px;
878
+ bottom: 6px;
879
+ width: 3px;
880
+ border-radius: 0 3px 3px 0;
881
+ background: #14B8A6;
882
+ }
883
+
884
+ .tack-comment-avatar {
885
+ width: 32px;
886
+ height: 32px;
887
+ border-radius: 50%;
888
+ display: flex;
889
+ align-items: center;
890
+ justify-content: center;
891
+ color: white;
892
+ font-size: 12px;
893
+ font-weight: 600;
894
+ flex-shrink: 0;
895
+ line-height: 1;
896
+ margin-bottom: 8px;
897
+ }
898
+
899
+ .tack-comment-meta {
900
+ display: flex;
901
+ align-items: baseline;
902
+ gap: 4px;
903
+ flex-wrap: wrap;
904
+ margin-bottom: 4px;
905
+ }
906
+
907
+ .tack-comment-author {
908
+ font-weight: 600;
909
+ font-size: 13px;
910
+ color: #18181b;
911
+ }
912
+
913
+ .tack-comment-time {
914
+ font-size: 12px;
915
+ color: rgba(0, 0, 0, 0.35);
916
+ font-weight: 400;
917
+ }
918
+
919
+ .tack-comment-resolved-badge {
920
+ font-size: 11px;
921
+ font-weight: 500;
922
+ padding: 1px 6px;
923
+ border-radius: 9999px;
924
+ background: #ECFDF5;
925
+ color: #059669;
926
+ line-height: 1.4;
927
+ }
928
+
929
+ .tack-comment-page-badge {
930
+ font-size: 11px;
931
+ font-weight: 500;
932
+ padding: 1px 6px;
933
+ border-radius: 9999px;
934
+ background: #EEF2FF;
935
+ color: #4F46E5;
936
+ line-height: 1.4;
937
+ }
938
+
939
+ .tack-comment-body {
940
+ padding-left: 0;
941
+ margin-top: 2px;
942
+ }
943
+
944
+ .tack-comment-body.dimmed {
945
+ opacity: 0.6;
946
+ }
947
+
948
+ .tack-comment-screenshot {
949
+ width: 120px;
950
+ height: 64px;
951
+ object-fit: cover;
952
+ border-radius: 6px;
953
+ margin-bottom: 6px;
954
+ border: 1px solid #e4e4e7;
955
+ display: block;
956
+ }
957
+
958
+ .tack-comment-content {
959
+ font-size: 13.5px;
960
+ color: #3f3f46;
961
+ line-height: 1.5;
962
+ }
963
+
964
+ /* New Comment Input */
965
+ .tack-new-comment {
966
+ padding: 10px 12px;
967
+ border-top: 1px solid rgba(0, 0, 0, 0.06);
968
+ }
969
+
970
+ .tack-new-comment-input {
971
+ display: flex;
972
+ align-items: center;
973
+ gap: 8px;
974
+ padding: 10px 12px;
975
+ background: #f4f4f5;
976
+ border: 1px dashed #d4d4d8;
977
+ border-radius: 8px;
978
+ font-size: 13px;
979
+ color: #71717a;
980
+ cursor: pointer;
981
+ transition: all 0.15s ease;
982
+ font-weight: 500;
983
+ }
984
+
985
+ .tack-new-comment-input:hover {
986
+ background: #e4e4e7;
987
+ border-color: #a1a1aa;
988
+ color: #18181b;
989
+ }
990
+
991
+ /* Comment Form - Floating pill input */
992
+ .tack-form {
993
+ position: fixed;
994
+ z-index: 10003;
995
+ display: none;
996
+ }
997
+
998
+ .tack-form.visible {
999
+ display: block;
1000
+ animation: tack-form-in 0.15s ease;
1001
+ }
1002
+
1003
+ @keyframes tack-form-in {
1004
+ from {
1005
+ opacity: 0;
1006
+ transform: translateY(4px);
1007
+ }
1008
+ to {
1009
+ opacity: 1;
1010
+ transform: translateY(0);
1011
+ }
1012
+ }
1013
+
1014
+ /* Pill container */
1015
+ .tack-form-pill {
1016
+ display: flex;
1017
+ align-items: flex-end;
1018
+ gap: 6px;
1019
+ background: #FFFFFF;
1020
+ border: 1px solid #D1D5DB;
1021
+ border-radius: 20px;
1022
+ padding: 4px 4px 4px 16px;
1023
+ width: 300px;
1024
+ box-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.06);
1025
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
1026
+ }
1027
+
1028
+ .tack-form-pill:focus-within {
1029
+ border-color: #9CA3AF;
1030
+ box-shadow: 0 4px 12px rgba(0,0,0,0.10), 0 1px 3px rgba(0,0,0,0.06);
1031
+ }
1032
+
1033
+ /* Textarea styled as single-line input */
1034
+ .tack-form-pill-input {
1035
+ flex: 1;
1036
+ border: none;
1037
+ background: transparent;
1038
+ font-size: 13px;
1039
+ font-family: inherit;
1040
+ color: #111827;
1041
+ outline: none;
1042
+ resize: none;
1043
+ min-width: 0;
1044
+ padding: 4px 0;
1045
+ line-height: 20px;
1046
+ height: 28px;
1047
+ overflow-y: hidden;
1048
+ min-height: 28px;
1049
+ }
1050
+
1051
+ .tack-form-pill-input::placeholder {
1052
+ color: #9CA3AF;
1053
+ }
1054
+
1055
+ /* Send button */
1056
+ .tack-form-pill-send {
1057
+ width: 28px;
1058
+ height: 28px;
1059
+ border-radius: 50%;
1060
+ border: none;
1061
+ background: #E5E7EB;
1062
+ color: #9CA3AF;
1063
+ cursor: pointer;
1064
+ display: flex;
1065
+ align-items: center;
1066
+ justify-content: center;
1067
+ flex-shrink: 0;
1068
+ transition: background-color 150ms ease, color 150ms ease;
1069
+ }
1070
+
1071
+ .tack-form-pill-send.active {
1072
+ background: #18181B;
1073
+ color: #FFFFFF;
1074
+ }
1075
+
1076
+ .tack-form-pill-send.active:hover {
1077
+ background: #27272A;
1078
+ }
1079
+
1080
+ .tack-form-pill-send.sending {
1081
+ opacity: 0.5;
1082
+ pointer-events: none;
1083
+ }
1084
+
1085
+ /* Empty State */
1086
+ .tack-empty {
1087
+ text-align: center;
1088
+ padding: 40px 20px;
1089
+ }
1090
+
1091
+ .tack-empty-icon {
1092
+ margin-bottom: 12px;
1093
+ margin-left: auto;
1094
+ margin-right: auto;
1095
+ color: #a1a1aa;
1096
+ }
1097
+
1098
+ .tack-empty-title {
1099
+ font-size: 14px;
1100
+ font-weight: 500;
1101
+ color: #18181b;
1102
+ margin: 0 0 4px 0;
1103
+ }
1104
+
1105
+ .tack-empty-subtitle {
1106
+ font-size: 12px;
1107
+ color: #a1a1aa;
1108
+ margin: 0;
1109
+ }
1110
+
1111
+ /* Comment Card Popover */
1112
+ .tack-card {
1113
+ position: fixed;
1114
+ width: 320px;
1115
+ background: white;
1116
+ border-radius: 12px;
1117
+ border: 1px solid #e4e4e7;
1118
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04);
1119
+ z-index: 10003;
1120
+ max-height: 480px;
1121
+ overflow-y: auto;
1122
+ display: none;
1123
+ flex-direction: column;
1124
+ }
1125
+
1126
+ .tack-card.visible {
1127
+ display: flex;
1128
+ animation: tack-form-in 0.15s ease;
1129
+ }
1130
+
1131
+ /* Card title bar */
1132
+ .tack-card-title-bar {
1133
+ display: flex;
1134
+ align-items: center;
1135
+ padding: 10px 12px;
1136
+ border-bottom: 1px solid #F4F4F5;
1137
+ flex-shrink: 0;
1138
+ }
1139
+
1140
+ .tack-card-title {
1141
+ flex: 1;
1142
+ font-weight: 600;
1143
+ font-size: 13px;
1144
+ color: #18181b;
1145
+ }
1146
+
1147
+ .tack-card-title-actions {
1148
+ display: flex;
1149
+ align-items: center;
1150
+ gap: 2px;
1151
+ }
1152
+
1153
+ .tack-card-resolve-icon,
1154
+ .tack-card-close-btn {
1155
+ width: 28px;
1156
+ height: 28px;
1157
+ border-radius: 6px;
1158
+ border: none;
1159
+ background: transparent;
1160
+ cursor: pointer;
1161
+ color: #A1A1AA;
1162
+ display: flex;
1163
+ align-items: center;
1164
+ justify-content: center;
1165
+ transition: all 0.15s ease;
1166
+ }
1167
+
1168
+ .tack-card-resolve-icon:hover,
1169
+ .tack-card-close-btn:hover {
1170
+ background: #F4F4F5;
1171
+ color: #18181b;
1172
+ }
1173
+
1174
+ .tack-card-resolve-icon.resolved {
1175
+ color: #10B981;
1176
+ }
1177
+
1178
+ /* Resolved banner */
1179
+ .tack-card-resolved-banner {
1180
+ display: flex;
1181
+ align-items: center;
1182
+ gap: 6px;
1183
+ padding: 8px 12px;
1184
+ background: #ECFDF5;
1185
+ color: #059669;
1186
+ font-size: 12px;
1187
+ font-weight: 500;
1188
+ flex-shrink: 0;
1189
+ }
1190
+
1191
+ /* Card content area (scrollable) */
1192
+ .tack-card-row {
1193
+ padding: 12px;
1194
+ position: relative;
1195
+ }
1196
+
1197
+ .tack-card-row:hover .tack-card-more-btn {
1198
+ opacity: 1;
1199
+ }
1200
+
1201
+ .tack-card-row-header {
1202
+ display: flex;
1203
+ align-items: center;
1204
+ gap: 8px;
1205
+ }
1206
+
1207
+ .tack-card-row-avatar {
1208
+ width: 28px;
1209
+ height: 28px;
1210
+ border-radius: 50%;
1211
+ display: flex;
1212
+ align-items: center;
1213
+ justify-content: center;
1214
+ color: white;
1215
+ font-size: 10px;
1216
+ font-weight: 600;
1217
+ flex-shrink: 0;
1218
+ line-height: 1;
1219
+ }
1220
+
1221
+ .tack-card-row-meta {
1222
+ flex: 1;
1223
+ min-width: 0;
1224
+ display: flex;
1225
+ align-items: center;
1226
+ gap: 4px;
1227
+ flex-wrap: wrap;
1228
+ }
1229
+
1230
+ .tack-card-row-author {
1231
+ font-weight: 600;
1232
+ font-size: 13px;
1233
+ color: #18181b;
1234
+ }
1235
+
1236
+ .tack-card-row-time {
1237
+ font-size: 12px;
1238
+ color: #A1A1AA;
1239
+ }
1240
+
1241
+ .tack-card-editing-badge {
1242
+ font-size: 11px;
1243
+ font-weight: 600;
1244
+ color: #6366F1;
1245
+ }
1246
+
1247
+ /* More (three-dot) button */
1248
+ .tack-card-more-btn {
1249
+ width: 24px;
1250
+ height: 24px;
1251
+ border-radius: 6px;
1252
+ border: none;
1253
+ background: #F4F4F5;
1254
+ cursor: pointer;
1255
+ color: #71717a;
1256
+ display: flex;
1257
+ align-items: center;
1258
+ justify-content: center;
1259
+ flex-shrink: 0;
1260
+ opacity: 0;
1261
+ transition: opacity 0.15s ease, background 0.15s ease;
1262
+ }
1263
+
1264
+ .tack-card-more-btn:hover {
1265
+ background: #E4E4E7;
1266
+ color: #18181b;
1267
+ }
1268
+
1269
+ /* Dropdown menu */
1270
+ .tack-card-dropdown {
1271
+ position: absolute;
1272
+ top: 42px;
1273
+ right: 12px;
1274
+ background: white;
1275
+ border: 1px solid #E4E4E7;
1276
+ border-radius: 8px;
1277
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
1278
+ z-index: 10;
1279
+ overflow: hidden;
1280
+ min-width: 140px;
1281
+ }
1282
+
1283
+ .tack-card-dropdown-item {
1284
+ display: block;
1285
+ width: 100%;
1286
+ padding: 8px 12px;
1287
+ font-size: 13px;
1288
+ font-family: inherit;
1289
+ background: transparent;
1290
+ border: none;
1291
+ cursor: pointer;
1292
+ text-align: left;
1293
+ color: #18181b;
1294
+ transition: background 0.1s ease;
1295
+ }
1296
+
1297
+ .tack-card-dropdown-item:hover {
1298
+ background: #F4F4F5;
1299
+ }
1300
+
1301
+ .tack-card-dropdown-item.delete {
1302
+ color: #EF4444;
1303
+ }
1304
+
1305
+ .tack-card-dropdown-item.delete:hover {
1306
+ background: #FEF2F2;
1307
+ }
1308
+
1309
+ /* Comment body text */
1310
+ .tack-card-row-body {
1311
+ padding: 6px 0 0 36px;
1312
+ font-size: 13px;
1313
+ color: #3F3F46;
1314
+ line-height: 1.5;
1315
+ }
1316
+
1317
+ .tack-card-row-screenshot {
1318
+ width: 200px;
1319
+ border-radius: 6px;
1320
+ object-fit: cover;
1321
+ border: 1px solid #e4e4e7;
1322
+ display: block;
1323
+ margin: 8px 0 0 36px;
1324
+ }
1325
+
1326
+ /* Divider */
1327
+ .tack-card-divider {
1328
+ height: 1px;
1329
+ background: #F4F4F5;
1330
+ margin: 0;
1331
+ }
1332
+
1333
+ /* Edit mode */
1334
+ .tack-card-edit-wrap {
1335
+ padding: 8px 0 0 36px;
1336
+ }
1337
+
1338
+ .tack-card-edit-textarea {
1339
+ width: 100%;
1340
+ padding: 8px 10px;
1341
+ border: 1px solid #18181B;
1342
+ border-radius: 6px;
1343
+ font-size: 13px;
1344
+ font-family: inherit;
1345
+ resize: vertical;
1346
+ min-height: 60px;
1347
+ line-height: 1.5;
1348
+ color: #18181b;
1349
+ background: white;
1350
+ }
1351
+
1352
+ .tack-card-edit-textarea:focus {
1353
+ outline: none;
1354
+ }
1355
+
1356
+ .tack-card-edit-actions {
1357
+ display: flex;
1358
+ justify-content: flex-end;
1359
+ gap: 6px;
1360
+ margin-top: 8px;
1361
+ }
1362
+
1363
+ .tack-card-edit-cancel {
1364
+ padding: 5px 12px;
1365
+ font-size: 12px;
1366
+ font-weight: 500;
1367
+ border: 1px solid #E4E4E7;
1368
+ border-radius: 6px;
1369
+ background: white;
1370
+ cursor: pointer;
1371
+ color: #71717a;
1372
+ font-family: inherit;
1373
+ transition: all 0.15s ease;
1374
+ }
1375
+
1376
+ .tack-card-edit-cancel:hover {
1377
+ background: #F4F4F5;
1378
+ color: #18181b;
1379
+ }
1380
+
1381
+ .tack-card-edit-save {
1382
+ padding: 5px 12px;
1383
+ font-size: 12px;
1384
+ font-weight: 500;
1385
+ border: none;
1386
+ border-radius: 6px;
1387
+ background: #18181B;
1388
+ color: white;
1389
+ cursor: pointer;
1390
+ font-family: inherit;
1391
+ transition: all 0.15s ease;
1392
+ }
1393
+
1394
+ .tack-card-edit-save:hover {
1395
+ background: #27272a;
1396
+ }
1397
+
1398
+ /* Reply bar \u2014 always visible */
1399
+ .tack-card-reply-bar {
1400
+ display: flex;
1401
+ align-items: center;
1402
+ gap: 8px;
1403
+ padding: 10px 12px;
1404
+ flex-shrink: 0;
1405
+ }
1406
+
1407
+ .tack-card-reply-bar-avatar {
1408
+ width: 24px;
1409
+ height: 24px;
1410
+ border-radius: 50%;
1411
+ display: flex;
1412
+ align-items: center;
1413
+ justify-content: center;
1414
+ color: white;
1415
+ font-size: 9px;
1416
+ font-weight: 600;
1417
+ flex-shrink: 0;
1418
+ line-height: 1;
1419
+ }
1420
+
1421
+ .tack-card-reply-bar-input-wrap {
1422
+ flex: 1;
1423
+ display: flex;
1424
+ align-items: center;
1425
+ background: #F4F4F5;
1426
+ border-radius: 9999px;
1427
+ padding: 0 4px 0 12px;
1428
+ min-height: 32px;
1429
+ }
1430
+
1431
+ .tack-card-reply-bar-input {
1432
+ flex: 1;
1433
+ border: none;
1434
+ background: transparent;
1435
+ font-size: 13px;
1436
+ font-family: inherit;
1437
+ color: #18181b;
1438
+ outline: none;
1439
+ min-width: 0;
1440
+ }
1441
+
1442
+ .tack-card-reply-bar-input::placeholder {
1443
+ color: #A1A1AA;
1444
+ }
1445
+
1446
+ .tack-card-reply-bar-send {
1447
+ width: 24px;
1448
+ height: 24px;
1449
+ border-radius: 50%;
1450
+ border: none;
1451
+ background: #18181B;
1452
+ color: white;
1453
+ cursor: pointer;
1454
+ display: flex;
1455
+ align-items: center;
1456
+ justify-content: center;
1457
+ flex-shrink: 0;
1458
+ transition: background 0.15s ease;
1459
+ }
1460
+
1461
+ .tack-card-reply-bar-send:hover {
1462
+ background: #27272a;
1463
+ }
1464
+
1465
+ /* Sign-in pill variant */
1466
+ .tack-form-pill-signin {
1467
+ align-items: center;
1468
+ padding: 6px 6px 6px 16px;
1469
+ cursor: default;
1470
+ }
1471
+
1472
+ .tack-sign-in-prompt-text {
1473
+ flex: 1;
1474
+ font-size: 13px;
1475
+ color: #9CA3AF;
1476
+ margin: 0;
1477
+ line-height: 20px;
1478
+ }
1479
+
1480
+ .tack-sign-in-btn {
1481
+ display: inline-flex;
1482
+ align-items: center;
1483
+ gap: 6px;
1484
+ padding: 6px 12px;
1485
+ border: none;
1486
+ border-radius: 14px;
1487
+ background: #F4F4F5;
1488
+ cursor: pointer;
1489
+ font-size: 12px;
1490
+ font-weight: 500;
1491
+ color: #3c4043;
1492
+ font-family: inherit;
1493
+ transition: all 0.15s ease;
1494
+ flex-shrink: 0;
1495
+ white-space: nowrap;
1496
+ }
1497
+
1498
+ .tack-sign-in-btn:hover {
1499
+ background: #E5E7EB;
1500
+ color: #18181b;
1501
+ }
1502
+
1503
+ .tack-sign-in-btn:active {
1504
+ background: #D1D5DB;
1505
+ }
1506
+
1507
+ /* Avatar image support */
1508
+ .tack-avatar-img {
1509
+ flex-shrink: 0;
1510
+ }
1511
+
1512
+ .tack-avatar-fallback {
1513
+ flex-shrink: 0;
1514
+ }
1515
+
1516
+ .tack-card-row-avatar-wrap,
1517
+ .tack-card-reply-bar-avatar-wrap {
1518
+ display: flex;
1519
+ flex-shrink: 0;
1520
+ }
1521
+
1522
+ /* Presence bar */
1523
+ .tack-presence-bar {
1524
+ display: flex;
1525
+ align-items: center;
1526
+ justify-content: space-between;
1527
+ padding: 8px 16px;
1528
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
1529
+ }
1530
+
1531
+ .tack-presence-avatars {
1532
+ display: flex;
1533
+ align-items: center;
1534
+ }
1535
+
1536
+ .tack-presence-avatar {
1537
+ margin-left: -6px;
1538
+ border-radius: 50%;
1539
+ border: 2px solid white;
1540
+ display: flex;
1541
+ line-height: 1;
1542
+ }
1543
+
1544
+ .tack-presence-avatar:first-child {
1545
+ margin-left: 0;
1546
+ }
1547
+
1548
+ .tack-presence-overflow {
1549
+ margin-left: -6px;
1550
+ width: 24px;
1551
+ height: 24px;
1552
+ border-radius: 50%;
1553
+ background: #e4e4e7;
1554
+ color: #52525b;
1555
+ font-size: 10px;
1556
+ font-weight: 600;
1557
+ display: flex;
1558
+ align-items: center;
1559
+ justify-content: center;
1560
+ border: 2px solid white;
1561
+ }
1562
+
1563
+ .tack-presence-label {
1564
+ font-size: 12px;
1565
+ color: #a1a1aa;
1566
+ font-weight: 500;
1567
+ }
1568
+
1569
+ /* Toast */
1570
+ .tack-toast {
1571
+ animation: tack-toast-in 0.2s ease;
1572
+ }
1573
+
1574
+ @keyframes tack-toast-in {
1575
+ from {
1576
+ opacity: 0;
1577
+ transform: translateY(8px);
1578
+ }
1579
+ to {
1580
+ opacity: 1;
1581
+ transform: translateY(0);
1582
+ }
1583
+ }
1584
+ `}var x=class{constructor(e){this.enabled=!1;this.highlightedElement=null;this.highlight=null;this.scrim=null;this.cursor=null;this.styleElement=null;this.cursorRAF=null;this.cursorX=0;this.cursorY=0;this.targetX=0;this.targetY=0;this.settleTimer=null;this.pendingTarget=null;this.callback=e,this.onMouseMove=this.onMouseMove.bind(this),this.onMouseDown=this.onMouseDown.bind(this),this.onClick=this.onClick.bind(this),this.onKeyDown=this.onKeyDown.bind(this),this.animateCursor=this.animateCursor.bind(this)}enable(){this.enabled||(this.enabled=!0,document.addEventListener("mousemove",this.onMouseMove),document.addEventListener("mousedown",this.onMouseDown,!0),document.addEventListener("click",this.onClick,!0),document.addEventListener("keydown",this.onKeyDown),this.scrim=document.createElement("div"),this.scrim.style.cssText=`
1585
+ position: fixed;
1586
+ inset: 0;
1587
+ background: rgba(0, 0, 0, 0.03);
1588
+ z-index: 9997;
1589
+ pointer-events: none;
1590
+ transition: opacity 0.2s ease;
1591
+ `,document.body.appendChild(this.scrim),this.highlight=document.createElement("div"),this.highlight.style.cssText=`
1592
+ position: fixed;
1593
+ pointer-events: none;
1594
+ z-index: 9998;
1595
+ opacity: 0;
1596
+ transition: opacity 0.2s ease, left 0.15s ease-out, top 0.15s ease-out, width 0.15s ease-out, height 0.15s ease-out, border-radius 0.15s ease-out;
1597
+ border-radius: 6px;
1598
+ box-shadow: 0 0 0 1.5px rgba(0, 0, 0, 0.08), 0 2px 12px rgba(0, 0, 0, 0.06);
1599
+ background: rgba(255, 255, 255, 0.4);
1600
+ `,document.body.appendChild(this.highlight),this.cursor=document.createElement("div"),this.cursor.style.cssText=`
1601
+ position: fixed;
1602
+ pointer-events: none;
1603
+ z-index: 10004;
1604
+ width: 28px;
1605
+ height: 28px;
1606
+ border-radius: 50%;
1607
+ background: #18181B;
1608
+ color: white;
1609
+ display: flex;
1610
+ align-items: center;
1611
+ justify-content: center;
1612
+ font-size: 16px;
1613
+ font-weight: 300;
1614
+ font-family: -apple-system, BlinkMacSystemFont, sans-serif;
1615
+ line-height: 1;
1616
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
1617
+ transform: translate(-50%, -50%);
1618
+ opacity: 0;
1619
+ transition: opacity 0.15s ease;
1620
+ `,this.cursor.textContent="+",document.body.appendChild(this.cursor),requestAnimationFrame(()=>{this.cursor&&(this.cursor.style.opacity="1")}),this.styleElement=document.createElement("style"),this.styleElement.textContent=`
1621
+ body, body *:not(#tack-widget) { cursor: none !important; }
1622
+ #tack-widget, #tack-widget * { cursor: default !important; }
1623
+ `,document.head.appendChild(this.styleElement),this.cursorRAF=requestAnimationFrame(this.animateCursor))}disable(){this.enabled&&(this.enabled=!1,document.removeEventListener("mousemove",this.onMouseMove),document.removeEventListener("mousedown",this.onMouseDown,!0),document.removeEventListener("click",this.onClick,!0),document.removeEventListener("keydown",this.onKeyDown),this.settleTimer&&(clearTimeout(this.settleTimer),this.settleTimer=null),this.highlight&&(this.highlight.remove(),this.highlight=null),this.scrim&&(this.scrim.remove(),this.scrim=null),this.cursor&&(this.cursor.remove(),this.cursor=null),this.styleElement&&(this.styleElement.remove(),this.styleElement=null),this.cursorRAF&&(cancelAnimationFrame(this.cursorRAF),this.cursorRAF=null),this.highlightedElement=null,this.pendingTarget=null)}animateCursor(){this.cursorX+=(this.targetX-this.cursorX)*.2,this.cursorY+=(this.targetY-this.cursorY)*.2,this.cursor&&(this.cursor.style.left=`${this.cursorX}px`,this.cursor.style.top=`${this.cursorY}px`),this.cursorRAF=requestAnimationFrame(this.animateCursor)}onMouseDown(e){e.target.closest("#tack-widget")||(e.preventDefault(),e.stopPropagation())}onMouseMove(e){this.targetX=e.clientX,this.targetY=e.clientY;let t=e.target;if(t.closest("#tack-widget")){this.clearHighlight(),this.cursor&&(this.cursor.style.opacity="0");return}this.cursor&&this.cursor.style.opacity==="0"&&(this.cursor.style.opacity="1"),t!==this.pendingTarget&&t!==this.highlightedElement&&(this.pendingTarget=t,this.settleTimer&&clearTimeout(this.settleTimer),this.settleTimer=setTimeout(()=>{this.pendingTarget&&this.pendingTarget!==this.highlightedElement&&this.showHighlight(this.pendingTarget),this.pendingTarget=null},150))}showHighlight(e){if(this.highlightedElement=e,this.highlight){let t=e.getBoundingClientRect(),i=3;this.highlight.style.left=`${t.left-i}px`,this.highlight.style.top=`${t.top-i}px`,this.highlight.style.width=`${t.width+i*2}px`,this.highlight.style.height=`${t.height+i*2}px`,this.highlight.style.opacity="1";let r=window.getComputedStyle(e).borderRadius;this.highlight.style.borderRadius=r&&r!=="0px"?r:"6px"}}onClick(e){if(e.target.closest("#tack-widget"))return;e.preventDefault(),e.stopPropagation();let t=e.target,i=t.getBoundingClientRect(),n=(e.clientX-i.left)/i.width,r=(e.clientY-i.top)/i.height,a=this.generateAnchor(t,n,r);this.callback({element:t,anchor:a}),this.clearHighlight()}onKeyDown(e){e.key==="Escape"&&this.disable()}clearHighlight(){this.highlightedElement=null,this.pendingTarget=null,this.settleTimer&&(clearTimeout(this.settleTimer),this.settleTimer=null),this.highlight&&(this.highlight.style.opacity="0")}generateAnchor(e,t,i){return{cssSelector:this.generateCssSelector(e),xpath:this.generateXPath(e),textQuote:this.generateTextQuote(e),contentHash:this.generateContentHash(e),relativeX:t,relativeY:i,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight}}generateCssSelector(e){if(e.id&&!this.isDynamicId(e.id))return`#${CSS.escape(e.id)}`;let t=[],i=e;for(;i&&i!==document.body;){let n=i.tagName.toLowerCase();if(i.className&&typeof i.className=="string"){let o=i.className.trim().split(/\s+/).filter(c=>!this.isDynamicClass(c));o.length>0&&(n+="."+o.slice(0,2).map(c=>CSS.escape(c)).join("."))}let r=i.getAttribute("data-testid")||i.getAttribute("data-test-id");r&&(n+=`[data-testid="${CSS.escape(r)}"]`);let a=i.parentElement;if(a){let o=Array.from(a.children).filter(c=>c.tagName===i.tagName);if(o.length>1){let c=o.indexOf(i)+1;n+=`:nth-child(${c})`}}t.unshift(n);let s=t.join(" > ");try{if(document.querySelectorAll(s).length===1)return s}catch{}i=a}return t.join(" > ")}generateXPath(e){let t=[],i=e;for(;i&&i!==document.body;){let n=i.tagName.toLowerCase(),r=i.parentElement;if(r){let a=Array.from(r.children).filter(s=>s.tagName===i.tagName);if(a.length>1){let s=a.indexOf(i)+1;n+=`[${s}]`}}t.unshift(n),i=r}return"//"+t.join("/")}generateTextQuote(e){let t=e.textContent?.trim();if(!t||t.length===0||t.length>500)return null;let i=e.parentElement,n="",r="";if(i){let a=i.textContent||"",s=a.indexOf(t);s>0&&(n=a.slice(Math.max(0,s-32),s).trim());let o=s+t.length;o<a.length&&(r=a.slice(o,o+32).trim())}return{prefix:n,exact:t.slice(0,200),suffix:r}}generateContentHash(e){let t=e.textContent?.trim().slice(0,500)||"",i=e.tagName.toLowerCase(),n=e.className?.toString()||"",r=`${i}:${n}:${t}`,a=5381;for(let s=0;s<r.length;s++)a=(a<<5)+a+r.charCodeAt(s);return(a>>>0).toString(16)}isDynamicId(e){return/^[:_]/.test(e)||/[0-9]{5,}/.test(e)||/^(ember|react|vue|ng-|_)[0-9]+/.test(e)||/^[a-f0-9]{8,}$/i.test(e)}isDynamicClass(e){return e.startsWith("tack-")||/^css-[a-z0-9]+$/i.test(e)||/^sc-[a-z]+$/i.test(e)||/^emotion-[0-9]+$/i.test(e)||/^_[a-zA-Z0-9]{5,}$/.test(e)||/^[a-z]{1,3}[A-Z][a-zA-Z0-9]{10,}$/.test(e)}};async function _(l,e,t){let i=(await import("html2canvas")).default;try{if(l){let c=document.querySelector(l);if(c){let d=c.getBoundingClientRect(),h=20;return(await i(document.body,{logging:!1,useCORS:!0,allowTaint:!0,scale:1,x:Math.max(0,d.left+window.scrollX-h),y:Math.max(0,d.top+window.scrollY-h),width:Math.min(d.width+h*2,600),height:Math.min(d.height+h*2,400)})).toDataURL("image/png",.8)}}let n=e/100*document.documentElement.scrollWidth,r=t/100*document.documentElement.scrollHeight,a=400,s=300;return(await i(document.body,{logging:!1,useCORS:!0,allowTaint:!0,scale:1,x:Math.max(0,n-a/2),y:Math.max(0,r-s/2),width:a,height:s})).toDataURL("image/png",.8)}catch(n){console.warn("Tack: Screenshot capture failed",n);return}}var w=class{constructor(e,t){this.onHide=null;this.onSignIn=null;this.currentTarget=null;this.isSubmitting=!1;this.capturedScreenshot=void 0;this.highlightElement=null;this.isVisible=!1;this.currentUser=null;this.container=e,this.onSubmit=t,this.form=this.createForm(),this.container.appendChild(this.form),this.highlightElement=document.createElement("div"),this.highlightElement.className="tack-form-highlight",this.highlightElement.style.cssText=`
1624
+ position: fixed;
1625
+ pointer-events: none;
1626
+ border: 2px solid #14B8A6;
1627
+ border-radius: 4px;
1628
+ background: rgba(20, 184, 166, 0.1);
1629
+ z-index: 10002;
1630
+ display: none;
1631
+ transition: all 0.15s ease;
1632
+ `,document.body.appendChild(this.highlightElement)}setOnHide(e){this.onHide=e}setOnSignIn(e){this.onSignIn=e}setUser(e){this.currentUser=e,this.rebuildFormContent()}createForm(){let e=document.createElement("div");return e.className="tack-form",this.buildFormInner(e),e}rebuildFormContent(){this.buildFormInner(this.form)}buildFormInner(e){this.currentUser?e.innerHTML=`
1633
+ <div class="tack-form-pill">
1634
+ <textarea class="tack-form-pill-input" id="tack-comment-content" placeholder="Add a comment..." rows="1"></textarea>
1635
+ <button type="button" class="tack-form-pill-send" aria-label="Send">
1636
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
1637
+ <path d="M7 11.5V2.5M7 2.5L3 6.5M7 2.5L11 6.5" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/>
1638
+ </svg>
1639
+ </button>
1640
+ </div>
1641
+ `:e.innerHTML=`
1642
+ <div class="tack-form-pill tack-form-pill-signin">
1643
+ <span class="tack-sign-in-prompt-text">Sign in to leave feedback</span>
1644
+ <button type="button" class="tack-sign-in-btn">
1645
+ <svg width="14" height="14" viewBox="0 0 24 24">
1646
+ <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/>
1647
+ <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
1648
+ <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
1649
+ <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
1650
+ </svg>
1651
+ Sign in
1652
+ </button>
1653
+ </div>
1654
+ `,this.attachFormListeners(e)}attachFormListeners(e){e.addEventListener("mousedown",i=>i.stopPropagation()),e.addEventListener("click",i=>i.stopPropagation()),e.querySelector(".tack-sign-in-btn")?.addEventListener("click",i=>{i.stopPropagation(),this.onSignIn?.()}),e.querySelector(".tack-form-pill-send")?.addEventListener("click",i=>{i.stopPropagation(),this.handleSubmit()});let t=e.querySelector("#tack-comment-content");t&&(t.addEventListener("input",()=>{this.autoExpandTextarea(t);let i=e.querySelector(".tack-form-pill-send");i&&i.classList.toggle("active",t.value.trim().length>0)}),t.addEventListener("keydown",i=>{let n=i;n.key==="Enter"&&!n.shiftKey&&(n.preventDefault(),this.handleSubmit())})),e.addEventListener("keydown",i=>{let n=i;n.key==="Escape"&&(n.preventDefault(),this.hide())})}async show(e){if(this.isVisible)return;if(this.currentTarget=e,this.isVisible=!0,this.capturedScreenshot=await _(e.anchor.cssSelector,e.anchor.relativeX*100,e.anchor.relativeY*100),e.element&&this.highlightElement){let o=e.element.getBoundingClientRect();this.highlightElement.style.display="block",this.highlightElement.style.left=`${o.left-4}px`,this.highlightElement.style.top=`${o.top-4}px`,this.highlightElement.style.width=`${o.width+8}px`,this.highlightElement.style.height=`${o.height+8}px`}let t,i;if(e.element){let o=e.element.getBoundingClientRect();t=o.right+12,i=o.top;let c=300;t+c>window.innerWidth-380&&(t=o.left-c-12)}else t=e.anchor.relativeX*window.innerWidth+20,i=e.anchor.relativeY*window.innerHeight;let n=300,r=52,a=Math.min(t,window.innerWidth-n-380),s=Math.min(i,window.innerHeight-r-20);a=Math.max(20,a),s=Math.max(20,s),this.form.style.left=`${a}px`,this.form.style.top=`${s}px`,this.form.classList.add("visible"),setTimeout(()=>{this.form.querySelector("#tack-comment-content")?.focus()},50)}hide(){if(!this.isVisible)return;this.isVisible=!1,this.form.classList.remove("visible"),this.currentTarget=null,this.capturedScreenshot=void 0;let e=this.form.querySelector("#tack-comment-content");e&&(e.value="",e.style.height="28px",e.style.overflowY="hidden");let t=this.form.querySelector(".tack-form-pill-send");t&&t.classList.remove("active"),this.highlightElement&&(this.highlightElement.style.display="none"),this.onHide?.()}isFormVisible(){return this.isVisible}autoExpandTextarea(e){e.style.height="auto";let t=120;e.style.height=`${Math.min(e.scrollHeight,t)}px`,e.style.overflowY=e.scrollHeight>t?"auto":"hidden"}async handleSubmit(){if(this.isSubmitting||!this.currentTarget)return;let e=this.form.querySelector("#tack-comment-content")?.value.trim();if(!e)return;let t;if(this.currentUser)t=this.currentUser.name;else if(t=this.form.querySelector("#tack-author-name")?.value.trim()||"",!t)return;this.isSubmitting=!0;let i=this.form.querySelector(".tack-form-pill-send");i&&i.classList.add("sending");try{this.currentUser||localStorage.setItem("tack_author_name",t);let n={content:e,authorName:t,anchor:this.currentTarget.anchor,screenshot:this.capturedScreenshot};this.hide(),await this.onSubmit(n)}catch(n){console.error("[Tack] Submit failed:",n)}finally{this.isSubmitting=!1,i&&i.classList.remove("sending")}}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}};var M=[["#818CF8","#6366F1"],["#A78BFA","#7C3AED"],["#F472B6","#EC4899"],["#FBBF24","#F59E0B"],["#34D399","#10B981"],["#60A5FA","#3B82F6"],["#F87171","#EF4444"],["#2DD4BF","#14B8A6"]],I=M.map(([,l])=>l);function D(l){let e=0;for(let t=0;t<l.length;t++)e=l.charCodeAt(t)+((e<<5)-e);return Math.abs(e)}function U(l){return I[D(l)%I.length]}function P(l){let[e,t]=M[D(l)%M.length];return`linear-gradient(135deg, ${e} 0%, ${t} 100%)`}function p(l){let e=l.trim().split(/\s+/);return e.length>=2?(e[0][0]+e[e.length-1][0]).toUpperCase():l.slice(0,2).toUpperCase()}function u(l,e,t,i=""){let n=Math.round(t*.38),r=i?` ${i}`:"";return e?`<img
1655
+ src="${e}"
1656
+ alt="${p(l)}"
1657
+ class="tack-avatar-img${r}"
1658
+ style="width:${t}px;height:${t}px;border-radius:50%;object-fit:cover;"
1659
+ onerror="this.style.display='none';this.nextElementSibling.style.display='flex'"
1660
+ /><div
1661
+ class="tack-avatar-fallback${r}"
1662
+ style="display:none;width:${t}px;height:${t}px;border-radius:50%;background:${U(l)};color:white;font-size:${n}px;font-weight:600;align-items:center;justify-content:center;line-height:1;flex-shrink:0"
1663
+ >${p(l)}</div>`:`<div
1664
+ class="tack-avatar-fallback${r}"
1665
+ style="display:flex;width:${t}px;height:${t}px;border-radius:50%;background:${U(l)};color:white;font-size:${n}px;font-weight:600;align-items:center;justify-content:center;line-height:1;flex-shrink:0"
1666
+ >${p(l)}</div>`}var z="tack_panel_filters";function q(){try{let l=localStorage.getItem(z);if(l){let e=JSON.parse(l);return{sort:e.sort||"date",showResolved:e.showResolved??!1,onlyYourThreads:e.onlyYourThreads??!1,onlyCurrentPage:e.onlyCurrentPage??!1}}}catch{}return{sort:"date",showResolved:!1,onlyYourThreads:!1,onlyCurrentPage:!1}}function O(l){try{localStorage.setItem(z,JSON.stringify(l))}catch{}}var y=class{constructor(e,t){this.comments=[];this.searchQuery="";this.searchTimer=null;this.highlightedId=null;this.filterDropdownOpen=!1;this.moreDropdownOpen=!1;this.currentUser=null;this.projectId="";this.container=e,this.callbacks=t,this.filters=q(),this.readCommentIds=this.loadReadIds(),this.strip=this.createPanelStrip(),this.panel=this.strip.querySelector(".tack-panel"),this.container.appendChild(this.strip)}setUser(e){this.currentUser=e}setProjectId(e){this.projectId=e,this.readCommentIds=this.loadReadIds()}loadReadIds(){try{let e=`tack_read_${this.projectId}`,t=localStorage.getItem(e);if(t)return new Set(JSON.parse(t))}catch{}return new Set}saveReadIds(){try{let e=`tack_read_${this.projectId}`;localStorage.setItem(e,JSON.stringify([...this.readCommentIds]))}catch{}}markAsRead(e){this.readCommentIds.has(e)||(this.readCommentIds.add(e),this.saveReadIds())}markVisibleAsRead(){if(!this.isOpen())return;let e=this.getFilteredComments(),t=!1;for(let i of e)this.readCommentIds.has(i.id)||(this.readCommentIds.add(i.id),t=!0);t&&this.saveReadIds()}isRead(e){return this.readCommentIds.has(e)}createPanelStrip(){let e=document.createElement("div");e.className="tack-panel-strip",e.innerHTML=`
1667
+ <div class="tack-panel" role="complementary" aria-label="Comments panel">
1668
+ <div class="tack-panel-header">
1669
+ <button class="tack-panel-close" aria-label="Close comments panel">
1670
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1671
+ <line x1="18" y1="6" x2="6" y2="18"></line>
1672
+ <line x1="6" y1="6" x2="18" y2="18"></line>
1673
+ </svg>
1674
+ </button>
1675
+ <div class="tack-panel-search-wrap">
1676
+ <svg class="tack-panel-search-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1677
+ <circle cx="11" cy="11" r="8"></circle>
1678
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
1679
+ </svg>
1680
+ <input type="text" class="tack-panel-search" placeholder="Search" />
1681
+ <button class="tack-panel-search-clear" style="display:none;">
1682
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1683
+ <line x1="18" y1="6" x2="6" y2="18"></line>
1684
+ <line x1="6" y1="6" x2="18" y2="18"></line>
1685
+ </svg>
1686
+ </button>
1687
+ </div>
1688
+ <div class="tack-panel-filter-wrap">
1689
+ <button class="tack-panel-filter-btn">
1690
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
1691
+ <line x1="4" y1="6" x2="20" y2="6"></line>
1692
+ <line x1="7" y1="12" x2="17" y2="12"></line>
1693
+ <line x1="10" y1="18" x2="14" y2="18"></line>
1694
+ </svg>
1695
+ <span class="tack-panel-filter-badge" style="display:none;"></span>
1696
+ </button>
1697
+ <div class="tack-panel-filter-dropdown" data-state="closed"></div>
1698
+ </div>
1699
+ <div class="tack-panel-more-wrap">
1700
+ <button class="tack-panel-more-btn">
1701
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1702
+ <circle cx="12" cy="5" r="1" fill="currentColor"></circle>
1703
+ <circle cx="12" cy="12" r="1" fill="currentColor"></circle>
1704
+ <circle cx="12" cy="19" r="1" fill="currentColor"></circle>
1705
+ </svg>
1706
+ </button>
1707
+ <div class="tack-panel-more-dropdown" data-state="closed"></div>
1708
+ </div>
1709
+ </div>
1710
+ <div class="tack-panel-content"></div>
1711
+ <div class="tack-new-comment">
1712
+ <div class="tack-new-comment-input" role="button" aria-label="Add new comment">
1713
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1714
+ <path d="M15 15l-2 5L9 9l11 4-5 2z"></path>
1715
+ <path d="M22 2l-7 7"></path>
1716
+ </svg>
1717
+ Click anywhere to add comment
1718
+ </div>
1719
+ </div>
1720
+ </div>
1721
+ `,e.querySelector(".tack-panel-close").addEventListener("click",()=>{this.hide(),this.callbacks.onClose?.()});let t=e.querySelector(".tack-panel-search"),i=e.querySelector(".tack-panel-search-clear");return t.addEventListener("input",()=>{let n=t.value;i.style.display=n?"flex":"none",this.searchTimer&&clearTimeout(this.searchTimer),this.searchTimer=setTimeout(()=>{this.searchQuery=n.trim().toLowerCase(),this.renderComments()},200)}),i.addEventListener("click",n=>{n.stopPropagation(),t.value="",i.style.display="none",this.searchQuery="",this.renderComments(),t.focus()}),e.querySelector(".tack-panel-filter-btn").addEventListener("click",n=>{n.stopPropagation(),this.moreDropdownOpen=!1,this.renderMoreDropdown(),this.filterDropdownOpen=!this.filterDropdownOpen,this.renderFilterDropdown()}),e.querySelector(".tack-panel-more-btn").addEventListener("click",n=>{n.stopPropagation(),this.filterDropdownOpen=!1,this.renderFilterDropdown(),this.moreDropdownOpen=!this.moreDropdownOpen,this.renderMoreDropdown()}),e.querySelector(".tack-new-comment-input").addEventListener("click",()=>{this.callbacks.onAddComment?.()}),e.addEventListener("click",n=>{let r=n.target;!r.closest(".tack-panel-filter-wrap")&&this.filterDropdownOpen&&(this.filterDropdownOpen=!1,this.renderFilterDropdown()),!r.closest(".tack-panel-more-wrap")&&this.moreDropdownOpen&&(this.moreDropdownOpen=!1,this.renderMoreDropdown())}),e}renderFilterDropdown(){let e=this.strip.querySelector(".tack-panel-filter-dropdown");if(!this.filterDropdownOpen){e.setAttribute("data-state","closed"),e.innerHTML="";return}let t=this.filters,i='<svg class="tack-dropdown-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>',n='<span class="tack-dropdown-check-space"></span>';e.innerHTML=`
1722
+ <button class="tack-dropdown-item" data-action="sort-date">
1723
+ ${t.sort==="date"?i:n}
1724
+ <span>Sort by date</span>
1725
+ </button>
1726
+ <button class="tack-dropdown-item" data-action="sort-unread">
1727
+ ${t.sort==="unread"?i:n}
1728
+ <span>Sort by unread</span>
1729
+ </button>
1730
+ <button class="tack-dropdown-item" data-action="sort-replies">
1731
+ ${t.sort==="replies"?i:n}
1732
+ <span>Sort by most replies</span>
1733
+ </button>
1734
+ <div class="tack-dropdown-sep"></div>
1735
+ <button class="tack-dropdown-item tack-dropdown-toggle" data-action="showResolved">
1736
+ <span>Show resolved comments</span>
1737
+ <span class="tack-dropdown-toggle-switch ${t.showResolved?"on":""}"><span class="tack-dropdown-toggle-knob"></span></span>
1738
+ </button>
1739
+ <button class="tack-dropdown-item tack-dropdown-toggle" data-action="onlyYourThreads">
1740
+ <span>Only your threads</span>
1741
+ <span class="tack-dropdown-toggle-switch ${t.onlyYourThreads?"on":""}"><span class="tack-dropdown-toggle-knob"></span></span>
1742
+ </button>
1743
+ <button class="tack-dropdown-item tack-dropdown-toggle" data-action="onlyCurrentPage">
1744
+ <span>Only current page</span>
1745
+ <span class="tack-dropdown-toggle-switch ${t.onlyCurrentPage?"on":""}"><span class="tack-dropdown-toggle-knob"></span></span>
1746
+ </button>
1747
+ `,e.setAttribute("data-state","open"),e.querySelectorAll(".tack-dropdown-item").forEach(r=>{r.addEventListener("click",a=>{a.stopPropagation();let s=r.dataset.action;s.startsWith("sort-")?this.filters.sort=s.replace("sort-",""):s==="showResolved"?this.filters.showResolved=!this.filters.showResolved:s==="onlyYourThreads"?this.filters.onlyYourThreads=!this.filters.onlyYourThreads:s==="onlyCurrentPage"&&(this.filters.onlyCurrentPage=!this.filters.onlyCurrentPage),O(this.filters),this.updateFilterBadge(),this.renderFilterDropdown(),this.renderComments()})})}renderMoreDropdown(){let e=this.strip.querySelector(".tack-panel-more-dropdown");if(!this.moreDropdownOpen){e.setAttribute("data-state","closed"),e.innerHTML="";return}e.innerHTML=`
1748
+ <button class="tack-dropdown-item" data-action="share">
1749
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1750
+ <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
1751
+ <polyline points="16 6 12 2 8 6"></polyline>
1752
+ <line x1="12" y1="2" x2="12" y2="15"></line>
1753
+ </svg>
1754
+ <span>Copy share link</span>
1755
+ </button>
1756
+ `,e.setAttribute("data-state","open"),e.querySelector('[data-action="share"]').addEventListener("click",t=>{t.stopPropagation(),this.moreDropdownOpen=!1,this.renderMoreDropdown(),this.callbacks.onShare?.()})}updateFilterBadge(){let e=this.strip.querySelector(".tack-panel-filter-badge"),t=this.strip.querySelector(".tack-panel-filter-btn"),i=0;this.filters.showResolved&&i++,this.filters.onlyYourThreads&&i++,this.filters.onlyCurrentPage&&i++,i>0?(e.textContent=String(i),e.style.display="flex",t.classList.add("filter-active")):(e.style.display="none",t.classList.remove("filter-active"))}show(){this.strip.classList.add("open"),this.updateFilterBadge(),setTimeout(()=>this.markVisibleAsRead(),1500)}hide(){this.strip.classList.remove("open"),this.filterDropdownOpen=!1,this.moreDropdownOpen=!1,this.renderFilterDropdown(),this.renderMoreDropdown()}toggle(){this.strip.classList.contains("open")?this.hide():this.show()}isOpen(){return this.strip.classList.contains("open")}render(e){this.comments=e,this.renderComments(),this.isOpen()&&setTimeout(()=>this.markVisibleAsRead(),1500)}renderPresence(e){let t=this.panel.querySelector(".tack-presence-bar");if(!t){t=document.createElement("div"),t.className="tack-presence-bar";let s=this.panel.querySelector(".tack-panel-header");s&&s.parentNode?.insertBefore(t,s.nextSibling)}if(e.length===0){t.style.display="none";return}t.style.display="flex";let i=5,n=e.slice(0,i),r=e.length-i,a='<div class="tack-presence-avatars">';n.forEach(s=>{a+=`<div class="tack-presence-avatar" title="${this.escapeHtml(s.name)}">${u(s.name,s.avatar_url,24)}</div>`}),r>0&&(a+=`<div class="tack-presence-overflow">+${r}</div>`),a+="</div>",a+=`<span class="tack-presence-label">${e.length} viewing</span>`,t.innerHTML=a}renderComments(){let e=this.panel.querySelector(".tack-panel-content"),t=this.getFilteredComments();if(t.length===0){if(this.searchQuery){let i=(this.filters.showResolved?1:0)+(this.filters.onlyYourThreads?1:0)+(this.filters.onlyCurrentPage?1:0);e.innerHTML=`
1757
+ <div class="tack-empty">
1758
+ <div class="tack-empty-icon">
1759
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
1760
+ <circle cx="11" cy="11" r="8"></circle>
1761
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
1762
+ </svg>
1763
+ </div>
1764
+ <p class="tack-empty-title">No comments matching "${this.escapeHtml(this.searchQuery)}"</p>
1765
+ <p class="tack-empty-subtitle">${i>0?"Try adjusting your search or filters":"Try a different search term"}</p>
1766
+ ${i>0?'<button class="tack-empty-reset">Reset filters</button>':""}
1767
+ </div>
1768
+ `}else this.hasActiveFilters()?e.innerHTML=`
1769
+ <div class="tack-empty">
1770
+ <div class="tack-empty-icon">
1771
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
1772
+ <line x1="4" y1="6" x2="20" y2="6"></line>
1773
+ <line x1="7" y1="12" x2="17" y2="12"></line>
1774
+ <line x1="10" y1="18" x2="14" y2="18"></line>
1775
+ </svg>
1776
+ </div>
1777
+ <p class="tack-empty-title">No comments to show</p>
1778
+ <p class="tack-empty-subtitle">${this.describeActiveFilters()}</p>
1779
+ <button class="tack-empty-reset">Reset filters</button>
1780
+ </div>
1781
+ `:e.innerHTML=`
1782
+ <div class="tack-empty">
1783
+ <div class="tack-empty-icon">
1784
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
1785
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
1786
+ </svg>
1787
+ </div>
1788
+ <p class="tack-empty-title">No comments yet</p>
1789
+ <p class="tack-empty-subtitle">Click anywhere on the page to add feedback</p>
1790
+ </div>
1791
+ `;e.querySelector(".tack-empty-reset")?.addEventListener("click",()=>{this.resetFilters()});return}e.innerHTML=t.map(i=>this.renderCommentRow(i)).join(""),e.querySelectorAll(".tack-comment-row").forEach(i=>{let n=i.dataset.id;i.addEventListener("click",()=>{this.markAsRead(n);let s=i.querySelector(".tack-unread-dot");s&&s.remove();let o=this.comments.find(c=>c.id===n);o&&this.callbacks.onCommentClick(o)});let r=i.querySelector(".tack-comment-row-resolve");r&&r.addEventListener("click",s=>{s.stopPropagation();let o=this.comments.find(c=>c.id===n);o&&this.callbacks.onResolve(n,!o.resolved)});let a=i.querySelector(".tack-comment-row-more");a&&a.addEventListener("click",s=>{s.stopPropagation();let o=this.comments.find(c=>c.id===n);o&&this.callbacks.onCommentClick(o)})})}hasActiveFilters(){return this.filters.showResolved||this.filters.onlyYourThreads||this.filters.onlyCurrentPage}describeActiveFilters(){let e=[];return this.filters.onlyYourThreads&&e.push("only your threads"),this.filters.onlyCurrentPage&&e.push("only current page"),this.filters.showResolved||e.push("resolved comments hidden"),e.length>0?e.join(", ").replace(/^./,t=>t.toUpperCase()):""}resetFilters(){this.filters={sort:"date",showResolved:!1,onlyYourThreads:!1,onlyCurrentPage:!1},this.searchQuery="";let e=this.strip.querySelector(".tack-panel-search");e&&(e.value="");let t=this.strip.querySelector(".tack-panel-search-clear");t&&(t.style.display="none"),O(this.filters),this.updateFilterBadge(),this.renderComments()}getCurrentPagePath(){return window.location.pathname+window.location.search}formatPagePath(e){if(!e||e==="/")return"Home";let t=e.split("?")[0].slice(1);return t.charAt(0).toUpperCase()+t.slice(1)}renderCommentRow(e){let t=e.id===this.highlightedId,i=this.formatTimeAgo(new Date(e.created_at)),n=this.getCurrentPagePath(),r=e.page_path===n,a=this.formatPagePath(e.page_path),s=!!e.screenshot_url,o=!this.isRead(e.id);return`
1792
+ <div class="tack-comment-row ${t?"highlighted":""}" data-id="${e.id}">
1793
+ <div class="tack-comment-row-actions">
1794
+ <button class="tack-comment-row-more" data-action="more" data-id="${e.id}" title="More actions">
1795
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="3" cy="8" r="1.25" fill="currentColor"/><circle cx="8" cy="8" r="1.25" fill="currentColor"/><circle cx="13" cy="8" r="1.25" fill="currentColor"/></svg>
1796
+ </button>
1797
+ <button class="tack-comment-row-resolve ${e.resolved?"resolved":""}" data-action="resolve" data-id="${e.id}" title="${e.resolved?"Unresolve":"Resolve"}">
1798
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5"/>${e.resolved?'<path d="M5.5 8L7.2 9.7L10.5 6.3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>':""}</svg>
1799
+ </button>
1800
+ </div>
1801
+ <div class="tack-comment-row-top">
1802
+ ${u(e.author_name,e.author_avatar_url,32,"tack-comment-avatar")}
1803
+ ${o?'<span class="tack-unread-dot"></span>':""}
1804
+ </div>
1805
+ <div class="tack-comment-meta">
1806
+ <span class="tack-comment-author">${this.escapeHtml(e.author_name)}</span>
1807
+ <span class="tack-comment-time">${i}</span>
1808
+ ${e.resolved?'<span class="tack-comment-resolved-badge">Resolved</span>':""}
1809
+ ${r?"":`<span class="tack-comment-page-badge">${this.escapeHtml(a)}</span>`}
1810
+ </div>
1811
+ <div class="tack-comment-body ${e.resolved?"dimmed":""}">
1812
+ ${s?`<img src="${e.screenshot_url}" class="tack-comment-screenshot" alt="Screenshot" />`:""}
1813
+ <div class="tack-comment-content">${this.escapeHtml(e.content)}</div>
1814
+ </div>
1815
+ </div>
1816
+ `}getFilteredComments(){let e=this.comments.filter(t=>!t.parent_id);if(this.filters.showResolved||(e=e.filter(t=>!t.resolved)),this.filters.onlyYourThreads&&this.currentUser){let t=this.currentUser.id,i=this.currentUser.name;e=e.filter(n=>!!(n.user_id===t||n.author_name===i||n.replies?.some(r=>r.user_id===t||r.author_name===i)))}if(this.filters.onlyCurrentPage){let t=this.getCurrentPagePath();e=e.filter(i=>i.page_path===t)}if(this.searchQuery){let t=this.searchQuery;e=e.filter(i=>!!(i.author_name.toLowerCase().includes(t)||i.content.toLowerCase().includes(t)||i.replies?.some(n=>n.content.toLowerCase().includes(t)||n.author_name.toLowerCase().includes(t))))}switch(this.filters.sort){case"unread":e.sort((t,i)=>{let n=this.isRead(t.id)?1:0,r=this.isRead(i.id)?1:0;return n!==r?n-r:new Date(i.created_at).getTime()-new Date(t.created_at).getTime()});break;case"replies":e.sort((t,i)=>{let n=t.replies?.length||0,r=i.replies?.length||0;return r!==n?r-n:new Date(i.created_at).getTime()-new Date(t.created_at).getTime()});break;default:e.sort((t,i)=>new Date(i.created_at).getTime()-new Date(t.created_at).getTime());break}return e}scrollToComment(e){this.highlightedId=e,this.renderComments(),this.panel.querySelector(`[data-id="${e}"]`)?.scrollIntoView({behavior:"smooth",block:"nearest"}),setTimeout(()=>{this.highlightedId=null,this.renderComments()},2e3)}formatTimeAgo(e){let t=Math.floor((Date.now()-e.getTime())/1e3);return t<60?"just now":t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:t<604800?`${Math.floor(t/86400)}d ago`:e.toLocaleDateString()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}};var C=class{resolve(e){if(!e)return{element:null,confidence:"none",method:"none"};if(e.cssSelector){let t=this.tryCssSelector(e);if(t.element)return t}if(e.xpath){let t=this.tryXPath(e);if(t.element)return t}if(e.textQuote){let t=this.tryTextQuote(e.textQuote);if(t.element)return t}if(e.textQuote){let t=this.tryFuzzySearch(e.textQuote);if(t.element)return t}return{element:null,confidence:"none",method:"none"}}calculatePinPosition(e,t){let i=e.getBoundingClientRect(),n=i.left+t.relativeX*i.width+window.scrollX,r=i.top+t.relativeY*i.height+window.scrollY;return{x:n,y:r}}tryCssSelector(e){try{let t=document.querySelector(e.cssSelector);return t?e.contentHash?this.generateContentHash(t)===e.contentHash?{element:t,confidence:"high",method:"css"}:{element:t,confidence:"medium",method:"css"}:{element:t,confidence:"high",method:"css"}:{element:null,confidence:"none",method:"css"}}catch{return{element:null,confidence:"none",method:"css"}}}tryXPath(e){try{let i=document.evaluate(e.xpath,document.body,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;return i?e.contentHash?this.generateContentHash(i)===e.contentHash?{element:i,confidence:"high",method:"xpath"}:{element:i,confidence:"medium",method:"xpath"}:{element:i,confidence:"medium",method:"xpath"}:{element:null,confidence:"none",method:"xpath"}}catch{return{element:null,confidence:"none",method:"xpath"}}}tryTextQuote(e){let t=document.createTreeWalker(document.body,NodeFilter.SHOW_ELEMENT,null),i=t.nextNode();for(;i;){let n=i,r=n.textContent?.trim();if(r&&r.includes(e.exact)&&this.verifyContext(n,e))return{element:n,confidence:"high",method:"textQuote"};i=t.nextNode()}return{element:null,confidence:"none",method:"textQuote"}}tryFuzzySearch(e){let t=document.createTreeWalker(document.body,NodeFilter.SHOW_ELEMENT,null),i=null,n=e.exact.toLowerCase(),r=t.nextNode();for(;r;){let a=r,s=a.textContent?.trim().toLowerCase();if(s&&s.length<1e3){let o=this.similarityScore(s,n);o>.7&&(!i||o>i.score)&&(i={element:a,score:o})}r=t.nextNode()}return i?{element:i.element,confidence:i.score>.9?"medium":"low",method:"fuzzy"}:{element:null,confidence:"none",method:"fuzzy"}}verifyContext(e,t){if(!t.prefix&&!t.suffix)return!0;let i=e.parentElement;if(!i)return!0;let n=i.textContent||"",r=e.textContent||"",a=n.indexOf(r);if(a===-1)return!0;if(t.prefix&&!n.slice(Math.max(0,a-50),a).trim().includes(t.prefix.slice(-20)))return!1;if(t.suffix){let s=a+r.length;if(!n.slice(s,s+50).trim().includes(t.suffix.slice(0,20)))return!1}return!0}similarityScore(e,t){if(e===t)return 1;if(e.length<2||t.length<2)return 0;let i=new Set,n=new Set;for(let a=0;a<e.length-1;a++)i.add(e.slice(a,a+2));for(let a=0;a<t.length-1;a++)n.add(t.slice(a,a+2));let r=0;return i.forEach(a=>{n.has(a)&&r++}),2*r/(i.size+n.size)}generateContentHash(e){let t=e.textContent?.trim().slice(0,500)||"",i=e.tagName.toLowerCase(),n=e.className?.toString()||"",r=`${i}:${n}:${t}`,a=5381;for(let s=0;s<r.length;s++)a=(a<<5)+a+r.charCodeAt(s);return(a>>>0).toString(16)}};var E=class{constructor(e,t){this.highlightedId=null;this.activeId=null;this.pins=new Map;this.resizeObserver=null;this.repositionRAF=null;this.hoverTimers=new Map;this.leaveTimers=new Map;this.currentlyExpanded=null;this.container=e,this.onClick=t,this.anchoring=new C,this.boundScheduleReposition=this.scheduleReposition.bind(this),this.pinsContainer=document.createElement("div"),this.pinsContainer.className="tack-pins-container",this.container.appendChild(this.pinsContainer),this.setupResizeObserver(),window.addEventListener("scroll",this.boundScheduleReposition,{passive:!0}),window.addEventListener("resize",this.boundScheduleReposition,{passive:!0})}setupResizeObserver(){this.resizeObserver=new ResizeObserver(()=>{this.scheduleReposition()}),this.resizeObserver.observe(document.body)}scheduleReposition(){this.repositionRAF&&cancelAnimationFrame(this.repositionRAF),this.repositionRAF=requestAnimationFrame(()=>{this.repositionAllPins()})}repositionAllPins(){this.pins.forEach((e,t)=>{e.targetElement&&!document.contains(e.targetElement)&&(e.anchorResult=this.anchoring.resolve(e.comment.anchor),e.targetElement=e.anchorResult.element),this.positionPin(e)})}getCurrentPagePath(){return window.location.pathname+window.location.search}render(e){this.hoverTimers.forEach(n=>clearTimeout(n)),this.hoverTimers.clear(),this.leaveTimers.forEach(n=>clearTimeout(n)),this.leaveTimers.clear(),this.currentlyExpanded=null,this.pinsContainer.innerHTML="",this.pins.clear();let t=this.getCurrentPagePath();e.filter(n=>!n.parent_id&&n.page_path===t).forEach(n=>{let r=this.createPin(n);r&&(this.pins.set(n.id,r),this.pinsContainer.appendChild(r.element))})}getUniqueAuthors(e){let t=new Set,i=[],n=e.user_id||e.author_name;if(t.add(n),i.push({name:e.author_name,avatar_url:e.author_avatar_url}),e.replies)for(let r of e.replies){let a=r.user_id||r.author_name;t.has(a)||(t.add(a),i.push({name:r.author_name,avatar_url:r.author_avatar_url}))}return i}renderInsetAvatar(e,t){let i=p(e.name),n=P(e.name),r=Math.round(t*.38);return e.avatar_url?`<img src="${e.avatar_url}" alt="${i}" class="tack-pin-avatar-img" style="width:${t}px;height:${t}px;" onerror="this.outerHTML='<div class=\\'tack-pin-avatar-fallback\\' style=\\'width:${t}px;height:${t}px;background:${n};font-size:${r}px;\\'>${i}</div>'" />`:`<div class="tack-pin-avatar-fallback" style="width:${t}px;height:${t}px;background:${n};font-size:${r}px;">${i}</div>`}renderStackedAvatar(e,t){let i=p(e.name),n=P(e.name),r=Math.round(t*.38);return e.avatar_url?`<img src="${e.avatar_url}" alt="${i}" class="tack-pin-avatar-img" style="width:${t}px;height:${t}px;" onerror="this.outerHTML='<div class=\\'tack-pin-avatar-fallback\\' style=\\'width:${t}px;height:${t}px;background:${n};font-size:${r}px;\\'>${i}</div>'" />`:`<div class="tack-pin-avatar-fallback" style="width:${t}px;height:${t}px;background:${n};font-size:${r}px;">${i}</div>`}createPin(e){let t={element:null,confidence:"none",method:"none"},i=null;e.anchor?(t=this.anchoring.resolve(e.anchor),i=t.element):e.element_selector&&(i=document.querySelector(e.element_selector),i&&(t={element:i,confidence:"medium",method:"css"}));let n=document.createElement("div"),r=e.id===this.activeId,a=e.id===this.highlightedId,s=this.getUniqueAuthors(e),o=s.length>1;n.className=`tack-pin${e.resolved?" resolved":""}${r?" active":""}${a?" highlighted":""}${o?" multi-author":""}`,n.dataset.id=e.id,t.confidence==="low"&&n.classList.add("low-confidence");let c=s[0],d=this.formatTimeAgo(new Date(e.created_at)),h=this.escapeHtml(e.content),m=e.replies?.length||0,R=`<div class="tack-pin-preview">
1817
+ <div class="tack-pin-preview-header">
1818
+ <div class="tack-pin-preview-avatar">${this.renderInsetAvatar(c,28)}</div>
1819
+ <div class="tack-pin-preview-meta">
1820
+ <span class="tack-pin-preview-name">${this.escapeHtml(c.name)}</span>
1821
+ <span class="tack-pin-preview-time">${d}</span>
1822
+ </div>
1823
+ </div>
1824
+ <p class="tack-pin-preview-text">${h}</p>
1825
+ ${m>0?`<span class="tack-pin-preview-replies">${m} ${m===1?"reply":"replies"}</span>`:""}
1826
+ </div>`,g;if(o){let j=s.slice(0,3),F=s.length-3,f='<div class="tack-pin-shell"><div class="tack-pin-avatars">';j.forEach((B,N)=>{f+=`<div class="tack-pin-avatar-stacked" style="z-index:${N+1}">${this.renderStackedAvatar(B,24)}</div>`}),F>0&&(f+=`<div class="tack-pin-avatar-stacked tack-pin-avatar-overflow" style="z-index:4">+${F}</div>`),f+=`</div>${R}</div>`,g=f}else{let v=s[0];g=`<div class="tack-pin-shell">${this.renderInsetAvatar(v,32)}${R}</div>`}e.resolved&&(g+='<div class="tack-pin-check"></div>'),n.innerHTML=g,n.addEventListener("click",v=>{v.stopPropagation(),this.collapsePin(e.id,n),this.onClick(e.id,n)}),this.setupHoverListeners(n,e);let $={comment:e,element:n,targetElement:i,anchorResult:t};return this.positionPin($),n.style.left===""&&n.style.top===""?null:$}positionPin(e){let{comment:t,element:i,targetElement:n}=e,r=7,a=45;if(t.anchor&&n){let s=this.anchoring.calculatePinPosition(n,t.anchor);i.style.left=`${s.x-r}px`,i.style.top=`${s.y-a}px`;return}if(n){let s=n.getBoundingClientRect();i.style.left=`${s.left+window.scrollX-r}px`,i.style.top=`${s.top+window.scrollY-a}px`;return}if(t.x_percent!==null&&t.y_percent!==null){let s=t.x_percent/100*document.documentElement.scrollWidth,o=t.y_percent/100*document.documentElement.scrollHeight;i.style.left=`${s-r}px`,i.style.top=`${o-a}px`;return}i.style.display="none"}formatTimeAgo(e){let t=Math.floor((Date.now()-e.getTime())/1e3);return t<60?"just now":t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:t<604800?`${Math.floor(t/86400)}d ago`:e.toLocaleDateString()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}setupHoverListeners(e,t){e.addEventListener("mouseenter",()=>{let i=this.leaveTimers.get(t.id);i&&(clearTimeout(i),this.leaveTimers.delete(t.id));let n=window.setTimeout(()=>{this.expandPin(t.id,e),this.hoverTimers.delete(t.id)},150);this.hoverTimers.set(t.id,n)}),e.addEventListener("mouseleave",()=>{let i=this.hoverTimers.get(t.id);i&&(clearTimeout(i),this.hoverTimers.delete(t.id));let n=window.setTimeout(()=>{this.collapsePin(t.id,e),this.leaveTimers.delete(t.id)},120);this.leaveTimers.set(t.id,n)})}expandPin(e,t){if(t.classList.contains("active"))return;if(this.currentlyExpanded&&this.currentlyExpanded!==e){let n=this.pins.get(this.currentlyExpanded);n&&n.element.classList.remove("expanded","expand-left")}this.currentlyExpanded=e;let i=t.getBoundingClientRect();window.innerWidth-i.left<300?t.classList.add("expand-left"):t.classList.remove("expand-left"),t.classList.add("expanded")}collapsePin(e,t){t.classList.remove("expanded","expand-left"),this.currentlyExpanded===e&&(this.currentlyExpanded=null)}setActive(e){this.activeId=e,this.pins.forEach((t,i)=>{t.element.classList.toggle("active",i===e)})}highlight(e){this.highlightedId=e,this.pins.forEach((t,i)=>{t.element.classList.toggle("highlighted",i===e)}),setTimeout(()=>{this.highlightedId=null,this.pins.forEach(t=>{t.element.classList.remove("highlighted")})},2e3)}show(){this.pinsContainer.classList.add("visible")}hide(){this.pinsContainer.classList.remove("visible")}destroy(){this.hoverTimers.forEach(e=>clearTimeout(e)),this.leaveTimers.forEach(e=>clearTimeout(e)),this.resizeObserver&&this.resizeObserver.disconnect(),this.repositionRAF&&cancelAnimationFrame(this.repositionRAF),window.removeEventListener("scroll",this.boundScheduleReposition),window.removeEventListener("resize",this.boundScheduleReposition)}};var T=class{constructor(e,t){this.currentComment=null;this.currentUser=null;this.visible=!1;this.editing=!1;this.dropdownOpen=!1;this.container=e,this.callbacks=t,this.card=document.createElement("div"),this.card.className="tack-card",this.card.setAttribute("role","dialog"),this.card.setAttribute("aria-label","Comment details"),this.container.appendChild(this.card),this.card.addEventListener("mousedown",i=>i.stopPropagation()),this.card.addEventListener("click",i=>i.stopPropagation()),this.boundOnMouseDown=this.onDocumentMouseDown.bind(this),this.boundOnKeyDown=this.onKeyDown.bind(this)}setUser(e){this.currentUser=e}show(e,t){if(this.visible&&this.currentComment?.id===e.id){this.hide();return}this.currentComment=e,this.visible=!0,this.editing=!1,this.dropdownOpen=!1,this.renderCard(e),this.positionNear(t),this.card.classList.add("visible"),this.attachListeners(),document.addEventListener("mousedown",this.boundOnMouseDown,!0),document.addEventListener("keydown",this.boundOnKeyDown)}hide(){this.visible&&(this.visible=!1,this.currentComment=null,this.editing=!1,this.dropdownOpen=!1,this.card.classList.remove("visible"),document.removeEventListener("mousedown",this.boundOnMouseDown,!0),document.removeEventListener("keydown",this.boundOnKeyDown),this.callbacks.onClose())}isVisible(){return this.visible}updateComment(e){!this.visible||this.currentComment?.id!==e.id||(this.currentComment=e,this.renderCard(e),this.attachListeners())}onDocumentMouseDown(e){this.card.contains(e.target)||e.composedPath().includes(this.card)||this.hide()}onKeyDown(e){if(e.key==="Escape"){if(this.dropdownOpen){this.dropdownOpen=!1,this.renderCard(this.currentComment),this.attachListeners();return}if(this.editing){this.editing=!1,this.renderCard(this.currentComment),this.attachListeners();return}e.preventDefault(),this.hide()}}positionNear(e){let t=e.getBoundingClientRect(),i=320,n=8,r=document.body.classList.contains("tack-panel-open")?360:0,a=window.innerWidth-r-16,s=t.right+n+window.scrollX;s+i>a+window.scrollX&&(s=t.left-i-n+window.scrollX),s=Math.max(16,s);let o=t.top+window.scrollY,c=window.innerHeight+window.scrollY-16,d=300;o+d>c&&(o=c-d),o=Math.max(16+window.scrollY,o),this.card.style.left=`${s}px`,this.card.style.top=`${o}px`}isOwnComment(e){return this.currentUser?.id&&e.user_id?this.currentUser.id===e.user_id:(localStorage.getItem("tack_author_name")||"")===e.author_name}renderCard(e){let t=e.replies||[],i=localStorage.getItem("tack_author_name")||"Anonymous",n=`
1827
+ <div class="tack-card-title-bar">
1828
+ <span class="tack-card-title">Comment</span>
1829
+ <div class="tack-card-title-actions">
1830
+ <button class="tack-card-resolve-icon ${e.resolved?"resolved":""}" data-id="${e.id}" title="${e.resolved?"Unresolve":"Resolve"}" aria-label="${e.resolved?"Unresolve comment":"Resolve comment"}">
1831
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1832
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
1833
+ <polyline points="22 4 12 14.01 9 11.01"/>
1834
+ </svg>
1835
+ </button>
1836
+ <button class="tack-card-close-btn" title="Close" aria-label="Close comment">
1837
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1838
+ <line x1="18" y1="6" x2="6" y2="18"/>
1839
+ <line x1="6" y1="6" x2="18" y2="18"/>
1840
+ </svg>
1841
+ </button>
1842
+ </div>
1843
+ </div>`;e.resolved&&(n+=`
1844
+ <div class="tack-card-resolved-banner">
1845
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
1846
+ <polyline points="20 6 9 17 4 12"/>
1847
+ </svg>
1848
+ Resolved by ${this.escapeHtml(e.author_name)}
1849
+ </div>`),n+=this.renderCommentRow(e,!0),n+='<div class="tack-card-divider"></div>',t.length>0&&(t.forEach((r,a)=>{n+=this.renderCommentRow(r,!1),a<t.length-1&&(n+='<div class="tack-card-divider"></div>')}),n+='<div class="tack-card-divider"></div>'),n+=`
1850
+ <div class="tack-card-reply-bar">
1851
+ <div class="tack-card-reply-bar-avatar-wrap">${u(i,this.currentUser?.avatar_url||null,24)}</div>
1852
+ <div class="tack-card-reply-bar-input-wrap">
1853
+ <input type="text" class="tack-card-reply-bar-input" placeholder="Reply\u2026" data-parent-id="${e.id}" />
1854
+ <button class="tack-card-reply-bar-send" data-parent-id="${e.id}">
1855
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
1856
+ <line x1="12" y1="19" x2="12" y2="5"/>
1857
+ <polyline points="5 12 12 5 19 12"/>
1858
+ </svg>
1859
+ </button>
1860
+ </div>
1861
+ </div>`,this.card.innerHTML=n}renderCommentRow(e,t){let i=this.formatTimeAgo(new Date(e.created_at)),n=t&&this.isOwnComment(e),r=`<div class="tack-card-row${t?" main":""}" data-comment-id="${e.id}">`;return r+=`
1862
+ <div class="tack-card-row-header">
1863
+ <div class="tack-card-row-avatar-wrap">${u(e.author_name,e.author_avatar_url,28)}</div>
1864
+ <div class="tack-card-row-meta">
1865
+ <span class="tack-card-row-author">${this.escapeHtml(e.author_name)}</span>
1866
+ ${this.editing&&t?'<span class="tack-card-editing-badge">Editing</span>':""}
1867
+ <span class="tack-card-row-time">\xB7 ${i}</span>
1868
+ </div>
1869
+ ${n?`<button class="tack-card-more-btn" data-comment-id="${e.id}">
1870
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
1871
+ <circle cx="12" cy="5" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="12" cy="19" r="1"/>
1872
+ </svg>
1873
+ </button>`:""}
1874
+ </div>`,n&&this.dropdownOpen&&(r+=`
1875
+ <div class="tack-card-dropdown">
1876
+ <button class="tack-card-dropdown-item" data-action="edit" aria-label="Edit comment">Edit Comment</button>
1877
+ <button class="tack-card-dropdown-item delete" data-action="delete" aria-label="Delete comment">Delete Comment</button>
1878
+ </div>`),t&&e.screenshot_url&&(r+=`<img src="${e.screenshot_url}" class="tack-card-row-screenshot" alt="Screenshot" />`),this.editing&&t?r+=`
1879
+ <div class="tack-card-edit-wrap">
1880
+ <textarea class="tack-card-edit-textarea">${this.escapeHtml(e.content)}</textarea>
1881
+ <div class="tack-card-edit-actions">
1882
+ <button class="tack-card-edit-cancel">Cancel</button>
1883
+ <button class="tack-card-edit-save">Save</button>
1884
+ </div>
1885
+ </div>`:r+=`<div class="tack-card-row-body">${this.escapeHtml(e.content)}</div>`,r+="</div>",r}attachListeners(){this.card.querySelector(".tack-card-close-btn")?.addEventListener("click",t=>{t.stopPropagation(),this.hide()}),this.card.querySelector(".tack-card-resolve-icon")?.addEventListener("click",t=>{t.stopPropagation(),this.currentComment&&this.callbacks.onResolve(this.currentComment.id,!this.currentComment.resolved)}),this.card.querySelector(".tack-card-more-btn")?.addEventListener("click",t=>{t.stopPropagation(),this.dropdownOpen=!this.dropdownOpen,this.renderCard(this.currentComment),this.attachListeners()}),this.card.querySelectorAll(".tack-card-dropdown-item").forEach(t=>{t.addEventListener("click",i=>{i.stopPropagation();let n=t.dataset.action;if(n==="edit"){this.dropdownOpen=!1,this.editing=!0,this.renderCard(this.currentComment),this.attachListeners();let r=this.card.querySelector(".tack-card-edit-textarea");r?.focus(),r?.setSelectionRange(r.value.length,r.value.length)}else n==="delete"&&(this.dropdownOpen=!1,this.currentComment&&(this.callbacks.onDelete(this.currentComment.id),this.hide()))})}),this.card.querySelector(".tack-card-edit-cancel")?.addEventListener("click",t=>{t.stopPropagation(),this.editing=!1,this.renderCard(this.currentComment),this.attachListeners()}),this.card.querySelector(".tack-card-edit-save")?.addEventListener("click",t=>{t.stopPropagation(),this.saveEdit()}),this.card.querySelector(".tack-card-edit-textarea")?.addEventListener("keydown",t=>{let i=t;i.key==="Enter"&&(i.metaKey||i.ctrlKey)&&(i.preventDefault(),this.saveEdit())}),this.card.querySelector(".tack-card-reply-bar-input")?.addEventListener("keydown",t=>{let i=t;i.key==="Enter"&&!i.shiftKey&&(i.preventDefault(),this.submitReply())}),this.card.querySelector(".tack-card-reply-bar-send")?.addEventListener("click",t=>{t.stopPropagation(),this.submitReply()})}saveEdit(){if(!this.currentComment)return;let t=this.card.querySelector(".tack-card-edit-textarea")?.value.trim();t&&(this.editing=!1,this.callbacks.onEdit(this.currentComment.id,t))}submitReply(){if(!this.currentComment)return;let e=this.card.querySelector(".tack-card-reply-bar-input"),t=e?.value.trim();t&&(this.callbacks.onReply(this.currentComment.id,t),e.value="")}formatTimeAgo(e){let t=Math.floor((Date.now()-e.getTime())/1e3);return t<60?"just now":t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:t<604800?`${Math.floor(t/86400)}d ago`:e.toLocaleDateString()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}};var S=class{constructor(e,t,i,n){this.ws=null;this.reconnectAttempts=0;this.maxReconnectAttempts=5;this.heartbeatInterval=null;this.supabaseUrl=e,this.supabaseKey=t,this.versionId=i,this.callback=n}connect(){let e=this.supabaseUrl.replace("https://","wss://")+"/realtime/v1/websocket",t=new URLSearchParams({apikey:this.supabaseKey,vsn:"1.0.0"});this.ws=new WebSocket(`${e}?${t}`),this.ws.onopen=()=>{this.reconnectAttempts=0,this.subscribe()},this.ws.onmessage=i=>{let n=JSON.parse(i.data);this.handleMessage(n)},this.ws.onclose=()=>{this.attemptReconnect()},this.ws.onerror=i=>{console.error("[Tack] Realtime error:",i)}}subscribe(){if(!this.ws)return;let e={topic:"realtime:public:comments",event:"phx_join",payload:{config:{postgres_changes:[{event:"*",schema:"public",table:"comments",filter:`version_id=eq.${this.versionId}`}]}},ref:"1"};this.ws.send(JSON.stringify(e)),this.startHeartbeat()}handleMessage(e){if(e.event==="postgres_changes"&&e.payload?.data){let{type:t,record:i}=e.payload.data,n=t.toUpperCase();this.callback(n,i)}}startHeartbeat(){this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.heartbeatInterval=setInterval(()=>{this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify({topic:"phoenix",event:"heartbeat",payload:{},ref:Date.now().toString()}))},3e4)}attemptReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts){console.error("[Tack] Max reconnect attempts reached");return}this.reconnectAttempts++;let e=Math.min(1e3*Math.pow(2,this.reconnectAttempts),3e4);setTimeout(()=>{console.log(`[Tack] Reconnecting... (attempt ${this.reconnectAttempts})`),this.connect()},e)}disconnect(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null),this.ws&&(this.ws.close(),this.ws=null)}};var A=class{constructor(e,t,i){this.ws=null;this.currentUser=null;this.pagePath="";this.users=new Map;this.onPresenceChange=null;this.heartbeatTimer=null;this.reconnectAttempts=0;this.joined=!1;this.ref=1;this.supabaseUrl=e,this.supabaseKey=t,this.projectId=i}join(e){this.currentUser=e,this.connect()}leave(){this.joined=!1,this.currentUser=null,this.users.clear(),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.ws&&(this.ws.close(),this.ws=null)}updatePagePath(e){this.pagePath=e,this.joined&&this.ws?.readyState===WebSocket.OPEN&&this.trackPresence()}setOnPresenceChange(e){this.onPresenceChange=e}connect(){let e=this.supabaseUrl.replace("https://","wss://")+"/realtime/v1/websocket",t=new URLSearchParams({apikey:this.supabaseKey,vsn:"1.0.0"});this.ws=new WebSocket(`${e}?${t}`),this.ws.onopen=()=>{this.reconnectAttempts=0,this.joinChannel()},this.ws.onmessage=i=>{try{let n=JSON.parse(i.data);this.handleMessage(n)}catch{}},this.ws.onclose=()=>{this.currentUser&&this.attemptReconnect()},this.ws.onerror=()=>{}}nextRef(){return(this.ref++).toString()}joinChannel(){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return;let e=`realtime:presence:${this.projectId}`;this.ws.send(JSON.stringify({topic:e,event:"phx_join",payload:{config:{presence:{key:this.currentUser.id}}},ref:this.nextRef()})),this.startHeartbeat(),setTimeout(()=>{this.joined=!0,this.trackPresence()},200)}trackPresence(){if(!this.ws||this.ws.readyState!==WebSocket.OPEN||!this.currentUser)return;let e=`realtime:presence:${this.projectId}`;this.ws.send(JSON.stringify({topic:e,event:"presence",payload:{type:"presence",event:"track",payload:{user_id:this.currentUser.id,name:this.currentUser.name,avatar_url:this.currentUser.avatar_url,page_path:this.pagePath,online_at:Date.now()}},ref:this.nextRef()}))}handleMessage(e){if(e.event==="presence_state"){this.users.clear();let t=e.payload||{};for(let i of Object.keys(t)){let n=t[i]?.metas;if(n?.length>0){let r=n[0];this.users.set(i,{id:r.user_id||i,name:r.name||"Unknown",avatar_url:r.avatar_url||null})}}this.notifyChange()}else if(e.event==="presence_diff"){let{joins:t,leaves:i}=e.payload||{};if(t)for(let n of Object.keys(t)){let r=t[n]?.metas;if(r?.length>0){let a=r[0];this.users.set(n,{id:a.user_id||n,name:a.name||"Unknown",avatar_url:a.avatar_url||null})}}if(i)for(let n of Object.keys(i))this.users.delete(n);this.notifyChange()}}notifyChange(){let e=Array.from(this.users.values());this.onPresenceChange?.(e)}startHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify({topic:"phoenix",event:"heartbeat",payload:{},ref:this.nextRef()}))},3e4)}attemptReconnect(){if(this.reconnectAttempts>=5)return;this.reconnectAttempts++;let e=Math.min(1e3*Math.pow(2,this.reconnectAttempts),3e4);setTimeout(()=>{this.currentUser&&this.connect()},e)}};var L=class{constructor(e){this.container=null;this.shadowRoot=null;this.selector=null;this.form=null;this.panel=null;this.pins=null;this.card=null;this.realtime=null;this.presence=null;this.commentMode=!1;this.comments=[];this.currentVersion=null;this.currentPagePath="";this.originalPushState=null;this.originalReplaceState=null;this.pollingInterval=null;this.boundOnUrlChange=null;this.config={apiUrl:"https://tacktext.vercel.app/api",supabaseUrl:"https://etpavqvnpupqdxdptkhc.supabase.co",supabaseKey:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImV0cGF2cXZucHVwcWR4ZHB0a2hjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ4MzU4MzgsImV4cCI6MjA5MDQxMTgzOH0.GMSIuPmMaEkNhss_laSm_NchihN_KF9VyyEh5YH4Ru0",...e},this.api=new k(this.config.apiUrl,this.config.projectId),this.auth=new b(this.config.apiUrl)}async mount(){this.container=document.createElement("div"),this.container.id="tack-widget",this.shadowRoot=this.container.attachShadow({mode:"open"});let e=document.createElement("style");e.textContent=H(),this.shadowRoot.appendChild(e),this.injectPageStyles();let t=document.createElement("div");if(t.className="tack-wrapper",this.shadowRoot.appendChild(t),this.auth.setOnAuthChange(i=>{this.api.setAuthToken(i?this.auth.getSessionToken():null),this.form?.setUser(i),this.card?.setUser(i),this.panel?.setUser(i),i?this.joinPresence(i):(this.presence?.leave(),this.presence=null)}),await this.auth.init(),this.auth.isAuthenticated()&&this.api.setAuthToken(this.auth.getSessionToken()),this.api.setOnUnauthorized(()=>{this.auth.signOut(),this.showToast("Session expired. Please sign in again.")}),this.selector=new x(this.onElementSelected.bind(this)),this.form=new w(t,this.onCommentSubmit.bind(this)),this.form.setUser(this.auth.getUser()),this.form.setOnHide(()=>{this.panel?.isOpen()&&this.enableCommentMode()}),this.form.setOnSignIn(()=>this.signIn()),this.panel=new y(t,{onCommentClick:this.onCommentClick.bind(this),onResolve:this.onResolve.bind(this),onReply:this.onReply.bind(this),onClose:this.onPanelClose.bind(this),onAddComment:this.enableCommentMode.bind(this),onShare:this.onShare.bind(this)}),this.card=new T(t,{onResolve:this.onResolve.bind(this),onReply:this.onReply.bind(this),onEdit:this.onEdit.bind(this),onDelete:this.onDelete.bind(this),onClose:this.onCardClose.bind(this)}),this.card.setUser(this.auth.getUser()),this.panel.setUser(this.auth.getUser()),this.panel.setProjectId(this.config.projectId),this.pins=new E(t,this.onPinClick.bind(this)),this.createToggleButton(t),document.body.appendChild(this.container),await this.loadComments(),this.handleDeepLink(),this.subscribeToRealtime(),this.setupUrlChangeDetection(),this.auth.isAuthenticated()){let i=this.auth.getUser();this.joinPresence(i)}}joinPresence(e){!this.config.supabaseUrl||!this.config.supabaseKey||(this.presence?.leave(),this.presence=new A(this.config.supabaseUrl,this.config.supabaseKey,this.config.projectId),this.presence.setOnPresenceChange(t=>{this.panel?.renderPresence(t)}),this.presence.join(e),this.presence.updatePagePath(this.getPagePath()))}async signIn(){try{await this.auth.signIn()}catch(e){console.error("[Tack] Sign in failed:",e)}}getPagePath(){return window.location.pathname+window.location.search}setupUrlChangeDetection(){this.currentPagePath=this.getPagePath(),this.boundOnUrlChange=this.onUrlChange.bind(this),window.addEventListener("popstate",this.boundOnUrlChange),this.originalPushState=history.pushState.bind(history),this.originalReplaceState=history.replaceState.bind(history),history.pushState=(...e)=>{this.originalPushState(...e),this.onUrlChange()},history.replaceState=(...e)=>{this.originalReplaceState(...e),this.onUrlChange()}}onUrlChange(){let e=this.getPagePath();e!==this.currentPagePath&&(this.currentPagePath=e,this.presence?.updatePagePath(e),setTimeout(()=>{console.log("[Tack] URL changed, re-rendering pins after DOM settle"),this.pins?.render(this.comments),this.panel?.render(this.comments)},100))}createToggleButton(e){let t=document.createElement("button");t.className="tack-toggle",t.setAttribute("aria-label","Toggle comments"),t.setAttribute("aria-expanded","false"),t.innerHTML=`
1886
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1887
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
1888
+ </svg>
1889
+ `,t.addEventListener("click",()=>this.toggleCommentMode()),e.appendChild(t)}toggleCommentMode(){this.panel?.isOpen()?(this.panel.hide(),this.setPanelOpen(!1),this.pins?.hide(),this.disableCommentMode(),this.updateToggleAria(!1)):(this.panel?.show(),this.setPanelOpen(!0),this.pins?.show(),this.container?.classList.add("tack-active"),this.enableCommentMode(),this.updateToggleAria(!0))}enableCommentMode(){this.commentMode=!0,this.selector?.enable()}disableCommentMode(){this.commentMode=!1,this.selector?.disable(),this.form?.hide(),this.container?.classList.remove("tack-active")}onPanelClose(){this.setPanelOpen(!1),this.pins?.hide(),this.disableCommentMode(),this.updateToggleAria(!1)}onShare(){let e=new URL(window.location.href);e.searchParams.set("tack_open","true"),navigator.clipboard.writeText(e.toString()).then(()=>{this.showToast("Link copied to clipboard")}).catch(()=>{this.showToast("Failed to copy link")})}injectPageStyles(){let e="tack-page-styles";if(document.getElementById(e))return;let t=document.createElement("style");t.id=e,t.textContent=`
1890
+ body.tack-panel-open {
1891
+ margin-right: 360px !important;
1892
+ transition: margin-right 250ms cubic-bezier(0.16, 1, 0.3, 1) !important;
1893
+ }
1894
+ body {
1895
+ transition: margin-right 250ms cubic-bezier(0.16, 1, 0.3, 1);
1896
+ }
1897
+ `,document.head.appendChild(t)}setPanelOpen(e){e?document.body.classList.add("tack-panel-open"):document.body.classList.remove("tack-panel-open")}updateToggleAria(e){let t=this.shadowRoot?.querySelector(".tack-toggle");t&&t.setAttribute("aria-expanded",String(e))}async loadComments(){try{let e=await this.api.getComments();this.comments=e.comments,this.currentVersion=e.version,this.pins?.render(this.comments),this.panel?.render(this.comments)}catch(e){console.error("[Tack] Failed to load comments:",e)}}onElementSelected(e){this.form?.isFormVisible()||(this.selector?.disable(),this.form?.show(e))}async onCommentSubmit(e){try{let t={content:e.content,element_selector:e.anchor.cssSelector,x_percent:e.anchor.relativeX*100,y_percent:e.anchor.relativeY*100,anchor:e.anchor,page_path:window.location.pathname+window.location.search,screenshot_data:e.screenshot};this.auth.isAuthenticated()||(t.author_name=e.authorName,t.author_email=e.authorEmail);let i=await this.api.createComment(t),n={...i,screenshot_url:i.screenshot_url||e.screenshot||null};this.comments.push(n),this.pins?.render(this.comments),this.panel?.render(this.comments),this.showToast("Comment added"),this.panel?.isOpen()&&this.enableCommentMode()}catch(t){console.error("[Tack] Failed to submit comment:",t),this.showToast("Failed to add comment")}}onCommentClick(e){this.pins?.highlight(e.id),this.scrollToComment(e)}onPinClick(e,t){let i=this.comments.find(n=>n.id===e);i&&(this.card?.show(i,t),this.pins?.setActive(e),this.pins?.highlight(e),this.panel?.isOpen()&&this.panel.scrollToComment(e))}onCardClose(){this.pins?.setActive(null)}async onResolve(e,t){try{await this.api.updateComment(e,{resolved:t});let i=this.comments.find(n=>n.id===e);i&&(i.resolved=t,this.pins?.render(this.comments),this.panel?.render(this.comments),this.card?.updateComment(i))}catch(i){console.error("[Tack] Failed to update comment:",i)}}async onReply(e,t){try{let i={content:t,parent_id:e,page_path:window.location.pathname+window.location.search};this.auth.isAuthenticated()||(i.author_name=localStorage.getItem("tack_author_name")||"Anonymous");let n=await this.api.createComment(i),r=this.comments.find(a=>a.id===e);r&&(r.replies=r.replies||[],r.replies.push(n),this.panel?.render(this.comments),this.card?.updateComment(r))}catch(i){console.error("[Tack] Failed to submit reply:",i)}}async onEdit(e,t){try{await this.api.updateComment(e,{content:t});let i=this.comments.find(n=>n.id===e);i&&(i.content=t,this.pins?.render(this.comments),this.panel?.render(this.comments),this.card?.updateComment(i))}catch(i){console.error("[Tack] Failed to edit comment:",i),this.showToast("Failed to edit comment")}}async onDelete(e){try{await this.api.deleteComment(e),this.comments=this.comments.filter(t=>t.id!==e),this.pins?.render(this.comments),this.panel?.render(this.comments),this.showToast("Comment deleted")}catch(t){console.error("[Tack] Failed to delete comment:",t),this.showToast("Failed to delete comment")}}scrollToComment(e){if(e.element_selector)document.querySelector(e.element_selector)?.scrollIntoView({behavior:"smooth",block:"center"});else if(e.x_percent!==null&&e.y_percent!==null){let t=e.x_percent/100*document.documentElement.scrollWidth,i=e.y_percent/100*document.documentElement.scrollHeight;window.scrollTo({left:t-window.innerWidth/2,top:i-window.innerHeight/2,behavior:"smooth"})}}handleDeepLink(){let e=new URLSearchParams(window.location.search);e.get("tack_open")==="true"&&(this.panel?.show(),this.setPanelOpen(!0),this.pins?.show(),this.container?.classList.add("tack-active"),this.updateToggleAria(!0));let i=e.get("tack_comment");if(i){let n=this.comments.find(r=>r.id===i);n&&(this.panel?.show(),this.setPanelOpen(!0),this.pins?.show(),this.container?.classList.add("tack-active"),this.updateToggleAria(!0),this.panel?.scrollToComment(i),this.pins?.highlight(i),this.scrollToComment(n))}}subscribeToRealtime(){if(!this.currentVersion||!this.config.supabaseUrl||!this.config.supabaseKey){this.pollingInterval=setInterval(()=>this.loadComments(),3e4);return}this.realtime=new S(this.config.supabaseUrl,this.config.supabaseKey,this.currentVersion.id,(e,t)=>{this.handleRealtimeEvent(e,t)}),this.realtime.connect()}handleRealtimeEvent(e,t){switch(e){case"INSERT":if(this.comments.some(r=>r.id===t.id)||this.comments.some(r=>r.replies?.some(a=>a.id===t.id)))return;if(t.parent_id){let r=this.comments.find(a=>a.id===t.parent_id);r&&(r.replies=r.replies||[],r.replies.push(t))}else this.comments.push(t);this.showToast(`New comment from ${t.author_name}`);break;case"UPDATE":let n=this.comments.find(r=>r.id===t.id);n&&Object.assign(n,t);break;case"DELETE":this.comments=this.comments.filter(r=>r.id!==t.id);break}this.pins?.render(this.comments),this.panel?.render(this.comments)}showToast(e){if(!this.shadowRoot)return;let t=document.createElement("div");t.className="tack-toast",t.textContent=e,t.style.cssText=`
1898
+ position: fixed;
1899
+ bottom: 80px;
1900
+ right: 20px;
1901
+ background: #1a1a1a;
1902
+ color: white;
1903
+ padding: 12px 20px;
1904
+ border-radius: 8px;
1905
+ font-size: 14px;
1906
+ z-index: 10002;
1907
+ animation: tack-toast-in 0.3s ease;
1908
+ `,this.shadowRoot.appendChild(t),setTimeout(()=>{t.remove()},3e3)}destroy(){this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=null),this.selector?.disable(),this.realtime?.disconnect(),this.presence?.leave(),this.pins?.destroy(),this.boundOnUrlChange&&(window.removeEventListener("popstate",this.boundOnUrlChange),this.boundOnUrlChange=null),this.originalPushState&&(history.pushState=this.originalPushState),this.originalReplaceState&&(history.replaceState=this.originalReplaceState),document.getElementById("tack-page-styles")?.remove(),document.body.classList.remove("tack-panel-open"),this.container?.remove()}};var Me={init(l){let e=new L(l);return e.mount(),e}};export{Me as Tack,L as TackWidget};
1909
+ //# sourceMappingURL=index.mjs.map