@ourroadmaps/web-sdk 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -414,9 +414,16 @@ var REVIEW_STYLES = `
414
414
  }
415
415
  `;
416
416
  var PIN_DOCUMENT_STYLES = `
417
+ body {
418
+ position: relative !important;
419
+ }
420
+
417
421
  .pin-container {
418
- position: fixed;
419
- inset: 0;
422
+ position: absolute;
423
+ top: 0;
424
+ left: 0;
425
+ width: 100%;
426
+ min-height: 100%;
420
427
  pointer-events: none;
421
428
  z-index: 10000;
422
429
  }
@@ -893,5 +900,5 @@ var Review = class {
893
900
  };
894
901
 
895
902
  export { Review };
896
- //# sourceMappingURL=chunk-HOJDCIWN.js.map
897
- //# sourceMappingURL=chunk-HOJDCIWN.js.map
903
+ //# sourceMappingURL=chunk-2Q3BAT55.js.map
904
+ //# sourceMappingURL=chunk-2Q3BAT55.js.map
@@ -0,0 +1 @@
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/ReviewMode.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-2Q3BAT55.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\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, HTMLElement> = 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 // Inject pin styles into document.head (pins live outside shadow DOM)\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): void {\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 this.container.appendChild(pin)\n this.pins.set(pinNumber, pin)\n }\n\n removePin(pinNumber: number): void {\n const pin = this.pins.get(pinNumber)\n if (pin) {\n pin.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 destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n // Clean up injected style element\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","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\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\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 // 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)\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)\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: window.location.pathname,\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: window.location.pathname,\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.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\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\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 // Fetch all comments\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)\n }\n }\n } catch {\n // Show error state\n }\n\n this.renderToolbar()\n\n // Listen for clicks on pins\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\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n const count = this.comments.filter((c) => c.pinNumber != null).length\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">Triage Mode</span>\n <span style=\"opacity:0.7;\">${count} pin${count !== 1 ? 's' : ''}</span>\n `\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\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 this.tooltipEl.innerHTML = `\n <div style=\"font-weight:500;margin-bottom:4px;\">Pin #${pinNumber}</div>\n <div style=\"margin-bottom:4px;\">${comment.commentText || '(no text)'}</div>\n <div style=\"font-size:11px;opacity:0.7;\">${time}</div>\n `\n\n // Position near pin\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(e.clientX + 16, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(e.clientY - 10, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // Click elsewhere to dismiss\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 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.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\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(): Promise<void> {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n\n if (reviewToken) {\n try {\n const data = await validateToken(reviewToken)\n this.mode = new ReviewMode(reviewToken, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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 (triageToken) {\n try {\n await validateToken(triageToken)\n this.mode = new TriageMode(triageToken, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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"]}
@@ -5,7 +5,7 @@ var chunk4DE2IREA_cjs = require('./chunk-4DE2IREA.cjs');
5
5
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
6
6
  // src/review/api.ts
