@ourroadmaps/web-sdk 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WL3SZK6Q.cjs → chunk-M2R3KO66.cjs} +51 -10
- package/dist/chunk-M2R3KO66.cjs.map +1 -0
- package/dist/{chunk-2Q3BAT55.js → chunk-OHL77RGU.js} +50 -9
- package/dist/chunk-OHL77RGU.js.map +1 -0
- package/dist/index.cjs +4 -4
- package/dist/index.js +2 -2
- package/dist/review/index.cjs +2 -2
- package/dist/review/index.js +1 -1
- package/dist/review.global.js +7 -11
- package/dist/review.global.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2Q3BAT55.js.map +0 -1
- package/dist/chunk-WL3SZK6Q.cjs.map +0 -1
|
@@ -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-
|
|
8
|
+
if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-M2R3KO66.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";
|
|
@@ -784,6 +784,7 @@ var TriageMode = class {
|
|
|
784
784
|
}
|
|
785
785
|
};
|
|
786
786
|
document.addEventListener("click", this.pinClickHandler, true);
|
|
787
|
+
this.autoFocusPin();
|
|
787
788
|
}
|
|
788
789
|
renderToolbar() {
|
|
789
790
|
this.toolbarEl = document.createElement("div");
|
|
@@ -798,19 +799,38 @@ var TriageMode = class {
|
|
|
798
799
|
handlePinClick(pinNumber, e) {
|
|
799
800
|
this.hideTooltip();
|
|
800
801
|
this.pinManager.highlightPin(pinNumber);
|
|
802
|
+
this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10);
|
|
803
|
+
}
|
|
804
|
+
showTooltipForPin(pinNumber, x, y) {
|
|
805
|
+
this.hideTooltip();
|
|
801
806
|
const comment = this.comments.find((c) => c.pinNumber === pinNumber);
|
|
802
807
|
if (!comment) return;
|
|
803
808
|
this.tooltipEl = document.createElement("div");
|
|
804
809
|
this.tooltipEl.className = "review-tooltip";
|
|
805
810
|
const time = new Date(comment.createdAt).toLocaleString();
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
+
const header = document.createElement("div");
|
|
812
|
+
header.style.cssText = "font-weight:500;margin-bottom:4px;";
|
|
813
|
+
header.textContent = `Pin #${pinNumber}`;
|
|
814
|
+
this.tooltipEl.appendChild(header);
|
|
815
|
+
if (comment.reviewerName) {
|
|
816
|
+
const name = document.createElement("div");
|
|
817
|
+
name.style.cssText = "font-size:11px;opacity:0.7;margin-bottom:2px;";
|
|
818
|
+
name.textContent = comment.reviewerName;
|
|
819
|
+
this.tooltipEl.appendChild(name);
|
|
820
|
+
}
|
|
821
|
+
const text = document.createElement("div");
|
|
822
|
+
text.style.cssText = "margin-bottom:4px;";
|
|
823
|
+
text.textContent = comment.commentText || "(no text)";
|
|
824
|
+
this.tooltipEl.appendChild(text);
|
|
825
|
+
const timeEl = document.createElement("div");
|
|
826
|
+
timeEl.style.cssText = "font-size:11px;opacity:0.7;";
|
|
827
|
+
timeEl.textContent = time;
|
|
828
|
+
this.tooltipEl.appendChild(timeEl);
|
|
829
|
+
const posX = x ?? window.innerWidth / 2;
|
|
830
|
+
const posY = y ?? window.innerHeight / 3;
|
|
811
831
|
this.tooltipEl.style.position = "fixed";
|
|
812
|
-
this.tooltipEl.style.left = `${Math.min(
|
|
813
|
-
this.tooltipEl.style.top = `${Math.min(
|
|
832
|
+
this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`;
|
|
833
|
+
this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`;
|
|
814
834
|
this.shadowRoot.appendChild(this.tooltipEl);
|
|
815
835
|
const dismiss = (ev) => {
|
|
816
836
|
if (ev.target !== this.tooltipEl && !this.tooltipEl?.contains(ev.target)) {
|
|
@@ -821,6 +841,27 @@ var TriageMode = class {
|
|
|
821
841
|
};
|
|
822
842
|
setTimeout(() => document.addEventListener("click", dismiss, true), 0);
|
|
823
843
|
}
|
|
844
|
+
autoFocusPin() {
|
|
845
|
+
const params = new URLSearchParams(window.location.search);
|
|
846
|
+
const pinParam = params.get("pin");
|
|
847
|
+
if (!pinParam) return;
|
|
848
|
+
const pinNumber = Number(pinParam);
|
|
849
|
+
if (!pinNumber) return;
|
|
850
|
+
const comment = this.comments.find((c) => c.pinNumber === pinNumber);
|
|
851
|
+
if (!comment?.pinData) return;
|
|
852
|
+
const docEl = document.documentElement;
|
|
853
|
+
const scrollX = comment.pinData.pinX / 100 * docEl.scrollWidth;
|
|
854
|
+
const scrollY = comment.pinData.pinY / 100 * docEl.scrollHeight;
|
|
855
|
+
window.scrollTo({
|
|
856
|
+
left: scrollX - window.innerWidth / 2,
|
|
857
|
+
top: scrollY - window.innerHeight / 2,
|
|
858
|
+
behavior: "smooth"
|
|
859
|
+
});
|
|
860
|
+
setTimeout(() => {
|
|
861
|
+
this.pinManager.highlightPin(pinNumber);
|
|
862
|
+
this.showTooltipForPin(pinNumber);
|
|
863
|
+
}, 500);
|
|
864
|
+
}
|
|
824
865
|
hideTooltip() {
|
|
825
866
|
this.tooltipEl?.remove();
|
|
826
867
|
this.tooltipEl = null;
|
|
@@ -903,5 +944,5 @@ var Review = class {
|
|
|
903
944
|
};
|
|
904
945
|
|
|
905
946
|
exports.Review = Review;
|
|
906
|
-
//# sourceMappingURL=chunk-
|
|
907
|
-
//# sourceMappingURL=chunk-
|
|
947
|
+
//# sourceMappingURL=chunk-M2R3KO66.cjs.map
|
|
948
|
+
//# sourceMappingURL=chunk-M2R3KO66.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;A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","file":"chunk-M2R3KO66.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 // Auto-focus pin from URL param\n this.autoFocusPin()\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 this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10)\n }\n\n private showTooltipForPin(pinNumber: number, x?: number, y?: number): void {\n this.hideTooltip()\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment) return\n\n this.tooltipEl = document.createElement('div')\n this.tooltipEl.className = 'review-tooltip'\n\n const time = new Date(comment.createdAt).toLocaleString()\n\n // Build tooltip with textContent to prevent XSS\n const header = document.createElement('div')\n header.style.cssText = 'font-weight:500;margin-bottom:4px;'\n header.textContent = `Pin #${pinNumber}`\n this.tooltipEl.appendChild(header)\n\n if (comment.reviewerName) {\n const name = document.createElement('div')\n name.style.cssText = 'font-size:11px;opacity:0.7;margin-bottom:2px;'\n name.textContent = comment.reviewerName\n this.tooltipEl.appendChild(name)\n }\n\n const text = document.createElement('div')\n text.style.cssText = 'margin-bottom:4px;'\n text.textContent = comment.commentText || '(no text)'\n this.tooltipEl.appendChild(text)\n\n const timeEl = document.createElement('div')\n timeEl.style.cssText = 'font-size:11px;opacity:0.7;'\n timeEl.textContent = time\n this.tooltipEl.appendChild(timeEl)\n\n // Position: use provided coords or center of viewport\n const posX = x ?? window.innerWidth / 2\n const posY = y ?? window.innerHeight / 3\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // 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 autoFocusPin(): void {\n const params = new URLSearchParams(window.location.search)\n const pinParam = params.get('pin')\n if (!pinParam) return\n\n const pinNumber = Number(pinParam)\n if (!pinNumber) return\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment?.pinData) return\n\n // Scroll to pin position (pinX/pinY are document-relative percentages)\n const docEl = document.documentElement\n const scrollX = (comment.pinData.pinX / 100) * docEl.scrollWidth\n const scrollY = (comment.pinData.pinY / 100) * docEl.scrollHeight\n\n window.scrollTo({\n left: scrollX - window.innerWidth / 2,\n top: scrollY - window.innerHeight / 2,\n behavior: 'smooth',\n })\n\n // Open tooltip after scroll completes\n setTimeout(() => {\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber)\n }, 500)\n }\n\n private hideTooltip(): void {\n this.tooltipEl?.remove()\n this.tooltipEl = null\n }\n\n destroy(): void {\n if (this.pinClickHandler) {\n document.removeEventListener('click', this.pinClickHandler, true)\n }\n this.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"]}
|
|
@@ -781,6 +781,7 @@ var TriageMode = class {
|
|
|
781
781
|
}
|
|
782
782
|
};
|
|
783
783
|
document.addEventListener("click", this.pinClickHandler, true);
|
|
784
|
+
this.autoFocusPin();
|
|
784
785
|
}
|
|
785
786
|
renderToolbar() {
|
|
786
787
|
this.toolbarEl = document.createElement("div");
|
|
@@ -795,19 +796,38 @@ var TriageMode = class {
|
|
|
795
796
|
handlePinClick(pinNumber, e) {
|
|
796
797
|
this.hideTooltip();
|
|
797
798
|
this.pinManager.highlightPin(pinNumber);
|
|
799
|
+
this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10);
|
|
800
|
+
}
|
|
801
|
+
showTooltipForPin(pinNumber, x, y) {
|
|
802
|
+
this.hideTooltip();
|
|
798
803
|
const comment = this.comments.find((c) => c.pinNumber === pinNumber);
|
|
799
804
|
if (!comment) return;
|
|
800
805
|
this.tooltipEl = document.createElement("div");
|
|
801
806
|
this.tooltipEl.className = "review-tooltip";
|
|
802
807
|
const time = new Date(comment.createdAt).toLocaleString();
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
+
const header = document.createElement("div");
|
|
809
|
+
header.style.cssText = "font-weight:500;margin-bottom:4px;";
|
|
810
|
+
header.textContent = `Pin #${pinNumber}`;
|
|
811
|
+
this.tooltipEl.appendChild(header);
|
|
812
|
+
if (comment.reviewerName) {
|
|
813
|
+
const name = document.createElement("div");
|
|
814
|
+
name.style.cssText = "font-size:11px;opacity:0.7;margin-bottom:2px;";
|
|
815
|
+
name.textContent = comment.reviewerName;
|
|
816
|
+
this.tooltipEl.appendChild(name);
|
|
817
|
+
}
|
|
818
|
+
const text = document.createElement("div");
|
|
819
|
+
text.style.cssText = "margin-bottom:4px;";
|
|
820
|
+
text.textContent = comment.commentText || "(no text)";
|
|
821
|
+
this.tooltipEl.appendChild(text);
|
|
822
|
+
const timeEl = document.createElement("div");
|
|
823
|
+
timeEl.style.cssText = "font-size:11px;opacity:0.7;";
|
|
824
|
+
timeEl.textContent = time;
|
|
825
|
+
this.tooltipEl.appendChild(timeEl);
|
|
826
|
+
const posX = x ?? window.innerWidth / 2;
|
|
827
|
+
const posY = y ?? window.innerHeight / 3;
|
|
808
828
|
this.tooltipEl.style.position = "fixed";
|
|
809
|
-
this.tooltipEl.style.left = `${Math.min(
|
|
810
|
-
this.tooltipEl.style.top = `${Math.min(
|
|
829
|
+
this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`;
|
|
830
|
+
this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`;
|
|
811
831
|
this.shadowRoot.appendChild(this.tooltipEl);
|
|
812
832
|
const dismiss = (ev) => {
|
|
813
833
|
if (ev.target !== this.tooltipEl && !this.tooltipEl?.contains(ev.target)) {
|
|
@@ -818,6 +838,27 @@ var TriageMode = class {
|
|
|
818
838
|
};
|
|
819
839
|
setTimeout(() => document.addEventListener("click", dismiss, true), 0);
|
|
820
840
|
}
|
|
841
|
+
autoFocusPin() {
|
|
842
|
+
const params = new URLSearchParams(window.location.search);
|
|
843
|
+
const pinParam = params.get("pin");
|
|
844
|
+
if (!pinParam) return;
|
|
845
|
+
const pinNumber = Number(pinParam);
|
|
846
|
+
if (!pinNumber) return;
|
|
847
|
+
const comment = this.comments.find((c) => c.pinNumber === pinNumber);
|
|
848
|
+
if (!comment?.pinData) return;
|
|
849
|
+
const docEl = document.documentElement;
|
|
850
|
+
const scrollX = comment.pinData.pinX / 100 * docEl.scrollWidth;
|
|
851
|
+
const scrollY = comment.pinData.pinY / 100 * docEl.scrollHeight;
|
|
852
|
+
window.scrollTo({
|
|
853
|
+
left: scrollX - window.innerWidth / 2,
|
|
854
|
+
top: scrollY - window.innerHeight / 2,
|
|
855
|
+
behavior: "smooth"
|
|
856
|
+
});
|
|
857
|
+
setTimeout(() => {
|
|
858
|
+
this.pinManager.highlightPin(pinNumber);
|
|
859
|
+
this.showTooltipForPin(pinNumber);
|
|
860
|
+
}, 500);
|
|
861
|
+
}
|
|
821
862
|
hideTooltip() {
|
|
822
863
|
this.tooltipEl?.remove();
|
|
823
864
|
this.tooltipEl = null;
|
|
@@ -900,5 +941,5 @@ var Review = class {
|
|
|
900
941
|
};
|
|
901
942
|
|
|
902
943
|
export { Review };
|
|
903
|
-
//# sourceMappingURL=chunk-
|
|
904
|
-
//# sourceMappingURL=chunk-
|
|
944
|
+
//# sourceMappingURL=chunk-OHL77RGU.js.map
|
|
945
|
+
//# sourceMappingURL=chunk-OHL77RGU.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":";;;AAGA,IAAM,WAAW,MAAM;AAErB,EAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAe,MAAA,CAAA,IAAA,CAAY,KAAK,YAAA,EAAc;AACvE,IAAA,OAAO,YAAY,GAAA,CAAI,YAAA;AAAA,EACzB;AACA,EAAA,OAAO,6BAAA;AACT,CAAA,GAAG;AAEH,eAAsB,cAAc,KAAA,EAAwC;AAC1E,EAAA,MAAM,MAAM,MAAM,KAAA,CAAM,GAAG,OAAO,CAAA,qBAAA,EAAwB,KAAK,CAAA,CAAE,CAAA;AACjE,EAAA,IAAI,IAAI,MAAA,KAAW,GAAA,QAAW,IAAI,WAAA,CAAY,WAAW,mCAAmC,CAAA;AAC5F,EAAA,IAAI,IAAI,MAAA,KAAW,GAAA,QAAW,IAAI,WAAA,CAAY,WAAW,+BAA+B,CAAA;AACxF,EAAA,IAAI,CAAC,GAAA,CAAI,EAAA,QAAU,IAAI,WAAA,CAAY,SAAS,sBAAsB,CAAA;AAClE,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,EAAA,OAAO,IAAA,CAAK,IAAA;AACd;AAYA,eAAsB,aAAA,CACpB,OACA,OAAA,EACsE;AACtE,EAAA,MAAM,MAAM,MAAM,KAAA,CAAM,GAAG,OAAO,CAAA,qBAAA,EAAwB,KAAK,CAAA,SAAA,CAAA,EAAa;AAAA,IAC1E,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,IAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,GAC7B,CAAA;AACD,EAAA,IAAI,CAAC,GAAA,CAAI,EAAA,QAAU,IAAI,WAAA,CAAY,SAAS,0BAA0B,CAAA;AACtE,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,EAAA,OAAO,IAAA,CAAK,IAAA;AACd;AAEA,eAAsB,cAAc,KAAA,EAAyC;AAC3E,EAAA,MAAM,MAAM,MAAM,KAAA,CAAM,GAAG,OAAO,CAAA,qBAAA,EAAwB,KAAK,CAAA,SAAA,CAAW,CAAA;AAC1E,EAAA,IAAI,CAAC,GAAA,CAAI,EAAA,QAAU,IAAI,WAAA,CAAY,SAAS,yBAAyB,CAAA;AACrE,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,EAAA,OAAO,IAAA,CAAK,IAAA;AACd;AAEO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAA,EACrC,WAAA,CACS,MACP,OAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAHN,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAIP,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AAAA,EACd;AACF,CAAA;;;ACzDO,SAAS,qBAAA,CAAsB,SAAiB,OAAA,EAA0B;AAC/E,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,gBAAA,CAAiB,OAAA,EAAS,OAAO,CAAA;AACrD,EAAA,MAAM,QAAQ,QAAA,CAAS,eAAA;AAEvB,EAAA,MAAM,IAAA,GAAA,CAAS,OAAA,GAAU,MAAA,CAAO,OAAA,IAAW,MAAM,WAAA,GAAe,GAAA;AAChE,EAAA,MAAM,IAAA,GAAA,CAAS,OAAA,GAAU,MAAA,CAAO,OAAA,IAAW,MAAM,YAAA,GAAgB,GAAA;AAEjE,EAAA,IAAI,CAAC,EAAA,IAAM,EAAA,KAAO,QAAA,CAAS,IAAA,IAAQ,OAAO,KAAA,EAAO;AAC/C,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAA,EAAS;AAAA,QACP,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK,MAAA;AAAA,QACL,IAAA,EAAM,EAAA;AAAA,QACN,SAAA,EAAW,IAAA;AAAA,QACX,SAAA,EAAW,EAAA;AAAA,QACX,WAAA,EAAa,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,KAAA,CAAM,WAAA,EAAa,CAAA,EAAG,KAAA,CAAM,YAAA;AAAa,OACzE;AAAA,MACA,OAAA,EAAS;AAAA,QACP,SAAA,EAAW,EAAA;AAAA,QACX,UAAA,EAAY,EAAA;AAAA,QACZ,cAAA,EAAgB,EAAA;AAAA,QAChB,UAAU,EAAC;AAAA,QACX,UAAA,EAAY;AAAA,OACd;AAAA,MACA,eAAe,MAAA,CAAO,UAAA;AAAA,MACtB,gBAAgB,MAAA,CAAO;AAAA,KACzB;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,GAAG,qBAAA,EAAsB;AAEtC,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACP,QAAA,EAAU,cAAc,EAAE,CAAA;AAAA,MAC1B,GAAA,EAAK,EAAA,CAAG,OAAA,CAAQ,WAAA,EAAY;AAAA,MAC5B,MAAM,aAAA,CAAc,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,GAAG,CAAA;AAAA,MACpC,SAAA,EAAW,EAAA,CAAG,YAAA,CAAa,YAAY,CAAA;AAAA,MACvC,SAAA,EAAW,GAAG,SAAA,IAAa,OAAO,GAAG,SAAA,KAAc,QAAA,GAAW,GAAG,SAAA,GAAY,EAAA;AAAA,MAC7E,WAAA,EAAa;AAAA,QACX,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA;AAAA,QACpB,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA;AAAA,QACpB,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,KAAK,CAAA;AAAA,QACxB,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAM;AAAA;AAC3B,KACF;AAAA,IACA,OAAA,EAAS;AAAA,MACP,SAAA,EAAW,EAAA,CAAG,aAAA,GAAgB,CAAA,EAAG,GAAG,aAAA,CAAc,OAAA,CAAQ,WAAA,EAAa,CAAA,EAAG,QAAA,CAAS,EAAA,CAAG,aAAa,CAAC,CAAA,CAAA,GAAK,EAAA;AAAA,MACzG,UAAA,EAAY,EAAA,CAAG,aAAA,GAAgB,aAAA,CAAc,EAAA,CAAG,aAAa,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,GAAI,EAAA;AAAA,MAC/E,gBAAgB,EAAA,CAAG,aAAA,EAAe,aAAA,GAC9B,CAAA,EAAG,GAAG,aAAA,CAAc,aAAA,CAAc,OAAA,CAAQ,WAAA,EAAa,CAAA,EAAG,QAAA,CAAS,GAAG,aAAA,CAAc,aAAa,CAAC,CAAA,CAAA,GAClG,EAAA;AAAA,MACJ,QAAA,EAAU,mBAAmB,EAAE,CAAA;AAAA,MAC/B,YAAY,aAAA,CAAc,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,GAAG;AAAA,KAC5C;AAAA,IACA,eAAe,MAAA,CAAO,UAAA;AAAA,IACtB,gBAAgB,MAAA,CAAO;AAAA,GACzB;AACF;AAEA,SAAS,cAAc,EAAA,EAAyB;AAC9C,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,IAAI,OAAA,GAA8B,EAAA;AAElC,EAAA,OAAO,OAAA,IAAW,OAAA,KAAY,QAAA,CAAS,IAAA,EAAM;AAC3C,IAAA,IAAI,QAAQ,EAAA,EAAI;AACd,MAAA,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,EAAE,CAAA,CAAE,CAAA;AAC9B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAY;AACvC,IAAA,IAAI,OAAA,CAAQ,SAAA,IAAa,OAAO,OAAA,CAAQ,cAAc,QAAA,EAAU;AAC9D,MAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,SAAA,CAAU,IAAA,EAAK,CAAE,KAAA,CAAM,KAAK,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,KAAK,GAAG,CAAA;AAC1E,MAAA,IAAI,OAAA,EAAS,IAAA,IAAQ,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AAAA,IAClC;AAEA,IAAA,KAAA,CAAM,QAAQ,IAAI,CAAA;AAClB,IAAA,OAAA,GAAU,OAAA,CAAQ,aAAA;AAAA,EACpB;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,KAAK,CAAA;AACzB;AAEA,SAAS,cAAc,EAAA,EAAyB;AAC9C,EAAA,IAAI,IAAA,GAAO,EAAA;AACX,EAAA,KAAA,MAAW,IAAA,IAAQ,GAAG,UAAA,EAAY;AAChC,IAAA,IAAI,IAAA,CAAK,QAAA,KAAa,IAAA,CAAK,SAAA,EAAW;AACpC,MAAA,IAAA,IAAQ,IAAA,CAAK,WAAA,EAAa,IAAA,EAAK,IAAK,EAAA;AAAA,IACtC;AAAA,EACF;AACA,EAAA,OAAO,IAAA,CAAK,IAAA,EAAK,IAAK,EAAA,CAAG,WAAA,EAAa,MAAK,CAAE,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,IAAK,EAAA;AAChE;AAEA,SAAS,SAAS,EAAA,EAAyB;AACzC,EAAA,IAAI,CAAC,EAAA,CAAG,SAAA,IAAa,OAAO,EAAA,CAAG,SAAA,KAAc,UAAU,OAAO,EAAA;AAC9D,EAAA,MAAM,GAAA,GAAM,EAAA,CAAG,SAAA,CAAU,IAAA,EAAK,CAAE,KAAA,CAAM,KAAK,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,KAAK,GAAG,CAAA;AACjE,EAAA,OAAO,GAAA,GAAM,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,EAAA;AAC3B;AAEA,SAAS,mBAAmB,EAAA,EAA2B;AACrD,EAAA,IAAI,CAAC,EAAA,CAAG,aAAA,EAAe,OAAO,EAAC;AAC/B,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,KAAA,MAAW,KAAA,IAAS,EAAA,CAAG,aAAA,CAAc,QAAA,EAAU;AAC7C,IAAA,IAAI,UAAU,EAAA,EAAI;AAClB,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,WAAA,EAAY;AACtC,IAAA,MAAM,IAAA,GAAA,CAAQ,MAAM,WAAA,EAAa,IAAA,MAAU,EAAA,EAAI,KAAA,CAAM,GAAG,EAAE,CAAA;AAC1D,IAAA,QAAA,CAAS,KAAK,IAAA,GAAO,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,IAAI,KAAK,GAAG,CAAA;AAC3C,IAAA,IAAI,QAAA,CAAS,UAAU,CAAA,EAAG;AAAA,EAC5B;AACA,EAAA,OAAO,QAAA;AACT;AAEA,SAAS,cAAc,EAAA,EAAyB;AAE9C,EAAA,IAAI,UAA8B,EAAA,CAAG,aAAA;AACrC,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,OAAO,OAAA,IAAW,QAAQ,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAA,GAAO,cAAc,OAAO,CAAA;AAClC,IAAA,IAAI,IAAA,IAAQ,IAAA,KAAS,aAAA,CAAc,EAAE,GAAG,OAAO,IAAA;AAC/C,IAAA,OAAA,GAAU,OAAA,CAAQ,aAAA;AAClB,IAAA,KAAA,EAAA;AAAA,EACF;AACA,EAAA,OAAO,EAAA;AACT;;;AChIO,IAAM,aAAA,GAAgB;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;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-OHL77RGU.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 // Auto-focus pin from URL param\n this.autoFocusPin()\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 this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10)\n }\n\n private showTooltipForPin(pinNumber: number, x?: number, y?: number): void {\n this.hideTooltip()\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment) return\n\n this.tooltipEl = document.createElement('div')\n this.tooltipEl.className = 'review-tooltip'\n\n const time = new Date(comment.createdAt).toLocaleString()\n\n // Build tooltip with textContent to prevent XSS\n const header = document.createElement('div')\n header.style.cssText = 'font-weight:500;margin-bottom:4px;'\n header.textContent = `Pin #${pinNumber}`\n this.tooltipEl.appendChild(header)\n\n if (comment.reviewerName) {\n const name = document.createElement('div')\n name.style.cssText = 'font-size:11px;opacity:0.7;margin-bottom:2px;'\n name.textContent = comment.reviewerName\n this.tooltipEl.appendChild(name)\n }\n\n const text = document.createElement('div')\n text.style.cssText = 'margin-bottom:4px;'\n text.textContent = comment.commentText || '(no text)'\n this.tooltipEl.appendChild(text)\n\n const timeEl = document.createElement('div')\n timeEl.style.cssText = 'font-size:11px;opacity:0.7;'\n timeEl.textContent = time\n this.tooltipEl.appendChild(timeEl)\n\n // Position: use provided coords or center of viewport\n const posX = x ?? window.innerWidth / 2\n const posY = y ?? window.innerHeight / 3\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // 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 autoFocusPin(): void {\n const params = new URLSearchParams(window.location.search)\n const pinParam = params.get('pin')\n if (!pinParam) return\n\n const pinNumber = Number(pinParam)\n if (!pinNumber) return\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment?.pinData) return\n\n // Scroll to pin position (pinX/pinY are document-relative percentages)\n const docEl = document.documentElement\n const scrollX = (comment.pinData.pinX / 100) * docEl.scrollWidth\n const scrollY = (comment.pinData.pinY / 100) * docEl.scrollHeight\n\n window.scrollTo({\n left: scrollX - window.innerWidth / 2,\n top: scrollY - window.innerHeight / 2,\n behavior: 'smooth',\n })\n\n // Open tooltip after scroll completes\n setTimeout(() => {\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber)\n }, 500)\n }\n\n private hideTooltip(): void {\n this.tooltipEl?.remove()\n this.tooltipEl = null\n }\n\n destroy(): void {\n if (this.pinClickHandler) {\n document.removeEventListener('click', this.pinClickHandler, true)\n }\n this.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
|
|
5
|
+
var chunkM2R3KO66_cjs = require('./chunk-M2R3KO66.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:
|
|
51
|
+
window.OurRoadmaps = { init, destroy, createOverlay: chunkEKWWSLHU_cjs.createOverlay, Review: chunkM2R3KO66_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
|
|
56
|
+
const review = new chunkM2R3KO66_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
|
|
71
|
+
get: function () { return chunkM2R3KO66_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-
|
|
5
|
-
export { Review } from './chunk-
|
|
4
|
+
import { Review } from './chunk-OHL77RGU.js';
|
|
5
|
+
export { Review } from './chunk-OHL77RGU.js';
|
|
6
6
|
import './chunk-V6TY7KAL.js';
|
|
7
7
|
|
|
8
8
|
// src/feedback/dev-mode.ts
|
package/dist/review/index.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkM2R3KO66_cjs = require('../chunk-M2R3KO66.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
|
|
10
|
+
get: function () { return chunkM2R3KO66_cjs.Review; }
|
|
11
11
|
});
|
|
12
12
|
//# sourceMappingURL=index.cjs.map
|
|
13
13
|
//# sourceMappingURL=index.cjs.map
|
package/dist/review/index.js
CHANGED
package/dist/review.global.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var OurRoadmaps=(()=>{var
|
|
1
|
+
"use strict";var OurRoadmaps=(()=>{var h=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var z=(n,e,t)=>e in n?h(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var $=(n,e)=>{for(var t in e)h(n,t,{get:e[t],enumerable:!0})},Y=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of D(e))!R.call(n,o)&&o!==t&&h(n,o,{get:()=>e[o],enumerable:!(i=H(e,o))||i.enumerable});return n};var X=n=>Y(h({},"__esModule",{value:!0}),n);var r=(n,e,t)=>z(n,typeof e!="symbol"?e+"":e,t);var j={};$(j,{Review:()=>x});var _={},g=(typeof _<"u","https://api.ourroadmaps.com");async function y(n){let e=await fetch(`${g}/v1/prototype-review/${n}`);if(e.status===410)throw new l("expired","This feedback session has expired");if(e.status===404)throw new l("invalid","This review link is not valid");if(!e.ok)throw new l("error","Something went wrong");return(await e.json()).data}async function E(n,e){let t=await fetch(`${g}/v1/prototype-review/${n}/comments`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok)throw new l("error","Failed to submit comment");return(await t.json()).data}async function u(n){let e=await fetch(`${g}/v1/prototype-review/${n}/comments`);if(!e.ok)throw new l("error","Failed to load comments");return(await e.json()).data}var l=class extends Error{constructor(t,i){super(i);this.code=t;this.name="ReviewError"}};function P(n,e){let t=document.elementFromPoint(n,e),i=document.documentElement,o=(n+window.scrollX)/i.scrollWidth*100,s=(e+window.scrollY)/i.scrollHeight*100;if(!t||t===document.body||t===i)return{pinX:o,pinY:s,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 a=t.getBoundingClientRect();return{pinX:o,pinY:s,element:{selector:I(t),tag:t.tagName.toLowerCase(),text:f(t).slice(0,200),ariaLabel:t.getAttribute("aria-label"),className:t.className&&typeof t.className=="string"?t.className:"",boundingBox:{x:Math.round(a.x),y:Math.round(a.y),w:Math.round(a.width),h:Math.round(a.height)}},context:{parentTag:t.parentElement?`${t.parentElement.tagName.toLowerCase()}${k(t.parentElement)}`:"",parentText:t.parentElement?f(t.parentElement).slice(0,100):"",grandparentTag:t.parentElement?.parentElement?`${t.parentElement.parentElement.tagName.toLowerCase()}${k(t.parentElement.parentElement)}`:"",siblings:O(t),nearbyText:W(t).slice(0,200)},viewportWidth:window.innerWidth,viewportHeight:window.innerHeight}}function I(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 f(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 k(n){if(!n.className||typeof n.className!="string")return"";let e=n.className.trim().split(/\s+/).slice(0,2).join(".");return e?`.${e}`:""}function O(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 W(n){let e=n.parentElement,t=0;for(;e&&t<3;){let i=f(e);if(i&&i!==f(n))return i;e=e.parentElement,t++}return""}var M=`
|
|
2
2
|
:host {
|
|
3
3
|
all: initial;
|
|
4
4
|
}
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
transition-duration: 0.01ms !important;
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
|
-
`,
|
|
260
|
+
`,N=`
|
|
261
261
|
body {
|
|
262
262
|
position: relative !important;
|
|
263
263
|
}
|
|
@@ -314,32 +314,28 @@ body {
|
|
|
314
314
|
transition-duration: 0.01ms !important;
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
|
-
`;var
|
|
317
|
+
`;var T="roadmaps-review-pin-styles",p=class{constructor(){r(this,"container");r(this,"pins",new Map);r(this,"styleEl",null);this.container=document.createElement("div"),this.container.className="pin-container"}mount(){document.getElementById(T)||(this.styleEl=document.createElement("style"),this.styleEl.id=T,this.styleEl.textContent=N,document.head.appendChild(this.styleEl)),document.body.appendChild(this.container)}addPin(e,t,i,o){let s=document.createElement("div");s.className=o?"review-pin":"review-pin review-pin--other",s.textContent=String(e),s.style.left=`${t}%`,s.style.top=`${i}%`,s.dataset.pinNumber=String(e),this.container.appendChild(s),this.pins.set(e,s)}removePin(e){let t=this.pins.get(e);t&&(t.remove(),this.pins.delete(e))}highlightPin(e){for(let[t,i]of this.pins)i.classList.toggle("review-pin--highlighted",t===e)}clearHighlight(){for(let e of this.pins.values())e.classList.remove("review-pin--highlighted")}destroy(){this.container.remove(),this.pins.clear(),this.styleEl?(this.styleEl.remove(),this.styleEl=null):document.getElementById(T)?.remove()}};var v=class{constructor(e){this.shadowRoot=e;r(this,"card",null);r(this,"onSubmit",null);r(this,"onCancel",null)}show(e){this.hide(),this.onSubmit=e.onSubmit,this.onCancel=e.onCancel,this.card=document.createElement("div"),this.card.className="review-comment-card",this.card.style.left=`${Math.min(e.x,window.innerWidth-320)}px`,this.card.style.top=`${Math.min(e.y+20,window.innerHeight-200)}px`,this.card.innerHTML=`
|
|
318
318
|
<textarea class="review-comment-input" placeholder="Leave your feedback..." rows="3"></textarea>
|
|
319
319
|
<div class="review-comment-actions">
|
|
320
320
|
<button class="review-btn review-btn--cancel">Cancel</button>
|
|
321
321
|
<button class="review-btn review-btn--submit">Submit</button>
|
|
322
322
|
</div>
|
|
323
323
|
<div class="review-comment-error" style="display:none"></div>
|
|
324
|
-
`,this.attachListeners(),this.shadowRoot.appendChild(this.card),this.card.querySelector("textarea")?.focus()}hide(){this.card?.remove(),this.card=null}showForGeneral(e){this.show({x:window.innerWidth/2-150,y:window.innerHeight/2-100,onSubmit:e.onSubmit,onCancel:e.onCancel})}attachListeners(){this.card&&(this.card.querySelector(".review-btn--cancel")?.addEventListener("click",()=>{this.onCancel?.(),this.hide()}),this.card.querySelector(".review-btn--submit")?.addEventListener("click",()=>this.handleSubmit()),this.card.querySelector("textarea")?.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&e.key==="Enter"&&(e.preventDefault(),this.handleSubmit())}))}async handleSubmit(){if(!this.card)return;let e=this.card.querySelector("textarea"),t=e.value.trim();if(!t)return;let i=this.card.querySelector(".review-btn--submit"),o=this.card.querySelector(".review-comment-error");i.disabled=!0,i.textContent="Submitting...",e.disabled=!0,o.style.display="none";try{await this.onSubmit?.(t),this.hide()}catch(
|
|
324
|
+
`,this.attachListeners(),this.shadowRoot.appendChild(this.card),this.card.querySelector("textarea")?.focus()}hide(){this.card?.remove(),this.card=null}showForGeneral(e){this.show({x:window.innerWidth/2-150,y:window.innerHeight/2-100,onSubmit:e.onSubmit,onCancel:e.onCancel})}attachListeners(){this.card&&(this.card.querySelector(".review-btn--cancel")?.addEventListener("click",()=>{this.onCancel?.(),this.hide()}),this.card.querySelector(".review-btn--submit")?.addEventListener("click",()=>this.handleSubmit()),this.card.querySelector("textarea")?.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&e.key==="Enter"&&(e.preventDefault(),this.handleSubmit())}))}async handleSubmit(){if(!this.card)return;let e=this.card.querySelector("textarea"),t=e.value.trim();if(!t)return;let i=this.card.querySelector(".review-btn--submit"),o=this.card.querySelector(".review-comment-error");i.disabled=!0,i.textContent="Submitting...",e.disabled=!0,o.style.display="none";try{await this.onSubmit?.(t),this.hide()}catch(s){i.disabled=!1,i.textContent="Submit",e.disabled=!1,o.textContent=s instanceof Error?s.message:"Failed to submit",o.style.display="block"}}};var b=class{constructor(e,t,i){this.token=e;this.shadowRoot=t;this.initData=i;r(this,"pinManager");r(this,"commentCard");r(this,"nextPinNumber");r(this,"pendingPinNumber",null);r(this,"promptEl",null);r(this,"toolbarEl",null);r(this,"clickHandler",null);this.pinManager=new p,this.commentCard=new v(t),this.nextPinNumber=i.nextPinNumber}async init(){document.body.style.cursor="crosshair",this.pinManager.mount(),this.showPrompt(),this.renderToolbar(),await this.loadExistingPins(),this.clickHandler=e=>this.handleClick(e),document.addEventListener("click",this.clickHandler,!0)}showPrompt(){this.promptEl=document.createElement("div"),this.promptEl.className="review-prompt",this.promptEl.innerHTML=`
|
|
325
325
|
<h3 style="margin:0 0 8px;font-size:16px;">Click anywhere to leave feedback</h3>
|
|
326
326
|
<p style="margin:0;color:#666;font-size:14px;">Drop numbered pins on elements you want to comment on</p>
|
|
327
327
|
`,this.shadowRoot.appendChild(this.promptEl),setTimeout(()=>this.dismissPrompt(),5e3)}dismissPrompt(){this.promptEl&&(this.promptEl.remove(),this.promptEl=null)}renderToolbar(){this.toolbarEl=document.createElement("div"),this.toolbarEl.className="review-toolbar",this.updateToolbar(),this.shadowRoot.appendChild(this.toolbarEl)}updateToolbar(){if(!this.toolbarEl)return;let e=this.nextPinNumber-1;this.toolbarEl.innerHTML=`
|
|
328
328
|
<span style="font-weight:500;">${this.initData.session.name}</span>
|
|
329
329
|
<span style="opacity:0.7;">${e} pin${e!==1?"s":""}</span>
|
|
330
330
|
<button class="review-btn review-btn--submit" style="margin-left:auto;padding:6px 12px;font-size:13px;">General Comment</button>
|
|
331
|
-
`,this.toolbarEl.querySelector("button")?.addEventListener("click",t=>{t.stopPropagation(),this.handleGeneralComment()})}async loadExistingPins(){try{let e=await
|
|
331
|
+
`,this.toolbarEl.querySelector("button")?.addEventListener("click",t=>{t.stopPropagation(),this.handleGeneralComment()})}async loadExistingPins(){try{let e=await u(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(s=>s instanceof HTMLElement&&s.closest?.("#ourroadmaps-review"))||this.pendingPinNumber!=null)return;this.dismissPrompt();let i=P(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 s=>{await E(this.token,{commentText:s,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 E(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 w=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 p}async init(){this.pinManager.mount();try{this.comments=await u(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),this.autoFocusPin()}renderToolbar(){this.toolbarEl=document.createElement("div"),this.toolbarEl.className="review-toolbar";let e=this.comments.filter(t=>t.pinNumber!=null).length;this.toolbarEl.innerHTML=`
|
|
332
332
|
<span style="font-weight:500;">Triage Mode</span>
|
|
333
333
|
<span style="opacity:0.7;">${e} pin${e!==1?"s":""}</span>
|
|
334
|
-
`,this.shadowRoot.appendChild(this.toolbarEl)}handlePinClick(e,t){this.hideTooltip(),this.pinManager.highlightPin(e);let
|
|
335
|
-
<div style="font-weight:500;margin-bottom:4px;">Pin #${e}</div>
|
|
336
|
-
<div style="margin-bottom:4px;">${i.commentText||"(no text)"}</div>
|
|
337
|
-
<div style="font-size:11px;opacity:0.7;">${o}</div>
|
|
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=`
|
|
334
|
+
`,this.shadowRoot.appendChild(this.toolbarEl)}handlePinClick(e,t){this.hideTooltip(),this.pinManager.highlightPin(e),this.showTooltipForPin(e,t.clientX+16,t.clientY-10)}showTooltipForPin(e,t,i){this.hideTooltip();let o=this.comments.find(d=>d.pinNumber===e);if(!o)return;this.tooltipEl=document.createElement("div"),this.tooltipEl.className="review-tooltip";let s=new Date(o.createdAt).toLocaleString(),a=document.createElement("div");if(a.style.cssText="font-weight:500;margin-bottom:4px;",a.textContent=`Pin #${e}`,this.tooltipEl.appendChild(a),o.reviewerName){let d=document.createElement("div");d.style.cssText="font-size:11px;opacity:0.7;margin-bottom:2px;",d.textContent=o.reviewerName,this.tooltipEl.appendChild(d)}let m=document.createElement("div");m.style.cssText="margin-bottom:4px;",m.textContent=o.commentText||"(no text)",this.tooltipEl.appendChild(m);let c=document.createElement("div");c.style.cssText="font-size:11px;opacity:0.7;",c.textContent=s,this.tooltipEl.appendChild(c);let L=t??window.innerWidth/2,S=i??window.innerHeight/3;this.tooltipEl.style.position="fixed",this.tooltipEl.style.left=`${Math.min(L,window.innerWidth-300)}px`,this.tooltipEl.style.top=`${Math.min(S,window.innerHeight-150)}px`,this.shadowRoot.appendChild(this.tooltipEl);let C=d=>{d.target!==this.tooltipEl&&!this.tooltipEl?.contains(d.target)&&(this.hideTooltip(),this.pinManager.clearHighlight(),document.removeEventListener("click",C,!0))};setTimeout(()=>document.addEventListener("click",C,!0),0)}autoFocusPin(){let t=new URLSearchParams(window.location.search).get("pin");if(!t)return;let i=Number(t);if(!i)return;let o=this.comments.find(c=>c.pinNumber===i);if(!o?.pinData)return;let s=document.documentElement,a=o.pinData.pinX/100*s.scrollWidth,m=o.pinData.pinY/100*s.scrollHeight;window.scrollTo({left:a-window.innerWidth/2,top:m-window.innerHeight/2,behavior:"smooth"}),setTimeout(()=>{this.pinManager.highlightPin(i),this.showTooltipForPin(i)},500)}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 x=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=M,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 y(t);this.mode=new b(t,this.shadow,o),await this.mode.init()}catch(o){o instanceof l&&this.showErrorOverlay(o.code==="expired"?"This feedback session has expired":"This review link is no longer valid")}else if(i)try{await y(i),this.mode=new w(i,this.shadow),await this.mode.init()}catch(o){o instanceof l&&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=`
|
|
339
335
|
<div class="review-expired-card">
|
|
340
336
|
<h2 style="margin:0 0 8px;font-size:18px;">Session Unavailable</h2>
|
|
341
337
|
<p style="margin:0;color:#666;font-size:14px;">${e}</p>
|
|
342
338
|
<p style="margin:12px 0 0;color:#999;font-size:13px;">Contact the prototype owner for a new link.</p>
|
|
343
339
|
</div>
|
|
344
|
-
`,this.shadow.appendChild(t),document.body.style.filter="grayscale(0.8)"}destroy(){this._isDestroyed||(this._isDestroyed=!0,this.mode?.destroy(),this.root.remove(),document.body.style.filter="")}};return
|
|
340
|
+
`,this.shadow.appendChild(t),document.body.style.filter="grayscale(0.8)"}destroy(){this._isDestroyed||(this._isDestroyed=!0,this.mode?.destroy(),this.root.remove(),document.body.style.filter="")}};return X(j);})();
|
|
345
341
|
//# sourceMappingURL=review.global.js.map
|
|
@@ -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 = `\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;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;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 // Auto-focus pin from URL param\n this.autoFocusPin()\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 this.showTooltipForPin(pinNumber, e.clientX + 16, e.clientY - 10)\n }\n\n private showTooltipForPin(pinNumber: number, x?: number, y?: number): void {\n this.hideTooltip()\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment) return\n\n this.tooltipEl = document.createElement('div')\n this.tooltipEl.className = 'review-tooltip'\n\n const time = new Date(comment.createdAt).toLocaleString()\n\n // Build tooltip with textContent to prevent XSS\n const header = document.createElement('div')\n header.style.cssText = 'font-weight:500;margin-bottom:4px;'\n header.textContent = `Pin #${pinNumber}`\n this.tooltipEl.appendChild(header)\n\n if (comment.reviewerName) {\n const name = document.createElement('div')\n name.style.cssText = 'font-size:11px;opacity:0.7;margin-bottom:2px;'\n name.textContent = comment.reviewerName\n this.tooltipEl.appendChild(name)\n }\n\n const text = document.createElement('div')\n text.style.cssText = 'margin-bottom:4px;'\n text.textContent = comment.commentText || '(no text)'\n this.tooltipEl.appendChild(text)\n\n const timeEl = document.createElement('div')\n timeEl.style.cssText = 'font-size:11px;opacity:0.7;'\n timeEl.textContent = time\n this.tooltipEl.appendChild(timeEl)\n\n // Position: use provided coords or center of viewport\n const posX = x ?? window.innerWidth / 2\n const posY = y ?? window.innerHeight / 3\n this.tooltipEl.style.position = 'fixed'\n this.tooltipEl.style.left = `${Math.min(posX, window.innerWidth - 300)}px`\n this.tooltipEl.style.top = `${Math.min(posY, window.innerHeight - 150)}px`\n this.shadowRoot.appendChild(this.tooltipEl)\n\n // 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 autoFocusPin(): void {\n const params = new URLSearchParams(window.location.search)\n const pinParam = params.get('pin')\n if (!pinParam) return\n\n const pinNumber = Number(pinParam)\n if (!pinNumber) return\n\n const comment = this.comments.find((c) => c.pinNumber === pinNumber)\n if (!comment?.pinData) return\n\n // Scroll to pin position (pinX/pinY are document-relative percentages)\n const docEl = document.documentElement\n const scrollX = (comment.pinData.pinX / 100) * docEl.scrollWidth\n const scrollY = (comment.pinData.pinY / 100) * docEl.scrollHeight\n\n window.scrollTo({\n left: scrollX - window.innerWidth / 2,\n top: scrollY - window.innerHeight / 2,\n behavior: 'smooth',\n })\n\n // Open tooltip after scroll completes\n setTimeout(() => {\n this.pinManager.highlightPin(pinNumber)\n this.showTooltipForPin(pinNumber)\n }, 500)\n }\n\n private hideTooltip(): void {\n this.tooltipEl?.remove()\n this.tooltipEl = null\n }\n\n destroy(): void {\n if (this.pinClickHandler) {\n document.removeEventListener('click', this.pinClickHandler, true)\n }\n this.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","x","y","comment","time","header","name","text","timeEl","posX","posY","dismiss","ev","pinParam","docEl","scrollX","scrollY","Review","_options","__publicField","styleEl","REVIEW_STYLES","params","reviewToken","triageToken","data","validateToken","ReviewMode","err","ReviewError","TriageMode","message","overlay"]}
|
package/package.json
CHANGED
|
@@ -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-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"]}
|
|
@@ -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-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"]}
|