@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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/review/index.ts","../src/review/api.ts","../src/review/ElementCapture.ts","../src/review/styles.ts","../src/review/PinManager.ts","../src/review/CommentCard.ts","../src/review/PageTracker.ts","../src/review/ReviewMode.ts","../src/review/TokenStorage.ts","../src/review/TriageMode.ts","../src/review/Review.ts"],"sourcesContent":["export { Review } from './Review'\nexport type { PinData, ReviewOptions } from './types'\n","import type { PinData, ReviewComment, ReviewInitData } from './types'\n\n// Default to production, override with env var for local dev\nconst API_URL = (() => {\n // Vite dev mode\n if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_URL) {\n return import.meta.env.VITE_API_URL\n }\n return 'https://api.ourroadmaps.com'\n})()\n\nexport async function validateToken(token: string): Promise<ReviewInitData> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}`)\n if (res.status === 410) throw new ReviewError('expired', 'This feedback session has expired')\n if (res.status === 404) throw new ReviewError('invalid', 'This review link is not valid')\n if (!res.ok) throw new ReviewError('error', 'Something went wrong')\n const body = await res.json()\n return body.data\n}\n\n// TODO: Wire into SDK — show name prompt on first visit, call identify() to transition invite status from 'pending' to 'opened'\nexport async function identify(token: string, name: string): Promise<void> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/identify`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n if (!res.ok) throw new ReviewError('error', 'Failed to identify')\n}\n\nexport async function submitComment(\n token: string,\n comment: { commentText: string; pinNumber: number | null; pinData: PinData | null; pageUrl: string | null },\n): Promise<{ id: string; pinNumber: number | null; createdAt: string }> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/comments`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(comment),\n })\n if (!res.ok) throw new ReviewError('error', 'Failed to submit comment')\n const body = await res.json()\n return body.data\n}\n\nexport async function fetchComments(token: string): Promise<ReviewComment[]> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/comments`)\n if (!res.ok) throw new ReviewError('error', 'Failed to load comments')\n const body = await res.json()\n return body.data\n}\n\nexport class ReviewError extends Error {\n constructor(\n public code: 'expired' | 'invalid' | 'error',\n message: string,\n ) {\n super(message)\n this.name = 'ReviewError'\n }\n}\n","import type { PinData } from './types'\n\nexport function captureElementContext(clientX: number, clientY: number): PinData {\n const el = document.elementFromPoint(clientX, clientY) as HTMLElement | null\n const docEl = document.documentElement\n\n const pinX = ((clientX + window.scrollX) / docEl.scrollWidth) * 100\n const pinY = ((clientY + window.scrollY) / docEl.scrollHeight) * 100\n\n if (!el || el === document.body || el === docEl) {\n return {\n pinX,\n pinY,\n element: {\n selector: 'body',\n tag: 'body',\n text: '',\n ariaLabel: null,\n className: '',\n boundingBox: { x: 0, y: 0, w: docEl.scrollWidth, h: docEl.scrollHeight },\n },\n context: {\n parentTag: '',\n parentText: '',\n grandparentTag: '',\n siblings: [],\n nearbyText: '',\n },\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }\n }\n\n const rect = el.getBoundingClientRect()\n\n return {\n pinX,\n pinY,\n element: {\n selector: buildSelector(el),\n tag: el.tagName.toLowerCase(),\n text: getDirectText(el).slice(0, 200),\n ariaLabel: el.getAttribute('aria-label'),\n className: el.className && typeof el.className === 'string' ? el.className : '',\n boundingBox: {\n x: Math.round(rect.x),\n y: Math.round(rect.y),\n w: Math.round(rect.width),\n h: Math.round(rect.height),\n },\n },\n context: {\n parentTag: el.parentElement ? `${el.parentElement.tagName.toLowerCase()}${classStr(el.parentElement)}` : '',\n parentText: el.parentElement ? getDirectText(el.parentElement).slice(0, 100) : '',\n grandparentTag: el.parentElement?.parentElement\n ? `${el.parentElement.parentElement.tagName.toLowerCase()}${classStr(el.parentElement.parentElement)}`\n : '',\n siblings: getSiblingsSummary(el),\n nearbyText: getNearbyText(el).slice(0, 200),\n },\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }\n}\n\nfunction buildSelector(el: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = el\n\n while (current && current !== document.body) {\n if (current.id) {\n parts.unshift(`#${current.id}`)\n break\n }\n\n let part = current.tagName.toLowerCase()\n if (current.className && typeof current.className === 'string') {\n const classes = current.className.trim().split(/\\s+/).slice(0, 2).join('.')\n if (classes) part += `.${classes}`\n }\n\n parts.unshift(part)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\nfunction getDirectText(el: HTMLElement): string {\n let text = ''\n for (const node of el.childNodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent?.trim() || ''\n }\n }\n return text.trim() || el.textContent?.trim().slice(0, 200) || ''\n}\n\nfunction classStr(el: HTMLElement): string {\n if (!el.className || typeof el.className !== 'string') return ''\n const cls = el.className.trim().split(/\\s+/).slice(0, 2).join('.')\n return cls ? `.${cls}` : ''\n}\n\nfunction getSiblingsSummary(el: HTMLElement): string[] {\n if (!el.parentElement) return []\n const siblings: string[] = []\n for (const child of el.parentElement.children) {\n if (child === el) continue\n const tag = child.tagName.toLowerCase()\n const text = (child.textContent?.trim() || '').slice(0, 50)\n siblings.push(text ? `${tag}:${text}` : tag)\n if (siblings.length >= 4) break\n }\n return siblings\n}\n\nfunction getNearbyText(el: HTMLElement): string {\n // Walk up to find the nearest container with meaningful text\n let current: HTMLElement | null = el.parentElement\n let depth = 0\n while (current && depth < 3) {\n const text = getDirectText(current)\n if (text && text !== getDirectText(el)) return text\n current = current.parentElement\n depth++\n }\n return ''\n}\n","export const REVIEW_STYLES = `\n:host {\n all: initial;\n}\n\n/* ─── Pin Container (rendered outside shadow DOM) ─── */\n.pin-container {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 10000;\n}\n\n/* ─── Pin Marker ─── */\n.review-pin {\n position: absolute;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #7c3aed;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n z-index: 1;\n}\n\n.review-pin:hover {\n transform: translate(-50%, -50%) scale(1.15);\n box-shadow: 0 3px 10px rgba(124, 58, 237, 0.4);\n}\n\n.review-pin--other {\n opacity: 0.5;\n}\n\n.review-pin--highlighted {\n transform: translate(-50%, -50%) scale(1.2);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3), 0 3px 10px rgba(124, 58, 237, 0.4);\n z-index: 2;\n}\n\n/* ─── Toolbar ─── */\n.review-toolbar {\n position: fixed;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n border-radius: 12px 12px 0 0;\n padding: 10px 20px;\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n z-index: 10001;\n backdrop-filter: blur(8px);\n box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.2);\n}\n\n/* ─── Comment Card ─── */\n.review-comment-card {\n position: fixed;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15), 0 1px 4px rgba(0, 0, 0, 0.08);\n width: 300px;\n padding: 16px;\n z-index: 10002;\n font-family: system-ui, -apple-system, sans-serif;\n border: 1px solid rgba(0, 0, 0, 0.08);\n}\n\n/* ─── Comment Textarea ─── */\n.review-comment-input {\n width: 100%;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n padding: 10px 12px;\n resize: vertical;\n font-family: inherit;\n font-size: 14px;\n line-height: 1.5;\n color: #1f2937;\n background: #fafafa;\n box-sizing: border-box;\n outline: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.review-comment-input:focus {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15);\n background: #fff;\n}\n\n.review-comment-input::placeholder {\n color: #9ca3af;\n}\n\n.review-comment-input:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* ─── Comment Actions ─── */\n.review-comment-actions {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n gap: 8px;\n margin-top: 12px;\n}\n\n/* ─── Buttons ─── */\n.review-btn {\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n border: none;\n transition: background 0.15s ease, opacity 0.15s ease;\n}\n\n.review-btn--cancel {\n color: #6b7280;\n background: transparent;\n}\n\n.review-btn--cancel:hover {\n background: #f3f4f6;\n}\n\n.review-btn--submit {\n background: #7c3aed;\n color: #fff;\n}\n\n.review-btn--submit:hover {\n background: #6d28d9;\n}\n\n.review-btn--submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* ─── Comment Error ─── */\n.review-comment-error {\n color: #dc2626;\n font-size: 12px;\n margin-top: 8px;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n/* ─── Name Prompt ─── */\n.review-prompt {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: #fff;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2), 0 1px 4px rgba(0, 0, 0, 0.08);\n border-radius: 12px;\n padding: 28px 32px;\n z-index: 10002;\n text-align: center;\n font-family: system-ui, -apple-system, sans-serif;\n border: 1px solid rgba(0, 0, 0, 0.06);\n max-width: 360px;\n width: 90%;\n}\n\n/* ─── Expired Overlay ─── */\n.review-expired-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10003;\n backdrop-filter: blur(2px);\n}\n\n.review-expired-card {\n background: #fff;\n border-radius: 12px;\n padding: 28px 32px;\n text-align: center;\n font-family: system-ui, -apple-system, sans-serif;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);\n max-width: 400px;\n width: 90%;\n border: 1px solid rgba(0, 0, 0, 0.06);\n}\n\n/* ─── Toast ─── */\n.review-toast {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n padding: 10px 20px;\n border-radius: 8px;\n z-index: 10003;\n pointer-events: none;\n animation: review-toast-fade 2.5s ease forwards;\n backdrop-filter: blur(8px);\n}\n\n@keyframes review-toast-fade {\n 0% { opacity: 0; transform: translateX(-50%) translateY(8px); }\n 10% { opacity: 1; transform: translateX(-50%) translateY(0); }\n 80% { opacity: 1; transform: translateX(-50%) translateY(0); }\n 100% { opacity: 0; transform: translateX(-50%) translateY(-4px); }\n}\n\n/* ─── Tooltip ─── */\n.review-tooltip {\n position: absolute;\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 13px;\n line-height: 1.4;\n border-radius: 6px;\n padding: 8px 12px;\n max-width: 280px;\n z-index: 10002;\n pointer-events: none;\n backdrop-filter: blur(8px);\n}\n\n/* ─── Reduced Motion ─── */\n@media (prefers-reduced-motion: reduce) {\n *, *::before, *::after {\n animation-duration: 0.01ms !important;\n transition-duration: 0.01ms !important;\n }\n}\n`\n\n/**\n * Subset of styles for pins that render outside the shadow DOM.\n * Injected into document.head by PinManager.\n */\nexport const PIN_DOCUMENT_STYLES = `\nbody {\n position: relative !important;\n}\n\n.pin-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n min-height: 100%;\n pointer-events: none;\n z-index: 10000;\n}\n\n.review-pin {\n position: absolute;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #7c3aed;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n z-index: 1;\n}\n\n.review-pin:hover {\n transform: translate(-50%, -50%) scale(1.15);\n box-shadow: 0 3px 10px rgba(124, 58, 237, 0.4);\n}\n\n.review-pin--other {\n opacity: 0.5;\n}\n\n.review-pin--highlighted {\n transform: translate(-50%, -50%) scale(1.2);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3), 0 3px 10px rgba(124, 58, 237, 0.4);\n z-index: 2;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .review-pin, .review-pin:hover, .review-pin--highlighted {\n transition-duration: 0.01ms !important;\n }\n}\n`\n","import { PIN_DOCUMENT_STYLES } from './styles'\n\nconst STYLE_ID = 'roadmaps-review-pin-styles'\n\ninterface PinEntry {\n el: HTMLElement\n pageUrl: string\n}\n\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, PinEntry> = new Map()\n private styleEl: HTMLStyleElement | null = null\n\n constructor() {\n this.container = document.createElement('div')\n this.container.className = 'pin-container'\n }\n\n mount(): void {\n if (!document.getElementById(STYLE_ID)) {\n this.styleEl = document.createElement('style')\n this.styleEl.id = STYLE_ID\n this.styleEl.textContent = PIN_DOCUMENT_STYLES\n document.head.appendChild(this.styleEl)\n }\n\n document.body.appendChild(this.container)\n }\n\n addPin(pinNumber: number, x: number, y: number, isMine: boolean, pageUrl: string): void {\n // Remove existing pin with same number to prevent orphaned DOM elements\n const existing = this.pins.get(pinNumber)\n if (existing) {\n existing.el.remove()\n this.pins.delete(pinNumber)\n }\n\n const pin = document.createElement('div')\n pin.className = isMine ? 'review-pin' : 'review-pin review-pin--other'\n pin.textContent = String(pinNumber)\n pin.style.left = `${x}%`\n pin.style.top = `${y}%`\n pin.dataset.pinNumber = String(pinNumber)\n pin.dataset.pageUrl = pageUrl\n this.container.appendChild(pin)\n this.pins.set(pinNumber, { el: pin, pageUrl })\n }\n\n removePin(pinNumber: number): void {\n const entry = this.pins.get(pinNumber)\n if (entry) {\n entry.el.remove()\n this.pins.delete(pinNumber)\n }\n }\n\n highlightPin(pinNumber: number): void {\n for (const [num, { el }] of this.pins) {\n el.classList.toggle('review-pin--highlighted', num === pinNumber)\n }\n }\n\n clearHighlight(): void {\n for (const { el } of this.pins.values()) {\n el.classList.remove('review-pin--highlighted')\n }\n }\n\n filterByPage(currentPageId: string): void {\n for (const { el, pageUrl } of this.pins.values()) {\n el.style.display = pageUrl === currentPageId ? '' : 'none'\n }\n }\n\n showAll(): void {\n for (const { el } of this.pins.values()) {\n el.style.display = ''\n }\n }\n\n countForPage(pageId: string): number {\n let count = 0\n for (const { pageUrl } of this.pins.values()) {\n if (pageUrl === pageId) count++\n }\n return count\n }\n\n destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n if (this.styleEl) {\n this.styleEl.remove()\n this.styleEl = null\n } else {\n document.getElementById(STYLE_ID)?.remove()\n }\n }\n}\n","export class CommentCard {\n private card: HTMLElement | null = null\n private onSubmit: ((text: string) => Promise<void>) | null = null\n private onCancel: (() => void) | null = null\n\n constructor(private shadowRoot: ShadowRoot) {}\n\n show(options: {\n x: number\n y: number\n onSubmit: (text: string) => Promise<void>\n onCancel: () => void\n }): void {\n this.hide()\n this.onSubmit = options.onSubmit\n this.onCancel = options.onCancel\n\n this.card = document.createElement('div')\n this.card.className = 'review-comment-card'\n // Position near click point, adjust to stay in viewport\n this.card.style.left = `${Math.min(options.x, window.innerWidth - 320)}px`\n this.card.style.top = `${Math.min(options.y + 20, window.innerHeight - 200)}px`\n\n this.card.innerHTML = `\n <textarea class=\"review-comment-input\" placeholder=\"Leave your feedback...\" rows=\"3\"></textarea>\n <div class=\"review-comment-actions\">\n <button class=\"review-btn review-btn--cancel\">Cancel</button>\n <button class=\"review-btn review-btn--submit\">Submit</button>\n </div>\n <div class=\"review-comment-error\" style=\"display:none\"></div>\n `\n\n this.attachListeners()\n this.shadowRoot.appendChild(this.card)\n\n const textarea = this.card.querySelector('textarea')\n textarea?.focus()\n }\n\n hide(): void {\n this.card?.remove()\n this.card = null\n }\n\n showForGeneral(options: {\n onSubmit: (text: string) => Promise<void>\n onCancel: () => void\n }): void {\n // Show centered for general (non-pinned) comments\n this.show({\n x: window.innerWidth / 2 - 150,\n y: window.innerHeight / 2 - 100,\n onSubmit: options.onSubmit,\n onCancel: options.onCancel,\n })\n }\n\n private attachListeners(): void {\n if (!this.card) return\n\n this.card.querySelector('.review-btn--cancel')?.addEventListener('click', () => {\n this.onCancel?.()\n this.hide()\n })\n\n this.card.querySelector('.review-btn--submit')?.addEventListener('click', () => this.handleSubmit())\n\n this.card.querySelector('textarea')?.addEventListener('keydown', (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n this.handleSubmit()\n }\n })\n }\n\n private async handleSubmit(): Promise<void> {\n if (!this.card) return\n const textarea = this.card.querySelector('textarea') as HTMLTextAreaElement\n const text = textarea.value.trim()\n if (!text) return\n\n const submitBtn = this.card.querySelector('.review-btn--submit') as HTMLButtonElement\n const errorEl = this.card.querySelector('.review-comment-error') as HTMLElement\n submitBtn.disabled = true\n submitBtn.textContent = 'Submitting...'\n textarea.disabled = true\n errorEl.style.display = 'none'\n\n try {\n await this.onSubmit?.(text)\n this.hide()\n } catch (err) {\n submitBtn.disabled = false\n submitBtn.textContent = 'Submit'\n textarea.disabled = false\n errorEl.textContent = err instanceof Error ? err.message : 'Failed to submit'\n errorEl.style.display = 'block'\n }\n }\n}\n","/**\n * Returns the current page identity as pathname + search + hash.\n * This captures all routing strategies: pathname, hash, and query-param based.\n */\nexport function getCurrentPageId(): string {\n return window.location.pathname + window.location.search + window.location.hash\n}\n\n/**\n * Detects page navigation within a prototype.\n * Covers: popstate (back/forward), hashchange (hash routing),\n * and pushState/replaceState (SPA routing via history API).\n */\nexport class PageNavigationListener {\n private popstateHandler: (() => void) | null = null\n private hashchangeHandler: (() => void) | null = null\n private originalPushState: typeof history.pushState | null = null\n private originalReplaceState: typeof history.replaceState | null = null\n private lastPageId: string\n\n constructor(private onChange: (pageId: string) => void) {\n this.lastPageId = getCurrentPageId()\n }\n\n start(): void {\n // Guard against double-start — would capture already-patched history methods\n if (this.originalPushState) return\n\n const check = () => {\n const current = getCurrentPageId()\n if (current !== this.lastPageId) {\n this.lastPageId = current\n this.onChange(current)\n }\n }\n\n this.popstateHandler = check\n window.addEventListener('popstate', this.popstateHandler)\n\n this.hashchangeHandler = check\n window.addEventListener('hashchange', this.hashchangeHandler)\n\n this.originalPushState = history.pushState.bind(history)\n this.originalReplaceState = history.replaceState.bind(history)\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args)\n check()\n }\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args)\n check()\n }\n }\n\n destroy(): void {\n if (this.popstateHandler) {\n window.removeEventListener('popstate', this.popstateHandler)\n }\n if (this.hashchangeHandler) {\n window.removeEventListener('hashchange', this.hashchangeHandler)\n }\n\n if (this.originalPushState) {\n history.pushState = this.originalPushState\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState\n }\n }\n}\n","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\nimport { getCurrentPageId, PageNavigationListener } from './PageTracker'\n\nexport class ReviewMode {\n private pinManager: PinManager\n private commentCard: CommentCard\n private nextPinNumber: number\n private pendingPinNumber: number | null = null\n private promptEl: HTMLElement | null = null\n private toolbarEl: HTMLElement | null = null\n private clickHandler: ((e: MouseEvent) => void) | null = null\n private pageListener: PageNavigationListener | null = null\n\n constructor(\n private token: string,\n private shadowRoot: ShadowRoot,\n private initData: ReviewInitData,\n ) {\n this.pinManager = new PinManager()\n this.commentCard = new CommentCard(shadowRoot)\n this.nextPinNumber = initData.nextPinNumber\n }\n\n async init(): Promise<void> {\n // Set crosshair cursor on body\n document.body.style.cursor = 'crosshair'\n\n // Mount pin manager\n this.pinManager.mount()\n\n // Render first-visit prompt\n this.showPrompt()\n\n // Render bottom toolbar\n this.renderToolbar()\n\n // Fetch and render existing pins\n await this.loadExistingPins()\n\n // Filter pins to current page and listen for navigation\n this.pinManager.filterByPage(getCurrentPageId())\n this.pageListener = new PageNavigationListener((pageId) => {\n this.pinManager.filterByPage(pageId)\n })\n this.pageListener.start()\n\n // Listen for clicks to drop pins\n this.clickHandler = (e: MouseEvent) => this.handleClick(e)\n document.addEventListener('click', this.clickHandler, true)\n }\n\n private showPrompt(): void {\n this.promptEl = document.createElement('div')\n this.promptEl.className = 'review-prompt'\n this.promptEl.innerHTML = `\n <h3 style=\"margin:0 0 8px;font-size:16px;\">Click anywhere to leave feedback</h3>\n <p style=\"margin:0;color:#666;font-size:14px;\">Drop numbered pins on elements you want to comment on</p>\n `\n this.shadowRoot.appendChild(this.promptEl)\n\n // Auto-dismiss after 5 seconds\n setTimeout(() => this.dismissPrompt(), 5000)\n }\n\n private dismissPrompt(): void {\n if (this.promptEl) {\n this.promptEl.remove()\n this.promptEl = null\n }\n }\n\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n this.updateToolbar()\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private updateToolbar(): void {\n if (!this.toolbarEl) return\n const pinCount = this.nextPinNumber - 1\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">${this.initData.session.name}</span>\n <span style=\"opacity:0.7;\">${pinCount} pin${pinCount !== 1 ? 's' : ''}</span>\n <button class=\"review-btn review-btn--submit\" style=\"margin-left:auto;padding:6px 12px;font-size:13px;\">General Comment</button>\n `\n this.toolbarEl.querySelector('button')?.addEventListener('click', (e) => {\n e.stopPropagation()\n this.handleGeneralComment()\n })\n }\n\n private async loadExistingPins(): Promise<void> {\n try {\n const comments = await fetchComments(this.token)\n for (const c of comments) {\n if (c.pinNumber != null && c.pinData) {\n // commentText being non-null means it's the current reviewer's pin\n const isMine = c.commentText != null\n this.pinManager.addPin(c.pinNumber, c.pinData.pinX, c.pinData.pinY, isMine, c.pageUrl ?? '')\n }\n }\n } catch {\n // Silently fail - pins just won't show up\n }\n }\n\n private handleClick(e: MouseEvent): void {\n // Don't intercept clicks on our own shadow DOM elements\n const path = e.composedPath()\n if (path.some((el) => el instanceof HTMLElement && el.closest?.('#ourroadmaps-review'))) return\n\n // Don't intercept if comment card is open\n if (this.pendingPinNumber != null) return\n\n // Dismiss first-visit prompt on first click\n this.dismissPrompt()\n\n const pinData = captureElementContext(e.clientX, e.clientY)\n const pinNumber = this.nextPinNumber\n\n // Add pin at click position\n this.pinManager.addPin(pinNumber, pinData.pinX, pinData.pinY, true, getCurrentPageId())\n this.pendingPinNumber = pinNumber\n\n // Show comment card\n this.commentCard.show({\n x: e.clientX,\n y: e.clientY,\n onSubmit: async (text: string) => {\n await submitComment(this.token, {\n commentText: text,\n pinNumber,\n pinData,\n pageUrl: getCurrentPageId(),\n })\n this.pendingPinNumber = null\n this.nextPinNumber++\n this.updateToolbar()\n this.showToast('Comment saved')\n },\n onCancel: () => {\n // Remove the pending pin\n this.pinManager.removePin(pinNumber)\n this.pendingPinNumber = null\n },\n })\n\n e.preventDefault()\n e.stopPropagation()\n }\n\n private handleGeneralComment(): void {\n if (this.pendingPinNumber != null) return\n\n this.commentCard.showForGeneral({\n onSubmit: async (text: string) => {\n await submitComment(this.token, {\n commentText: text,\n pinNumber: null,\n pinData: null,\n pageUrl: getCurrentPageId(),\n })\n this.showToast('Comment saved')\n },\n onCancel: () => {},\n })\n }\n\n private showToast(message: string): void {\n const toast = document.createElement('div')\n toast.className = 'review-toast'\n toast.textContent = message\n this.shadowRoot.appendChild(toast)\n setTimeout(() => toast.remove(), 2500)\n }\n\n destroy(): void {\n document.body.style.cursor = ''\n if (this.clickHandler) {\n document.removeEventListener('click', this.clickHandler, true)\n }\n this.pageListener?.destroy()\n this.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","const STORAGE_KEY = 'ourroadmaps:token'\n\ninterface StoredToken {\n type: 'review' | 'triage'\n token: string\n}\n\nexport function storeToken(type: 'review' | 'triage', token: string): void {\n try {\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ type, token }))\n } catch {\n // sessionStorage unavailable (private browsing, storage full, etc.)\n }\n}\n\nexport function getStoredToken(): StoredToken | null {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed.type === 'string' && typeof parsed.token === 'string') {\n return parsed as StoredToken\n }\n return null\n } catch {\n return null\n }\n}\n\nexport function clearStoredToken(): void {\n try {\n sessionStorage.removeItem(STORAGE_KEY)\n } catch {\n // sessionStorage unavailable\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\nimport { getCurrentPageId, PageNavigationListener } from './PageTracker'\nimport type { ReviewComment } from './types'\n\nexport class TriageMode {\n private pinManager: PinManager\n private toolbarEl: HTMLElement | null = null\n private tooltipEl: HTMLElement | null = null\n private comments: ReviewComment[] = []\n private pinClickHandler: ((e: MouseEvent) => void) | null = null\n private pageListener: PageNavigationListener | null = null\n private showAllPages = false\n\n constructor(\n private token: string,\n private shadowRoot: ShadowRoot,\n ) {\n this.pinManager = new PinManager()\n }\n\n async init(): Promise<void> {\n this.pinManager.mount()\n\n try {\n this.comments = await fetchComments(this.token)\n for (const c of this.comments) {\n if (c.pinNumber != null && c.pinData) {\n this.pinManager.addPin(c.pinNumber, c.pinData.pinX, c.pinData.pinY, true, c.pageUrl ?? '')\n }\n }\n } catch {\n // Show error state\n }\n\n this.pinManager.filterByPage(getCurrentPageId())\n\n this.renderToolbar()\n\n this.pageListener = new PageNavigationListener((pageId) => {\n if (!this.showAllPages) {\n this.pinManager.filterByPage(pageId)\n }\n this.updateToolbar()\n })\n this.pageListener.start()\n\n this.pinClickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n if (target.classList?.contains('review-pin')) {\n const num = Number(target.dataset.pinNumber)\n if (num) this.handlePinClick(num, e)\n }\n }\n document.addEventListener('click', this.pinClickHandler, true)\n\n this.autoFocusPin()\n }\n\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n this.updateToolbar()\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private updateToolbar(): void {\n if (!this.toolbarEl) return\n\n const totalCount = this.comments.filter((c) => c.pinNumber != null).length\n const currentPageId = getCurrentPageId()\n const pageCount = this.pinManager.countForPage(currentPageId)\n // Show pathname for path-routed apps, or pathname#hash for hash-routed apps\n const url = new URL(currentPageId, window.location.origin)\n const pagePath = url.hash ? `${url.pathname}${url.hash.split('?')[0]}` : (url.pathname || '/')\n\n this.toolbarEl.innerHTML = ''\n\n const label = document.createElement('span')\n label.style.fontWeight = '500'\n label.textContent = 'Triage Mode'\n this.toolbarEl.appendChild(label)\n\n const count = document.createElement('span')\n count.style.opacity = '0.7'\n if (this.showAllPages) {\n count.textContent = `${totalCount} pin${totalCount !== 1 ? 's' : ''}`\n } else {\n count.textContent = `${pageCount} pin${pageCount !== 1 ? 's' : ''} on ${pagePath} (${totalCount} total)`\n }\n this.toolbarEl.appendChild(count)\n\n const toggleBtn = document.createElement('button')\n toggleBtn.className = 'review-btn review-btn--submit'\n toggleBtn.style.cssText = 'margin-left:auto;padding:6px 12px;font-size:13px;'\n toggleBtn.textContent = this.showAllPages ? 'This page' : 'All pages'\n toggleBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.togglePageFilter()\n })\n this.toolbarEl.appendChild(toggleBtn)\n }\n\n private togglePageFilter(): void {\n this.showAllPages = !this.showAllPages\n if (this.showAllPages) {\n this.pinManager.showAll()\n } else {\n this.pinManager.filterByPage(getCurrentPageId())\n }\n this.updateToolbar()\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10)\n }\n\n private showTooltipForPin(pinNumber: number, x?: number, y?: number): void {\n this.hideTooltip()\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment) return\n\n this.tooltipEl = document.createElement('div')\n this.tooltipEl.className = 'review-tooltip'\n\n const time = new Date(comment.createdAt).toLocaleString()\n\n const header = document.createElement('div')\n header.style.cssText = 'font-weight:500;margin-bottom:4px;'\n header.textContent = `Pin #${pinNumber}`\n this.tooltipEl.appendChild(header)\n\n if (comment.reviewerName) {\n const name = document.createElement('div')\n name.style.cssText = 'font-size:11px;opacity:0.7;margin-bottom:2px;'\n name.textContent = comment.reviewerName\n this.tooltipEl.appendChild(name)\n }\n\n const text = document.createElement('div')\n text.style.cssText = 'margin-bottom:4px;'\n text.textContent = comment.commentText || '(no text)'\n this.tooltipEl.appendChild(text)\n\n const timeEl = document.createElement('div')\n timeEl.style.cssText = 'font-size:11px;opacity:0.7;'\n timeEl.textContent = time\n this.tooltipEl.appendChild(timeEl)\n\n const pinPageUrl = comment.pageUrl ?? ''\n if (pinPageUrl && pinPageUrl !== getCurrentPageId()) {\n const navEl = document.createElement('div')\n navEl.style.cssText = 'margin-top:6px;padding-top:6px;border-top:1px solid rgba(255,255,255,0.2);font-size:12px;'\n\n const pageLabel = document.createElement('span')\n pageLabel.style.opacity = '0.7'\n pageLabel.textContent = `Page: ${pinPageUrl.split('?')[0].split('#')[0] || '/'} \\u2014 `\n navEl.appendChild(pageLabel)\n\n const navLink = document.createElement('a')\n navLink.style.cssText = 'color:#a78bfa;cursor:pointer;text-decoration:underline;pointer-events:auto;'\n navLink.textContent = 'Navigate there?'\n navLink.addEventListener('click', (e) => {\n e.stopPropagation()\n this.navigateToPin(pinPageUrl, pinNumber)\n })\n navEl.appendChild(navLink)\n\n this.tooltipEl.appendChild(navEl)\n this.tooltipEl.style.pointerEvents = 'auto'\n }\n\n const posX = x ?? window.innerWidth / 2\n const posY = y ?? window.innerHeight / 3\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n const dismiss = (ev: MouseEvent) => {\n if (ev.target !== this.tooltipEl && !this.tooltipEl?.contains(ev.target as Node)) {\n this.hideTooltip()\n this.pinManager.clearHighlight()\n document.removeEventListener('click', dismiss, true)\n }\n }\n setTimeout(() => document.addEventListener('click', dismiss, true), 0)\n }\n\n private navigateToPin(pageUrl: string, pinNumber: number): void {\n this.hideTooltip()\n this.pinManager.clearHighlight()\n\n const url = new URL(pageUrl, window.location.origin)\n url.searchParams.set('pin', String(pinNumber))\n window.location.href = url.toString()\n }\n\n private autoFocusPin(): void {\n const params = new URLSearchParams(window.location.search)\n const pinParam = params.get('pin')\n if (!pinParam) return\n\n const pinNumber = Number(pinParam)\n if (!pinNumber) return\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment?.pinData) return\n\n const pinPageUrl = comment.pageUrl ?? ''\n if (pinPageUrl) {\n // Strip ?pin=N from current URL before comparing — navigateToPin adds it,\n // so after redirect getCurrentPageId() includes it but comment.pageUrl doesn't\n const currentUrl = new URL(window.location.href)\n currentUrl.searchParams.delete('pin')\n const currentPageWithoutPin = currentUrl.pathname + currentUrl.search + currentUrl.hash\n if (pinPageUrl !== currentPageWithoutPin) {\n this.navigateToPin(pinPageUrl, pinNumber)\n return\n }\n }\n\n this.pinManager.showAll()\n this.showAllPages = true\n this.updateToolbar()\n\n const docEl = document.documentElement\n const scrollX = (comment.pinData.pinX / 100) * docEl.scrollWidth\n const scrollY = (comment.pinData.pinY / 100) * docEl.scrollHeight\n\n window.scrollTo({\n left: scrollX - window.innerWidth / 2,\n top: scrollY - window.innerHeight / 2,\n behavior: 'smooth',\n })\n\n setTimeout(() => {\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber)\n }, 500)\n }\n\n private hideTooltip(): void {\n this.tooltipEl?.remove()\n this.tooltipEl = null\n }\n\n destroy(): void {\n if (this.pinClickHandler) {\n document.removeEventListener('click', this.pinClickHandler, true)\n }\n this.pageListener?.destroy()\n this.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\nimport { storeToken, getStoredToken, clearStoredToken } from './TokenStorage'\nimport { TriageMode } from './TriageMode'\nimport { REVIEW_STYLES } from './styles'\nimport type { ReviewOptions } from './types'\n\nexport class Review {\n private root: HTMLElement\n private shadow: ShadowRoot\n private mode: ReviewMode | TriageMode | null = null\n private _isDestroyed = false\n\n constructor(_options: ReviewOptions = {}) {\n this.root = document.createElement('div')\n this.root.id = 'ourroadmaps-review'\n this.shadow = this.root.attachShadow({ mode: 'open' })\n\n const styleEl = document.createElement('style')\n styleEl.textContent = REVIEW_STYLES\n this.shadow.appendChild(styleEl)\n\n document.body.appendChild(this.root)\n }\n\n async init(tokenType?: 'review' | 'triage', token?: string): Promise<void> {\n // Backward compat: if no args, check URL params then sessionStorage\n if (!tokenType || !token) {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n if (reviewToken) {\n tokenType = 'review'\n token = reviewToken\n storeToken('review', reviewToken)\n const clean = new URL(window.location.href)\n clean.searchParams.delete('review')\n history.replaceState(null, '', clean.toString())\n } else if (triageToken) {\n tokenType = 'triage'\n token = triageToken\n storeToken('triage', triageToken)\n const clean = new URL(window.location.href)\n clean.searchParams.delete('triage')\n history.replaceState(null, '', clean.toString())\n } else {\n const stored = getStoredToken()\n if (stored) {\n tokenType = stored.type\n token = stored.token\n } else {\n return\n }\n }\n }\n\n if (tokenType === 'review') {\n try {\n const data = await validateToken(token)\n this.mode = new ReviewMode(token, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\n clearStoredToken()\n this.showErrorOverlay(\n err.code === 'expired'\n ? 'This feedback session has expired'\n : 'This review link is no longer valid',\n )\n }\n }\n } else if (tokenType === 'triage') {\n try {\n await validateToken(token)\n this.mode = new TriageMode(token, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\n clearStoredToken()\n this.showErrorOverlay(\n err.code === 'expired'\n ? 'This feedback session has expired'\n : 'This review link is no longer valid',\n )\n }\n }\n }\n }\n\n private showErrorOverlay(message: string): void {\n const overlay = document.createElement('div')\n overlay.className = 'review-expired-overlay'\n overlay.innerHTML = `\n <div class=\"review-expired-card\">\n <h2 style=\"margin:0 0 8px;font-size:18px;\">Session Unavailable</h2>\n <p style=\"margin:0;color:#666;font-size:14px;\">${message}</p>\n <p style=\"margin:12px 0 0;color:#999;font-size:13px;\">Contact the prototype owner for a new link.</p>\n </div>\n `\n this.shadow.appendChild(overlay)\n\n // Apply grayscale to body\n document.body.style.filter = 'grayscale(0.8)'\n }\n\n destroy(): void {\n if (this._isDestroyed) return\n this._isDestroyed = true\n this.mode?.destroy()\n this.root.remove()\n document.body.style.filter = ''\n }\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["review_exports","__export","Review","import_meta","API_URL","validateToken","token","res","ReviewError","submitComment","token","comment","res","API_URL","ReviewError","fetchComments","code","message","captureElementContext","clientX","clientY","el","docEl","pinX","pinY","rect","buildSelector","getDirectText","classStr","getSiblingsSummary","getNearbyText","parts","current","part","classes","text","node","cls","siblings","child","tag","depth","REVIEW_STYLES","PIN_DOCUMENT_STYLES","STYLE_ID","PinManager","__publicField","PIN_DOCUMENT_STYLES","pinNumber","x","y","isMine","pageUrl","existing","pin","entry","num","el","currentPageId","pageId","count","CommentCard","shadowRoot","__publicField","options","textarea","text","submitBtn","errorEl","err","getCurrentPageId","PageNavigationListener","onChange","__publicField","check","current","args","ReviewMode","token","shadowRoot","initData","__publicField","PinManager","CommentCard","getCurrentPageId","PageNavigationListener","pageId","pinCount","e","comments","fetchComments","c","isMine","el","pinData","captureElementContext","pinNumber","text","submitComment","message","toast","STORAGE_KEY","storeToken","type","token","getStoredToken","raw","parsed","clearStoredToken","TriageMode","token","shadowRoot","__publicField","PinManager","fetchComments","c","getCurrentPageId","PageNavigationListener","pageId","target","num","totalCount","currentPageId","pageCount","url","pagePath","label","count","toggleBtn","e","pinNumber","x","y","comment","time","header","name","text","timeEl","pinPageUrl","navEl","pageLabel","navLink","posX","posY","dismiss","ev","pageUrl","pinParam","currentUrl","currentPageWithoutPin","docEl","scrollX","scrollY","Review","_options","__publicField","styleEl","REVIEW_STYLES","tokenType","token","params","reviewToken","triageToken","storeToken","clean","stored","getStoredToken","data","validateToken","ReviewMode","err","ReviewError","clearStoredToken","TriageMode","message","overlay"]}
1
+ {"version":3,"sources":["../src/review/index.ts","../src/review/api.ts","../src/review/ElementCapture.ts","../src/review/styles.ts","../src/review/PinManager.ts","../src/review/CommentCard.ts","../src/review/PageTracker.ts","../src/review/CommentListPanel.ts","../src/review/ReviewMode.ts","../src/review/TokenStorage.ts","../src/review/TriageMode.ts","../src/review/Review.ts"],"sourcesContent":["export { Review } from './Review'\nexport type { PinData, ReviewOptions } from './types'\n","import type { PinData, ReviewComment, ReviewInitData } from './types'\n\n// Default to production, override with env var for local dev\nconst API_URL = (() => {\n // Vite dev mode\n if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_URL) {\n return import.meta.env.VITE_API_URL\n }\n return 'https://api.ourroadmaps.com'\n})()\n\nexport async function validateToken(token: string): Promise<ReviewInitData> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}`)\n if (res.status === 410) throw new ReviewError('expired', 'This feedback session has expired')\n if (res.status === 404) throw new ReviewError('invalid', 'This review link is not valid')\n if (!res.ok) throw new ReviewError('error', 'Something went wrong')\n const body = await res.json()\n return body.data\n}\n\nexport async function identify(token: string, name?: string, email?: string): Promise<void> {\n const body: Record<string, string> = {}\n if (name) body.name = name\n if (email) body.email = email\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/identify`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!res.ok) throw new ReviewError('error', 'Failed to identify')\n}\n\nexport async function submitComment(\n token: string,\n comment: { commentText: string; pinNumber: number | null; pinData: PinData | null; pageUrl: string | null },\n): Promise<{ id: string; pinNumber: number | null; createdAt: string }> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/comments`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(comment),\n })\n if (!res.ok) throw new ReviewError('error', 'Failed to submit comment')\n const body = await res.json()\n return body.data\n}\n\nexport async function fetchComments(token: string): Promise<ReviewComment[]> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/comments`)\n if (!res.ok) throw new ReviewError('error', 'Failed to load comments')\n const body = await res.json()\n return body.data\n}\n\nexport class ReviewError extends Error {\n constructor(\n public code: 'expired' | 'invalid' | 'error',\n message: string,\n ) {\n super(message)\n this.name = 'ReviewError'\n }\n}\n","import type { PinData } from './types'\n\nexport function captureElementContext(clientX: number, clientY: number): PinData {\n const el = document.elementFromPoint(clientX, clientY) as HTMLElement | null\n const docEl = document.documentElement\n\n const pinX = ((clientX + window.scrollX) / docEl.scrollWidth) * 100\n const pinY = ((clientY + window.scrollY) / docEl.scrollHeight) * 100\n\n if (!el || el === document.body || el === docEl) {\n return {\n pinX,\n pinY,\n element: {\n selector: 'body',\n tag: 'body',\n text: '',\n ariaLabel: null,\n className: '',\n boundingBox: { x: 0, y: 0, w: docEl.scrollWidth, h: docEl.scrollHeight },\n },\n context: {\n parentTag: '',\n parentText: '',\n grandparentTag: '',\n siblings: [],\n nearbyText: '',\n },\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }\n }\n\n const rect = el.getBoundingClientRect()\n\n return {\n pinX,\n pinY,\n element: {\n selector: buildSelector(el),\n tag: el.tagName.toLowerCase(),\n text: getDirectText(el).slice(0, 200),\n ariaLabel: el.getAttribute('aria-label'),\n className: el.className && typeof el.className === 'string' ? el.className : '',\n boundingBox: {\n x: Math.round(rect.x),\n y: Math.round(rect.y),\n w: Math.round(rect.width),\n h: Math.round(rect.height),\n },\n },\n context: {\n parentTag: el.parentElement ? `${el.parentElement.tagName.toLowerCase()}${classStr(el.parentElement)}` : '',\n parentText: el.parentElement ? getDirectText(el.parentElement).slice(0, 100) : '',\n grandparentTag: el.parentElement?.parentElement\n ? `${el.parentElement.parentElement.tagName.toLowerCase()}${classStr(el.parentElement.parentElement)}`\n : '',\n siblings: getSiblingsSummary(el),\n nearbyText: getNearbyText(el).slice(0, 200),\n },\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }\n}\n\nfunction buildSelector(el: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = el\n\n while (current && current !== document.body) {\n if (current.id) {\n parts.unshift(`#${current.id}`)\n break\n }\n\n let part = current.tagName.toLowerCase()\n if (current.className && typeof current.className === 'string') {\n const classes = current.className.trim().split(/\\s+/).slice(0, 2).join('.')\n if (classes) part += `.${classes}`\n }\n\n parts.unshift(part)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\nfunction getDirectText(el: HTMLElement): string {\n let text = ''\n for (const node of el.childNodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent?.trim() || ''\n }\n }\n return text.trim() || el.textContent?.trim().slice(0, 200) || ''\n}\n\nfunction classStr(el: HTMLElement): string {\n if (!el.className || typeof el.className !== 'string') return ''\n const cls = el.className.trim().split(/\\s+/).slice(0, 2).join('.')\n return cls ? `.${cls}` : ''\n}\n\nfunction getSiblingsSummary(el: HTMLElement): string[] {\n if (!el.parentElement) return []\n const siblings: string[] = []\n for (const child of el.parentElement.children) {\n if (child === el) continue\n const tag = child.tagName.toLowerCase()\n const text = (child.textContent?.trim() || '').slice(0, 50)\n siblings.push(text ? `${tag}:${text}` : tag)\n if (siblings.length >= 4) break\n }\n return siblings\n}\n\nfunction getNearbyText(el: HTMLElement): string {\n // Walk up to find the nearest container with meaningful text\n let current: HTMLElement | null = el.parentElement\n let depth = 0\n while (current && depth < 3) {\n const text = getDirectText(current)\n if (text && text !== getDirectText(el)) return text\n current = current.parentElement\n depth++\n }\n return ''\n}\n","export const REVIEW_STYLES = `\n:host {\n all: initial;\n}\n\n/* ─── Pin Container (rendered outside shadow DOM) ─── */\n.pin-container {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 10000;\n}\n\n/* ─── Pin Marker ─── */\n.review-pin {\n position: absolute;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #7c3aed;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n z-index: 1;\n}\n\n.review-pin:hover {\n transform: translate(-50%, -50%) scale(1.15);\n box-shadow: 0 3px 10px rgba(124, 58, 237, 0.4);\n}\n\n.review-pin--other {\n opacity: 0.5;\n}\n\n.review-pin--highlighted {\n transform: translate(-50%, -50%) scale(1.2);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3), 0 3px 10px rgba(124, 58, 237, 0.4);\n z-index: 2;\n}\n\n/* ─── Toolbar ─── */\n.review-toolbar {\n position: fixed;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n border-radius: 12px 12px 0 0;\n padding: 10px 20px;\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n z-index: 10001;\n backdrop-filter: blur(8px);\n box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.2);\n}\n\n/* ─── Mode Toggle ─── */\n.review-mode-toggle {\n display: flex;\n border-radius: 6px;\n overflow: hidden;\n background: rgba(255, 255, 255, 0.1);\n}\n\n.review-mode-btn {\n padding: 5px 10px;\n font-family: inherit;\n font-size: 12px;\n font-weight: 500;\n border: none;\n cursor: pointer;\n color: rgba(255, 255, 255, 0.6);\n background: transparent;\n transition: background 0.15s ease, color 0.15s ease;\n display: flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.review-mode-btn:hover {\n color: rgba(255, 255, 255, 0.85);\n background: rgba(255, 255, 255, 0.08);\n}\n\n.review-mode-btn--active {\n background: #7c3aed;\n color: #fff;\n}\n\n.review-mode-btn--active:hover {\n background: #6d28d9;\n color: #fff;\n}\n\n/* ─── Comments Panel ─── */\n.review-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 320px;\n background: #111;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n z-index: 10001;\n display: flex;\n flex-direction: column;\n border-left: 1px solid rgba(255, 255, 255, 0.1);\n transform: translateX(100%);\n transition: transform 0.2s ease;\n}\n\n.review-panel--open {\n transform: translateX(0);\n}\n\n.review-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n flex-shrink: 0;\n}\n\n.review-panel-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.9);\n}\n\n.review-panel-close {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.5);\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.15s ease, background 0.15s ease;\n}\n\n.review-panel-close:hover {\n color: rgba(255, 255, 255, 0.9);\n background: rgba(255, 255, 255, 0.1);\n}\n\n.review-panel-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px 16px;\n}\n\n.review-panel-section {\n margin-bottom: 16px;\n}\n\n.review-panel-section-header {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: rgba(255, 255, 255, 0.4);\n margin-bottom: 8px;\n padding-bottom: 6px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.06);\n}\n\n.review-panel-section-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: rgba(255, 255, 255, 0.4);\n margin-bottom: 8px;\n padding-bottom: 6px;\n border: none;\n border-bottom: 1px solid rgba(255, 255, 255, 0.06);\n background: none;\n cursor: pointer;\n width: 100%;\n text-align: left;\n transition: color 0.15s ease;\n}\n\n.review-panel-section-toggle:hover {\n color: rgba(255, 255, 255, 0.6);\n}\n\n.review-panel-section-toggle svg {\n transition: transform 0.15s ease;\n}\n\n.review-panel-section-toggle--expanded svg {\n transform: rotate(90deg);\n}\n\n.review-panel-page-label {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.3);\n margin: 10px 0 6px;\n font-family: monospace;\n}\n\n.review-panel-page-label:first-child {\n margin-top: 0;\n}\n\n/* ─── Comment Item ─── */\n.review-panel-comment {\n background: rgba(255, 255, 255, 0.05);\n border-radius: 6px;\n padding: 10px 12px;\n margin-bottom: 6px;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.review-panel-comment:hover {\n background: rgba(255, 255, 255, 0.08);\n}\n\n.review-panel-comment-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 4px;\n}\n\n.review-panel-pin-badge {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #7c3aed;\n color: #fff;\n font-size: 10px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.review-panel-general-badge {\n width: 18px;\n height: 18px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n color: rgba(255, 255, 255, 0.5);\n}\n\n.review-panel-comment-author {\n font-size: 12px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.8);\n}\n\n.review-panel-comment-text {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.6);\n line-height: 1.4;\n margin-bottom: 4px;\n}\n\n.review-panel-comment-time {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.3);\n}\n\n.review-panel-empty {\n text-align: center;\n padding: 24px 16px;\n color: rgba(255, 255, 255, 0.3);\n font-size: 13px;\n}\n\n.review-panel-loading {\n text-align: center;\n padding: 24px 16px;\n color: rgba(255, 255, 255, 0.4);\n font-size: 13px;\n}\n\n/* ─── Comment Card ─── */\n.review-comment-card {\n position: fixed;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15), 0 1px 4px rgba(0, 0, 0, 0.08);\n width: 300px;\n padding: 16px;\n z-index: 10002;\n font-family: system-ui, -apple-system, sans-serif;\n border: 1px solid rgba(0, 0, 0, 0.08);\n}\n\n/* ─── Comment Textarea ─── */\n.review-comment-input {\n width: 100%;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n padding: 10px 12px;\n resize: vertical;\n font-family: inherit;\n font-size: 14px;\n line-height: 1.5;\n color: #1f2937;\n background: #fafafa;\n box-sizing: border-box;\n outline: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.review-comment-input:focus {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15);\n background: #fff;\n}\n\n.review-comment-input::placeholder {\n color: #9ca3af;\n}\n\n.review-comment-input:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* ─── Comment Actions ─── */\n.review-comment-actions {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n gap: 8px;\n margin-top: 12px;\n}\n\n/* ─── Buttons ─── */\n.review-btn {\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n border: none;\n transition: background 0.15s ease, opacity 0.15s ease;\n}\n\n.review-btn--cancel {\n color: #6b7280;\n background: transparent;\n}\n\n.review-btn--cancel:hover {\n background: #f3f4f6;\n}\n\n.review-btn--submit {\n background: #7c3aed;\n color: #fff;\n}\n\n.review-btn--submit:hover {\n background: #6d28d9;\n}\n\n.review-btn--submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* ─── Comment Error ─── */\n.review-comment-error {\n color: #dc2626;\n font-size: 12px;\n margin-top: 8px;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n/* ─── Name Prompt ─── */\n.review-prompt {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: #fff;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2), 0 1px 4px rgba(0, 0, 0, 0.08);\n border-radius: 12px;\n padding: 28px 32px;\n z-index: 10002;\n text-align: center;\n font-family: system-ui, -apple-system, sans-serif;\n border: 1px solid rgba(0, 0, 0, 0.06);\n max-width: 360px;\n width: 90%;\n}\n\n/* ─── Expired Overlay ─── */\n.review-expired-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10003;\n backdrop-filter: blur(2px);\n}\n\n.review-expired-card {\n background: #fff;\n border-radius: 12px;\n padding: 28px 32px;\n text-align: center;\n font-family: system-ui, -apple-system, sans-serif;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);\n max-width: 400px;\n width: 90%;\n border: 1px solid rgba(0, 0, 0, 0.06);\n}\n\n/* ─── Toast ─── */\n.review-toast {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n padding: 10px 20px;\n border-radius: 8px;\n z-index: 10003;\n pointer-events: none;\n animation: review-toast-fade 2.5s ease forwards;\n backdrop-filter: blur(8px);\n}\n\n@keyframes review-toast-fade {\n 0% { opacity: 0; transform: translateX(-50%) translateY(8px); }\n 10% { opacity: 1; transform: translateX(-50%) translateY(0); }\n 80% { opacity: 1; transform: translateX(-50%) translateY(0); }\n 100% { opacity: 0; transform: translateX(-50%) translateY(-4px); }\n}\n\n/* ─── Tooltip ─── */\n.review-tooltip {\n position: absolute;\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 13px;\n line-height: 1.4;\n border-radius: 6px;\n padding: 8px 12px;\n max-width: 280px;\n z-index: 10002;\n pointer-events: none;\n backdrop-filter: blur(8px);\n}\n\n/* ─── Name Entry Overlay ─── */\n.review-name-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10003;\n backdrop-filter: blur(2px);\n}\n\n.review-name-card {\n background: #fff;\n border-radius: 12px;\n padding: 28px 32px;\n font-family: system-ui, -apple-system, sans-serif;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);\n max-width: 360px;\n width: 90%;\n border: 1px solid rgba(0, 0, 0, 0.06);\n}\n\n.review-name-card h3 {\n margin: 0 0 4px;\n font-size: 17px;\n font-weight: 600;\n color: #111;\n}\n\n.review-name-card p {\n margin: 0 0 20px;\n font-size: 14px;\n color: #666;\n}\n\n.review-name-card label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: #374151;\n margin-bottom: 6px;\n}\n\n.review-name-card input {\n width: 100%;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n padding: 10px 12px;\n font-family: inherit;\n font-size: 14px;\n color: #1f2937;\n background: #fafafa;\n box-sizing: border-box;\n outline: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.review-name-card input:focus {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15);\n background: #fff;\n}\n\n.review-name-card input::placeholder {\n color: #9ca3af;\n}\n\n.review-name-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-top: 20px;\n}\n\n/* ─── Reduced Motion ─── */\n@media (prefers-reduced-motion: reduce) {\n *, *::before, *::after {\n animation-duration: 0.01ms !important;\n transition-duration: 0.01ms !important;\n }\n}\n`\n\n/**\n * Subset of styles for pins that render outside the shadow DOM.\n * Injected into document.head by PinManager.\n */\nexport const PIN_DOCUMENT_STYLES = `\nbody {\n position: relative !important;\n}\n\n.pin-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n min-height: 100%;\n pointer-events: none;\n z-index: 10000;\n}\n\n.review-pin {\n position: absolute;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #7c3aed;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n z-index: 1;\n}\n\n.review-pin:hover {\n transform: translate(-50%, -50%) scale(1.15);\n box-shadow: 0 3px 10px rgba(124, 58, 237, 0.4);\n}\n\n.review-pin--other {\n opacity: 0.5;\n}\n\n.review-pin--highlighted {\n transform: translate(-50%, -50%) scale(1.2);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3), 0 3px 10px rgba(124, 58, 237, 0.4);\n z-index: 2;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .review-pin, .review-pin:hover, .review-pin--highlighted {\n transition-duration: 0.01ms !important;\n }\n}\n`\n","import { PIN_DOCUMENT_STYLES } from './styles'\n\nconst STYLE_ID = 'roadmaps-review-pin-styles'\n\ninterface PinEntry {\n el: HTMLElement\n pageUrl: string\n}\n\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, PinEntry> = new Map()\n private styleEl: HTMLStyleElement | null = null\n\n constructor() {\n this.container = document.createElement('div')\n this.container.className = 'pin-container'\n }\n\n mount(): void {\n if (!document.getElementById(STYLE_ID)) {\n this.styleEl = document.createElement('style')\n this.styleEl.id = STYLE_ID\n this.styleEl.textContent = PIN_DOCUMENT_STYLES\n document.head.appendChild(this.styleEl)\n }\n\n document.body.appendChild(this.container)\n }\n\n addPin(pinNumber: number, x: number, y: number, isMine: boolean, pageUrl: string): void {\n // Remove existing pin with same number to prevent orphaned DOM elements\n const existing = this.pins.get(pinNumber)\n if (existing) {\n existing.el.remove()\n this.pins.delete(pinNumber)\n }\n\n const pin = document.createElement('div')\n pin.className = isMine ? 'review-pin' : 'review-pin review-pin--other'\n pin.textContent = String(pinNumber)\n pin.style.left = `${x}%`\n pin.style.top = `${y}%`\n pin.dataset.pinNumber = String(pinNumber)\n pin.dataset.pageUrl = pageUrl\n this.container.appendChild(pin)\n this.pins.set(pinNumber, { el: pin, pageUrl })\n }\n\n removePin(pinNumber: number): void {\n const entry = this.pins.get(pinNumber)\n if (entry) {\n entry.el.remove()\n this.pins.delete(pinNumber)\n }\n }\n\n highlightPin(pinNumber: number): void {\n for (const [num, { el }] of this.pins) {\n el.classList.toggle('review-pin--highlighted', num === pinNumber)\n }\n }\n\n clearHighlight(): void {\n for (const { el } of this.pins.values()) {\n el.classList.remove('review-pin--highlighted')\n }\n }\n\n filterByPage(currentPageId: string): void {\n for (const { el, pageUrl } of this.pins.values()) {\n el.style.display = pageUrl === currentPageId ? '' : 'none'\n }\n }\n\n showAll(): void {\n for (const { el } of this.pins.values()) {\n el.style.display = ''\n }\n }\n\n countForPage(pageId: string): number {\n let count = 0\n for (const { pageUrl } of this.pins.values()) {\n if (pageUrl === pageId) count++\n }\n return count\n }\n\n destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n if (this.styleEl) {\n this.styleEl.remove()\n this.styleEl = null\n } else {\n document.getElementById(STYLE_ID)?.remove()\n }\n }\n}\n","export class CommentCard {\n private card: HTMLElement | null = null\n private onSubmit: ((text: string) => Promise<void>) | null = null\n private onCancel: (() => void) | null = null\n\n constructor(private shadowRoot: ShadowRoot) {}\n\n show(options: {\n x: number\n y: number\n onSubmit: (text: string) => Promise<void>\n onCancel: () => void\n }): void {\n this.hide()\n this.onSubmit = options.onSubmit\n this.onCancel = options.onCancel\n\n this.card = document.createElement('div')\n this.card.className = 'review-comment-card'\n // Position near click point, adjust to stay in viewport\n this.card.style.left = `${Math.min(options.x, window.innerWidth - 320)}px`\n this.card.style.top = `${Math.min(options.y + 20, window.innerHeight - 200)}px`\n\n this.card.innerHTML = `\n <textarea class=\"review-comment-input\" placeholder=\"Leave your feedback...\" rows=\"3\"></textarea>\n <div class=\"review-comment-actions\">\n <button class=\"review-btn review-btn--cancel\">Cancel</button>\n <button class=\"review-btn review-btn--submit\">Submit</button>\n </div>\n <div class=\"review-comment-error\" style=\"display:none\"></div>\n `\n\n this.attachListeners()\n this.shadowRoot.appendChild(this.card)\n\n const textarea = this.card.querySelector('textarea')\n textarea?.focus()\n }\n\n hide(): void {\n this.card?.remove()\n this.card = null\n }\n\n showForGeneral(options: {\n onSubmit: (text: string) => Promise<void>\n onCancel: () => void\n }): void {\n // Show centered for general (non-pinned) comments\n this.show({\n x: window.innerWidth / 2 - 150,\n y: window.innerHeight / 2 - 100,\n onSubmit: options.onSubmit,\n onCancel: options.onCancel,\n })\n }\n\n private attachListeners(): void {\n if (!this.card) return\n\n this.card.querySelector('.review-btn--cancel')?.addEventListener('click', () => {\n this.onCancel?.()\n this.hide()\n })\n\n this.card.querySelector('.review-btn--submit')?.addEventListener('click', () => this.handleSubmit())\n\n this.card.querySelector('textarea')?.addEventListener('keydown', (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n this.handleSubmit()\n }\n })\n }\n\n private async handleSubmit(): Promise<void> {\n if (!this.card) return\n const textarea = this.card.querySelector('textarea') as HTMLTextAreaElement\n const text = textarea.value.trim()\n if (!text) return\n\n const submitBtn = this.card.querySelector('.review-btn--submit') as HTMLButtonElement\n const errorEl = this.card.querySelector('.review-comment-error') as HTMLElement\n submitBtn.disabled = true\n submitBtn.textContent = 'Submitting...'\n textarea.disabled = true\n errorEl.style.display = 'none'\n\n try {\n await this.onSubmit?.(text)\n this.hide()\n } catch (err) {\n submitBtn.disabled = false\n submitBtn.textContent = 'Submit'\n textarea.disabled = false\n errorEl.textContent = err instanceof Error ? err.message : 'Failed to submit'\n errorEl.style.display = 'block'\n }\n }\n}\n","/**\n * Returns the current page identity as pathname + search + hash.\n * This captures all routing strategies: pathname, hash, and query-param based.\n */\nexport function getCurrentPageId(): string {\n return window.location.pathname + window.location.search + window.location.hash\n}\n\n/**\n * Detects page navigation within a prototype.\n * Covers: popstate (back/forward), hashchange (hash routing),\n * and pushState/replaceState (SPA routing via history API).\n */\nexport class PageNavigationListener {\n private popstateHandler: (() => void) | null = null\n private hashchangeHandler: (() => void) | null = null\n private originalPushState: typeof history.pushState | null = null\n private originalReplaceState: typeof history.replaceState | null = null\n private lastPageId: string\n\n constructor(private onChange: (pageId: string) => void) {\n this.lastPageId = getCurrentPageId()\n }\n\n start(): void {\n // Guard against double-start — would capture already-patched history methods\n if (this.originalPushState) return\n\n const check = () => {\n const current = getCurrentPageId()\n if (current !== this.lastPageId) {\n this.lastPageId = current\n this.onChange(current)\n }\n }\n\n this.popstateHandler = check\n window.addEventListener('popstate', this.popstateHandler)\n\n this.hashchangeHandler = check\n window.addEventListener('hashchange', this.hashchangeHandler)\n\n this.originalPushState = history.pushState.bind(history)\n this.originalReplaceState = history.replaceState.bind(history)\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args)\n check()\n }\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args)\n check()\n }\n }\n\n destroy(): void {\n if (this.popstateHandler) {\n window.removeEventListener('popstate', this.popstateHandler)\n }\n if (this.hashchangeHandler) {\n window.removeEventListener('hashchange', this.hashchangeHandler)\n }\n\n if (this.originalPushState) {\n history.pushState = this.originalPushState\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState\n }\n }\n}\n","import { fetchComments } from './api'\nimport type { ReviewComment } from './types'\nimport { getCurrentPageId } from './PageTracker'\nimport type { PinManager } from './PinManager'\n\ninterface GroupedComments {\n pageUrl: string\n comments: ReviewComment[]\n}\n\nexport class CommentListPanel {\n private panelEl: HTMLElement\n private bodyEl: HTMLElement | null = null\n private isOpen = false\n private allPagesExpanded = false\n private comments: ReviewComment[] = []\n\n constructor(\n private token: string,\n private shadowRoot: ShadowRoot,\n private pinManager: PinManager,\n ) {\n this.panelEl = document.createElement('div')\n this.panelEl.className = 'review-panel'\n this.panelEl.innerHTML = `\n <div class=\"review-panel-header\">\n <h3>Comments</h3>\n <button class=\"review-panel-close\" title=\"Close\">\n <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>\n </button>\n </div>\n <div class=\"review-panel-body\"></div>\n `\n this.bodyEl = this.panelEl.querySelector('.review-panel-body')\n this.panelEl.querySelector('.review-panel-close')?.addEventListener('click', () => this.close())\n this.shadowRoot.appendChild(this.panelEl)\n }\n\n async toggle(): Promise<void> {\n if (this.isOpen) {\n this.close()\n } else {\n await this.open()\n }\n }\n\n async open(): Promise<void> {\n this.isOpen = true\n this.panelEl.classList.add('review-panel--open')\n await this.refresh()\n }\n\n close(): void {\n this.isOpen = false\n this.panelEl.classList.remove('review-panel--open')\n this.pinManager.clearHighlight()\n }\n\n async refresh(): Promise<void> {\n if (!this.isOpen || !this.bodyEl) return\n\n this.bodyEl.innerHTML = '<div class=\"review-panel-loading\">Loading comments...</div>'\n\n try {\n this.comments = await fetchComments(this.token)\n this.render()\n } catch {\n this.bodyEl.innerHTML = '<div class=\"review-panel-empty\">Failed to load comments</div>'\n }\n }\n\n private render(): void {\n if (!this.bodyEl) return\n\n const withText = this.comments.filter((c) => c.commentText)\n if (withText.length === 0) {\n this.bodyEl.innerHTML = '<div class=\"review-panel-empty\">No comments yet</div>'\n return\n }\n\n const currentPage = getCurrentPageId()\n const currentPageComments = withText.filter((c) => c.pageUrl === currentPage)\n const otherComments = withText.filter((c) => c.pageUrl !== currentPage)\n\n let html = ''\n\n // Current page section\n html += '<div class=\"review-panel-section\">'\n html += `<div class=\"review-panel-section-header\">This Page</div>`\n if (currentPageComments.length === 0) {\n html += '<div class=\"review-panel-empty\" style=\"padding:8px 0;\">No comments on this page</div>'\n } else {\n html += this.renderComments(currentPageComments)\n }\n html += '</div>'\n\n // Other pages section (collapsible)\n if (otherComments.length > 0) {\n const groups = this.groupByPage(otherComments)\n html += '<div class=\"review-panel-section\">'\n html += `<button class=\"review-panel-section-toggle ${this.allPagesExpanded ? 'review-panel-section-toggle--expanded' : ''}\" data-action=\"toggle-all\">\n <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>\n All Pages (${otherComments.length} comment${otherComments.length !== 1 ? 's' : ''})\n </button>`\n if (this.allPagesExpanded) {\n html += '<div class=\"review-panel-all-pages\">'\n for (const group of groups) {\n html += `<div class=\"review-panel-page-label\">${this.escapeHtml(this.formatPageUrl(group.pageUrl))}</div>`\n html += this.renderComments(group.comments)\n }\n html += '</div>'\n }\n html += '</div>'\n }\n\n this.bodyEl.innerHTML = html\n this.attachEventListeners()\n }\n\n private renderComments(comments: ReviewComment[]): string {\n return comments\n .map((c) => {\n const badge =\n c.pinNumber != null\n ? `<span class=\"review-panel-pin-badge\">${c.pinNumber}</span>`\n : `<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>`\n const author = c.reviewerName || 'Anonymous'\n const text = c.commentText || ''\n const time = this.formatTime(c.createdAt)\n const pinAttr = c.pinNumber != null ? `data-pin=\"${c.pinNumber}\"` : ''\n const pageAttr = c.pageUrl ? `data-page=\"${this.escapeAttr(c.pageUrl)}\"` : ''\n\n return `<div class=\"review-panel-comment\" ${pinAttr} ${pageAttr}>\n <div class=\"review-panel-comment-header\">\n ${badge}\n <span class=\"review-panel-comment-author\">${this.escapeHtml(author)}</span>\n </div>\n <div class=\"review-panel-comment-text\">${this.escapeHtml(text)}</div>\n <div class=\"review-panel-comment-time\">${time}</div>\n </div>`\n })\n .join('')\n }\n\n private attachEventListeners(): void {\n if (!this.bodyEl) return\n\n // Toggle \"All Pages\" section\n this.bodyEl.querySelector('[data-action=\"toggle-all\"]')?.addEventListener('click', () => {\n this.allPagesExpanded = !this.allPagesExpanded\n this.render()\n })\n\n // Click on comment to highlight pin\n this.bodyEl.querySelectorAll('.review-panel-comment[data-pin]').forEach((el) => {\n el.addEventListener('click', () => {\n const pinNumber = Number((el as HTMLElement).dataset.pin)\n if (pinNumber) {\n this.pinManager.highlightPin(pinNumber)\n }\n })\n })\n }\n\n private groupByPage(comments: ReviewComment[]): GroupedComments[] {\n const groups = new Map<string, ReviewComment[]>()\n for (const c of comments) {\n const key = c.pageUrl || '(unknown)'\n const arr = groups.get(key) || []\n arr.push(c)\n groups.set(key, arr)\n }\n return Array.from(groups.entries()).map(([pageUrl, comments]) => ({ pageUrl, comments }))\n }\n\n private formatPageUrl(url: string): string {\n try {\n // Show just the pathname, remove the origin if it's a full URL\n const u = new URL(url, 'https://placeholder')\n return u.pathname + u.search + u.hash\n } catch {\n return url || '/'\n }\n }\n\n private formatTime(iso: string): string {\n const date = new Date(iso)\n if (Number.isNaN(date.getTime())) return ''\n const now = Date.now()\n const diffMs = Math.max(0, now - date.getTime())\n const diffMin = Math.floor(diffMs / 60000)\n if (diffMin < 1) return 'just now'\n if (diffMin < 60) return `${diffMin}m ago`\n const diffHr = Math.floor(diffMin / 60)\n if (diffHr < 24) return `${diffHr}h ago`\n const diffDays = Math.floor(diffHr / 24)\n return `${diffDays}d ago`\n }\n\n private escapeHtml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n }\n\n private escapeAttr(str: string): string {\n return str.replace(/\"/g, '&quot;').replace(/'/g, '&#39;')\n }\n\n destroy(): void {\n this.panelEl.remove()\n }\n}\n","import { submitComment, fetchComments, identify } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport { CommentListPanel } from './CommentListPanel'\nimport type { ReviewInitData } from './types'\nimport { getCurrentPageId, PageNavigationListener } from './PageTracker'\n\nexport class ReviewMode {\n private pinManager: PinManager\n private commentCard: CommentCard\n private nextPinNumber: number\n private pendingPinNumber: number | null = null\n private promptEl: HTMLElement | null = null\n private toolbarEl: HTMLElement | null = null\n private clickHandler: ((e: MouseEvent) => void) | null = null\n private pageListener: PageNavigationListener | null = null\n private mode: 'comment' | 'navigate' = 'comment'\n private keydownHandler: ((e: KeyboardEvent) => void) | null = null\n private commentPanel: CommentListPanel | null = null\n\n constructor(\n private token: string,\n private shadowRoot: ShadowRoot,\n private initData: ReviewInitData,\n ) {\n this.pinManager = new PinManager()\n this.commentCard = new CommentCard(shadowRoot)\n this.nextPinNumber = initData.nextPinNumber\n }\n\n async init(): Promise<void> {\n // If reviewer has no name, show name-entry overlay first\n if (!this.initData.reviewer.name) {\n await this.showNameEntry()\n }\n\n document.body.style.cursor = 'crosshair'\n this.pinManager.mount()\n this.showPrompt()\n this.renderToolbar()\n this.commentPanel = new CommentListPanel(this.token, this.shadowRoot, this.pinManager)\n await this.loadExistingPins()\n this.pinManager.filterByPage(getCurrentPageId())\n this.pageListener = new PageNavigationListener((pageId) => {\n this.pinManager.filterByPage(pageId)\n })\n this.pageListener.start()\n this.clickHandler = (e: MouseEvent) => this.handleClick(e)\n document.addEventListener('click', this.clickHandler, true)\n this.keydownHandler = (e: KeyboardEvent) => {\n // Don't toggle if user is typing in an input/textarea/contenteditable\n const tag = (e.target as HTMLElement)?.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return\n if ((e.target as HTMLElement)?.isContentEditable) return\n if (e.key === 'c' && !e.ctrlKey && !e.metaKey && !e.altKey) {\n this.setMode(this.mode === 'comment' ? 'navigate' : 'comment')\n }\n }\n document.addEventListener('keydown', this.keydownHandler)\n }\n\n private showPrompt(): void {\n this.promptEl = document.createElement('div')\n this.promptEl.className = 'review-prompt'\n this.promptEl.innerHTML = `\n <h3 style=\"margin:0 0 8px;font-size:16px;\">Click anywhere to leave feedback</h3>\n <p style=\"margin:0;color:#666;font-size:14px;\">Drop numbered pins on elements you want to comment on</p>\n `\n this.shadowRoot.appendChild(this.promptEl)\n\n // Auto-dismiss after 5 seconds\n setTimeout(() => this.dismissPrompt(), 5000)\n }\n\n private dismissPrompt(): void {\n if (this.promptEl) {\n this.promptEl.remove()\n this.promptEl = null\n }\n }\n\n private showNameEntry(): Promise<void> {\n return new Promise((resolve) => {\n const overlay = document.createElement('div')\n overlay.className = 'review-name-overlay'\n overlay.innerHTML = `\n <div class=\"review-name-card\">\n <h3>Welcome to the review</h3>\n <p>Optionally enter your name so the team knows who left the feedback.</p>\n <div style=\"margin-bottom:12px;\">\n <label>Name</label>\n <input type=\"text\" class=\"review-name-input\" placeholder=\"Your name (optional)\" />\n </div>\n <div>\n <label>Email</label>\n <input type=\"email\" class=\"review-email-input\" placeholder=\"Your email (optional)\" />\n </div>\n <div class=\"review-name-actions\">\n <button class=\"review-btn review-btn--cancel review-name-skip\">Skip</button>\n <button class=\"review-btn review-btn--submit review-name-continue\">Continue</button>\n </div>\n </div>\n `\n this.shadowRoot.appendChild(overlay)\n\n const nameInput = overlay.querySelector('.review-name-input') as HTMLInputElement\n const emailInput = overlay.querySelector('.review-email-input') as HTMLInputElement\n const skipBtn = overlay.querySelector('.review-name-skip') as HTMLButtonElement\n const continueBtn = overlay.querySelector('.review-name-continue') as HTMLButtonElement\n\n const finish = async () => {\n const name = nameInput.value.trim() || undefined\n const email = emailInput.value.trim() || undefined\n try {\n await identify(this.token, name, email)\n } catch {\n // Non-fatal — still let them review\n }\n overlay.remove()\n resolve()\n }\n\n skipBtn.addEventListener('click', () => {\n identify(this.token).catch(() => {}) // fire-and-forget to upgrade status\n overlay.remove()\n resolve()\n })\n continueBtn.addEventListener('click', finish)\n nameInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') finish()\n })\n emailInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') finish()\n })\n\n nameInput.focus()\n })\n }\n\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n this.updateToolbar()\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private updateToolbar(): void {\n if (!this.toolbarEl) return\n const pinCount = this.nextPinNumber - 1\n const isNav = this.mode === 'navigate'\n const isComment = this.mode === 'comment'\n this.toolbarEl.innerHTML = `\n <div class=\"review-mode-toggle\">\n <button class=\"review-mode-btn ${isNav ? 'review-mode-btn--active' : ''}\" data-mode=\"navigate\">\n <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>\n Navigate\n </button>\n <button class=\"review-mode-btn ${isComment ? 'review-mode-btn--active' : ''}\" data-mode=\"comment\">\n <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>\n Comment\n </button>\n </div>\n <span style=\"font-weight:500;\">${this.initData.session.name}</span>\n <span style=\"opacity:0.7;\">${pinCount} pin${pinCount !== 1 ? 's' : ''}</span>\n <button class=\"review-btn review-btn--submit\" data-action=\"general\" style=\"margin-left:auto;padding:6px 12px;font-size:13px;\">General Comment</button>\n <button class=\"review-mode-btn\" data-action=\"panel\" title=\"View all comments\" style=\"padding:5px 8px;\">\n <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>\n </button>\n `\n // Mode toggle buttons\n this.toolbarEl.querySelectorAll('[data-mode]').forEach((btn) => {\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n const mode = (btn as HTMLElement).dataset.mode as 'comment' | 'navigate'\n this.setMode(mode)\n })\n })\n // General comment button\n this.toolbarEl.querySelector('[data-action=\"general\"]')?.addEventListener('click', (e) => {\n e.stopPropagation()\n this.handleGeneralComment()\n })\n // Panel toggle button\n this.toolbarEl.querySelector('[data-action=\"panel\"]')?.addEventListener('click', (e) => {\n e.stopPropagation()\n this.toggleCommentPanel()\n })\n }\n\n private async loadExistingPins(): Promise<void> {\n try {\n const comments = await fetchComments(this.token)\n for (const c of comments) {\n if (c.pinNumber != null && c.pinData) {\n // commentText being non-null means it's the current reviewer's pin\n const isMine = c.commentText != null\n this.pinManager.addPin(c.pinNumber, c.pinData.pinX, c.pinData.pinY, isMine, c.pageUrl ?? '')\n }\n }\n } catch {\n // Silently fail - pins just won't show up\n }\n }\n\n private handleClick(e: MouseEvent): void {\n // In navigate mode, let clicks pass through to the page\n if (this.mode === 'navigate') return\n\n // Don't intercept clicks on our own shadow DOM elements\n const path = e.composedPath()\n if (path.some((el) => el instanceof HTMLElement && el.closest?.('#ourroadmaps-review'))) return\n\n // Don't intercept if comment card is open\n if (this.pendingPinNumber != null) return\n\n // Dismiss first-visit prompt on first click\n this.dismissPrompt()\n\n const pinData = captureElementContext(e.clientX, e.clientY)\n const pinNumber = this.nextPinNumber\n\n // Add pin at click position\n this.pinManager.addPin(pinNumber, pinData.pinX, pinData.pinY, true, getCurrentPageId())\n this.pendingPinNumber = pinNumber\n\n // Show comment card\n this.commentCard.show({\n x: e.clientX,\n y: e.clientY,\n onSubmit: async (text: string) => {\n await submitComment(this.token, {\n commentText: text,\n pinNumber,\n pinData,\n pageUrl: getCurrentPageId(),\n })\n this.pendingPinNumber = null\n this.nextPinNumber++\n this.updateToolbar()\n this.showToast('Comment saved')\n this.commentPanel?.refresh()\n },\n onCancel: () => {\n // Remove the pending pin\n this.pinManager.removePin(pinNumber)\n this.pendingPinNumber = null\n },\n })\n\n e.preventDefault()\n e.stopPropagation()\n }\n\n private setMode(newMode: 'comment' | 'navigate'): void {\n this.mode = newMode\n document.body.style.cursor = newMode === 'comment' ? 'crosshair' : ''\n this.updateToolbar()\n }\n\n private toggleCommentPanel(): void {\n this.commentPanel?.toggle()\n }\n\n private handleGeneralComment(): void {\n if (this.pendingPinNumber != null) return\n\n this.commentCard.showForGeneral({\n onSubmit: async (text: string) => {\n await submitComment(this.token, {\n commentText: text,\n pinNumber: null,\n pinData: null,\n pageUrl: getCurrentPageId(),\n })\n this.showToast('Comment saved')\n this.commentPanel?.refresh()\n },\n onCancel: () => {},\n })\n }\n\n private showToast(message: string): void {\n const toast = document.createElement('div')\n toast.className = 'review-toast'\n toast.textContent = message\n this.shadowRoot.appendChild(toast)\n setTimeout(() => toast.remove(), 2500)\n }\n\n destroy(): void {\n document.body.style.cursor = ''\n if (this.clickHandler) {\n document.removeEventListener('click', this.clickHandler, true)\n }\n this.pageListener?.destroy()\n if (this.keydownHandler) {\n document.removeEventListener('keydown', this.keydownHandler)\n }\n this.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.commentPanel?.destroy()\n this.pinManager.destroy()\n }\n}\n","const STORAGE_KEY = 'ourroadmaps:token'\n\ninterface StoredToken {\n type: 'review' | 'triage'\n token: string\n}\n\nexport function storeToken(type: 'review' | 'triage', token: string): void {\n try {\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ type, token }))\n } catch {\n // sessionStorage unavailable (private browsing, storage full, etc.)\n }\n}\n\nexport function getStoredToken(): StoredToken | null {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed.type === 'string' && typeof parsed.token === 'string') {\n return parsed as StoredToken\n }\n return null\n } catch {\n return null\n }\n}\n\nexport function clearStoredToken(): void {\n try {\n sessionStorage.removeItem(STORAGE_KEY)\n } catch {\n // sessionStorage unavailable\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\nimport { getCurrentPageId, PageNavigationListener } from './PageTracker'\nimport type { ReviewComment } from './types'\n\nexport class TriageMode {\n private pinManager: PinManager\n private toolbarEl: HTMLElement | null = null\n private tooltipEl: HTMLElement | null = null\n private comments: ReviewComment[] = []\n private pinClickHandler: ((e: MouseEvent) => void) | null = null\n private pageListener: PageNavigationListener | null = null\n private showAllPages = false\n\n constructor(\n private token: string,\n private shadowRoot: ShadowRoot,\n ) {\n this.pinManager = new PinManager()\n }\n\n async init(): Promise<void> {\n this.pinManager.mount()\n\n try {\n this.comments = await fetchComments(this.token)\n for (const c of this.comments) {\n if (c.pinNumber != null && c.pinData) {\n this.pinManager.addPin(c.pinNumber, c.pinData.pinX, c.pinData.pinY, true, c.pageUrl ?? '')\n }\n }\n } catch {\n // Show error state\n }\n\n this.pinManager.filterByPage(getCurrentPageId())\n\n this.renderToolbar()\n\n this.pageListener = new PageNavigationListener((pageId) => {\n if (!this.showAllPages) {\n this.pinManager.filterByPage(pageId)\n }\n this.updateToolbar()\n })\n this.pageListener.start()\n\n this.pinClickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n if (target.classList?.contains('review-pin')) {\n const num = Number(target.dataset.pinNumber)\n if (num) this.handlePinClick(num, e)\n }\n }\n document.addEventListener('click', this.pinClickHandler, true)\n\n this.autoFocusPin()\n }\n\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n this.updateToolbar()\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private updateToolbar(): void {\n if (!this.toolbarEl) return\n\n const totalCount = this.comments.filter((c) => c.pinNumber != null).length\n const currentPageId = getCurrentPageId()\n const pageCount = this.pinManager.countForPage(currentPageId)\n // Show pathname for path-routed apps, or pathname#hash for hash-routed apps\n const url = new URL(currentPageId, window.location.origin)\n const pagePath = url.hash ? `${url.pathname}${url.hash.split('?')[0]}` : (url.pathname || '/')\n\n this.toolbarEl.innerHTML = ''\n\n const label = document.createElement('span')\n label.style.fontWeight = '500'\n label.textContent = 'Triage Mode'\n this.toolbarEl.appendChild(label)\n\n const count = document.createElement('span')\n count.style.opacity = '0.7'\n if (this.showAllPages) {\n count.textContent = `${totalCount} pin${totalCount !== 1 ? 's' : ''}`\n } else {\n count.textContent = `${pageCount} pin${pageCount !== 1 ? 's' : ''} on ${pagePath} (${totalCount} total)`\n }\n this.toolbarEl.appendChild(count)\n\n const toggleBtn = document.createElement('button')\n toggleBtn.className = 'review-btn review-btn--submit'\n toggleBtn.style.cssText = 'margin-left:auto;padding:6px 12px;font-size:13px;'\n toggleBtn.textContent = this.showAllPages ? 'This page' : 'All pages'\n toggleBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.togglePageFilter()\n })\n this.toolbarEl.appendChild(toggleBtn)\n }\n\n private togglePageFilter(): void {\n this.showAllPages = !this.showAllPages\n if (this.showAllPages) {\n this.pinManager.showAll()\n } else {\n this.pinManager.filterByPage(getCurrentPageId())\n }\n this.updateToolbar()\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10)\n }\n\n private showTooltipForPin(pinNumber: number, x?: number, y?: number): void {\n this.hideTooltip()\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment) return\n\n this.tooltipEl = document.createElement('div')\n this.tooltipEl.className = 'review-tooltip'\n\n const time = new Date(comment.createdAt).toLocaleString()\n\n const header = document.createElement('div')\n header.style.cssText = 'font-weight:500;margin-bottom:4px;'\n header.textContent = `Pin #${pinNumber}`\n this.tooltipEl.appendChild(header)\n\n if (comment.reviewerName) {\n const name = document.createElement('div')\n name.style.cssText = 'font-size:11px;opacity:0.7;margin-bottom:2px;'\n name.textContent = comment.reviewerName\n this.tooltipEl.appendChild(name)\n }\n\n const text = document.createElement('div')\n text.style.cssText = 'margin-bottom:4px;'\n text.textContent = comment.commentText || '(no text)'\n this.tooltipEl.appendChild(text)\n\n const timeEl = document.createElement('div')\n timeEl.style.cssText = 'font-size:11px;opacity:0.7;'\n timeEl.textContent = time\n this.tooltipEl.appendChild(timeEl)\n\n const pinPageUrl = comment.pageUrl ?? ''\n if (pinPageUrl && pinPageUrl !== getCurrentPageId()) {\n const navEl = document.createElement('div')\n navEl.style.cssText = 'margin-top:6px;padding-top:6px;border-top:1px solid rgba(255,255,255,0.2);font-size:12px;'\n\n const pageLabel = document.createElement('span')\n pageLabel.style.opacity = '0.7'\n pageLabel.textContent = `Page: ${pinPageUrl.split('?')[0].split('#')[0] || '/'} \\u2014 `\n navEl.appendChild(pageLabel)\n\n const navLink = document.createElement('a')\n navLink.style.cssText = 'color:#a78bfa;cursor:pointer;text-decoration:underline;pointer-events:auto;'\n navLink.textContent = 'Navigate there?'\n navLink.addEventListener('click', (e) => {\n e.stopPropagation()\n this.navigateToPin(pinPageUrl, pinNumber)\n })\n navEl.appendChild(navLink)\n\n this.tooltipEl.appendChild(navEl)\n this.tooltipEl.style.pointerEvents = 'auto'\n }\n\n const posX = x ?? window.innerWidth / 2\n const posY = y ?? window.innerHeight / 3\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n const dismiss = (ev: MouseEvent) => {\n if (ev.target !== this.tooltipEl && !this.tooltipEl?.contains(ev.target as Node)) {\n this.hideTooltip()\n this.pinManager.clearHighlight()\n document.removeEventListener('click', dismiss, true)\n }\n }\n setTimeout(() => document.addEventListener('click', dismiss, true), 0)\n }\n\n private navigateToPin(pageUrl: string, pinNumber: number): void {\n this.hideTooltip()\n this.pinManager.clearHighlight()\n\n const url = new URL(pageUrl, window.location.origin)\n url.searchParams.set('pin', String(pinNumber))\n window.location.href = url.toString()\n }\n\n private autoFocusPin(): void {\n const params = new URLSearchParams(window.location.search)\n const pinParam = params.get('pin')\n if (!pinParam) return\n\n const pinNumber = Number(pinParam)\n if (!pinNumber) return\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment?.pinData) return\n\n const pinPageUrl = comment.pageUrl ?? ''\n if (pinPageUrl) {\n // Strip ?pin=N from current URL before comparing — navigateToPin adds it,\n // so after redirect getCurrentPageId() includes it but comment.pageUrl doesn't\n const currentUrl = new URL(window.location.href)\n currentUrl.searchParams.delete('pin')\n const currentPageWithoutPin = currentUrl.pathname + currentUrl.search + currentUrl.hash\n if (pinPageUrl !== currentPageWithoutPin) {\n this.navigateToPin(pinPageUrl, pinNumber)\n return\n }\n }\n\n this.pinManager.showAll()\n this.showAllPages = true\n this.updateToolbar()\n\n const docEl = document.documentElement\n const scrollX = (comment.pinData.pinX / 100) * docEl.scrollWidth\n const scrollY = (comment.pinData.pinY / 100) * docEl.scrollHeight\n\n window.scrollTo({\n left: scrollX - window.innerWidth / 2,\n top: scrollY - window.innerHeight / 2,\n behavior: 'smooth',\n })\n\n setTimeout(() => {\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber)\n }, 500)\n }\n\n private hideTooltip(): void {\n this.tooltipEl?.remove()\n this.tooltipEl = null\n }\n\n destroy(): void {\n if (this.pinClickHandler) {\n document.removeEventListener('click', this.pinClickHandler, true)\n }\n this.pageListener?.destroy()\n this.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\nimport { storeToken, getStoredToken, clearStoredToken } from './TokenStorage'\nimport { TriageMode } from './TriageMode'\nimport { REVIEW_STYLES } from './styles'\nimport type { ReviewOptions } from './types'\n\nexport class Review {\n private root: HTMLElement\n private shadow: ShadowRoot\n private mode: ReviewMode | TriageMode | null = null\n private _isDestroyed = false\n\n constructor(_options: ReviewOptions = {}) {\n this.root = document.createElement('div')\n this.root.id = 'ourroadmaps-review'\n this.shadow = this.root.attachShadow({ mode: 'open' })\n\n const styleEl = document.createElement('style')\n styleEl.textContent = REVIEW_STYLES\n this.shadow.appendChild(styleEl)\n\n document.body.appendChild(this.root)\n }\n\n async init(tokenType?: 'review' | 'triage', token?: string): Promise<void> {\n // Backward compat: if no args, check URL params then sessionStorage\n if (!tokenType || !token) {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n if (reviewToken) {\n tokenType = 'review'\n token = reviewToken\n storeToken('review', reviewToken)\n const clean = new URL(window.location.href)\n clean.searchParams.delete('review')\n history.replaceState(null, '', clean.toString())\n } else if (triageToken) {\n tokenType = 'triage'\n token = triageToken\n storeToken('triage', triageToken)\n const clean = new URL(window.location.href)\n clean.searchParams.delete('triage')\n history.replaceState(null, '', clean.toString())\n } else {\n const stored = getStoredToken()\n if (stored) {\n tokenType = stored.type\n token = stored.token\n } else {\n return\n }\n }\n }\n\n if (tokenType === 'review') {\n try {\n const data = await validateToken(token)\n this.mode = new ReviewMode(token, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\n clearStoredToken()\n this.showErrorOverlay(\n err.code === 'expired'\n ? 'This feedback session has expired'\n : 'This review link is no longer valid',\n )\n }\n }\n } else if (tokenType === 'triage') {\n try {\n await validateToken(token)\n this.mode = new TriageMode(token, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\n clearStoredToken()\n this.showErrorOverlay(\n err.code === 'expired'\n ? 'This feedback session has expired'\n : 'This review link is no longer valid',\n )\n }\n }\n }\n }\n\n private showErrorOverlay(message: string): void {\n const overlay = document.createElement('div')\n overlay.className = 'review-expired-overlay'\n overlay.innerHTML = `\n <div class=\"review-expired-card\">\n <h2 style=\"margin:0 0 8px;font-size:18px;\">Session Unavailable</h2>\n <p style=\"margin:0;color:#666;font-size:14px;\">${message}</p>\n <p style=\"margin:12px 0 0;color:#999;font-size:13px;\">Contact the prototype owner for a new link.</p>\n </div>\n `\n this.shadow.appendChild(overlay)\n\n // Apply grayscale to body\n document.body.style.filter = 'grayscale(0.8)'\n }\n\n destroy(): void {\n if (this._isDestroyed) return\n this._isDestroyed = true\n this.mode?.destroy()\n this.root.remove()\n document.body.style.filter = ''\n }\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["review_exports","__export","Review","import_meta","API_URL","validateToken","token","res","ReviewError","identify","name","email","body","submitComment","comment","fetchComments","code","message","captureElementContext","clientX","clientY","el","docEl","pinX","pinY","rect","buildSelector","getDirectText","classStr","getSiblingsSummary","getNearbyText","parts","current","part","classes","text","node","cls","siblings","child","tag","depth","REVIEW_STYLES","PIN_DOCUMENT_STYLES","STYLE_ID","PinManager","__publicField","PIN_DOCUMENT_STYLES","pinNumber","x","y","isMine","pageUrl","existing","pin","entry","num","el","currentPageId","pageId","count","CommentCard","shadowRoot","__publicField","options","textarea","text","submitBtn","errorEl","err","getCurrentPageId","PageNavigationListener","onChange","__publicField","check","current","args","CommentListPanel","token","shadowRoot","pinManager","__publicField","fetchComments","withText","c","currentPage","getCurrentPageId","currentPageComments","otherComments","html","groups","group","comments","badge","author","text","time","pinAttr","pageAttr","el","pinNumber","key","arr","pageUrl","url","u","iso","date","now","diffMs","diffMin","diffHr","str","ReviewMode","token","shadowRoot","initData","__publicField","PinManager","CommentCard","CommentListPanel","getCurrentPageId","PageNavigationListener","pageId","tag","resolve","overlay","nameInput","emailInput","skipBtn","continueBtn","finish","name","email","identify","e","pinCount","isNav","isComment","btn","mode","comments","fetchComments","c","isMine","el","pinData","captureElementContext","pinNumber","text","submitComment","newMode","message","toast","STORAGE_KEY","storeToken","type","token","getStoredToken","raw","parsed","clearStoredToken","TriageMode","token","shadowRoot","__publicField","PinManager","fetchComments","c","getCurrentPageId","PageNavigationListener","pageId","target","num","totalCount","currentPageId","pageCount","url","pagePath","label","count","toggleBtn","e","pinNumber","x","y","comment","time","header","name","text","timeEl","pinPageUrl","navEl","pageLabel","navLink","posX","posY","dismiss","ev","pageUrl","pinParam","currentUrl","currentPageWithoutPin","docEl","scrollX","scrollY","Review","_options","__publicField","styleEl","REVIEW_STYLES","tokenType","token","params","reviewToken","triageToken","storeToken","clean","stored","getStoredToken","data","validateToken","ReviewMode","err","ReviewError","clearStoredToken","TriageMode","message","overlay"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ourroadmaps/web-sdk",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "description": "Feedback widget and overlay system for OurRoadmaps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/review/api.ts","../src/review/ElementCapture.ts","../src/review/styles.ts","../src/review/PinManager.ts","../src/review/CommentCard.ts","../src/review/PageTracker.ts","../src/review/ReviewMode.ts","../src/review/TokenStorage.ts","../src/review/TriageMode.ts","../src/review/Review.ts"],"names":[],"mappings":"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AgB;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;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","file":"chunk-D2JMXL2O.js","sourcesContent":["import type { PinData, ReviewComment, ReviewInitData } from './types'\n\n// Default to production, override with env var for local dev\nconst API_URL = (() => {\n // Vite dev mode\n if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_URL) {\n return import.meta.env.VITE_API_URL\n }\n return 'https://api.ourroadmaps.com'\n})()\n\nexport async function validateToken(token: string): Promise<ReviewInitData> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}`)\n if (res.status === 410) throw new ReviewError('expired', 'This feedback session has expired')\n if (res.status === 404) throw new ReviewError('invalid', 'This review link is not valid')\n if (!res.ok) throw new ReviewError('error', 'Something went wrong')\n const body = await res.json()\n return body.data\n}\n\n// TODO: Wire into SDK — show name prompt on first visit, call identify() to transition invite status from 'pending' to 'opened'\nexport async function identify(token: string, name: string): Promise<void> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/identify`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n if (!res.ok) throw new ReviewError('error', 'Failed to identify')\n}\n\nexport async function submitComment(\n token: string,\n comment: { commentText: string; pinNumber: number | null; pinData: PinData | null; pageUrl: string | null },\n): Promise<{ id: string; pinNumber: number | null; createdAt: string }> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/comments`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(comment),\n })\n if (!res.ok) throw new ReviewError('error', 'Failed to submit comment')\n const body = await res.json()\n return body.data\n}\n\nexport async function fetchComments(token: string): Promise<ReviewComment[]> {\n const res = await fetch(`${API_URL}/v1/prototype-review/${token}/comments`)\n if (!res.ok) throw new ReviewError('error', 'Failed to load comments')\n const body = await res.json()\n return body.data\n}\n\nexport class ReviewError extends Error {\n constructor(\n public code: 'expired' | 'invalid' | 'error',\n message: string,\n ) {\n super(message)\n this.name = 'ReviewError'\n }\n}\n","import type { PinData } from './types'\n\nexport function captureElementContext(clientX: number, clientY: number): PinData {\n const el = document.elementFromPoint(clientX, clientY) as HTMLElement | null\n const docEl = document.documentElement\n\n const pinX = ((clientX + window.scrollX) / docEl.scrollWidth) * 100\n const pinY = ((clientY + window.scrollY) / docEl.scrollHeight) * 100\n\n if (!el || el === document.body || el === docEl) {\n return {\n pinX,\n pinY,\n element: {\n selector: 'body',\n tag: 'body',\n text: '',\n ariaLabel: null,\n className: '',\n boundingBox: { x: 0, y: 0, w: docEl.scrollWidth, h: docEl.scrollHeight },\n },\n context: {\n parentTag: '',\n parentText: '',\n grandparentTag: '',\n siblings: [],\n nearbyText: '',\n },\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }\n }\n\n const rect = el.getBoundingClientRect()\n\n return {\n pinX,\n pinY,\n element: {\n selector: buildSelector(el),\n tag: el.tagName.toLowerCase(),\n text: getDirectText(el).slice(0, 200),\n ariaLabel: el.getAttribute('aria-label'),\n className: el.className && typeof el.className === 'string' ? el.className : '',\n boundingBox: {\n x: Math.round(rect.x),\n y: Math.round(rect.y),\n w: Math.round(rect.width),\n h: Math.round(rect.height),\n },\n },\n context: {\n parentTag: el.parentElement ? `${el.parentElement.tagName.toLowerCase()}${classStr(el.parentElement)}` : '',\n parentText: el.parentElement ? getDirectText(el.parentElement).slice(0, 100) : '',\n grandparentTag: el.parentElement?.parentElement\n ? `${el.parentElement.parentElement.tagName.toLowerCase()}${classStr(el.parentElement.parentElement)}`\n : '',\n siblings: getSiblingsSummary(el),\n nearbyText: getNearbyText(el).slice(0, 200),\n },\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }\n}\n\nfunction buildSelector(el: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = el\n\n while (current && current !== document.body) {\n if (current.id) {\n parts.unshift(`#${current.id}`)\n break\n }\n\n let part = current.tagName.toLowerCase()\n if (current.className && typeof current.className === 'string') {\n const classes = current.className.trim().split(/\\s+/).slice(0, 2).join('.')\n if (classes) part += `.${classes}`\n }\n\n parts.unshift(part)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\nfunction getDirectText(el: HTMLElement): string {\n let text = ''\n for (const node of el.childNodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent?.trim() || ''\n }\n }\n return text.trim() || el.textContent?.trim().slice(0, 200) || ''\n}\n\nfunction classStr(el: HTMLElement): string {\n if (!el.className || typeof el.className !== 'string') return ''\n const cls = el.className.trim().split(/\\s+/).slice(0, 2).join('.')\n return cls ? `.${cls}` : ''\n}\n\nfunction getSiblingsSummary(el: HTMLElement): string[] {\n if (!el.parentElement) return []\n const siblings: string[] = []\n for (const child of el.parentElement.children) {\n if (child === el) continue\n const tag = child.tagName.toLowerCase()\n const text = (child.textContent?.trim() || '').slice(0, 50)\n siblings.push(text ? `${tag}:${text}` : tag)\n if (siblings.length >= 4) break\n }\n return siblings\n}\n\nfunction getNearbyText(el: HTMLElement): string {\n // Walk up to find the nearest container with meaningful text\n let current: HTMLElement | null = el.parentElement\n let depth = 0\n while (current && depth < 3) {\n const text = getDirectText(current)\n if (text && text !== getDirectText(el)) return text\n current = current.parentElement\n depth++\n }\n return ''\n}\n","export const REVIEW_STYLES = `\n:host {\n all: initial;\n}\n\n/* ─── Pin Container (rendered outside shadow DOM) ─── */\n.pin-container {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 10000;\n}\n\n/* ─── Pin Marker ─── */\n.review-pin {\n position: absolute;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #7c3aed;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n z-index: 1;\n}\n\n.review-pin:hover {\n transform: translate(-50%, -50%) scale(1.15);\n box-shadow: 0 3px 10px rgba(124, 58, 237, 0.4);\n}\n\n.review-pin--other {\n opacity: 0.5;\n}\n\n.review-pin--highlighted {\n transform: translate(-50%, -50%) scale(1.2);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3), 0 3px 10px rgba(124, 58, 237, 0.4);\n z-index: 2;\n}\n\n/* ─── Toolbar ─── */\n.review-toolbar {\n position: fixed;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n border-radius: 12px 12px 0 0;\n padding: 10px 20px;\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n z-index: 10001;\n backdrop-filter: blur(8px);\n box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.2);\n}\n\n/* ─── Comment Card ─── */\n.review-comment-card {\n position: fixed;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15), 0 1px 4px rgba(0, 0, 0, 0.08);\n width: 300px;\n padding: 16px;\n z-index: 10002;\n font-family: system-ui, -apple-system, sans-serif;\n border: 1px solid rgba(0, 0, 0, 0.08);\n}\n\n/* ─── Comment Textarea ─── */\n.review-comment-input {\n width: 100%;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n padding: 10px 12px;\n resize: vertical;\n font-family: inherit;\n font-size: 14px;\n line-height: 1.5;\n color: #1f2937;\n background: #fafafa;\n box-sizing: border-box;\n outline: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.review-comment-input:focus {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15);\n background: #fff;\n}\n\n.review-comment-input::placeholder {\n color: #9ca3af;\n}\n\n.review-comment-input:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* ─── Comment Actions ─── */\n.review-comment-actions {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n gap: 8px;\n margin-top: 12px;\n}\n\n/* ─── Buttons ─── */\n.review-btn {\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n border: none;\n transition: background 0.15s ease, opacity 0.15s ease;\n}\n\n.review-btn--cancel {\n color: #6b7280;\n background: transparent;\n}\n\n.review-btn--cancel:hover {\n background: #f3f4f6;\n}\n\n.review-btn--submit {\n background: #7c3aed;\n color: #fff;\n}\n\n.review-btn--submit:hover {\n background: #6d28d9;\n}\n\n.review-btn--submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* ─── Comment Error ─── */\n.review-comment-error {\n color: #dc2626;\n font-size: 12px;\n margin-top: 8px;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n/* ─── Name Prompt ─── */\n.review-prompt {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: #fff;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2), 0 1px 4px rgba(0, 0, 0, 0.08);\n border-radius: 12px;\n padding: 28px 32px;\n z-index: 10002;\n text-align: center;\n font-family: system-ui, -apple-system, sans-serif;\n border: 1px solid rgba(0, 0, 0, 0.06);\n max-width: 360px;\n width: 90%;\n}\n\n/* ─── Expired Overlay ─── */\n.review-expired-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10003;\n backdrop-filter: blur(2px);\n}\n\n.review-expired-card {\n background: #fff;\n border-radius: 12px;\n padding: 28px 32px;\n text-align: center;\n font-family: system-ui, -apple-system, sans-serif;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);\n max-width: 400px;\n width: 90%;\n border: 1px solid rgba(0, 0, 0, 0.06);\n}\n\n/* ─── Toast ─── */\n.review-toast {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n padding: 10px 20px;\n border-radius: 8px;\n z-index: 10003;\n pointer-events: none;\n animation: review-toast-fade 2.5s ease forwards;\n backdrop-filter: blur(8px);\n}\n\n@keyframes review-toast-fade {\n 0% { opacity: 0; transform: translateX(-50%) translateY(8px); }\n 10% { opacity: 1; transform: translateX(-50%) translateY(0); }\n 80% { opacity: 1; transform: translateX(-50%) translateY(0); }\n 100% { opacity: 0; transform: translateX(-50%) translateY(-4px); }\n}\n\n/* ─── Tooltip ─── */\n.review-tooltip {\n position: absolute;\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 13px;\n line-height: 1.4;\n border-radius: 6px;\n padding: 8px 12px;\n max-width: 280px;\n z-index: 10002;\n pointer-events: none;\n backdrop-filter: blur(8px);\n}\n\n/* ─── Reduced Motion ─── */\n@media (prefers-reduced-motion: reduce) {\n *, *::before, *::after {\n animation-duration: 0.01ms !important;\n transition-duration: 0.01ms !important;\n }\n}\n`\n\n/**\n * Subset of styles for pins that render outside the shadow DOM.\n * Injected into document.head by PinManager.\n */\nexport const PIN_DOCUMENT_STYLES = `\nbody {\n position: relative !important;\n}\n\n.pin-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n min-height: 100%;\n pointer-events: none;\n z-index: 10000;\n}\n\n.review-pin {\n position: absolute;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #7c3aed;\n color: #fff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n z-index: 1;\n}\n\n.review-pin:hover {\n transform: translate(-50%, -50%) scale(1.15);\n box-shadow: 0 3px 10px rgba(124, 58, 237, 0.4);\n}\n\n.review-pin--other {\n opacity: 0.5;\n}\n\n.review-pin--highlighted {\n transform: translate(-50%, -50%) scale(1.2);\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3), 0 3px 10px rgba(124, 58, 237, 0.4);\n z-index: 2;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .review-pin, .review-pin:hover, .review-pin--highlighted {\n transition-duration: 0.01ms !important;\n }\n}\n`\n","import { PIN_DOCUMENT_STYLES } from './styles'\n\nconst STYLE_ID = 'roadmaps-review-pin-styles'\n\ninterface PinEntry {\n el: HTMLElement\n pageUrl: string\n}\n\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, PinEntry> = new Map()\n private styleEl: HTMLStyleElement | null = null\n\n constructor() {\n this.container = document.createElement('div')\n this.container.className = 'pin-container'\n }\n\n mount(): void {\n if (!document.getElementById(STYLE_ID)) {\n this.styleEl = document.createElement('style')\n this.styleEl.id = STYLE_ID\n this.styleEl.textContent = PIN_DOCUMENT_STYLES\n document.head.appendChild(this.styleEl)\n }\n\n document.body.appendChild(this.container)\n }\n\n addPin(pinNumber: number, x: number, y: number, isMine: boolean, pageUrl: string): void {\n // Remove existing pin with same number to prevent orphaned DOM elements\n const existing = this.pins.get(pinNumber)\n if (existing) {\n existing.el.remove()\n this.pins.delete(pinNumber)\n }\n\n const pin = document.createElement('div')\n pin.className = isMine ? 'review-pin' : 'review-pin review-pin--other'\n pin.textContent = String(pinNumber)\n pin.style.left = `${x}%`\n pin.style.top = `${y}%`\n pin.dataset.pinNumber = String(pinNumber)\n pin.dataset.pageUrl = pageUrl\n this.container.appendChild(pin)\n this.pins.set(pinNumber, { el: pin, pageUrl })\n }\n\n removePin(pinNumber: number): void {\n const entry = this.pins.get(pinNumber)\n if (entry) {\n entry.el.remove()\n this.pins.delete(pinNumber)\n }\n }\n\n highlightPin(pinNumber: number): void {\n for (const [num, { el }] of this.pins) {\n el.classList.toggle('review-pin--highlighted', num === pinNumber)\n }\n }\n\n clearHighlight(): void {\n for (const { el } of this.pins.values()) {\n el.classList.remove('review-pin--highlighted')\n }\n }\n\n filterByPage(currentPageId: string): void {\n for (const { el, pageUrl } of this.pins.values()) {\n el.style.display = pageUrl === currentPageId ? '' : 'none'\n }\n }\n\n showAll(): void {\n for (const { el } of this.pins.values()) {\n el.style.display = ''\n }\n }\n\n countForPage(pageId: string): number {\n let count = 0\n for (const { pageUrl } of this.pins.values()) {\n if (pageUrl === pageId) count++\n }\n return count\n }\n\n destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n if (this.styleEl) {\n this.styleEl.remove()\n this.styleEl = null\n } else {\n document.getElementById(STYLE_ID)?.remove()\n }\n }\n}\n","export class CommentCard {\n private card: HTMLElement | null = null\n private onSubmit: ((text: string) => Promise<void>) | null = null\n private onCancel: (() => void) | null = null\n\n constructor(private shadowRoot: ShadowRoot) {}\n\n show(options: {\n x: number\n y: number\n onSubmit: (text: string) => Promise<void>\n onCancel: () => void\n }): void {\n this.hide()\n this.onSubmit = options.onSubmit\n this.onCancel = options.onCancel\n\n this.card = document.createElement('div')\n this.card.className = 'review-comment-card'\n // Position near click point, adjust to stay in viewport\n this.card.style.left = `${Math.min(options.x, window.innerWidth - 320)}px`\n this.card.style.top = `${Math.min(options.y + 20, window.innerHeight - 200)}px`\n\n this.card.innerHTML = `\n <textarea class=\"review-comment-input\" placeholder=\"Leave your feedback...\" rows=\"3\"></textarea>\n <div class=\"review-comment-actions\">\n <button class=\"review-btn review-btn--cancel\">Cancel</button>\n <button class=\"review-btn review-btn--submit\">Submit</button>\n </div>\n <div class=\"review-comment-error\" style=\"display:none\"></div>\n `\n\n this.attachListeners()\n this.shadowRoot.appendChild(this.card)\n\n const textarea = this.card.querySelector('textarea')\n textarea?.focus()\n }\n\n hide(): void {\n this.card?.remove()\n this.card = null\n }\n\n showForGeneral(options: {\n onSubmit: (text: string) => Promise<void>\n onCancel: () => void\n }): void {\n // Show centered for general (non-pinned) comments\n this.show({\n x: window.innerWidth / 2 - 150,\n y: window.innerHeight / 2 - 100,\n onSubmit: options.onSubmit,\n onCancel: options.onCancel,\n })\n }\n\n private attachListeners(): void {\n if (!this.card) return\n\n this.card.querySelector('.review-btn--cancel')?.addEventListener('click', () => {\n this.onCancel?.()\n this.hide()\n })\n\n this.card.querySelector('.review-btn--submit')?.addEventListener('click', () => this.handleSubmit())\n\n this.card.querySelector('textarea')?.addEventListener('keydown', (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n this.handleSubmit()\n }\n })\n }\n\n private async handleSubmit(): Promise<void> {\n if (!this.card) return\n const textarea = this.card.querySelector('textarea') as HTMLTextAreaElement\n const text = textarea.value.trim()\n if (!text) return\n\n const submitBtn = this.card.querySelector('.review-btn--submit') as HTMLButtonElement\n const errorEl = this.card.querySelector('.review-comment-error') as HTMLElement\n submitBtn.disabled = true\n submitBtn.textContent = 'Submitting...'\n textarea.disabled = true\n errorEl.style.display = 'none'\n\n try {\n await this.onSubmit?.(text)\n this.hide()\n } catch (err) {\n submitBtn.disabled = false\n submitBtn.textContent = 'Submit'\n textarea.disabled = false\n errorEl.textContent = err instanceof Error ? err.message : 'Failed to submit'\n errorEl.style.display = 'block'\n }\n }\n}\n","/**\n * Returns the current page identity as pathname + search + hash.\n * This captures all routing strategies: pathname, hash, and query-param based.\n */\nexport function getCurrentPageId(): string {\n return window.location.pathname + window.location.search + window.location.hash\n}\n\n/**\n * Detects page navigation within a prototype.\n * Covers: popstate (back/forward), hashchange (hash routing),\n * and pushState/replaceState (SPA routing via history API).\n */\nexport class PageNavigationListener {\n private popstateHandler: (() => void) | null = null\n private hashchangeHandler: (() => void) | null = null\n private originalPushState: typeof history.pushState | null = null\n private originalReplaceState: typeof history.replaceState | null = null\n private lastPageId: string\n\n constructor(private onChange: (pageId: string) => void) {\n this.lastPageId = getCurrentPageId()\n }\n\n start(): void {\n // Guard against double-start — would capture already-patched history methods\n if (this.originalPushState) return\n\n const check = () => {\n const current = getCurrentPageId()\n if (current !== this.lastPageId) {\n this.lastPageId = current\n this.onChange(current)\n }\n }\n\n this.popstateHandler = check\n window.addEventListener('popstate', this.popstateHandler)\n\n this.hashchangeHandler = check\n window.addEventListener('hashchange', this.hashchangeHandler)\n\n this.originalPushState = history.pushState.bind(history)\n this.originalReplaceState = history.replaceState.bind(history)\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args)\n check()\n }\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args)\n check()\n }\n }\n\n destroy(): void {\n if (this.popstateHandler) {\n window.removeEventListener('popstate', this.popstateHandler)\n }\n if (this.hashchangeHandler) {\n window.removeEventListener('hashchange', this.hashchangeHandler)\n }\n\n if (this.originalPushState) {\n history.pushState = this.originalPushState\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState\n }\n }\n}\n","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\nimport { getCurrentPageId, PageNavigationListener } from './PageTracker'\n\nexport class ReviewMode {\n private pinManager: PinManager\n private commentCard: CommentCard\n private nextPinNumber: number\n private pendingPinNumber: number | null = null\n private promptEl: HTMLElement | null = null\n private toolbarEl: HTMLElement | null = null\n private clickHandler: ((e: MouseEvent) => void) | null = null\n private pageListener: PageNavigationListener | null = null\n\n constructor(\n private token: string,\n private shadowRoot: ShadowRoot,\n private initData: ReviewInitData,\n ) {\n this.pinManager = new PinManager()\n this.commentCard = new CommentCard(shadowRoot)\n this.nextPinNumber = initData.nextPinNumber\n }\n\n async init(): Promise<void> {\n // Set crosshair cursor on body\n document.body.style.cursor = 'crosshair'\n\n // Mount pin manager\n this.pinManager.mount()\n\n // Render first-visit prompt\n this.showPrompt()\n\n // Render bottom toolbar\n this.renderToolbar()\n\n // Fetch and render existing pins\n await this.loadExistingPins()\n\n // Filter pins to current page and listen for navigation\n this.pinManager.filterByPage(getCurrentPageId())\n this.pageListener = new PageNavigationListener((pageId) => {\n this.pinManager.filterByPage(pageId)\n })\n this.pageListener.start()\n\n // Listen for clicks to drop pins\n this.clickHandler = (e: MouseEvent) => this.handleClick(e)\n document.addEventListener('click', this.clickHandler, true)\n }\n\n private showPrompt(): void {\n this.promptEl = document.createElement('div')\n this.promptEl.className = 'review-prompt'\n this.promptEl.innerHTML = `\n <h3 style=\"margin:0 0 8px;font-size:16px;\">Click anywhere to leave feedback</h3>\n <p style=\"margin:0;color:#666;font-size:14px;\">Drop numbered pins on elements you want to comment on</p>\n `\n this.shadowRoot.appendChild(this.promptEl)\n\n // Auto-dismiss after 5 seconds\n setTimeout(() => this.dismissPrompt(), 5000)\n }\n\n private dismissPrompt(): void {\n if (this.promptEl) {\n this.promptEl.remove()\n this.promptEl = null\n }\n }\n\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n this.updateToolbar()\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private updateToolbar(): void {\n if (!this.toolbarEl) return\n const pinCount = this.nextPinNumber - 1\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">${this.initData.session.name}</span>\n <span style=\"opacity:0.7;\">${pinCount} pin${pinCount !== 1 ? 's' : ''}</span>\n <button class=\"review-btn review-btn--submit\" style=\"margin-left:auto;padding:6px 12px;font-size:13px;\">General Comment</button>\n `\n this.toolbarEl.querySelector('button')?.addEventListener('click', (e) => {\n e.stopPropagation()\n this.handleGeneralComment()\n })\n }\n\n private async loadExistingPins(): Promise<void> {\n try {\n const comments = await fetchComments(this.token)\n for (const c of comments) {\n if (c.pinNumber != null && c.pinData) {\n // commentText being non-null means it's the current reviewer's pin\n const isMine = c.commentText != null\n this.pinManager.addPin(c.pinNumber, c.pinData.pinX, c.pinData.pinY, isMine, c.pageUrl ?? '')\n }\n }\n } catch {\n // Silently fail - pins just won't show up\n }\n }\n\n private handleClick(e: MouseEvent): void {\n // Don't intercept clicks on our own shadow DOM elements\n const path = e.composedPath()\n if (path.some((el) => el instanceof HTMLElement && el.closest?.('#ourroadmaps-review'))) return\n\n // Don't intercept if comment card is open\n if (this.pendingPinNumber != null) return\n\n // Dismiss first-visit prompt on first click\n this.dismissPrompt()\n\n const pinData = captureElementContext(e.clientX, e.clientY)\n const pinNumber = this.nextPinNumber\n\n // Add pin at click position\n this.pinManager.addPin(pinNumber, pinData.pinX, pinData.pinY, true, getCurrentPageId())\n this.pendingPinNumber = pinNumber\n\n // Show comment card\n this.commentCard.show({\n x: e.clientX,\n y: e.clientY,\n onSubmit: async (text: string) => {\n await submitComment(this.token, {\n commentText: text,\n pinNumber,\n pinData,\n pageUrl: getCurrentPageId(),\n })\n this.pendingPinNumber = null\n this.nextPinNumber++\n this.updateToolbar()\n this.showToast('Comment saved')\n },\n onCancel: () => {\n // Remove the pending pin\n this.pinManager.removePin(pinNumber)\n this.pendingPinNumber = null\n },\n })\n\n e.preventDefault()\n e.stopPropagation()\n }\n\n private handleGeneralComment(): void {\n if (this.pendingPinNumber != null) return\n\n this.commentCard.showForGeneral({\n onSubmit: async (text: string) => {\n await submitComment(this.token, {\n commentText: text,\n pinNumber: null,\n pinData: null,\n pageUrl: getCurrentPageId(),\n })\n this.showToast('Comment saved')\n },\n onCancel: () => {},\n })\n }\n\n private showToast(message: string): void {\n const toast = document.createElement('div')\n toast.className = 'review-toast'\n toast.textContent = message\n this.shadowRoot.appendChild(toast)\n setTimeout(() => toast.remove(), 2500)\n }\n\n destroy(): void {\n document.body.style.cursor = ''\n if (this.clickHandler) {\n document.removeEventListener('click', this.clickHandler, true)\n }\n this.pageListener?.destroy()\n this.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","const STORAGE_KEY = 'ourroadmaps:token'\n\ninterface StoredToken {\n type: 'review' | 'triage'\n token: string\n}\n\nexport function storeToken(type: 'review' | 'triage', token: string): void {\n try {\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ type, token }))\n } catch {\n // sessionStorage unavailable (private browsing, storage full, etc.)\n }\n}\n\nexport function getStoredToken(): StoredToken | null {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed.type === 'string' && typeof parsed.token === 'string') {\n return parsed as StoredToken\n }\n return null\n } catch {\n return null\n }\n}\n\nexport function clearStoredToken(): void {\n try {\n sessionStorage.removeItem(STORAGE_KEY)\n } catch {\n // sessionStorage unavailable\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\nimport { getCurrentPageId, PageNavigationListener } from './PageTracker'\nimport type { ReviewComment } from './types'\n\nexport class TriageMode {\n private pinManager: PinManager\n private toolbarEl: HTMLElement | null = null\n private tooltipEl: HTMLElement | null = null\n private comments: ReviewComment[] = []\n private pinClickHandler: ((e: MouseEvent) => void) | null = null\n private pageListener: PageNavigationListener | null = null\n private showAllPages = false\n\n constructor(\n private token: string,\n private shadowRoot: ShadowRoot,\n ) {\n this.pinManager = new PinManager()\n }\n\n async init(): Promise<void> {\n this.pinManager.mount()\n\n try {\n this.comments = await fetchComments(this.token)\n for (const c of this.comments) {\n if (c.pinNumber != null && c.pinData) {\n this.pinManager.addPin(c.pinNumber, c.pinData.pinX, c.pinData.pinY, true, c.pageUrl ?? '')\n }\n }\n } catch {\n // Show error state\n }\n\n this.pinManager.filterByPage(getCurrentPageId())\n\n this.renderToolbar()\n\n this.pageListener = new PageNavigationListener((pageId) => {\n if (!this.showAllPages) {\n this.pinManager.filterByPage(pageId)\n }\n this.updateToolbar()\n })\n this.pageListener.start()\n\n this.pinClickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n if (target.classList?.contains('review-pin')) {\n const num = Number(target.dataset.pinNumber)\n if (num) this.handlePinClick(num, e)\n }\n }\n document.addEventListener('click', this.pinClickHandler, true)\n\n this.autoFocusPin()\n }\n\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n this.updateToolbar()\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private updateToolbar(): void {\n if (!this.toolbarEl) return\n\n const totalCount = this.comments.filter((c) => c.pinNumber != null).length\n const currentPageId = getCurrentPageId()\n const pageCount = this.pinManager.countForPage(currentPageId)\n // Show pathname for path-routed apps, or pathname#hash for hash-routed apps\n const url = new URL(currentPageId, window.location.origin)\n const pagePath = url.hash ? `${url.pathname}${url.hash.split('?')[0]}` : (url.pathname || '/')\n\n this.toolbarEl.innerHTML = ''\n\n const label = document.createElement('span')\n label.style.fontWeight = '500'\n label.textContent = 'Triage Mode'\n this.toolbarEl.appendChild(label)\n\n const count = document.createElement('span')\n count.style.opacity = '0.7'\n if (this.showAllPages) {\n count.textContent = `${totalCount} pin${totalCount !== 1 ? 's' : ''}`\n } else {\n count.textContent = `${pageCount} pin${pageCount !== 1 ? 's' : ''} on ${pagePath} (${totalCount} total)`\n }\n this.toolbarEl.appendChild(count)\n\n const toggleBtn = document.createElement('button')\n toggleBtn.className = 'review-btn review-btn--submit'\n toggleBtn.style.cssText = 'margin-left:auto;padding:6px 12px;font-size:13px;'\n toggleBtn.textContent = this.showAllPages ? 'This page' : 'All pages'\n toggleBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.togglePageFilter()\n })\n this.toolbarEl.appendChild(toggleBtn)\n }\n\n private togglePageFilter(): void {\n this.showAllPages = !this.showAllPages\n if (this.showAllPages) {\n this.pinManager.showAll()\n } else {\n this.pinManager.filterByPage(getCurrentPageId())\n }\n this.updateToolbar()\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10)\n }\n\n private showTooltipForPin(pinNumber: number, x?: number, y?: number): void {\n this.hideTooltip()\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment) return\n\n this.tooltipEl = document.createElement('div')\n this.tooltipEl.className = 'review-tooltip'\n\n const time = new Date(comment.createdAt).toLocaleString()\n\n const header = document.createElement('div')\n header.style.cssText = 'font-weight:500;margin-bottom:4px;'\n header.textContent = `Pin #${pinNumber}`\n this.tooltipEl.appendChild(header)\n\n if (comment.reviewerName) {\n const name = document.createElement('div')\n name.style.cssText = 'font-size:11px;opacity:0.7;margin-bottom:2px;'\n name.textContent = comment.reviewerName\n this.tooltipEl.appendChild(name)\n }\n\n const text = document.createElement('div')\n text.style.cssText = 'margin-bottom:4px;'\n text.textContent = comment.commentText || '(no text)'\n this.tooltipEl.appendChild(text)\n\n const timeEl = document.createElement('div')\n timeEl.style.cssText = 'font-size:11px;opacity:0.7;'\n timeEl.textContent = time\n this.tooltipEl.appendChild(timeEl)\n\n const pinPageUrl = comment.pageUrl ?? ''\n if (pinPageUrl && pinPageUrl !== getCurrentPageId()) {\n const navEl = document.createElement('div')\n navEl.style.cssText = 'margin-top:6px;padding-top:6px;border-top:1px solid rgba(255,255,255,0.2);font-size:12px;'\n\n const pageLabel = document.createElement('span')\n pageLabel.style.opacity = '0.7'\n pageLabel.textContent = `Page: ${pinPageUrl.split('?')[0].split('#')[0] || '/'} \\u2014 `\n navEl.appendChild(pageLabel)\n\n const navLink = document.createElement('a')\n navLink.style.cssText = 'color:#a78bfa;cursor:pointer;text-decoration:underline;pointer-events:auto;'\n navLink.textContent = 'Navigate there?'\n navLink.addEventListener('click', (e) => {\n e.stopPropagation()\n this.navigateToPin(pinPageUrl, pinNumber)\n })\n navEl.appendChild(navLink)\n\n this.tooltipEl.appendChild(navEl)\n this.tooltipEl.style.pointerEvents = 'auto'\n }\n\n const posX = x ?? window.innerWidth / 2\n const posY = y ?? window.innerHeight / 3\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n const dismiss = (ev: MouseEvent) => {\n if (ev.target !== this.tooltipEl && !this.tooltipEl?.contains(ev.target as Node)) {\n this.hideTooltip()\n this.pinManager.clearHighlight()\n document.removeEventListener('click', dismiss, true)\n }\n }\n setTimeout(() => document.addEventListener('click', dismiss, true), 0)\n }\n\n private navigateToPin(pageUrl: string, pinNumber: number): void {\n this.hideTooltip()\n this.pinManager.clearHighlight()\n\n const url = new URL(pageUrl, window.location.origin)\n url.searchParams.set('pin', String(pinNumber))\n window.location.href = url.toString()\n }\n\n private autoFocusPin(): void {\n const params = new URLSearchParams(window.location.search)\n const pinParam = params.get('pin')\n if (!pinParam) return\n\n const pinNumber = Number(pinParam)\n if (!pinNumber) return\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment?.pinData) return\n\n const pinPageUrl = comment.pageUrl ?? ''\n if (pinPageUrl) {\n // Strip ?pin=N from current URL before comparing — navigateToPin adds it,\n // so after redirect getCurrentPageId() includes it but comment.pageUrl doesn't\n const currentUrl = new URL(window.location.href)\n currentUrl.searchParams.delete('pin')\n const currentPageWithoutPin = currentUrl.pathname + currentUrl.search + currentUrl.hash\n if (pinPageUrl !== currentPageWithoutPin) {\n this.navigateToPin(pinPageUrl, pinNumber)\n return\n }\n }\n\n this.pinManager.showAll()\n this.showAllPages = true\n this.updateToolbar()\n\n const docEl = document.documentElement\n const scrollX = (comment.pinData.pinX / 100) * docEl.scrollWidth\n const scrollY = (comment.pinData.pinY / 100) * docEl.scrollHeight\n\n window.scrollTo({\n left: scrollX - window.innerWidth / 2,\n top: scrollY - window.innerHeight / 2,\n behavior: 'smooth',\n })\n\n setTimeout(() => {\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber)\n }, 500)\n }\n\n private hideTooltip(): void {\n this.tooltipEl?.remove()\n this.tooltipEl = null\n }\n\n destroy(): void {\n if (this.pinClickHandler) {\n document.removeEventListener('click', this.pinClickHandler, true)\n }\n this.pageListener?.destroy()\n this.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\nimport { storeToken, getStoredToken, clearStoredToken } from './TokenStorage'\nimport { TriageMode } from './TriageMode'\nimport { REVIEW_STYLES } from './styles'\nimport type { ReviewOptions } from './types'\n\nexport class Review {\n private root: HTMLElement\n private shadow: ShadowRoot\n private mode: ReviewMode | TriageMode | null = null\n private _isDestroyed = false\n\n constructor(_options: ReviewOptions = {}) {\n this.root = document.createElement('div')\n this.root.id = 'ourroadmaps-review'\n this.shadow = this.root.attachShadow({ mode: 'open' })\n\n const styleEl = document.createElement('style')\n styleEl.textContent = REVIEW_STYLES\n this.shadow.appendChild(styleEl)\n\n document.body.appendChild(this.root)\n }\n\n async init(tokenType?: 'review' | 'triage', token?: string): Promise<void> {\n // Backward compat: if no args, check URL params then sessionStorage\n if (!tokenType || !token) {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n if (reviewToken) {\n tokenType = 'review'\n token = reviewToken\n storeToken('review', reviewToken)\n const clean = new URL(window.location.href)\n clean.searchParams.delete('review')\n history.replaceState(null, '', clean.toString())\n } else if (triageToken) {\n tokenType = 'triage'\n token = triageToken\n storeToken('triage', triageToken)\n const clean = new URL(window.location.href)\n clean.searchParams.delete('triage')\n history.replaceState(null, '', clean.toString())\n } else {\n const stored = getStoredToken()\n if (stored) {\n tokenType = stored.type\n token = stored.token\n } else {\n return\n }\n }\n }\n\n if (tokenType === 'review') {\n try {\n const data = await validateToken(token)\n this.mode = new ReviewMode(token, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\n clearStoredToken()\n this.showErrorOverlay(\n err.code === 'expired'\n ? 'This feedback session has expired'\n : 'This review link is no longer valid',\n )\n }\n }\n } else if (tokenType === 'triage') {\n try {\n await validateToken(token)\n this.mode = new TriageMode(token, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\n clearStoredToken()\n this.showErrorOverlay(\n err.code === 'expired'\n ? 'This feedback session has expired'\n : 'This review link is no longer valid',\n )\n }\n }\n }\n }\n\n private showErrorOverlay(message: string): void {\n const overlay = document.createElement('div')\n overlay.className = 'review-expired-overlay'\n overlay.innerHTML = `\n <div class=\"review-expired-card\">\n <h2 style=\"margin:0 0 8px;font-size:18px;\">Session Unavailable</h2>\n <p style=\"margin:0;color:#666;font-size:14px;\">${message}</p>\n <p style=\"margin:12px 0 0;color:#999;font-size:13px;\">Contact the prototype owner for a new link.</p>\n </div>\n `\n this.shadow.appendChild(overlay)\n\n // Apply grayscale to body\n document.body.style.filter = 'grayscale(0.8)'\n }\n\n destroy(): void {\n if (this._isDestroyed) return\n this._isDestroyed = true\n this.mode?.destroy()\n this.root.remove()\n document.body.style.filter = ''\n }\n}\n"]}