7
7
  var API_URL = (() => {
8
- if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-46WI5367.cjs', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_API_URL) {
8
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-WL3SZK6Q.cjs', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_API_URL) {
9
9
  return undefined.VITE_API_URL;
10
10
  }
11
11
  return "https://api.ourroadmaps.com";
@@ -417,9 +417,16 @@ var REVIEW_STYLES = `
417
417
  }
418
418
  `;
419
419
  var PIN_DOCUMENT_STYLES = `
420
+ body {
421
+ position: relative !important;
422
+ }
423
+
420
424
  .pin-container {
421
- position: fixed;
422
- inset: 0;
425
+ position: absolute;
426
+ top: 0;
427
+ left: 0;
428
+ width: 100%;
429
+ min-height: 100%;
423
430
  pointer-events: none;
424
431
  z-index: 10000;
425
432
  }
@@ -896,5 +903,5 @@ var Review = class {
896
903
  };
897
904
 
898
905
  exports.Review = Review;
899
- //# sourceMappingURL=chunk-46WI5367.cjs.map
900
- //# sourceMappingURL=chunk-46WI5367.cjs.map
906
+ //# sourceMappingURL=chunk-WL3SZK6Q.cjs.map
907
+ //# sourceMappingURL=chunk-WL3SZK6Q.cjs.map
@@ -0,0 +1 @@
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/ReviewMode.ts","../src/review/TriageMode.ts","../src/review/Review.ts"],"names":["__publicField"],"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,CAAA;AAyQtB,IAAM,mBAAA,GAAsB;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;;;ACvQnC,IAAM,QAAA,GAAW,4BAAA;AAEV,IAAM,aAAN,MAAiB;AAAA,EAKtB,WAAA,GAAc;AAJd,IAAAA,+BAAA,CAAA,IAAA,EAAQ,WAAA,CAAA;AACR,IAAAA,+BAAA,CAAA,IAAA,EAAQ,MAAA,sBAAqC,GAAA,EAAI,CAAA;AACjD,IAAAA,+BAAA,CAAA,IAAA,EAAQ,SAAA,EAAmC,IAAA,CAAA;AAGzC,IAAA,IAAA,CAAK,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC7C,IAAA,IAAA,CAAK,UAAU,SAAA,GAAY,eAAA;AAAA,EAC7B;AAAA,EAEA,KAAA,GAAc;AAEZ,IAAA,IAAI,CAAC,QAAA,CAAS,cAAA,CAAe,QAAQ,CAAA,EAAG;AACtC,MAAA,IAAA,CAAK,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC7C,MAAA,IAAA,CAAK,QAAQ,EAAA,GAAK,QAAA;AAClB,MAAA,IAAA,CAAK,QAAQ,WAAA,GAAc,mBAAA;AAC3B,MAAA,QAAA,CAAS,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,OAAO,CAAA;AAAA,IACxC;AAEA,IAAA,QAAA,CAAS,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,SAAS,CAAA;AAAA,EAC1C;AAAA,EAEA,MAAA,CAAO,SAAA,EAAmB,CAAA,EAAW,CAAA,EAAW,MAAA,EAAuB;AACrE,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACxC,IAAA,GAAA,CAAI,SAAA,GAAY,SAAS,YAAA,GAAe,8BAAA;AACxC,IAAA,GAAA,CAAI,WAAA,GAAc,OAAO,SAAS,CAAA;AAClC,IAAA,GAAA,CAAI,KAAA,CAAM,IAAA,GAAO,CAAA,EAAG,CAAC,CAAA,CAAA,CAAA;AACrB,IAAA,GAAA,CAAI,KAAA,CAAM,GAAA,GAAM,CAAA,EAAG,CAAC,CAAA,CAAA,CAAA;AACpB,IAAA,GAAA,CAAI,OAAA,CAAQ,SAAA,GAAY,MAAA,CAAO,SAAS,CAAA;AACxC,IAAA,IAAA,CAAK,SAAA,CAAU,YAAY,GAAG,CAAA;AAC9B,IAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,SAAA,EAAW,GAAG,CAAA;AAAA,EAC9B;AAAA,EAEA,UAAU,SAAA,EAAyB;AACjC,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,SAAS,CAAA;AACnC,IAAA,IAAI,GAAA,EAAK;AACP,MAAA,GAAA,CAAI,MAAA,EAAO;AACX,MAAA,IAAA,CAAK,IAAA,CAAK,OAAO,SAAS,CAAA;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,aAAa,SAAA,EAAyB;AACpC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,EAAE,CAAA,IAAK,KAAK,IAAA,EAAM;AACjC,MAAA,EAAA,CAAG,SAAA,CAAU,MAAA,CAAO,yBAAA,EAA2B,GAAA,KAAQ,SAAS,CAAA;AAAA,IAClE;AAAA,EACF;AAAA,EAEA,cAAA,GAAuB;AACrB,IAAA,KAAA,MAAW,EAAA,IAAM,IAAA,CAAK,IAAA,CAAK,MAAA,EAAO,EAAG;AACnC,MAAA,EAAA,CAAG,SAAA,CAAU,OAAO,yBAAyB,CAAA;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,UAAU,MAAA,EAAO;AACtB,IAAA,IAAA,CAAK,KAAK,KAAA,EAAM;AAGhB,IAAA,IAAI,KAAK,OAAA,EAAS;AAChB,MAAA,IAAA,CAAK,QAAQ,MAAA,EAAO;AACpB,MAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,IACjB,CAAA,MAAO;AACL,MAAA,QAAA,CAAS,cAAA,CAAe,QAAQ,CAAA,EAAG,MAAA,EAAO;AAAA,IAC5C;AAAA,EACF;AACF,CAAA;;;ACrEO,IAAM,cAAN,MAAkB;AAAA,EAKvB,YAAoB,UAAA,EAAwB;AAAxB,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AAJpB,IAAAA,+BAAA,CAAA,IAAA,EAAQ,MAAA,EAA2B,IAAA,CAAA;AACnC,IAAAA,+BAAA,CAAA,IAAA,EAAQ,UAAA,EAAqD,IAAA,CAAA;AAC7D,IAAAA,+BAAA,CAAA,IAAA,EAAQ,UAAA,EAAgC,IAAA,CAAA;AAAA,EAEK;AAAA,EAE7C,KAAK,OAAA,EAKI;AACP,IAAA,IAAA,CAAK,IAAA,EAAK;AACV,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AACxB,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AAExB,IAAA,IAAA,CAAK,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACxC,IAAA,IAAA,CAAK,KAAK,SAAA,GAAY,qBAAA;AAEtB,IAAA,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,CAAA,EAAG,MAAA,CAAO,UAAA,GAAa,GAAG,CAAC,CAAA,EAAA,CAAA;AACtE,IAAA,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,CAAA,GAAI,EAAA,EAAI,MAAA,CAAO,WAAA,GAAc,GAAG,CAAC,CAAA,EAAA,CAAA;AAE3E,IAAA,IAAA,CAAK,KAAK,SAAA,GAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAStB,IAAA,IAAA,CAAK,eAAA,EAAgB;AACrB,IAAA,IAAA,CAAK,UAAA,CAAW,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAErC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,aAAA,CAAc,UAAU,CAAA;AACnD,IAAA,QAAA,EAAU,KAAA,EAAM;AAAA,EAClB;AAAA,EAEA,IAAA,GAAa;AACX,IAAA,IAAA,CAAK,MAAM,MAAA,EAAO;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AAAA,EAEA,eAAe,OAAA,EAGN;AAEP,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,CAAA,EAAG,MAAA,CAAO,UAAA,GAAa,CAAA,GAAI,GAAA;AAAA,MAC3B,CAAA,EAAG,MAAA,CAAO,WAAA,GAAc,CAAA,GAAI,GAAA;AAAA,MAC5B,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,UAAU,OAAA,CAAQ;AAAA,KACnB,CAAA;AAAA,EACH;AAAA,EAEQ,eAAA,GAAwB;AAC9B,IAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AAEhB,IAAA,IAAA,CAAK,KAAK,aAAA,CAAc,qBAAqB,CAAA,EAAG,gBAAA,CAAiB,SAAS,MAAM;AAC9E,MAAA,IAAA,CAAK,QAAA,IAAW;AAChB,MAAA,IAAA,CAAK,IAAA,EAAK;AAAA,IACZ,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,CAAK,cAAc,qBAAqB,CAAA,EAAG,iBAAiB,OAAA,EAAS,MAAM,IAAA,CAAK,YAAA,EAAc,CAAA;AAEnG,IAAA,IAAA,CAAK,KAAK,aAAA,CAAc,UAAU,GAAG,gBAAA,CAAiB,SAAA,EAAW,CAAC,CAAA,KAAqB;AACrF,MAAA,IAAA,CAAK,EAAE,OAAA,IAAW,CAAA,CAAE,OAAA,KAAY,CAAA,CAAE,QAAQ,OAAA,EAAS;AACjD,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,IAAA,CAAK,YAAA,EAAa;AAAA,MACpB;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAc,YAAA,GAA8B;AAC1C,IAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AAChB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,aAAA,CAAc,UAAU,CAAA;AACnD,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,CAAM,IAAA,EAAK;AACjC,IAAA,IAAI,CAAC,IAAA,EAAM;AAEX,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,aAAA,CAAc,qBAAqB,CAAA;AAC/D,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,aAAA,CAAc,uBAAuB,CAAA;AAC/D,IAAA,SAAA,CAAU,QAAA,GAAW,IAAA;AACrB,IAAA,SAAA,CAAU,WAAA,GAAc,eAAA;AACxB,IAAA,QAAA,CAAS,QAAA,GAAW,IAAA;AACpB,IAAA,OAAA,CAAQ,MAAM,OAAA,GAAU,MAAA;AAExB,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,WAAW,IAAI,CAAA;AAC1B,MAAA,IAAA,CAAK,IAAA,EAAK;AAAA,IACZ,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,CAAU,QAAA,GAAW,KAAA;AACrB,MAAA,SAAA,CAAU,WAAA,GAAc,QAAA;AACxB,MAAA,QAAA,CAAS,QAAA,GAAW,KAAA;AACpB,MAAA,OAAA,CAAQ,WAAA,GAAc,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,kBAAA;AAC3D,MAAA,OAAA,CAAQ,MAAM,OAAA,GAAU,OAAA;AAAA,IAC1B;AAAA,EACF;AACF,CAAA;;;AC7FO,IAAM,aAAN,MAAiB;AAAA,EAStB,WAAA,CACU,KAAA,EACA,UAAA,EACA,QAAA,EACR;AAHQ,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACA,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAXV,IAAAA,+BAAA,CAAA,IAAA,EAAQ,YAAA,CAAA;AACR,IAAAA,+BAAA,CAAA,IAAA,EAAQ,aAAA,CAAA;AACR,IAAAA,+BAAA,CAAA,IAAA,EAAQ,eAAA,CAAA;AACR,IAAAA,+BAAA,CAAA,IAAA,EAAQ,kBAAA,EAAkC,IAAA,CAAA;AAC1C,IAAAA,+BAAA,CAAA,IAAA,EAAQ,UAAA,EAA+B,IAAA,CAAA;AACvC,IAAAA,+BAAA,CAAA,IAAA,EAAQ,WAAA,EAAgC,IAAA,CAAA;AACxC,IAAAA,+BAAA,CAAA,IAAA,EAAQ,cAAA,EAAiD,IAAA,CAAA;AAOvD,IAAA,IAAA,CAAK,UAAA,GAAa,IAAI,UAAA,EAAW;AACjC,IAAA,IAAA,CAAK,WAAA,GAAc,IAAI,WAAA,CAAY,UAAU,CAAA;AAC7C,IAAA,IAAA,CAAK,gBAAgB,QAAA,CAAS,aAAA;AAAA,EAChC;AAAA,EAEA,MAAM,IAAA,GAAsB;AAE1B,IAAA,QAAA,CAAS,IAAA,CAAK,MAAM,MAAA,GAAS,WAAA;AAG7B,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AAGtB,IAAA,IAAA,CAAK,UAAA,EAAW;AAGhB,IAAA,IAAA,CAAK,aAAA,EAAc;AAGnB,IAAA,MAAM,KAAK,gBAAA,EAAiB;AAG5B,IAAA,IAAA,CAAK,YAAA,GAAe,CAAC,CAAA,KAAkB,IAAA,CAAK,YAAY,CAAC,CAAA;AACzD,IAAA,QAAA,CAAS,gBAAA,CAAiB,OAAA,EAAS,IAAA,CAAK,YAAA,EAAc,IAAI,CAAA;AAAA,EAC5D;AAAA,EAEQ,UAAA,GAAmB;AACzB,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC5C,IAAA,IAAA,CAAK,SAAS,SAAA,GAAY,eAAA;AAC1B,IAAA,IAAA,CAAK,SAAS,SAAA,GAAY;AAAA;AAAA;AAAA,IAAA,CAAA;AAI1B,IAAA,IAAA,CAAK,UAAA,CAAW,WAAA,CAAY,IAAA,CAAK,QAAQ,CAAA;AAGzC,IAAA,UAAA,CAAW,MAAM,IAAA,CAAK,aAAA,EAAc,EAAG,GAAI,CAAA;AAAA,EAC7C;AAAA,EAEQ,aAAA,GAAsB;AAC5B,IAAA,IAAI,KAAK,QAAA,EAAU;AACjB,MAAA,IAAA,CAAK,SAAS,MAAA,EAAO;AACrB,MAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,IAClB;AAAA,EACF;AAAA,EAEQ,aAAA,GAAsB;AAC5B,IAAA,IAAA,CAAK,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC7C,IAAA,IAAA,CAAK,UAAU,SAAA,GAAY,gBAAA;AAC3B,IAAA,IAAA,CAAK,aAAA,EAAc;AACnB,IAAA,IAAA,CAAK,UAAA,CAAW,WAAA,CAAY,IAAA,CAAK,SAAS,CAAA;AAAA,EAC5C;AAAA,EAEQ,aAAA,GAAsB;AAC5B,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AACrB,IAAA,MAAM,QAAA,GAAW,KAAK,aAAA,GAAgB,CAAA;AACtC,IAAA,IAAA,CAAK,UAAU,SAAA,GAAY;AAAA,qCAAA,EACQ,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,IAAI,CAAA;AAAA,iCAAA,EAC9B,QAAQ,CAAA,IAAA,EAAO,QAAA,KAAa,CAAA,GAAI,MAAM,EAAE,CAAA;AAAA;AAAA,IAAA,CAAA;AAGvE,IAAA,IAAA,CAAK,UAAU,aAAA,CAAc,QAAQ,GAAG,gBAAA,CAAiB,OAAA,EAAS,CAAC,CAAA,KAAM;AACvE,MAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,MAAA,IAAA,CAAK,oBAAA,EAAqB;AAAA,IAC5B,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAc,gBAAA,GAAkC;AAC9C,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,aAAA,CAAc,IAAA,CAAK,KAAK,CAAA;AAC/C,MAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,QAAA,IAAI,CAAA,CAAE,SAAA,IAAa,IAAA,IAAQ,CAAA,CAAE,OAAA,EAAS;AAEpC,UAAA,MAAM,MAAA,GAAS,EAAE,WAAA,IAAe,IAAA;AAChC,UAAA,IAAA,CAAK,UAAA,CAAW,MAAA,CAAO,CAAA,CAAE,SAAA,EAAW,CAAA,CAAE,QAAQ,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,MAAM,CAAA;AAAA,QAC5E;AAAA,MACF;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,YAAY,CAAA,EAAqB;AAEvC,IAAA,MAAM,IAAA,GAAO,EAAE,YAAA,EAAa;AAC5B,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,YAAc,eAAe,EAAA,CAAG,OAAA,GAAU,qBAAqB,CAAC,CAAA,EAAG;AAGzF,IAAA,IAAI,IAAA,CAAK,oBAAoB,IAAA,EAAM;AAGnC,IAAA,IAAA,CAAK,aAAA,EAAc;AAEnB,IAAA,MAAM,OAAA,GAAU,qBAAA,CAAsB,CAAA,CAAE,OAAA,EAAS,EAAE,OAAO,CAAA;AAC1D,IAAA,MAAM,YAAY,IAAA,CAAK,aAAA;AAGvB,IAAA,IAAA,CAAK,WAAW,MAAA,CAAO,SAAA,EAAW,QAAQ,IAAA,EAAM,OAAA,CAAQ,MAAM,IAAI,CAAA;AAClE,IAAA,IAAA,CAAK,gBAAA,GAAmB,SAAA;AAGxB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK;AAAA,MACpB,GAAG,CAAA,CAAE,OAAA;AAAA,MACL,GAAG,CAAA,CAAE,OAAA;AAAA,MACL,QAAA,EAAU,OAAO,IAAA,KAAiB;AAChC,QAAA,MAAM,aAAA,CAAc,KAAK,KAAA,EAAO;AAAA,UAC9B,WAAA,EAAa,IAAA;AAAA,UACb,SAAA;AAAA,UACA,OAAA;AAAA,UACA,OAAA,EAAS,OAAO,QAAA,CAAS;AAAA,SAC1B,CAAA;AACD,QAAA,IAAA,CAAK,gBAAA,GAAmB,IAAA;AACxB,QAAA,IAAA,CAAK,aAAA,EAAA;AACL,QAAA,IAAA,CAAK,aAAA,EAAc;AACnB,QAAA,IAAA,CAAK,UAAU,eAAe,CAAA;AAAA,MAChC,CAAA;AAAA,MACA,UAAU,MAAM;AAEd,QAAA,IAAA,CAAK,UAAA,CAAW,UAAU,SAAS,CAAA;AACnC,QAAA,IAAA,CAAK,gBAAA,GAAmB,IAAA;AAAA,MAC1B;AAAA,KACD,CAAA;AAED,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,CAAA,CAAE,eAAA,EAAgB;AAAA,EACpB;AAAA,EAEQ,oBAAA,GAA6B;AACnC,IAAA,IAAI,IAAA,CAAK,oBAAoB,IAAA,EAAM;AAEnC,IAAA,IAAA,CAAK,YAAY,cAAA,CAAe;AAAA,MAC9B,QAAA,EAAU,OAAO,IAAA,KAAiB;AAChC,QAAA,MAAM,aAAA,CAAc,KAAK,KAAA,EAAO;AAAA,UAC9B,WAAA,EAAa,IAAA;AAAA,UACb,SAAA,EAAW,IAAA;AAAA,UACX,OAAA,EAAS,IAAA;AAAA,UACT,OAAA,EAAS,OAAO,QAAA,CAAS;AAAA,SAC1B,CAAA;AACD,QAAA,IAAA,CAAK,UAAU,eAAe,CAAA;AAAA,MAChC,CAAA;AAAA,MACA,UAAU,MAAM;AAAA,MAAC;AAAA,KAClB,CAAA;AAAA,EACH;AAAA,EAEQ,UAAU,OAAA,EAAuB;AACvC,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC1C,IAAA,KAAA,CAAM,SAAA,GAAY,cAAA;AAClB,IAAA,KAAA,CAAM,WAAA,GAAc,OAAA;AACpB,IAAA,IAAA,CAAK,UAAA,CAAW,YAAY,KAAK,CAAA;AACjC,IAAA,UAAA,CAAW,MAAM,KAAA,CAAM,MAAA,EAAO,EAAG,IAAI,CAAA;AAAA,EACvC;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,QAAA,CAAS,IAAA,CAAK,MAAM,MAAA,GAAS,EAAA;AAC7B,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,QAAA,CAAS,mBAAA,CAAoB,OAAA,EAAS,IAAA,CAAK,YAAA,EAAc,IAAI,CAAA;AAAA,IAC/D;AACA,IAAA,IAAA,CAAK,aAAA,EAAc;AACnB,IAAA,IAAA,CAAK,WAAW,MAAA,EAAO;AACvB,IAAA,IAAA,CAAK,YAAY,IAAA,EAAK;AACtB,IAAA,IAAA,CAAK,WAAW,OAAA,EAAQ;AAAA,EAC1B;AACF,CAAA;;;AClLO,IAAM,aAAN,MAAiB;AAAA,EAOtB,WAAA,CACU,OACA,UAAA,EACR;AAFQ,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACA,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AARV,IAAAA,+BAAA,CAAA,IAAA,EAAQ,YAAA,CAAA;AACR,IAAAA,+BAAA,CAAA,IAAA,EAAQ,WAAA,EAAgC,IAAA,CAAA;AACxC,IAAAA,+BAAA,CAAA,IAAA,EAAQ,WAAA,EAAgC,IAAA,CAAA;AACxC,IAAAA,+BAAA,CAAA,IAAA,EAAQ,YAA4B,EAAC,CAAA;AACrC,IAAAA,+BAAA,CAAA,IAAA,EAAQ,iBAAA,EAAoD,IAAA,CAAA;AAM1D,IAAA,IAAA,CAAK,UAAA,GAAa,IAAI,UAAA,EAAW;AAAA,EACnC;AAAA,EAEA,MAAM,IAAA,GAAsB;AAC1B,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AAGtB,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,QAAA,GAAW,MAAM,aAAA,CAAc,IAAA,CAAK,KAAK,CAAA;AAC9C,MAAA,KAAA,MAAW,CAAA,IAAK,KAAK,QAAA,EAAU;AAC7B,QAAA,IAAI,CAAA,CAAE,SAAA,IAAa,IAAA,IAAQ,CAAA,CAAE,OAAA,EAAS;AACpC,UAAA,IAAA,CAAK,UAAA,CAAW,MAAA,CAAO,CAAA,CAAE,SAAA,EAAW,CAAA,CAAE,QAAQ,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,QAC1E;AAAA,MACF;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAEA,IAAA,IAAA,CAAK,aAAA,EAAc;AAGnB,IAAA,IAAA,CAAK,eAAA,GAAkB,CAAC,CAAA,KAAkB;AACxC,MAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,MAAA,IAAI,MAAA,CAAO,SAAA,EAAW,QAAA,CAAS,YAAY,CAAA,EAAG;AAC5C,QAAA,MAAM,GAAA,GAAM,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,SAAS,CAAA;AAC3C,QAAA,IAAI,GAAA,EAAK,IAAA,CAAK,cAAA,CAAe,GAAA,EAAK,CAAC,CAAA;AAAA,MACrC;AAAA,IACF,CAAA;AACA,IAAA,QAAA,CAAS,gBAAA,CAAiB,OAAA,EAAS,IAAA,CAAK,eAAA,EAAiB,IAAI,CAAA;AAAA,EAC/D;AAAA,EAEQ,aAAA,GAAsB;AAC5B,IAAA,IAAA,CAAK,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC7C,IAAA,IAAA,CAAK,UAAU,SAAA,GAAY,gBAAA;AAC3B,IAAA,MAAM,KAAA,GAAQ,KAAK,QAAA,CAAS,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,SAAA,IAAa,IAAI,CAAA,CAAE,MAAA;AAC/D,IAAA,IAAA,CAAK,UAAU,SAAA,GAAY;AAAA;AAAA,iCAAA,EAEI,KAAK,CAAA,IAAA,EAAO,KAAA,KAAU,CAAA,GAAI,MAAM,EAAE,CAAA;AAAA,IAAA,CAAA;AAEjE,IAAA,IAAA,CAAK,UAAA,CAAW,WAAA,CAAY,IAAA,CAAK,SAAS,CAAA;AAAA,EAC5C;AAAA,EAEQ,cAAA,CAAe,WAAmB,CAAA,EAAqB;AAC7D,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,IAAA,CAAK,UAAA,CAAW,aAAa,SAAS,CAAA;AAEtC,IAAA,MAAM,OAAA,GAAU,KAAK,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,cAAc,SAAS,CAAA;AACnE,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,IAAA,CAAK,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC7C,IAAA,IAAA,CAAK,UAAU,SAAA,GAAY,gBAAA;AAE3B,IAAA,MAAM,OAAO,IAAI,IAAA,CAAK,OAAA,CAAQ,SAAS,EAAE,cAAA,EAAe;AACxD,IAAA,IAAA,CAAK,UAAU,SAAA,GAAY;AAAA,2DAAA,EAC8B,SAAS,CAAA;AAAA,sCAAA,EAC9B,OAAA,CAAQ,eAAe,WAAW,CAAA;AAAA,+CAAA,EACzB,IAAI,CAAA;AAAA,IAAA,CAAA;AAIjD,IAAA,IAAA,CAAK,SAAA,CAAU,MAAM,QAAA,GAAW,OAAA;AAChC,IAAA,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,OAAA,GAAU,EAAA,EAAI,MAAA,CAAO,UAAA,GAAa,GAAG,CAAC,CAAA,EAAA,CAAA;AAChF,IAAA,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,OAAA,GAAU,EAAA,EAAI,MAAA,CAAO,WAAA,GAAc,GAAG,CAAC,CAAA,EAAA,CAAA;AAChF,IAAA,IAAA,CAAK,UAAA,CAAW,WAAA,CAAY,IAAA,CAAK,SAAS,CAAA;AAG1C,IAAA,MAAM,OAAA,GAAU,CAAC,EAAA,KAAmB;AAClC,MAAA,IAAI,EAAA,CAAG,MAAA,KAAW,IAAA,CAAK,SAAA,IAAa,CAAC,KAAK,SAAA,EAAW,QAAA,CAAS,EAAA,CAAG,MAAc,CAAA,EAAG;AAChF,QAAA,IAAA,CAAK,WAAA,EAAY;AACjB,QAAA,IAAA,CAAK,WAAW,cAAA,EAAe;AAC/B,QAAA,QAAA,CAAS,mBAAA,CAAoB,OAAA,EAAS,OAAA,EAAS,IAAI,CAAA;AAAA,MACrD;AAAA,IACF,CAAA;AACA,IAAA,UAAA,CAAW,MAAM,QAAA,CAAS,gBAAA,CAAiB,SAAS,OAAA,EAAS,IAAI,GAAG,CAAC,CAAA;AAAA,EACvE;AAAA,EAEQ,WAAA,GAAoB;AAC1B,IAAA,IAAA,CAAK,WAAW,MAAA,EAAO;AACvB,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,EACnB;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,QAAA,CAAS,mBAAA,CAAoB,OAAA,EAAS,IAAA,CAAK,eAAA,EAAiB,IAAI,CAAA;AAAA,IAClE;AACA,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,IAAA,CAAK,WAAW,MAAA,EAAO;AACvB,IAAA,IAAA,CAAK,WAAW,OAAA,EAAQ;AAAA,EAC1B;AACF,CAAA;;;AClGO,IAAM,SAAN,MAAa;AAAA,EAMlB,WAAA,CAAY,QAAA,GAA0B,EAAC,EAAG;AAL1C,IAAAA,+BAAA,CAAA,IAAA,EAAQ,MAAA,CAAA;AACR,IAAAA,+BAAA,CAAA,IAAA,EAAQ,QAAA,CAAA;AACR,IAAAA,+BAAA,CAAA,IAAA,EAAQ,MAAA,EAAuC,IAAA,CAAA;AAC/C,IAAAA,+BAAA,CAAA,IAAA,EAAQ,cAAA,EAAe,KAAA,CAAA;AAGrB,IAAA,IAAA,CAAK,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACxC,IAAA,IAAA,CAAK,KAAK,EAAA,GAAK,oBAAA;AACf,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,IAAA,CAAK,aAAa,EAAE,IAAA,EAAM,QAAQ,CAAA;AAErD,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC9C,IAAA,OAAA,CAAQ,WAAA,GAAc,aAAA;AACtB,IAAA,IAAA,CAAK,MAAA,CAAO,YAAY,OAAO,CAAA;AAE/B,IAAA,QAAA,CAAS,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAAA,EACrC;AAAA,EAEA,MAAM,IAAA,GAAsB;AAC1B,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,MAAA,CAAO,SAAS,MAAM,CAAA;AACzD,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAA;AACvC,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAA;AAEvC,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,MAAM,aAAA,CAAc,WAAW,CAAA;AAC5C,QAAA,IAAA,CAAK,OAAO,IAAI,UAAA,CAAW,WAAA,EAAa,IAAA,CAAK,QAAQ,IAAI,CAAA;AACzD,QAAA,MAAM,IAAA,CAAK,KAAK,IAAA,EAAK;AAAA,MACvB,SAAS,GAAA,EAAK;AACZ,QAAA,IAAI,eAAe,WAAA,EAAa;AAC9B,UAAA,IAAA,CAAK,gBAAA;AAAA,YACH,GAAA,CAAI,IAAA,KAAS,SAAA,GACT,mCAAA,GACA;AAAA,WACN;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,WAAA,EAAa;AACtB,MAAA,IAAI;AACF,QAAA,MAAM,cAAc,WAAW,CAAA;AAC/B,QAAA,IAAA,CAAK,IAAA,GAAO,IAAI,UAAA,CAAW,WAAA,EAAa,KAAK,MAAM,CAAA;AACnD,QAAA,MAAM,IAAA,CAAK,KAAK,IAAA,EAAK;AAAA,MACvB,SAAS,GAAA,EAAK;AACZ,QAAA,IAAI,eAAe,WAAA,EAAa;AAC9B,UAAA,IAAA,CAAK,gBAAA;AAAA,YACH,GAAA,CAAI,IAAA,KAAS,SAAA,GACT,mCAAA,GACA;AAAA,WACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,iBAAiB,OAAA,EAAuB;AAC9C,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC5C,IAAA,OAAA,CAAQ,SAAA,GAAY,wBAAA;AACpB,IAAA,OAAA,CAAQ,SAAA,GAAY;AAAA;AAAA;AAAA,uDAAA,EAGiC,OAAO,CAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAI5D,IAAA,IAAA,CAAK,MAAA,CAAO,YAAY,OAAO,CAAA;AAG/B,IAAA,QAAA,CAAS,IAAA,CAAK,MAAM,MAAA,GAAS,gBAAA;AAAA,EAC/B;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,YAAA,EAAc;AACvB,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,IAAA,IAAA,CAAK,MAAM,OAAA,EAAQ;AACnB,IAAA,IAAA,CAAK,KAAK,MAAA,EAAO;AACjB,IAAA,QAAA,CAAS,IAAA,CAAK,MAAM,MAAA,GAAS,EAAA;AAAA,EAC/B;AACF","file":"chunk-WL3SZK6Q.cjs","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\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, HTMLElement> = 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 // Inject pin styles into document.head (pins live outside shadow DOM)\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): void {\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 this.container.appendChild(pin)\n this.pins.set(pinNumber, pin)\n }\n\n removePin(pinNumber: number): void {\n const pin = this.pins.get(pinNumber)\n if (pin) {\n pin.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 destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n // Clean up injected style element\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","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\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\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 // 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)\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)\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: window.location.pathname,\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: window.location.pathname,\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.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\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\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 // Fetch all comments\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)\n }\n }\n } catch {\n // Show error state\n }\n\n this.renderToolbar()\n\n // Listen for clicks on pins\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\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n const count = this.comments.filter((c) => c.pinNumber != null).length\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">Triage Mode</span>\n <span style=\"opacity:0.7;\">${count} pin${count !== 1 ? 's' : ''}</span>\n `\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\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 this.tooltipEl.innerHTML = `\n <div style=\"font-weight:500;margin-bottom:4px;\">Pin #${pinNumber}</div>\n <div style=\"margin-bottom:4px;\">${comment.commentText || '(no text)'}</div>\n <div style=\"font-size:11px;opacity:0.7;\">${time}</div>\n `\n\n // Position near pin\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(e.clientX + 16, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(e.clientY - 10, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // Click elsewhere to dismiss\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 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.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\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(): Promise<void> {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n\n if (reviewToken) {\n try {\n const data = await validateToken(reviewToken)\n this.mode = new ReviewMode(reviewToken, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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 (triageToken) {\n try {\n await validateToken(triageToken)\n this.mode = new TriageMode(triageToken, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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"]}
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunkKGQKTDB4_cjs = require('./chunk-KGQKTDB4.cjs');
4
4
  var chunkEKWWSLHU_cjs = require('./chunk-EKWWSLHU.cjs');
5
- var chunk46WI5367_cjs = require('./chunk-46WI5367.cjs');
5
+ var chunkWL3SZK6Q_cjs = require('./chunk-WL3SZK6Q.cjs');
6
6
  require('./chunk-4DE2IREA.cjs');
7
7
 
8
8
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -48,12 +48,12 @@ function destroy() {
48
48
 
49
49
  // src/index.ts
50
50
  if (typeof window !== "undefined") {
51
- window.OurRoadmaps = { init, destroy, createOverlay: chunkEKWWSLHU_cjs.createOverlay, Review: chunk46WI5367_cjs.Review };
51
+ window.OurRoadmaps = { init, destroy, createOverlay: chunkEKWWSLHU_cjs.createOverlay, Review: chunkWL3SZK6Q_cjs.Review };
52
52
  }
53
53
  if (typeof window !== "undefined") {
54
54
  const params = new URLSearchParams(window.location.search);
55
55
  if (params.has("review") || params.has("triage")) {
56
- const review = new chunk46WI5367_cjs.Review();
56
+ const review = new chunkWL3SZK6Q_cjs.Review();
57
57
  review.init();
58
58
  }
59
59
  }
@@ -68,7 +68,7 @@ Object.defineProperty(exports, "createOverlay", {
68
68
  });
69
69
  Object.defineProperty(exports, "Review", {
70
70
  enumerable: true,
71
- get: function () { return chunk46WI5367_cjs.Review; }
71
+ get: function () { return chunkWL3SZK6Q_cjs.Review; }
72
72
  });
73
73
  exports.destroy = destroy;
74
74
  exports.init = init;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Feedback } from './chunk-MKPDPQXT.js';
2
2
  import { createOverlay } from './chunk-JPUN7P3T.js';
3
3
  export { Overlay, createOverlay } from './chunk-JPUN7P3T.js';
4
- import { Review } from './chunk-HOJDCIWN.js';
5
- export { Review } from './chunk-HOJDCIWN.js';
4
+ import { Review } from './chunk-2Q3BAT55.js';
5
+ export { Review } from './chunk-2Q3BAT55.js';
6
6
  import './chunk-V6TY7KAL.js';
7
7
 
8
8
  // src/feedback/dev-mode.ts
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var chunk46WI5367_cjs = require('../chunk-46WI5367.cjs');
3
+ var chunkWL3SZK6Q_cjs = require('../chunk-WL3SZK6Q.cjs');
4
4
  require('../chunk-4DE2IREA.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "Review", {
9
9
  enumerable: true,
10
- get: function () { return chunk46WI5367_cjs.Review; }
10
+ get: function () { return chunkWL3SZK6Q_cjs.Review; }
11
11
  });
