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