@ourroadmaps/web-sdk 1.4.2 → 1.5.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/{chunk-D2JMXL2O.js → chunk-SCGNQ7Z7.js} +603 -4
- package/dist/chunk-SCGNQ7Z7.js.map +1 -0
- package/dist/{chunk-Z4GLX2BM.cjs → chunk-VXDCDIG3.cjs} +604 -5
- package/dist/chunk-VXDCDIG3.cjs.map +1 -0
- package/dist/index.cjs +7 -7
- package/dist/index.js +2 -2
- package/dist/review/index.cjs +2 -2
- package/dist/review/index.js +1 -1
- package/dist/review.global.js +367 -8
- package/dist/review.global.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-D2JMXL2O.js.map +0 -1
- package/dist/chunk-Z4GLX2BM.cjs.map +0 -1
|
@@ -15,6 +15,17 @@ async function validateToken(token) {
|
|
|
15
15
|
const body = await res.json();
|
|
16
16
|
return body.data;
|
|
17
17
|
}
|
|
18
|
+
async function identify(token, name, email) {
|
|
19
|
+
const body = {};
|
|
20
|
+
if (name) body.name = name;
|
|
21
|
+
if (email) body.email = email;
|
|
22
|
+
const res = await fetch(`${API_URL}/v1/prototype-review/${token}/identify`, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { "Content-Type": "application/json" },
|
|
25
|
+
body: JSON.stringify(body)
|
|
26
|
+
});
|
|
27
|
+
if (!res.ok) throw new ReviewError("error", "Failed to identify");
|
|
28
|
+
}
|
|
18
29
|
async function submitComment(token, comment) {
|
|
19
30
|
const res = await fetch(`${API_URL}/v1/prototype-review/${token}/comments`, {
|
|
20
31
|
method: "POST",
|
|
@@ -225,6 +236,242 @@ var REVIEW_STYLES = `
|
|
|
225
236
|
box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.2);
|
|
226
237
|
}
|
|
227
238
|
|
|
239
|
+
/* \u2500\u2500\u2500 Mode Toggle \u2500\u2500\u2500 */
|
|
240
|
+
.review-mode-toggle {
|
|
241
|
+
display: flex;
|
|
242
|
+
border-radius: 6px;
|
|
243
|
+
overflow: hidden;
|
|
244
|
+
background: rgba(255, 255, 255, 0.1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.review-mode-btn {
|
|
248
|
+
padding: 5px 10px;
|
|
249
|
+
font-family: inherit;
|
|
250
|
+
font-size: 12px;
|
|
251
|
+
font-weight: 500;
|
|
252
|
+
border: none;
|
|
253
|
+
cursor: pointer;
|
|
254
|
+
color: rgba(255, 255, 255, 0.6);
|
|
255
|
+
background: transparent;
|
|
256
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
257
|
+
display: flex;
|
|
258
|
+
align-items: center;
|
|
259
|
+
gap: 4px;
|
|
260
|
+
white-space: nowrap;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.review-mode-btn:hover {
|
|
264
|
+
color: rgba(255, 255, 255, 0.85);
|
|
265
|
+
background: rgba(255, 255, 255, 0.08);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.review-mode-btn--active {
|
|
269
|
+
background: #7c3aed;
|
|
270
|
+
color: #fff;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.review-mode-btn--active:hover {
|
|
274
|
+
background: #6d28d9;
|
|
275
|
+
color: #fff;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* \u2500\u2500\u2500 Comments Panel \u2500\u2500\u2500 */
|
|
279
|
+
.review-panel {
|
|
280
|
+
position: fixed;
|
|
281
|
+
top: 0;
|
|
282
|
+
right: 0;
|
|
283
|
+
bottom: 0;
|
|
284
|
+
width: 320px;
|
|
285
|
+
background: #111;
|
|
286
|
+
color: #fff;
|
|
287
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
288
|
+
z-index: 10001;
|
|
289
|
+
display: flex;
|
|
290
|
+
flex-direction: column;
|
|
291
|
+
border-left: 1px solid rgba(255, 255, 255, 0.1);
|
|
292
|
+
transform: translateX(100%);
|
|
293
|
+
transition: transform 0.2s ease;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.review-panel--open {
|
|
297
|
+
transform: translateX(0);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.review-panel-header {
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
justify-content: space-between;
|
|
304
|
+
padding: 14px 16px;
|
|
305
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
306
|
+
flex-shrink: 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.review-panel-header h3 {
|
|
310
|
+
margin: 0;
|
|
311
|
+
font-size: 14px;
|
|
312
|
+
font-weight: 600;
|
|
313
|
+
color: rgba(255, 255, 255, 0.9);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.review-panel-close {
|
|
317
|
+
background: none;
|
|
318
|
+
border: none;
|
|
319
|
+
color: rgba(255, 255, 255, 0.5);
|
|
320
|
+
cursor: pointer;
|
|
321
|
+
padding: 4px;
|
|
322
|
+
border-radius: 4px;
|
|
323
|
+
display: flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
justify-content: center;
|
|
326
|
+
transition: color 0.15s ease, background 0.15s ease;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.review-panel-close:hover {
|
|
330
|
+
color: rgba(255, 255, 255, 0.9);
|
|
331
|
+
background: rgba(255, 255, 255, 0.1);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.review-panel-body {
|
|
335
|
+
flex: 1;
|
|
336
|
+
overflow-y: auto;
|
|
337
|
+
padding: 12px 16px;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.review-panel-section {
|
|
341
|
+
margin-bottom: 16px;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.review-panel-section-header {
|
|
345
|
+
font-size: 11px;
|
|
346
|
+
font-weight: 600;
|
|
347
|
+
text-transform: uppercase;
|
|
348
|
+
letter-spacing: 0.05em;
|
|
349
|
+
color: rgba(255, 255, 255, 0.4);
|
|
350
|
+
margin-bottom: 8px;
|
|
351
|
+
padding-bottom: 6px;
|
|
352
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.review-panel-section-toggle {
|
|
356
|
+
display: flex;
|
|
357
|
+
align-items: center;
|
|
358
|
+
gap: 6px;
|
|
359
|
+
font-size: 11px;
|
|
360
|
+
font-weight: 600;
|
|
361
|
+
text-transform: uppercase;
|
|
362
|
+
letter-spacing: 0.05em;
|
|
363
|
+
color: rgba(255, 255, 255, 0.4);
|
|
364
|
+
margin-bottom: 8px;
|
|
365
|
+
padding-bottom: 6px;
|
|
366
|
+
border: none;
|
|
367
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
368
|
+
background: none;
|
|
369
|
+
cursor: pointer;
|
|
370
|
+
width: 100%;
|
|
371
|
+
text-align: left;
|
|
372
|
+
transition: color 0.15s ease;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.review-panel-section-toggle:hover {
|
|
376
|
+
color: rgba(255, 255, 255, 0.6);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.review-panel-section-toggle svg {
|
|
380
|
+
transition: transform 0.15s ease;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.review-panel-section-toggle--expanded svg {
|
|
384
|
+
transform: rotate(90deg);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.review-panel-page-label {
|
|
388
|
+
font-size: 11px;
|
|
389
|
+
color: rgba(255, 255, 255, 0.3);
|
|
390
|
+
margin: 10px 0 6px;
|
|
391
|
+
font-family: monospace;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.review-panel-page-label:first-child {
|
|
395
|
+
margin-top: 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* \u2500\u2500\u2500 Comment Item \u2500\u2500\u2500 */
|
|
399
|
+
.review-panel-comment {
|
|
400
|
+
background: rgba(255, 255, 255, 0.05);
|
|
401
|
+
border-radius: 6px;
|
|
402
|
+
padding: 10px 12px;
|
|
403
|
+
margin-bottom: 6px;
|
|
404
|
+
cursor: pointer;
|
|
405
|
+
transition: background 0.15s ease;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.review-panel-comment:hover {
|
|
409
|
+
background: rgba(255, 255, 255, 0.08);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.review-panel-comment-header {
|
|
413
|
+
display: flex;
|
|
414
|
+
align-items: center;
|
|
415
|
+
gap: 8px;
|
|
416
|
+
margin-bottom: 4px;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.review-panel-pin-badge {
|
|
420
|
+
width: 18px;
|
|
421
|
+
height: 18px;
|
|
422
|
+
border-radius: 50%;
|
|
423
|
+
background: #7c3aed;
|
|
424
|
+
color: #fff;
|
|
425
|
+
font-size: 10px;
|
|
426
|
+
font-weight: 600;
|
|
427
|
+
display: flex;
|
|
428
|
+
align-items: center;
|
|
429
|
+
justify-content: center;
|
|
430
|
+
flex-shrink: 0;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.review-panel-general-badge {
|
|
434
|
+
width: 18px;
|
|
435
|
+
height: 18px;
|
|
436
|
+
display: flex;
|
|
437
|
+
align-items: center;
|
|
438
|
+
justify-content: center;
|
|
439
|
+
flex-shrink: 0;
|
|
440
|
+
color: rgba(255, 255, 255, 0.5);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.review-panel-comment-author {
|
|
444
|
+
font-size: 12px;
|
|
445
|
+
font-weight: 600;
|
|
446
|
+
color: rgba(255, 255, 255, 0.8);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.review-panel-comment-text {
|
|
450
|
+
font-size: 13px;
|
|
451
|
+
color: rgba(255, 255, 255, 0.6);
|
|
452
|
+
line-height: 1.4;
|
|
453
|
+
margin-bottom: 4px;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.review-panel-comment-time {
|
|
457
|
+
font-size: 11px;
|
|
458
|
+
color: rgba(255, 255, 255, 0.3);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.review-panel-empty {
|
|
462
|
+
text-align: center;
|
|
463
|
+
padding: 24px 16px;
|
|
464
|
+
color: rgba(255, 255, 255, 0.3);
|
|
465
|
+
font-size: 13px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.review-panel-loading {
|
|
469
|
+
text-align: center;
|
|
470
|
+
padding: 24px 16px;
|
|
471
|
+
color: rgba(255, 255, 255, 0.4);
|
|
472
|
+
font-size: 13px;
|
|
473
|
+
}
|
|
474
|
+
|
|
228
475
|
/* \u2500\u2500\u2500 Comment Card \u2500\u2500\u2500 */
|
|
229
476
|
.review-comment-card {
|
|
230
477
|
position: fixed;
|
|
@@ -405,6 +652,81 @@ var REVIEW_STYLES = `
|
|
|
405
652
|
backdrop-filter: blur(8px);
|
|
406
653
|
}
|
|
407
654
|
|
|
655
|
+
/* \u2500\u2500\u2500 Name Entry Overlay \u2500\u2500\u2500 */
|
|
656
|
+
.review-name-overlay {
|
|
657
|
+
position: fixed;
|
|
658
|
+
inset: 0;
|
|
659
|
+
background: rgba(0, 0, 0, 0.4);
|
|
660
|
+
display: flex;
|
|
661
|
+
align-items: center;
|
|
662
|
+
justify-content: center;
|
|
663
|
+
z-index: 10003;
|
|
664
|
+
backdrop-filter: blur(2px);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.review-name-card {
|
|
668
|
+
background: #fff;
|
|
669
|
+
border-radius: 12px;
|
|
670
|
+
padding: 28px 32px;
|
|
671
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
672
|
+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
|
|
673
|
+
max-width: 360px;
|
|
674
|
+
width: 90%;
|
|
675
|
+
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.review-name-card h3 {
|
|
679
|
+
margin: 0 0 4px;
|
|
680
|
+
font-size: 17px;
|
|
681
|
+
font-weight: 600;
|
|
682
|
+
color: #111;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.review-name-card p {
|
|
686
|
+
margin: 0 0 20px;
|
|
687
|
+
font-size: 14px;
|
|
688
|
+
color: #666;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.review-name-card label {
|
|
692
|
+
display: block;
|
|
693
|
+
font-size: 13px;
|
|
694
|
+
font-weight: 500;
|
|
695
|
+
color: #374151;
|
|
696
|
+
margin-bottom: 6px;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.review-name-card input {
|
|
700
|
+
width: 100%;
|
|
701
|
+
border: 1px solid #d1d5db;
|
|
702
|
+
border-radius: 8px;
|
|
703
|
+
padding: 10px 12px;
|
|
704
|
+
font-family: inherit;
|
|
705
|
+
font-size: 14px;
|
|
706
|
+
color: #1f2937;
|
|
707
|
+
background: #fafafa;
|
|
708
|
+
box-sizing: border-box;
|
|
709
|
+
outline: none;
|
|
710
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.review-name-card input:focus {
|
|
714
|
+
border-color: #7c3aed;
|
|
715
|
+
box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15);
|
|
716
|
+
background: #fff;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.review-name-card input::placeholder {
|
|
720
|
+
color: #9ca3af;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.review-name-actions {
|
|
724
|
+
display: flex;
|
|
725
|
+
justify-content: flex-end;
|
|
726
|
+
gap: 8px;
|
|
727
|
+
margin-top: 20px;
|
|
728
|
+
}
|
|
729
|
+
|
|
408
730
|
/* \u2500\u2500\u2500 Reduced Motion \u2500\u2500\u2500 */
|
|
409
731
|
@media (prefers-reduced-motion: reduce) {
|
|
410
732
|
*, *::before, *::after {
|
|
@@ -686,6 +1008,173 @@ var PageNavigationListener = class {
|
|
|
686
1008
|
}
|
|
687
1009
|
};
|
|
688
1010
|
|
|
1011
|
+
// src/review/CommentListPanel.ts
|
|
1012
|
+
var CommentListPanel = class {
|
|
1013
|
+
constructor(token, shadowRoot, pinManager) {
|
|
1014
|
+
this.token = token;
|
|
1015
|
+
this.shadowRoot = shadowRoot;
|
|
1016
|
+
this.pinManager = pinManager;
|
|
1017
|
+
__publicField(this, "panelEl");
|
|
1018
|
+
__publicField(this, "bodyEl", null);
|
|
1019
|
+
__publicField(this, "isOpen", false);
|
|
1020
|
+
__publicField(this, "allPagesExpanded", false);
|
|
1021
|
+
__publicField(this, "comments", []);
|
|
1022
|
+
this.panelEl = document.createElement("div");
|
|
1023
|
+
this.panelEl.className = "review-panel";
|
|
1024
|
+
this.panelEl.innerHTML = `
|
|
1025
|
+
<div class="review-panel-header">
|
|
1026
|
+
<h3>Comments</h3>
|
|
1027
|
+
<button class="review-panel-close" title="Close">
|
|
1028
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
1029
|
+
</button>
|
|
1030
|
+
</div>
|
|
1031
|
+
<div class="review-panel-body"></div>
|
|
1032
|
+
`;
|
|
1033
|
+
this.bodyEl = this.panelEl.querySelector(".review-panel-body");
|
|
1034
|
+
this.panelEl.querySelector(".review-panel-close")?.addEventListener("click", () => this.close());
|
|
1035
|
+
this.shadowRoot.appendChild(this.panelEl);
|
|
1036
|
+
}
|
|
1037
|
+
async toggle() {
|
|
1038
|
+
if (this.isOpen) {
|
|
1039
|
+
this.close();
|
|
1040
|
+
} else {
|
|
1041
|
+
await this.open();
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
async open() {
|
|
1045
|
+
this.isOpen = true;
|
|
1046
|
+
this.panelEl.classList.add("review-panel--open");
|
|
1047
|
+
await this.refresh();
|
|
1048
|
+
}
|
|
1049
|
+
close() {
|
|
1050
|
+
this.isOpen = false;
|
|
1051
|
+
this.panelEl.classList.remove("review-panel--open");
|
|
1052
|
+
this.pinManager.clearHighlight();
|
|
1053
|
+
}
|
|
1054
|
+
async refresh() {
|
|
1055
|
+
if (!this.isOpen || !this.bodyEl) return;
|
|
1056
|
+
this.bodyEl.innerHTML = '<div class="review-panel-loading">Loading comments...</div>';
|
|
1057
|
+
try {
|
|
1058
|
+
this.comments = await fetchComments(this.token);
|
|
1059
|
+
this.render();
|
|
1060
|
+
} catch {
|
|
1061
|
+
this.bodyEl.innerHTML = '<div class="review-panel-empty">Failed to load comments</div>';
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
render() {
|
|
1065
|
+
if (!this.bodyEl) return;
|
|
1066
|
+
const withText = this.comments.filter((c) => c.commentText);
|
|
1067
|
+
if (withText.length === 0) {
|
|
1068
|
+
this.bodyEl.innerHTML = '<div class="review-panel-empty">No comments yet</div>';
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const currentPage = getCurrentPageId();
|
|
1072
|
+
const currentPageComments = withText.filter((c) => c.pageUrl === currentPage);
|
|
1073
|
+
const otherComments = withText.filter((c) => c.pageUrl !== currentPage);
|
|
1074
|
+
let html = "";
|
|
1075
|
+
html += '<div class="review-panel-section">';
|
|
1076
|
+
html += `<div class="review-panel-section-header">This Page</div>`;
|
|
1077
|
+
if (currentPageComments.length === 0) {
|
|
1078
|
+
html += '<div class="review-panel-empty" style="padding:8px 0;">No comments on this page</div>';
|
|
1079
|
+
} else {
|
|
1080
|
+
html += this.renderComments(currentPageComments);
|
|
1081
|
+
}
|
|
1082
|
+
html += "</div>";
|
|
1083
|
+
if (otherComments.length > 0) {
|
|
1084
|
+
const groups = this.groupByPage(otherComments);
|
|
1085
|
+
html += '<div class="review-panel-section">';
|
|
1086
|
+
html += `<button class="review-panel-section-toggle ${this.allPagesExpanded ? "review-panel-section-toggle--expanded" : ""}" data-action="toggle-all">
|
|
1087
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
|
1088
|
+
All Pages (${otherComments.length} comment${otherComments.length !== 1 ? "s" : ""})
|
|
1089
|
+
</button>`;
|
|
1090
|
+
if (this.allPagesExpanded) {
|
|
1091
|
+
html += '<div class="review-panel-all-pages">';
|
|
1092
|
+
for (const group of groups) {
|
|
1093
|
+
html += `<div class="review-panel-page-label">${this.escapeHtml(this.formatPageUrl(group.pageUrl))}</div>`;
|
|
1094
|
+
html += this.renderComments(group.comments);
|
|
1095
|
+
}
|
|
1096
|
+
html += "</div>";
|
|
1097
|
+
}
|
|
1098
|
+
html += "</div>";
|
|
1099
|
+
}
|
|
1100
|
+
this.bodyEl.innerHTML = html;
|
|
1101
|
+
this.attachEventListeners();
|
|
1102
|
+
}
|
|
1103
|
+
renderComments(comments) {
|
|
1104
|
+
return comments.map((c) => {
|
|
1105
|
+
const badge = c.pinNumber != null ? `<span class="review-panel-pin-badge">${c.pinNumber}</span>` : `<span class="review-panel-general-badge"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></span>`;
|
|
1106
|
+
const author = c.reviewerName || "Anonymous";
|
|
1107
|
+
const text = c.commentText || "";
|
|
1108
|
+
const time = this.formatTime(c.createdAt);
|
|
1109
|
+
const pinAttr = c.pinNumber != null ? `data-pin="${c.pinNumber}"` : "";
|
|
1110
|
+
const pageAttr = c.pageUrl ? `data-page="${this.escapeAttr(c.pageUrl)}"` : "";
|
|
1111
|
+
return `<div class="review-panel-comment" ${pinAttr} ${pageAttr}>
|
|
1112
|
+
<div class="review-panel-comment-header">
|
|
1113
|
+
${badge}
|
|
1114
|
+
<span class="review-panel-comment-author">${this.escapeHtml(author)}</span>
|
|
1115
|
+
</div>
|
|
1116
|
+
<div class="review-panel-comment-text">${this.escapeHtml(text)}</div>
|
|
1117
|
+
<div class="review-panel-comment-time">${time}</div>
|
|
1118
|
+
</div>`;
|
|
1119
|
+
}).join("");
|
|
1120
|
+
}
|
|
1121
|
+
attachEventListeners() {
|
|
1122
|
+
if (!this.bodyEl) return;
|
|
1123
|
+
this.bodyEl.querySelector('[data-action="toggle-all"]')?.addEventListener("click", () => {
|
|
1124
|
+
this.allPagesExpanded = !this.allPagesExpanded;
|
|
1125
|
+
this.render();
|
|
1126
|
+
});
|
|
1127
|
+
this.bodyEl.querySelectorAll(".review-panel-comment[data-pin]").forEach((el) => {
|
|
1128
|
+
el.addEventListener("click", () => {
|
|
1129
|
+
const pinNumber = Number(el.dataset.pin);
|
|
1130
|
+
if (pinNumber) {
|
|
1131
|
+
this.pinManager.highlightPin(pinNumber);
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
groupByPage(comments) {
|
|
1137
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1138
|
+
for (const c of comments) {
|
|
1139
|
+
const key = c.pageUrl || "(unknown)";
|
|
1140
|
+
const arr = groups.get(key) || [];
|
|
1141
|
+
arr.push(c);
|
|
1142
|
+
groups.set(key, arr);
|
|
1143
|
+
}
|
|
1144
|
+
return Array.from(groups.entries()).map(([pageUrl, comments2]) => ({ pageUrl, comments: comments2 }));
|
|
1145
|
+
}
|
|
1146
|
+
formatPageUrl(url) {
|
|
1147
|
+
try {
|
|
1148
|
+
const u = new URL(url, "https://placeholder");
|
|
1149
|
+
return u.pathname + u.search + u.hash;
|
|
1150
|
+
} catch {
|
|
1151
|
+
return url || "/";
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
formatTime(iso) {
|
|
1155
|
+
const date = new Date(iso);
|
|
1156
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
1157
|
+
const now = Date.now();
|
|
1158
|
+
const diffMs = Math.max(0, now - date.getTime());
|
|
1159
|
+
const diffMin = Math.floor(diffMs / 6e4);
|
|
1160
|
+
if (diffMin < 1) return "just now";
|
|
1161
|
+
if (diffMin < 60) return `${diffMin}m ago`;
|
|
1162
|
+
const diffHr = Math.floor(diffMin / 60);
|
|
1163
|
+
if (diffHr < 24) return `${diffHr}h ago`;
|
|
1164
|
+
const diffDays = Math.floor(diffHr / 24);
|
|
1165
|
+
return `${diffDays}d ago`;
|
|
1166
|
+
}
|
|
1167
|
+
escapeHtml(str) {
|
|
1168
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1169
|
+
}
|
|
1170
|
+
escapeAttr(str) {
|
|
1171
|
+
return str.replace(/"/g, """).replace(/'/g, "'");
|
|
1172
|
+
}
|
|
1173
|
+
destroy() {
|
|
1174
|
+
this.panelEl.remove();
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
|
|
689
1178
|
// src/review/ReviewMode.ts
|
|
690
1179
|
var ReviewMode = class {
|
|
691
1180
|
constructor(token, shadowRoot, initData) {
|
|
@@ -700,15 +1189,22 @@ var ReviewMode = class {
|
|
|
700
1189
|
__publicField(this, "toolbarEl", null);
|
|
701
1190
|
__publicField(this, "clickHandler", null);
|
|
702
1191
|
__publicField(this, "pageListener", null);
|
|
1192
|
+
__publicField(this, "mode", "comment");
|
|
1193
|
+
__publicField(this, "keydownHandler", null);
|
|
1194
|
+
__publicField(this, "commentPanel", null);
|
|
703
1195
|
this.pinManager = new PinManager();
|
|
704
1196
|
this.commentCard = new CommentCard(shadowRoot);
|
|
705
1197
|
this.nextPinNumber = initData.nextPinNumber;
|
|
706
1198
|
}
|
|
707
1199
|
async init() {
|
|
1200
|
+
if (!this.initData.reviewer.name) {
|
|
1201
|
+
await this.showNameEntry();
|
|
1202
|
+
}
|
|
708
1203
|
document.body.style.cursor = "crosshair";
|
|
709
1204
|
this.pinManager.mount();
|
|
710
1205
|
this.showPrompt();
|
|
711
1206
|
this.renderToolbar();
|
|
1207
|
+
this.commentPanel = new CommentListPanel(this.token, this.shadowRoot, this.pinManager);
|
|
712
1208
|
await this.loadExistingPins();
|
|
713
1209
|
this.pinManager.filterByPage(getCurrentPageId());
|
|
714
1210
|
this.pageListener = new PageNavigationListener((pageId) => {
|
|
@@ -717,6 +1213,15 @@ var ReviewMode = class {
|
|
|
717
1213
|
this.pageListener.start();
|
|
718
1214
|
this.clickHandler = (e) => this.handleClick(e);
|
|
719
1215
|
document.addEventListener("click", this.clickHandler, true);
|
|
1216
|
+
this.keydownHandler = (e) => {
|
|
1217
|
+
const tag = e.target?.tagName;
|
|
1218
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
1219
|
+
if (e.target?.isContentEditable) return;
|
|
1220
|
+
if (e.key === "c" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1221
|
+
this.setMode(this.mode === "comment" ? "navigate" : "comment");
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
document.addEventListener("keydown", this.keydownHandler);
|
|
720
1225
|
}
|
|
721
1226
|
showPrompt() {
|
|
722
1227
|
this.promptEl = document.createElement("div");
|
|
@@ -734,6 +1239,59 @@ var ReviewMode = class {
|
|
|
734
1239
|
this.promptEl = null;
|
|
735
1240
|
}
|
|
736
1241
|
}
|
|
1242
|
+
showNameEntry() {
|
|
1243
|
+
return new Promise((resolve) => {
|
|
1244
|
+
const overlay = document.createElement("div");
|
|
1245
|
+
overlay.className = "review-name-overlay";
|
|
1246
|
+
overlay.innerHTML = `
|
|
1247
|
+
<div class="review-name-card">
|
|
1248
|
+
<h3>Welcome to the review</h3>
|
|
1249
|
+
<p>Optionally enter your name so the team knows who left the feedback.</p>
|
|
1250
|
+
<div style="margin-bottom:12px;">
|
|
1251
|
+
<label>Name</label>
|
|
1252
|
+
<input type="text" class="review-name-input" placeholder="Your name (optional)" />
|
|
1253
|
+
</div>
|
|
1254
|
+
<div>
|
|
1255
|
+
<label>Email</label>
|
|
1256
|
+
<input type="email" class="review-email-input" placeholder="Your email (optional)" />
|
|
1257
|
+
</div>
|
|
1258
|
+
<div class="review-name-actions">
|
|
1259
|
+
<button class="review-btn review-btn--cancel review-name-skip">Skip</button>
|
|
1260
|
+
<button class="review-btn review-btn--submit review-name-continue">Continue</button>
|
|
1261
|
+
</div>
|
|
1262
|
+
</div>
|
|
1263
|
+
`;
|
|
1264
|
+
this.shadowRoot.appendChild(overlay);
|
|
1265
|
+
const nameInput = overlay.querySelector(".review-name-input");
|
|
1266
|
+
const emailInput = overlay.querySelector(".review-email-input");
|
|
1267
|
+
const skipBtn = overlay.querySelector(".review-name-skip");
|
|
1268
|
+
const continueBtn = overlay.querySelector(".review-name-continue");
|
|
1269
|
+
const finish = async () => {
|
|
1270
|
+
const name = nameInput.value.trim() || void 0;
|
|
1271
|
+
const email = emailInput.value.trim() || void 0;
|
|
1272
|
+
try {
|
|
1273
|
+
await identify(this.token, name, email);
|
|
1274
|
+
} catch {
|
|
1275
|
+
}
|
|
1276
|
+
overlay.remove();
|
|
1277
|
+
resolve();
|
|
1278
|
+
};
|
|
1279
|
+
skipBtn.addEventListener("click", () => {
|
|
1280
|
+
identify(this.token).catch(() => {
|
|
1281
|
+
});
|
|
1282
|
+
overlay.remove();
|
|
1283
|
+
resolve();
|
|
1284
|
+
});
|
|
1285
|
+
continueBtn.addEventListener("click", finish);
|
|
1286
|
+
nameInput.addEventListener("keydown", (e) => {
|
|
1287
|
+
if (e.key === "Enter") finish();
|
|
1288
|
+
});
|
|
1289
|
+
emailInput.addEventListener("keydown", (e) => {
|
|
1290
|
+
if (e.key === "Enter") finish();
|
|
1291
|
+
});
|
|
1292
|
+
nameInput.focus();
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
737
1295
|
renderToolbar() {
|
|
738
1296
|
this.toolbarEl = document.createElement("div");
|
|
739
1297
|
this.toolbarEl.className = "review-toolbar";
|
|
@@ -743,15 +1301,41 @@ var ReviewMode = class {
|
|
|
743
1301
|
updateToolbar() {
|
|
744
1302
|
if (!this.toolbarEl) return;
|
|
745
1303
|
const pinCount = this.nextPinNumber - 1;
|
|
1304
|
+
const isNav = this.mode === "navigate";
|
|
1305
|
+
const isComment = this.mode === "comment";
|
|
746
1306
|
this.toolbarEl.innerHTML = `
|
|
1307
|
+
<div class="review-mode-toggle">
|
|
1308
|
+
<button class="review-mode-btn ${isNav ? "review-mode-btn--active" : ""}" data-mode="navigate">
|
|
1309
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
|
1310
|
+
Navigate
|
|
1311
|
+
</button>
|
|
1312
|
+
<button class="review-mode-btn ${isComment ? "review-mode-btn--active" : ""}" data-mode="comment">
|
|
1313
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0Z"/><circle cx="12" cy="10" r="3"/></svg>
|
|
1314
|
+
Comment
|
|
1315
|
+
</button>
|
|
1316
|
+
</div>
|
|
747
1317
|
<span style="font-weight:500;">${this.initData.session.name}</span>
|
|
748
1318
|
<span style="opacity:0.7;">${pinCount} pin${pinCount !== 1 ? "s" : ""}</span>
|
|
749
|
-
<button class="review-btn review-btn--submit" style="margin-left:auto;padding:6px 12px;font-size:13px;">General Comment</button>
|
|
1319
|
+
<button class="review-btn review-btn--submit" data-action="general" style="margin-left:auto;padding:6px 12px;font-size:13px;">General Comment</button>
|
|
1320
|
+
<button class="review-mode-btn" data-action="panel" title="View all comments" style="padding:5px 8px;">
|
|
1321
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
|
1322
|
+
</button>
|
|
750
1323
|
`;
|
|
751
|
-
this.toolbarEl.
|
|
1324
|
+
this.toolbarEl.querySelectorAll("[data-mode]").forEach((btn) => {
|
|
1325
|
+
btn.addEventListener("click", (e) => {
|
|
1326
|
+
e.stopPropagation();
|
|
1327
|
+
const mode = btn.dataset.mode;
|
|
1328
|
+
this.setMode(mode);
|
|
1329
|
+
});
|
|
1330
|
+
});
|
|
1331
|
+
this.toolbarEl.querySelector('[data-action="general"]')?.addEventListener("click", (e) => {
|
|
752
1332
|
e.stopPropagation();
|
|
753
1333
|
this.handleGeneralComment();
|
|
754
1334
|
});
|
|
1335
|
+
this.toolbarEl.querySelector('[data-action="panel"]')?.addEventListener("click", (e) => {
|
|
1336
|
+
e.stopPropagation();
|
|
1337
|
+
this.toggleCommentPanel();
|
|
1338
|
+
});
|
|
755
1339
|
}
|
|
756
1340
|
async loadExistingPins() {
|
|
757
1341
|
try {
|
|
@@ -766,6 +1350,7 @@ var ReviewMode = class {
|
|
|
766
1350
|
}
|
|
767
1351
|
}
|
|
768
1352
|
handleClick(e) {
|
|
1353
|
+
if (this.mode === "navigate") return;
|
|
769
1354
|
const path = e.composedPath();
|
|
770
1355
|
if (path.some((el) => el instanceof HTMLElement && el.closest?.("#ourroadmaps-review"))) return;
|
|
771
1356
|
if (this.pendingPinNumber != null) return;
|
|
@@ -788,6 +1373,7 @@ var ReviewMode = class {
|
|
|
788
1373
|
this.nextPinNumber++;
|
|
789
1374
|
this.updateToolbar();
|
|
790
1375
|
this.showToast("Comment saved");
|
|
1376
|
+
this.commentPanel?.refresh();
|
|
791
1377
|
},
|
|
792
1378
|
onCancel: () => {
|
|
793
1379
|
this.pinManager.removePin(pinNumber);
|
|
@@ -797,6 +1383,14 @@ var ReviewMode = class {
|
|
|
797
1383
|
e.preventDefault();
|
|
798
1384
|
e.stopPropagation();
|
|
799
1385
|
}
|
|
1386
|
+
setMode(newMode) {
|
|
1387
|
+
this.mode = newMode;
|
|
1388
|
+
document.body.style.cursor = newMode === "comment" ? "crosshair" : "";
|
|
1389
|
+
this.updateToolbar();
|
|
1390
|
+
}
|
|
1391
|
+
toggleCommentPanel() {
|
|
1392
|
+
this.commentPanel?.toggle();
|
|
1393
|
+
}
|
|
800
1394
|
handleGeneralComment() {
|
|
801
1395
|
if (this.pendingPinNumber != null) return;
|
|
802
1396
|
this.commentCard.showForGeneral({
|
|
@@ -808,6 +1402,7 @@ var ReviewMode = class {
|
|
|
808
1402
|
pageUrl: getCurrentPageId()
|
|
809
1403
|
});
|
|
810
1404
|
this.showToast("Comment saved");
|
|
1405
|
+
this.commentPanel?.refresh();
|
|
811
1406
|
},
|
|
812
1407
|
onCancel: () => {
|
|
813
1408
|
}
|
|
@@ -826,9 +1421,13 @@ var ReviewMode = class {
|
|
|
826
1421
|
document.removeEventListener("click", this.clickHandler, true);
|
|
827
1422
|
}
|
|
828
1423
|
this.pageListener?.destroy();
|
|
1424
|
+
if (this.keydownHandler) {
|
|
1425
|
+
document.removeEventListener("keydown", this.keydownHandler);
|
|
1426
|
+
}
|
|
829
1427
|
this.dismissPrompt();
|
|
830
1428
|
this.toolbarEl?.remove();
|
|
831
1429
|
this.commentCard.hide();
|
|
1430
|
+
this.commentPanel?.destroy();
|
|
832
1431
|
this.pinManager.destroy();
|
|
833
1432
|
}
|
|
834
1433
|
};
|
|
@@ -1165,5 +1764,5 @@ var Review = class {
|
|
|
1165
1764
|
};
|
|
1166
1765
|
|
|
1167
1766
|
export { Review, getStoredToken, storeToken };
|
|
1168
|
-
//# sourceMappingURL=chunk-
|
|
1169
|
-
//# sourceMappingURL=chunk-
|
|
1767
|
+
//# sourceMappingURL=chunk-SCGNQ7Z7.js.map
|
|
1768
|
+
//# sourceMappingURL=chunk-SCGNQ7Z7.js.map
|