12
12
  //# sourceMappingURL=index.cjs.map
13
13
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- export { Review } from '../chunk-HOJDCIWN.js';
1
+ export { Review } from '../chunk-2Q3BAT55.js';
2
2
  import '../chunk-V6TY7KAL.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- "use strict";var OurRoadmaps=(()=>{var p=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var L=(n,e,t)=>e in n?p(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var N=(n,e)=>{for(var t in e)p(n,t,{get:e[t],enumerable:!0})},S=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of M(e))!P.call(n,o)&&o!==t&&p(n,o,{get:()=>e[o],enumerable:!(i=k(e,o))||i.enumerable});return n};var H=n=>S(p({},"__esModule",{value:!0}),n);var r=(n,e,t)=>L(n,typeof e!="symbol"?e+"":e,t);var _={};N(_,{Review:()=>v});var D={},b=(typeof D<"u","https://api.ourroadmaps.com");async function w(n){let e=await fetch(`${b}/v1/prototype-review/${n}`);if(e.status===410)throw new s("expired","This feedback session has expired");if(e.status===404)throw new s("invalid","This review link is not valid");if(!e.ok)throw new s("error","Something went wrong");return(await e.json()).data}async function x(n,e){let t=await fetch(`${b}/v1/prototype-review/${n}/comments`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok)throw new s("error","Failed to submit comment");return(await t.json()).data}async function m(n){let e=await fetch(`${b}/v1/prototype-review/${n}/comments`);if(!e.ok)throw new s("error","Failed to load comments");return(await e.json()).data}var s=class extends Error{constructor(t,i){super(i);this.code=t;this.name="ReviewError"}};function E(n,e){let t=document.elementFromPoint(n,e),i=document.documentElement,o=(n+window.scrollX)/i.scrollWidth*100,a=(e+window.scrollY)/i.scrollHeight*100;if(!t||t===document.body||t===i)return{pinX:o,pinY:a,element:{selector:"body",tag:"body",text:"",ariaLabel:null,className:"",boundingBox:{x:0,y:0,w:i.scrollWidth,h:i.scrollHeight}},context:{parentTag:"",parentText:"",grandparentTag:"",siblings:[],nearbyText:""},viewportWidth:window.innerWidth,viewportHeight:window.innerHeight};let l=t.getBoundingClientRect();return{pinX:o,pinY:a,element:{selector:R(t),tag:t.tagName.toLowerCase(),text:c(t).slice(0,200),ariaLabel:t.getAttribute("aria-label"),className:t.className&&typeof t.className=="string"?t.className:"",boundingBox:{x:Math.round(l.x),y:Math.round(l.y),w:Math.round(l.width),h:Math.round(l.height)}},context:{parentTag:t.parentElement?`${t.parentElement.tagName.toLowerCase()}${y(t.parentElement)}`:"",parentText:t.parentElement?c(t.parentElement).slice(0,100):"",grandparentTag:t.parentElement?.parentElement?`${t.parentElement.parentElement.tagName.toLowerCase()}${y(t.parentElement.parentElement)}`:"",siblings:$(t),nearbyText:z(t).slice(0,200)},viewportWidth:window.innerWidth,viewportHeight:window.innerHeight}}function R(n){let e=[],t=n;for(;t&&t!==document.body;){if(t.id){e.unshift(`#${t.id}`);break}let i=t.tagName.toLowerCase();if(t.className&&typeof t.className=="string"){let o=t.className.trim().split(/\s+/).slice(0,2).join(".");o&&(i+=`.${o}`)}e.unshift(i),t=t.parentElement}return e.join(" > ")}function c(n){let e="";for(let t of n.childNodes)t.nodeType===Node.TEXT_NODE&&(e+=t.textContent?.trim()||"");return e.trim()||n.textContent?.trim().slice(0,200)||""}function y(n){if(!n.className||typeof n.className!="string")return"";let e=n.className.trim().split(/\s+/).slice(0,2).join(".");return e?`.${e}`:""}function $(n){if(!n.parentElement)return[];let e=[];for(let t of n.parentElement.children){if(t===n)continue;let i=t.tagName.toLowerCase(),o=(t.textContent?.trim()||"").slice(0,50);if(e.push(o?`${i}:${o}`:i),e.length>=4)break}return e}function z(n){let e=n.parentElement,t=0;for(;e&&t<3;){let i=c(e);if(i&&i!==c(n))return i;e=e.parentElement,t++}return""}var T=`
1
+ "use strict";var OurRoadmaps=(()=>{var p=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var L=(n,e,t)=>e in n?p(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var N=(n,e)=>{for(var t in e)p(n,t,{get:e[t],enumerable:!0})},S=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of M(e))!P.call(n,o)&&o!==t&&p(n,o,{get:()=>e[o],enumerable:!(i=k(e,o))||i.enumerable});return n};var H=n=>S(p({},"__esModule",{value:!0}),n);var r=(n,e,t)=>L(n,typeof e!="symbol"?e+"":e,t);var _={};N(_,{Review:()=>f});var D={},b=(typeof D<"u","https://api.ourroadmaps.com");async function w(n){let e=await fetch(`${b}/v1/prototype-review/${n}`);if(e.status===410)throw new s("expired","This feedback session has expired");if(e.status===404)throw new s("invalid","This review link is not valid");if(!e.ok)throw new s("error","Something went wrong");return(await e.json()).data}async function x(n,e){let t=await fetch(`${b}/v1/prototype-review/${n}/comments`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok)throw new s("error","Failed to submit comment");return(await t.json()).data}async function m(n){let e=await fetch(`${b}/v1/prototype-review/${n}/comments`);if(!e.ok)throw new s("error","Failed to load comments");return(await e.json()).data}var s=class extends Error{constructor(t,i){super(i);this.code=t;this.name="ReviewError"}};function E(n,e){let t=document.elementFromPoint(n,e),i=document.documentElement,o=(n+window.scrollX)/i.scrollWidth*100,a=(e+window.scrollY)/i.scrollHeight*100;if(!t||t===document.body||t===i)return{pinX:o,pinY:a,element:{selector:"body",tag:"body",text:"",ariaLabel:null,className:"",boundingBox:{x:0,y:0,w:i.scrollWidth,h:i.scrollHeight}},context:{parentTag:"",parentText:"",grandparentTag:"",siblings:[],nearbyText:""},viewportWidth:window.innerWidth,viewportHeight:window.innerHeight};let l=t.getBoundingClientRect();return{pinX:o,pinY:a,element:{selector:R(t),tag:t.tagName.toLowerCase(),text:c(t).slice(0,200),ariaLabel:t.getAttribute("aria-label"),className:t.className&&typeof t.className=="string"?t.className:"",boundingBox:{x:Math.round(l.x),y:Math.round(l.y),w:Math.round(l.width),h:Math.round(l.height)}},context:{parentTag:t.parentElement?`${t.parentElement.tagName.toLowerCase()}${y(t.parentElement)}`:"",parentText:t.parentElement?c(t.parentElement).slice(0,100):"",grandparentTag:t.parentElement?.parentElement?`${t.parentElement.parentElement.tagName.toLowerCase()}${y(t.parentElement.parentElement)}`:"",siblings:$(t),nearbyText:z(t).slice(0,200)},viewportWidth:window.innerWidth,viewportHeight:window.innerHeight}}function R(n){let e=[],t=n;for(;t&&t!==document.body;){if(t.id){e.unshift(`#${t.id}`);break}let i=t.tagName.toLowerCase();if(t.className&&typeof t.className=="string"){let o=t.className.trim().split(/\s+/).slice(0,2).join(".");o&&(i+=`.${o}`)}e.unshift(i),t=t.parentElement}return e.join(" > ")}function c(n){let e="";for(let t of n.childNodes)t.nodeType===Node.TEXT_NODE&&(e+=t.textContent?.trim()||"");return e.trim()||n.textContent?.trim().slice(0,200)||""}function y(n){if(!n.className||typeof n.className!="string")return"";let e=n.className.trim().split(/\s+/).slice(0,2).join(".");return e?`.${e}`:""}function $(n){if(!n.parentElement)return[];let e=[];for(let t of n.parentElement.children){if(t===n)continue;let i=t.tagName.toLowerCase(),o=(t.textContent?.trim()||"").slice(0,50);if(e.push(o?`${i}:${o}`:i),e.length>=4)break}return e}function z(n){let e=n.parentElement,t=0;for(;e&&t<3;){let i=c(e);if(i&&i!==c(n))return i;e=e.parentElement,t++}return""}var T=`
2
2
  :host {
3
3
  all: initial;
4
4
  }
@@ -258,9 +258,16 @@
258
258
  }
259
259
  }
260
260
  `,C=`
261
+ body {
262
+ position: relative !important;
263
+ }
264
+
261
265
  .pin-container {
262
- position: fixed;
263
- inset: 0;
266
+ position: absolute;
267
+ top: 0;
268
+ left: 0;
269
+ width: 100%;
270
+ min-height: 100%;
264
271
  pointer-events: none;
265
272
  z-index: 10000;
266
273
  }
@@ -321,14 +328,14 @@
321
328
  <span style="font-weight:500;">${this.initData.session.name}</span>
322
329
  <span style="opacity:0.7;">${e} pin${e!==1?"s":""}</span>
323
330
  <button class="review-btn review-btn--submit" style="margin-left:auto;padding:6px 12px;font-size:13px;">General Comment</button>
324
- `,this.toolbarEl.querySelector("button")?.addEventListener("click",t=>{t.stopPropagation(),this.handleGeneralComment()})}async loadExistingPins(){try{let e=await m(this.token);for(let t of e)if(t.pinNumber!=null&&t.pinData){let i=t.commentText!=null;this.pinManager.addPin(t.pinNumber,t.pinData.pinX,t.pinData.pinY,i)}}catch{}}handleClick(e){if(e.composedPath().some(a=>a instanceof HTMLElement&&a.closest?.("#ourroadmaps-review"))||this.pendingPinNumber!=null)return;this.dismissPrompt();let i=E(e.clientX,e.clientY),o=this.nextPinNumber;this.pinManager.addPin(o,i.pinX,i.pinY,!0),this.pendingPinNumber=o,this.commentCard.show({x:e.clientX,y:e.clientY,onSubmit:async a=>{await x(this.token,{commentText:a,pinNumber:o,pinData:i,pageUrl:window.location.pathname}),this.pendingPinNumber=null,this.nextPinNumber++,this.updateToolbar(),this.showToast("Comment saved")},onCancel:()=>{this.pinManager.removePin(o),this.pendingPinNumber=null}}),e.preventDefault(),e.stopPropagation()}handleGeneralComment(){this.pendingPinNumber==null&&this.commentCard.showForGeneral({onSubmit:async e=>{await x(this.token,{commentText:e,pinNumber:null,pinData:null,pageUrl:window.location.pathname}),this.showToast("Comment saved")},onCancel:()=>{}})}showToast(e){let t=document.createElement("div");t.className="review-toast",t.textContent=e,this.shadowRoot.appendChild(t),setTimeout(()=>t.remove(),2500)}destroy(){document.body.style.cursor="",this.clickHandler&&document.removeEventListener("click",this.clickHandler,!0),this.dismissPrompt(),this.toolbarEl?.remove(),this.commentCard.hide(),this.pinManager.destroy()}};var f=class{constructor(e,t){this.token=e;this.shadowRoot=t;r(this,"pinManager");r(this,"toolbarEl",null);r(this,"tooltipEl",null);r(this,"comments",[]);r(this,"pinClickHandler",null);this.pinManager=new d}async init(){this.pinManager.mount();try{this.comments=await m(this.token);for(let e of this.comments)e.pinNumber!=null&&e.pinData&&this.pinManager.addPin(e.pinNumber,e.pinData.pinX,e.pinData.pinY,!0)}catch{}this.renderToolbar(),this.pinClickHandler=e=>{let t=e.target;if(t.classList?.contains("review-pin")){let i=Number(t.dataset.pinNumber);i&&this.handlePinClick(i,e)}},document.addEventListener("click",this.pinClickHandler,!0)}renderToolbar(){this.toolbarEl=document.createElement("div"),this.toolbarEl.className="review-toolbar";let e=this.comments.filter(t=>t.pinNumber!=null).length;this.toolbarEl.innerHTML=`
331
+ `,this.toolbarEl.querySelector("button")?.addEventListener("click",t=>{t.stopPropagation(),this.handleGeneralComment()})}async loadExistingPins(){try{let e=await m(this.token);for(let t of e)if(t.pinNumber!=null&&t.pinData){let i=t.commentText!=null;this.pinManager.addPin(t.pinNumber,t.pinData.pinX,t.pinData.pinY,i)}}catch{}}handleClick(e){if(e.composedPath().some(a=>a instanceof HTMLElement&&a.closest?.("#ourroadmaps-review"))||this.pendingPinNumber!=null)return;this.dismissPrompt();let i=E(e.clientX,e.clientY),o=this.nextPinNumber;this.pinManager.addPin(o,i.pinX,i.pinY,!0),this.pendingPinNumber=o,this.commentCard.show({x:e.clientX,y:e.clientY,onSubmit:async a=>{await x(this.token,{commentText:a,pinNumber:o,pinData:i,pageUrl:window.location.pathname}),this.pendingPinNumber=null,this.nextPinNumber++,this.updateToolbar(),this.showToast("Comment saved")},onCancel:()=>{this.pinManager.removePin(o),this.pendingPinNumber=null}}),e.preventDefault(),e.stopPropagation()}handleGeneralComment(){this.pendingPinNumber==null&&this.commentCard.showForGeneral({onSubmit:async e=>{await x(this.token,{commentText:e,pinNumber:null,pinData:null,pageUrl:window.location.pathname}),this.showToast("Comment saved")},onCancel:()=>{}})}showToast(e){let t=document.createElement("div");t.className="review-toast",t.textContent=e,this.shadowRoot.appendChild(t),setTimeout(()=>t.remove(),2500)}destroy(){document.body.style.cursor="",this.clickHandler&&document.removeEventListener("click",this.clickHandler,!0),this.dismissPrompt(),this.toolbarEl?.remove(),this.commentCard.hide(),this.pinManager.destroy()}};var v=class{constructor(e,t){this.token=e;this.shadowRoot=t;r(this,"pinManager");r(this,"toolbarEl",null);r(this,"tooltipEl",null);r(this,"comments",[]);r(this,"pinClickHandler",null);this.pinManager=new d}async init(){this.pinManager.mount();try{this.comments=await m(this.token);for(let e of this.comments)e.pinNumber!=null&&e.pinData&&this.pinManager.addPin(e.pinNumber,e.pinData.pinX,e.pinData.pinY,!0)}catch{}this.renderToolbar(),this.pinClickHandler=e=>{let t=e.target;if(t.classList?.contains("review-pin")){let i=Number(t.dataset.pinNumber);i&&this.handlePinClick(i,e)}},document.addEventListener("click",this.pinClickHandler,!0)}renderToolbar(){this.toolbarEl=document.createElement("div"),this.toolbarEl.className="review-toolbar";let e=this.comments.filter(t=>t.pinNumber!=null).length;this.toolbarEl.innerHTML=`
325
332
  <span style="font-weight:500;">Triage Mode</span>
326
333
  <span style="opacity:0.7;">${e} pin${e!==1?"s":""}</span>
327
334
  `,this.shadowRoot.appendChild(this.toolbarEl)}handlePinClick(e,t){this.hideTooltip(),this.pinManager.highlightPin(e);let i=this.comments.find(l=>l.pinNumber===e);if(!i)return;this.tooltipEl=document.createElement("div"),this.tooltipEl.className="review-tooltip";let o=new Date(i.createdAt).toLocaleString();this.tooltipEl.innerHTML=`
328
335
  <div style="font-weight:500;margin-bottom:4px;">Pin #${e}</div>
329
336
  <div style="margin-bottom:4px;">${i.commentText||"(no text)"}</div>
330
337
  <div style="font-size:11px;opacity:0.7;">${o}</div>
331
- `,this.tooltipEl.style.position="fixed",this.tooltipEl.style.left=`${Math.min(t.clientX+16,window.innerWidth-300)}px`,this.tooltipEl.style.top=`${Math.min(t.clientY-10,window.innerHeight-150)}px`,this.shadowRoot.appendChild(this.tooltipEl);let a=l=>{l.target!==this.tooltipEl&&!this.tooltipEl?.contains(l.target)&&(this.hideTooltip(),this.pinManager.clearHighlight(),document.removeEventListener("click",a,!0))};setTimeout(()=>document.addEventListener("click",a,!0),0)}hideTooltip(){this.tooltipEl?.remove(),this.tooltipEl=null}destroy(){this.pinClickHandler&&document.removeEventListener("click",this.pinClickHandler,!0),this.hideTooltip(),this.toolbarEl?.remove(),this.pinManager.destroy()}};var v=class{constructor(e={}){r(this,"root");r(this,"shadow");r(this,"mode",null);r(this,"_isDestroyed",!1);this.root=document.createElement("div"),this.root.id="ourroadmaps-review",this.shadow=this.root.attachShadow({mode:"open"});let t=document.createElement("style");t.textContent=T,this.shadow.appendChild(t),document.body.appendChild(this.root)}async init(){let e=new URLSearchParams(window.location.search),t=e.get("review"),i=e.get("triage");if(t)try{let o=await w(t);this.mode=new u(t,this.shadow,o),await this.mode.init()}catch(o){o instanceof s&&this.showErrorOverlay(o.code==="expired"?"This feedback session has expired":"This review link is no longer valid")}else if(i)try{await w(i),this.mode=new f(i,this.shadow),await this.mode.init()}catch(o){o instanceof s&&this.showErrorOverlay(o.code==="expired"?"This feedback session has expired":"This review link is no longer valid")}}showErrorOverlay(e){let t=document.createElement("div");t.className="review-expired-overlay",t.innerHTML=`
338
+ `,this.tooltipEl.style.position="fixed",this.tooltipEl.style.left=`${Math.min(t.clientX+16,window.innerWidth-300)}px`,this.tooltipEl.style.top=`${Math.min(t.clientY-10,window.innerHeight-150)}px`,this.shadowRoot.appendChild(this.tooltipEl);let a=l=>{l.target!==this.tooltipEl&&!this.tooltipEl?.contains(l.target)&&(this.hideTooltip(),this.pinManager.clearHighlight(),document.removeEventListener("click",a,!0))};setTimeout(()=>document.addEventListener("click",a,!0),0)}hideTooltip(){this.tooltipEl?.remove(),this.tooltipEl=null}destroy(){this.pinClickHandler&&document.removeEventListener("click",this.pinClickHandler,!0),this.hideTooltip(),this.toolbarEl?.remove(),this.pinManager.destroy()}};var f=class{constructor(e={}){r(this,"root");r(this,"shadow");r(this,"mode",null);r(this,"_isDestroyed",!1);this.root=document.createElement("div"),this.root.id="ourroadmaps-review",this.shadow=this.root.attachShadow({mode:"open"});let t=document.createElement("style");t.textContent=T,this.shadow.appendChild(t),document.body.appendChild(this.root)}async init(){let e=new URLSearchParams(window.location.search),t=e.get("review"),i=e.get("triage");if(t)try{let o=await w(t);this.mode=new u(t,this.shadow,o),await this.mode.init()}catch(o){o instanceof s&&this.showErrorOverlay(o.code==="expired"?"This feedback session has expired":"This review link is no longer valid")}else if(i)try{await w(i),this.mode=new v(i,this.shadow),await this.mode.init()}catch(o){o instanceof s&&this.showErrorOverlay(o.code==="expired"?"This feedback session has expired":"This review link is no longer valid")}}showErrorOverlay(e){let t=document.createElement("div");t.className="review-expired-overlay",t.innerHTML=`
332
339
  <div class="review-expired-card">
333
340
  <h2 style="margin:0 0 8px;font-size:18px;">Session Unavailable</h2>
334
341
  <p style="margin:0;color:#666;font-size:14px;">${e}</p>
@@ -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/ReviewMode.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 = `\n.pin-container {\n position: fixed;\n inset: 0;\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\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, HTMLElement> = 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 // Inject pin styles into document.head (pins live outside shadow DOM)\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): void {\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 this.container.appendChild(pin)\n this.pins.set(pinNumber, pin)\n }\n\n removePin(pinNumber: number): void {\n const pin = this.pins.get(pinNumber)\n if (pin) {\n pin.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 destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n // Clean up injected style element\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","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\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\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 // 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)\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)\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: window.location.pathname,\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: window.location.pathname,\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.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\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\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 // Fetch all comments\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)\n }\n }\n } catch {\n // Show error state\n }\n\n this.renderToolbar()\n\n // Listen for clicks on pins\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\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n const count = this.comments.filter((c) => c.pinNumber != null).length\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">Triage Mode</span>\n <span style=\"opacity:0.7;\">${count} pin${count !== 1 ? 's' : ''}</span>\n `\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\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 this.tooltipEl.innerHTML = `\n <div style=\"font-weight:500;margin-bottom:4px;\">Pin #${pinNumber}</div>\n <div style=\"margin-bottom:4px;\">${comment.commentText || '(no text)'}</div>\n <div style=\"font-size:11px;opacity:0.7;\">${time}</div>\n `\n\n // Position near pin\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(e.clientX + 16, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(e.clientY - 10, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // Click elsewhere to dismiss\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 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.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\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(): Promise<void> {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n\n if (reviewToken) {\n try {\n const data = await validateToken(reviewToken)\n this.mode = new ReviewMode(reviewToken, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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 (triageToken) {\n try {\n await validateToken(triageToken)\n this.mode = new TriageMode(triageToken, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyQhBC,EAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ECvQnC,IAAMC,EAAW,6BAEJC,EAAN,KAAiB,CAKtB,aAAc,CAJdC,EAAA,KAAQ,aACRA,EAAA,KAAQ,OAAiC,IAAI,KAC7CA,EAAA,KAAQ,UAAmC,MAGzC,KAAK,UAAY,SAAS,cAAc,KAAK,EAC7C,KAAK,UAAU,UAAY,eAC7B,CAEA,OAAc,CAEP,SAAS,eAAeF,CAAQ,IACnC,KAAK,QAAU,SAAS,cAAc,OAAO,EAC7C,KAAK,QAAQ,GAAKA,EAClB,KAAK,QAAQ,YAAcG,EAC3B,SAAS,KAAK,YAAY,KAAK,OAAO,GAGxC,SAAS,KAAK,YAAY,KAAK,SAAS,CAC1C,CAEA,OAAOC,EAAmBC,EAAWC,EAAWC,EAAuB,CACrE,IAAMC,EAAM,SAAS,cAAc,KAAK,EACxCA,EAAI,UAAYD,EAAS,aAAe,+BACxCC,EAAI,YAAc,OAAOJ,CAAS,EAClCI,EAAI,MAAM,KAAO,GAAGH,CAAC,IACrBG,EAAI,MAAM,IAAM,GAAGF,CAAC,IACpBE,EAAI,QAAQ,UAAY,OAAOJ,CAAS,EACxC,KAAK,UAAU,YAAYI,CAAG,EAC9B,KAAK,KAAK,IAAIJ,EAAWI,CAAG,CAC9B,CAEA,UAAUJ,EAAyB,CACjC,IAAMI,EAAM,KAAK,KAAK,IAAIJ,CAAS,EAC/BI,IACFA,EAAI,OAAO,EACX,KAAK,KAAK,OAAOJ,CAAS,EAE9B,CAEA,aAAaA,EAAyB,CACpC,OAAW,CAACK,EAAKC,CAAE,IAAK,KAAK,KAC3BA,EAAG,UAAU,OAAO,0BAA2BD,IAAQL,CAAS,CAEpE,CAEA,gBAAuB,CACrB,QAAWM,KAAM,KAAK,KAAK,OAAO,EAChCA,EAAG,UAAU,OAAO,yBAAyB,CAEjD,CAEA,SAAgB,CACd,KAAK,UAAU,OAAO,EACtB,KAAK,KAAK,MAAM,EAGZ,KAAK,SACP,KAAK,QAAQ,OAAO,EACpB,KAAK,QAAU,MAEf,SAAS,eAAeV,CAAQ,GAAG,OAAO,CAE9C,CACF,ECrEO,IAAMW,EAAN,KAAkB,CAKvB,YAAoBC,EAAwB,CAAxB,gBAAAA,EAJpBC,EAAA,KAAQ,OAA2B,MACnCA,EAAA,KAAQ,WAAqD,MAC7DA,EAAA,KAAQ,WAAgC,KAEK,CAE7C,KAAKC,EAKI,CACP,KAAK,KAAK,EACV,KAAK,SAAWA,EAAQ,SACxB,KAAK,SAAWA,EAAQ,SAExB,KAAK,KAAO,SAAS,cAAc,KAAK,EACxC,KAAK,KAAK,UAAY,sBAEtB,KAAK,KAAK,MAAM,KAAO,GAAG,KAAK,IAAIA,EAAQ,EAAG,OAAO,WAAa,GAAG,CAAC,KACtE,KAAK,KAAK,MAAM,IAAM,GAAG,KAAK,IAAIA,EAAQ,EAAI,GAAI,OAAO,YAAc,GAAG,CAAC,KAE3E,KAAK,KAAK,UAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAStB,KAAK,gBAAgB,EACrB,KAAK,WAAW,YAAY,KAAK,IAAI,EAEpB,KAAK,KAAK,cAAc,UAAU,GACzC,MAAM,CAClB,CAEA,MAAa,CACX,KAAK,MAAM,OAAO,EAClB,KAAK,KAAO,IACd,CAEA,eAAeA,EAGN,CAEP,KAAK,KAAK,CACR,EAAG,OAAO,WAAa,EAAI,IAC3B,EAAG,OAAO,YAAc,EAAI,IAC5B,SAAUA,EAAQ,SAClB,SAAUA,EAAQ,QACpB,CAAC,CACH,CAEQ,iBAAwB,CACzB,KAAK,OAEV,KAAK,KAAK,cAAc,qBAAqB,GAAG,iBAAiB,QAAS,IAAM,CAC9E,KAAK,WAAW,EAChB,KAAK,KAAK,CACZ,CAAC,EAED,KAAK,KAAK,cAAc,qBAAqB,GAAG,iBAAiB,QAAS,IAAM,KAAK,aAAa,CAAC,EAEnG,KAAK,KAAK,cAAc,UAAU,GAAG,iBAAiB,UAAY,GAAqB,EAChF,EAAE,SAAW,EAAE,UAAY,EAAE,MAAQ,UACxC,EAAE,eAAe,EACjB,KAAK,aAAa,EAEtB,CAAC,EACH,CAEA,MAAc,cAA8B,CAC1C,GAAI,CAAC,KAAK,KAAM,OAChB,IAAMC,EAAW,KAAK,KAAK,cAAc,UAAU,EAC7CC,EAAOD,EAAS,MAAM,KAAK,EACjC,GAAI,CAACC,EAAM,OAEX,IAAMC,EAAY,KAAK,KAAK,cAAc,qBAAqB,EACzDC,EAAU,KAAK,KAAK,cAAc,uBAAuB,EAC/DD,EAAU,SAAW,GACrBA,EAAU,YAAc,gBACxBF,EAAS,SAAW,GACpBG,EAAQ,MAAM,QAAU,OAExB,GAAI,CACF,MAAM,KAAK,WAAWF,CAAI,EAC1B,KAAK,KAAK,CACZ,OAASG,EAAK,CACZF,EAAU,SAAW,GACrBA,EAAU,YAAc,SACxBF,EAAS,SAAW,GACpBG,EAAQ,YAAcC,aAAe,MAAQA,EAAI,QAAU,mBAC3DD,EAAQ,MAAM,QAAU,OAC1B,CACF,CACF,EC7FO,IAAME,EAAN,KAAiB,CAStB,YACUC,EACAC,EACAC,EACR,CAHQ,WAAAF,EACA,gBAAAC,EACA,cAAAC,EAXVC,EAAA,KAAQ,cACRA,EAAA,KAAQ,eACRA,EAAA,KAAQ,iBACRA,EAAA,KAAQ,mBAAkC,MAC1CA,EAAA,KAAQ,WAA+B,MACvCA,EAAA,KAAQ,YAAgC,MACxCA,EAAA,KAAQ,eAAiD,MAOvD,KAAK,WAAa,IAAIC,EACtB,KAAK,YAAc,IAAIC,EAAYJ,CAAU,EAC7C,KAAK,cAAgBC,EAAS,aAChC,CAEA,MAAM,MAAsB,CAE1B,SAAS,KAAK,MAAM,OAAS,YAG7B,KAAK,WAAW,MAAM,EAGtB,KAAK,WAAW,EAGhB,KAAK,cAAc,EAGnB,MAAM,KAAK,iBAAiB,EAG5B,KAAK,aAAgB,GAAkB,KAAK,YAAY,CAAC,EACzD,SAAS,iBAAiB,QAAS,KAAK,aAAc,EAAI,CAC5D,CAEQ,YAAmB,CACzB,KAAK,SAAW,SAAS,cAAc,KAAK,EAC5C,KAAK,SAAS,UAAY,gBAC1B,KAAK,SAAS,UAAY;AAAA;AAAA;AAAA,MAI1B,KAAK,WAAW,YAAY,KAAK,QAAQ,EAGzC,WAAW,IAAM,KAAK,cAAc,EAAG,GAAI,CAC7C,CAEQ,eAAsB,CACxB,KAAK,WACP,KAAK,SAAS,OAAO,EACrB,KAAK,SAAW,KAEpB,CAEQ,eAAsB,CAC5B,KAAK,UAAY,SAAS,cAAc,KAAK,EAC7C,KAAK,UAAU,UAAY,iBAC3B,KAAK,cAAc,EACnB,KAAK,WAAW,YAAY,KAAK,SAAS,CAC5C,CAEQ,eAAsB,CAC5B,GAAI,CAAC,KAAK,UAAW,OACrB,IAAMI,EAAW,KAAK,cAAgB,EACtC,KAAK,UAAU,UAAY;AAAA,uCACQ,KAAK,SAAS,QAAQ,IAAI;AAAA,mCAC9BA,CAAQ,OAAOA,IAAa,EAAI,IAAM,EAAE;AAAA;AAAA,MAGvE,KAAK,UAAU,cAAc,QAAQ,GAAG,iBAAiB,QAAUC,GAAM,CACvEA,EAAE,gBAAgB,EAClB,KAAK,qBAAqB,CAC5B,CAAC,CACH,CAEA,MAAc,kBAAkC,CAC9C,GAAI,CACF,IAAMC,EAAW,MAAMC,EAAc,KAAK,KAAK,EAC/C,QAAWC,KAAKF,EACd,GAAIE,EAAE,WAAa,MAAQA,EAAE,QAAS,CAEpC,IAAMC,EAASD,EAAE,aAAe,KAChC,KAAK,WAAW,OAAOA,EAAE,UAAWA,EAAE,QAAQ,KAAMA,EAAE,QAAQ,KAAMC,CAAM,CAC5E,CAEJ,MAAQ,CAER,CACF,CAEQ,YAAY,EAAqB,CAMvC,GAJa,EAAE,aAAa,EACnB,KAAMC,GAAOA,aAAc,aAAeA,EAAG,UAAU,qBAAqB,CAAC,GAGlF,KAAK,kBAAoB,KAAM,OAGnC,KAAK,cAAc,EAEnB,IAAMC,EAAUC,EAAsB,EAAE,QAAS,EAAE,OAAO,EACpDC,EAAY,KAAK,cAGvB,KAAK,WAAW,OAAOA,EAAWF,EAAQ,KAAMA,EAAQ,KAAM,EAAI,EAClE,KAAK,iBAAmBE,EAGxB,KAAK,YAAY,KAAK,CACpB,EAAG,EAAE,QACL,EAAG,EAAE,QACL,SAAU,MAAOC,GAAiB,CAChC,MAAMC,EAAc,KAAK,MAAO,CAC9B,YAAaD,EACb,UAAAD,EACA,QAAAF,EACA,QAAS,OAAO,SAAS,QAC3B,CAAC,EACD,KAAK,iBAAmB,KACxB,KAAK,gBACL,KAAK,cAAc,EACnB,KAAK,UAAU,eAAe,CAChC,EACA,SAAU,IAAM,CAEd,KAAK,WAAW,UAAUE,CAAS,EACnC,KAAK,iBAAmB,IAC1B,CACF,CAAC,EAED,EAAE,eAAe,EACjB,EAAE,gBAAgB,CACpB,CAEQ,sBAA6B,CAC/B,KAAK,kBAAoB,MAE7B,KAAK,YAAY,eAAe,CAC9B,SAAU,MAAOC,GAAiB,CAChC,MAAMC,EAAc,KAAK,MAAO,CAC9B,YAAaD,EACb,UAAW,KACX,QAAS,KACT,QAAS,OAAO,SAAS,QAC3B,CAAC,EACD,KAAK,UAAU,eAAe,CAChC,EACA,SAAU,IAAM,CAAC,CACnB,CAAC,CACH,CAEQ,UAAUE,EAAuB,CACvC,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,eAClBA,EAAM,YAAcD,EACpB,KAAK,WAAW,YAAYC,CAAK,EACjC,WAAW,IAAMA,EAAM,OAAO,EAAG,IAAI,CACvC,CAEA,SAAgB,CACd,SAAS,KAAK,MAAM,OAAS,GACzB,KAAK,cACP,SAAS,oBAAoB,QAAS,KAAK,aAAc,EAAI,EAE/D,KAAK,cAAc,EACnB,KAAK,WAAW,OAAO,EACvB,KAAK,YAAY,KAAK,EACtB,KAAK,WAAW,QAAQ,CAC1B,CACF,EClLO,IAAMC,EAAN,KAAiB,CAOtB,YACUC,EACAC,EACR,CAFQ,WAAAD,EACA,gBAAAC,EARVC,EAAA,KAAQ,cACRA,EAAA,KAAQ,YAAgC,MACxCA,EAAA,KAAQ,YAAgC,MACxCA,EAAA,KAAQ,WAA4B,CAAC,GACrCA,EAAA,KAAQ,kBAAoD,MAM1D,KAAK,WAAa,IAAIC,CACxB,CAEA,MAAM,MAAsB,CAC1B,KAAK,WAAW,MAAM,EAGtB,GAAI,CACF,KAAK,SAAW,MAAMC,EAAc,KAAK,KAAK,EAC9C,QAAWC,KAAK,KAAK,SACfA,EAAE,WAAa,MAAQA,EAAE,SAC3B,KAAK,WAAW,OAAOA,EAAE,UAAWA,EAAE,QAAQ,KAAMA,EAAE,QAAQ,KAAM,EAAI,CAG9E,MAAQ,CAER,CAEA,KAAK,cAAc,EAGnB,KAAK,gBAAmB,GAAkB,CACxC,IAAMC,EAAS,EAAE,OACjB,GAAIA,EAAO,WAAW,SAAS,YAAY,EAAG,CAC5C,IAAMC,EAAM,OAAOD,EAAO,QAAQ,SAAS,EACvCC,GAAK,KAAK,eAAeA,EAAK,CAAC,CACrC,CACF,EACA,SAAS,iBAAiB,QAAS,KAAK,gBAAiB,EAAI,CAC/D,CAEQ,eAAsB,CAC5B,KAAK,UAAY,SAAS,cAAc,KAAK,EAC7C,KAAK,UAAU,UAAY,iBAC3B,IAAMC,EAAQ,KAAK,SAAS,OAAQH,GAAMA,EAAE,WAAa,IAAI,EAAE,OAC/D,KAAK,UAAU,UAAY;AAAA;AAAA,mCAEIG,CAAK,OAAOA,IAAU,EAAI,IAAM,EAAE;AAAA,MAEjE,KAAK,WAAW,YAAY,KAAK,SAAS,CAC5C,CAEQ,eAAeC,EAAmBC,EAAqB,CAC7D,KAAK,YAAY,EACjB,KAAK,WAAW,aAAaD,CAAS,EAEtC,IAAME,EAAU,KAAK,SAAS,KAAMN,GAAMA,EAAE,YAAcI,CAAS,EACnE,GAAI,CAACE,EAAS,OAEd,KAAK,UAAY,SAAS,cAAc,KAAK,EAC7C,KAAK,UAAU,UAAY,iBAE3B,IAAMC,EAAO,IAAI,KAAKD,EAAQ,SAAS,EAAE,eAAe,EACxD,KAAK,UAAU,UAAY;AAAA,6DAC8BF,CAAS;AAAA,wCAC9BE,EAAQ,aAAe,WAAW;AAAA,iDACzBC,CAAI;AAAA,MAIjD,KAAK,UAAU,MAAM,SAAW,QAChC,KAAK,UAAU,MAAM,KAAO,GAAG,KAAK,IAAIF,EAAE,QAAU,GAAI,OAAO,WAAa,GAAG,CAAC,KAChF,KAAK,UAAU,MAAM,IAAM,GAAG,KAAK,IAAIA,EAAE,QAAU,GAAI,OAAO,YAAc,GAAG,CAAC,KAChF,KAAK,WAAW,YAAY,KAAK,SAAS,EAG1C,IAAMG,EAAWC,GAAmB,CAC9BA,EAAG,SAAW,KAAK,WAAa,CAAC,KAAK,WAAW,SAASA,EAAG,MAAc,IAC7E,KAAK,YAAY,EACjB,KAAK,WAAW,eAAe,EAC/B,SAAS,oBAAoB,QAASD,EAAS,EAAI,EAEvD,EACA,WAAW,IAAM,SAAS,iBAAiB,QAASA,EAAS,EAAI,EAAG,CAAC,CACvE,CAEQ,aAAoB,CAC1B,KAAK,WAAW,OAAO,EACvB,KAAK,UAAY,IACnB,CAEA,SAAgB,CACV,KAAK,iBACP,SAAS,oBAAoB,QAAS,KAAK,gBAAiB,EAAI,EAElE,KAAK,YAAY,EACjB,KAAK,WAAW,OAAO,EACvB,KAAK,WAAW,QAAQ,CAC1B,CACF,EClGO,IAAME,EAAN,KAAa,CAMlB,YAAYC,EAA0B,CAAC,EAAG,CAL1CC,EAAA,KAAQ,QACRA,EAAA,KAAQ,UACRA,EAAA,KAAQ,OAAuC,MAC/CA,EAAA,KAAQ,eAAe,IAGrB,KAAK,KAAO,SAAS,cAAc,KAAK,EACxC,KAAK,KAAK,GAAK,qBACf,KAAK,OAAS,KAAK,KAAK,aAAa,CAAE,KAAM,MAAO,CAAC,EAErD,IAAMC,EAAU,SAAS,cAAc,OAAO,EAC9CA,EAAQ,YAAcC,EACtB,KAAK,OAAO,YAAYD,CAAO,EAE/B,SAAS,KAAK,YAAY,KAAK,IAAI,CACrC,CAEA,MAAM,MAAsB,CAC1B,IAAME,EAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM,EACnDC,EAAcD,EAAO,IAAI,QAAQ,EACjCE,EAAcF,EAAO,IAAI,QAAQ,EAEvC,GAAIC,EACF,GAAI,CACF,IAAME,EAAO,MAAMC,EAAcH,CAAW,EAC5C,KAAK,KAAO,IAAII,EAAWJ,EAAa,KAAK,OAAQE,CAAI,EACzD,MAAM,KAAK,KAAK,KAAK,CACvB,OAASG,EAAK,CACRA,aAAeC,GACjB,KAAK,iBACHD,EAAI,OAAS,UACT,oCACA,qCACN,CAEJ,SACSJ,EACT,GAAI,CACF,MAAME,EAAcF,CAAW,EAC/B,KAAK,KAAO,IAAIM,EAAWN,EAAa,KAAK,MAAM,EACnD,MAAM,KAAK,KAAK,KAAK,CACvB,OAASI,EAAK,CACRA,aAAeC,GACjB,KAAK,iBACHD,EAAI,OAAS,UACT,oCACA,qCACN,CAEJ,CAEJ,CAEQ,iBAAiBG,EAAuB,CAC9C,IAAMC,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,yBACpBA,EAAQ,UAAY;AAAA;AAAA;AAAA,yDAGiCD,CAAO;AAAA;AAAA;AAAA,MAI5D,KAAK,OAAO,YAAYC,CAAO,EAG/B,SAAS,KAAK,MAAM,OAAS,gBAC/B,CAEA,SAAgB,CACV,KAAK,eACT,KAAK,aAAe,GACpB,KAAK,MAAM,QAAQ,EACnB,KAAK,KAAK,OAAO,EACjB,SAAS,KAAK,MAAM,OAAS,GAC/B,CACF","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","pin","num","el","CommentCard","shadowRoot","__publicField","options","textarea","text","submitBtn","errorEl","err","ReviewMode","token","shadowRoot","initData","__publicField","PinManager","CommentCard","pinCount","e","comments","fetchComments","c","isMine","el","pinData","captureElementContext","pinNumber","text","submitComment","message","toast","TriageMode","token","shadowRoot","__publicField","PinManager","fetchComments","c","target","num","count","pinNumber","e","comment","time","dismiss","ev","Review","_options","__publicField","styleEl","REVIEW_STYLES","params","reviewToken","triageToken","data","validateToken","ReviewMode","err","ReviewError","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/ReviewMode.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\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, HTMLElement> = 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 // Inject pin styles into document.head (pins live outside shadow DOM)\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): void {\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 this.container.appendChild(pin)\n this.pins.set(pinNumber, pin)\n }\n\n removePin(pinNumber: number): void {\n const pin = this.pins.get(pinNumber)\n if (pin) {\n pin.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 destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n // Clean up injected style element\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","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\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\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 // 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)\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)\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: window.location.pathname,\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: window.location.pathname,\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.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\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\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 // Fetch all comments\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)\n }\n }\n } catch {\n // Show error state\n }\n\n this.renderToolbar()\n\n // Listen for clicks on pins\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\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n const count = this.comments.filter((c) => c.pinNumber != null).length\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">Triage Mode</span>\n <span style=\"opacity:0.7;\">${count} pin${count !== 1 ? 's' : ''}</span>\n `\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\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 this.tooltipEl.innerHTML = `\n <div style=\"font-weight:500;margin-bottom:4px;\">Pin #${pinNumber}</div>\n <div style=\"margin-bottom:4px;\">${comment.commentText || '(no text)'}</div>\n <div style=\"font-size:11px;opacity:0.7;\">${time}</div>\n `\n\n // Position near pin\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(e.clientX + 16, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(e.clientY - 10, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // Click elsewhere to dismiss\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 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.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\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(): Promise<void> {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n\n if (reviewToken) {\n try {\n const data = await validateToken(reviewToken)\n this.mode = new ReviewMode(reviewToken, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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 (triageToken) {\n try {\n await validateToken(triageToken)\n this.mode = new TriageMode(triageToken, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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","pin","num","el","CommentCard","shadowRoot","__publicField","options","textarea","text","submitBtn","errorEl","err","ReviewMode","token","shadowRoot","initData","__publicField","PinManager","CommentCard","pinCount","e","comments","fetchComments","c","isMine","el","pinData","captureElementContext","pinNumber","text","submitComment","message","toast","TriageMode","token","shadowRoot","__publicField","PinManager","fetchComments","c","target","num","count","pinNumber","e","comment","time","dismiss","ev","Review","_options","__publicField","styleEl","REVIEW_STYLES","params","reviewToken","triageToken","data","validateToken","ReviewMode","err","ReviewError","TriageMode","message","overlay"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ourroadmaps/web-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.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/ReviewMode.ts","../src/review/TriageMode.ts","../src/review/Review.ts"],"names":["__publicField"],"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,CAAA;AAyQtB,IAAM,mBAAA,GAAsB;AAAA;A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","file":"chunk-46WI5367.cjs","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 = `\n.pin-container {\n position: fixed;\n inset: 0;\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\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, HTMLElement> = 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 // Inject pin styles into document.head (pins live outside shadow DOM)\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): void {\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 this.container.appendChild(pin)\n this.pins.set(pinNumber, pin)\n }\n\n removePin(pinNumber: number): void {\n const pin = this.pins.get(pinNumber)\n if (pin) {\n pin.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 destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n // Clean up injected style element\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","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\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\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 // 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)\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)\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: window.location.pathname,\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: window.location.pathname,\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.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\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\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 // Fetch all comments\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)\n }\n }\n } catch {\n // Show error state\n }\n\n this.renderToolbar()\n\n // Listen for clicks on pins\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\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n const count = this.comments.filter((c) => c.pinNumber != null).length\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">Triage Mode</span>\n <span style=\"opacity:0.7;\">${count} pin${count !== 1 ? 's' : ''}</span>\n `\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\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 this.tooltipEl.innerHTML = `\n <div style=\"font-weight:500;margin-bottom:4px;\">Pin #${pinNumber}</div>\n <div style=\"margin-bottom:4px;\">${comment.commentText || '(no text)'}</div>\n <div style=\"font-size:11px;opacity:0.7;\">${time}</div>\n `\n\n // Position near pin\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(e.clientX + 16, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(e.clientY - 10, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // Click elsewhere to dismiss\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 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.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\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(): Promise<void> {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n\n if (reviewToken) {\n try {\n const data = await validateToken(reviewToken)\n this.mode = new ReviewMode(reviewToken, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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 (triageToken) {\n try {\n await validateToken(triageToken)\n this.mode = new TriageMode(triageToken, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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"]}
@@ -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/ReviewMode.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-HOJDCIWN.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 = `\n.pin-container {\n position: fixed;\n inset: 0;\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\nexport class PinManager {\n private container: HTMLDivElement\n private pins: Map<number, HTMLElement> = 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 // Inject pin styles into document.head (pins live outside shadow DOM)\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): void {\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 this.container.appendChild(pin)\n this.pins.set(pinNumber, pin)\n }\n\n removePin(pinNumber: number): void {\n const pin = this.pins.get(pinNumber)\n if (pin) {\n pin.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 destroy(): void {\n this.container.remove()\n this.pins.clear()\n\n // Clean up injected style element\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","import { submitComment, fetchComments } from './api'\nimport { captureElementContext } from './ElementCapture'\nimport { PinManager } from './PinManager'\nimport { CommentCard } from './CommentCard'\nimport type { ReviewInitData } from './types'\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\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 // 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)\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)\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: window.location.pathname,\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: window.location.pathname,\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.dismissPrompt()\n this.toolbarEl?.remove()\n this.commentCard.hide()\n this.pinManager.destroy()\n }\n}\n","import { fetchComments } from './api'\nimport { PinManager } from './PinManager'\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\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 // Fetch all comments\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)\n }\n }\n } catch {\n // Show error state\n }\n\n this.renderToolbar()\n\n // Listen for clicks on pins\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\n private renderToolbar(): void {\n this.toolbarEl = document.createElement('div')\n this.toolbarEl.className = 'review-toolbar'\n const count = this.comments.filter((c) => c.pinNumber != null).length\n this.toolbarEl.innerHTML = `\n <span style=\"font-weight:500;\">Triage Mode</span>\n <span style=\"opacity:0.7;\">${count} pin${count !== 1 ? 's' : ''}</span>\n `\n this.shadowRoot.appendChild(this.toolbarEl)\n }\n\n private handlePinClick(pinNumber: number, e: MouseEvent): void {\n this.hideTooltip()\n this.pinManager.highlightPin(pinNumber)\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 this.tooltipEl.innerHTML = `\n <div style=\"font-weight:500;margin-bottom:4px;\">Pin #${pinNumber}</div>\n <div style=\"margin-bottom:4px;\">${comment.commentText || '(no text)'}</div>\n <div style=\"font-size:11px;opacity:0.7;\">${time}</div>\n `\n\n // Position near pin\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(e.clientX + 16, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(e.clientY - 10, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // Click elsewhere to dismiss\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 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.hideTooltip()\n this.toolbarEl?.remove()\n this.pinManager.destroy()\n }\n}\n","import { validateToken, ReviewError } from './api'\nimport { ReviewMode } from './ReviewMode'\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(): Promise<void> {\n const params = new URLSearchParams(window.location.search)\n const reviewToken = params.get('review')\n const triageToken = params.get('triage')\n\n if (reviewToken) {\n try {\n const data = await validateToken(reviewToken)\n this.mode = new ReviewMode(reviewToken, this.shadow, data)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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 (triageToken) {\n try {\n await validateToken(triageToken)\n this.mode = new TriageMode(triageToken, this.shadow)\n await this.mode.init()\n } catch (err) {\n if (err instanceof ReviewError) {\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"]}