helldots 0.3.0 → 0.4.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/helldots.esm.js +644 -208
- package/dist/helldots.esm.js.map +4 -4
- package/dist/helldots.umd.js +656 -220
- package/dist/index.d.ts +43 -9
- package/package.json +3 -1
package/dist/helldots.esm.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/capture.js", "../src/root-element.js", "../src/metadata.js", "../src/constants.js", "../src/styles.js", "../src/locales/en.js", "../src/locales/es.js", "../src/i18n.js", "../src/anchor.js", "../src/storage.js", "../src/comment-actions.js", "../src/components.js", "../src/agent-context.js", "../src/inbox.js", "../src/overlay.js", "../src/index.js"],
|
|
4
|
-
"sourcesContent": ["// Screenshot primitives. The page render is the expensive part, so it's\n// split out from the cropping: a drag capture and the automatic context\n// capture of the same comment share ONE render instead of paying for two.\n//\n// WE own the crop in page coordinates \u2014 html2canvas used to own it and\n// silently shifted it by the window scroll (double-counting: the hero\n// showed up in captures taken further down the page). Owning the crop\n// makes that whole bug class impossible.\n\nimport { domToCanvas } from \"modern-screenshot\";\nimport { TAG_NAME } from \"./root-element.js\";\n\n/** Automatic captures render and encode small \u2014 they live in localStorage. */\nexport const AUTO_SCALE = 0.5;\nexport const AUTO_QUALITY = 0.7;\n\nconst isUnpainted = (color) =>\n !color || color === \"transparent\" || color === \"rgba(0, 0, 0, 0)\";\n\n// What the user visually perceives as the page background: the html/body\n// CSS color when one is painted, else white \u2014 browsers paint their own\n// white canvas under a transparent document, but that canvas is not part\n// of the DOM, so a DOM-based render would come out as a transparent PNG\n// (invisible against the dark inbox UI).\nconst effectiveBackgroundColor = () => {\n const htmlBg = getComputedStyle(document.documentElement).backgroundColor;\n if (!isUnpainted(htmlBg)) return htmlBg;\n const bodyBg = getComputedStyle(document.body).backgroundColor;\n if (!isUnpainted(bodyBg)) return bodyBg;\n return \"#ffffff\";\n};\n\n/**\n * Renders the whole page to a canvas. This is the expensive call \u2014 callers\n * that need more than one image should render once and crop repeatedly.\n * @param {{ scale?: number }} [options] scale 1 keeps the canvas in CSS\n * pixels so crop rects map 1:1 to page coordinates.\n * @returns {Promise<any>}\n */\nexport async function renderPage({ scale = 1 } = {}) {\n return domToCanvas(document.body, {\n scale,\n backgroundColor: effectiveBackgroundColor(),\n });\n}\n\n/**\n * Runs `fn` with the HellDots UI hidden, so the widget never renders into\n * its own screenshot. Restores the host even if `fn` throws \u2014 a failed\n * render must not leave the widget invisible.\n * @template T\n * @param {() => Promise<T>} fn\n * @returns {Promise<T>}\n */\nexport async function withHiddenOverlay(fn) {\n const host = /** @type {HTMLElement} */ (document.querySelector(TAG_NAME));\n const previousDisplay = host?.style.display;\n if (host) host.style.display = \"none\";\n try {\n return await fn();\n } finally {\n if (host) host.style.display = previousDisplay || \"\";\n }\n}\n\n/**\n * Crops a viewport-relative region out of a scale-1 page render.\n * @param {any} canvas full-page render from renderPage({ scale: 1 })\n * @param {{ left: number, top: number, width: number, height: number }} region\n * Viewport (client) coordinates of the drag selection.\n * @returns {string | null} PNG data-URL, or null with no 2d context.\n */\nexport function cropRegion(canvas, { left, top, width, height }) {\n const out = document.createElement(\"canvas\");\n out.width = width;\n out.height = height;\n const ctx = out.getContext(\"2d\");\n if (!ctx) return null;\n\n ctx.drawImage(\n canvas,\n left + window.scrollX,\n top + window.scrollY,\n width,\n height,\n 0,\n 0,\n width,\n height\n );\n return out.toDataURL(\"image/png\");\n}\n\n/**\n * Crops the current viewport out of a page render and encodes it small.\n * @param {any} canvas full-page render\n * @param {{ sourceScale?: number, outputScale?: number, quality?: number }} [options]\n * `sourceScale` is the scale `canvas` was rendered at \u2014 the source rect is\n * mapped through it. `outputScale` is the final size in CSS pixels.\n * @returns {string | null} JPEG data-URL, or null with no 2d context.\n */\nexport function cropViewport(\n canvas,\n { sourceScale = 1, outputScale = AUTO_SCALE, quality = AUTO_QUALITY } = {}\n) {\n const out = document.createElement(\"canvas\");\n out.width = Math.round(window.innerWidth * outputScale);\n out.height = Math.round(window.innerHeight * outputScale);\n const ctx = out.getContext(\"2d\");\n if (!ctx) return null;\n\n ctx.drawImage(\n canvas,\n window.scrollX * sourceScale,\n window.scrollY * sourceScale,\n window.innerWidth * sourceScale,\n window.innerHeight * sourceScale,\n 0,\n 0,\n out.width,\n out.height\n );\n return out.toDataURL(\"image/jpeg\", quality);\n}\n\n/**\n * Captures a viewport-relative region of the current page.\n * Kept for API compatibility \u2014 renders and crops in one call.\n * @param {{ left: number, top: number, width: number, height: number }} region\n * @returns {Promise<string | null>} PNG data-URL. Render failures reject.\n */\nexport async function captureRegion(region) {\n return cropRegion(await renderPage({ scale: 1 }), region);\n}\n", "const TAG_NAME = \"helldots-root\";\n\n// Defined lazily rather than at module scope: `extends HTMLElement` is\n// evaluated when the class expression runs, so a top-level declaration makes\n// a bare `import \"helldots\"` throw on any server renderer (Next.js, Remix,\n// Astro) long before the app calls anything. Deferring it keeps the module\n// import-safe everywhere and only touches the DOM when we actually mount.\nconst ensureDefined = () => {\n if (customElements.get(TAG_NAME)) return;\n\n customElements.define(\n TAG_NAME,\n class HelldotsRoot extends HTMLElement {\n constructor() {\n super();\n this.attachShadow({ mode: \"open\" });\n }\n }\n );\n};\n\n/**\n * Returns the shared shadow root used to render all HellDots UI, creating\n * the host element and mounting it on document.body on first call.\n * @returns {ShadowRoot}\n */\nexport function getShadowRoot() {\n ensureDefined();\n\n let host = document.querySelector(TAG_NAME);\n if (!host) {\n host = document.createElement(TAG_NAME);\n document.body.appendChild(host);\n }\n\n return host.shadowRoot;\n}\n\nexport { TAG_NAME };\n", "// RF2 \u2014 environment snapshot attached to every comment at creation time.\n// Kept as a pure function over an injectable `window` so the UA parsing\n// paths are testable without touching jsdom's real navigator.\n\n// Order is load-bearing: Edge's UA contains \"Chrome\", and Chrome's UA\n// contains \"Safari\". First match wins, so the more specific entries lead.\nconst BROWSERS = [\n { name: \"Edge\", re: /Edg\\/([\\d.]+)/ },\n { name: \"Chrome\", re: /Chrome\\/([\\d.]+)/ },\n { name: \"Firefox\", re: /Firefox\\/([\\d.]+)/ },\n { name: \"Safari\", re: /Version\\/([\\d.]+).*Safari/ },\n];\n\n// iOS before macOS: an iPhone UA also carries \"Mac OS X\".\nconst OPERATING_SYSTEMS = [\n { name: \"iOS\", re: /(?:iPhone|iPad).*OS ([\\d_]+) like Mac OS X/ },\n { name: \"Android\", re: /Android ([\\d.]+)/ },\n { name: \"Windows\", re: /Windows NT ([\\d.]+)/ },\n { name: \"macOS\", re: /Mac OS X ([\\d_.]+)/ },\n { name: \"Linux\", re: /Linux/ },\n];\n\nconst UNKNOWN = { name: \"unknown\", version: \"\" };\n\n// Chromium pads its brand list with a randomised \"GREASE\" entry to stop\n// consumers hardcoding brand positions. It is never the real browser.\nconst isGreaseBrand = (brand) => /not[\\W_]*a[\\W_]*brand/i.test(brand);\n\nconst matchFirst = (table, ua) => {\n for (const { name, re } of table) {\n const match = ua.match(re);\n if (match) {\n return { name, version: (match[1] || \"\").replace(/_/g, \".\") };\n }\n }\n return { ...UNKNOWN };\n};\n\n/**\n * Snapshots the browsing environment of the current page.\n * @param {any} [win] Injectable window \u2014 defaults to the real one.\n * @returns {import('./index.d.ts').CommentContext}\n */\nexport function captureContext(win = window) {\n const nav = win.navigator || {};\n const ua = nav.userAgent || \"\";\n const uaData = nav.userAgentData;\n\n let browser = matchFirst(BROWSERS, ua);\n const brand = uaData?.brands?.find((b) => !isGreaseBrand(b.brand));\n if (brand) {\n browser = { name: brand.brand, version: brand.version || \"\" };\n }\n\n const os = matchFirst(OPERATING_SYSTEMS, ua);\n if (uaData?.platform) os.name = uaData.platform;\n\n return {\n version: 1,\n url: win.location?.href || \"\",\n viewport: { width: win.innerWidth, height: win.innerHeight },\n screen: {\n width: win.screen?.width ?? 0,\n height: win.screen?.height ?? 0,\n },\n devicePixelRatio: win.devicePixelRatio ?? 1,\n userAgent: ua,\n browser,\n os,\n language: nav.language || \"\",\n };\n}\n", "export const CLASSES = {\n CIRCLE: \"comment-circle\",\n CIRCLE_WRAPPER: \"comment-circle-wrapper\",\n TOOLTIP: \"comment-tooltip\",\n TOOLBAR_TEXT: \"toolbar-text\",\n SHORTCUT_HINT: \"shortcut-hint\",\n COMMENT_INPUT_AREA: \"comment-input-area\",\n CLOSE_TOOLTIP: \"close-tooltip\",\n ACTIVE: \"active\",\n COMMENT_CURSOR: \"comment-cursor\",\n COMMENT_OVERLAY: \"comment-overlay\",\n THREAD_POPOVER: \"comment-thread-popover\",\n THREAD_HEADER: \"thread-header\",\n THREAD_BODY: \"thread-body\",\n THREAD_REPLIES: \"thread-replies\",\n THREAD_REPLY: \"thread-reply\",\n THREAD_INPUT_AREA: \"thread-input-area\",\n THREAD_INPUT: \"thread-input\",\n THREAD_SUBMIT: \"thread-submit\",\n THREAD_META: \"thread-meta\",\n THREAD_AUTHOR: \"thread-author\",\n THREAD_TIME: \"thread-time\",\n PREVIEW_CIRCLE: \"preview-circle\",\n SELECTION_RECT: \"selection-rect\",\n SCREENSHOT_IMG: \"screenshot-img\",\n SCREENSHOT_REMOVE: \"screenshot-remove\",\n SCREENSHOTS_CONTAINER: \"screenshots-container\",\n SCREENSHOT_ITEM: \"screenshot-item\",\n LIGHTBOX: \"helldots-lightbox\",\n LIGHTBOX_IMG: \"helldots-lightbox-img\",\n LIGHTBOX_CLOSE: \"helldots-lightbox-close\",\n COMMENT_ACTIONS_BAR: \"comment-actions-bar\",\n ATTACH_IMAGE_BTN: \"attach-image-btn\",\n TOOLBAR_ACTIONS: \"toolbar-actions\",\n TOOLBAR_ACTION_BTN: \"toolbar-action-btn\",\n TOOLBAR_ACTION_WRAPPER: \"toolbar-action-wrapper\",\n TOOLBAR_ACTION_TOOLTIP: \"toolbar-action-tooltip\",\n TOOLBAR_COMMENT_BTN: \"toolbar-comment-btn\",\n TOOLBAR_MENU_BTN: \"toolbar-menu-btn\",\n INBOX_PANEL: \"inbox-panel\",\n INBOX_HEADER: \"inbox-header\",\n INBOX_FILTER: \"inbox-filter\",\n INBOX_FILTER_MENU: \"inbox-filter-menu\",\n INBOX_FILTER_OPTION: \"inbox-filter-option\",\n INBOX_FILTER_SECTION: \"inbox-filter-section\",\n INBOX_CLOSE: \"inbox-close\",\n INBOX_LIST: \"inbox-list\",\n INBOX_CARD: \"inbox-card\",\n INBOX_CARD_HEADER: \"inbox-card-header\",\n INBOX_CARD_ACTIONS: \"inbox-card-actions\",\n INBOX_CARD_TEXT: \"inbox-card-text\",\n INBOX_CARD_TAG: \"inbox-card-tag\",\n INBOX_CARD_REPLY_LINK: \"inbox-card-reply-link\",\n INBOX_ACTION_BTN: \"inbox-action-btn\",\n INBOX_ACTION_BTN_LABELED: \"inbox-action-btn--labeled\",\n INBOX_ACTION_LABEL: \"inbox-action-label\",\n INBOX_STATUS_DOT: \"inbox-status-dot\",\n INBOX_MENU: \"inbox-menu\",\n INBOX_MENU_ITEM: \"inbox-menu-item\",\n INBOX_DETAIL: \"inbox-detail\",\n INBOX_DETAIL_HEADER: \"inbox-detail-header\",\n INBOX_BACK: \"inbox-back\",\n INBOX_NAV_BTN: \"inbox-nav-btn\",\n INBOX_REPLIES: \"inbox-replies\",\n INBOX_EMPTY: \"inbox-empty\",\n CLASSIFY_ROW: \"classify-row\",\n TAGS_INPUT: \"tags-input\",\n TAG_CHIP: \"tag-chip\",\n TAG_CHIP_REMOVE: \"tag-chip-remove\",\n INBOX_BADGES: \"inbox-badges\",\n BADGE: \"helldots-badge\",\n BADGE_TYPE: \"helldots-badge--type\",\n BADGE_PRIORITY: \"helldots-badge--priority\",\n BADGE_TAG: \"helldots-badge--tag\",\n BADGE_DURATION: \"helldots-badge--duration\",\n CONTEXT_BLOCK: \"inbox-context\",\n CONTEXT_ROW: \"inbox-context-row\",\n CONTEXT_SCREENSHOT_CAPTION: \"inbox-context-screenshot-caption\",\n HIGHLIGHT: \"helldots-highlight\",\n};\n\nexport const IDS = {\n TOOLBAR: \"comment-toolbar\",\n COMMENT_BOX: \"comment-box\",\n COMMENT_INPUT: \"comment-input\",\n SUBMIT_COMMENT: \"submit-comment\",\n STYLES: \"comment-overlay-styles\",\n GLOBAL_STYLES: \"comment-overlay-global-styles\",\n ATTACH_IMAGE_INPUT: \"attach-image-input\",\n};\n\n// RF09 \u2014 comment lifecycle. Order matters: it's the order shown in the\n// status picker menu.\nexport const STATUSES = [\"open\", \"in_progress\", \"resolved\"];\n\nexport const STATUS_COLORS = {\n open: \"#2E90FA\",\n in_progress: \"#FF9F0A\",\n resolved: \"#30D158\",\n};\n\n// RF3 \u2014 comment category. Order matters: it's the order shown in the picker.\nexport const COMMENT_TYPES = [\"bug\", \"suggestion\", \"question\", \"improvement\"];\n\nexport const TYPE_COLORS = {\n bug: \"#FF453A\",\n suggestion: \"#BF5AF2\",\n question: \"#64D2FF\",\n improvement: \"#5E5CE6\",\n};\n\n// RF4 \u2014 priority, ordered high\u2192low so the picker reads as a scale.\nexport const PRIORITIES = [\"high\", \"medium\", \"low\"];\n\n// Deliberate red/orange/grey ramp: it reads as urgency at a glance. `high`\n// sharing red with `bug` (and `medium` sharing orange with `in_progress`) is\n// fine \u2014 they're different dimensions in different UI slots, and no badge\n// ever conveys meaning by colour alone (WCAG 1.4.1).\nexport const PRIORITY_COLORS = {\n high: \"#FF453A\",\n medium: \"#FF9F0A\",\n low: \"#8E8E93\",\n};\n\nexport const SELECTORS = {\n CONTAINER: 'section, div[class*=\"container\"], div[class*=\"content\"]',\n};\n\nexport const Z_INDEX = {\n CIRCLE: 9997,\n TOOLTIP: 10000,\n TOOLBAR: 9998,\n COMMENT_BOX: 9999,\n LIGHTBOX: 10001,\n};\n\nexport const CURSOR_SVG = `data:image/svg+xml;utf8,<svg width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g filter=\"url(%23filter0_d_4_97)\"><path d=\"M6 8C6 6.89543 6.89543 6 8 6H20C27.732 6 34 12.268 34 20V20C34 27.732 27.732 34 20 34V34C12.268 34 6 27.732 6 20V8Z\" fill=\"%232E90FA\"/><path d=\"M8 7H20C27.1797 7 33 12.8203 33 20C33 27.1797 27.1797 33 20 33C12.8203 33 7 27.1797 7 20V8C7 7.44772 7.44772 7 8 7Z\" stroke=\"white\" stroke-width=\"2\"/></g><filter id=\"filter0_d_4_97\" x=\"0\" y=\"0\" width=\"48\" height=\"48\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\"><feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/><feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\" result=\"hardAlpha\"/><feOffset dx=\"4\" dy=\"4\"/><feGaussianBlur stdDeviation=\"5\"/><feComposite in2=\"hardAlpha\" operator=\"out\"/><feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.180392 0 0 0 0 0.564706 0 0 0 0 0.980392 0 0 0 0.16 0\"/><feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow_4_97\"/><feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect1_dropShadow_4_97\" result=\"shape\"/></filter></svg>`;\n", "import { CLASSES, IDS, Z_INDEX, CURSOR_SVG } from \"./constants.js\";\n\nexport const getStyles = () => `\n\n :host {\n all: initial;\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n line-height: 1.5;\n color-scheme: light;\n }\n\n :host *,\n :host *::before,\n :host *::after {\n box-sizing: border-box;\n font-family: inherit;\n }\n\n button {\n padding: 0;\n font: inherit;\n color: inherit;\n }\n\n #${IDS.TOOLBAR} {\n position: fixed;\n bottom: 20px;\n left: 50%;\n transform: translateX(-50%);\n z-index: ${Z_INDEX.TOOLBAR};\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER} {\n position: relative;\n }\n\n .${CLASSES.TOOLBAR_ACTION_TOOLTIP} {\n position: absolute;\n bottom: calc(100% + 10px);\n left: 50%;\n transform: translateX(-50%) translateY(4px);\n display: flex;\n align-items: center;\n gap: 8px;\n background: rgba(20, 20, 23, 0.95);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n padding: 8px 12px;\n border-radius: 10px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35);\n color: white;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease, transform 0.15s ease;\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER}:hover .${\n CLASSES.TOOLBAR_ACTION_TOOLTIP\n } {\n opacity: 1;\n pointer-events: auto;\n transform: translateX(-50%) translateY(0);\n }\n\n .${CLASSES.TOOLBAR_TEXT} {\n font-size: 13px;\n font-weight: 500;\n letter-spacing: -0.01em;\n }\n\n .${CLASSES.SHORTCUT_HINT} {\n font-size: 11px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.5);\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(255, 255, 255, 0.1);\n padding: 2px 6px;\n border-radius: 5px;\n line-height: 1;\n white-space: nowrap;\n }\n\n .${CLASSES.TOOLBAR_ACTIONS} {\n display: flex;\n flex-direction: row;\n background: rgba(20, 20, 23, 0.95);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n border-radius: 12px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35);\n }\n\n .${CLASSES.TOOLBAR_ACTION_BTN} {\n width: 42px;\n height: 42px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n outline: none;\n color: rgba(255, 255, 255, 0.65);\n cursor: pointer;\n transition: background 0.2s, color 0.2s;\n padding: 0;\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER}:first-child .${\n CLASSES.TOOLBAR_ACTION_BTN\n } {\n border-radius: 12px 0 0 12px;\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER}:last-child .${\n CLASSES.TOOLBAR_ACTION_BTN\n } {\n border-radius: 0 12px 12px 0;\n }\n\n .${CLASSES.TOOLBAR_ACTION_BTN}:hover {\n background: rgba(255, 255, 255, 0.08);\n color: white;\n }\n\n .${CLASSES.TOOLBAR_COMMENT_BTN}.${CLASSES.ACTIVE} {\n color: #2E90FA;\n background: rgba(46, 144, 250, 0.1);\n }\n\n \n #${IDS.COMMENT_BOX} {\n position: fixed;\n background: #1C1C1E;\n border-radius: 12px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.4);\n padding: 16px;\n z-index: ${Z_INDEX.COMMENT_BOX};\n width: 400px;\n display: none;\n box-sizing: border-box;\n }\n \n #${IDS.COMMENT_BOX} .${CLASSES.COMMENT_INPUT_AREA} {\n display: flex;\n flex-direction: column;\n gap: 0;\n }\n\n .${CLASSES.CLASSIFY_ROW} {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 10px 0;\n }\n .${CLASSES.TAGS_INPUT} {\n flex: 1 1 90px;\n min-width: 90px;\n background: transparent;\n border: none;\n outline: none;\n color: inherit;\n font-size: 12px;\n padding: 2px 0;\n }\n .${CLASSES.TAG_CHIP} {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.1);\n font-size: 11px;\n line-height: 1.4;\n }\n .${CLASSES.TAG_CHIP_REMOVE} {\n background: none;\n border: none;\n color: inherit;\n cursor: pointer;\n padding: 0;\n font-size: 13px;\n line-height: 1;\n opacity: 0.6;\n }\n .${CLASSES.TAG_CHIP_REMOVE}:hover {\n opacity: 1;\n }\n\n #${IDS.COMMENT_INPUT} {\n flex: 1;\n min-height: 20px;\n background: #1C1C1E;\n border: none;\n resize: none;\n font-family: inherit;\n color: white;\n font-size: 14px;\n line-height: 1.4;\n box-sizing: border-box;\n field-sizing: content;\n padding-top: 4px;\n padding-bottom: 8px;\n }\n\n #${IDS.COMMENT_INPUT}::placeholder {\n color: rgba(255, 255, 255, 0.5);\n }\n \n #${IDS.COMMENT_INPUT}:focus {\n outline: none;\n box-shadow: none;\n }\n\n .${CLASSES.COMMENT_ACTIONS_BAR} {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-top: 12px;\n }\n\n .${CLASSES.ATTACH_IMAGE_BTN} {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.5);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: background 0.2s, color 0.2s;\n }\n\n .${CLASSES.ATTACH_IMAGE_BTN}:hover {\n background: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.8);\n }\n\n .${CLASSES.CIRCLE} {\n position: absolute;\n width: 28px;\n height: 28px;\n background: #2E90FA;\n border-radius: 0% 100% 100% 100%;\n border: 2px solid #FFF;\n cursor: pointer;\n box-shadow: 0 1px 5px rgba(0,0,0,0.2);\n transition: transform 0.2s, background 0.2s;\n z-index: ${Z_INDEX.CIRCLE};\n transform: translate(-50%, -50%);\n }\n\n .${CLASSES.CIRCLE}:hover {\n transform: translate(-50%, -50%) scale(1.2) !important;\n background: rgb(0, 123, 255);\n }\n\n .${CLASSES.CIRCLE}.${CLASSES.HIGHLIGHT} {\n transform: translate(-50%, -50%) scale(1.2) !important;\n background: rgb(0, 123, 255);\n box-shadow: 0 0 0 4px rgba(46, 144, 250, 0.35), 0 1px 5px rgba(0,0,0,0.2);\n }\n\n .${CLASSES.CIRCLE_WRAPPER} {\n position: absolute;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n z-index: ${Z_INDEX.CIRCLE};\n }\n\n .${CLASSES.TOOLTIP} {\n position: fixed;\n background: #1C1C1E;\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.4);\n width: 400px;\n z-index: ${Z_INDEX.TOOLTIP};\n color: white;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n }\n\n .${CLASSES.TOOLTIP} .${CLASSES.THREAD_HEADER} {\n padding: 0 0 0;\n }\n\n .${CLASSES.TOOLTIP} .${CLASSES.THREAD_BODY} {\n padding: 8px 0;\n }\n\n .${CLASSES.THREAD_POPOVER} {\n position: fixed;\n background: #1C1C1E;\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.4);\n width: 400px;\n z-index: ${Z_INDEX.TOOLTIP};\n color: white;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n }\n\n .${CLASSES.INBOX_PANEL} {\n position: fixed;\n top: 16px;\n right: 16px;\n bottom: 16px;\n width: 380px;\n display: flex;\n flex-direction: column;\n background: #1C1C1E;\n border: 1px solid rgba(255,255,255,0.08);\n border-radius: 14px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.5);\n z-index: ${Z_INDEX.COMMENT_BOX};\n color: white;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n @media (max-width: 420px) {\n .${CLASSES.INBOX_PANEL} {\n left: 16px;\n width: auto;\n }\n }\n\n .${CLASSES.INBOX_HEADER},\n .${CLASSES.INBOX_DETAIL_HEADER} {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 12px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.08);\n flex: none;\n }\n\n .${CLASSES.INBOX_FILTER}-wrapper {\n position: relative;\n }\n\n .${CLASSES.INBOX_FILTER} {\n display: flex;\n align-items: center;\n gap: 6px;\n background: transparent;\n border: none;\n color: white;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n padding: 4px 6px;\n border-radius: 6px;\n }\n\n .${CLASSES.INBOX_FILTER}:hover {\n background: rgba(255,255,255,0.08);\n }\n\n .${CLASSES.INBOX_FILTER_MENU} {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n background: #2C2C2E;\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 8px;\n padding: 4px;\n min-width: 190px;\n z-index: 1;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n }\n\n .${CLASSES.INBOX_FILTER_SECTION} {\n padding: 8px 10px 4px;\n font-size: 11px;\n font-weight: 600;\n color: rgba(255,255,255,0.45);\n text-transform: none;\n }\n\n .${CLASSES.INBOX_FILTER_SECTION}:not(:first-child) {\n margin-top: 4px;\n border-top: 1px solid rgba(255,255,255,0.1);\n padding-top: 10px;\n }\n\n .${CLASSES.INBOX_FILTER_OPTION} {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n }\n\n .${CLASSES.INBOX_FILTER_OPTION},\n .${CLASSES.INBOX_MENU_ITEM} {\n display: block;\n width: 100%;\n text-align: left;\n background: transparent;\n border: none;\n color: white;\n font-size: 13px;\n padding: 7px 10px;\n border-radius: 6px;\n cursor: pointer;\n }\n\n .${CLASSES.INBOX_FILTER_OPTION}:hover,\n .${CLASSES.INBOX_MENU_ITEM}:hover {\n background: rgba(255,255,255,0.08);\n }\n\n .${CLASSES.INBOX_CLOSE},\n .${CLASSES.INBOX_NAV_BTN},\n .${CLASSES.INBOX_BACK} {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: none;\n color: rgba(255,255,255,0.75);\n cursor: pointer;\n padding: 4px 6px;\n border-radius: 6px;\n font-size: 14px;\n }\n\n .${CLASSES.INBOX_CLOSE} {\n font-size: 20px;\n line-height: 1;\n }\n\n .${CLASSES.INBOX_CLOSE}:hover,\n .${CLASSES.INBOX_NAV_BTN}:not(:disabled):hover,\n .${CLASSES.INBOX_BACK}:hover {\n background: rgba(255,255,255,0.08);\n color: white;\n }\n\n .${CLASSES.INBOX_NAV_BTN}:disabled {\n opacity: 0.35;\n cursor: default;\n }\n\n .${CLASSES.INBOX_LIST},\n .${CLASSES.INBOX_DETAIL} {\n flex: 1;\n overflow-y: auto;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .${CLASSES.INBOX_CARD} {\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 10px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD} {\n cursor: pointer;\n }\n\n .${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD}:hover {\n border-color: rgba(255,255,255,0.22);\n }\n\n .${CLASSES.INBOX_CARD}--resolved {\n border-color: rgba(48, 209, 88, 0.4);\n opacity: 0.75;\n }\n\n .${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD}--resolved:hover {\n border-color: rgba(48, 209, 88, 0.7);\n opacity: 1;\n }\n\n .${CLASSES.INBOX_CARD_HEADER} {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n }\n\n .${CLASSES.INBOX_CARD_ACTIONS} {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .${CLASSES.INBOX_ACTION_BTN} {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n background: transparent;\n border: none;\n border-radius: 6px;\n color: rgba(255,255,255,0.65);\n cursor: pointer;\n }\n\n .${CLASSES.INBOX_ACTION_BTN}:hover {\n background: rgba(255,255,255,0.08);\n color: white;\n }\n\n /* Type/priority pickers show their current value as text next to the\n dot (colour alone can't tell bug/high apart \u2014 same hex, and there's\n no hover on touch). Grow from the square icon-button width but stay\n bounded so the strip doesn't crowd out copy/status/\u22EF next to it. */\n .${CLASSES.INBOX_ACTION_BTN_LABELED} {\n width: auto;\n max-width: 72px;\n padding: 0 8px 0 6px;\n gap: 5px;\n justify-content: flex-start;\n }\n\n .${CLASSES.INBOX_ACTION_LABEL} {\n font-size: 11px;\n line-height: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .${CLASSES.INBOX_STATUS_DOT} {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n border: 1.5px solid rgba(255,255,255,0.45);\n display: inline-block;\n flex: none;\n }\n\n .${CLASSES.INBOX_MENU_ITEM} .${CLASSES.INBOX_STATUS_DOT} {\n width: 9px;\n height: 9px;\n border: none;\n margin-right: 8px;\n vertical-align: baseline;\n }\n\n .${CLASSES.INBOX_MENU_ITEM}[aria-checked=\"true\"] {\n background: rgba(255,255,255,0.08);\n }\n\n .${CLASSES.THREAD_HEADER} .${CLASSES.INBOX_CARD_ACTIONS} {\n margin-left: auto;\n margin-right: 8px;\n }\n\n /* Generic hover tooltip, same look as .thread-time[data-full-date] */\n [data-hd-tooltip] {\n position: relative;\n }\n\n [data-hd-tooltip]::after {\n content: attr(data-hd-tooltip);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n background: #000;\n color: white;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 11px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.12s ease;\n z-index: 2;\n }\n\n [data-hd-tooltip]:hover::after {\n opacity: 1;\n }\n\n .${CLASSES.INBOX_MENU} {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n background: #2C2C2E;\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 8px;\n padding: 4px;\n min-width: 130px;\n z-index: 1;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n }\n\n .${CLASSES.INBOX_CARD_TEXT} {\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .${CLASSES.INBOX_CARD_TAG} {\n align-self: flex-start;\n padding: 1px 8px;\n border-radius: 999px;\n background: rgba(255, 159, 10, 0.2);\n color: #FF9F0A;\n font-size: 11px;\n font-weight: 600;\n }\n\n .${CLASSES.INBOX_BADGES} {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n /* Vertical separation from the card text above only applies to the\n badge row on inbox cards -- inside the comment-box classify row,\n .inbox-badges is one flex item among the type/priority pickers and\n the tags input, and the classify row's own gap already spaces it\n from its siblings, so an extra margin here would nudge it out of\n alignment with them. */\n .${CLASSES.INBOX_CARD} .${CLASSES.INBOX_BADGES} {\n margin-top: 6px;\n }\n .${CLASSES.BADGE} {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n border-radius: 10px;\n font-size: 10px;\n line-height: 1.6;\n letter-spacing: 0.01em;\n white-space: nowrap;\n }\n .${CLASSES.BADGE_TYPE},\n .${CLASSES.BADGE_PRIORITY} {\n font-weight: 600;\n }\n .${CLASSES.BADGE_TAG} {\n opacity: 0.75;\n }\n .${CLASSES.BADGE_DURATION} {\n opacity: 0.75;\n border-style: dashed;\n }\n\n .${CLASSES.CONTEXT_BLOCK} {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 10px 12px;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n font-size: 11px;\n }\n .${CLASSES.CONTEXT_BLOCK} img {\n width: 100%;\n border-radius: 6px;\n margin-bottom: 6px;\n cursor: zoom-in;\n }\n .${CLASSES.CONTEXT_SCREENSHOT_CAPTION} {\n opacity: 0.75;\n }\n .${CLASSES.CONTEXT_ROW} {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n opacity: 0.75;\n }\n /* URLs and user agents have no spaces to break on, so the value column\n would otherwise push the row wider than the panel. */\n .${CLASSES.CONTEXT_ROW} span:last-child {\n text-align: right;\n word-break: break-all;\n }\n\n .${CLASSES.INBOX_CARD_REPLY_LINK} {\n align-self: flex-start;\n background: transparent;\n border: none;\n color: rgba(255,255,255,0.55);\n font-size: 13px;\n cursor: pointer;\n padding: 0;\n }\n\n .${CLASSES.INBOX_CARD_REPLY_LINK}:hover {\n color: white;\n }\n\n .${CLASSES.INBOX_REPLIES} {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 0 4px;\n }\n\n .${CLASSES.INBOX_EMPTY} {\n padding: 24px 12px;\n color: rgba(255,255,255,0.55);\n text-align: center;\n }\n\n .${CLASSES.THREAD_HEADER} {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 0 0;\n }\n\n .${CLASSES.THREAD_META} {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .${CLASSES.THREAD_AUTHOR} {\n font-weight: 600;\n font-size: 13px;\n }\n\n .${CLASSES.THREAD_TIME} {\n font-size: 12px;\n color: rgba(255,255,255,0.5);\n cursor: default;\n position: relative;\n }\n\n .${CLASSES.THREAD_TIME}::after {\n content: attr(data-full-date);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n background: #000;\n color: white;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 11px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n z-index: 1;\n }\n\n .${CLASSES.THREAD_TIME}:hover::after {\n opacity: 1;\n }\n\n .${CLASSES.THREAD_BODY} {\n padding: 8px 0;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .${CLASSES.THREAD_REPLIES} {\n padding: 0;\n }\n\n .${CLASSES.THREAD_REPLIES}:empty {\n display: none;\n }\n\n .${CLASSES.THREAD_REPLY} {\n padding: 16px 0 0 0;\n border-top: 1px solid rgba(255,255,255,0.1);\n white-space: pre-wrap;\n word-break: break-word;\n font-size: 13px;\n color: rgba(255,255,255,0.85);\n }\n\n .${CLASSES.THREAD_REPLY} .${CLASSES.THREAD_META} {\n margin-bottom: 2px;\n }\n\n .${CLASSES.THREAD_REPLY} .${CLASSES.SCREENSHOT_IMG} {\n width: 144px;\n height: 100px;\n object-fit: cover;\n border-radius: 8px;\n margin-top: 4px;\n cursor: pointer;\n display: block;\n }\n\n .${CLASSES.THREAD_INPUT_AREA} {\n display: flex;\n flex-direction: column;\n gap: 0;\n padding: 12px 0 0;\n border-top: 1px solid rgba(255,255,255,0.1);\n }\n\n .${CLASSES.THREAD_INPUT} {\n width: 100%;\n background: transparent;\n border: none;\n padding: 0;\n color: white;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n box-sizing: border-box;\n }\n\n .${CLASSES.THREAD_INPUT}::placeholder {\n color: rgba(255,255,255,0.5);\n }\n\n .${CLASSES.THREAD_INPUT}:focus {\n outline: none;\n box-shadow: none;\n }\n\n .${CLASSES.THREAD_SUBMIT} {\n background: none;\n border: none;\n color: #2E90FA;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: background 0.2s;\n }\n\n .${CLASSES.THREAD_SUBMIT}:hover {\n background: rgba(46, 144, 250, 0.15);\n color: #1570D6;\n }\n\n .${CLASSES.CLOSE_TOOLTIP} {\n background: none;\n border: none;\n font-size: 18px;\n cursor: pointer;\n color: rgba(255,255,255,0.5);\n line-height: 1;\n }\n \n .${CLASSES.CLOSE_TOOLTIP}:hover {\n color: white;\n }\n\n .${CLASSES.PREVIEW_CIRCLE} {\n animation: helldots-pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes helldots-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(46, 144, 250, 0.4), 0 1px 5px rgba(0,0,0,0.2); }\n 50% { box-shadow: 0 0 0 8px rgba(46, 144, 250, 0), 0 1px 5px rgba(0,0,0,0.2); }\n }\n\n .${CLASSES.COMMENT_OVERLAY} {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: transparent;\n pointer-events: none;\n z-index: ${Z_INDEX.TOOLBAR - 1};\n }\n\n .${CLASSES.COMMENT_OVERLAY}.${CLASSES.ACTIVE} {\n pointer-events: auto;\n background: rgba(0, 0, 0, 0.1);\n }\n\n .${CLASSES.SELECTION_RECT} {\n position: fixed;\n border: 2px solid #2E90FA;\n background: rgba(46, 144, 250, 0.1);\n pointer-events: none;\n z-index: ${Z_INDEX.TOOLTIP};\n box-sizing: border-box;\n }\n\n .${CLASSES.SCREENSHOTS_CONTAINER} {\n display: none;\n overflow-x: auto;\n gap: 8px;\n margin-top: 4px;\n padding: 4px 0;\n scrollbar-width: none;\n -ms-overflow-style: none;\n margin-bottom: 16px;\n }\n\n .${CLASSES.SCREENSHOTS_CONTAINER}::-webkit-scrollbar {\n display: none;\n }\n\n .${CLASSES.SCREENSHOTS_CONTAINER}.${CLASSES.ACTIVE} {\n display: flex;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} {\n position: relative;\n flex-shrink: 0;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_IMG} {\n width: 50px;\n height: 50px;\n object-fit: cover;\n border-radius: 8px;\n cursor: pointer;\n display: block;\n margin: 0;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_IMG}:hover {\n opacity: 0.85;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_REMOVE} {\n position: absolute;\n top: -5px;\n right: -5px;\n width: 18px;\n height: 18px;\n background: rgba(0,0,0,0.7);\n border: none;\n border-radius: 50%;\n color: white;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n display: none;\n align-items: center;\n justify-content: center;\n z-index: 1;\n }\n\n .${CLASSES.SCREENSHOT_ITEM}:hover .${CLASSES.SCREENSHOT_REMOVE} {\n display: flex;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_REMOVE}:hover {\n background: rgba(0,0,0,0.9);\n }\n\n .${CLASSES.TOOLTIP} > .${CLASSES.SCREENSHOTS_CONTAINER} .${\n CLASSES.SCREENSHOT_ITEM\n } .${CLASSES.SCREENSHOT_IMG},\n .${CLASSES.THREAD_POPOVER} > .${CLASSES.SCREENSHOTS_CONTAINER} .${\n CLASSES.SCREENSHOT_ITEM\n } .${CLASSES.SCREENSHOT_IMG},\n .${CLASSES.THREAD_REPLY} .${CLASSES.SCREENSHOT_ITEM} .${\n CLASSES.SCREENSHOT_IMG\n } {\n width: 144px;\n height: 100px;\n }\n\n .${CLASSES.LIGHTBOX} {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0,0,0,0.92);\n z-index: ${Z_INDEX.LIGHTBOX};\n display: flex;\n align-items: center;\n justify-content: center;\n animation: helldots-fade-in 0.2s ease;\n }\n\n @keyframes helldots-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .${CLASSES.LIGHTBOX_IMG} {\n max-width: 90vw;\n max-height: 90vh;\n object-fit: contain;\n border-radius: 8px;\n }\n\n .${CLASSES.LIGHTBOX_CLOSE} {\n position: absolute;\n top: 16px;\n right: 16px;\n background: rgba(255,255,255,0.15);\n border: none;\n color: white;\n font-size: 24px;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .${CLASSES.LIGHTBOX_CLOSE}:hover {\n background: rgba(255,255,255,0.3);\n }\n`;\n\n/**\n * Styles that must apply to the host page itself (outside the shadow root),\n * because they target elements HellDots doesn't own \u2014 e.g. `document.body`\n * while in comment mode. A shadow root's stylesheet never reaches outside\n * it, so these can't live in `getStyles()`; they're injected separately\n * into `document.head` instead (see `CommentOverlay.injectStyles`).\n */\nexport const getGlobalStyles = () => `\n .${CLASSES.COMMENT_CURSOR},\n .${CLASSES.COMMENT_CURSOR} * {\n cursor: url('${CURSOR_SVG}') 6 6, auto !important;\n }\n`;\n", "export default {\n commentAriaLabelPrefix: \"Comment: \",\n anonymous: \"Anonymous\",\n justNow: \"Just now\",\n minutesAgoTemplate: \"{n}m\",\n hoursAgoTemplate: \"{n}h\",\n daysAgoTemplate: \"{n}d\",\n toolbarComment: \"Comment\",\n toolbarInbox: \"Inbox\",\n modifierAlt: \"Alt\",\n modifierCtrl: \"Ctrl\",\n commentBoxAriaLabel: \"New comment\",\n commentPlaceholder: \"Type your comment...\",\n attachImage: \"Attach image\",\n send: \"Send\",\n tooltipAriaLabel: \"Comment preview\",\n close: \"Close\",\n popoverAriaLabel: \"Comment thread\",\n replyPlaceholder: \"Reply...\",\n attachedScreenshot: \"Attached screenshot\",\n screenshotPreview: \"Screenshot preview\",\n removeScreenshot: \"Remove screenshot\",\n inboxAriaLabel: \"Comments inbox\",\n inboxEmpty: \"No comments yet\",\n orphanedBadge: \"Unanchored\",\n hiddenBadge: \"Hidden\",\n filterAll: \"All comments\",\n filterCurrentPage: \"Current page\",\n filterByPage: \"Filter by Page\",\n filterByStatus: \"Filter by Status\",\n filterStatusAll: \"All\",\n filterUnresolved: \"Unresolved\",\n filterResolved: \"Resolved\",\n back: \"Back\",\n deleteComment: \"Delete\",\n copyAgentContext: \"Copy agent context\",\n copied: \"Copied\",\n statusLabel: \"Status\",\n prevComment: \"Previous comment\",\n nextComment: \"Next comment\",\n replyLink: \"Reply\",\n commentOptions: \"Comment options\",\n moreOptions: \"More\",\n statusOpen: \"Open\",\n statusInProgress: \"In progress\",\n statusResolved: \"Resolved\",\n durationLessThanMinute: \"<1m\",\n resolvedInTemplate: \"Resolved in {n}\",\n typeLabel: \"Type\",\n priorityLabel: \"Priority\",\n unset: \"Unset\",\n typeBug: \"Bug\",\n typeSuggestion: \"Suggestion\",\n typeQuestion: \"Question\",\n typeImprovement: \"Improvement\",\n priorityHigh: \"High\",\n priorityMedium: \"Medium\",\n priorityLow: \"Low\",\n tagsPlaceholder: \"Add tags...\",\n removeTag: \"Remove tag\",\n filterByType: \"Filter by Type\",\n filterByPriority: \"Filter by Priority\",\n contextSection: \"Context\",\n autoScreenshotLabel: \"Automatic context\",\n contextUrl: \"URL\",\n contextViewport: \"Viewport\",\n contextScreen: \"Screen\",\n contextBrowser: \"Browser\",\n contextOs: \"OS\",\n};\n", "export default {\n commentAriaLabelPrefix: \"Comentario: \",\n anonymous: \"An\u00F3nimo\",\n justNow: \"Justo ahora\",\n minutesAgoTemplate: \"{n}m\",\n hoursAgoTemplate: \"{n}h\",\n daysAgoTemplate: \"{n}d\",\n toolbarComment: \"Comentar\",\n toolbarInbox: \"Bandeja\",\n modifierAlt: \"Alt\",\n modifierCtrl: \"Ctrl\",\n commentBoxAriaLabel: \"Nuevo comentario\",\n commentPlaceholder: \"Escribe tu comentario...\",\n attachImage: \"Adjuntar imagen\",\n send: \"Enviar\",\n tooltipAriaLabel: \"Vista previa del comentario\",\n close: \"Cerrar\",\n popoverAriaLabel: \"Hilo de comentarios\",\n replyPlaceholder: \"Responder...\",\n attachedScreenshot: \"Captura de pantalla adjunta\",\n screenshotPreview: \"Vista previa de la captura\",\n removeScreenshot: \"Quitar captura de pantalla\",\n inboxAriaLabel: \"Bandeja de comentarios\",\n inboxEmpty: \"A\u00FAn no hay comentarios\",\n orphanedBadge: \"Desanclado\",\n hiddenBadge: \"Oculto\",\n filterAll: \"Todos los comentarios\",\n filterCurrentPage: \"P\u00E1gina actual\",\n filterByPage: \"Filtrar por p\u00E1gina\",\n filterByStatus: \"Filtrar por estado\",\n filterStatusAll: \"Todos\",\n filterUnresolved: \"Sin resolver\",\n filterResolved: \"Resueltos\",\n back: \"Volver\",\n deleteComment: \"Eliminar\",\n copyAgentContext: \"Copiar contexto de agente\",\n copied: \"Copiado\",\n statusLabel: \"Estado\",\n prevComment: \"Comentario anterior\",\n nextComment: \"Comentario siguiente\",\n replyLink: \"Responder\",\n commentOptions: \"Opciones del comentario\",\n moreOptions: \"M\u00E1s\",\n statusOpen: \"Abierto\",\n statusInProgress: \"En progreso\",\n statusResolved: \"Resuelto\",\n durationLessThanMinute: \"<1m\",\n resolvedInTemplate: \"Resuelto en {n}\",\n typeLabel: \"Tipo\",\n priorityLabel: \"Prioridad\",\n unset: \"Sin definir\",\n typeBug: \"Bug\",\n typeSuggestion: \"Sugerencia\",\n typeQuestion: \"Pregunta\",\n typeImprovement: \"Mejora\",\n priorityHigh: \"Alta\",\n priorityMedium: \"Media\",\n priorityLow: \"Baja\",\n tagsPlaceholder: \"A\u00F1adir etiquetas...\",\n removeTag: \"Quitar etiqueta\",\n filterByType: \"Filtrar por tipo\",\n filterByPriority: \"Filtrar por prioridad\",\n contextSection: \"Contexto\",\n autoScreenshotLabel: \"Contexto autom\u00E1tico\",\n contextUrl: \"URL\",\n contextViewport: \"Viewport\",\n contextScreen: \"Pantalla\",\n contextBrowser: \"Navegador\",\n contextOs: \"SO\",\n};\n", "import en from \"./locales/en.js\";\nimport es from \"./locales/es.js\";\n\nconst LOCALES = { en, es };\nconst DEFAULT_LOCALE = \"en\";\n\n/**\n * Picks a supported locale code from the browser's language, falling back\n * to English for anything HellDots doesn't ship a translation for.\n * @returns {\"en\" | \"es\"}\n */\nexport function detectLocale() {\n const lang = (navigator.language || DEFAULT_LOCALE).slice(0, 2).toLowerCase();\n return lang in LOCALES ? /** @type {\"en\" | \"es\"} */ (lang) : DEFAULT_LOCALE;\n}\n\n/**\n * Resolves the UI strings dictionary for a given locale code, falling back\n * to English if the code isn't one HellDots ships.\n * @param {string} [localeCode]\n * @returns {typeof en}\n */\nexport function getStrings(localeCode) {\n return LOCALES[localeCode] || LOCALES[DEFAULT_LOCALE];\n}\n\n/**\n * Substitutes `{n}` in a template string, used for relative time labels and\n * resolution-duration badges (where the substitution is already a string,\n * e.g. \"2d 4h\" or the \"\u2014\" fallback).\n * @param {string} template\n * @param {number | string} n\n */\nexport function formatTemplate(template, n) {\n return template.replace(\"{n}\", String(n));\n}\n\nconst MINUTE_MS = 60_000;\n\n/**\n * RF5 \u2014 human-readable elapsed time (\"<1m\", \"45m\", \"3h 12m\", \"2d 4h\").\n * Reuses the same {n}-templates the relative timestamps already use.\n * @param {number} ms\n * @param {ReturnType<typeof getStrings>} strings\n * @returns {string} empty string when `ms` isn't a usable duration\n */\nexport function formatDuration(ms, strings) {\n if (!Number.isFinite(ms) || ms < 0) return \"\";\n\n const totalMinutes = Math.floor(ms / MINUTE_MS);\n if (totalMinutes < 1) return strings.durationLessThanMinute;\n if (totalMinutes < 60) {\n return formatTemplate(strings.minutesAgoTemplate, totalMinutes);\n }\n\n const totalHours = Math.floor(totalMinutes / 60);\n if (totalHours < 24) {\n const minutes = totalMinutes % 60;\n const hours = formatTemplate(strings.hoursAgoTemplate, totalHours);\n return minutes\n ? `${hours} ${formatTemplate(strings.minutesAgoTemplate, minutes)}`\n : hours;\n }\n\n const totalDays = Math.floor(totalHours / 24);\n const hours = totalHours % 24;\n const days = formatTemplate(strings.daysAgoTemplate, totalDays);\n return hours\n ? `${days} ${formatTemplate(strings.hoursAgoTemplate, hours)}`\n : days;\n}\n", "// Serializable comment anchors. An anchor pairs a best-effort CSS selector\n// (fast path) with a content fingerprint (verification + rescue path) so a\n// comment can be re-attached to its element after a page reload. Design:\n// docs/superpowers/specs/2026-07-02-comment-anchoring-design.md\n\nconst TEXT_SNIPPET_MAX = 64;\nconst MAX_CLASS_PATH_DEPTH = 3;\nconst MAX_STRUCTURAL_DEPTH = 5;\n\n// A selector match is verified against the fingerprint before being trusted;\n// the rescue path (fingerprint-only, no structural signal) demands more.\nconst SELECTOR_THRESHOLD = 0.6;\nconst RESCUE_THRESHOLD = 0.7;\n\nconst GENERATED_CLASS_PREFIX_RE = /^(css|sc|jsx|emotion)-/i;\nconst FRAMEWORK_DATA_ATTR_RE = /^data-(reactid|react-|v-|svelte-)/;\nconst STABLE_ATTR_NAMES = [\"id\", \"name\", \"role\", \"aria-label\"];\nconst SELECTOR_ATTR_NAMES = [\"data-testid\", \"name\", \"aria-label\"];\n\nconst escapeCss = (value) => {\n if (typeof CSS !== \"undefined\" && CSS.escape) return CSS.escape(value);\n return String(value).replace(/[^a-zA-Z0-9_-]/g, \"\\\\$&\");\n};\n\nconst escapeAttrValue = (value) => String(value).replace(/[\"\\\\]/g, \"\\\\$&\");\n\nconst isUnique = (selector, doc) => {\n try {\n return doc.querySelectorAll(selector).length === 1;\n } catch {\n return false;\n }\n};\n\nconst normalizeText = (text) =>\n (text || \"\").replace(/\\s+/g, \" \").trim().slice(0, TEXT_SNIPPET_MAX);\n\n// Heuristic: tooling-generated class names (CSS-in-JS, scoped-CSS hashes)\n// either carry a known prefix or contain a long token with digits in it.\nconst isStableClass = (cls) => {\n if (GENERATED_CLASS_PREFIX_RE.test(cls)) return false;\n return !cls.split(/[-_]/).some((part) => part.length >= 5 && /\\d/.test(part));\n};\n\nconst stableClassesOf = (element) =>\n [...element.classList].filter(isStableClass);\n\nconst stableAttributes = (element) => {\n /** @type {Record<string, string>} */\n const attrs = {};\n for (const { name, value } of element.attributes) {\n const isStable =\n STABLE_ATTR_NAMES.includes(name) ||\n (name.startsWith(\"data-\") && !FRAMEWORK_DATA_ATTR_RE.test(name));\n if (isStable && value) attrs[name] = value.slice(0, TEXT_SNIPPET_MAX);\n }\n return attrs;\n};\n\nconst siblingPosition = (element) => {\n const parent = element.parentElement;\n if (!parent) return { index: 0, count: 1 };\n const sameTag = [...parent.children].filter(\n (child) => child.tagName === element.tagName\n );\n return { index: sameTag.indexOf(element), count: sameTag.length };\n};\n\nconst idSelector = (element, doc) => {\n if (!element.id) return null;\n const selector = `#${escapeCss(element.id)}`;\n return isUnique(selector, doc) ? selector : null;\n};\n\nconst attributeSelector = (element, doc) => {\n const tag = element.tagName.toLowerCase();\n for (const name of SELECTOR_ATTR_NAMES) {\n const value = element.getAttribute(name);\n if (!value) continue;\n const selector = `${tag}[${name}=\"${escapeAttrValue(value)}\"]`;\n if (isUnique(selector, doc)) return selector;\n }\n return null;\n};\n\nconst classPathSelector = (element, doc) => {\n const segments = [];\n let current = element;\n for (let depth = 0; depth < MAX_CLASS_PATH_DEPTH && current; depth++) {\n const classes = stableClassesOf(current);\n const tag = current.tagName.toLowerCase();\n segments.unshift(\n classes.length ? `${tag}.${classes.map(escapeCss).join(\".\")}` : tag\n );\n // The element's own segment must carry at least one stable class for\n // this strategy to say anything a structural path wouldn't.\n if (depth === 0 && !classes.length) return null;\n const selector = segments.join(\" > \");\n if (isUnique(selector, doc)) return selector;\n current = current.parentElement;\n }\n return null;\n};\n\nconst structuralSelector = (element, doc) => {\n if (element === doc.body) return \"body\";\n const segments = [];\n let current = element;\n for (let depth = 0; depth < MAX_STRUCTURAL_DEPTH && current; depth++) {\n if (current === doc.body) {\n segments.unshift(\"body\");\n break;\n }\n if (current.id) {\n const rooted = [`#${escapeCss(current.id)}`, ...segments].join(\" > \");\n if (isUnique(rooted, doc)) return rooted;\n }\n const { index } = siblingPosition(current);\n segments.unshift(\n `${current.tagName.toLowerCase()}:nth-of-type(${index + 1})`\n );\n current = current.parentElement;\n }\n const selector = segments.join(\" > \");\n return isUnique(selector, doc) ? selector : null;\n};\n\nconst generateSelector = (element, doc) =>\n idSelector(element, doc) ||\n attributeSelector(element, doc) ||\n classPathSelector(element, doc) ||\n structuralSelector(element, doc);\n\n/**\n * Best-effort unique CSS selector for any element (or null). Exposed for\n * the overlay's target-visibility tracking; same cascade used by anchors.\n * @param {HTMLElement} element\n * @returns {string | null}\n */\nexport function generateElementSelector(element) {\n return generateSelector(element, element.ownerDocument);\n}\n\n/**\n * Captures a serializable anchor for `element` at creation time.\n * @param {HTMLElement} element\n * @param {number} relativeX\n * @param {number} relativeY\n * @returns {import('./index.d.ts').CommentAnchor}\n */\nexport function createAnchor(element, relativeX, relativeY) {\n const doc = element.ownerDocument;\n const { index, count } = siblingPosition(element);\n return {\n version: 1,\n selector: generateSelector(element, doc),\n fingerprint: {\n tagName: element.tagName,\n textSnippet: normalizeText(element.textContent),\n attributes: stableAttributes(element),\n siblingIndex: index,\n siblingCount: count,\n },\n relativeX,\n relativeY,\n };\n}\n\nconst textSimilarity = (a, b) => {\n if (!a && !b) return 1;\n if (!a || !b) return 0;\n if (a === b) return 1;\n if (a.startsWith(b) || b.startsWith(a)) return 0.8;\n const tokensA = new Set(a.split(\" \"));\n const tokensB = new Set(b.split(\" \"));\n let common = 0;\n for (const token of tokensA) if (tokensB.has(token)) common++;\n return (2 * common) / (tokensA.size + tokensB.size);\n};\n\nconst attributeSimilarity = (element, attrs) => {\n const names = Object.keys(attrs);\n if (!names.length) return 1;\n let matched = 0;\n for (const name of names) {\n if (\n (element.getAttribute(name) || \"\").slice(0, TEXT_SNIPPET_MAX) ===\n attrs[name]\n ) {\n matched++;\n }\n }\n return matched / names.length;\n};\n\nconst positionSimilarity = (element, fingerprint) => {\n const { index, count } = siblingPosition(element);\n const delta = Math.abs(index - fingerprint.siblingIndex);\n const span = Math.max(fingerprint.siblingCount, count, 1);\n return Math.max(0, 1 - delta / span);\n};\n\nconst scoreElement = (element, fingerprint) => {\n if (element.tagName !== fingerprint.tagName) return 0;\n\n const hasText = Boolean(fingerprint.textSnippet);\n const hasAttrs = Object.keys(fingerprint.attributes || {}).length > 0;\n\n // Base weights (text 0.5 / attrs 0.3 / position 0.2); a missing signal's\n // weight shifts to the other content signal so the scale stays 0\u20131.\n let textWeight = 0.5;\n let attrWeight = 0.3;\n const posWeight = 0.2;\n if (!hasAttrs) {\n textWeight += attrWeight;\n attrWeight = 0;\n } else if (!hasText) {\n attrWeight += textWeight;\n textWeight = 0;\n }\n\n // Degenerate fingerprint: only structural signal remains.\n if (!hasText && !hasAttrs) {\n return positionSimilarity(element, fingerprint);\n }\n\n return (\n textWeight *\n textSimilarity(\n normalizeText(element.textContent),\n fingerprint.textSnippet\n ) +\n attrWeight * attributeSimilarity(element, fingerprint.attributes || {}) +\n posWeight * positionSimilarity(element, fingerprint)\n );\n};\n\nconst bestMatch = (candidates, fingerprint) => {\n let best = null;\n for (const element of candidates) {\n const confidence = scoreElement(element, fingerprint);\n if (!best || confidence > best.confidence) best = { element, confidence };\n }\n return best;\n};\n\n/**\n * Re-locates the element an anchor points at, or null if no candidate is\n * trustworthy (orphaned comment). Never throws.\n * @param {import('./index.d.ts').CommentAnchor} anchor\n * @param {Document} [doc]\n * @returns {{ element: HTMLElement, confidence: number } | null}\n */\nexport function resolveAnchor(anchor, doc = document) {\n const fingerprint = anchor?.fingerprint;\n if (!fingerprint || !fingerprint.tagName) return null;\n\n if (anchor.selector) {\n let candidates = [];\n try {\n candidates = [...doc.querySelectorAll(anchor.selector)];\n } catch {\n // Corrupt selector \u2014 the rescue search below still applies.\n }\n const best = bestMatch(candidates, fingerprint);\n if (best && best.confidence >= SELECTOR_THRESHOLD) return best;\n }\n\n // Rescue search: only meaningful when the fingerprint carries content\n // signal \u2014 anonymous elements would make any tag-wide match a guess.\n const hasSignal =\n Boolean(fingerprint.textSnippet) ||\n Object.keys(fingerprint.attributes || {}).length > 0;\n if (!hasSignal) return null;\n\n let candidates;\n try {\n candidates = [...doc.querySelectorAll(fingerprint.tagName)];\n } catch {\n return null;\n }\n const best = bestMatch(candidates, fingerprint);\n return best && best.confidence >= RESCUE_THRESHOLD ? best : null;\n}\n", "// localStorage adapter for the optional `persistence: \"localStorage\"` mode.\n// One key holds the serialized comments of EVERY page (the inbox \"all\n// comments\" filter needs them); merge logic keeps other pages' entries\n// intact while treating in-memory state as the source of truth for ids it\n// knows about. Storage failures (quota, disabled, corrupt JSON) never\n// throw \u2014 the widget just runs without persistence.\n\nexport const STORAGE_KEY = \"helldots-comments\";\n\n// sessionStorage handoff: set right before navigating to another page so\n// the overlay there opens the inbox directly on that comment's detail.\nexport const PENDING_DETAIL_KEY = \"helldots-pending-detail\";\n\n/**\n * @returns {import('./index.d.ts').SerializedComment[]}\n */\nexport function readStoredComments() {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return [];\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch (err) {\n console.warn(\"HellDots: could not read stored comments\", err);\n return [];\n }\n}\n\nfunction tryWriteStoredComments(comments) {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(comments));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Writes the full cross-page corpus. Each comment can carry a base64 JPEG\n * `contextScreenshot` (~33KB) captured automatically (RF1/RF2); a growing\n * corpus of those eventually blows through the ~5MB localStorage quota. On\n * a failed write, the automatic screenshots are the only thing sacrificed \u2014\n * dropped one at a time starting with the oldest comment \u2014 so the comments\n * themselves (and any deliberate, user-attached `screenshots[]`) survive.\n * Never throws: a hostile or disabled localStorage just means no\n * persistence, not a broken widget.\n * @param {import('./index.d.ts').SerializedComment[]} comments\n * @returns {boolean} true once the write succeeded, possibly after shedding\n * automatic screenshots; false if it still failed with nothing left to shed\n */\nexport function writeStoredComments(comments) {\n if (tryWriteStoredComments(comments)) return true;\n\n // Oldest first (by createdAt; ties/missing dates fall back to array\n // order), and only entries that actually have something to shed.\n const shedOrder = comments\n .map((comment, index) => ({ comment, index }))\n .filter(({ comment }) => comment?.contextScreenshot)\n .sort((a, b) => {\n const timeA = Date.parse(a.comment.createdAt);\n const timeB = Date.parse(b.comment.createdAt);\n if (Number.isFinite(timeA) && Number.isFinite(timeB) && timeA !== timeB) {\n return timeA - timeB;\n }\n return a.index - b.index;\n });\n\n if (shedOrder.length === 0) {\n console.warn(\n \"HellDots: could not persist comments (storage quota exceeded, nothing left to shed)\"\n );\n return false;\n }\n\n const working = [...comments];\n let shed = 0;\n for (const { index } of shedOrder) {\n working[index] = { ...working[index], contextScreenshot: null };\n shed++;\n if (tryWriteStoredComments(working)) {\n console.warn(\n `HellDots: localStorage quota exceeded \u2014 dropped the automatic ` +\n `context screenshot from the ${shed} oldest comment(s) to keep ` +\n `all comments persisted. Comment text, replies and user-attached ` +\n `screenshots were not touched.`\n );\n return true;\n }\n }\n\n console.warn(\n `HellDots: could not persist comments even after dropping all ${shed} ` +\n `automatic context screenshot(s); storage quota exceeded`\n );\n return false;\n}\n\n/**\n * Merges the in-memory snapshot into what's already stored: entries the\n * memory knows about (by id) and entries of the current page are replaced\n * by the snapshot; entries from other pages are preserved.\n * @param {import('./index.d.ts').SerializedComment[]} stored\n * @param {import('./index.d.ts').SerializedComment[]} current\n * @param {string} currentPage\n * @returns {import('./index.d.ts').SerializedComment[]}\n */\nexport function mergeForStorage(stored, current, currentPage) {\n const currentIds = new Set(current.map((c) => c.id));\n const kept = stored.filter(\n (c) => !currentIds.has(c.id) && c.page !== currentPage\n );\n return [...kept, ...current];\n}\n", "// Shared per-comment action strip (copy agent context / lifecycle status /\n// more menu) used by both the inbox cards and the thread popover header.\n// Pure view: every mutation goes through the callbacks; the component only\n// keeps its own dot color and tooltips in sync after a selection.\n\nimport {\n CLASSES,\n STATUSES,\n STATUS_COLORS,\n COMMENT_TYPES,\n TYPE_COLORS,\n PRIORITIES,\n PRIORITY_COLORS,\n} from \"./constants.js\";\n\nconst COPY_ICON_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg>`;\nconst CHECK_ICON_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\nconst DOTS_ICON_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><circle cx=\"5\" cy=\"12\" r=\"1.6\"/><circle cx=\"12\" cy=\"12\" r=\"1.6\"/><circle cx=\"19\" cy=\"12\" r=\"1.6\"/></svg>`;\n\nexport const copyToClipboard = (text) => {\n if (navigator.clipboard?.writeText) {\n return navigator.clipboard.writeText(text).catch(() => {});\n }\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n document.execCommand(\"copy\");\n } catch {}\n textarea.remove();\n return Promise.resolve();\n};\n\nexport const statusLabelOf = (status, strings) =>\n ({\n open: strings.statusOpen,\n in_progress: strings.statusInProgress,\n resolved: strings.statusResolved,\n })[status] || strings.statusOpen;\n\nexport const typeLabelOf = (type, strings) =>\n ({\n bug: strings.typeBug,\n suggestion: strings.typeSuggestion,\n question: strings.typeQuestion,\n improvement: strings.typeImprovement,\n })[type] || strings.unset;\n\nexport const priorityLabelOf = (priority, strings) =>\n ({\n high: strings.priorityHigh,\n medium: strings.priorityMedium,\n low: strings.priorityLow,\n })[priority] || strings.unset;\n\n/**\n * Dot-and-menu picker shared by the status, type and priority controls.\n * Keeps its own copy of the selection so the UI stays correct even when the\n * consumer's onSelect is async or doesn't mutate the comment in place.\n * @param {{\n * action: string,\n * options: Array<string|null>,\n * value: string|null,\n * colorOf: (option: string|null) => string,\n * labelOf: (option: string|null) => string,\n * tooltipLabel: string,\n * onSelect: (option: string|null) => void,\n * showLabel?: boolean,\n * }} config\n * @returns {HTMLElement}\n */\nexport const createPicker = ({\n action,\n options,\n value,\n colorOf,\n labelOf,\n tooltipLabel,\n onSelect,\n showLabel = false,\n}) => {\n const wrapper = document.createElement(\"div\");\n wrapper.style.position = \"relative\";\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_ACTION_BTN;\n if (showLabel) btn.classList.add(CLASSES.INBOX_ACTION_BTN_LABELED);\n btn.dataset.action = action;\n btn.setAttribute(\"aria-haspopup\", \"true\");\n\n const dot = document.createElement(\"span\");\n dot.className = CLASSES.INBOX_STATUS_DOT;\n btn.appendChild(dot);\n\n // Type and priority share exact colours (bug === high === #FF453A), and\n // unset shares \"no colour\" with unset \u2014 the dot alone can't tell them\n // apart, and hover-only disambiguation doesn't exist on touch. A short\n // text label next to the dot makes the current value legible without it.\n let labelEl = null;\n if (showLabel) {\n labelEl = document.createElement(\"span\");\n labelEl.className = CLASSES.INBOX_ACTION_LABEL;\n btn.appendChild(labelEl);\n }\n\n const menu = document.createElement(\"div\");\n menu.className = CLASSES.INBOX_MENU;\n menu.style.display = \"none\";\n menu.setAttribute(\"role\", \"menu\");\n\n let current = value;\n\n const syncUi = () => {\n const label = `${tooltipLabel}: ${labelOf(current)}`;\n dot.style.backgroundColor = colorOf(current);\n btn.dataset.hdTooltip = label;\n btn.setAttribute(\"aria-label\", label);\n if (labelEl) labelEl.textContent = labelOf(current);\n menu\n .querySelectorAll(\"[data-picker-option]\")\n .forEach((/** @type {HTMLElement} */ item) => {\n const raw = item.dataset.pickerOption;\n const option = raw === \"\" ? null : raw;\n item.setAttribute(\"aria-checked\", String(option === current));\n });\n };\n\n for (const option of options) {\n const item = document.createElement(\"button\");\n item.type = \"button\";\n item.className = CLASSES.INBOX_MENU_ITEM;\n // \"\" is how a null option round-trips through a dataset string.\n item.dataset.pickerOption = option === null ? \"\" : option;\n item.setAttribute(\"role\", \"menuitemradio\");\n\n const itemDot = document.createElement(\"span\");\n itemDot.className = CLASSES.INBOX_STATUS_DOT;\n itemDot.style.backgroundColor = colorOf(option);\n item.appendChild(itemDot);\n item.appendChild(document.createTextNode(labelOf(option)));\n\n item.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n menu.style.display = \"none\";\n // No-op: re-picking the option that's already selected must not fire\n // onSelect \u2014 for the status picker that would re-stamp resolvedAt on\n // every redundant \"Resolved\" click, destroying RF5's elapsed time.\n if (option === current) return;\n current = option;\n onSelect(option);\n syncUi();\n });\n menu.appendChild(item);\n }\n\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n menu.style.display = menu.style.display === \"none\" ? \"block\" : \"none\";\n });\n\n wrapper.appendChild(btn);\n wrapper.appendChild(menu);\n syncUi();\n return wrapper;\n};\n\n/**\n * @param {Object} comment\n * @param {{ strings: Object, onCopy: Function, onSetStatus: Function, onSetType: Function, onSetPriority: Function, onDelete: Function }} deps\n * @returns {HTMLElement}\n */\nexport const createCommentActions = (\n comment,\n { strings, onCopy, onSetStatus, onSetType, onSetPriority, onDelete }\n) => {\n const actions = document.createElement(\"div\");\n actions.className = CLASSES.INBOX_CARD_ACTIONS;\n\n // --- copy agent context ---\n const copyBtn = document.createElement(\"button\");\n copyBtn.type = \"button\";\n copyBtn.className = CLASSES.INBOX_ACTION_BTN;\n copyBtn.dataset.action = \"copy\";\n copyBtn.dataset.hdTooltip = strings.copyAgentContext;\n copyBtn.setAttribute(\"aria-label\", strings.copyAgentContext);\n copyBtn.innerHTML = COPY_ICON_SVG;\n copyBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onCopy(comment);\n copyBtn.innerHTML = CHECK_ICON_SVG;\n copyBtn.dataset.hdTooltip = strings.copied;\n setTimeout(() => {\n copyBtn.innerHTML = COPY_ICON_SVG;\n copyBtn.dataset.hdTooltip = strings.copyAgentContext;\n }, 1500);\n });\n actions.appendChild(copyBtn);\n\n // --- lifecycle status picker (RF09) ---\n actions.appendChild(\n createPicker({\n action: \"status\",\n options: STATUSES,\n value: comment.status || \"open\",\n colorOf: (status) => STATUS_COLORS[status] || \"\",\n labelOf: (status) => statusLabelOf(status, strings),\n tooltipLabel: strings.statusLabel,\n onSelect: (status) => onSetStatus(comment, status),\n })\n );\n\n // --- category picker (RF3) ---\n actions.appendChild(\n createPicker({\n action: \"type\",\n // `null` first: returning to the neutral state must be reachable.\n options: [null, ...COMMENT_TYPES],\n value: comment.type || null,\n colorOf: (type) => TYPE_COLORS[type] || \"transparent\",\n labelOf: (type) => typeLabelOf(type, strings),\n tooltipLabel: strings.typeLabel,\n onSelect: (type) => onSetType?.(comment, type),\n showLabel: true,\n })\n );\n\n // --- priority picker (RF4) ---\n actions.appendChild(\n createPicker({\n action: \"priority\",\n options: [null, ...PRIORITIES],\n value: comment.priority || null,\n colorOf: (priority) => PRIORITY_COLORS[priority] || \"transparent\",\n labelOf: (priority) => priorityLabelOf(priority, strings),\n tooltipLabel: strings.priorityLabel,\n onSelect: (priority) => onSetPriority?.(comment, priority),\n showLabel: true,\n })\n );\n\n // --- more (\u22EF) menu ---\n const menuWrapper = document.createElement(\"div\");\n menuWrapper.style.position = \"relative\";\n\n const menuBtn = document.createElement(\"button\");\n menuBtn.type = \"button\";\n menuBtn.className = CLASSES.INBOX_ACTION_BTN;\n menuBtn.dataset.action = \"menu\";\n menuBtn.dataset.hdTooltip = strings.moreOptions;\n menuBtn.setAttribute(\"aria-label\", strings.commentOptions);\n menuBtn.innerHTML = DOTS_ICON_SVG;\n\n const menu = document.createElement(\"div\");\n menu.className = CLASSES.INBOX_MENU;\n menu.style.display = \"none\";\n\n const deleteItem = document.createElement(\"button\");\n deleteItem.type = \"button\";\n deleteItem.className = CLASSES.INBOX_MENU_ITEM;\n deleteItem.textContent = strings.deleteComment;\n deleteItem.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n menu.style.display = \"none\";\n onDelete(comment);\n });\n menu.appendChild(deleteItem);\n\n menuBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n menu.style.display = menu.style.display === \"none\" ? \"block\" : \"none\";\n });\n\n menuWrapper.appendChild(menuBtn);\n menuWrapper.appendChild(menu);\n actions.appendChild(menuWrapper);\n\n return actions;\n};\n", "import {\n CLASSES,\n IDS,\n COMMENT_TYPES,\n TYPE_COLORS,\n PRIORITIES,\n PRIORITY_COLORS,\n} from \"./constants.js\";\nimport { formatTemplate } from \"./i18n.js\";\nimport defaultStrings from \"./locales/en.js\";\nimport {\n createPicker,\n typeLabelOf,\n priorityLabelOf,\n} from \"./comment-actions.js\";\n\nconst formatRelativeTime = (date, strings) => {\n const diff = Date.now() - new Date(date).getTime();\n const minutes = Math.floor(diff / 60000);\n const hours = Math.floor(diff / 3600000);\n const days = Math.floor(diff / 86400000);\n\n if (minutes < 1) return strings.justNow;\n if (minutes < 60) return formatTemplate(strings.minutesAgoTemplate, minutes);\n if (hours < 24) return formatTemplate(strings.hoursAgoTemplate, hours);\n return formatTemplate(strings.daysAgoTemplate, days);\n};\n\nconst formatFullDate = (date, locale) => {\n return new Intl.DateTimeFormat(locale, {\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n }).format(new Date(date));\n};\n\nexport const createMetaElement = (author, createdAt, strings, locale) => {\n const meta = document.createElement(\"div\");\n meta.className = CLASSES.THREAD_META;\n\n const authorEl = document.createElement(\"span\");\n authorEl.className = CLASSES.THREAD_AUTHOR;\n authorEl.textContent = author || strings.anonymous;\n\n const timeEl = document.createElement(\"span\");\n timeEl.className = CLASSES.THREAD_TIME;\n timeEl.textContent = formatRelativeTime(createdAt, strings);\n timeEl.dataset.fullDate = formatFullDate(createdAt, locale);\n\n meta.appendChild(authorEl);\n meta.appendChild(timeEl);\n\n return meta;\n};\n\nconst getShortcutText = (options, strings) => {\n const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.userAgent);\n const modifierMap = {\n alt: isMac ? \"\u2325\" : strings.modifierAlt,\n ctrl: isMac ? \"\u2318\" : strings.modifierCtrl,\n shift: \"\u21E7\",\n };\n\n const modifier = modifierMap[options.shortcutModifier] || modifierMap.alt;\n const key = options.shortcutKey?.toUpperCase() || \"C\";\n\n return `${modifier} + ${key}`;\n};\n\nconst ATTACH_ICON_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>`;\n\nconst SEND_ICON_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 2L11 13M22 2L15 22L11 13M11 13L2 9L22 2\"/></svg>`;\n\nconst COMMENT_BUBBLE_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" stroke-linejoin=\"round\" fill=\"currentColor\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2.8914 10.4028L2.98327 10.6318C3.22909 11.2445 3.5 12.1045 3.5 13C3.5 13.3588 3.4564 13.7131 3.38773 14.0495C3.69637 13.9446 4.01409 13.8159 4.32918 13.6584C4.87888 13.3835 5.33961 13.0611 5.70994 12.7521L6.22471 12.3226L6.88809 12.4196C7.24851 12.4724 7.61994 12.5 8 12.5C11.7843 12.5 14.5 9.85569 14.5 7C14.5 4.14431 11.7843 1.5 8 1.5C4.21574 1.5 1.5 4.14431 1.5 7C1.5 8.18175 1.94229 9.29322 2.73103 10.2153L2.8914 10.4028ZM2.8135 15.7653C1.76096 16 1 16 1 16C1 16 1.43322 15.3097 1.72937 14.4367C1.88317 13.9834 2 13.4808 2 13C2 12.3826 1.80733 11.7292 1.59114 11.1903C0.591845 10.0221 0 8.57152 0 7C0 3.13401 3.58172 0 8 0C12.4183 0 16 3.13401 16 7C16 10.866 12.4183 14 8 14C7.54721 14 7.10321 13.9671 6.67094 13.9038C6.22579 14.2753 5.66881 14.6656 5 15C4.23366 15.3832 3.46733 15.6195 2.8135 15.7653Z\"/></svg>`;\n\nconst MENU_ICON_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" stroke-linejoin=\"round\" fill=\"currentColor\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M1.67705 7.5L3.92705 3H12.0729L14.3229 7.5H10H9.25V8.25C9.25 8.94036 8.69036 9.5 8 9.5C7.30964 9.5 6.75 8.94036 6.75 8.25V7.5H6H1.67705ZM1.5 9V12C1.5 12.5523 1.94772 13 2.5 13H13.5C14.0523 13 14.5 12.5523 14.5 12V9H10.6465C10.32 10.1543 9.25878 11 8 11C6.74122 11 5.67998 10.1543 5.35352 9H1.5ZM3 1.5H13L15.8944 7.28885C15.9639 7.42771 16 7.58082 16 7.73607V12C16 13.3807 14.8807 14.5 13.5 14.5H2.5C1.11929 14.5 0 13.3807 0 12V7.73607C0 7.58082 0.0361451 7.42771 0.105573 7.28885L3 1.5Z\"/></svg>`;\n\n/**\n * Shared input area component used by both the comment box and the thread popover.\n * Returns the container element and references to key child elements.\n * @param {Object} options\n * @param {string} options.areaClassName\n * @param {\"textarea\" | \"input\"} [options.inputTag]\n * @param {string} [options.inputClassName]\n * @param {string} [options.inputId]\n * @param {string} options.inputPlaceholder\n * @param {string} [options.submitBtnId]\n * @param {string} [options.fileInputId]\n * @param {typeof defaultStrings} strings\n */\nexport const createInputArea = (\n {\n areaClassName,\n inputTag = \"textarea\",\n inputClassName,\n inputId,\n inputPlaceholder,\n submitBtnId,\n fileInputId,\n },\n strings\n) => {\n const container = document.createElement(\"div\");\n container.className = areaClassName;\n\n /** @type {HTMLInputElement | HTMLTextAreaElement} */\n const inputEl = document.createElement(inputTag);\n if (inputId) inputEl.id = inputId;\n if (inputClassName) inputEl.className = inputClassName;\n inputEl.placeholder = inputPlaceholder;\n inputEl.setAttribute(\"aria-label\", inputPlaceholder);\n if (inputTag === \"input\")\n /** @type {HTMLInputElement} */ (inputEl).type = \"text\";\n\n const screenshotsContainer = document.createElement(\"div\");\n screenshotsContainer.className = CLASSES.SCREENSHOTS_CONTAINER;\n\n const actionsBar = document.createElement(\"div\");\n actionsBar.className = CLASSES.COMMENT_ACTIONS_BAR;\n\n const attachBtn = document.createElement(\"button\");\n attachBtn.className = CLASSES.ATTACH_IMAGE_BTN;\n attachBtn.type = \"button\";\n attachBtn.setAttribute(\"aria-label\", strings.attachImage);\n attachBtn.innerHTML = ATTACH_ICON_SVG;\n\n const fileInput = document.createElement(\"input\");\n fileInput.type = \"file\";\n if (fileInputId) fileInput.id = fileInputId;\n fileInput.accept = \"image/*\";\n fileInput.style.display = \"none\";\n\n const submitBtn = document.createElement(\"button\");\n if (submitBtnId) submitBtn.id = submitBtnId;\n submitBtn.className = CLASSES.THREAD_SUBMIT;\n submitBtn.type = \"button\";\n submitBtn.setAttribute(\"aria-label\", strings.send);\n submitBtn.innerHTML = SEND_ICON_SVG;\n\n actionsBar.appendChild(attachBtn);\n actionsBar.appendChild(fileInput);\n actionsBar.appendChild(submitBtn);\n\n container.appendChild(inputEl);\n container.appendChild(screenshotsContainer);\n container.appendChild(actionsBar);\n\n return {\n container,\n inputEl,\n screenshotsContainer,\n attachBtn,\n fileInput,\n submitBtn,\n };\n};\n\nconst createActionWithTooltip = (btnClass, btnSvg, tooltipContent, label) => {\n const wrapper = document.createElement(\"div\");\n wrapper.className = CLASSES.TOOLBAR_ACTION_WRAPPER;\n\n const tooltip = document.createElement(\"div\");\n tooltip.className = CLASSES.TOOLBAR_ACTION_TOOLTIP;\n tooltipContent.forEach((el) => tooltip.appendChild(el));\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = `${CLASSES.TOOLBAR_ACTION_BTN} ${btnClass}`;\n btn.setAttribute(\"aria-label\", label);\n btn.innerHTML = btnSvg;\n\n wrapper.appendChild(tooltip);\n wrapper.appendChild(btn);\n return wrapper;\n};\n\nexport const createToolbar = (options = {}, strings = defaultStrings) => {\n const toolbar = document.createElement(\"div\");\n toolbar.id = IDS.TOOLBAR;\n\n const actions = document.createElement(\"div\");\n actions.className = CLASSES.TOOLBAR_ACTIONS;\n\n const commentLabel = document.createElement(\"span\");\n commentLabel.className = CLASSES.TOOLBAR_TEXT;\n commentLabel.textContent = strings.toolbarComment;\n\n const shortcutKey = document.createElement(\"span\");\n shortcutKey.className = CLASSES.SHORTCUT_HINT;\n shortcutKey.textContent = getShortcutText(options, strings);\n\n const commentWrapper = createActionWithTooltip(\n CLASSES.TOOLBAR_COMMENT_BTN,\n COMMENT_BUBBLE_SVG,\n [commentLabel, shortcutKey],\n strings.toolbarComment\n );\n commentWrapper\n .querySelector(`.${CLASSES.TOOLBAR_COMMENT_BTN}`)\n ?.setAttribute(\"aria-pressed\", \"false\");\n\n const inboxLabel = document.createElement(\"span\");\n inboxLabel.className = CLASSES.TOOLBAR_TEXT;\n inboxLabel.textContent = strings.toolbarInbox;\n\n const inboxWrapper = createActionWithTooltip(\n CLASSES.TOOLBAR_MENU_BTN,\n MENU_ICON_SVG,\n [inboxLabel],\n strings.toolbarInbox\n );\n\n actions.appendChild(commentWrapper);\n actions.appendChild(inboxWrapper);\n toolbar.appendChild(actions);\n\n return toolbar;\n};\n\n/**\n * RF3 + RF4 \u2014 the classification strip inside the new-comment box: type,\n * priority and free-form tags, all starting neutral.\n *\n * The comment box is built once and reused for every comment, so this\n * exposes reset(): without it the previous comment's selections would leak\n * into the next one.\n *\n * @param {object} strings\n * @returns {{ container: HTMLElement, getType: () => string|null,\n * getPriority: () => string|null, getTags: () => string[],\n * reset: () => void }}\n */\nexport const createClassifyRow = (strings) => {\n const container = document.createElement(\"div\");\n container.className = CLASSES.CLASSIFY_ROW;\n\n let type = null;\n let priority = null;\n /** @type {string[]} */\n const tags = [];\n\n const chips = document.createElement(\"div\");\n chips.className = CLASSES.INBOX_BADGES;\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.className = CLASSES.TAGS_INPUT;\n input.placeholder = strings.tagsPlaceholder;\n input.setAttribute(\"aria-label\", strings.tagsPlaceholder);\n\n const renderChips = () => {\n chips.innerHTML = \"\";\n tags.forEach((tag, index) => {\n const chip = document.createElement(\"span\");\n chip.className = CLASSES.TAG_CHIP;\n chip.appendChild(document.createTextNode(tag));\n\n const remove = document.createElement(\"button\");\n remove.type = \"button\";\n remove.className = CLASSES.TAG_CHIP_REMOVE;\n remove.setAttribute(\"aria-label\", `${strings.removeTag}: ${tag}`);\n remove.innerHTML = \"×\";\n remove.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n tags.splice(index, 1);\n renderChips();\n });\n\n chip.appendChild(remove);\n chips.appendChild(chip);\n });\n };\n\n // Commits whatever is currently typed (if anything) as a tag. Shared by\n // the Enter/comma keydown handler and getTags(), so text left in the\n // input when the user clicks Send isn't silently discarded.\n const commitPendingTag = () => {\n const tag = input.value.trim().toLowerCase();\n // Blanks and duplicates are silently ignored \u2014 nothing to tell the user.\n if (tag && !tags.includes(tag)) {\n tags.push(tag);\n renderChips();\n }\n input.value = \"\";\n };\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key !== \"Enter\" && e.key !== \",\") return;\n e.preventDefault();\n commitPendingTag();\n });\n\n // Pickers keep their selection internally, so returning them to neutral\n // means rebuilding them \u2014 hence mount() rather than a one-shot append.\n const mount = () => {\n container.replaceChildren();\n container.appendChild(\n createPicker({\n action: \"type\",\n options: [null, ...COMMENT_TYPES],\n value: null,\n colorOf: (value) => TYPE_COLORS[value] || \"transparent\",\n labelOf: (value) => typeLabelOf(value, strings),\n tooltipLabel: strings.typeLabel,\n onSelect: (value) => (type = value),\n showLabel: true,\n })\n );\n container.appendChild(\n createPicker({\n action: \"priority\",\n options: [null, ...PRIORITIES],\n value: null,\n colorOf: (value) => PRIORITY_COLORS[value] || \"transparent\",\n labelOf: (value) => priorityLabelOf(value, strings),\n tooltipLabel: strings.priorityLabel,\n onSelect: (value) => (priority = value),\n showLabel: true,\n })\n );\n container.appendChild(input);\n container.appendChild(chips);\n };\n\n mount();\n\n return {\n container,\n getType: () => type,\n getPriority: () => priority,\n getTags: () => {\n commitPendingTag();\n return [...tags];\n },\n reset: () => {\n type = null;\n priority = null;\n tags.length = 0;\n input.value = \"\";\n renderChips();\n mount();\n },\n };\n};\n\nexport const createCommentBox = (strings = defaultStrings) => {\n const commentBox = document.createElement(\"div\");\n commentBox.id = IDS.COMMENT_BOX;\n commentBox.setAttribute(\"role\", \"dialog\");\n commentBox.setAttribute(\"aria-label\", strings.commentBoxAriaLabel);\n\n const { container: inputArea } = createInputArea(\n {\n areaClassName: CLASSES.COMMENT_INPUT_AREA,\n inputTag: \"textarea\",\n inputId: IDS.COMMENT_INPUT,\n inputPlaceholder: strings.commentPlaceholder,\n submitBtnId: IDS.SUBMIT_COMMENT,\n fileInputId: IDS.ATTACH_IMAGE_INPUT,\n },\n strings\n );\n\n const classify = createClassifyRow(strings);\n\n commentBox.appendChild(classify.container);\n commentBox.appendChild(inputArea);\n commentBox.style.display = \"none\";\n // Exposed so the overlay can read the selections at save time without\n // re-querying the DOM.\n /** @type {any} */ (commentBox).classify = classify;\n return commentBox;\n};\n\nexport const createCommentCircle = (comment, strings = defaultStrings) => {\n const circle = document.createElement(\"div\");\n circle.className = CLASSES.CIRCLE;\n circle.dataset.commentId = comment.id;\n circle.dataset.commentText = comment.text;\n circle.setAttribute(\"role\", \"button\");\n circle.setAttribute(\"tabindex\", \"0\");\n circle.setAttribute(\n \"aria-label\",\n `${strings.commentAriaLabelPrefix}${comment.text}`\n );\n\n // Basic positioning - will be updated by position validation system\n circle.style.cssText = `\n position: absolute;\n pointer-events: auto;\n `;\n\n return circle;\n};\n\nexport const createScreenshotsDisplay = (screenshots, strings) => {\n const container = document.createElement(\"div\");\n container.className = CLASSES.SCREENSHOTS_CONTAINER;\n container.classList.add(CLASSES.ACTIVE);\n\n screenshots.forEach((src) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = src;\n img.alt = strings.attachedScreenshot;\n\n item.appendChild(img);\n container.appendChild(item);\n });\n\n return container;\n};\n\nexport const createTooltip = (comment, strings = defaultStrings, locale) => {\n const tooltip = document.createElement(\"div\");\n tooltip.className = CLASSES.TOOLTIP;\n tooltip.dataset.for = comment.id;\n tooltip.setAttribute(\"role\", \"dialog\");\n tooltip.setAttribute(\"aria-label\", strings.tooltipAriaLabel);\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.THREAD_HEADER;\n\n const meta = createMetaElement(\n comment.author,\n comment.createdAt,\n strings,\n locale\n );\n const closeButton = document.createElement(\"button\");\n closeButton.type = \"button\";\n closeButton.className = CLASSES.CLOSE_TOOLTIP;\n closeButton.setAttribute(\"aria-label\", strings.close);\n closeButton.innerHTML = \"×\";\n\n header.appendChild(meta);\n header.appendChild(closeButton);\n\n const body = document.createElement(\"div\");\n body.className = CLASSES.THREAD_BODY;\n body.textContent = comment.text;\n\n tooltip.appendChild(header);\n tooltip.appendChild(body);\n const tooltipScreenshots =\n comment.screenshots || (comment.screenshot ? [comment.screenshot] : []);\n if (tooltipScreenshots.length > 0) {\n tooltip.appendChild(createScreenshotsDisplay(tooltipScreenshots, strings));\n }\n return tooltip;\n};\n\nexport const createReplyElement = (reply, strings = defaultStrings, locale) => {\n const replyEl = document.createElement(\"div\");\n replyEl.className = CLASSES.THREAD_REPLY;\n\n const meta = createMetaElement(\n reply.author,\n reply.timestamp,\n strings,\n locale\n );\n const text = document.createElement(\"div\");\n text.className = CLASSES.THREAD_BODY;\n text.textContent = reply.text;\n\n replyEl.appendChild(meta);\n replyEl.appendChild(text);\n const replyScreenshots =\n reply.screenshots || (reply.screenshot ? [reply.screenshot] : []);\n if (replyScreenshots.length > 0) {\n replyEl.appendChild(createScreenshotsDisplay(replyScreenshots, strings));\n }\n return replyEl;\n};\n\nexport const createThreadPopover = (\n comment,\n strings = defaultStrings,\n locale\n) => {\n const popover = document.createElement(\"div\");\n popover.className = CLASSES.THREAD_POPOVER;\n popover.dataset.for = comment.id;\n popover.setAttribute(\"role\", \"dialog\");\n popover.setAttribute(\"aria-label\", strings.popoverAriaLabel);\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.THREAD_HEADER;\n\n const meta = createMetaElement(\n comment.author,\n comment.createdAt,\n strings,\n locale\n );\n const closeButton = document.createElement(\"button\");\n closeButton.type = \"button\";\n closeButton.className = CLASSES.CLOSE_TOOLTIP;\n closeButton.setAttribute(\"aria-label\", strings.close);\n closeButton.innerHTML = \"×\";\n\n header.appendChild(meta);\n header.appendChild(closeButton);\n\n const body = document.createElement(\"div\");\n body.className = CLASSES.THREAD_BODY;\n body.textContent = comment.text;\n\n const replies = document.createElement(\"div\");\n replies.className = CLASSES.THREAD_REPLIES;\n if (comment.replies) {\n comment.replies.forEach((reply) => {\n replies.appendChild(createReplyElement(reply, strings, locale));\n });\n }\n\n const { container: inputArea } = createInputArea(\n {\n areaClassName: CLASSES.THREAD_INPUT_AREA,\n inputTag: \"input\",\n inputClassName: CLASSES.THREAD_INPUT,\n inputPlaceholder: strings.replyPlaceholder,\n },\n strings\n );\n\n popover.appendChild(header);\n popover.appendChild(body);\n const popoverScreenshots =\n comment.screenshots || (comment.screenshot ? [comment.screenshot] : []);\n if (popoverScreenshots.length > 0) {\n popover.appendChild(createScreenshotsDisplay(popoverScreenshots, strings));\n }\n popover.appendChild(replies);\n popover.appendChild(inputArea);\n\n return popover;\n};\n", "// Builds the plain-text context block the inbox \"copy\" action puts on the\n// clipboard \u2014 enough for a coding agent to locate the element and understand\n// the reported issue (page, viewport, selector, DOM path, thread).\n\nimport { formatDuration } from \"./i18n.js\";\n\nconst openingTagOf = (element) => {\n const attrs = [...element.attributes]\n .map(({ name, value }) => `${name}=\"${value}\"`)\n .join(\" \");\n return `<${element.tagName.toLowerCase()}${attrs ? ` ${attrs}` : \"\"}>`;\n};\n\nconst openingTagFromFingerprint = (fingerprint) => {\n if (!fingerprint?.tagName) return \"(unknown)\";\n const attrs = Object.entries(fingerprint.attributes || {})\n .map(([name, value]) => `${name}=\"${value}\"`)\n .join(\" \");\n return `<${fingerprint.tagName.toLowerCase()}${attrs ? ` ${attrs}` : \"\"}>`;\n};\n\n// body > main.flex.layout > section#pricing.plans \u2014 tag + id + up to two\n// classes per level, from body down to the element.\nconst domPathOf = (element) => {\n const segments = [];\n let current = element;\n while (current && current !== document.documentElement) {\n const tag = current.tagName.toLowerCase();\n const id = current.id ? `#${current.id}` : \"\";\n const classes = [...current.classList]\n .slice(0, 2)\n .map((cls) => `.${cls}`)\n .join(\"\");\n segments.unshift(`${tag}${id}${classes}`);\n if (current === document.body) break;\n current = current.parentElement;\n }\n return segments.join(\" > \");\n};\n\n/**\n * @param {import('./index.d.ts').Comment} comment\n * @param {{ viewportWidth: number, viewportHeight: number, strings?: object }} env\n * @returns {string}\n */\nexport function buildAgentContext(\n comment,\n { viewportWidth, viewportHeight, strings }\n) {\n const anchor = comment.anchor;\n const fingerprint = anchor?.fingerprint;\n const live = comment.container?.isConnected ? comment.container : null;\n\n const state = comment.hidden ? \"hidden\" : comment.anchorState;\n const element = live\n ? openingTagOf(live)\n : openingTagFromFingerprint(fingerprint);\n const path = live ? domPathOf(live) : \"(unavailable)\";\n\n // The reporter's viewport, not the reader's: `comment.context.viewport`\n // was captured at report time (RF2) and travels with the comment. Live\n // `viewportWidth`/`viewportHeight` (the copying browser's window) is only\n // a fallback for legacy records persisted before RF1/RF2 had no context.\n const capturedViewport = comment.context?.viewport;\n const reportedViewportWidth = capturedViewport?.width ?? viewportWidth;\n const reportedViewportHeight = capturedViewport?.height ?? viewportHeight;\n\n const lines = [\n `Page: ${comment.page}`,\n `Viewport: ${reportedViewportWidth}x${reportedViewportHeight}`,\n `Anchor state: ${state}`,\n `Status: ${comment.status || \"open\"}`,\n `Selector: ${anchor?.selector || \"(none)\"}`,\n `Element: ${element}`,\n `DOM path: ${path}`,\n `Nearby text: \"${fingerprint?.textSnippet ?? \"\"}\"`,\n `Comment by ${comment.author} (${comment.createdAt}):`,\n `\"${comment.text}\"`,\n ];\n\n // Neutral classification fields are omitted rather than printed as\n // \"(none)\" \u2014 the agent shouldn't read noise for data nobody filled in.\n if (comment.type) lines.push(`Type: ${comment.type}`);\n if (comment.priority) lines.push(`Priority: ${comment.priority}`);\n if (comment.tags?.length) lines.push(`Tags: ${comment.tags.join(\", \")}`);\n\n const context = comment.context;\n if (context) {\n if (context.url) lines.push(`URL: ${context.url}`);\n if (context.screen) {\n lines.push(`Screen: ${context.screen.width}x${context.screen.height}`);\n }\n if (context.browser?.name) {\n lines.push(\n `Browser: ${`${context.browser.name} ${context.browser.version || \"\"}`.trim()}`\n );\n }\n if (context.os?.name) {\n lines.push(\n `OS: ${`${context.os.name} ${context.os.version || \"\"}`.trim()}`\n );\n }\n }\n\n if (strings && comment.status === \"resolved\" && comment.resolvedAt) {\n const elapsed = formatDuration(\n new Date(comment.resolvedAt).getTime() -\n new Date(comment.createdAt).getTime(),\n strings\n );\n if (elapsed) lines.push(`Resolution time: ${elapsed}`);\n }\n\n const replies = comment.replies || [];\n if (replies.length > 0) {\n lines.push(`Replies (${replies.length}):`);\n for (const reply of replies) {\n lines.push(`- ${reply.author}: \"${reply.text}\"`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n", "// Right-side inbox sidebar: a filterable list of every comment and a detail\n// view with the full thread. Pure view layer \u2014 all data mutations flow back\n// to CommentOverlay through the callbacks contract passed to the\n// constructor, so this module never touches storage or the page markers.\n\nimport {\n CLASSES,\n TYPE_COLORS,\n PRIORITY_COLORS,\n COMMENT_TYPES,\n PRIORITIES,\n} from \"./constants.js\";\nimport { buildAgentContext } from \"./agent-context.js\";\nimport { formatDuration, formatTemplate } from \"./i18n.js\";\nimport {\n createCommentActions,\n copyToClipboard,\n typeLabelOf,\n priorityLabelOf,\n} from \"./comment-actions.js\";\nimport {\n createMetaElement,\n createScreenshotsDisplay,\n createInputArea,\n createReplyElement,\n} from \"./components.js\";\n\nconst CARET_ICON_SVG = `<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>`;\nconst CHEVRON_LEFT_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"/></svg>`;\nconst ARROW_UP_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"18 15 12 9 6 15\"/></svg>`;\nconst ARROW_DOWN_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>`;\n\nexport class InboxView {\n /**\n * @param {Object} deps\n * @param {ShadowRoot} deps.shadowRoot\n * @param {Object} deps.strings\n * @param {string} deps.locale\n * @param {string} deps.currentPage\n * @param {() => Array<Object>} deps.getComments\n * @param {{ onOpenDetailScroll: Function, onReply: Function, onDelete: Function, onSetStatus: Function, onSetType: Function, onSetPriority: Function, onNavigateToPage: Function, onShowLightbox: Function, onClose: Function }} deps.callbacks\n */\n constructor({\n shadowRoot,\n strings,\n locale,\n currentPage,\n getComments,\n callbacks,\n }) {\n this.shadowRoot = shadowRoot;\n this.strings = strings;\n this.locale = locale;\n this.currentPage = currentPage;\n this.getComments = getComments;\n this.callbacks = callbacks;\n this.pageFilter = \"page\"; // \"all\" | \"page\"\n this.statusFilter = \"all\"; // \"all\" | \"unresolved\" | \"resolved\"\n this.typeFilter = \"all\"; // \"all\" | COMMENT_TYPES\n this.priorityFilter = \"all\"; // \"all\" | PRIORITIES\n this.detailId = null;\n /** @type {HTMLElement | null} */\n this.el = null;\n }\n\n isOpen() {\n return Boolean(this.el);\n }\n\n open() {\n if (this.el) return;\n this.el = document.createElement(\"div\");\n this.el.className = CLASSES.INBOX_PANEL;\n this.el.setAttribute(\"role\", \"dialog\");\n this.el.setAttribute(\"aria-label\", this.strings.inboxAriaLabel);\n this.shadowRoot.appendChild(this.el);\n this.render();\n }\n\n close() {\n this._clearHighlight();\n this.el?.remove();\n this.el = null;\n this.detailId = null;\n }\n\n refresh() {\n if (this.el) this.render();\n }\n\n _highlight(comment) {\n this._clearHighlight();\n if (\n comment.anchorState !== \"anchored\" ||\n comment.hidden ||\n comment.status === \"resolved\"\n ) {\n return;\n }\n const circle = this.shadowRoot.querySelector(\n `[data-comment-id=\"${comment.id}\"]`\n );\n if (!circle) return;\n circle.classList.add(CLASSES.HIGHLIGHT);\n this._highlightedEl = circle;\n }\n\n _clearHighlight() {\n this._highlightedEl?.classList.remove(CLASSES.HIGHLIGHT);\n this._highlightedEl = null;\n }\n\n filteredComments() {\n let comments = this.getComments();\n if (this.pageFilter === \"page\") {\n comments = comments.filter(\n (comment) => comment.page === this.currentPage\n );\n }\n if (this.statusFilter === \"resolved\") {\n comments = comments.filter((comment) => comment.status === \"resolved\");\n } else if (this.statusFilter === \"unresolved\") {\n comments = comments.filter((comment) => comment.status !== \"resolved\");\n }\n if (this.typeFilter !== \"all\") {\n comments = comments.filter((comment) => comment.type === this.typeFilter);\n }\n if (this.priorityFilter !== \"all\") {\n comments = comments.filter(\n (comment) => comment.priority === this.priorityFilter\n );\n }\n // Resolved sink to the bottom; both partitions keep their original order.\n return [\n ...comments.filter((comment) => comment.status !== \"resolved\"),\n ...comments.filter((comment) => comment.status === \"resolved\"),\n ];\n }\n\n render() {\n if (!this.el) return;\n this._clearHighlight();\n this.el.innerHTML = \"\";\n const comments = this.filteredComments();\n const detail =\n this.detailId != null\n ? comments.find((comment) => comment.id === this.detailId)\n : null;\n if (detail) {\n this._renderDetail(detail, comments);\n } else {\n this.detailId = null;\n this._renderList(comments);\n }\n }\n\n /**\n * Opens the panel (if needed) directly on a comment's detail. Used by\n * the overlay for the cross-page handoff on startup.\n * @param {number} id\n */\n openDetail(id) {\n if (!this.el) this.open();\n const comment = this.getComments().find((c) => c.id === id);\n if (comment) this._openDetail(comment);\n }\n\n _openDetail(comment) {\n this.detailId = comment.id;\n if (comment.anchorState === \"anchored\" && !comment.hidden) {\n this.callbacks.onOpenDetailScroll(comment);\n }\n this.render();\n }\n\n _closeButton() {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_CLOSE;\n btn.setAttribute(\"aria-label\", this.strings.close);\n btn.innerHTML = \"×\";\n btn.addEventListener(\"click\", () => this.callbacks.onClose());\n return btn;\n }\n\n _renderList(comments) {\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_HEADER;\n header.appendChild(this._buildFilter());\n header.appendChild(this._closeButton());\n this.el.appendChild(header);\n\n const list = document.createElement(\"div\");\n list.className = CLASSES.INBOX_LIST;\n this.el.appendChild(list);\n\n if (comments.length === 0) {\n const empty = document.createElement(\"div\");\n empty.className = CLASSES.INBOX_EMPTY;\n empty.textContent = this.strings.inboxEmpty;\n list.appendChild(empty);\n return;\n }\n\n for (const comment of comments) {\n list.appendChild(this._buildCard(comment, { interactive: true }));\n }\n }\n\n _pageFilterLabel(value) {\n return value === \"all\"\n ? this.strings.filterAll\n : this.strings.filterCurrentPage;\n }\n\n _statusFilterLabel(value) {\n if (value === \"unresolved\") return this.strings.filterUnresolved;\n if (value === \"resolved\") return this.strings.filterResolved;\n return this.strings.filterStatusAll;\n }\n\n /**\n * Summary label for the collapsed filter button. The page filter always\n * contributes (it's either \"All\" or \"Current page\"); status, type, and\n * priority only join in when active, so an active filter is never hidden\n * from a user who hasn't opened the menu.\n */\n _filterSummaryLabel() {\n const parts = [this._pageFilterLabel(this.pageFilter)];\n if (this.statusFilter !== \"all\") {\n parts.push(this._statusFilterLabel(this.statusFilter));\n }\n if (this.typeFilter !== \"all\") {\n parts.push(typeLabelOf(this.typeFilter, this.strings));\n }\n if (this.priorityFilter !== \"all\") {\n parts.push(priorityLabelOf(this.priorityFilter, this.strings));\n }\n return parts.join(\" \u00B7 \");\n }\n\n _buildFilter() {\n const wrapper = document.createElement(\"div\");\n wrapper.className = CLASSES.INBOX_FILTER + \"-wrapper\";\n\n const label = this._filterSummaryLabel();\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_FILTER;\n btn.setAttribute(\"aria-haspopup\", \"true\");\n btn.setAttribute(\"aria-expanded\", \"false\");\n btn.innerHTML = `<span>${label}</span>${CARET_ICON_SVG}`;\n\n const menu = document.createElement(\"div\");\n menu.className = CLASSES.INBOX_FILTER_MENU;\n menu.style.display = \"none\";\n\n const addSection = (title) => {\n const section = document.createElement(\"div\");\n section.className = CLASSES.INBOX_FILTER_SECTION;\n section.textContent = title;\n menu.appendChild(section);\n };\n\n const addOption = (text, checked, dataAttr, value, onSelect) => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.className = CLASSES.INBOX_FILTER_OPTION;\n option.dataset[dataAttr] = value;\n option.setAttribute(\"role\", \"menuitemradio\");\n option.setAttribute(\"aria-checked\", String(checked));\n option.innerHTML = `<span>${text}</span>${checked ? \"\u2713\" : \"\"}`;\n option.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onSelect();\n this.render();\n });\n menu.appendChild(option);\n };\n\n addSection(this.strings.filterByPage);\n for (const value of [\"all\", \"page\"]) {\n addOption(\n this._pageFilterLabel(value),\n this.pageFilter === value,\n \"filterPage\",\n value,\n () => (this.pageFilter = value)\n );\n }\n\n addSection(this.strings.filterByStatus);\n for (const value of [\"all\", \"unresolved\", \"resolved\"]) {\n addOption(\n this._statusFilterLabel(value),\n this.statusFilter === value,\n \"filterStatus\",\n value,\n () => (this.statusFilter = value)\n );\n }\n\n addSection(this.strings.filterByType);\n for (const value of [\"all\", ...COMMENT_TYPES]) {\n addOption(\n value === \"all\"\n ? this.strings.filterStatusAll\n : typeLabelOf(value, this.strings),\n this.typeFilter === value,\n \"filterType\",\n value,\n () => (this.typeFilter = value)\n );\n }\n\n addSection(this.strings.filterByPriority);\n for (const value of [\"all\", ...PRIORITIES]) {\n addOption(\n value === \"all\"\n ? this.strings.filterStatusAll\n : priorityLabelOf(value, this.strings),\n this.priorityFilter === value,\n \"filterPriority\",\n value,\n () => (this.priorityFilter = value)\n );\n }\n\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const open = menu.style.display !== \"none\";\n menu.style.display = open ? \"none\" : \"block\";\n btn.setAttribute(\"aria-expanded\", String(!open));\n });\n\n wrapper.appendChild(btn);\n wrapper.appendChild(menu);\n return wrapper;\n }\n\n _buildCard(comment, { interactive }) {\n const card = document.createElement(\"div\");\n card.className = CLASSES.INBOX_CARD;\n if (comment.status === \"resolved\") {\n card.classList.add(`${CLASSES.INBOX_CARD}--resolved`);\n }\n card.dataset.commentId = comment.id;\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_CARD_HEADER;\n header.appendChild(\n createMetaElement(\n comment.author,\n comment.createdAt,\n this.strings,\n this.locale\n )\n );\n header.appendChild(this._buildCardActions(comment));\n card.appendChild(header);\n\n const text = document.createElement(\"div\");\n text.className = CLASSES.INBOX_CARD_TEXT;\n text.textContent = comment.text;\n card.appendChild(text);\n\n if (comment.screenshots?.length) {\n const shots = createScreenshotsDisplay(comment.screenshots, this.strings);\n shots\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.callbacks.onShowLightbox(img.src);\n });\n });\n card.appendChild(shots);\n }\n\n const badges = this._buildBadges(comment);\n if (badges) card.appendChild(badges);\n\n const tag = this._buildTag(comment);\n if (tag) card.appendChild(tag);\n\n if (interactive) {\n card.setAttribute(\"role\", \"button\");\n card.setAttribute(\"tabindex\", \"0\");\n\n // Inactive comments belong to another page: activating them hands\n // off to that page (the detail opens there after the redirect).\n const activate = () =>\n comment.anchorState === \"inactive\"\n ? this.callbacks.onNavigateToPage(comment)\n : this._openDetail(comment);\n\n const replyLink = document.createElement(\"button\");\n replyLink.type = \"button\";\n replyLink.className = CLASSES.INBOX_CARD_REPLY_LINK;\n replyLink.textContent = this.strings.replyLink;\n replyLink.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n activate();\n });\n card.appendChild(replyLink);\n\n card.addEventListener(\"click\", activate);\n card.addEventListener(\"keydown\", (/** @type {KeyboardEvent} */ e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n activate();\n }\n });\n\n // Hovering a card spotlights its marker on the page \u2014 only when the\n // marker is actually there (anchored, visible, not resolved).\n card.addEventListener(\"mouseenter\", () => this._highlight(comment));\n card.addEventListener(\"mouseleave\", () => this._clearHighlight());\n }\n\n return card;\n }\n\n _buildTag(comment) {\n let label = null;\n if (comment.anchorState === \"orphaned\") label = this.strings.orphanedBadge;\n else if (comment.hidden) label = this.strings.hiddenBadge;\n else if (comment.anchorState === \"inactive\") label = comment.page;\n if (!label) return null;\n\n const tag = document.createElement(\"span\");\n tag.className = CLASSES.INBOX_CARD_TAG;\n tag.textContent = label;\n return tag;\n }\n\n /**\n * RF3/RF4/RF5 \u2014 classification and resolution-time badges. Every badge\n * carries text: colour alone must never be the only signal (WCAG 1.4.1).\n * @param {any} comment\n * @returns {HTMLElement | null} null when there's nothing to show\n */\n _buildBadges(comment) {\n const row = document.createElement(\"div\");\n row.className = CLASSES.INBOX_BADGES;\n\n const addBadge = (text, modifier, color) => {\n const badge = document.createElement(\"span\");\n badge.className = `${CLASSES.BADGE} ${modifier}`;\n badge.textContent = text;\n if (color) badge.style.borderColor = color;\n row.appendChild(badge);\n };\n\n if (comment.type) {\n addBadge(\n typeLabelOf(comment.type, this.strings),\n CLASSES.BADGE_TYPE,\n TYPE_COLORS[comment.type]\n );\n }\n if (comment.priority) {\n addBadge(\n priorityLabelOf(comment.priority, this.strings),\n CLASSES.BADGE_PRIORITY,\n PRIORITY_COLORS[comment.priority]\n );\n }\n for (const tag of comment.tags || []) {\n addBadge(tag, CLASSES.BADGE_TAG, null);\n }\n\n if (comment.status === \"resolved\") {\n // Comments resolved before RF5 shipped have no timestamp \u2014 show a\n // dash rather than a duration computed from data we don't have.\n const elapsed = comment.resolvedAt\n ? formatDuration(\n new Date(comment.resolvedAt).getTime() -\n new Date(comment.createdAt).getTime(),\n this.strings\n )\n : \"\";\n addBadge(\n formatTemplate(this.strings.resolvedInTemplate, elapsed || \"\u2014\"),\n CLASSES.BADGE_DURATION,\n null\n );\n }\n\n return row.children.length ? row : null;\n }\n\n /**\n * RF2 \u2014 the environment the comment was reported from, plus the\n * automatic capture. Returns null for comments created before RF1/RF2.\n * @param {any} comment\n * @returns {HTMLElement | null}\n */\n _buildContextBlock(comment) {\n const { context, contextScreenshot } = comment;\n if (!context && !contextScreenshot) return null;\n\n const block = document.createElement(\"div\");\n block.className = CLASSES.CONTEXT_BLOCK;\n\n const title = document.createElement(\"div\");\n title.className = CLASSES.INBOX_FILTER_SECTION;\n title.textContent = this.strings.contextSection;\n block.appendChild(title);\n\n if (contextScreenshot) {\n const caption = document.createElement(\"div\");\n caption.className = CLASSES.CONTEXT_SCREENSHOT_CAPTION;\n caption.textContent = this.strings.autoScreenshotLabel;\n block.appendChild(caption);\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = contextScreenshot;\n img.alt = this.strings.autoScreenshotLabel;\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.callbacks.onShowLightbox(contextScreenshot);\n });\n block.appendChild(img);\n }\n\n if (context) {\n const addRow = (label, value) => {\n if (!value) return;\n const row = document.createElement(\"div\");\n row.className = CLASSES.CONTEXT_ROW;\n const key = document.createElement(\"span\");\n key.textContent = label;\n const val = document.createElement(\"span\");\n val.textContent = value;\n row.appendChild(key);\n row.appendChild(val);\n block.appendChild(row);\n };\n\n const size = (dimensions) =>\n dimensions ? `${dimensions.width}\u00D7${dimensions.height}` : \"\";\n const named = (entry) =>\n entry?.name ? `${entry.name} ${entry.version || \"\"}`.trim() : \"\";\n\n addRow(this.strings.contextUrl, context.url);\n addRow(this.strings.contextViewport, size(context.viewport));\n addRow(this.strings.contextScreen, size(context.screen));\n addRow(this.strings.contextBrowser, named(context.browser));\n addRow(this.strings.contextOs, named(context.os));\n }\n\n return block;\n }\n\n _buildCardActions(comment) {\n return createCommentActions(comment, {\n strings: this.strings,\n onCopy: (c) =>\n copyToClipboard(\n buildAgentContext(c, {\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n strings: this.strings,\n })\n ),\n onSetStatus: (c, status) => this.callbacks.onSetStatus(c.id, status),\n onSetType: (c, type) => this.callbacks.onSetType(c.id, type),\n onSetPriority: (c, priority) =>\n this.callbacks.onSetPriority(c.id, priority),\n onDelete: (c) => {\n if (this.detailId === c.id) this.detailId = null;\n this.callbacks.onDelete(c.id);\n this.render();\n },\n });\n }\n\n _renderDetail(comment, comments) {\n const index = comments.indexOf(comment);\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_DETAIL_HEADER;\n\n const backBtn = document.createElement(\"button\");\n backBtn.type = \"button\";\n backBtn.className = CLASSES.INBOX_BACK;\n backBtn.innerHTML = `${CHEVRON_LEFT_SVG}<span>${this.strings.back}</span>`;\n backBtn.addEventListener(\"click\", () => {\n this.detailId = null;\n this.render();\n });\n header.appendChild(backBtn);\n\n const nav = document.createElement(\"div\");\n nav.className = CLASSES.INBOX_CARD_ACTIONS;\n\n const navBtn = (svg, label, targetIndex) => {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_NAV_BTN;\n btn.setAttribute(\"aria-label\", label);\n btn.title = label;\n btn.innerHTML = svg;\n const target = comments[targetIndex];\n btn.disabled = !target;\n if (target) {\n btn.addEventListener(\"click\", () => this._openDetail(target));\n }\n return btn;\n };\n\n nav.appendChild(navBtn(ARROW_UP_SVG, this.strings.prevComment, index - 1));\n nav.appendChild(\n navBtn(ARROW_DOWN_SVG, this.strings.nextComment, index + 1)\n );\n nav.appendChild(this._closeButton());\n header.appendChild(nav);\n\n this.el.appendChild(header);\n\n const detail = document.createElement(\"div\");\n detail.className = CLASSES.INBOX_DETAIL;\n\n detail.appendChild(this._buildCard(comment, { interactive: false }));\n\n const context = this._buildContextBlock(comment);\n if (context) detail.appendChild(context);\n\n const replies = document.createElement(\"div\");\n replies.className = CLASSES.INBOX_REPLIES;\n for (const reply of comment.replies || []) {\n const replyEl = createReplyElement(reply, this.strings, this.locale);\n replyEl\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.callbacks.onShowLightbox(img.src);\n });\n });\n replies.appendChild(replyEl);\n }\n detail.appendChild(replies);\n\n detail.appendChild(this._buildReplyInput(comment));\n this.el.appendChild(detail);\n }\n\n _buildReplyInput(comment) {\n const {\n container,\n inputEl,\n screenshotsContainer,\n attachBtn,\n fileInput,\n submitBtn,\n } = createInputArea(\n {\n areaClassName: CLASSES.THREAD_INPUT_AREA,\n inputTag: \"input\",\n inputClassName: CLASSES.THREAD_INPUT,\n inputPlaceholder: this.strings.replyPlaceholder,\n },\n this.strings\n );\n\n let pendingScreenshots = [];\n\n const updatePreview = () => {\n screenshotsContainer.innerHTML = \"\";\n screenshotsContainer.classList.toggle(\n CLASSES.ACTIVE,\n pendingScreenshots.length > 0\n );\n pendingScreenshots.forEach((dataUrl, i) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = dataUrl;\n img.alt = this.strings.attachedScreenshot;\n img.onclick = () => this.callbacks.onShowLightbox(dataUrl);\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = CLASSES.SCREENSHOT_REMOVE;\n removeBtn.setAttribute(\"aria-label\", this.strings.removeScreenshot);\n removeBtn.innerHTML = \"×\";\n removeBtn.onclick = (e) => {\n e.stopPropagation();\n pendingScreenshots.splice(i, 1);\n updatePreview();\n };\n item.appendChild(img);\n item.appendChild(removeBtn);\n screenshotsContainer.appendChild(item);\n });\n };\n\n attachBtn.addEventListener(\"click\", () => fileInput.click());\n fileInput.addEventListener(\"change\", (e) => {\n const file = /** @type {HTMLInputElement} */ (e.target).files[0];\n if (!file || pendingScreenshots.length >= 5) return;\n const reader = new FileReader();\n reader.onload = (ev) => {\n pendingScreenshots.push(ev.target.result);\n updatePreview();\n };\n reader.readAsDataURL(file);\n fileInput.value = \"\";\n });\n\n const submit = () => {\n const text = inputEl.value.trim();\n if (!text && pendingScreenshots.length === 0) return;\n this.callbacks.onReply(comment, text, [...pendingScreenshots]);\n pendingScreenshots = [];\n this.render();\n };\n\n submitBtn.addEventListener(\"click\", submit);\n inputEl.addEventListener(\"keydown\", (/** @type {KeyboardEvent} */ e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n });\n\n return container;\n }\n}\n", "import {\n renderPage,\n cropRegion,\n cropViewport,\n withHiddenOverlay,\n AUTO_SCALE,\n} from \"./capture.js\";\nimport { captureContext } from \"./metadata.js\";\nimport {\n CLASSES,\n IDS,\n SELECTORS,\n STATUSES,\n COMMENT_TYPES,\n PRIORITIES,\n} from \"./constants.js\";\nimport { getStyles, getGlobalStyles } from \"./styles.js\";\nimport { getShadowRoot, TAG_NAME } from \"./root-element.js\";\nimport { getStrings, detectLocale } from \"./i18n.js\";\nimport {\n createAnchor,\n resolveAnchor,\n generateElementSelector,\n} from \"./anchor.js\";\nimport {\n readStoredComments,\n writeStoredComments,\n mergeForStorage,\n PENDING_DETAIL_KEY,\n} from \"./storage.js\";\nimport {\n createToolbar,\n createCommentBox,\n createCommentCircle,\n createTooltip,\n createThreadPopover,\n createReplyElement,\n} from \"./components.js\";\nimport { InboxView } from \"./inbox.js\";\nimport { createCommentActions, copyToClipboard } from \"./comment-actions.js\";\nimport { buildAgentContext } from \"./agent-context.js\";\n\n// Tags are user-typed, so they arrive with stray case and whitespace.\n// Normalising here (rather than at each entry point) is what makes\n// \"Checkout\" and \"checkout \" the same tag for filtering.\nconst normalizeTags = (tags) => {\n const seen = new Set();\n for (const tag of tags) {\n const clean = String(tag).trim().toLowerCase();\n if (clean) seen.add(clean);\n }\n return [...seen];\n};\n\nclass CommentOverlay {\n /**\n * @param {import('./index.d.ts').CommentOverlayOptions} [options]\n */\n constructor(options = {}) {\n this.comments = [];\n this.commentMode = false;\n this.isMac = /Mac|iPod|iPhone|iPad/.test(navigator.userAgent);\n this.options = {\n shortcutKey: options.shortcutKey || (this.isMac ? \"c\" : \"C\"),\n shortcutModifier: options.shortcutModifier || \"alt\",\n autoScreenshot: options.autoScreenshot !== false,\n ...options,\n };\n this.locale = this.options.locale || detectLocale();\n this.strings = getStrings(this.locale);\n\n // Initialize resize observers and position validation\n this.resizeObservers = new Map();\n // Track mutation observers per comment\n this.mutationObservers = new Map();\n this.positionValidationEnabled = true;\n\n // rAF scheduling flag for bulk updates\n this._pendingRaf = null;\n this._pendingContextScreenshot = null;\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", () => this.initOverlay());\n } else {\n this.initOverlay();\n }\n }\n\n initOverlay() {\n // Mount inside a dedicated shadow root so widget styles/markup stay\n // isolated from the host page in both directions.\n this.shadowRoot = getShadowRoot();\n\n // Create and append UI elements\n this.toolbar = createToolbar(this.options, this.strings);\n this.commentBox = createCommentBox(this.strings);\n this.overlay = document.createElement(\"div\");\n this.overlay.className = CLASSES.COMMENT_OVERLAY;\n\n this.shadowRoot.appendChild(this.overlay);\n this.shadowRoot.appendChild(this.toolbar);\n this.shadowRoot.appendChild(this.commentBox);\n\n this.commentBtn = this.toolbar.querySelector(\n `.${CLASSES.TOOLBAR_COMMENT_BTN}`\n );\n this.inboxBtn = this.toolbar.querySelector(`.${CLASSES.TOOLBAR_MENU_BTN}`);\n /** @type {HTMLButtonElement} */\n this.submitButton = /** @type {any} */ (\n this.shadowRoot.getElementById(IDS.SUBMIT_COMMENT)\n );\n /** @type {HTMLTextAreaElement} */\n this.commentInput = /** @type {any} */ (\n this.shadowRoot.getElementById(IDS.COMMENT_INPUT)\n );\n this.attachImageBtn = this.commentBox.querySelector(\n `.${CLASSES.ATTACH_IMAGE_BTN}`\n );\n /** @type {HTMLInputElement} */\n this.attachImageInput = /** @type {any} */ (\n this.shadowRoot.getElementById(IDS.ATTACH_IMAGE_INPUT)\n );\n\n // Bind event listeners\n this.bindEventListeners();\n this.setupKeyboardShortcut();\n this.setupResizeHandlers();\n this.injectStyles();\n\n if (this.options.persistence === \"localStorage\") {\n this.loadComments(readStoredComments());\n this._openPendingDetail();\n }\n }\n\n _navigateTo(url) {\n location.assign(url);\n }\n\n // Cross-page handoff: an inactive card click on the previous page left a\n // comment id in sessionStorage; open the inbox on its detail here.\n _openPendingDetail() {\n let id = null;\n try {\n id = sessionStorage.getItem(PENDING_DETAIL_KEY);\n if (id != null) sessionStorage.removeItem(PENDING_DETAIL_KEY);\n } catch {\n return;\n }\n if (!id) return;\n const comment = this.comments.find((c) => String(c.id) === id);\n if (!comment) return;\n this.showInbox();\n this.inboxView.openDetail(comment.id);\n }\n\n _syncStorage() {\n if (this.options.persistence !== \"localStorage\") return;\n writeStoredComments(\n mergeForStorage(\n readStoredComments(),\n this.serializeComments(),\n location.pathname\n )\n );\n }\n\n bindEventListeners() {\n this.commentBtn.addEventListener(\"click\", () => this.toggleCommentMode());\n this.inboxBtn.addEventListener(\"click\", () => this.toggleInbox());\n this.submitButton.addEventListener(\"click\", () => this.saveComment());\n\n this.commentInput.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n this.saveComment();\n }\n });\n\n this.attachImageBtn.addEventListener(\"click\", () => {\n this.attachImageInput.click();\n });\n\n this.attachImageInput.addEventListener(\"change\", (e) => {\n const file = /** @type {HTMLInputElement} */ (e.target).files[0];\n if (!file) return;\n if (!this._pendingScreenshots) this._pendingScreenshots = [];\n if (this._pendingScreenshots.length >= 5) return;\n\n const reader = new FileReader();\n reader.onload = (ev) => {\n this._pendingScreenshots.push(ev.target.result);\n this._updateScreenshotsPreview();\n };\n reader.readAsDataURL(file);\n this.attachImageInput.value = \"\";\n });\n\n this._handleDocumentClickBound = (e) => this.handleDocumentClick(e);\n document.addEventListener(\"mousedown\", this._handleDocumentClickBound);\n }\n\n setupKeyboardShortcut() {\n // Remove any existing event listeners\n if (this.keydownHandler) {\n document.removeEventListener(\"keydown\", this.keydownHandler);\n }\n\n // Create a new handler with proper binding\n this.keydownHandler = (e) => {\n if (e.key === \"Escape\") {\n if (this._activeLightbox) {\n this.closeLightbox();\n } else if (this.activeThreadPopover) {\n this.closeThreadPopover();\n } else if (this.inboxView?.isOpen()) {\n this.closeInbox();\n } else if (this.commentBox.style.display !== \"none\") {\n this.hideCommentBox();\n this.toggleCommentMode();\n } else if (this.commentMode) {\n this.toggleCommentMode();\n }\n return;\n }\n\n const isMacOptionC =\n this.isMac && e.altKey && (e.key === \"\u00E7\" || e.key === \"\u00C7\");\n const isWindowsAltC =\n !this.isMac && e.altKey && e.key.toLowerCase() === \"c\";\n const isCustomShortcut =\n e.key.toLowerCase() === this.options.shortcutKey.toLowerCase() &&\n ((this.options.shortcutModifier === \"alt\" && e.altKey) ||\n (this.options.shortcutModifier === \"ctrl\" &&\n (e.ctrlKey || e.metaKey)) ||\n (this.options.shortcutModifier === \"shift\" && e.shiftKey));\n\n if (isMacOptionC || isWindowsAltC || isCustomShortcut) {\n e.preventDefault();\n e.stopPropagation();\n this.toggleCommentMode();\n return false;\n }\n };\n\n // Add the event listener\n document.addEventListener(\"keydown\", this.keydownHandler);\n }\n\n handleDocumentClick(e) {\n if (!this.commentMode) return;\n\n // Listener is attached on `document`, outside the shadow boundary, so\n // `e.target` gets retargeted to the shadow host. Use composedPath() to\n // recover the real, deepest target inside the shadow tree.\n const target = e.composedPath()[0] || e.target;\n\n if (\n this.toolbar.contains(target) ||\n target?.closest?.(`.${CLASSES.CIRCLE}`) ||\n target?.closest?.(`.${CLASSES.TOOLTIP}`) ||\n target?.closest?.(`.${CLASSES.THREAD_POPOVER}`) ||\n target?.closest?.(`.${CLASSES.INBOX_PANEL}`) ||\n target?.closest?.(`.${CLASSES.LIGHTBOX}`)\n ) {\n return;\n }\n\n if (this.commentBox.contains(target)) {\n return;\n }\n\n if (this.commentBox.style.display !== \"none\") {\n this.hideCommentBox();\n this.toggleCommentMode();\n return;\n }\n\n if (e.button !== 0) return;\n e.preventDefault();\n\n this._dragStart = { x: e.clientX, y: e.clientY };\n this._isDragging = false;\n\n this._boundDragMove = (ev) => this._onDragMove(ev);\n this._boundDragEnd = (ev) => this._onDragEnd(ev);\n document.addEventListener(\"mousemove\", this._boundDragMove);\n document.addEventListener(\"mouseup\", this._boundDragEnd);\n }\n\n _onDragMove(e) {\n const dx = e.clientX - this._dragStart.x;\n const dy = e.clientY - this._dragStart.y;\n\n if (!this._isDragging && Math.hypot(dx, dy) < 5) return;\n\n this._isDragging = true;\n\n const left = Math.min(this._dragStart.x, e.clientX);\n const top = Math.min(this._dragStart.y, e.clientY);\n const width = Math.abs(dx);\n const height = Math.abs(dy);\n\n if (!this._selectionRect) {\n this._selectionRect = document.createElement(\"div\");\n this._selectionRect.className = CLASSES.SELECTION_RECT;\n this.shadowRoot.appendChild(this._selectionRect);\n }\n\n this._selectionRect.style.left = `${left}px`;\n this._selectionRect.style.top = `${top}px`;\n this._selectionRect.style.width = `${width}px`;\n this._selectionRect.style.height = `${height}px`;\n }\n\n async _onDragEnd(e) {\n document.removeEventListener(\"mousemove\", this._boundDragMove);\n document.removeEventListener(\"mouseup\", this._boundDragEnd);\n\n if (this._isDragging) {\n const left = Math.min(this._dragStart.x, e.clientX);\n const top = Math.min(this._dragStart.y, e.clientY);\n const width = Math.abs(e.clientX - this._dragStart.x);\n const height = Math.abs(e.clientY - this._dragStart.y);\n\n this._selectionRect?.remove();\n this._selectionRect = null;\n\n if (width > 10 && height > 10) {\n try {\n if (!this._pendingScreenshots) this._pendingScreenshots = [];\n // One render feeds both images: the PNG region the user selected\n // and the automatic JPEG context shot.\n const full = await withHiddenOverlay(() => renderPage({ scale: 1 }));\n const dataUrl = cropRegion(full, { left, top, width, height });\n if (dataUrl && this._pendingScreenshots.length < 5) {\n this._pendingScreenshots.push(dataUrl);\n }\n if (this.options.autoScreenshot) {\n this._pendingContextScreenshot = cropViewport(full, {\n sourceScale: 1,\n });\n }\n } catch (err) {\n console.warn(\"Screenshot capture failed:\", err);\n }\n }\n\n await this._placeCommentAtPoint(e.clientX, e.clientY);\n } else {\n await this._placeCommentAtPoint(this._dragStart.x, this._dragStart.y);\n }\n\n this._isDragging = false;\n this._dragStart = null;\n }\n\n async _placeCommentAtPoint(clientX, clientY) {\n // The no-drag path has no render yet. Half scale because the output is\n // half scale anyway \u2014 the render is the expensive part, and it costs\n // ~4x less here than at scale 1.\n if (this.options.autoScreenshot && !this._pendingContextScreenshot) {\n try {\n const full = await withHiddenOverlay(() =>\n renderPage({ scale: AUTO_SCALE })\n );\n this._pendingContextScreenshot = cropViewport(full, {\n sourceScale: AUTO_SCALE,\n });\n } catch (err) {\n console.warn(\"HellDots: automatic screenshot failed\", err);\n this._pendingContextScreenshot = null;\n }\n }\n\n const prevPointerEvents = this.overlay.style.pointerEvents;\n this.overlay.style.pointerEvents = \"none\";\n const underlying = document.elementFromPoint(clientX, clientY);\n this.overlay.style.pointerEvents = prevPointerEvents || \"\";\n\n const container =\n underlying?.closest?.(SELECTORS.CONTAINER) || document.body;\n const containerRect = container.getBoundingClientRect();\n\n // Zero-size containers (display:none, not yet laid out) would make the\n // division blow up to Infinity \u2014 which isn't JSON-serializable either.\n const relativeX =\n containerRect.width > 0\n ? (clientX - containerRect.left) / containerRect.width\n : 0;\n const relativeY =\n containerRect.height > 0\n ? (clientY - containerRect.top) / containerRect.height\n : 0;\n\n const anchor = createAnchor(\n /** @type {HTMLElement} */ (container),\n relativeX,\n relativeY\n );\n // The clicked element can disappear (responsive display:none) while the\n // coarse anchor container stays visible \u2014 track it separately so the\n // marker hides with what the user actually commented on.\n anchor.targetSelector =\n underlying && underlying !== container\n ? generateElementSelector(/** @type {HTMLElement} */ (underlying))\n : null;\n\n this.currentPosition = {\n container,\n relativeX,\n relativeY,\n anchor,\n target: /** @type {HTMLElement} */ (underlying || container),\n };\n\n this.createPreviewCircle(clientX, clientY);\n document.body.classList.remove(CLASSES.COMMENT_CURSOR);\n\n if (this._pendingScreenshots && this._pendingScreenshots.length > 0) {\n this._updateScreenshotsPreview();\n }\n\n this.showCommentBox(clientX, clientY);\n }\n\n _updateScreenshotsPreview() {\n const container = this.commentBox.querySelector(\n `.${CLASSES.SCREENSHOTS_CONTAINER}`\n );\n if (!container) return;\n container.innerHTML = \"\";\n\n if (!this._pendingScreenshots || this._pendingScreenshots.length === 0) {\n container.classList.remove(CLASSES.ACTIVE);\n return;\n }\n\n container.classList.add(CLASSES.ACTIVE);\n\n this._pendingScreenshots.forEach((dataUrl, i) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = dataUrl;\n img.alt = this.strings.attachedScreenshot;\n img.onclick = () => this.showLightbox(dataUrl);\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = CLASSES.SCREENSHOT_REMOVE;\n removeBtn.setAttribute(\"aria-label\", this.strings.removeScreenshot);\n removeBtn.innerHTML = \"×\";\n removeBtn.onclick = (e) => {\n e.stopPropagation();\n this._pendingScreenshots.splice(i, 1);\n this._updateScreenshotsPreview();\n };\n\n item.appendChild(img);\n item.appendChild(removeBtn);\n container.appendChild(item);\n });\n }\n\n _clearScreenshotPreview() {\n this._pendingScreenshots = [];\n const container = this.commentBox.querySelector(\n `.${CLASSES.SCREENSHOTS_CONTAINER}`\n );\n if (container) {\n container.innerHTML = \"\";\n container.classList.remove(CLASSES.ACTIVE);\n }\n }\n\n showCommentBox(x, y) {\n this.commentBox.style.display = \"block\";\n\n const boxWidth = 300;\n const circleBaseSize = 28;\n const circleRadius = circleBaseSize / 2;\n const offset = circleRadius + 10;\n const windowWidth = window.innerWidth;\n const windowHeight = window.innerHeight;\n\n const centerX = x + circleRadius;\n const centerY = y + circleRadius;\n\n let adjustedX = centerX + offset;\n let adjustedY = centerY - circleRadius;\n\n if (adjustedX + boxWidth > windowWidth) {\n adjustedX = centerX - offset - boxWidth;\n }\n adjustedX = Math.max(10, adjustedX);\n\n const boxRect = this.commentBox.getBoundingClientRect();\n if (adjustedY + boxRect.height > windowHeight) {\n adjustedY = windowHeight - boxRect.height - 10;\n }\n adjustedY = Math.max(10, adjustedY);\n\n this.commentBox.style.left = `${adjustedX}px`;\n this.commentBox.style.top = `${adjustedY}px`;\n\n this.commentInput.value = \"\";\n setTimeout(() => this.commentInput.focus(), 50);\n }\n\n hideCommentBox() {\n this.commentBox.style.display = \"none\";\n this.commentInput.style.height = \"auto\";\n this.currentPosition = null;\n this.removePreviewCircle();\n this._clearScreenshotPreview();\n this._pendingContextScreenshot = null;\n /** @type {any} */ (this.commentBox).classify?.reset();\n\n if (this.commentMode) {\n document.body.classList.add(CLASSES.COMMENT_CURSOR);\n }\n }\n\n toggleCommentMode() {\n this.commentMode = !this.commentMode;\n this.commentBtn?.classList.toggle(CLASSES.ACTIVE, this.commentMode);\n this.commentBtn?.setAttribute(\"aria-pressed\", String(this.commentMode));\n this.overlay.classList.toggle(CLASSES.ACTIVE, this.commentMode);\n document.body.classList.toggle(CLASSES.COMMENT_CURSOR, this.commentMode);\n\n if (!this.commentMode) {\n this.hideCommentBox();\n }\n }\n\n saveComment() {\n if (!this.commentInput.value.trim() || !this.currentPosition) return;\n\n const comment = {\n text: this.commentInput.value,\n container: this.currentPosition.container,\n relativeX: this.currentPosition.relativeX,\n relativeY: this.currentPosition.relativeY,\n anchor: this.currentPosition.anchor,\n anchorState: \"anchored\",\n target: this.currentPosition.target,\n hidden: false,\n status: \"open\",\n page: location.pathname,\n id: Date.now(),\n replies: [],\n author: this.options.user?.name || this.strings.anonymous,\n createdAt: new Date().toISOString(),\n screenshots: this._pendingScreenshots\n ? [...this._pendingScreenshots]\n : [],\n type: /** @type {any} */ (this.commentBox).classify?.getType() ?? null,\n priority:\n /** @type {any} */ (this.commentBox).classify?.getPriority() ?? null,\n tags: /** @type {any} */ (this.commentBox).classify?.getTags() ?? [],\n resolvedAt: null,\n context: captureContext(),\n contextScreenshot: this._pendingContextScreenshot,\n };\n\n this.comments.push(comment);\n this._syncStorage();\n this.options.onCommentCreated?.(this._serializeComment(comment));\n this.renderCommentCircle(comment);\n this.hideCommentBox();\n this.toggleCommentMode();\n\n const circle = this.shadowRoot.querySelector(\n `[data-comment-id=\"${comment.id}\"]`\n );\n if (circle) {\n this.showThreadPopover(circle, comment);\n }\n }\n\n renderCommentCircle(comment) {\n const circle = createCommentCircle(comment, this.strings);\n\n circle.addEventListener(\"mouseenter\", () =>\n this.showCommentTooltip(circle, comment)\n );\n circle.addEventListener(\"mouseleave\", () => {\n setTimeout(() => {\n const tooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (tooltip && !tooltip.matches(\":hover\")) {\n tooltip.remove();\n }\n }, 250);\n });\n\n circle.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const tooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (tooltip) tooltip.remove();\n this.showThreadPopover(circle, comment);\n });\n\n // The circle is a <div role=\"button\">, so unlike a real <button> it\n // doesn't get Enter/Space-activates-click for free.\n circle.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n circle.click();\n }\n });\n\n this.overlay.appendChild(circle);\n this.updateCommentPosition(comment, circle);\n\n this.createResizeObserver(comment, circle);\n this.createMutationObserver(comment);\n }\n\n showCommentTooltip(circle, comment) {\n const existingPopover = this.shadowRoot.querySelector(\n `.${CLASSES.THREAD_POPOVER}[data-for=\"${comment.id}\"]`\n );\n if (existingPopover) return;\n\n const existingTooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (existingTooltip) return;\n\n const tooltip = createTooltip(comment, this.strings, this.locale);\n this.shadowRoot.appendChild(tooltip);\n\n tooltip\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.showLightbox(img.src);\n });\n });\n\n setTimeout(() => {\n this.positionPopoverAtCircle(tooltip, circle);\n }, 10);\n\n tooltip\n .querySelector(`.${CLASSES.CLOSE_TOOLTIP}`)\n .addEventListener(\"click\", (e) => {\n e.stopPropagation();\n tooltip.remove();\n });\n\n tooltip.addEventListener(\"mouseleave\", () => tooltip.remove());\n }\n\n toggleInbox() {\n if (this.inboxView?.isOpen()) {\n this.closeInbox();\n } else {\n this.showInbox();\n }\n }\n\n showInbox() {\n this.closeThreadPopover();\n\n if (!this.inboxView) {\n this.inboxView = new InboxView({\n shadowRoot: this.shadowRoot,\n strings: this.strings,\n locale: this.locale,\n currentPage: location.pathname,\n getComments: () => this.comments,\n callbacks: {\n onOpenDetailScroll: (comment) =>\n comment.container?.scrollIntoView?.({ block: \"center\" }),\n onReply: (comment, text, screenshots) =>\n this.addReply(comment, text, screenshots),\n onDelete: (id) => this.deleteComment(id),\n onSetStatus: (id, status) => this.setCommentStatus(id, status),\n onSetType: (id, type) => this.setCommentType(id, type),\n onSetPriority: (id, priority) =>\n this.setCommentPriority(id, priority),\n onNavigateToPage: (comment) => {\n try {\n sessionStorage.setItem(PENDING_DETAIL_KEY, String(comment.id));\n } catch {}\n this._navigateTo(comment.page);\n },\n onShowLightbox: (src) => this.showLightbox(src),\n onClose: () => this.closeInbox(),\n },\n });\n }\n this.inboxView.open();\n\n setTimeout(() => {\n this._inboxClickHandler = (e) => {\n const target = e.composedPath()[0] || e.target;\n if (\n !this.inboxView.el?.contains(target) &&\n !this.inboxBtn.contains(target)\n ) {\n this.closeInbox();\n }\n };\n document.addEventListener(\"mousedown\", this._inboxClickHandler);\n }, 0);\n }\n\n closeInbox() {\n this.inboxView?.close();\n if (this._inboxClickHandler) {\n document.removeEventListener(\"mousedown\", this._inboxClickHandler);\n this._inboxClickHandler = null;\n }\n }\n\n // `circle` may be null for orphaned comments (opened from the inbox):\n // the popover is centered in the viewport instead of pinned to a marker.\n showThreadPopover(circle, comment) {\n this.closeThreadPopover();\n\n const existingTooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (existingTooltip) existingTooltip.remove();\n\n const popover = createThreadPopover(comment, this.strings, this.locale);\n this.shadowRoot.appendChild(popover);\n\n // Same action strip as the inbox cards: copy agent context, lifecycle\n // status picker (RF09) and the \u22EF menu.\n const headerEl = popover.querySelector(`.${CLASSES.THREAD_HEADER}`);\n const actionsEl = createCommentActions(comment, {\n strings: this.strings,\n onCopy: (c) =>\n copyToClipboard(\n buildAgentContext(c, {\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n strings: this.strings,\n })\n ),\n onSetStatus: (c, status) => this.setCommentStatus(c.id, status),\n onSetType: (c, type) => this.setCommentType(c.id, type),\n onSetPriority: (c, priority) => this.setCommentPriority(c.id, priority),\n onDelete: (c) => {\n this.closeThreadPopover();\n this.deleteComment(c.id);\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n },\n });\n headerEl.insertBefore(\n actionsEl,\n headerEl.querySelector(`.${CLASSES.CLOSE_TOOLTIP}`)\n );\n\n const mainScreenshotsContainer = Array.from(popover.children).find(\n (child) => child.classList.contains(CLASSES.SCREENSHOTS_CONTAINER)\n );\n if (mainScreenshotsContainer) {\n mainScreenshotsContainer\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.showLightbox(img.src);\n });\n });\n }\n\n setTimeout(() => {\n if (circle) {\n this.positionPopoverAtCircle(popover, circle);\n } else {\n this.centerPopover(popover);\n }\n }, 10);\n\n popover\n .querySelector(`.${CLASSES.CLOSE_TOOLTIP}`)\n .addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.closeThreadPopover();\n });\n\n /** @type {HTMLInputElement} */\n const input = /** @type {any} */ (\n popover.querySelector(`.${CLASSES.THREAD_INPUT}`)\n );\n const submitBtn = popover.querySelector(`.${CLASSES.THREAD_SUBMIT}`);\n const threadAttachBtn = popover.querySelector(\n `.${CLASSES.THREAD_INPUT_AREA} .${CLASSES.ATTACH_IMAGE_BTN}`\n );\n /** @type {HTMLInputElement} */\n const threadFileInput = /** @type {any} */ (\n popover.querySelector(`.${CLASSES.THREAD_INPUT_AREA} input[type=\"file\"]`)\n );\n const threadScreenshotsContainer = popover.querySelector(\n `.${CLASSES.THREAD_INPUT_AREA} .${CLASSES.SCREENSHOTS_CONTAINER}`\n );\n\n let pendingReplyScreenshots = [];\n\n const updateReplyScreenshotsPreview = () => {\n threadScreenshotsContainer.innerHTML = \"\";\n if (pendingReplyScreenshots.length === 0) {\n threadScreenshotsContainer.classList.remove(CLASSES.ACTIVE);\n return;\n }\n threadScreenshotsContainer.classList.add(CLASSES.ACTIVE);\n pendingReplyScreenshots.forEach((dataUrl, i) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = dataUrl;\n img.alt = this.strings.attachedScreenshot;\n img.onclick = () => this.showLightbox(dataUrl);\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.className = CLASSES.SCREENSHOT_REMOVE;\n removeBtn.innerHTML = \"×\";\n removeBtn.onclick = (e) => {\n e.stopPropagation();\n pendingReplyScreenshots.splice(i, 1);\n updateReplyScreenshotsPreview();\n };\n\n item.appendChild(img);\n item.appendChild(removeBtn);\n threadScreenshotsContainer.appendChild(item);\n });\n };\n\n threadAttachBtn.addEventListener(\"click\", () => {\n threadFileInput.click();\n });\n\n threadFileInput.addEventListener(\"change\", (e) => {\n const file = /** @type {HTMLInputElement} */ (e.target).files[0];\n if (!file) return;\n if (pendingReplyScreenshots.length >= 5) return;\n\n const reader = new FileReader();\n reader.onload = (ev) => {\n pendingReplyScreenshots.push(ev.target.result);\n updateReplyScreenshotsPreview();\n };\n reader.readAsDataURL(file);\n threadFileInput.value = \"\";\n });\n\n const submitReply = () => {\n const text = input.value.trim();\n if (!text && pendingReplyScreenshots.length === 0) return;\n\n const reply = this.addReply(\n comment,\n text,\n pendingReplyScreenshots.length > 0 ? [...pendingReplyScreenshots] : []\n );\n\n const repliesContainer = popover.querySelector(\n `.${CLASSES.THREAD_REPLIES}`\n );\n const replyEl = createReplyElement(reply, this.strings, this.locale);\n repliesContainer.appendChild(replyEl);\n\n replyEl\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.showLightbox(img.src);\n });\n });\n\n input.value = \"\";\n pendingReplyScreenshots = [];\n updateReplyScreenshotsPreview();\n input.focus();\n };\n\n submitBtn.addEventListener(\"click\", submitReply);\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submitReply();\n }\n });\n\n this.activeThreadPopover = popover;\n\n setTimeout(() => input.focus(), 50);\n\n setTimeout(() => {\n this._threadClickHandler = (e) => {\n const target = e.composedPath()[0] || e.target;\n if (!popover.contains(target) && !circle?.contains(target)) {\n this.closeThreadPopover();\n }\n };\n document.addEventListener(\"mousedown\", this._threadClickHandler);\n }, 0);\n }\n\n closeThreadPopover() {\n if (this.activeThreadPopover) {\n this.activeThreadPopover.remove();\n this.activeThreadPopover = null;\n }\n if (this._threadClickHandler) {\n document.removeEventListener(\"mousedown\", this._threadClickHandler);\n this._threadClickHandler = null;\n }\n }\n\n showLightbox(imageSrc) {\n this.closeLightbox();\n\n const lightbox = document.createElement(\"div\");\n lightbox.className = CLASSES.LIGHTBOX;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.LIGHTBOX_IMG;\n img.src = imageSrc;\n img.alt = this.strings.screenshotPreview;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = CLASSES.LIGHTBOX_CLOSE;\n closeBtn.setAttribute(\"aria-label\", this.strings.close);\n closeBtn.innerHTML = \"×\";\n closeBtn.addEventListener(\"click\", () => this.closeLightbox());\n\n lightbox.appendChild(img);\n lightbox.appendChild(closeBtn);\n\n lightbox.addEventListener(\"click\", (e) => {\n if (e.target === lightbox) this.closeLightbox();\n });\n\n this.shadowRoot.appendChild(lightbox);\n this._activeLightbox = lightbox;\n }\n\n closeLightbox() {\n this._activeLightbox?.remove();\n this._activeLightbox = null;\n }\n\n addReply(comment, text, screenshots = []) {\n if (!comment.replies) comment.replies = [];\n const reply = {\n id: Date.now(),\n text,\n author: this.options.user?.name || this.strings.anonymous,\n timestamp: new Date().toISOString(),\n screenshots,\n };\n comment.replies.push(reply);\n this._syncStorage();\n this.options.onReplyAdded?.(\n this._serializeComment(comment),\n this._serializeReply(reply)\n );\n return reply;\n }\n\n _serializeReply({ id, text, author, timestamp, screenshots }) {\n return { id, text, author, timestamp, screenshots: screenshots || [] };\n }\n\n /**\n * Serializable snapshot of one comment: the live `container` element is\n * replaced by its `anchor`. Screenshots (data-URLs) are included \u2014 the\n * localStorage mode and the inbox cards need them.\n */\n _serializeComment(comment) {\n return {\n id: comment.id,\n text: comment.text,\n anchor: comment.anchor || null,\n page: comment.page || location.pathname,\n replies: (comment.replies || []).map((reply) =>\n this._serializeReply(reply)\n ),\n author: comment.author,\n createdAt: comment.createdAt,\n screenshots: comment.screenshots || [],\n status: comment.status || \"open\",\n type: comment.type || null,\n priority: comment.priority || null,\n // Copied rather than referenced: a host mutating serializeComments()\n // output must not be able to reach back into overlay internals.\n tags: comment.tags ? [...comment.tags] : [],\n resolvedAt: comment.resolvedAt || null,\n context: comment.context ? { ...comment.context } : null,\n contextScreenshot: comment.contextScreenshot || null,\n };\n }\n\n /**\n * RF09 \u2014 moves a comment through its lifecycle\n * (open \u2192 in_progress \u2192 resolved \u2192 closed, in any order).\n * @param {number} id\n * @param {import('./index.d.ts').CommentStatus} status\n * @returns {boolean} false when the id or status is unknown\n */\n setCommentStatus(id, status) {\n if (!STATUSES.includes(status)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n // No-op: picking the status the comment is already in must not re-stamp\n // resolvedAt (that would reset RF5's elapsed time to \"<1m\") or trigger a\n // storage write / inbox refresh / callback for nothing having changed.\n if (comment.status === status) return true;\n comment.status = status;\n // RF5 \u2014 the timestamp always describes the CURRENT resolution: a\n // reopened comment loses it, and resolving again re-stamps it.\n comment.resolvedAt =\n status === \"resolved\" ? new Date().toISOString() : null;\n // Resolving removes the on-page marker; reopening restores it.\n const circle = /** @type {HTMLElement} */ (\n this.shadowRoot?.querySelector(`[data-comment-id=\"${id}\"]`)\n );\n if (circle) this.updateCommentPosition(comment, circle);\n this._syncStorage();\n // Re-render the inbox so the card picks up the new status right away\n // (resolved styling + sink-to-bottom sorting) no matter where the\n // change came from \u2014 card, detail or thread popover.\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n this.options.onCommentStatusChanged?.(this._serializeComment(comment));\n return true;\n }\n\n /**\n * Shared tail of the classification setters: persist, re-render the\n * inbox if it's showing, and notify the host app.\n * @param {any} comment\n * @returns {true}\n */\n _commitUpdate(comment) {\n this._syncStorage();\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n this.options.onCommentUpdated?.(this._serializeComment(comment));\n return true;\n }\n\n /**\n * RF3 \u2014 categorises a comment. `null` returns it to the neutral state.\n * @param {number} id\n * @param {import('./index.d.ts').CommentType | null} type\n * @returns {boolean} false when the id or type is unknown\n */\n setCommentType(id, type) {\n if (type !== null && !COMMENT_TYPES.includes(type)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n comment.type = type;\n return this._commitUpdate(comment);\n }\n\n /**\n * RF4 \u2014 prioritises a comment. `null` returns it to the neutral state.\n * @param {number} id\n * @param {import('./index.d.ts').CommentPriority | null} priority\n * @returns {boolean} false when the id or priority is unknown\n */\n setCommentPriority(id, priority) {\n if (priority !== null && !PRIORITIES.includes(priority)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n comment.priority = priority;\n return this._commitUpdate(comment);\n }\n\n /**\n * RF3 \u2014 replaces a comment's free-form labels. Values are trimmed,\n * lowercased and de-duplicated.\n * @param {number} id\n * @param {string[]} tags\n * @returns {boolean} false when the id is unknown or tags isn't an array\n */\n setCommentTags(id, tags) {\n if (!Array.isArray(tags)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n comment.tags = normalizeTags(tags);\n return this._commitUpdate(comment);\n }\n\n /**\n * @returns {import('./index.d.ts').SerializedComment[]}\n */\n serializeComments() {\n return this.comments.map((comment) => this._serializeComment(comment));\n }\n\n /**\n * Removes a comment everywhere: page marker, memory and (when the\n * localStorage mode is on) persisted storage.\n * @param {number} id\n * @returns {boolean} false when the id is unknown\n */\n deleteComment(id) {\n if (!this.comments.some((comment) => comment.id === id)) return false;\n this._removeComment(id);\n if (this.options.persistence === \"localStorage\") {\n // The merge preserves other-page entries missing from memory, which\n // would resurrect a deleted inactive comment \u2014 drop the id explicitly.\n writeStoredComments(\n mergeForStorage(\n readStoredComments().filter((comment) => comment.id !== id),\n this.serializeComments(),\n location.pathname\n )\n );\n }\n this.options.onCommentDeleted?.(id);\n return true;\n }\n\n _removeComment(id) {\n this.cleanupResizeObserver(id);\n if (this.mutationObservers.has(id)) {\n try {\n this.mutationObservers.get(id).disconnect();\n } catch {}\n this.mutationObservers.delete(id);\n }\n this.shadowRoot.querySelector(`[data-comment-id=\"${id}\"]`)?.remove();\n this.comments = this.comments.filter((comment) => comment.id !== id);\n }\n\n /**\n * Restores serialized comments: each anchor is resolved back to a live\n * element (circle re-rendered) or the comment is kept as an orphan \u2014\n * present in the list/inbox but never positioned over the wrong element.\n * Loading the same id again replaces the previous copy (idempotent).\n * @param {import('./index.d.ts').SerializedComment[]} data\n * @returns {{ anchored: number, orphaned: number, inactive: number }}\n */\n loadComments(data) {\n let anchored = 0;\n let orphaned = 0;\n let inactive = 0;\n if (!Array.isArray(data)) return { anchored, orphaned, inactive };\n\n for (const item of data) {\n if (!item || item.id == null || typeof item.text !== \"string\") {\n console.warn(\"HellDots: skipping malformed serialized comment\", item);\n continue;\n }\n this._removeComment(item.id);\n\n const comment = {\n id: item.id,\n text: item.text,\n anchor: item.anchor || null,\n anchorState: \"orphaned\",\n target: null,\n hidden: false,\n page: item.page || location.pathname,\n container: null,\n relativeX: 0,\n relativeY: 0,\n replies: Array.isArray(item.replies) ? [...item.replies] : [],\n author: item.author || this.strings.anonymous,\n createdAt: item.createdAt || new Date().toISOString(),\n screenshots: Array.isArray(item.screenshots)\n ? [...item.screenshots]\n : [],\n // \"closed\" existed briefly and was folded into \"resolved\".\n status:\n /** @type {string} */ (item.status) === \"closed\"\n ? \"resolved\"\n : STATUSES.includes(item.status)\n ? item.status\n : \"open\",\n // Records persisted before RF1-RF5 have none of these \u2014 every\n // reader downstream may assume they exist after this point.\n type: COMMENT_TYPES.includes(item.type) ? item.type : null,\n priority: PRIORITIES.includes(item.priority) ? item.priority : null,\n tags: Array.isArray(item.tags) ? [...item.tags] : [],\n resolvedAt: item.resolvedAt || null,\n context: item.context || null,\n contextScreenshot: item.contextScreenshot || null,\n };\n\n // Comments from other pages aren't broken \u2014 their elements just\n // don't exist here. They stay listed (inbox \"all\" filter) without a\n // marker and without an onAnchorLost false alarm.\n if (item.page && item.page !== location.pathname) {\n comment.anchorState = \"inactive\";\n this.comments.push(comment);\n inactive++;\n continue;\n }\n\n const resolved = item.anchor ? resolveAnchor(item.anchor) : null;\n if (resolved) {\n comment.container = resolved.element;\n comment.relativeX = item.anchor.relativeX;\n comment.relativeY = item.anchor.relativeY;\n comment.anchorState = \"anchored\";\n this.comments.push(comment);\n this.renderCommentCircle(comment);\n anchored++;\n } else {\n this.comments.push(comment);\n orphaned++;\n this.options.onAnchorLost?.(this._serializeComment(comment));\n }\n }\n\n return { anchored, orphaned, inactive };\n }\n\n centerPopover(el) {\n const elRect = el.getBoundingClientRect();\n const x = Math.max(10, (window.innerWidth - (elRect.width || 400)) / 2);\n const y = Math.max(10, (window.innerHeight - elRect.height) / 2);\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n }\n\n positionPopoverAtCircle(el, circle) {\n const circleRect = circle.getBoundingClientRect();\n const centerX = circleRect.left + circleRect.width / 2;\n const centerY = circleRect.top + circleRect.height / 2;\n const circleBaseSize = 28;\n const offset = circleBaseSize / 2 + 10;\n\n let x = centerX + offset;\n let y = centerY - circleBaseSize / 2;\n\n if (x + 400 > window.innerWidth) {\n x = centerX - offset - 400;\n }\n x = Math.max(10, x);\n\n const elRect = el.getBoundingClientRect();\n if (y + elRect.height > window.innerHeight) {\n y = window.innerHeight - elRect.height - 10;\n }\n y = Math.max(10, y);\n\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n }\n\n createPreviewCircle(x, y) {\n this.removePreviewCircle();\n\n const circle = document.createElement(\"div\");\n circle.className = `${CLASSES.CIRCLE} ${CLASSES.PREVIEW_CIRCLE}`;\n circle.style.position = \"absolute\";\n const circleRadius = 14;\n circle.style.left = `${x + circleRadius}px`;\n circle.style.top = `${y + circleRadius}px`;\n circle.style.transform = \"translate(-50%, -50%)\";\n circle.style.pointerEvents = \"none\";\n\n this.overlay.appendChild(circle);\n this.previewCircle = circle;\n }\n\n removePreviewCircle() {\n this.previewCircle?.remove();\n this.previewCircle = null;\n }\n\n cleanupResizeObserver(commentId) {\n if (this.resizeObservers && this.resizeObservers.has(commentId)) {\n const { circle, observer } = this.resizeObservers.get(commentId);\n if (observer) {\n observer.disconnect();\n }\n if (circle && circle.parentNode) {\n circle.parentNode.removeChild(circle);\n }\n this.resizeObservers.delete(commentId);\n }\n }\n\n /**\n * Validates and recalculates comment position based on container dimensions\n * @param {Object} comment - The comment object with position data\n * @param {HTMLElement} circle - The comment circle element\n * @returns {Object} - Validated position data\n */\n validateAndCalculatePosition(comment, circle) {\n if (!comment.container || !circle) return null;\n\n const containerRect = comment.container.getBoundingClientRect();\n const containerWidth = containerRect.width;\n const containerHeight = containerRect.height;\n\n // Zero size isn't an anomaly: it's what display:none (e.g. responsive\n // media queries) looks like. The caller hides the marker until the\n // element gets its size back.\n if (containerWidth <= 0 || containerHeight <= 0) {\n return null;\n }\n\n // Use simple relative positioning for consistent results\n const absoluteX = comment.relativeX * containerWidth;\n const absoluteY = comment.relativeY * containerHeight;\n\n const circleSize = 28;\n const validatedX = Math.max(\n 0,\n Math.min(absoluteX, containerWidth - circleSize)\n );\n const validatedY = Math.max(\n 0,\n Math.min(absoluteY, containerHeight - circleSize)\n );\n\n // Recalculate relative position for future calculations\n const validatedRelativeX = validatedX / containerWidth;\n const validatedRelativeY = validatedY / containerHeight;\n\n return {\n absoluteX: validatedX,\n absoluteY: validatedY,\n relativeX: validatedRelativeX,\n relativeY: validatedRelativeY,\n containerWidth,\n containerHeight,\n containerLeft: containerRect.left,\n containerTop: containerRect.top,\n };\n }\n\n /**\n * Updates comment circle position based on validated calculations\n * @param {Object} comment - The comment object\n * @param {HTMLElement} circle - The comment circle element\n */\n /**\n * The exact element the user clicked on can vanish (responsive\n * display:none) while its coarse anchor container stays visible. When we\n * have a live target \u2014 or can re-derive one from the serialized\n * targetSelector \u2014 the marker follows ITS visibility too.\n */\n _isAnchorTargetVisible(comment) {\n let target = comment.target;\n if ((!target || !target.isConnected) && comment.anchor?.targetSelector) {\n try {\n target = document.querySelector(comment.anchor.targetSelector);\n } catch {\n target = null;\n }\n comment.target = target || null;\n }\n if (!target || !target.isConnected) return true; // no signal \u2014 assume visible\n const rect = target.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n }\n\n /**\n * A marker floats above the whole page (own shadow host, high z-index),\n * so a host-page modal overlay can never cover it with CSS alone. Hit-test\n * the marker's point instead: if the topmost page element there is\n * unrelated to the comment's anchor (neither ancestor nor descendant),\n * something like a modal backdrop is covering it and the marker should\n * hide with it.\n */\n _isMarkerOccluded(comment, x, y) {\n if (typeof document.elementsFromPoint !== \"function\") return false;\n // Off-viewport points can't be hit-tested; the marker isn't visible\n // there anyway, so keep the current (visible) state.\n if (x < 0 || y < 0 || x >= window.innerWidth || y >= window.innerHeight) {\n return false;\n }\n const stack = document.elementsFromPoint(x, y);\n // Our own shadow host shows up first (the marker itself, toolbar, \u2026).\n const top = stack.find(\n (el) => el.tagName.toLowerCase() !== TAG_NAME.toLowerCase()\n );\n if (!top) return false;\n\n const target = comment.target?.isConnected ? comment.target : null;\n // The precise element the comment was left on (or its subtree /\n // ancestors) is what should be under the marker \u2014 never an occluder.\n if (target && (target.contains(top) || top.contains(target))) {\n return false;\n }\n\n const container = comment.container;\n if (!container?.isConnected) return false;\n // Something entirely unrelated to the anchor sits on top of it.\n if (!container.contains(top) && !top.contains(container)) return true;\n\n // `top` lives inside the anchor container \u2014 usually normal content of\n // the anchored subtree. But broad containers (body, page wrappers) also\n // contain the page's modals, so walk the chain up to the container: if\n // it crosses a modal-looking layer the anchor doesn't belong to, the\n // marker is covered after all.\n if (container.contains(top) && top !== container) {\n for (let el = top; el && el !== container; el = el.parentElement) {\n if (this._looksLikeModalLayer(el, target)) return true;\n }\n }\n return false;\n }\n\n /**\n * Heuristic for \"this element is a modal/backdrop layer\": explicit dialog\n * semantics, or a hit-testable fixed element covering most of the\n * viewport. Elements that contain the comment's own target are the layer\n * the comment lives in, never an occluder.\n */\n _looksLikeModalLayer(el, target) {\n if (target && el.contains(target)) return false;\n if (el.matches?.('dialog, [aria-modal=\"true\"], [role=\"dialog\"]')) {\n return true;\n }\n if (getComputedStyle(el).position !== \"fixed\") return false;\n const rect = el.getBoundingClientRect();\n return (\n rect.width >= window.innerWidth * 0.5 &&\n rect.height >= window.innerHeight * 0.5\n );\n }\n\n // A marker that just went away must not leave its hover tooltip or its\n // open thread popover floating on the page (e.g. above the modal that\n // now covers the marker).\n _dismissMarkerUi(comment) {\n this.shadowRoot\n .querySelector(`.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`)\n ?.remove();\n if (this.activeThreadPopover?.dataset.for === String(comment.id)) {\n this.closeThreadPopover();\n }\n }\n\n updateCommentPosition(comment, circle) {\n // Resolved comments have no on-page marker (RF09). This is not the\n // \"hidden\" state \u2014 the anchor is fine, the issue is just done.\n if (comment.status === \"resolved\") {\n if (circle) circle.style.display = \"none\";\n return;\n }\n\n let positionData = this.validateAndCalculatePosition(comment, circle);\n if (positionData && !this._isAnchorTargetVisible(comment)) {\n positionData = null;\n }\n const wasHidden = comment.hidden === true;\n\n if (!positionData) {\n // Anchor element currently invisible (zero-size container): hide the\n // marker; it comes back automatically when the observers fire again.\n if (circle && comment.container) {\n comment.hidden = true;\n circle.style.display = \"none\";\n this._dismissMarkerUi(comment);\n if (!wasHidden && this.inboxView?.isOpen()) this.inboxView.refresh();\n }\n return;\n }\n\n // Offset so the circle's top-left tip (sharp corner) aligns with the stored position\n const circleRadius = 14;\n const viewportX =\n positionData.containerLeft + positionData.absoluteX + circleRadius;\n const viewportY =\n positionData.containerTop + positionData.absoluteY + circleRadius;\n\n // A host-page modal (or any unrelated overlay) covering the anchor also\n // hides the marker \u2014 it must not float above the modal's backdrop.\n if (this._isMarkerOccluded(comment, viewportX, viewportY)) {\n comment.hidden = true;\n circle.style.display = \"none\";\n this._dismissMarkerUi(comment);\n if (!wasHidden && this.inboxView?.isOpen()) this.inboxView.refresh();\n return;\n }\n\n comment.hidden = false;\n circle.style.display = \"\";\n if (wasHidden && this.inboxView?.isOpen()) this.inboxView.refresh();\n\n circle.style.left = `${viewportX}px`;\n circle.style.top = `${viewportY}px`;\n circle.style.transform = \"translate(-50%, -50%)\";\n circle.style.position = \"absolute\";\n\n comment.relativeX = positionData.relativeX;\n comment.relativeY = positionData.relativeY;\n }\n\n /**\n * Sets up resize observers and window resize handlers\n */\n setupResizeHandlers() {\n // Throttled updater\n this.scheduleUpdatePositions = () => {\n if (this._pendingRaf) return;\n this._pendingRaf = requestAnimationFrame(() => {\n this._pendingRaf = null;\n if (!this.positionValidationEnabled) return;\n this.comments.forEach((comment) => {\n /** @type {HTMLElement} */\n const circle = /** @type {any} */ (\n this.shadowRoot.querySelector(`[data-comment-id=\"${comment.id}\"]`)\n );\n if (circle) this.updateCommentPosition(comment, circle);\n });\n });\n };\n\n // Window resize handler for viewport changes\n this.windowResizeHandler = () => {\n this.scheduleUpdatePositions();\n };\n window.addEventListener(\"resize\", this.windowResizeHandler, {\n passive: true,\n });\n\n // Capture scroll on any scrolling ancestor\n this.scrollHandler = () => {\n this.scheduleUpdatePositions();\n };\n window.addEventListener(\"scroll\", this.scrollHandler, {\n capture: true,\n passive: true,\n });\n\n // Update after resources load (images, fonts)\n this.loadHandler = () => {\n this.scheduleUpdatePositions();\n };\n window.addEventListener(\"load\", this.loadHandler);\n\n // Modals open/close outside any comment's container (backdrops are\n // usually appended to <body> or toggled via style/class), so the\n // per-comment observers never see them. One page-wide observer keeps\n // the occlusion check honest; shadow-root internals don't bubble into\n // it, so our own marker updates can't retrigger it.\n if (window.MutationObserver) {\n this._globalMutationObserver = new MutationObserver(() => {\n this.scheduleUpdatePositions();\n });\n this._globalMutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"style\", \"class\", \"hidden\", \"open\"],\n });\n }\n }\n\n /**\n * Debug function to log position information\n * @param {Object} comment - The comment object\n * @param {HTMLElement} circle - The comment circle element\n */\n debugPosition(comment, circle) {\n if (!comment || !circle) return;\n\n const containerRect = comment.container.getBoundingClientRect();\n const circleRect = circle.getBoundingClientRect();\n\n // Calculate expected position from relative coordinates\n const expectedX = comment.relativeX * containerRect.width;\n const expectedY = comment.relativeY * containerRect.height;\n\n console.log(\"Position Debug:\", {\n commentId: comment.id,\n relativePosition: { x: comment.relativeX, y: comment.relativeY },\n containerRect: {\n left: containerRect.left,\n top: containerRect.top,\n width: containerRect.width,\n height: containerRect.height,\n },\n circlePosition: {\n left: circleRect.left,\n top: circleRect.top,\n centerX: circleRect.left + circleRect.width / 2,\n centerY: circleRect.top + circleRect.height / 2,\n },\n expectedPosition: {\n x: expectedX,\n y: expectedY,\n },\n offset: {\n x:\n circleRect.left +\n circleRect.width / 2 -\n (containerRect.left + expectedX),\n y:\n circleRect.top +\n circleRect.height / 2 -\n (containerRect.top + expectedY),\n },\n });\n }\n\n /**\n * Creates a ResizeObserver for a specific comment container\n * @param {Object} comment - The comment object\n * @param {HTMLElement} circle - The comment circle element\n */\n createResizeObserver(comment, circle) {\n if (!window.ResizeObserver) {\n console.warn(\n \"ResizeObserver not supported, position validation will be limited\"\n );\n return;\n }\n\n const observer = new ResizeObserver((entries) => {\n if (!this.positionValidationEnabled) return;\n\n for (const entry of entries) {\n // Only update if the container size actually changed\n if (entry.target === comment.container) {\n this.updateCommentPosition(comment, circle);\n }\n }\n });\n\n // Start observing the container\n observer.observe(comment.container);\n\n // Store the observer for cleanup\n this.resizeObservers.set(comment.id, {\n circle,\n observer,\n container: comment.container,\n });\n }\n\n /**\n * Creates a MutationObserver to react to layout-affecting DOM changes\n * @param {Object} comment\n */\n createMutationObserver(comment) {\n if (!window.MutationObserver) return;\n\n // Disconnect existing for this comment if any\n if (this.mutationObservers.has(comment.id)) {\n try {\n this.mutationObservers.get(comment.id).disconnect();\n } catch {}\n this.mutationObservers.delete(comment.id);\n }\n\n const observer = new MutationObserver(() => {\n this.scheduleUpdatePositions();\n });\n\n observer.observe(comment.container, {\n attributes: true,\n attributeFilter: undefined,\n childList: true,\n subtree: true,\n });\n\n this.mutationObservers.set(comment.id, observer);\n }\n\n /**\n * Cleanup method to remove all event listeners and observers\n */\n cleanup() {\n this.closeThreadPopover();\n this.closeInbox();\n this.closeLightbox();\n this.removePreviewCircle();\n this._selectionRect?.remove();\n this._pendingScreenshots = [];\n\n if (this._handleDocumentClickBound) {\n document.removeEventListener(\"mousedown\", this._handleDocumentClickBound);\n }\n\n if (this.windowResizeHandler) {\n window.removeEventListener(\"resize\", this.windowResizeHandler);\n }\n\n if (this.scrollHandler) {\n window.removeEventListener(\"scroll\", this.scrollHandler, {\n capture: true,\n });\n }\n\n if (this.loadHandler) {\n window.removeEventListener(\"load\", this.loadHandler);\n }\n\n if (this._globalMutationObserver) {\n this._globalMutationObserver.disconnect();\n this._globalMutationObserver = null;\n }\n\n // Cleanup all resize observers\n if (this.resizeObservers) {\n this.resizeObservers.forEach(({ observer }) => {\n if (observer) {\n observer.disconnect();\n }\n });\n this.resizeObservers.clear();\n }\n\n // Cleanup mutation observers\n if (this.mutationObservers) {\n this.mutationObservers.forEach((observer) => {\n try {\n observer.disconnect();\n } catch {}\n });\n this.mutationObservers.clear();\n }\n\n // Remove keyboard shortcut handler\n if (this.keydownHandler) {\n document.removeEventListener(\"keydown\", this.keydownHandler);\n }\n\n // Remove DOM elements\n if (this.toolbar && this.toolbar.parentNode) {\n this.toolbar.parentNode.removeChild(this.toolbar);\n }\n if (this.commentBox && this.commentBox.parentNode) {\n this.commentBox.parentNode.removeChild(this.commentBox);\n }\n if (this.overlay && this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n\n document.body.classList.remove(CLASSES.COMMENT_CURSOR);\n document.getElementById(IDS.GLOBAL_STYLES)?.remove();\n\n // Remove all comment circles\n this.comments.forEach((comment) => {\n const circle = this.shadowRoot.querySelector(\n `[data-comment-id=\"${comment.id}\"]`\n );\n if (circle && circle.parentNode) {\n circle.parentNode.removeChild(circle);\n }\n });\n }\n\n injectStyles() {\n const existingStyle = this.shadowRoot.getElementById(IDS.STYLES);\n if (existingStyle) {\n existingStyle.remove();\n }\n\n const style = document.createElement(\"style\");\n style.id = IDS.STYLES;\n style.textContent = getStyles();\n this.shadowRoot.appendChild(style);\n\n // A few rules (e.g. the comment-mode cursor on document.body) target\n // the host page itself, which a shadow root's stylesheet can't reach \u2014\n // those live in a separate <style> in document.head instead.\n const existingGlobalStyle = document.getElementById(IDS.GLOBAL_STYLES);\n if (existingGlobalStyle) {\n existingGlobalStyle.remove();\n }\n\n const globalStyle = document.createElement(\"style\");\n globalStyle.id = IDS.GLOBAL_STYLES;\n globalStyle.textContent = getGlobalStyles();\n document.head.appendChild(globalStyle);\n }\n}\n\nexport default CommentOverlay;\n", "import CommentOverlay from \"./overlay.js\";\n\n/**\n * Creates a CommentOverlay.\n *\n * Safe to call before the document is ready: `CommentOverlay`'s constructor\n * already defers its own DOM work to `DOMContentLoaded`, so callers always\n * get a real instance back and never have to branch on `readyState`. An\n * earlier version duplicated that same check here and, while the document\n * was loading, both registered a listener AND returned the uninvoked\n * initializer \u2014 so a caller who invoked it (reasonably, since the type said\n * it might be a function) ended up with two overlays, and the one the\n * listener built had no handle to call `cleanup()` on.\n *\n * @overload\n * @param {import('./index.d.ts').CommentOverlayOptions & { autoInit?: true }} [options]\n * @returns {CommentOverlay}\n */\n/**\n * @overload\n * @param {import('./index.d.ts').CommentOverlayOptions & { autoInit: false }} options\n * @returns {() => CommentOverlay}\n */\n/**\n * @param {import('./index.d.ts').CommentOverlayOptions} [options]\n * @returns {CommentOverlay | (() => CommentOverlay)}\n */\nexport function createCommentOverlay(options = {}) {\n const { autoInit = true, ...overlayOptions } = options;\n const initialize = () => new CommentOverlay(overlayOptions);\n return autoInit ? initialize() : initialize;\n}\n\n// Export the class for advanced usage\nexport { CommentOverlay };\n\n// Export a default instance creator for simple usage\nexport default createCommentOverlay;\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["domToCanvas", "TAG_NAME", "ensureDefined", "getShadowRoot", "host", "AUTO_SCALE", "AUTO_QUALITY", "isUnpainted", "color", "effectiveBackgroundColor", "htmlBg", "bodyBg", "renderPage", "scale", "domToCanvas", "withHiddenOverlay", "fn", "host", "TAG_NAME", "previousDisplay", "cropRegion", "canvas", "left", "top", "width", "height", "out", "ctx", "cropViewport", "sourceScale", "outputScale", "quality", "BROWSERS", "OPERATING_SYSTEMS", "UNKNOWN", "isGreaseBrand", "brand", "matchFirst", "table", "ua", "name", "re", "match", "captureContext", "win", "nav", "uaData", "browser", "b", "os", "CLASSES", "IDS", "STATUSES", "STATUS_COLORS", "COMMENT_TYPES", "TYPE_COLORS", "PRIORITIES", "PRIORITY_COLORS", "SELECTORS", "Z_INDEX", "CURSOR_SVG", "getStyles", "IDS", "Z_INDEX", "CLASSES", "getGlobalStyles", "CURSOR_SVG", "en_default", "es_default", "LOCALES", "en_default", "es_default", "DEFAULT_LOCALE", "detectLocale", "lang", "getStrings", "localeCode", "formatTemplate", "template", "n", "MINUTE_MS", "formatDuration", "ms", "strings", "totalMinutes", "totalHours", "minutes", "hours", "totalDays", "days", "GENERATED_CLASS_PREFIX_RE", "FRAMEWORK_DATA_ATTR_RE", "STABLE_ATTR_NAMES", "SELECTOR_ATTR_NAMES", "escapeCss", "value", "escapeAttrValue", "isUnique", "selector", "doc", "normalizeText", "text", "isStableClass", "cls", "part", "stableClassesOf", "element", "stableAttributes", "attrs", "name", "siblingPosition", "parent", "sameTag", "child", "idSelector", "attributeSelector", "tag", "classPathSelector", "segments", "current", "depth", "classes", "structuralSelector", "rooted", "index", "generateSelector", "generateElementSelector", "createAnchor", "relativeX", "relativeY", "count", "textSimilarity", "a", "b", "tokensA", "tokensB", "common", "token", "attributeSimilarity", "names", "matched", "positionSimilarity", "fingerprint", "delta", "span", "scoreElement", "hasText", "hasAttrs", "textWeight", "attrWeight", "posWeight", "bestMatch", "candidates", "best", "confidence", "resolveAnchor", "anchor", "STORAGE_KEY", "PENDING_DETAIL_KEY", "readStoredComments", "raw", "parsed", "err", "tryWriteStoredComments", "comments", "writeStoredComments", "shedOrder", "comment", "index", "a", "b", "timeA", "timeB", "working", "shed", "mergeForStorage", "stored", "current", "currentPage", "currentIds", "c", "COPY_ICON_SVG", "CHECK_ICON_SVG", "DOTS_ICON_SVG", "copyToClipboard", "text", "textarea", "statusLabelOf", "status", "strings", "typeLabelOf", "type", "priorityLabelOf", "priority", "createPicker", "action", "options", "value", "colorOf", "labelOf", "tooltipLabel", "onSelect", "showLabel", "wrapper", "btn", "CLASSES", "dot", "labelEl", "menu", "current", "syncUi", "label", "item", "raw", "option", "itemDot", "e", "createCommentActions", "comment", "onCopy", "onSetStatus", "onSetType", "onSetPriority", "onDelete", "actions", "copyBtn", "STATUSES", "STATUS_COLORS", "COMMENT_TYPES", "TYPE_COLORS", "PRIORITIES", "PRIORITY_COLORS", "menuWrapper", "menuBtn", "deleteItem", "formatRelativeTime", "date", "strings", "diff", "minutes", "hours", "days", "formatTemplate", "formatFullDate", "locale", "createMetaElement", "author", "createdAt", "meta", "CLASSES", "authorEl", "timeEl", "getShortcutText", "options", "isMac", "modifierMap", "modifier", "key", "ATTACH_ICON_SVG", "SEND_ICON_SVG", "COMMENT_BUBBLE_SVG", "MENU_ICON_SVG", "createInputArea", "areaClassName", "inputTag", "inputClassName", "inputId", "inputPlaceholder", "submitBtnId", "fileInputId", "container", "inputEl", "screenshotsContainer", "actionsBar", "attachBtn", "fileInput", "submitBtn", "createActionWithTooltip", "btnClass", "btnSvg", "tooltipContent", "label", "wrapper", "tooltip", "el", "btn", "createToolbar", "en_default", "toolbar", "IDS", "actions", "commentLabel", "shortcutKey", "commentWrapper", "inboxLabel", "inboxWrapper", "createClassifyRow", "type", "priority", "tags", "chips", "input", "renderChips", "tag", "index", "chip", "remove", "e", "commitPendingTag", "mount", "createPicker", "COMMENT_TYPES", "value", "TYPE_COLORS", "typeLabelOf", "PRIORITIES", "PRIORITY_COLORS", "priorityLabelOf", "createCommentBox", "commentBox", "inputArea", "classify", "createCommentCircle", "comment", "circle", "createScreenshotsDisplay", "screenshots", "src", "item", "img", "createTooltip", "header", "closeButton", "body", "tooltipScreenshots", "createReplyElement", "reply", "replyEl", "text", "replyScreenshots", "createThreadPopover", "popover", "replies", "popoverScreenshots", "openingTagOf", "element", "attrs", "name", "value", "openingTagFromFingerprint", "fingerprint", "domPathOf", "segments", "current", "tag", "id", "classes", "cls", "buildAgentContext", "comment", "viewportWidth", "viewportHeight", "strings", "anchor", "live", "state", "path", "capturedViewport", "reportedViewportWidth", "reportedViewportHeight", "lines", "context", "elapsed", "formatDuration", "replies", "reply", "CARET_ICON_SVG", "CHEVRON_LEFT_SVG", "ARROW_UP_SVG", "ARROW_DOWN_SVG", "InboxView", "shadowRoot", "strings", "locale", "currentPage", "getComments", "callbacks", "CLASSES", "comment", "circle", "comments", "detail", "id", "c", "btn", "header", "list", "empty", "value", "parts", "typeLabelOf", "priorityLabelOf", "wrapper", "label", "menu", "addSection", "title", "section", "addOption", "text", "checked", "dataAttr", "onSelect", "option", "e", "COMMENT_TYPES", "PRIORITIES", "open", "interactive", "card", "createMetaElement", "shots", "createScreenshotsDisplay", "img", "badges", "tag", "activate", "replyLink", "row", "addBadge", "modifier", "color", "badge", "TYPE_COLORS", "PRIORITY_COLORS", "elapsed", "formatDuration", "formatTemplate", "context", "contextScreenshot", "block", "caption", "addRow", "key", "val", "size", "dimensions", "named", "entry", "createCommentActions", "copyToClipboard", "buildAgentContext", "status", "type", "priority", "index", "backBtn", "nav", "navBtn", "svg", "targetIndex", "target", "replies", "reply", "replyEl", "createReplyElement", "container", "inputEl", "screenshotsContainer", "attachBtn", "fileInput", "submitBtn", "createInputArea", "pendingScreenshots", "updatePreview", "dataUrl", "i", "item", "removeBtn", "file", "reader", "ev", "submit", "normalizeTags", "tags", "seen", "tag", "clean", "CommentOverlay", "options", "detectLocale", "getStrings", "getShadowRoot", "createToolbar", "createCommentBox", "CLASSES", "IDS", "readStoredComments", "url", "id", "PENDING_DETAIL_KEY", "comment", "c", "writeStoredComments", "mergeForStorage", "file", "reader", "ev", "isMacOptionC", "isWindowsAltC", "isCustomShortcut", "target", "dx", "dy", "left", "top", "width", "height", "full", "withHiddenOverlay", "renderPage", "dataUrl", "cropRegion", "cropViewport", "err", "clientX", "clientY", "AUTO_SCALE", "prevPointerEvents", "underlying", "container", "SELECTORS", "containerRect", "relativeX", "relativeY", "anchor", "createAnchor", "generateElementSelector", "i", "item", "img", "removeBtn", "e", "x", "y", "boxWidth", "circleRadius", "offset", "windowWidth", "windowHeight", "centerX", "centerY", "adjustedX", "adjustedY", "boxRect", "captureContext", "circle", "createCommentCircle", "tooltip", "createTooltip", "InboxView", "text", "screenshots", "status", "type", "priority", "src", "existingTooltip", "popover", "createThreadPopover", "headerEl", "actionsEl", "createCommentActions", "copyToClipboard", "buildAgentContext", "mainScreenshotsContainer", "child", "input", "submitBtn", "threadAttachBtn", "threadFileInput", "threadScreenshotsContainer", "pendingReplyScreenshots", "updateReplyScreenshotsPreview", "submitReply", "reply", "repliesContainer", "replyEl", "createReplyElement", "imageSrc", "lightbox", "closeBtn", "author", "timestamp", "STATUSES", "COMMENT_TYPES", "PRIORITIES", "data", "anchored", "orphaned", "inactive", "resolved", "resolveAnchor", "el", "elRect", "circleRect", "circleBaseSize", "commentId", "observer", "containerWidth", "containerHeight", "absoluteX", "absoluteY", "circleSize", "validatedX", "validatedY", "validatedRelativeX", "validatedRelativeY", "rect", "TAG_NAME", "positionData", "wasHidden", "viewportX", "viewportY", "expectedX", "expectedY", "entries", "entry", "existingStyle", "style", "getStyles", "existingGlobalStyle", "globalStyle", "getGlobalStyles", "overlay_default", "createCommentOverlay", "options", "autoInit", "overlayOptions", "initialize", "overlay_default", "index_default", "createCommentOverlay"]
|
|
3
|
+
"sources": ["../src/capture.js", "../src/root-element.js", "../src/metadata.js", "../src/constants.js", "../src/styles.js", "../src/locales/en.js", "../src/locales/es.js", "../src/i18n.js", "../src/anchor.js", "../src/storage.js", "../node_modules/nanoid/url-alphabet/index.js", "../node_modules/nanoid/index.browser.js", "../src/id.js", "../src/link.js", "../src/menus.js", "../src/confirm-dialog.js", "../src/comment-actions.js", "../src/inline-editor.js", "../src/components.js", "../src/agent-context.js", "../src/context-block.js", "../src/inbox.js", "../src/overlay.js", "../src/index.js"],
|
|
4
|
+
"sourcesContent": ["// Screenshot primitives. The page render is the expensive part, so it's\n// split out from the cropping: a drag capture and the automatic context\n// capture of the same comment share ONE render instead of paying for two.\n//\n// WE own the crop in page coordinates \u2014 html2canvas used to own it and\n// silently shifted it by the window scroll (double-counting: the hero\n// showed up in captures taken further down the page). Owning the crop\n// makes that whole bug class impossible.\n\nimport { domToCanvas } from \"modern-screenshot\";\nimport { TAG_NAME } from \"./root-element.js\";\n\n/** Automatic captures render and encode small \u2014 they live in localStorage. */\nexport const AUTO_SCALE = 0.5;\nexport const AUTO_QUALITY = 0.7;\n\nconst isUnpainted = (color) =>\n !color || color === \"transparent\" || color === \"rgba(0, 0, 0, 0)\";\n\n// What the user visually perceives as the page background: the html/body\n// CSS color when one is painted, else white \u2014 browsers paint their own\n// white canvas under a transparent document, but that canvas is not part\n// of the DOM, so a DOM-based render would come out as a transparent PNG\n// (invisible against the dark inbox UI).\nconst effectiveBackgroundColor = () => {\n const htmlBg = getComputedStyle(document.documentElement).backgroundColor;\n if (!isUnpainted(htmlBg)) return htmlBg;\n const bodyBg = getComputedStyle(document.body).backgroundColor;\n if (!isUnpainted(bodyBg)) return bodyBg;\n return \"#ffffff\";\n};\n\n/**\n * Renders the whole page to a canvas. This is the expensive call \u2014 callers\n * that need more than one image should render once and crop repeatedly.\n * @param {{ scale?: number }} [options] scale 1 keeps the canvas in CSS\n * pixels so crop rects map 1:1 to page coordinates.\n * @returns {Promise<any>}\n */\nexport async function renderPage({ scale = 1 } = {}) {\n return domToCanvas(document.body, {\n scale,\n backgroundColor: effectiveBackgroundColor(),\n });\n}\n\n/**\n * Runs `fn` with the HellDots UI hidden, so the widget never renders into\n * its own screenshot. Restores the host even if `fn` throws \u2014 a failed\n * render must not leave the widget invisible.\n * @template T\n * @param {() => Promise<T>} fn\n * @returns {Promise<T>}\n */\nexport async function withHiddenOverlay(fn) {\n const host = /** @type {HTMLElement} */ (document.querySelector(TAG_NAME));\n const previousDisplay = host?.style.display;\n if (host) host.style.display = \"none\";\n try {\n return await fn();\n } finally {\n if (host) host.style.display = previousDisplay || \"\";\n }\n}\n\n/**\n * Crops a viewport-relative region out of a scale-1 page render.\n * @param {any} canvas full-page render from renderPage({ scale: 1 })\n * @param {{ left: number, top: number, width: number, height: number }} region\n * Viewport (client) coordinates of the drag selection.\n * @returns {string | null} PNG data-URL, or null with no 2d context.\n */\nexport function cropRegion(canvas, { left, top, width, height }) {\n const out = document.createElement(\"canvas\");\n out.width = width;\n out.height = height;\n const ctx = out.getContext(\"2d\");\n if (!ctx) return null;\n\n ctx.drawImage(\n canvas,\n left + window.scrollX,\n top + window.scrollY,\n width,\n height,\n 0,\n 0,\n width,\n height\n );\n return out.toDataURL(\"image/png\");\n}\n\n/**\n * Crops the current viewport out of a page render and encodes it small.\n * @param {any} canvas full-page render\n * @param {{ sourceScale?: number, outputScale?: number, quality?: number }} [options]\n * `sourceScale` is the scale `canvas` was rendered at \u2014 the source rect is\n * mapped through it. `outputScale` is the final size in CSS pixels.\n * @returns {string | null} JPEG data-URL, or null with no 2d context.\n */\nexport function cropViewport(\n canvas,\n { sourceScale = 1, outputScale = AUTO_SCALE, quality = AUTO_QUALITY } = {}\n) {\n const out = document.createElement(\"canvas\");\n out.width = Math.round(window.innerWidth * outputScale);\n out.height = Math.round(window.innerHeight * outputScale);\n const ctx = out.getContext(\"2d\");\n if (!ctx) return null;\n\n ctx.drawImage(\n canvas,\n window.scrollX * sourceScale,\n window.scrollY * sourceScale,\n window.innerWidth * sourceScale,\n window.innerHeight * sourceScale,\n 0,\n 0,\n out.width,\n out.height\n );\n return out.toDataURL(\"image/jpeg\", quality);\n}\n\n/**\n * Captures a viewport-relative region of the current page.\n * Kept for API compatibility \u2014 renders and crops in one call.\n * @param {{ left: number, top: number, width: number, height: number }} region\n * @returns {Promise<string | null>} PNG data-URL. Render failures reject.\n */\nexport async function captureRegion(region) {\n return cropRegion(await renderPage({ scale: 1 }), region);\n}\n", "const TAG_NAME = \"helldots-root\";\n\n// Defined lazily rather than at module scope: `extends HTMLElement` is\n// evaluated when the class expression runs, so a top-level declaration makes\n// a bare `import \"helldots\"` throw on any server renderer (Next.js, Remix,\n// Astro) long before the app calls anything. Deferring it keeps the module\n// import-safe everywhere and only touches the DOM when we actually mount.\nconst ensureDefined = () => {\n if (customElements.get(TAG_NAME)) return;\n\n customElements.define(\n TAG_NAME,\n class HelldotsRoot extends HTMLElement {\n constructor() {\n super();\n this.attachShadow({ mode: \"open\" });\n }\n }\n );\n};\n\n/**\n * Returns the shared shadow root used to render all HellDots UI, creating\n * the host element and mounting it on document.body on first call.\n * @returns {ShadowRoot}\n */\nexport function getShadowRoot() {\n ensureDefined();\n\n let host = document.querySelector(TAG_NAME);\n if (!host) {\n host = document.createElement(TAG_NAME);\n document.body.appendChild(host);\n }\n\n return host.shadowRoot;\n}\n\nexport { TAG_NAME };\n", "// RF2 \u2014 environment snapshot attached to every comment at creation time.\n// Kept as a pure function over an injectable `window` so the UA parsing\n// paths are testable without touching jsdom's real navigator.\n\n// Order is load-bearing: Edge's UA contains \"Chrome\", and Chrome's UA\n// contains \"Safari\". First match wins, so the more specific entries lead.\nconst BROWSERS = [\n { name: \"Edge\", re: /Edg\\/([\\d.]+)/ },\n { name: \"Chrome\", re: /Chrome\\/([\\d.]+)/ },\n { name: \"Firefox\", re: /Firefox\\/([\\d.]+)/ },\n { name: \"Safari\", re: /Version\\/([\\d.]+).*Safari/ },\n];\n\n// iOS before macOS: an iPhone UA also carries \"Mac OS X\".\nconst OPERATING_SYSTEMS = [\n { name: \"iOS\", re: /(?:iPhone|iPad).*OS ([\\d_]+) like Mac OS X/ },\n { name: \"Android\", re: /Android ([\\d.]+)/ },\n { name: \"Windows\", re: /Windows NT ([\\d.]+)/ },\n { name: \"macOS\", re: /Mac OS X ([\\d_.]+)/ },\n { name: \"Linux\", re: /Linux/ },\n];\n\nconst UNKNOWN = { name: \"unknown\", version: \"\" };\n\n// Chromium pads its brand list with a randomised \"GREASE\" entry to stop\n// consumers hardcoding brand positions. It is never the real browser.\nconst isGreaseBrand = (brand) => /not[\\W_]*a[\\W_]*brand/i.test(brand);\n\nconst matchFirst = (table, ua) => {\n for (const { name, re } of table) {\n const match = ua.match(re);\n if (match) {\n return { name, version: (match[1] || \"\").replace(/_/g, \".\") };\n }\n }\n return { ...UNKNOWN };\n};\n\n/**\n * Snapshots the browsing environment of the current page.\n * @param {any} [win] Injectable window \u2014 defaults to the real one.\n * @returns {import('./index.d.ts').CommentContext}\n */\nexport function captureContext(win = window) {\n const nav = win.navigator || {};\n const ua = nav.userAgent || \"\";\n const uaData = nav.userAgentData;\n\n let browser = matchFirst(BROWSERS, ua);\n const brand = uaData?.brands?.find((b) => !isGreaseBrand(b.brand));\n if (brand) {\n browser = { name: brand.brand, version: brand.version || \"\" };\n }\n\n const os = matchFirst(OPERATING_SYSTEMS, ua);\n if (uaData?.platform) os.name = uaData.platform;\n\n return {\n version: 1,\n url: win.location?.href || \"\",\n viewport: { width: win.innerWidth, height: win.innerHeight },\n screen: {\n width: win.screen?.width ?? 0,\n height: win.screen?.height ?? 0,\n },\n devicePixelRatio: win.devicePixelRatio ?? 1,\n userAgent: ua,\n browser,\n os,\n language: nav.language || \"\",\n };\n}\n", "export const CLASSES = {\n CIRCLE: \"comment-circle\",\n CIRCLE_ACTIVE: \"comment-circle--active\",\n CIRCLE_WRAPPER: \"comment-circle-wrapper\",\n TOOLTIP: \"comment-tooltip\",\n TOOLBAR_TEXT: \"toolbar-text\",\n SHORTCUT_HINT: \"shortcut-hint\",\n COMMENT_INPUT_AREA: \"comment-input-area\",\n CLOSE_TOOLTIP: \"close-tooltip\",\n ACTIVE: \"active\",\n COMMENT_CURSOR: \"comment-cursor\",\n COMMENT_OVERLAY: \"comment-overlay\",\n THREAD_POPOVER: \"comment-thread-popover\",\n THREAD_HEADER: \"thread-header\",\n THREAD_BODY: \"thread-body\",\n THREAD_REPLIES: \"thread-replies\",\n THREAD_REPLY: \"thread-reply\",\n THREAD_REPLY_ACTIONS: \"thread-reply-actions\",\n THREAD_INPUT_AREA: \"thread-input-area\",\n THREAD_INPUT: \"thread-input\",\n THREAD_SUBMIT: \"thread-submit\",\n THREAD_ACTIONS_ROW: \"thread-actions-row\",\n THREAD_SCROLL: \"thread-scroll\",\n THREAD_META: \"thread-meta\",\n THREAD_AUTHOR: \"thread-author\",\n THREAD_TIME: \"thread-time\",\n THREAD_EDITED: \"thread-edited\",\n EDITOR: \"helldots-editor\",\n EDITOR_INPUT: \"helldots-editor-input\",\n EDITOR_ACTIONS: \"helldots-editor-actions\",\n EDITOR_SAVE: \"helldots-editor-save\",\n EDITOR_CANCEL: \"helldots-editor-cancel\",\n INBOX_NOTICE: \"inbox-notice\",\n PREVIEW_CIRCLE: \"preview-circle\",\n SELECTION_RECT: \"selection-rect\",\n SCREENSHOT_IMG: \"screenshot-img\",\n SCREENSHOT_REMOVE: \"screenshot-remove\",\n SCREENSHOTS_CONTAINER: \"screenshots-container\",\n SCREENSHOT_ITEM: \"screenshot-item\",\n CONFIRM: \"helldots-confirm\",\n CONFIRM_PANEL: \"helldots-confirm-panel\",\n CONFIRM_TITLE: \"helldots-confirm-title\",\n CONFIRM_MESSAGE: \"helldots-confirm-message\",\n CONFIRM_ACTIONS: \"helldots-confirm-actions\",\n CONFIRM_CANCEL: \"helldots-confirm-cancel\",\n CONFIRM_ACCEPT: \"helldots-confirm-accept\",\n LIGHTBOX: \"helldots-lightbox\",\n LIGHTBOX_IMG: \"helldots-lightbox-img\",\n LIGHTBOX_CLOSE: \"helldots-lightbox-close\",\n COMMENT_ACTIONS_BAR: \"comment-actions-bar\",\n ATTACH_IMAGE_BTN: \"attach-image-btn\",\n TOOLBAR_ACTIONS: \"toolbar-actions\",\n TOOLBAR_ACTION_BTN: \"toolbar-action-btn\",\n TOOLBAR_ACTION_WRAPPER: \"toolbar-action-wrapper\",\n TOOLBAR_ACTION_TOOLTIP: \"toolbar-action-tooltip\",\n TOOLBAR_COMMENT_BTN: \"toolbar-comment-btn\",\n TOOLBAR_MENU_BTN: \"toolbar-menu-btn\",\n INBOX_PANEL: \"inbox-panel\",\n INBOX_HEADER: \"inbox-header\",\n INBOX_FILTER: \"inbox-filter\",\n INBOX_FILTER_MENU: \"inbox-filter-menu\",\n INBOX_FILTER_MENU_HEADER: \"inbox-filter-menu-header\",\n INBOX_FILTER_CLEAR: \"inbox-filter-clear\",\n INBOX_FILTER_GROUP: \"inbox-filter-group\",\n INBOX_FILTER_CHIPS: \"inbox-filter-chips\",\n INBOX_FILTER_CHIP: \"inbox-filter-chip\",\n INBOX_FILTER_SECTION: \"inbox-filter-section\",\n INBOX_CLOSE: \"inbox-close\",\n INBOX_LIST: \"inbox-list\",\n INBOX_CARD: \"inbox-card\",\n INBOX_CARD_HEADER: \"inbox-card-header\",\n INBOX_CARD_ACTIONS: \"inbox-card-actions\",\n INBOX_CARD_TEXT: \"inbox-card-text\",\n INBOX_CARD_TAG: \"inbox-card-tag\",\n INBOX_CARD_REPLY_LINK: \"inbox-card-reply-link\",\n INBOX_ACTION_BTN: \"inbox-action-btn\",\n INBOX_ACTION_BTN_LABELED: \"inbox-action-btn--labeled\",\n INBOX_ACTION_LABEL: \"inbox-action-label\",\n INBOX_STATUS_DOT: \"inbox-status-dot\",\n INBOX_MENU: \"inbox-menu\",\n INBOX_MENU_ITEM: \"inbox-menu-item\",\n INBOX_DETAIL: \"inbox-detail\",\n INBOX_DETAIL_HEADER: \"inbox-detail-header\",\n INBOX_BACK: \"inbox-back\",\n INBOX_NAV_BTN: \"inbox-nav-btn\",\n INBOX_REPLIES: \"inbox-replies\",\n TOOLTIP_REPLY_COUNT: \"comment-tooltip-reply-count\",\n INBOX_EMPTY: \"inbox-empty\",\n INBOX_EMPTY_ICON: \"inbox-empty-icon\",\n INBOX_EMPTY_TITLE: \"inbox-empty-title\",\n INBOX_EMPTY_TEXT: \"inbox-empty-text\",\n INBOX_EMPTY_KBD: \"inbox-empty-kbd\",\n INBOX_EMPTY_ACTION: \"inbox-empty-action\",\n CLASSIFY_ROW: \"classify-row\",\n INBOX_BADGES: \"inbox-badges\",\n BADGE: \"helldots-badge\",\n BADGE_STATUS: \"helldots-badge--status\",\n BADGE_TYPE: \"helldots-badge--type\",\n BADGE_PRIORITY: \"helldots-badge--priority\",\n BADGE_TAG: \"helldots-badge--tag\",\n BADGE_DURATION: \"helldots-badge--duration\",\n CONTEXT_BLOCK: \"inbox-context\",\n CONTEXT_TITLE: \"inbox-context-title\",\n CONTEXT_BODY: \"inbox-context-body\",\n CONTEXT_ROW: \"inbox-context-row\",\n CONTEXT_SCREENSHOT_CAPTION: \"inbox-context-screenshot-caption\",\n CONTEXT_TOGGLE: \"inbox-context-toggle\",\n HIGHLIGHT: \"helldots-highlight\",\n};\n\n// The only classes HellDots puts on elements of the host page \u2014 everything\n// else it owns lives inside the shadow root. Anchors must never bake these\n// into a selector: they are transient widget state, so `body.comment-cursor`\n// stops matching the instant comment mode ends, killing the anchor's fast\n// path. Deliberately an explicit list rather than every value of CLASSES:\n// generic names like `active` belong to host pages too, and filtering those\n// would weaken anchors instead of protecting them.\nexport const HOST_PAGE_CLASSES = [CLASSES.COMMENT_CURSOR];\n\nexport const IDS = {\n TOOLBAR: \"comment-toolbar\",\n COMMENT_BOX: \"comment-box\",\n COMMENT_INPUT: \"comment-input\",\n SUBMIT_COMMENT: \"submit-comment\",\n STYLES: \"comment-overlay-styles\",\n GLOBAL_STYLES: \"comment-overlay-global-styles\",\n ATTACH_IMAGE_INPUT: \"attach-image-input\",\n};\n\n// RF09 \u2014 comment lifecycle. Order matters: it's the order shown in the\n// status picker menu.\nexport const STATUSES = [\"open\", \"in_progress\", \"resolved\"];\n\nexport const STATUS_COLORS = {\n open: \"#2E90FA\",\n in_progress: \"#FF9F0A\",\n resolved: \"#30D158\",\n};\n\n// RF3 \u2014 comment category. Order matters: it's the order shown in the picker.\nexport const COMMENT_TYPES = [\"bug\", \"suggestion\", \"question\", \"improvement\"];\n\nexport const TYPE_COLORS = {\n bug: \"#FF453A\",\n suggestion: \"#BF5AF2\",\n question: \"#64D2FF\",\n improvement: \"#5E5CE6\",\n};\n\n// RF4 \u2014 priority, ordered high\u2192low so the picker reads as a scale.\nexport const PRIORITIES = [\"high\", \"medium\", \"low\"];\n\n// Deliberate red/orange/grey ramp: it reads as urgency at a glance. `high`\n// sharing red with `bug` (and `medium` sharing orange with `in_progress`) is\n// fine \u2014 they're different dimensions in different UI slots, and no badge\n// ever conveys meaning by colour alone (WCAG 1.4.1).\nexport const PRIORITY_COLORS = {\n high: \"#FF453A\",\n medium: \"#FF9F0A\",\n low: \"#8E8E93\",\n};\n\nexport const SELECTORS = {\n CONTAINER: 'section, div[class*=\"container\"], div[class*=\"content\"]',\n};\n\nexport const Z_INDEX = {\n CIRCLE: 9997,\n TOOLTIP: 10000,\n TOOLBAR: 9998,\n COMMENT_BOX: 9999,\n LIGHTBOX: 10001,\n // Above the lightbox: a screenshot can be open full-screen when the \u22EF menu\n // behind it is used, and a confirmation nobody can see is worse than none.\n CONFIRM: 10002,\n};\n\n// 32x32 is a hard ceiling, not a design preference: Chromium drops a custom\n// cursor larger than that as soon as it can intersect native UI, which is\n// exactly what happens as the pointer nears the page edges \u2014 the marker\n// silently reverted to the default arrow there.\n// https://chromestatus.com/feature/5825971391299584\n//\n// The artwork itself is unchanged and still 28px; only the canvas shrank,\n// from 48 to 32, by translating the art from (6,6) to (2,2). That is why\n// CURSOR_HOTSPOT moved with it \u2014 it names the teardrop's sharp tip, which is\n// what the pointer must actually point at.\n//\n// The original blue drop shadow is gone with the canvas: at `dx=4 dy=4` and\n// `stdDeviation=5` it needed ~15px of margin the 32px canvas does not have.\n// The white 2px stroke is what carries contrast against any background; the\n// shadow was a 16%-opacity blue glow that barely registered.\nexport const CURSOR_SVG = `data:image/svg+xml;utf8,<svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g transform=\"translate(-4,-4)\"><path d=\"M6 8C6 6.89543 6.89543 6 8 6H20C27.732 6 34 12.268 34 20V20C34 27.732 27.732 34 20 34V34C12.268 34 6 27.732 6 20V8Z\" fill=\"%232E90FA\"/><path d=\"M8 7H20C27.1797 7 33 12.8203 33 20C33 27.1797 27.1797 33 20 33C12.8203 33 7 27.1797 7 20V8C7 7.44772 7.44772 7 8 7Z\" stroke=\"white\" stroke-width=\"2\"/></g></svg>`;\n\n// Where the pointer actually points: the teardrop's sharp top-left tip.\nexport const CURSOR_HOTSPOT = \"2 2\";\n", "import {\n CLASSES,\n IDS,\n Z_INDEX,\n CURSOR_SVG,\n CURSOR_HOTSPOT,\n} from \"./constants.js\";\n\n// Every scrollable surface in the widget sits on a #1C1C1E panel, so the\n// scrollbar has to be dark too. It was not: Chromium >= 121 ignores all\n// ::-webkit-scrollbar-* rules on an element that also declares\n// scrollbar-width or scrollbar-color, so the popover's styled thumb was\n// dropped and the platform default took over \u2014 a light thumb on a white\n// track, painted straight over the panel.\n//\n// The standard properties are the ones that win there, so they carry the\n// colour. The webkit block stays for Safari, which only shipped\n// scrollbar-color in 18.2 and still needs the pseudo-elements before that.\nconst SCROLLBAR = `\n scrollbar-width: thin;\n scrollbar-color: rgba(255,255,255,0.22) transparent;`;\n\nconst webkitScrollbar = (...selectors) =>\n selectors\n .map(\n (selector) => `\n ${selector}::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n }\n\n ${selector}::-webkit-scrollbar-track {\n background: transparent;\n }\n\n ${selector}::-webkit-scrollbar-thumb {\n background: rgba(255,255,255,0.22);\n border-radius: 4px;\n }\n\n ${selector}::-webkit-scrollbar-thumb:hover {\n background: rgba(255,255,255,0.35);\n }\n`\n )\n .join(\"\");\n\nexport const getStyles = () => `\n\n :host {\n all: initial;\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n line-height: 1.5;\n color-scheme: light;\n }\n\n :host *,\n :host *::before,\n :host *::after {\n box-sizing: border-box;\n font-family: inherit;\n }\n\n button {\n padding: 0;\n font: inherit;\n color: inherit;\n }\n\n #${IDS.TOOLBAR} {\n position: fixed;\n bottom: 20px;\n left: 50%;\n transform: translateX(-50%);\n z-index: ${Z_INDEX.TOOLBAR};\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER} {\n position: relative;\n }\n\n .${CLASSES.TOOLBAR_ACTION_TOOLTIP} {\n position: absolute;\n bottom: calc(100% + 10px);\n left: 50%;\n transform: translateX(-50%) translateY(4px);\n display: flex;\n align-items: center;\n gap: 8px;\n background: rgba(20, 20, 23, 0.95);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n padding: 8px 12px;\n border-radius: 10px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35);\n color: white;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease, transform 0.15s ease;\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER}:hover .${\n CLASSES.TOOLBAR_ACTION_TOOLTIP\n } {\n opacity: 1;\n pointer-events: auto;\n transform: translateX(-50%) translateY(0);\n }\n\n .${CLASSES.TOOLBAR_TEXT} {\n font-size: 13px;\n font-weight: 500;\n letter-spacing: -0.01em;\n }\n\n .${CLASSES.SHORTCUT_HINT} {\n font-size: 11px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.5);\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(255, 255, 255, 0.1);\n padding: 2px 6px;\n border-radius: 5px;\n line-height: 1;\n white-space: nowrap;\n }\n\n .${CLASSES.TOOLBAR_ACTIONS} {\n display: flex;\n flex-direction: row;\n background: rgba(20, 20, 23, 0.95);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n border-radius: 12px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35);\n }\n\n .${CLASSES.TOOLBAR_ACTION_BTN} {\n width: 42px;\n height: 42px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n outline: none;\n color: rgba(255, 255, 255, 0.65);\n cursor: pointer;\n transition: background 0.2s, color 0.2s;\n padding: 0;\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER}:first-child .${\n CLASSES.TOOLBAR_ACTION_BTN\n } {\n border-radius: 12px 0 0 12px;\n }\n\n .${CLASSES.TOOLBAR_ACTION_WRAPPER}:last-child .${\n CLASSES.TOOLBAR_ACTION_BTN\n } {\n border-radius: 0 12px 12px 0;\n }\n\n .${CLASSES.TOOLBAR_ACTION_BTN}:hover {\n background: rgba(255, 255, 255, 0.08);\n color: white;\n }\n\n .${CLASSES.TOOLBAR_COMMENT_BTN}.${CLASSES.ACTIVE} {\n color: #2E90FA;\n background: rgba(46, 144, 250, 0.1);\n }\n\n \n #${IDS.COMMENT_BOX} {\n position: fixed;\n background: #1C1C1E;\n border-radius: 12px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.4);\n padding: 16px;\n z-index: ${Z_INDEX.COMMENT_BOX};\n width: min(400px, calc(100vw - 24px));\n display: none;\n box-sizing: border-box;\n }\n\n #${IDS.COMMENT_BOX} .${CLASSES.COMMENT_INPUT_AREA} {\n display: flex;\n flex-direction: column;\n gap: 0;\n }\n\n /* Shares the comment box's own 16px inset instead of adding its own, so\n the pickers line up with the textarea below them. The hairline is the\n mirror of the one .thread-input-area carries on top. */\n .${CLASSES.CLASSIFY_ROW} {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n padding: 0 0 10px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n /* In the inbox the pickers sit in a strip of other icon buttons and read\n as controls on their own. Alone at the top of an empty comment box\n they need an outline to do the same. */\n .${CLASSES.CLASSIFY_ROW} .${CLASSES.INBOX_ACTION_BTN} {\n height: 26px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n }\n\n .${CLASSES.CLASSIFY_ROW} .${CLASSES.INBOX_ACTION_BTN}:hover {\n border-color: rgba(255, 255, 255, 0.28);\n }\n\n #${IDS.COMMENT_INPUT} {\n flex: 1;\n min-height: 20px;\n background: #1C1C1E;\n border: none;\n resize: none;\n font-family: inherit;\n color: white;\n font-size: 14px;\n line-height: 1.4;\n box-sizing: border-box;\n field-sizing: content;\n padding: 8px 0;\n }\n\n #${IDS.COMMENT_INPUT}::placeholder {\n color: rgba(255, 255, 255, 0.5);\n }\n \n #${IDS.COMMENT_INPUT}:focus {\n outline: none;\n box-shadow: none;\n }\n\n .${CLASSES.COMMENT_ACTIONS_BAR} {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-top: 12px;\n }\n\n .${CLASSES.ATTACH_IMAGE_BTN} {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.5);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: background 0.2s, color 0.2s;\n }\n\n .${CLASSES.ATTACH_IMAGE_BTN}:hover {\n background: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.8);\n }\n\n .${CLASSES.CIRCLE} {\n position: absolute;\n width: 28px;\n height: 28px;\n background: #2E90FA;\n border-radius: 0% 100% 100% 100%;\n border: 2px solid #FFF;\n cursor: pointer;\n box-shadow: 0 1px 5px rgba(0,0,0,0.2);\n transition: transform 0.2s, background 0.2s;\n z-index: ${Z_INDEX.CIRCLE};\n transform: translate(-50%, -50%);\n }\n\n .${CLASSES.CIRCLE}:hover {\n transform: translate(-50%, -50%) scale(1.2) !important;\n background: rgb(0, 123, 255);\n }\n\n .${CLASSES.CIRCLE}.${CLASSES.HIGHLIGHT} {\n transform: translate(-50%, -50%) scale(1.2) !important;\n background: rgb(0, 123, 255);\n box-shadow: 0 0 0 4px rgba(46, 144, 250, 0.35), 0 1px 5px rgba(0,0,0,0.2);\n }\n\n /* The marker whose thread is open. Grows like hover does \u2014 the pointer\n has to leave the circle to reach the popover, so hover alone can't\n carry the selected state \u2014 and adds a heavier ring. Size and ring,\n not colour alone (WCAG 1.4.1). */\n .${CLASSES.CIRCLE}.${CLASSES.CIRCLE_ACTIVE} {\n transform: translate(-50%, -50%) scale(1.2) !important;\n background: rgb(0, 123, 255);\n box-shadow: 0 0 0 5px rgba(46, 144, 250, 0.5), 0 1px 5px rgba(0,0,0,0.2);\n }\n\n .${CLASSES.CIRCLE_WRAPPER} {\n position: absolute;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n z-index: ${Z_INDEX.CIRCLE};\n }\n\n .${CLASSES.TOOLTIP} {\n position: fixed;\n background: #1C1C1E;\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.4);\n width: min(400px, calc(100vw - 24px));\n max-height: calc(100vh - 20px);\n overflow-y: auto;\n overscroll-behavior: contain;${SCROLLBAR}\n z-index: ${Z_INDEX.TOOLTIP};\n color: white;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n }\n\n .${CLASSES.TOOLTIP} .${CLASSES.THREAD_HEADER} {\n padding: 0 0 0;\n }\n\n .${CLASSES.TOOLTIP} .${CLASSES.THREAD_BODY} {\n padding: 8px 0;\n }\n\n .${CLASSES.THREAD_POPOVER} {\n position: fixed;\n background: #1C1C1E;\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.4);\n width: min(400px, calc(100vw - 24px));\n max-height: calc(100vh - 20px);\n display: flex;\n flex-direction: column;\n z-index: ${Z_INDEX.TOOLTIP};\n color: white;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n }\n\n /* The header, its action row and the reply box are pinned; the comment\n body, context block and replies scroll between them. The min-height: 0\n is required \u2014 a flex item's default minimum is its content height, so\n without it the popover grows past max-height instead of scrolling. */\n .${CLASSES.THREAD_POPOVER} > .${CLASSES.THREAD_HEADER},\n .${CLASSES.THREAD_POPOVER} > .${CLASSES.THREAD_ACTIONS_ROW},\n .${CLASSES.THREAD_POPOVER} > .${CLASSES.THREAD_INPUT_AREA} {\n flex: none;\n }\n\n .${CLASSES.THREAD_SCROLL} {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior: contain;${SCROLLBAR}\n }\n${webkitScrollbar(\n `.${CLASSES.THREAD_SCROLL}`,\n `.${CLASSES.TOOLTIP}`,\n `.${CLASSES.INBOX_LIST}`,\n `.${CLASSES.INBOX_DETAIL}`,\n `.${CLASSES.EDITOR_INPUT}`\n)}\n .${CLASSES.INBOX_PANEL} {\n position: fixed;\n top: 16px;\n right: 16px;\n bottom: 16px;\n width: 380px;\n display: flex;\n flex-direction: column;\n background: #1C1C1E;\n border: 1px solid rgba(255,255,255,0.08);\n border-radius: 14px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.5);\n z-index: ${Z_INDEX.COMMENT_BOX};\n color: white;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n /* 380px panel + 16px of gutter each side needs 412px to sit flush on\n the right; below that it spans the viewport instead. */\n @media (max-width: 480px) {\n .${CLASSES.INBOX_PANEL} {\n left: 16px;\n width: auto;\n }\n }\n\n .${CLASSES.INBOX_HEADER},\n .${CLASSES.INBOX_DETAIL_HEADER} {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 12px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.08);\n flex: none;\n }\n\n .${CLASSES.INBOX_FILTER}-wrapper {\n position: relative;\n }\n\n .${CLASSES.INBOX_FILTER} {\n display: flex;\n align-items: center;\n gap: 6px;\n background: transparent;\n border: none;\n color: white;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n padding: 4px 6px;\n border-radius: 6px;\n }\n\n .${CLASSES.INBOX_FILTER}:hover {\n background: rgba(255,255,255,0.08);\n }\n\n .${CLASSES.INBOX_FILTER_MENU} {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n background: #2C2C2E;\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 12px;\n padding: 14px;\n width: 300px;\n max-width: calc(100vw - 40px);\n z-index: 1;\n box-shadow: 0 8px 28px rgba(0,0,0,0.5);\n }\n\n .${CLASSES.INBOX_FILTER_MENU_HEADER} {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 13px;\n font-weight: 600;\n margin-bottom: 12px;\n }\n\n .${CLASSES.INBOX_FILTER_CLEAR} {\n background: transparent;\n border: none;\n color: rgba(255,255,255,0.55);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n padding: 0;\n }\n\n .${CLASSES.INBOX_FILTER_CLEAR}:hover:not(:disabled) {\n color: white;\n }\n\n .${CLASSES.INBOX_FILTER_CLEAR}:disabled {\n opacity: 0.35;\n cursor: default;\n }\n\n .${CLASSES.INBOX_FILTER_GROUP} + .${CLASSES.INBOX_FILTER_GROUP} {\n margin-top: 14px;\n }\n\n .${CLASSES.INBOX_FILTER_SECTION} {\n font-size: 11px;\n font-weight: 600;\n color: rgba(255,255,255,0.45);\n margin-bottom: 8px;\n }\n\n .${CLASSES.INBOX_FILTER_CHIPS} {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n\n .${CLASSES.INBOX_FILTER_CHIP} {\n background: transparent;\n border: 1px solid rgba(255,255,255,0.18);\n border-radius: 999px;\n color: rgba(255,255,255,0.75);\n font-size: 12px;\n line-height: 1;\n padding: 7px 12px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n }\n\n .${CLASSES.INBOX_FILTER_CHIP}:hover {\n border-color: rgba(255,255,255,0.4);\n color: white;\n }\n\n /* Selected chips are filled *and* carry aria-checked \u2014 the fill is a\n convenience, never the only way to tell them apart. */\n .${CLASSES.INBOX_FILTER_CHIP}[aria-checked=\"true\"] {\n background: rgba(255,255,255,0.92);\n border-color: rgba(255,255,255,0.92);\n color: #1C1C1E;\n font-weight: 600;\n }\n\n .${CLASSES.INBOX_MENU_ITEM} {\n display: block;\n width: 100%;\n text-align: left;\n background: transparent;\n border: none;\n color: white;\n font-size: 13px;\n padding: 7px 10px;\n border-radius: 6px;\n cursor: pointer;\n }\n\n .${CLASSES.INBOX_MENU_ITEM}:hover {\n background: rgba(255,255,255,0.08);\n }\n\n .${CLASSES.INBOX_CLOSE},\n .${CLASSES.INBOX_NAV_BTN},\n .${CLASSES.INBOX_BACK} {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: none;\n color: rgba(255,255,255,0.75);\n cursor: pointer;\n padding: 4px 6px;\n border-radius: 6px;\n font-size: 14px;\n }\n\n .${CLASSES.INBOX_CLOSE} {\n font-size: 20px;\n line-height: 1;\n }\n\n .${CLASSES.INBOX_CLOSE}:hover,\n .${CLASSES.INBOX_NAV_BTN}:not(:disabled):hover,\n .${CLASSES.INBOX_BACK}:hover {\n background: rgba(255,255,255,0.08);\n color: white;\n }\n\n .${CLASSES.INBOX_NAV_BTN}:disabled {\n opacity: 0.35;\n cursor: default;\n }\n\n .${CLASSES.INBOX_LIST},\n .${CLASSES.INBOX_DETAIL} {\n flex: 1;\n overflow-y: auto;${SCROLLBAR}\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .${CLASSES.INBOX_CARD} {\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 10px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD} {\n cursor: pointer;\n }\n\n .${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD}:hover {\n border-color: rgba(255,255,255,0.22);\n }\n\n .${CLASSES.INBOX_CARD}--resolved {\n border-color: rgba(48, 209, 88, 0.4);\n opacity: 0.75;\n }\n\n .${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD}--resolved:hover {\n border-color: rgba(48, 209, 88, 0.7);\n opacity: 1;\n }\n\n .${CLASSES.INBOX_CARD_HEADER} {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n }\n\n /* Labels made the strip wide enough to overflow a narrow card in the\n worst case (\"In progress\" + \"Improvement\" + \"Medium\"), so it wraps to\n a second line rather than pushing the card sideways. */\n .${CLASSES.INBOX_CARD_ACTIONS} {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 6px;\n }\n\n .${CLASSES.INBOX_ACTION_BTN} {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n background: transparent;\n border: none;\n border-radius: 6px;\n color: rgba(255,255,255,0.65);\n cursor: pointer;\n }\n\n .${CLASSES.INBOX_ACTION_BTN}:hover {\n background: rgba(255,255,255,0.08);\n color: white;\n }\n\n /* Status/type/priority pickers show their current value as text next to\n the dot (colour alone can't tell bug/high apart \u2014 same hex, and\n there's no hover on touch). The 72px cap they used to carry is gone:\n the strip has its own row now, so labels show in full. */\n .${CLASSES.INBOX_ACTION_BTN_LABELED} {\n width: auto;\n padding: 0 8px 0 6px;\n gap: 5px;\n justify-content: flex-start;\n flex: none;\n }\n\n .${CLASSES.INBOX_ACTION_LABEL} {\n font-size: 11px;\n line-height: 1;\n white-space: nowrap;\n }\n\n .${CLASSES.INBOX_STATUS_DOT} {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n border: 1.5px solid rgba(255,255,255,0.45);\n display: inline-block;\n flex: none;\n }\n\n .${CLASSES.INBOX_MENU_ITEM} .${CLASSES.INBOX_STATUS_DOT} {\n width: 9px;\n height: 9px;\n border: none;\n margin-right: 8px;\n vertical-align: baseline;\n }\n\n .${CLASSES.INBOX_MENU_ITEM}[aria-checked=\"true\"] {\n background: rgba(255,255,255,0.08);\n }\n\n /* The action strip on its own row under the author. Shared by the inbox\n card and the thread popover so the two read the same; only the\n popover needs the extra top padding, since the card's column gap\n already spaces it. */\n .${CLASSES.THREAD_ACTIONS_ROW} {\n display: flex;\n justify-content: flex-end;\n }\n\n .${CLASSES.THREAD_POPOVER} > .${CLASSES.THREAD_ACTIONS_ROW} {\n padding-top: 8px;\n }\n\n /* Generic hover tooltip, same look as .thread-time[data-full-date] */\n [data-hd-tooltip] {\n position: relative;\n }\n\n [data-hd-tooltip]::after {\n content: attr(data-hd-tooltip);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n background: #000;\n color: white;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 11px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.12s ease;\n z-index: 2;\n }\n\n [data-hd-tooltip]:hover::after {\n opacity: 1;\n }\n\n .${CLASSES.INBOX_MENU} {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n background: #2C2C2E;\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 8px;\n padding: 4px;\n min-width: 130px;\n z-index: 1;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n }\n\n .${CLASSES.INBOX_CARD_TEXT} {\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .${CLASSES.INBOX_CARD_TAG} {\n align-self: flex-start;\n padding: 1px 8px;\n border-radius: 999px;\n background: rgba(255, 159, 10, 0.2);\n color: #FF9F0A;\n font-size: 11px;\n font-weight: 600;\n }\n\n .${CLASSES.INBOX_BADGES} {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n /* Vertical separation from the card text above only applies to the\n badge row on inbox cards -- inside the comment-box classify row,\n .inbox-badges is one flex item among the type/priority pickers and\n the tags input, and the classify row's own gap already spaces it\n from its siblings, so an extra margin here would nudge it out of\n alignment with them. */\n .${CLASSES.INBOX_CARD} .${CLASSES.INBOX_BADGES} {\n margin-top: 6px;\n }\n .${CLASSES.TOOLTIP} .${CLASSES.INBOX_BADGES} {\n margin-bottom: 4px;\n }\n .${CLASSES.BADGE} {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n border-radius: 10px;\n font-size: 10px;\n line-height: 1.6;\n letter-spacing: 0.01em;\n white-space: nowrap;\n }\n .${CLASSES.BADGE_STATUS},\n .${CLASSES.BADGE_TYPE},\n .${CLASSES.BADGE_PRIORITY} {\n font-weight: 600;\n }\n .${CLASSES.BADGE_TAG} {\n opacity: 0.75;\n }\n .${CLASSES.BADGE_DURATION} {\n opacity: 0.75;\n border-style: dashed;\n }\n\n .${CLASSES.CONTEXT_BLOCK} {\n display: flex;\n flex-direction: column;\n padding: 10px 12px;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n font-size: 11px;\n }\n .${CLASSES.CONTEXT_BODY} {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .${CLASSES.CONTEXT_TITLE} {\n font-size: 11px;\n font-weight: 600;\n color: rgba(255,255,255,0.45);\n padding-bottom: 4px;\n }\n /* The popover's collapsed variant. Same weight and colour as the inbox\n title so the two surfaces read as the same block. */\n .${CLASSES.CONTEXT_TOGGLE} {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n background: transparent;\n border: none;\n color: rgba(255,255,255,0.45);\n font-size: 11px;\n font-weight: 600;\n cursor: pointer;\n padding: 2px 0;\n }\n .${CLASSES.CONTEXT_TOGGLE}:hover {\n color: rgba(255,255,255,0.75);\n }\n .${CLASSES.CONTEXT_TOGGLE} svg {\n flex: none;\n transition: transform 0.15s ease;\n }\n .${CLASSES.CONTEXT_TOGGLE}[aria-expanded=\"true\"] svg {\n transform: rotate(180deg);\n }\n .${CLASSES.CONTEXT_TOGGLE}[aria-expanded=\"true\"] + .${CLASSES.CONTEXT_BODY} {\n padding-top: 8px;\n }\n .${CLASSES.CONTEXT_BLOCK} img {\n width: 100%;\n border-radius: 6px;\n margin-bottom: 6px;\n cursor: zoom-in;\n }\n .${CLASSES.CONTEXT_SCREENSHOT_CAPTION} {\n opacity: 0.75;\n }\n .${CLASSES.CONTEXT_ROW} {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n opacity: 0.75;\n }\n /* URLs and user agents have no spaces to break on, so the value column\n would otherwise push the row wider than the panel. */\n .${CLASSES.CONTEXT_ROW} span:last-child {\n text-align: right;\n word-break: break-all;\n }\n\n .${CLASSES.INBOX_CARD_REPLY_LINK} {\n align-self: flex-start;\n background: transparent;\n border: none;\n color: rgba(255,255,255,0.55);\n font-size: 13px;\n cursor: pointer;\n padding: 0;\n }\n\n .${CLASSES.INBOX_CARD_REPLY_LINK}:hover {\n color: white;\n }\n\n .${CLASSES.INBOX_REPLIES} {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 0 4px;\n }\n\n /* Centred in whatever height the list has, so the state sits in the\n middle of the panel rather than clinging to the top. */\n /* Sits under the body as a quiet aside: the preview shows the root\n comment only, so this says \"there is more\" without competing with it. */\n .${CLASSES.TOOLTIP_REPLY_COUNT} {\n font-size: 12px;\n color: rgba(255,255,255,0.45);\n padding-top: 4px;\n }\n\n .${CLASSES.INBOX_EMPTY} {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 32px 24px;\n color: rgba(255,255,255,0.55);\n text-align: center;\n }\n\n /* The marker's own silhouette, drawn as an outline: what the user is\n about to place, not a generic placeholder. Same border-radius as\n .comment-circle. */\n .${CLASSES.INBOX_EMPTY_ICON} {\n width: 44px;\n height: 44px;\n margin-bottom: 6px;\n border: 2px dashed rgba(255,255,255,0.25);\n border-radius: 0% 100% 100% 100%;\n flex: none;\n }\n\n .${CLASSES.INBOX_EMPTY_TITLE} {\n color: white;\n font-size: 14px;\n font-weight: 600;\n }\n\n .${CLASSES.INBOX_EMPTY_TEXT} {\n font-size: 13px;\n line-height: 1.5;\n max-width: 30ch;\n }\n\n .${CLASSES.INBOX_EMPTY_KBD} {\n font-family: inherit;\n font-size: 12px;\n font-weight: 500;\n color: rgba(255,255,255,0.8);\n background: rgba(255,255,255,0.08);\n border: 1px solid rgba(255,255,255,0.14);\n border-radius: 5px;\n padding: 1px 5px;\n white-space: nowrap;\n }\n\n .${CLASSES.INBOX_EMPTY_ACTION} {\n margin-top: 6px;\n background: transparent;\n border: 1px solid rgba(255,255,255,0.2);\n border-radius: 8px;\n color: white;\n font-size: 13px;\n font-weight: 500;\n padding: 8px 16px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s;\n }\n\n .${CLASSES.INBOX_EMPTY_ACTION}:hover {\n background: rgba(255,255,255,0.08);\n border-color: rgba(255,255,255,0.35);\n }\n\n .${CLASSES.THREAD_HEADER} {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 0 0;\n }\n\n /* min-width:0 is what lets the author actually shrink: a flex item\n defaults to min-content, so without it the name pushes the row wider\n instead of truncating. */\n .${CLASSES.THREAD_META} {\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1;\n min-width: 0;\n }\n\n .${CLASSES.THREAD_AUTHOR} {\n font-weight: 600;\n font-size: 13px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .${CLASSES.THREAD_TIME} {\n font-size: 12px;\n color: rgba(255,255,255,0.5);\n cursor: default;\n position: relative;\n flex: none;\n }\n\n .${CLASSES.THREAD_TIME}::after {\n content: attr(data-full-date);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n background: #000;\n color: white;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 11px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n z-index: 1;\n }\n\n .${CLASSES.THREAD_TIME}:hover::after {\n opacity: 1;\n }\n\n .${CLASSES.THREAD_BODY} {\n padding: 8px 0;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .${CLASSES.THREAD_REPLIES} {\n padding: 0;\n }\n\n .${CLASSES.THREAD_REPLIES}:empty {\n display: none;\n }\n\n .${CLASSES.THREAD_REPLY} {\n padding: 16px 0 0 0;\n border-top: 1px solid rgba(255,255,255,0.1);\n white-space: pre-wrap;\n word-break: break-word;\n font-size: 13px;\n color: rgba(255,255,255,0.85);\n }\n\n .${CLASSES.THREAD_REPLY} .${CLASSES.THREAD_META} {\n margin-bottom: 2px;\n }\n\n /* Pushed to the far edge of the reply's meta row, where the same \u22EF sits\n on the comment above it. flex: none keeps it off the author's\n truncation budget. */\n .${CLASSES.THREAD_REPLY_ACTIONS} {\n margin-left: auto;\n flex: none;\n }\n\n .${CLASSES.THREAD_REPLY} .${CLASSES.SCREENSHOT_IMG} {\n width: 144px;\n height: 100px;\n object-fit: cover;\n border-radius: 8px;\n margin-top: 4px;\n cursor: pointer;\n display: block;\n }\n\n .${CLASSES.THREAD_INPUT_AREA} {\n display: flex;\n flex-direction: column;\n gap: 0;\n padding: 12px 0 0;\n border-top: 1px solid rgba(255,255,255,0.1);\n }\n\n .${CLASSES.THREAD_INPUT} {\n width: 100%;\n background: transparent;\n border: none;\n padding: 0;\n color: white;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n box-sizing: border-box;\n }\n\n .${CLASSES.THREAD_INPUT}::placeholder {\n color: rgba(255,255,255,0.5);\n }\n\n .${CLASSES.THREAD_INPUT}:focus {\n outline: none;\n box-shadow: none;\n }\n\n .${CLASSES.THREAD_SUBMIT} {\n background: none;\n border: none;\n color: #2E90FA;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: background 0.2s;\n }\n\n .${CLASSES.THREAD_SUBMIT}:hover {\n background: rgba(46, 144, 250, 0.15);\n color: #1570D6;\n }\n\n .${CLASSES.CLOSE_TOOLTIP} {\n background: none;\n border: none;\n font-size: 18px;\n cursor: pointer;\n color: rgba(255,255,255,0.5);\n line-height: 1;\n }\n \n .${CLASSES.CLOSE_TOOLTIP}:hover {\n color: white;\n }\n\n .${CLASSES.PREVIEW_CIRCLE} {\n animation: helldots-pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes helldots-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(46, 144, 250, 0.4), 0 1px 5px rgba(0,0,0,0.2); }\n 50% { box-shadow: 0 0 0 8px rgba(46, 144, 250, 0), 0 1px 5px rgba(0,0,0,0.2); }\n }\n\n .${CLASSES.COMMENT_OVERLAY} {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: transparent;\n pointer-events: none;\n z-index: ${Z_INDEX.TOOLBAR - 1};\n }\n\n .${CLASSES.COMMENT_OVERLAY}.${CLASSES.ACTIVE} {\n pointer-events: auto;\n background: rgba(0, 0, 0, 0.1);\n }\n\n .${CLASSES.SELECTION_RECT} {\n position: fixed;\n border: 2px solid #2E90FA;\n background: rgba(46, 144, 250, 0.1);\n pointer-events: none;\n z-index: ${Z_INDEX.TOOLTIP};\n box-sizing: border-box;\n }\n\n .${CLASSES.SCREENSHOTS_CONTAINER} {\n display: none;\n overflow-x: auto;\n gap: 8px;\n margin-top: 4px;\n padding: 4px 0;\n scrollbar-width: none;\n -ms-overflow-style: none;\n margin-bottom: 8px;\n }\n\n .${CLASSES.SCREENSHOTS_CONTAINER}::-webkit-scrollbar {\n display: none;\n }\n\n .${CLASSES.SCREENSHOTS_CONTAINER}.${CLASSES.ACTIVE} {\n display: flex;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} {\n position: relative;\n flex-shrink: 0;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_IMG} {\n width: 50px;\n height: 50px;\n object-fit: cover;\n border-radius: 8px;\n cursor: pointer;\n display: block;\n margin: 0;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_IMG}:hover {\n opacity: 0.85;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_REMOVE} {\n position: absolute;\n top: -5px;\n right: -5px;\n width: 18px;\n height: 18px;\n background: rgba(0,0,0,0.7);\n border: none;\n border-radius: 50%;\n color: white;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n display: none;\n align-items: center;\n justify-content: center;\n z-index: 1;\n }\n\n .${CLASSES.SCREENSHOT_ITEM}:hover .${CLASSES.SCREENSHOT_REMOVE} {\n display: flex;\n }\n\n .${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_REMOVE}:hover {\n background: rgba(0,0,0,0.9);\n }\n\n .${CLASSES.TOOLTIP} > .${CLASSES.SCREENSHOTS_CONTAINER} .${\n CLASSES.SCREENSHOT_ITEM\n } .${CLASSES.SCREENSHOT_IMG},\n .${CLASSES.THREAD_SCROLL} > .${CLASSES.SCREENSHOTS_CONTAINER} .${\n CLASSES.SCREENSHOT_ITEM\n } .${CLASSES.SCREENSHOT_IMG},\n .${CLASSES.THREAD_REPLY} .${CLASSES.SCREENSHOT_ITEM} .${\n CLASSES.SCREENSHOT_IMG\n } {\n width: 144px;\n height: 100px;\n }\n\n .${CLASSES.CONFIRM} {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0,0,0,0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n animation: helldots-fade-in 0.15s ease;\n }\n\n .${CLASSES.CONFIRM_PANEL} {\n width: min(360px, 100%);\n background: #1C1C1E;\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 14px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.5);\n padding: 20px;\n color: white;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n }\n\n /* :host { all: initial } does not reach descendants, so the heading and\n paragraph still arrive with UA margins. */\n .${CLASSES.CONFIRM_TITLE} {\n margin: 0 0 8px;\n font-size: 15px;\n font-weight: 600;\n }\n\n .${CLASSES.CONFIRM_MESSAGE} {\n margin: 0 0 20px;\n font-size: 13px;\n color: rgba(255,255,255,0.65);\n }\n\n .${CLASSES.CONFIRM_ACTIONS} {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n }\n\n .${CLASSES.CONFIRM_CANCEL},\n .${CLASSES.CONFIRM_ACCEPT} {\n padding: 7px 14px;\n border: 1px solid transparent;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n }\n\n /* The one place in the widget with visible focus rings. The dialog traps\n Tab between exactly two buttons, one of them destructive, so a\n keyboard user who cannot see which one is focused is being asked to\n guess. Scoped here on purpose \u2014 see DECISIONS.md; it does not reopen\n the rings that were reverted elsewhere for visual parity. */\n .${CLASSES.CONFIRM_CANCEL}:focus-visible,\n .${CLASSES.CONFIRM_ACCEPT}:focus-visible {\n outline: 2px solid #2E90FA;\n outline-offset: 2px;\n }\n\n .${CLASSES.CONFIRM_CANCEL} {\n background: rgba(255,255,255,0.08);\n border-color: rgba(255,255,255,0.12);\n color: white;\n }\n\n .${CLASSES.CONFIRM_CANCEL}:hover {\n background: rgba(255,255,255,0.14);\n }\n\n .${CLASSES.CONFIRM_ACCEPT} {\n background: #FF453A;\n color: white;\n }\n\n .${CLASSES.CONFIRM_ACCEPT}:hover {\n background: #FF6961;\n }\n\n .${CLASSES.LIGHTBOX} {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0,0,0,0.92);\n z-index: ${Z_INDEX.LIGHTBOX};\n display: flex;\n align-items: center;\n justify-content: center;\n animation: helldots-fade-in 0.2s ease;\n }\n\n @keyframes helldots-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .${CLASSES.LIGHTBOX_IMG} {\n max-width: 90vw;\n max-height: 90vh;\n object-fit: contain;\n border-radius: 8px;\n }\n\n .${CLASSES.LIGHTBOX_CLOSE} {\n position: absolute;\n top: 16px;\n right: 16px;\n background: rgba(255,255,255,0.15);\n border: none;\n color: white;\n font-size: 24px;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .${CLASSES.LIGHTBOX_CLOSE}:hover {\n background: rgba(255,255,255,0.3);\n }\n /* --- inline editor --- */\n\n .${CLASSES.EDITOR} {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 4px 0 2px;\n }\n\n .${CLASSES.EDITOR_INPUT} {\n width: 100%;\n box-sizing: border-box;\n resize: vertical;\n min-height: 60px;\n background: rgba(255,255,255,0.06);\n border: 1px solid rgba(255,255,255,0.14);\n border-radius: 8px;\n padding: 8px 10px;\n color: white;\n font-size: 14px;\n font-family: inherit;\n line-height: 1.45;\n outline: none;${SCROLLBAR}\n }\n\n .${CLASSES.EDITOR_INPUT}:focus {\n border-color: rgba(46,144,250,0.7);\n }\n\n .${CLASSES.EDITOR_ACTIONS} {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n }\n\n .${CLASSES.EDITOR_CANCEL},\n .${CLASSES.EDITOR_SAVE} {\n padding: 5px 12px;\n border: 1px solid transparent;\n border-radius: 7px;\n font-size: 13px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n }\n\n .${CLASSES.EDITOR_CANCEL} {\n background: rgba(255,255,255,0.08);\n border-color: rgba(255,255,255,0.12);\n color: white;\n }\n\n .${CLASSES.EDITOR_CANCEL}:hover {\n background: rgba(255,255,255,0.14);\n }\n\n .${CLASSES.EDITOR_SAVE} {\n background: #2E90FA;\n color: white;\n }\n\n .${CLASSES.EDITOR_SAVE}:hover:not(:disabled) {\n background: #57A6FB;\n }\n\n /* Blanking a body is not a way to delete \u2014 the comment would keep its\n marker, its replies and its inbox row while saying nothing. The\n disabled state is carried by more than colour: the cursor changes and\n the control stops responding (WCAG 1.4.1). */\n .${CLASSES.EDITOR_SAVE}:disabled {\n background: rgba(255,255,255,0.10);\n color: rgba(255,255,255,0.4);\n cursor: not-allowed;\n }\n\n .${CLASSES.THREAD_EDITED} {\n font-size: 12px;\n color: rgba(255,255,255,0.4);\n cursor: default;\n position: relative;\n flex: none;\n }\n\n .${CLASSES.THREAD_EDITED}::before {\n content: \"\u00B7\";\n margin-right: 4px;\n }\n\n .${CLASSES.THREAD_EDITED}::after {\n content: attr(data-full-date);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n background: #000;\n color: white;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 11px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n }\n\n .${CLASSES.THREAD_EDITED}:hover::after {\n opacity: 1;\n }\n\n .${CLASSES.INBOX_NOTICE} {\n margin: 0 0 10px;\n padding: 9px 11px;\n border-radius: 8px;\n background: rgba(255,255,255,0.06);\n border: 1px solid rgba(255,255,255,0.12);\n color: rgba(255,255,255,0.75);\n font-size: 13px;\n line-height: 1.4;\n }\n`;\n\n/**\n * Styles that must apply to the host page itself (outside the shadow root),\n * because they target elements HellDots doesn't own \u2014 e.g. `document.body`\n * while in comment mode. A shadow root's stylesheet never reaches outside\n * it, so these can't live in `getStyles()`; they're injected separately\n * into `document.head` instead (see `CommentOverlay.injectStyles`).\n */\nexport const getGlobalStyles = () => `\n .${CLASSES.COMMENT_CURSOR},\n .${CLASSES.COMMENT_CURSOR} * {\n cursor: url('${CURSOR_SVG}') ${CURSOR_HOTSPOT}, auto !important;\n }\n\n`;\n", "export default {\n commentAriaLabelPrefix: \"Comment: \",\n anonymous: \"Anonymous\",\n justNow: \"Just now\",\n minutesAgoTemplate: \"{n}m\",\n hoursAgoTemplate: \"{n}h\",\n daysAgoTemplate: \"{n}d\",\n toolbarComment: \"Comment\",\n toolbarInbox: \"Inbox\",\n modifierAlt: \"Alt\",\n modifierCtrl: \"Ctrl\",\n commentBoxAriaLabel: \"New comment\",\n commentPlaceholder: \"Type your comment...\",\n attachImage: \"Attach image\",\n send: \"Send\",\n tooltipAriaLabel: \"Comment preview\",\n close: \"Close\",\n popoverAriaLabel: \"Comment thread\",\n replyPlaceholder: \"Reply...\",\n attachedScreenshot: \"Attached screenshot\",\n screenshotPreview: \"Screenshot preview\",\n removeScreenshot: \"Remove screenshot\",\n inboxAriaLabel: \"Comments inbox\",\n inboxEmptyTitle: \"No comments yet\",\n inboxEmptyHintTemplate: \"Press {n} and click anywhere on the page to start.\",\n inboxEmptyAction: \"Turn on comment mode\",\n inboxNoMatches: \"No comments match these filters\",\n orphanedBadge: \"Unanchored\",\n hiddenBadge: \"Hidden\",\n filterAll: \"All pages\",\n filterCurrentPage: \"Current page\",\n filterTitle: \"Filter\",\n filterClear: \"Clear\",\n filterByPage: \"Page\",\n filterByStatus: \"Status\",\n back: \"Back\",\n deleteComment: \"Delete\",\n deleteReply: \"Delete reply\",\n replyOptions: \"Reply options\",\n editComment: \"Edit\",\n editReply: \"Edit reply\",\n copyLink: \"Copy link\",\n linkCopied: \"Link copied\",\n editorAriaLabel: \"Edit text\",\n editSave: \"Save\",\n editCancel: \"Cancel\",\n editedMark: \"edited\",\n editedAtPrefix: \"Edited \",\n confirmDiscardTitle: \"Discard changes?\",\n confirmDiscardMessage:\n \"The changes you made to this text have not been saved and will be lost.\",\n confirmDiscard: \"Discard\",\n confirmKeepEditing: \"Keep editing\",\n commentNotFound: \"That comment is not on this page.\",\n confirmDelete: \"Delete\",\n confirmCancel: \"Cancel\",\n confirmDeleteCommentTitle: \"Delete this comment?\",\n confirmDeleteCommentMessage:\n \"This comment will be permanently deleted. This action cannot be undone.\",\n confirmDeleteThreadMessage:\n \"This comment and all of its replies will be permanently deleted. This action cannot be undone.\",\n confirmDeleteReplyTitle: \"Delete this reply?\",\n confirmDeleteReplyMessage:\n \"This reply will be permanently deleted. This action cannot be undone.\",\n copyAgentContext: \"Copy agent context\",\n copied: \"Copied\",\n statusLabel: \"Status\",\n prevComment: \"Previous comment\",\n nextComment: \"Next comment\",\n replyLink: \"Reply\",\n replyCountOne: \"1 reply\",\n replyCountTemplate: \"{n} replies\",\n commentOptions: \"Comment options\",\n moreOptions: \"More\",\n statusOpen: \"Open\",\n statusInProgress: \"In progress\",\n statusResolved: \"Resolved\",\n durationLessThanMinute: \"<1m\",\n resolvedInTemplate: \"Resolved in {n}\",\n typeLabel: \"Type\",\n priorityLabel: \"Priority\",\n unset: \"Unset\",\n typeBug: \"Bug\",\n typeSuggestion: \"Suggestion\",\n typeQuestion: \"Question\",\n typeImprovement: \"Improvement\",\n priorityHigh: \"High\",\n priorityMedium: \"Medium\",\n priorityLow: \"Low\",\n filterByType: \"Type\",\n filterByPriority: \"Priority\",\n contextSection: \"Context\",\n autoScreenshotLabel: \"Automatic context\",\n contextUrl: \"URL\",\n contextViewport: \"Viewport\",\n contextScreen: \"Screen\",\n contextBrowser: \"Browser\",\n contextOs: \"OS\",\n};\n", "export default {\n commentAriaLabelPrefix: \"Comentario: \",\n anonymous: \"An\u00F3nimo\",\n justNow: \"Justo ahora\",\n minutesAgoTemplate: \"{n}m\",\n hoursAgoTemplate: \"{n}h\",\n daysAgoTemplate: \"{n}d\",\n toolbarComment: \"Comentar\",\n toolbarInbox: \"Bandeja\",\n modifierAlt: \"Alt\",\n modifierCtrl: \"Ctrl\",\n commentBoxAriaLabel: \"Nuevo comentario\",\n commentPlaceholder: \"Escribe tu comentario...\",\n attachImage: \"Adjuntar imagen\",\n send: \"Enviar\",\n tooltipAriaLabel: \"Vista previa del comentario\",\n close: \"Cerrar\",\n popoverAriaLabel: \"Hilo de comentarios\",\n replyPlaceholder: \"Responder...\",\n attachedScreenshot: \"Captura de pantalla adjunta\",\n screenshotPreview: \"Vista previa de la captura\",\n removeScreenshot: \"Quitar captura de pantalla\",\n inboxAriaLabel: \"Bandeja de comentarios\",\n inboxEmptyTitle: \"Todav\u00EDa no hay comentarios\",\n inboxEmptyHintTemplate:\n \"Pulsa {n} y haz clic en cualquier parte de la p\u00E1gina para empezar.\",\n inboxEmptyAction: \"Activar modo\",\n inboxNoMatches: \"Ning\u00FAn comentario coincide con estos filtros\",\n orphanedBadge: \"Desanclado\",\n hiddenBadge: \"Oculto\",\n filterAll: \"Todas las p\u00E1ginas\",\n filterCurrentPage: \"P\u00E1gina actual\",\n filterTitle: \"Filtrar\",\n filterClear: \"Limpiar\",\n filterByPage: \"P\u00E1gina\",\n filterByStatus: \"Estado\",\n back: \"Volver\",\n deleteComment: \"Eliminar\",\n deleteReply: \"Eliminar respuesta\",\n replyOptions: \"Opciones de la respuesta\",\n editComment: \"Editar\",\n editReply: \"Editar respuesta\",\n copyLink: \"Copiar enlace\",\n linkCopied: \"Enlace copiado\",\n editorAriaLabel: \"Editar texto\",\n editSave: \"Guardar\",\n editCancel: \"Cancelar\",\n editedMark: \"editado\",\n editedAtPrefix: \"Editado el \",\n confirmDiscardTitle: \"\u00BFDescartar los cambios?\",\n confirmDiscardMessage:\n \"Los cambios que hiciste en este texto no se han guardado y se perder\u00E1n.\",\n confirmDiscard: \"Descartar\",\n confirmKeepEditing: \"Seguir editando\",\n commentNotFound: \"Ese comentario no est\u00E1 en esta p\u00E1gina.\",\n confirmDelete: \"Eliminar\",\n confirmCancel: \"Cancelar\",\n confirmDeleteCommentTitle: \"\u00BFEliminar comentario?\",\n confirmDeleteCommentMessage:\n \"Este comentario se eliminar\u00E1 de forma permanente. Esta acci\u00F3n no se puede deshacer.\",\n confirmDeleteThreadMessage:\n \"Este comentario y todas sus respuestas se eliminar\u00E1n de forma permanente. Esta acci\u00F3n no se puede deshacer.\",\n confirmDeleteReplyTitle: \"\u00BFEliminar respuesta?\",\n confirmDeleteReplyMessage:\n \"Esta respuesta se eliminar\u00E1 de forma permanente. Esta acci\u00F3n no se puede deshacer.\",\n copyAgentContext: \"Copiar contexto de agente\",\n copied: \"Copiado\",\n statusLabel: \"Estado\",\n prevComment: \"Comentario anterior\",\n nextComment: \"Comentario siguiente\",\n replyLink: \"Responder\",\n replyCountOne: \"1 respuesta\",\n replyCountTemplate: \"{n} respuestas\",\n commentOptions: \"Opciones del comentario\",\n moreOptions: \"M\u00E1s\",\n statusOpen: \"Abierto\",\n statusInProgress: \"En progreso\",\n statusResolved: \"Resuelto\",\n durationLessThanMinute: \"<1m\",\n resolvedInTemplate: \"Resuelto en {n}\",\n typeLabel: \"Tipo\",\n priorityLabel: \"Prioridad\",\n unset: \"Sin definir\",\n typeBug: \"Bug\",\n typeSuggestion: \"Sugerencia\",\n typeQuestion: \"Pregunta\",\n typeImprovement: \"Mejora\",\n priorityHigh: \"Alta\",\n priorityMedium: \"Media\",\n priorityLow: \"Baja\",\n filterByType: \"Tipo\",\n filterByPriority: \"Prioridad\",\n contextSection: \"Contexto\",\n autoScreenshotLabel: \"Contexto autom\u00E1tico\",\n contextUrl: \"URL\",\n contextViewport: \"Viewport\",\n contextScreen: \"Pantalla\",\n contextBrowser: \"Navegador\",\n contextOs: \"SO\",\n};\n", "import en from \"./locales/en.js\";\nimport es from \"./locales/es.js\";\n\nconst LOCALES = { en, es };\nconst DEFAULT_LOCALE = \"en\";\n\n/**\n * Picks a supported locale code from the browser's language, falling back\n * to English for anything HellDots doesn't ship a translation for.\n * @returns {\"en\" | \"es\"}\n */\nexport function detectLocale() {\n const lang = (navigator.language || DEFAULT_LOCALE).slice(0, 2).toLowerCase();\n return lang in LOCALES ? /** @type {\"en\" | \"es\"} */ (lang) : DEFAULT_LOCALE;\n}\n\n/**\n * Resolves the UI strings dictionary for a given locale code, falling back\n * to English if the code isn't one HellDots ships.\n * @param {string} [localeCode]\n * @returns {typeof en}\n */\nexport function getStrings(localeCode) {\n return LOCALES[localeCode] || LOCALES[DEFAULT_LOCALE];\n}\n\n/**\n * Substitutes `{n}` in a template string, used for relative time labels and\n * resolution-duration badges (where the substitution is already a string,\n * e.g. \"2d 4h\" or the \"\u2014\" fallback).\n * @param {string} template\n * @param {number | string} n\n */\nexport function formatTemplate(template, n) {\n return template.replace(\"{n}\", String(n));\n}\n\nconst MINUTE_MS = 60_000;\n\n/**\n * RF5 \u2014 human-readable elapsed time (\"<1m\", \"45m\", \"3h 12m\", \"2d 4h\").\n * Reuses the same {n}-templates the relative timestamps already use.\n * @param {number} ms\n * @param {ReturnType<typeof getStrings>} strings\n * @returns {string} empty string when `ms` isn't a usable duration\n */\nexport function formatDuration(ms, strings) {\n if (!Number.isFinite(ms) || ms < 0) return \"\";\n\n const totalMinutes = Math.floor(ms / MINUTE_MS);\n if (totalMinutes < 1) return strings.durationLessThanMinute;\n if (totalMinutes < 60) {\n return formatTemplate(strings.minutesAgoTemplate, totalMinutes);\n }\n\n const totalHours = Math.floor(totalMinutes / 60);\n if (totalHours < 24) {\n const minutes = totalMinutes % 60;\n const hours = formatTemplate(strings.hoursAgoTemplate, totalHours);\n return minutes\n ? `${hours} ${formatTemplate(strings.minutesAgoTemplate, minutes)}`\n : hours;\n }\n\n const totalDays = Math.floor(totalHours / 24);\n const hours = totalHours % 24;\n const days = formatTemplate(strings.daysAgoTemplate, totalDays);\n return hours\n ? `${days} ${formatTemplate(strings.hoursAgoTemplate, hours)}`\n : days;\n}\n", "// Serializable comment anchors. An anchor pairs a best-effort CSS selector\n// (fast path) with a content fingerprint (verification + rescue path) so a\n// comment can be re-attached to its element after a page reload. Design:\n// docs/superpowers/specs/2026-07-02-comment-anchoring-design.md\n\nimport { HOST_PAGE_CLASSES } from \"./constants.js\";\n\nconst TEXT_SNIPPET_MAX = 64;\nconst MAX_CLASS_PATH_DEPTH = 3;\nconst MAX_STRUCTURAL_DEPTH = 5;\n\n// A selector match is verified against the fingerprint before being trusted;\n// the rescue path (fingerprint-only, no structural signal) demands more.\nconst SELECTOR_THRESHOLD = 0.6;\nconst RESCUE_THRESHOLD = 0.7;\n\nconst GENERATED_CLASS_PREFIX_RE = /^(css|sc|jsx|emotion)-/i;\nconst FRAMEWORK_DATA_ATTR_RE = /^data-(reactid|react-|v-|svelte-)/;\nconst STABLE_ATTR_NAMES = [\"id\", \"name\", \"role\", \"aria-label\"];\nconst SELECTOR_ATTR_NAMES = [\"data-testid\", \"name\", \"aria-label\"];\n\nconst escapeCss = (value) => {\n if (typeof CSS !== \"undefined\" && CSS.escape) return CSS.escape(value);\n return String(value).replace(/[^a-zA-Z0-9_-]/g, \"\\\\$&\");\n};\n\nconst escapeAttrValue = (value) => String(value).replace(/[\"\\\\]/g, \"\\\\$&\");\n\nconst isUnique = (selector, doc) => {\n try {\n return doc.querySelectorAll(selector).length === 1;\n } catch {\n return false;\n }\n};\n\nconst normalizeText = (text) =>\n (text || \"\").replace(/\\s+/g, \" \").trim().slice(0, TEXT_SNIPPET_MAX);\n\n// Heuristic: tooling-generated class names (CSS-in-JS, scoped-CSS hashes)\n// either carry a known prefix or contain a long token with digits in it.\n// HellDots' own host-page classes are excluded outright \u2014 they are our\n// transient state, not the page's structure, and anchoring to them produces\n// a selector that stops matching as soon as the state clears.\nconst isStableClass = (cls) => {\n if (HOST_PAGE_CLASSES.includes(cls)) return false;\n if (GENERATED_CLASS_PREFIX_RE.test(cls)) return false;\n return !cls.split(/[-_]/).some((part) => part.length >= 5 && /\\d/.test(part));\n};\n\nconst stableClassesOf = (element) =>\n [...element.classList].filter(isStableClass);\n\nconst stableAttributes = (element) => {\n /** @type {Record<string, string>} */\n const attrs = {};\n for (const { name, value } of element.attributes) {\n const isStable =\n STABLE_ATTR_NAMES.includes(name) ||\n (name.startsWith(\"data-\") && !FRAMEWORK_DATA_ATTR_RE.test(name));\n if (isStable && value) attrs[name] = value.slice(0, TEXT_SNIPPET_MAX);\n }\n return attrs;\n};\n\nconst siblingPosition = (element) => {\n const parent = element.parentElement;\n if (!parent) return { index: 0, count: 1 };\n const sameTag = [...parent.children].filter(\n (child) => child.tagName === element.tagName\n );\n return { index: sameTag.indexOf(element), count: sameTag.length };\n};\n\nconst idSelector = (element, doc) => {\n if (!element.id) return null;\n const selector = `#${escapeCss(element.id)}`;\n return isUnique(selector, doc) ? selector : null;\n};\n\nconst attributeSelector = (element, doc) => {\n const tag = element.tagName.toLowerCase();\n for (const name of SELECTOR_ATTR_NAMES) {\n const value = element.getAttribute(name);\n if (!value) continue;\n const selector = `${tag}[${name}=\"${escapeAttrValue(value)}\"]`;\n if (isUnique(selector, doc)) return selector;\n }\n return null;\n};\n\nconst classPathSelector = (element, doc) => {\n const segments = [];\n let current = element;\n for (let depth = 0; depth < MAX_CLASS_PATH_DEPTH && current; depth++) {\n const classes = stableClassesOf(current);\n const tag = current.tagName.toLowerCase();\n segments.unshift(\n classes.length ? `${tag}.${classes.map(escapeCss).join(\".\")}` : tag\n );\n // The element's own segment must carry at least one stable class for\n // this strategy to say anything a structural path wouldn't.\n if (depth === 0 && !classes.length) return null;\n const selector = segments.join(\" > \");\n if (isUnique(selector, doc)) return selector;\n current = current.parentElement;\n }\n return null;\n};\n\nconst structuralSelector = (element, doc) => {\n if (element === doc.body) return \"body\";\n const segments = [];\n let current = element;\n for (let depth = 0; depth < MAX_STRUCTURAL_DEPTH && current; depth++) {\n if (current === doc.body) {\n segments.unshift(\"body\");\n break;\n }\n if (current.id) {\n const rooted = [`#${escapeCss(current.id)}`, ...segments].join(\" > \");\n if (isUnique(rooted, doc)) return rooted;\n }\n const { index } = siblingPosition(current);\n segments.unshift(\n `${current.tagName.toLowerCase()}:nth-of-type(${index + 1})`\n );\n current = current.parentElement;\n }\n const selector = segments.join(\" > \");\n return isUnique(selector, doc) ? selector : null;\n};\n\nconst generateSelector = (element, doc) =>\n idSelector(element, doc) ||\n attributeSelector(element, doc) ||\n classPathSelector(element, doc) ||\n structuralSelector(element, doc);\n\n/**\n * Best-effort unique CSS selector for any element (or null). Exposed for\n * the overlay's target-visibility tracking; same cascade used by anchors.\n * @param {HTMLElement} element\n * @returns {string | null}\n */\nexport function generateElementSelector(element) {\n return generateSelector(element, element.ownerDocument);\n}\n\n/**\n * Captures a serializable anchor for `element` at creation time.\n * @param {HTMLElement} element\n * @param {number} relativeX\n * @param {number} relativeY\n * @returns {import('./index.d.ts').CommentAnchor}\n */\nexport function createAnchor(element, relativeX, relativeY) {\n const doc = element.ownerDocument;\n const { index, count } = siblingPosition(element);\n return {\n version: 1,\n selector: generateSelector(element, doc),\n fingerprint: {\n tagName: element.tagName,\n textSnippet: normalizeText(element.textContent),\n attributes: stableAttributes(element),\n siblingIndex: index,\n siblingCount: count,\n },\n relativeX,\n relativeY,\n };\n}\n\nconst textSimilarity = (a, b) => {\n if (!a && !b) return 1;\n if (!a || !b) return 0;\n if (a === b) return 1;\n if (a.startsWith(b) || b.startsWith(a)) return 0.8;\n const tokensA = new Set(a.split(\" \"));\n const tokensB = new Set(b.split(\" \"));\n let common = 0;\n for (const token of tokensA) if (tokensB.has(token)) common++;\n return (2 * common) / (tokensA.size + tokensB.size);\n};\n\nconst attributeSimilarity = (element, attrs) => {\n const names = Object.keys(attrs);\n if (!names.length) return 1;\n let matched = 0;\n for (const name of names) {\n if (\n (element.getAttribute(name) || \"\").slice(0, TEXT_SNIPPET_MAX) ===\n attrs[name]\n ) {\n matched++;\n }\n }\n return matched / names.length;\n};\n\nconst positionSimilarity = (element, fingerprint) => {\n const { index, count } = siblingPosition(element);\n const delta = Math.abs(index - fingerprint.siblingIndex);\n const span = Math.max(fingerprint.siblingCount, count, 1);\n return Math.max(0, 1 - delta / span);\n};\n\nconst scoreElement = (element, fingerprint) => {\n if (element.tagName !== fingerprint.tagName) return 0;\n\n const hasText = Boolean(fingerprint.textSnippet);\n const hasAttrs = Object.keys(fingerprint.attributes || {}).length > 0;\n\n // Base weights (text 0.5 / attrs 0.3 / position 0.2); a missing signal's\n // weight shifts to the other content signal so the scale stays 0\u20131.\n let textWeight = 0.5;\n let attrWeight = 0.3;\n const posWeight = 0.2;\n if (!hasAttrs) {\n textWeight += attrWeight;\n attrWeight = 0;\n } else if (!hasText) {\n attrWeight += textWeight;\n textWeight = 0;\n }\n\n // Degenerate fingerprint: only structural signal remains.\n if (!hasText && !hasAttrs) {\n return positionSimilarity(element, fingerprint);\n }\n\n return (\n textWeight *\n textSimilarity(\n normalizeText(element.textContent),\n fingerprint.textSnippet\n ) +\n attrWeight * attributeSimilarity(element, fingerprint.attributes || {}) +\n posWeight * positionSimilarity(element, fingerprint)\n );\n};\n\nconst bestMatch = (candidates, fingerprint) => {\n let best = null;\n for (const element of candidates) {\n const confidence = scoreElement(element, fingerprint);\n if (!best || confidence > best.confidence) best = { element, confidence };\n }\n return best;\n};\n\n/**\n * Re-locates the element an anchor points at, or null if no candidate is\n * trustworthy (orphaned comment). Never throws.\n * @param {import('./index.d.ts').CommentAnchor} anchor\n * @param {Document} [doc]\n * @returns {{ element: HTMLElement, confidence: number } | null}\n */\nexport function resolveAnchor(anchor, doc = document) {\n const fingerprint = anchor?.fingerprint;\n if (!fingerprint || !fingerprint.tagName) return null;\n\n if (anchor.selector) {\n let candidates = [];\n try {\n candidates = [...doc.querySelectorAll(anchor.selector)];\n } catch {\n // Corrupt selector \u2014 the rescue search below still applies.\n }\n const best = bestMatch(candidates, fingerprint);\n if (best && best.confidence >= SELECTOR_THRESHOLD) return best;\n }\n\n // Rescue search: only meaningful when the fingerprint carries content\n // signal \u2014 anonymous elements would make any tag-wide match a guess.\n const hasSignal =\n Boolean(fingerprint.textSnippet) ||\n Object.keys(fingerprint.attributes || {}).length > 0;\n if (!hasSignal) return null;\n\n let candidates;\n try {\n candidates = [...doc.querySelectorAll(fingerprint.tagName)];\n } catch {\n return null;\n }\n const best = bestMatch(candidates, fingerprint);\n return best && best.confidence >= RESCUE_THRESHOLD ? best : null;\n}\n", "// localStorage adapter for the optional `persistence: \"localStorage\"` mode.\n// One key holds the serialized comments of EVERY page (the inbox \"all\n// comments\" filter needs them); merge logic keeps other pages' entries\n// intact while treating in-memory state as the source of truth for ids it\n// knows about. Storage failures (quota, disabled, corrupt JSON) never\n// throw \u2014 the widget just runs without persistence.\n\nexport const STORAGE_KEY = \"helldots-comments\";\n\n// sessionStorage handoff: set right before navigating to another page so\n// the overlay there opens the inbox directly on that comment's detail.\nexport const PENDING_DETAIL_KEY = \"helldots-pending-detail\";\n\n/**\n * @returns {import('./index.d.ts').SerializedComment[]}\n */\nexport function readStoredComments() {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return [];\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch (err) {\n console.warn(\"HellDots: could not read stored comments\", err);\n return [];\n }\n}\n\nfunction tryWriteStoredComments(comments) {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(comments));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Writes the full cross-page corpus. Each comment can carry a base64 JPEG\n * `contextScreenshot` (~33KB) captured automatically (RF1/RF2); a growing\n * corpus of those eventually blows through the ~5MB localStorage quota. On\n * a failed write, the automatic screenshots are the only thing sacrificed \u2014\n * dropped one at a time starting with the oldest comment \u2014 so the comments\n * themselves (and any deliberate, user-attached `screenshots[]`) survive.\n * Never throws: a hostile or disabled localStorage just means no\n * persistence, not a broken widget.\n * @param {import('./index.d.ts').SerializedComment[]} comments\n * @returns {boolean} true once the write succeeded, possibly after shedding\n * automatic screenshots; false if it still failed with nothing left to shed\n */\nexport function writeStoredComments(comments) {\n if (tryWriteStoredComments(comments)) return true;\n\n // Oldest first (by createdAt; ties/missing dates fall back to array\n // order), and only entries that actually have something to shed.\n const shedOrder = comments\n .map((comment, index) => ({ comment, index }))\n .filter(({ comment }) => comment?.contextScreenshot)\n .sort((a, b) => {\n const timeA = Date.parse(a.comment.createdAt);\n const timeB = Date.parse(b.comment.createdAt);\n if (Number.isFinite(timeA) && Number.isFinite(timeB) && timeA !== timeB) {\n return timeA - timeB;\n }\n return a.index - b.index;\n });\n\n if (shedOrder.length === 0) {\n console.warn(\n \"HellDots: could not persist comments (storage quota exceeded, nothing left to shed)\"\n );\n return false;\n }\n\n const working = [...comments];\n let shed = 0;\n for (const { index } of shedOrder) {\n working[index] = { ...working[index], contextScreenshot: null };\n shed++;\n if (tryWriteStoredComments(working)) {\n console.warn(\n `HellDots: localStorage quota exceeded \u2014 dropped the automatic ` +\n `context screenshot from the ${shed} oldest comment(s) to keep ` +\n `all comments persisted. Comment text, replies and user-attached ` +\n `screenshots were not touched.`\n );\n return true;\n }\n }\n\n console.warn(\n `HellDots: could not persist comments even after dropping all ${shed} ` +\n `automatic context screenshot(s); storage quota exceeded`\n );\n return false;\n}\n\n/**\n * Merges the in-memory snapshot into what's already stored: entries the\n * memory knows about (by id) and entries of the current page are replaced\n * by the snapshot; entries from other pages are preserved.\n * @param {import('./index.d.ts').SerializedComment[]} stored\n * @param {import('./index.d.ts').SerializedComment[]} current\n * @param {string} currentPage\n * @returns {import('./index.d.ts').SerializedComment[]}\n */\nexport function mergeForStorage(stored, current, currentPage) {\n const currentIds = new Set(current.map((c) => c.id));\n const kept = stored.filter(\n (c) => !currentIds.has(c.id) && c.page !== currentPage\n );\n return [...kept, ...current];\n}\n", "export let urlAlphabet =\n 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'\n", "\n\nimport { urlAlphabet } from './url-alphabet/index.js'\n\nexport { urlAlphabet }\n\nexport let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))\n\nexport let customRandom = (alphabet, defaultSize, getRandom) => {\n let safeByteCutoff = 256 - (256 % alphabet.length)\n\n if (safeByteCutoff === 256) {\n let mask = alphabet.length - 1\n\n return (size = defaultSize) => {\n if (!size) return ''\n let id = ''\n while (true) {\n let bytes = getRandom(size)\n let j = size\n while (j--) {\n id += alphabet[bytes[j] & mask]\n if (id.length >= size) return id\n }\n }\n }\n }\n\n let step = Math.ceil((1.6 * 256 * defaultSize) / safeByteCutoff)\n\n return (size = defaultSize) => {\n if (!size) return ''\n let id = ''\n while (true) {\n let bytes = getRandom(step)\n let j = step\n while (j--) {\n if (bytes[j] < safeByteCutoff) {\n id += alphabet[bytes[j] % alphabet.length]\n if (id.length >= size) return id\n }\n }\n }\n }\n}\n\nexport let customAlphabet = (alphabet, size = 21) =>\n customRandom(alphabet, size | 0, random)\n\nexport let nanoid = (size = 21) => {\n let id = ''\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)))\n while (size--) {\n id += urlAlphabet[bytes[size] & 63]\n }\n return id\n}\n", "// The single source of ids for comments and replies.\n//\n// This used to be `Date.now()`, which is not an id \u2014 it is a timestamp that\n// usually happens not to repeat. Two things it was already breaking:\n// `mergeForStorage` deduplicates by id, and every lookup is a `find()` that\n// returns the first match, so a collision means one comment silently\n// overwrites another. Two people commenting on the same millisecond from\n// different machines, or any programmatic import, is enough to hit it.\n//\n// nanoid gives ~126 bits from a 64-symbol URL-safe alphabet in 21 chars \u2014\n// stronger than a UUIDv4's 122 bits, and short enough to sit in the\n// `?helldotsComment=` link without looking like a mistake. It reads its\n// randomness from `crypto.getRandomValues`, which \u2014 unlike\n// `crypto.randomUUID` \u2014 is NOT restricted to secure contexts, so a widget\n// dropped into a dev server on plain http://192.168.x.x still works.\n//\n// It is a devDependency bundled into both artifacts rather than a runtime\n// dependency: nanoid 6 requires Node 22+, and this package promises >=18.\n// Bundling keeps that promise honest for hosts importing us under SSR, and\n// costs ~516 B gzip against a 50 KB budget.\nimport { nanoid } from \"nanoid\";\n\n/**\n * @returns {string} a fresh id for a comment or a reply\n */\nexport const createId = () => nanoid();\n", "// Deep links to a single comment.\n//\n// There is no redirect hop here on purpose. Hosted tools can afford one\n// because their back end knows which deployment a thread belongs to and has\n// to resolve that before it can send you anywhere. HellDots has no server:\n// the page a comment lives on is already recorded on the comment, so the\n// link can point straight at its final destination.\n\nexport const DEFAULT_LINK_PARAM = \"helldotsComment\";\n\n/**\n * The shareable URL for one comment.\n *\n * For a comment on the page the user is currently looking at, this keeps the\n * rest of the current URL \u2014 query and hash included. That matters more than\n * it looks: a comment left on `/products?filter=archived` is *about* that\n * filtered view, and a link that drops the filter lands the reader somewhere\n * the comment does not make sense. For other pages only `comment.page` is\n * known (it stores `location.pathname`), so that is all the link can carry.\n *\n * @param {{ id: import('./index.d.ts').CommentId, page?: string }} comment\n * @param {string} [param]\n * @param {string} [href] current document URL; injectable for tests\n * @returns {string}\n */\nexport const buildCommentLink = (\n comment,\n param = DEFAULT_LINK_PARAM,\n href = location.href\n) => {\n const current = new URL(href);\n const page = comment.page || current.pathname;\n const url = page === current.pathname ? current : new URL(page, current);\n url.searchParams.set(param, String(comment.id));\n return url.href;\n};\n\n/**\n * The comment id requested by the current URL, if any.\n * @param {string} [param]\n * @param {string} [href]\n * @returns {string | null}\n */\nexport const readCommentLinkParam = (\n param = DEFAULT_LINK_PARAM,\n href = location.href\n) => {\n try {\n return new URL(href).searchParams.get(param);\n } catch {\n // A malformed URL is not worth breaking startup over \u2014 the widget just\n // opens without honouring a link it could not read.\n return null;\n }\n};\n", "// Shared open/close rule for every dropdown in the widget: the status, type\n// and priority pickers, the \u22EF menu and the inbox filter.\n//\n// Each menu used to own its state in isolation *and* stop the click from\n// propagating, so nothing could ever close one except its own button. Opening\n// a second picker left the first hanging open (three menus could overlap at\n// once), and clicking elsewhere inside the panel closed none of them.\n//\n// The registry gives them one rule instead: at most one menu open, and any\n// mousedown outside the open menu closes it. The outside listener is on\n// `document` in the CAPTURE phase precisely because the toggles call\n// stopPropagation() \u2014 a bubble-phase listener would never hear the click.\n\n/**\n * @typedef {{ button: HTMLElement, menu: HTMLElement, close: () => void }} MenuEntry\n */\n\n/** @type {Set<MenuEntry>} */\nconst openMenus = new Set();\n\n/** @type {((e: MouseEvent) => void) | null} */\nlet outsideListener = null;\n\n// Menus live inside the shadow root, so `e.target` on a document listener is\n// retargeted to the host. composedPath() is the only way to see the element\n// that was really clicked.\nconst eventHits = (el, e) => {\n const path = typeof e.composedPath === \"function\" ? e.composedPath() : [];\n return path.includes(el) || el.contains(/** @type {Node} */ (e.target));\n};\n\nconst startWatching = () => {\n if (outsideListener) return;\n outsideListener = (e) => {\n for (const entry of [...openMenus]) {\n if (eventHits(entry.menu, e) || eventHits(entry.button, e)) continue;\n // A menu detached by a re-render is never in the click path, so this\n // also drops stale entries \u2014 which is what eventually releases this\n // very listener.\n entry.close();\n }\n };\n document.addEventListener(\"mousedown\", outsideListener, true);\n};\n\nconst stopWatching = () => {\n if (!outsideListener) return;\n document.removeEventListener(\"mousedown\", outsideListener, true);\n outsideListener = null;\n};\n\n/** Closes every open menu. Safe to call when none are open. */\nexport const closeOpenMenus = () => {\n for (const entry of [...openMenus]) entry.close();\n};\n\n/**\n * Wires a button to its dropdown so it participates in the single-open rule.\n * The menu's `display` stays the source of truth, so callers that hide it by\n * hand stay consistent with the registry.\n *\n * @param {HTMLElement} button\n * @param {HTMLElement} menu\n * @returns {{ open: () => void, close: () => void, isOpen: () => boolean }}\n */\nexport const attachMenuToggle = (button, menu) => {\n /** @type {MenuEntry} */\n const entry = {\n button,\n menu,\n close: () => {\n menu.style.display = \"none\";\n button.setAttribute(\"aria-expanded\", \"false\");\n openMenus.delete(entry);\n if (openMenus.size === 0) stopWatching();\n },\n };\n\n const open = () => {\n closeOpenMenus();\n menu.style.display = \"block\";\n button.setAttribute(\"aria-expanded\", \"true\");\n openMenus.add(entry);\n startWatching();\n };\n\n const isOpen = () => menu.style.display !== \"none\";\n\n menu.style.display = \"none\";\n button.setAttribute(\"aria-expanded\", \"false\");\n\n button.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n // Read the state off the DOM, not off the set: a menu hidden directly by\n // a caller (or detached by a re-render) would otherwise be stuck\n // \"open\" in the registry and refuse to reopen.\n const wasOpen = isOpen();\n closeOpenMenus();\n if (!wasOpen) open();\n });\n\n return { open, close: entry.close, isOpen };\n};\n", "// A modal confirmation for the destructive actions. Deleting a comment or a\n// reply is the only thing in the widget that cannot be undone \u2014 there is no\n// trash and no history \u2014 and until now a single click on a menu item did it.\n//\n// Deliberately promise-based rather than callback-based: every call site\n// reads as \"ask, then act\", and the \"user said no\" path is a plain early\n// return instead of a second callback that does nothing.\n\nimport { CLASSES, Z_INDEX } from \"./constants.js\";\n\n/**\n * @typedef {{\n * title: string,\n * message: string,\n * confirmLabel: string,\n * cancelLabel: string,\n * }} ConfirmStrings\n */\n\n/**\n * Open dialogs, so teardown can settle them. Without this, unmounting the\n * widget mid-question would take the DOM away and leave the capture-phase\n * keydown listener on `document` swallowing Escape for the whole page.\n * @type {Set<(result: boolean) => void>}\n */\nconst openDialogs = new Set();\n\n/** Dismisses every open dialog as if the user had cancelled. */\nexport const closeOpenConfirmDialogs = () => {\n for (const dismiss of [...openDialogs]) dismiss(false);\n};\n\n/**\n * Opens the dialog and resolves once the user answers.\n *\n * @param {ShadowRoot | HTMLElement} host where to mount \u2014 pass the widget's\n * shadow root so the dialog inherits its styles and stacking context\n * @param {ConfirmStrings} strings all pre-localized, like every other view\n * builder here\n * @returns {Promise<boolean>} true only when the user confirms\n */\nexport const confirmDialog = (\n host,\n { title, message, confirmLabel, cancelLabel }\n) =>\n new Promise((resolve) => {\n const backdrop = document.createElement(\"div\");\n backdrop.className = CLASSES.CONFIRM;\n backdrop.style.zIndex = String(Z_INDEX.CONFIRM);\n\n const panel = document.createElement(\"div\");\n panel.className = CLASSES.CONFIRM_PANEL;\n panel.setAttribute(\"role\", \"alertdialog\");\n panel.setAttribute(\"aria-modal\", \"true\");\n\n const titleEl = document.createElement(\"h2\");\n titleEl.className = CLASSES.CONFIRM_TITLE;\n titleEl.textContent = title;\n // Generated rather than a constant id: two dialogs must never claim the\n // same one, and the shadow root is shared with everything else.\n titleEl.id = `hd-confirm-title-${Math.random().toString(36).slice(2, 9)}`;\n panel.setAttribute(\"aria-labelledby\", titleEl.id);\n\n const messageEl = document.createElement(\"p\");\n messageEl.className = CLASSES.CONFIRM_MESSAGE;\n messageEl.textContent = message;\n\n const actions = document.createElement(\"div\");\n actions.className = CLASSES.CONFIRM_ACTIONS;\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = CLASSES.CONFIRM_CANCEL;\n cancelBtn.textContent = cancelLabel;\n\n const acceptBtn = document.createElement(\"button\");\n acceptBtn.type = \"button\";\n acceptBtn.className = CLASSES.CONFIRM_ACCEPT;\n acceptBtn.textContent = confirmLabel;\n\n actions.appendChild(cancelBtn);\n actions.appendChild(acceptBtn);\n panel.appendChild(titleEl);\n panel.appendChild(messageEl);\n panel.appendChild(actions);\n backdrop.appendChild(panel);\n\n // Restored on close: the menu item that opened this is gone by then, so\n // without it focus would fall back to <body> and the keyboard user would\n // lose their place entirely.\n const previouslyFocused = /** @type {any} */ (\n /** @type {any} */ (host).activeElement || document.activeElement\n );\n\n let settled = false;\n const settle = (result) => {\n if (settled) return;\n settled = true;\n openDialogs.delete(settle);\n document.removeEventListener(\"keydown\", onKeydown, true);\n backdrop.remove();\n previouslyFocused?.focus?.();\n resolve(result);\n };\n\n // Capture phase, and it stops the event: the overlay's own Escape handler\n // is on document and would otherwise close the thread popover behind the\n // dialog while the question was still on screen.\n const onKeydown = (/** @type {KeyboardEvent} */ e) => {\n if (e.key === \"Escape\") {\n e.stopPropagation();\n e.preventDefault();\n settle(false);\n return;\n }\n if (e.key !== \"Tab\") return;\n // aria-modal is a claim about focus, so it has to be true. Only two\n // stops, which makes the trap a swap rather than a ring walk.\n const focusables = [cancelBtn, acceptBtn];\n const active = /** @type {any} */ (\n /** @type {any} */ (host).activeElement || document.activeElement\n );\n const index = focusables.indexOf(active);\n e.preventDefault();\n const next = e.shiftKey\n ? focusables[(index <= 0 ? focusables.length : index) - 1]\n : focusables[(index + 1) % focusables.length];\n next.focus();\n };\n\n cancelBtn.addEventListener(\"click\", () => settle(false));\n acceptBtn.addEventListener(\"click\", () => settle(true));\n // Only a press that both starts and ends on the backdrop dismisses, so a\n // drag that happens to release outside the panel does not answer for the\n // user.\n let pressedBackdrop = false;\n backdrop.addEventListener(\"mousedown\", (e) => {\n pressedBackdrop = e.target === backdrop;\n // The inbox and the thread popover both close on any mousedown outside\n // themselves; without this they tear down behind the dialog.\n e.stopPropagation();\n });\n backdrop.addEventListener(\"click\", (e) => {\n if (e.target === backdrop && pressedBackdrop) settle(false);\n pressedBackdrop = false;\n });\n\n openDialogs.add(settle);\n document.addEventListener(\"keydown\", onKeydown, true);\n // Callers reach us through getRootNode(), which is the Document \u2014 not a\n // shadow root \u2014 for anything mounted in the light DOM. A Document cannot\n // take a second element child, so mount into its body instead.\n const mountPoint = /** @type {any} */ (host).body || host;\n mountPoint.appendChild(backdrop);\n // Cancel, not confirm: the destructive button should never be one stray\n // Enter away.\n cancelBtn.focus();\n });\n", "// Shared per-comment action strip (copy agent context / lifecycle status /\n// more menu) used by both the inbox cards and the thread popover header.\n// Pure view: every mutation goes through the callbacks; the component only\n// keeps its own dot color and tooltips in sync after a selection.\n\nimport {\n CLASSES,\n STATUSES,\n STATUS_COLORS,\n COMMENT_TYPES,\n TYPE_COLORS,\n PRIORITIES,\n PRIORITY_COLORS,\n} from \"./constants.js\";\nimport { attachMenuToggle } from \"./menus.js\";\nimport { confirmDialog } from \"./confirm-dialog.js\";\n\nconst COPY_ICON_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg>`;\nconst CHECK_ICON_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\nconst DOTS_ICON_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><circle cx=\"5\" cy=\"12\" r=\"1.6\"/><circle cx=\"12\" cy=\"12\" r=\"1.6\"/><circle cx=\"19\" cy=\"12\" r=\"1.6\"/></svg>`;\n\nexport const copyToClipboard = (text) => {\n if (navigator.clipboard?.writeText) {\n return navigator.clipboard.writeText(text).catch(() => {});\n }\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n document.execCommand(\"copy\");\n } catch {}\n textarea.remove();\n return Promise.resolve();\n};\n\nexport const statusLabelOf = (status, strings) =>\n ({\n open: strings.statusOpen,\n in_progress: strings.statusInProgress,\n resolved: strings.statusResolved,\n })[status] || strings.statusOpen;\n\nexport const typeLabelOf = (type, strings) =>\n ({\n bug: strings.typeBug,\n suggestion: strings.typeSuggestion,\n question: strings.typeQuestion,\n improvement: strings.typeImprovement,\n })[type] || strings.unset;\n\nexport const priorityLabelOf = (priority, strings) =>\n ({\n high: strings.priorityHigh,\n medium: strings.priorityMedium,\n low: strings.priorityLow,\n })[priority] || strings.unset;\n\n/**\n * Dot-and-menu picker shared by the status, type and priority controls.\n * Keeps its own copy of the selection so the UI stays correct even when the\n * consumer's onSelect is async or doesn't mutate the comment in place.\n * @param {{\n * action: string,\n * options: Array<string|null>,\n * value: string|null,\n * colorOf: (option: string|null) => string,\n * labelOf: (option: string|null) => string,\n * tooltipLabel: string,\n * onSelect: (option: string|null) => void,\n * showLabel?: boolean,\n * }} config\n * @returns {HTMLElement}\n */\nexport const createPicker = ({\n action,\n options,\n value,\n colorOf,\n labelOf,\n tooltipLabel,\n onSelect,\n showLabel = false,\n}) => {\n const wrapper = document.createElement(\"div\");\n wrapper.style.position = \"relative\";\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_ACTION_BTN;\n if (showLabel) btn.classList.add(CLASSES.INBOX_ACTION_BTN_LABELED);\n btn.dataset.action = action;\n btn.setAttribute(\"aria-haspopup\", \"true\");\n\n const dot = document.createElement(\"span\");\n dot.className = CLASSES.INBOX_STATUS_DOT;\n btn.appendChild(dot);\n\n // Type and priority share exact colours (bug === high === #FF453A), and\n // unset shares \"no colour\" with unset \u2014 the dot alone can't tell them\n // apart, and hover-only disambiguation doesn't exist on touch. A short\n // text label next to the dot makes the current value legible without it.\n let labelEl = null;\n if (showLabel) {\n labelEl = document.createElement(\"span\");\n labelEl.className = CLASSES.INBOX_ACTION_LABEL;\n btn.appendChild(labelEl);\n }\n\n const menu = document.createElement(\"div\");\n menu.className = CLASSES.INBOX_MENU;\n menu.setAttribute(\"role\", \"menu\");\n\n const toggle = attachMenuToggle(btn, menu);\n\n let current = value;\n\n const syncUi = () => {\n const label = `${tooltipLabel}: ${labelOf(current)}`;\n dot.style.backgroundColor = colorOf(current);\n btn.dataset.hdTooltip = label;\n btn.setAttribute(\"aria-label\", label);\n if (labelEl) labelEl.textContent = labelOf(current);\n menu\n .querySelectorAll(\"[data-picker-option]\")\n .forEach((/** @type {HTMLElement} */ item) => {\n const raw = item.dataset.pickerOption;\n const option = raw === \"\" ? null : raw;\n item.setAttribute(\"aria-checked\", String(option === current));\n });\n };\n\n for (const option of options) {\n const item = document.createElement(\"button\");\n item.type = \"button\";\n item.className = CLASSES.INBOX_MENU_ITEM;\n // \"\" is how a null option round-trips through a dataset string.\n item.dataset.pickerOption = option === null ? \"\" : option;\n item.setAttribute(\"role\", \"menuitemradio\");\n\n const itemDot = document.createElement(\"span\");\n itemDot.className = CLASSES.INBOX_STATUS_DOT;\n itemDot.style.backgroundColor = colorOf(option);\n item.appendChild(itemDot);\n item.appendChild(document.createTextNode(labelOf(option)));\n\n item.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n toggle.close();\n // No-op: re-picking the option that's already selected must not fire\n // onSelect \u2014 for the status picker that would re-stamp resolvedAt on\n // every redundant \"Resolved\" click, destroying RF5's elapsed time.\n if (option === current) return;\n current = option;\n onSelect(option);\n syncUi();\n });\n menu.appendChild(item);\n }\n\n wrapper.appendChild(btn);\n wrapper.appendChild(menu);\n syncUi();\n return wrapper;\n};\n\n/**\n * The \u22EF dropdown, shared by the comment action strip and by each reply row.\n * One builder so both stay identical \u2014 same button, same menu chrome, same\n * single-open rule from menus.js \u2014 instead of two copies drifting apart.\n *\n * `tooltip` is optional, and the reply rows deliberately go without one: the\n * hover bubble is an absolutely positioned ::after on a button flush against\n * the right edge of `.thread-scroll`, and it stuck ~9px past it \u2014 enough to\n * give the thread a horizontal scrollbar it had no other reason to have. The\n * aria-label still names the control, and the menu it opens says the rest.\n *\n * An item may carry a `confirm` factory, and then nothing happens until the\n * user answers the modal. It lives here rather than at each call site so a\n * destructive item cannot be added without one being considered.\n *\n * A factory, not a plain object: the wording depends on the comment's state\n * (\"and all of its replies\"), and the menu is built once when the popover\n * opens. Reading it up front described the thread as it was minutes ago.\n *\n * @param {{\n * label: string,\n * tooltip?: string,\n * items: Array<{\n * label: string,\n * onSelect: () => void,\n * confirm?: () => import(\"./confirm-dialog.js\").ConfirmStrings,\n * feedbackLabel?: string,\n * }>,\n * }} config\n * @returns {HTMLElement}\n */\nexport const createMoreMenu = ({ label, tooltip, items }) => {\n const wrapper = document.createElement(\"div\");\n wrapper.style.position = \"relative\";\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_ACTION_BTN;\n btn.dataset.action = \"menu\";\n if (tooltip) btn.dataset.hdTooltip = tooltip;\n btn.setAttribute(\"aria-label\", label);\n btn.innerHTML = DOTS_ICON_SVG;\n\n const menu = document.createElement(\"div\");\n menu.className = CLASSES.INBOX_MENU;\n menu.setAttribute(\"role\", \"menu\");\n\n const toggle = attachMenuToggle(btn, menu);\n\n for (const entry of items) {\n const item = document.createElement(\"button\");\n item.type = \"button\";\n item.className = CLASSES.INBOX_MENU_ITEM;\n item.setAttribute(\"role\", \"menuitem\");\n item.textContent = entry.label;\n item.addEventListener(\"click\", async (e) => {\n e.stopPropagation();\n // Copying to the clipboard succeeds invisibly. Closing the menu at once\n // would leave the user with no evidence it happened, and the icon-swap\n // trick the copy button uses needs a control that stays on screen \u2014 so\n // the item says so itself and the menu waits before closing.\n if (entry.feedbackLabel) {\n entry.onSelect();\n item.textContent = entry.feedbackLabel;\n setTimeout(() => {\n item.textContent = entry.label;\n toggle.close();\n }, 1200);\n return;\n }\n toggle.close();\n if (entry.confirm) {\n // Read before awaiting: onSelect may detach the row this button\n // lives in, and a detached node has no shadow root to mount into.\n const host = /** @type {any} */ (item.getRootNode());\n if (!(await confirmDialog(host, entry.confirm()))) return;\n }\n entry.onSelect();\n });\n menu.appendChild(item);\n }\n\n wrapper.appendChild(btn);\n wrapper.appendChild(menu);\n return wrapper;\n};\n\n/**\n * @param {Object} comment\n * @param {{ strings: Object, onCopy: Function, onCopyLink?: Function, onEdit?: Function, onSetStatus: Function, onSetType: Function, onSetPriority: Function, onDelete: Function }} deps\n * @returns {HTMLElement}\n */\nexport const createCommentActions = (\n comment,\n {\n strings,\n onCopy,\n onCopyLink,\n onEdit,\n onSetStatus,\n onSetType,\n onSetPriority,\n onDelete,\n }\n) => {\n const actions = document.createElement(\"div\");\n actions.className = CLASSES.INBOX_CARD_ACTIONS;\n\n // --- copy agent context ---\n const copyBtn = document.createElement(\"button\");\n copyBtn.type = \"button\";\n copyBtn.className = CLASSES.INBOX_ACTION_BTN;\n copyBtn.dataset.action = \"copy\";\n copyBtn.dataset.hdTooltip = strings.copyAgentContext;\n copyBtn.setAttribute(\"aria-label\", strings.copyAgentContext);\n copyBtn.innerHTML = COPY_ICON_SVG;\n copyBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onCopy(comment);\n copyBtn.innerHTML = CHECK_ICON_SVG;\n copyBtn.dataset.hdTooltip = strings.copied;\n setTimeout(() => {\n copyBtn.innerHTML = COPY_ICON_SVG;\n copyBtn.dataset.hdTooltip = strings.copyAgentContext;\n }, 1500);\n });\n actions.appendChild(copyBtn);\n\n // --- lifecycle status picker (RF09) ---\n actions.appendChild(\n createPicker({\n action: \"status\",\n options: STATUSES,\n value: comment.status || \"open\",\n colorOf: (status) => STATUS_COLORS[status] || \"\",\n labelOf: (status) => statusLabelOf(status, strings),\n tooltipLabel: strings.statusLabel,\n onSelect: (status) => onSetStatus(comment, status),\n // Labelled like type and priority: the strip is on its own row now, so\n // there is room, and a lone coloured dot needed a hover to be read \u2014\n // which touch never provides.\n showLabel: true,\n })\n );\n\n // --- category picker (RF3) ---\n actions.appendChild(\n createPicker({\n action: \"type\",\n // `null` first: returning to the neutral state must be reachable.\n options: [null, ...COMMENT_TYPES],\n value: comment.type || null,\n colorOf: (type) => TYPE_COLORS[type] || \"transparent\",\n labelOf: (type) => typeLabelOf(type, strings),\n tooltipLabel: strings.typeLabel,\n onSelect: (type) => onSetType?.(comment, type),\n showLabel: true,\n })\n );\n\n // --- priority picker (RF4) ---\n actions.appendChild(\n createPicker({\n action: \"priority\",\n options: [null, ...PRIORITIES],\n value: comment.priority || null,\n colorOf: (priority) => PRIORITY_COLORS[priority] || \"transparent\",\n labelOf: (priority) => priorityLabelOf(priority, strings),\n tooltipLabel: strings.priorityLabel,\n onSelect: (priority) => onSetPriority?.(comment, priority),\n showLabel: true,\n })\n );\n\n // --- more (\u22EF) menu ---\n actions.appendChild(\n createMoreMenu({\n label: strings.commentOptions,\n tooltip: strings.moreOptions,\n items: [\n {\n label: strings.copyLink,\n feedbackLabel: strings.linkCopied,\n onSelect: () => onCopyLink?.(comment),\n },\n {\n label: strings.editComment,\n onSelect: () => onEdit?.(comment),\n },\n {\n label: strings.deleteComment,\n onSelect: () => onDelete(comment),\n confirm: () => ({\n title: strings.confirmDeleteCommentTitle,\n // Two wordings rather than a reply count: what matters is that a\n // discussion is about to go with the comment, and saying so\n // avoids pluralising a number in every locale.\n message: comment.replies?.length\n ? strings.confirmDeleteThreadMessage\n : strings.confirmDeleteCommentMessage,\n confirmLabel: strings.confirmDelete,\n cancelLabel: strings.confirmCancel,\n }),\n },\n ],\n })\n );\n\n return actions;\n};\n", "// The inline editor that replaces a comment or reply body while it is being\n// edited, plus the one question asked before an unsaved draft is thrown away.\n//\n// This component is deliberately dumb: it renders a draft and reports every\n// keystroke back. It does NOT own the draft. The panels re-render constantly\n// \u2014 ten `render()` call sites in the inbox alone, plus seven `refresh()`\n// calls from the overlay \u2014 and a draft living in this DOM would be destroyed\n// by any of them, silently, mid-sentence. So the owner keeps the draft as\n// state (the same reason `detailId` is state) and hands it back on rebuild.\n\nimport { CLASSES } from \"./constants.js\";\nimport { confirmDialog } from \"./confirm-dialog.js\";\n\n/**\n * @param {Object} config\n * @param {string} config.value current draft text\n * @param {Object} config.strings\n * @param {(text: string) => void} config.onInput fired on every keystroke\n * @param {(text: string) => void} config.onSave\n * @param {() => void} config.onCancel\n * @returns {HTMLElement}\n */\nexport const createInlineEditor = ({\n value,\n strings,\n onInput,\n onSave,\n onCancel,\n}) => {\n const wrapper = document.createElement(\"div\");\n wrapper.className = CLASSES.EDITOR;\n\n const input = document.createElement(\"textarea\");\n input.className = CLASSES.EDITOR_INPUT;\n input.value = value;\n input.rows = 3;\n input.setAttribute(\"aria-label\", strings.editorAriaLabel);\n\n const actions = document.createElement(\"div\");\n actions.className = CLASSES.EDITOR_ACTIONS;\n\n const cancel = document.createElement(\"button\");\n cancel.type = \"button\";\n cancel.className = CLASSES.EDITOR_CANCEL;\n cancel.textContent = strings.editCancel;\n\n const save = document.createElement(\"button\");\n save.type = \"button\";\n save.className = CLASSES.EDITOR_SAVE;\n save.textContent = strings.editSave;\n\n // An empty body is not a way to delete: the comment would keep its marker,\n // its replies and its row in the inbox while saying nothing. Deleting is\n // its own action, and it asks first.\n const syncSave = () => {\n save.disabled = input.value.trim().length === 0;\n };\n syncSave();\n\n input.addEventListener(\"input\", () => {\n syncSave();\n onInput(input.value);\n });\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") {\n // The overlay closes the thread popover on Escape from a bubble-phase\n // listener on `document`. Without this the panel would go too, and the\n // question about the unsaved draft would be asked about something the\n // user can no longer see.\n e.stopPropagation();\n e.preventDefault();\n onCancel();\n return;\n }\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey) && !save.disabled) {\n e.preventDefault();\n onSave(input.value);\n }\n });\n\n cancel.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onCancel();\n });\n save.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (!save.disabled) onSave(input.value);\n });\n\n actions.appendChild(cancel);\n actions.appendChild(save);\n wrapper.appendChild(input);\n wrapper.appendChild(actions);\n\n // Re-rendered panels rebuild this element from the stored draft, so the\n // caret has to be put back where a typist expects it rather than at 0.\n queueMicrotask(() => {\n input.focus();\n input.setSelectionRange(input.value.length, input.value.length);\n });\n\n return wrapper;\n};\n\n/**\n * Asks before an unsaved draft is thrown away. Callers gate on dirtiness\n * themselves \u2014 an untouched editor closes without a question, because there\n * is nothing to lose and a dialog nobody needs teaches people to dismiss\n * dialogs without reading them.\n *\n * @param {any} host node whose root the dialog mounts into\n * @param {Object} strings\n * @returns {Promise<boolean>} true when the draft may be discarded\n */\nexport const confirmDiscard = (host, strings) =>\n confirmDialog(host, {\n title: strings.confirmDiscardTitle,\n message: strings.confirmDiscardMessage,\n confirmLabel: strings.confirmDiscard,\n cancelLabel: strings.confirmKeepEditing,\n });\n", "import {\n CLASSES,\n IDS,\n COMMENT_TYPES,\n TYPE_COLORS,\n PRIORITIES,\n PRIORITY_COLORS,\n STATUS_COLORS,\n} from \"./constants.js\";\nimport { formatDuration, formatTemplate } from \"./i18n.js\";\nimport defaultStrings from \"./locales/en.js\";\nimport {\n createPicker,\n createMoreMenu,\n statusLabelOf,\n typeLabelOf,\n priorityLabelOf,\n} from \"./comment-actions.js\";\nimport { createInlineEditor } from \"./inline-editor.js\";\n\nconst formatRelativeTime = (date, strings) => {\n const diff = Date.now() - new Date(date).getTime();\n const minutes = Math.floor(diff / 60000);\n const hours = Math.floor(diff / 3600000);\n const days = Math.floor(diff / 86400000);\n\n if (minutes < 1) return strings.justNow;\n if (minutes < 60) return formatTemplate(strings.minutesAgoTemplate, minutes);\n if (hours < 24) return formatTemplate(strings.hoursAgoTemplate, hours);\n return formatTemplate(strings.daysAgoTemplate, days);\n};\n\nconst formatFullDate = (date, locale) => {\n return new Intl.DateTimeFormat(locale, {\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n }).format(new Date(date));\n};\n\nexport const createMetaElement = (\n author,\n createdAt,\n strings,\n locale,\n editedAt = null\n) => {\n const meta = document.createElement(\"div\");\n meta.className = CLASSES.THREAD_META;\n\n const authorEl = document.createElement(\"span\");\n authorEl.className = CLASSES.THREAD_AUTHOR;\n authorEl.textContent = author || strings.anonymous;\n\n const timeEl = document.createElement(\"span\");\n timeEl.className = CLASSES.THREAD_TIME;\n timeEl.textContent = formatRelativeTime(createdAt, strings);\n timeEl.dataset.fullDate = formatFullDate(createdAt, locale);\n\n meta.appendChild(authorEl);\n meta.appendChild(timeEl);\n\n if (editedAt) meta.appendChild(createEditedMark(editedAt, strings, locale));\n\n return meta;\n};\n\n/**\n * The \"edited\" mark for a meta line.\n *\n * Someone can answer \"the button is blue\", watch the text they answered get\n * rewritten, and have no way to know it happened \u2014 their reply is left\n * arguing with a sentence that no longer exists. Text rather than a colour,\n * so it holds up under WCAG 1.4.1 like every other badge here, and the exact\n * time hangs off the same `data-full-date` hover the timestamp uses.\n *\n * Exported because the open thread popover is mutated in place rather than\n * re-rendered, so the overlay has to build this same mark after a save.\n *\n * @param {string} editedAt\n * @param {object} strings\n * @param {string} [locale]\n * @returns {HTMLElement}\n */\nexport const createEditedMark = (editedAt, strings, locale) => {\n const editedEl = document.createElement(\"span\");\n editedEl.className = CLASSES.THREAD_EDITED;\n editedEl.textContent = strings.editedMark;\n editedEl.dataset.fullDate =\n strings.editedAtPrefix + formatFullDate(editedAt, locale);\n return editedEl;\n};\n\n/**\n * The comment shortcut as the user's platform spells it. Exported so the\n * inbox's empty state teaches the same chord the toolbar tooltip shows \u2014\n * two different renderings of one shortcut is how they drift apart.\n * @param {{ shortcutModifier?: string, shortcutKey?: string }} options\n * @param {object} strings\n */\nexport const getShortcutText = (options, strings) => {\n const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.userAgent);\n const modifierMap = {\n alt: isMac ? \"\u2325\" : strings.modifierAlt,\n ctrl: isMac ? \"\u2318\" : strings.modifierCtrl,\n shift: \"\u21E7\",\n };\n\n const modifier = modifierMap[options.shortcutModifier] || modifierMap.alt;\n const key = options.shortcutKey?.toUpperCase() || \"C\";\n\n return `${modifier} + ${key}`;\n};\n\nconst ATTACH_ICON_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>`;\n\nconst SEND_ICON_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 2L11 13M22 2L15 22L11 13M11 13L2 9L22 2\"/></svg>`;\n\nconst COMMENT_BUBBLE_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" stroke-linejoin=\"round\" fill=\"currentColor\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2.8914 10.4028L2.98327 10.6318C3.22909 11.2445 3.5 12.1045 3.5 13C3.5 13.3588 3.4564 13.7131 3.38773 14.0495C3.69637 13.9446 4.01409 13.8159 4.32918 13.6584C4.87888 13.3835 5.33961 13.0611 5.70994 12.7521L6.22471 12.3226L6.88809 12.4196C7.24851 12.4724 7.61994 12.5 8 12.5C11.7843 12.5 14.5 9.85569 14.5 7C14.5 4.14431 11.7843 1.5 8 1.5C4.21574 1.5 1.5 4.14431 1.5 7C1.5 8.18175 1.94229 9.29322 2.73103 10.2153L2.8914 10.4028ZM2.8135 15.7653C1.76096 16 1 16 1 16C1 16 1.43322 15.3097 1.72937 14.4367C1.88317 13.9834 2 13.4808 2 13C2 12.3826 1.80733 11.7292 1.59114 11.1903C0.591845 10.0221 0 8.57152 0 7C0 3.13401 3.58172 0 8 0C12.4183 0 16 3.13401 16 7C16 10.866 12.4183 14 8 14C7.54721 14 7.10321 13.9671 6.67094 13.9038C6.22579 14.2753 5.66881 14.6656 5 15C4.23366 15.3832 3.46733 15.6195 2.8135 15.7653Z\"/></svg>`;\n\nconst MENU_ICON_SVG = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" stroke-linejoin=\"round\" fill=\"currentColor\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M1.67705 7.5L3.92705 3H12.0729L14.3229 7.5H10H9.25V8.25C9.25 8.94036 8.69036 9.5 8 9.5C7.30964 9.5 6.75 8.94036 6.75 8.25V7.5H6H1.67705ZM1.5 9V12C1.5 12.5523 1.94772 13 2.5 13H13.5C14.0523 13 14.5 12.5523 14.5 12V9H10.6465C10.32 10.1543 9.25878 11 8 11C6.74122 11 5.67998 10.1543 5.35352 9H1.5ZM3 1.5H13L15.8944 7.28885C15.9639 7.42771 16 7.58082 16 7.73607V12C16 13.3807 14.8807 14.5 13.5 14.5H2.5C1.11929 14.5 0 13.3807 0 12V7.73607C0 7.58082 0.0361451 7.42771 0.105573 7.28885L3 1.5Z\"/></svg>`;\n\n/**\n * Shared input area component used by both the comment box and the thread popover.\n * Returns the container element and references to key child elements.\n * @param {Object} options\n * @param {string} options.areaClassName\n * @param {\"textarea\" | \"input\"} [options.inputTag]\n * @param {string} [options.inputClassName]\n * @param {string} [options.inputId]\n * @param {string} options.inputPlaceholder\n * @param {string} [options.submitBtnId]\n * @param {string} [options.fileInputId]\n * @param {typeof defaultStrings} strings\n */\nexport const createInputArea = (\n {\n areaClassName,\n inputTag = \"textarea\",\n inputClassName,\n inputId,\n inputPlaceholder,\n submitBtnId,\n fileInputId,\n },\n strings\n) => {\n const container = document.createElement(\"div\");\n container.className = areaClassName;\n\n /** @type {HTMLInputElement | HTMLTextAreaElement} */\n const inputEl = document.createElement(inputTag);\n if (inputId) inputEl.id = inputId;\n if (inputClassName) inputEl.className = inputClassName;\n inputEl.placeholder = inputPlaceholder;\n inputEl.setAttribute(\"aria-label\", inputPlaceholder);\n if (inputTag === \"input\")\n /** @type {HTMLInputElement} */ (inputEl).type = \"text\";\n\n const screenshotsContainer = document.createElement(\"div\");\n screenshotsContainer.className = CLASSES.SCREENSHOTS_CONTAINER;\n\n const actionsBar = document.createElement(\"div\");\n actionsBar.className = CLASSES.COMMENT_ACTIONS_BAR;\n\n const attachBtn = document.createElement(\"button\");\n attachBtn.className = CLASSES.ATTACH_IMAGE_BTN;\n attachBtn.type = \"button\";\n attachBtn.setAttribute(\"aria-label\", strings.attachImage);\n attachBtn.innerHTML = ATTACH_ICON_SVG;\n\n const fileInput = document.createElement(\"input\");\n fileInput.type = \"file\";\n if (fileInputId) fileInput.id = fileInputId;\n fileInput.accept = \"image/*\";\n fileInput.style.display = \"none\";\n\n const submitBtn = document.createElement(\"button\");\n if (submitBtnId) submitBtn.id = submitBtnId;\n submitBtn.className = CLASSES.THREAD_SUBMIT;\n submitBtn.type = \"button\";\n submitBtn.setAttribute(\"aria-label\", strings.send);\n submitBtn.innerHTML = SEND_ICON_SVG;\n\n actionsBar.appendChild(attachBtn);\n actionsBar.appendChild(fileInput);\n actionsBar.appendChild(submitBtn);\n\n container.appendChild(inputEl);\n container.appendChild(screenshotsContainer);\n container.appendChild(actionsBar);\n\n return {\n container,\n inputEl,\n screenshotsContainer,\n attachBtn,\n fileInput,\n submitBtn,\n };\n};\n\nconst createActionWithTooltip = (btnClass, btnSvg, tooltipContent, label) => {\n const wrapper = document.createElement(\"div\");\n wrapper.className = CLASSES.TOOLBAR_ACTION_WRAPPER;\n\n const tooltip = document.createElement(\"div\");\n tooltip.className = CLASSES.TOOLBAR_ACTION_TOOLTIP;\n tooltipContent.forEach((el) => tooltip.appendChild(el));\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = `${CLASSES.TOOLBAR_ACTION_BTN} ${btnClass}`;\n btn.setAttribute(\"aria-label\", label);\n btn.innerHTML = btnSvg;\n\n wrapper.appendChild(tooltip);\n wrapper.appendChild(btn);\n return wrapper;\n};\n\nexport const createToolbar = (options = {}, strings = defaultStrings) => {\n const toolbar = document.createElement(\"div\");\n toolbar.id = IDS.TOOLBAR;\n\n const actions = document.createElement(\"div\");\n actions.className = CLASSES.TOOLBAR_ACTIONS;\n\n const commentLabel = document.createElement(\"span\");\n commentLabel.className = CLASSES.TOOLBAR_TEXT;\n commentLabel.textContent = strings.toolbarComment;\n\n const shortcutKey = document.createElement(\"span\");\n shortcutKey.className = CLASSES.SHORTCUT_HINT;\n shortcutKey.textContent = getShortcutText(options, strings);\n\n const commentWrapper = createActionWithTooltip(\n CLASSES.TOOLBAR_COMMENT_BTN,\n COMMENT_BUBBLE_SVG,\n [commentLabel, shortcutKey],\n strings.toolbarComment\n );\n commentWrapper\n .querySelector(`.${CLASSES.TOOLBAR_COMMENT_BTN}`)\n ?.setAttribute(\"aria-pressed\", \"false\");\n\n const inboxLabel = document.createElement(\"span\");\n inboxLabel.className = CLASSES.TOOLBAR_TEXT;\n inboxLabel.textContent = strings.toolbarInbox;\n\n const inboxWrapper = createActionWithTooltip(\n CLASSES.TOOLBAR_MENU_BTN,\n MENU_ICON_SVG,\n [inboxLabel],\n strings.toolbarInbox\n );\n\n actions.appendChild(commentWrapper);\n actions.appendChild(inboxWrapper);\n toolbar.appendChild(actions);\n\n return toolbar;\n};\n\n/**\n * RF3/RF4/RF5 \u2014 the classification and resolution-time badge strip. Every\n * badge carries text: colour alone must never be the only signal\n * (WCAG 1.4.1), so the colour only ever tints the border.\n *\n * Both flags exist because a badge is only worth showing where nothing else\n * already says it. The tooltip is a read-only preview and needs all of it.\n * Inbox cards carry labelled status/type/priority pickers, so repeating\n * those three as badges is pure duplication \u2014 but tags and the resolution\n * time have no control anywhere, so they stay either way.\n *\n * @param {any} comment\n * @param {object} strings\n * @param {{ includeStatus?: boolean, includeClassification?: boolean }} [options]\n * @returns {HTMLElement | null} null when there's nothing to show\n */\nexport const createBadgeRow = (\n comment,\n strings,\n { includeStatus = false, includeClassification = true } = {}\n) => {\n const row = document.createElement(\"div\");\n row.className = CLASSES.INBOX_BADGES;\n\n const addBadge = (text, modifier, color) => {\n const badge = document.createElement(\"span\");\n badge.className = `${CLASSES.BADGE} ${modifier}`;\n badge.textContent = text;\n if (color) badge.style.borderColor = color;\n row.appendChild(badge);\n };\n\n if (includeStatus) {\n const status = comment.status || \"open\";\n addBadge(\n statusLabelOf(status, strings),\n CLASSES.BADGE_STATUS,\n STATUS_COLORS[status]\n );\n }\n if (includeClassification && comment.type) {\n addBadge(\n typeLabelOf(comment.type, strings),\n CLASSES.BADGE_TYPE,\n TYPE_COLORS[comment.type]\n );\n }\n if (includeClassification && comment.priority) {\n addBadge(\n priorityLabelOf(comment.priority, strings),\n CLASSES.BADGE_PRIORITY,\n PRIORITY_COLORS[comment.priority]\n );\n }\n // Tags are no longer authored in the widget, but comments saved before\n // that (or set through setCommentTags) still carry them.\n for (const tag of comment.tags || []) {\n addBadge(tag, CLASSES.BADGE_TAG, null);\n }\n\n if (comment.status === \"resolved\") {\n // Comments resolved before RF5 shipped have no timestamp \u2014 show a\n // dash rather than a duration computed from data we don't have.\n const elapsed = comment.resolvedAt\n ? formatDuration(\n new Date(comment.resolvedAt).getTime() -\n new Date(comment.createdAt).getTime(),\n strings\n )\n : \"\";\n addBadge(\n formatTemplate(strings.resolvedInTemplate, elapsed || \"\u2014\"),\n CLASSES.BADGE_DURATION,\n null\n );\n }\n\n return row.children.length ? row : null;\n};\n\n/**\n * RF3 + RF4 \u2014 the classification strip inside the new-comment box: type and\n * priority, both starting neutral.\n *\n * The comment box is built once and reused for every comment, so this\n * exposes reset(): without it the previous comment's selections would leak\n * into the next one.\n *\n * @param {object} strings\n * @returns {{ container: HTMLElement, getType: () => string|null,\n * getPriority: () => string|null, reset: () => void }}\n */\nexport const createClassifyRow = (strings) => {\n const container = document.createElement(\"div\");\n container.className = CLASSES.CLASSIFY_ROW;\n\n let type = null;\n let priority = null;\n\n // Pickers keep their selection internally, so returning them to neutral\n // means rebuilding them \u2014 hence mount() rather than a one-shot append.\n const mount = () => {\n container.replaceChildren();\n container.appendChild(\n createPicker({\n action: \"type\",\n options: [null, ...COMMENT_TYPES],\n value: null,\n colorOf: (value) => TYPE_COLORS[value] || \"transparent\",\n labelOf: (value) => typeLabelOf(value, strings),\n tooltipLabel: strings.typeLabel,\n onSelect: (value) => (type = value),\n showLabel: true,\n })\n );\n container.appendChild(\n createPicker({\n action: \"priority\",\n options: [null, ...PRIORITIES],\n value: null,\n colorOf: (value) => PRIORITY_COLORS[value] || \"transparent\",\n labelOf: (value) => priorityLabelOf(value, strings),\n tooltipLabel: strings.priorityLabel,\n onSelect: (value) => (priority = value),\n showLabel: true,\n })\n );\n };\n\n mount();\n\n return {\n container,\n getType: () => type,\n getPriority: () => priority,\n reset: () => {\n type = null;\n priority = null;\n mount();\n },\n };\n};\n\nexport const createCommentBox = (strings = defaultStrings) => {\n const commentBox = document.createElement(\"div\");\n commentBox.id = IDS.COMMENT_BOX;\n commentBox.setAttribute(\"role\", \"dialog\");\n commentBox.setAttribute(\"aria-label\", strings.commentBoxAriaLabel);\n\n const { container: inputArea } = createInputArea(\n {\n areaClassName: CLASSES.COMMENT_INPUT_AREA,\n inputTag: \"textarea\",\n inputId: IDS.COMMENT_INPUT,\n inputPlaceholder: strings.commentPlaceholder,\n submitBtnId: IDS.SUBMIT_COMMENT,\n fileInputId: IDS.ATTACH_IMAGE_INPUT,\n },\n strings\n );\n\n const classify = createClassifyRow(strings);\n\n commentBox.appendChild(classify.container);\n commentBox.appendChild(inputArea);\n commentBox.style.display = \"none\";\n // Exposed so the overlay can read the selections at save time without\n // re-querying the DOM.\n /** @type {any} */ (commentBox).classify = classify;\n return commentBox;\n};\n\nexport const createCommentCircle = (comment, strings = defaultStrings) => {\n const circle = document.createElement(\"div\");\n circle.className = CLASSES.CIRCLE;\n circle.dataset.commentId = comment.id;\n circle.dataset.commentText = comment.text;\n circle.setAttribute(\"role\", \"button\");\n circle.setAttribute(\"tabindex\", \"0\");\n circle.setAttribute(\n \"aria-label\",\n `${strings.commentAriaLabelPrefix}${comment.text}`\n );\n\n // Basic positioning - will be updated by position validation system\n circle.style.cssText = `\n position: absolute;\n pointer-events: auto;\n `;\n\n return circle;\n};\n\nexport const createScreenshotsDisplay = (screenshots, strings) => {\n const container = document.createElement(\"div\");\n container.className = CLASSES.SCREENSHOTS_CONTAINER;\n container.classList.add(CLASSES.ACTIVE);\n\n screenshots.forEach((src) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = src;\n img.alt = strings.attachedScreenshot;\n\n item.appendChild(img);\n container.appendChild(item);\n });\n\n return container;\n};\n\nexport const createTooltip = (comment, strings = defaultStrings, locale) => {\n const tooltip = document.createElement(\"div\");\n tooltip.className = CLASSES.TOOLTIP;\n tooltip.dataset.for = comment.id;\n tooltip.setAttribute(\"role\", \"dialog\");\n tooltip.setAttribute(\"aria-label\", strings.tooltipAriaLabel);\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.THREAD_HEADER;\n\n const meta = createMetaElement(\n comment.author,\n comment.createdAt,\n strings,\n locale,\n comment.editedAt\n );\n const closeButton = document.createElement(\"button\");\n closeButton.type = \"button\";\n closeButton.className = CLASSES.CLOSE_TOOLTIP;\n closeButton.setAttribute(\"aria-label\", strings.close);\n closeButton.innerHTML = \"×\";\n\n header.appendChild(meta);\n header.appendChild(closeButton);\n\n const body = document.createElement(\"div\");\n body.className = CLASSES.THREAD_BODY;\n body.textContent = comment.text;\n\n tooltip.appendChild(header);\n tooltip.appendChild(body);\n // The tooltip is a read-only preview with no pickers, so the badges are\n // the only place its status/type/priority can be read at all.\n const badges = createBadgeRow(comment, strings, { includeStatus: true });\n if (badges) tooltip.appendChild(badges);\n const tooltipScreenshots =\n comment.screenshots || (comment.screenshot ? [comment.screenshot] : []);\n if (tooltipScreenshots.length > 0) {\n tooltip.appendChild(createScreenshotsDisplay(tooltipScreenshots, strings));\n }\n\n // The preview shows the root comment only, so a thread with replies would\n // otherwise look like a lone remark. Omitted at zero rather than shown as\n // \"0 replies\": absence already says it, and a count of nothing is noise.\n const replyCount = comment.replies?.length || 0;\n if (replyCount > 0) {\n const replies = document.createElement(\"div\");\n replies.className = CLASSES.TOOLTIP_REPLY_COUNT;\n replies.textContent =\n replyCount === 1\n ? strings.replyCountOne\n : formatTemplate(strings.replyCountTemplate, replyCount);\n tooltip.appendChild(replies);\n }\n\n return tooltip;\n};\n\n/**\n * One reply inside a thread. `onDelete` and `onEdit` are optional so\n * read-only renderings (and any host that never wires them) keep the plain\n * row: the \u22EF menu is only built when there is something for it to do.\n *\n * `editing`, when present, replaces the body with the inline editor. The\n * draft it renders belongs to the caller \u2014 see `inline-editor.js` for why.\n *\n * @param {any} reply\n * @param {object} [strings]\n * @param {string} [locale]\n * @param {{\n * onDelete?: (reply: any, replyEl: HTMLElement) => void,\n * onEdit?: (reply: any) => void,\n * editing?: {\n * draft: string,\n * onInput: (text: string) => void,\n * onSave: (text: string) => void,\n * onCancel: () => void,\n * } | null,\n * }} [handlers]\n */\nexport const createReplyElement = (\n reply,\n strings = defaultStrings,\n locale,\n { onDelete, onEdit, editing = null } = {}\n) => {\n const replyEl = document.createElement(\"div\");\n replyEl.className = CLASSES.THREAD_REPLY;\n // The popover is built once and mutated in place, so anything that has to\n // find one reply again later \u2014 the editor, a text refresh \u2014 needs a handle.\n replyEl.dataset.replyId = String(reply.id);\n\n const meta = createMetaElement(\n reply.author,\n reply.timestamp,\n strings,\n locale,\n reply.editedAt\n );\n\n // Same \u22EF builder the comment strip uses, so a reply is edited and deleted\n // through the control the user already learned one level up.\n const items = [];\n if (onEdit) {\n items.push({ label: strings.editReply, onSelect: () => onEdit(reply) });\n }\n if (onDelete) {\n items.push({\n label: strings.deleteReply,\n onSelect: () => onDelete(reply, replyEl),\n confirm: () => ({\n title: strings.confirmDeleteReplyTitle,\n message: strings.confirmDeleteReplyMessage,\n confirmLabel: strings.confirmDelete,\n cancelLabel: strings.confirmCancel,\n }),\n });\n }\n if (items.length > 0) {\n const actions = createMoreMenu({ label: strings.replyOptions, items });\n actions.classList.add(CLASSES.THREAD_REPLY_ACTIONS);\n meta.appendChild(actions);\n }\n\n let text;\n if (editing) {\n text = createInlineEditor({\n value: editing.draft,\n strings,\n onInput: editing.onInput,\n onSave: editing.onSave,\n onCancel: editing.onCancel,\n });\n } else {\n text = document.createElement(\"div\");\n text.className = CLASSES.THREAD_BODY;\n text.textContent = reply.text;\n }\n\n replyEl.appendChild(meta);\n replyEl.appendChild(text);\n const replyScreenshots =\n reply.screenshots || (reply.screenshot ? [reply.screenshot] : []);\n if (replyScreenshots.length > 0) {\n replyEl.appendChild(createScreenshotsDisplay(replyScreenshots, strings));\n }\n return replyEl;\n};\n\n/**\n * @param {any} comment\n * @param {object} [strings]\n * @param {string} [locale]\n * @param {{\n * onDeleteReply?: (reply: any, replyEl: HTMLElement) => void,\n * onEditReply?: (reply: any) => void,\n * }} [handlers]\n */\nexport const createThreadPopover = (\n comment,\n strings = defaultStrings,\n locale,\n { onDeleteReply, onEditReply } = {}\n) => {\n const popover = document.createElement(\"div\");\n popover.className = CLASSES.THREAD_POPOVER;\n popover.dataset.for = comment.id;\n popover.setAttribute(\"role\", \"dialog\");\n popover.setAttribute(\"aria-label\", strings.popoverAriaLabel);\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.THREAD_HEADER;\n\n const meta = createMetaElement(\n comment.author,\n comment.createdAt,\n strings,\n locale,\n comment.editedAt\n );\n const closeButton = document.createElement(\"button\");\n closeButton.type = \"button\";\n closeButton.className = CLASSES.CLOSE_TOOLTIP;\n closeButton.setAttribute(\"aria-label\", strings.close);\n closeButton.innerHTML = \"×\";\n\n header.appendChild(meta);\n header.appendChild(closeButton);\n\n const body = document.createElement(\"div\");\n body.className = CLASSES.THREAD_BODY;\n body.textContent = comment.text;\n\n const replies = document.createElement(\"div\");\n replies.className = CLASSES.THREAD_REPLIES;\n if (comment.replies) {\n comment.replies.forEach((reply) => {\n replies.appendChild(\n createReplyElement(reply, strings, locale, {\n onDelete: onDeleteReply,\n onEdit: onEditReply,\n })\n );\n });\n }\n\n const { container: inputArea } = createInputArea(\n {\n areaClassName: CLASSES.THREAD_INPUT_AREA,\n inputTag: \"input\",\n inputClassName: CLASSES.THREAD_INPUT,\n inputPlaceholder: strings.replyPlaceholder,\n },\n strings\n );\n\n // The header (and the action row the overlay inserts after it) and the\n // reply box stay put; everything between them scrolls. Without this the\n // popover just grew past the viewport \u2014 expanding the context block or\n // adding replies made content unreachable, because the wheel event fell\n // through to the page.\n const scroll = document.createElement(\"div\");\n scroll.className = CLASSES.THREAD_SCROLL;\n\n popover.appendChild(header);\n scroll.appendChild(body);\n const popoverScreenshots =\n comment.screenshots || (comment.screenshot ? [comment.screenshot] : []);\n if (popoverScreenshots.length > 0) {\n scroll.appendChild(createScreenshotsDisplay(popoverScreenshots, strings));\n }\n scroll.appendChild(replies);\n popover.appendChild(scroll);\n popover.appendChild(inputArea);\n\n return popover;\n};\n", "// Builds the plain-text context block the inbox \"copy\" action puts on the\n// clipboard \u2014 enough for a coding agent to locate the element and understand\n// the reported issue (page, viewport, selector, DOM path, thread).\n\nimport { formatDuration } from \"./i18n.js\";\n\nconst openingTagOf = (element) => {\n const attrs = [...element.attributes]\n .map(({ name, value }) => `${name}=\"${value}\"`)\n .join(\" \");\n return `<${element.tagName.toLowerCase()}${attrs ? ` ${attrs}` : \"\"}>`;\n};\n\nconst openingTagFromFingerprint = (fingerprint) => {\n if (!fingerprint?.tagName) return \"(unknown)\";\n const attrs = Object.entries(fingerprint.attributes || {})\n .map(([name, value]) => `${name}=\"${value}\"`)\n .join(\" \");\n return `<${fingerprint.tagName.toLowerCase()}${attrs ? ` ${attrs}` : \"\"}>`;\n};\n\n// body > main.flex.layout > section#pricing.plans \u2014 tag + id + up to two\n// classes per level, from body down to the element.\nconst domPathOf = (element) => {\n const segments = [];\n let current = element;\n while (current && current !== document.documentElement) {\n const tag = current.tagName.toLowerCase();\n const id = current.id ? `#${current.id}` : \"\";\n const classes = [...current.classList]\n .slice(0, 2)\n .map((cls) => `.${cls}`)\n .join(\"\");\n segments.unshift(`${tag}${id}${classes}`);\n if (current === document.body) break;\n current = current.parentElement;\n }\n return segments.join(\" > \");\n};\n\n/**\n * @param {import('./index.d.ts').Comment} comment\n * @param {{ viewportWidth: number, viewportHeight: number, strings?: object }} env\n * @returns {string}\n */\nexport function buildAgentContext(\n comment,\n { viewportWidth, viewportHeight, strings }\n) {\n const anchor = comment.anchor;\n const fingerprint = anchor?.fingerprint;\n const live = comment.container?.isConnected ? comment.container : null;\n\n const state = comment.hidden ? \"hidden\" : comment.anchorState;\n const element = live\n ? openingTagOf(live)\n : openingTagFromFingerprint(fingerprint);\n const path = live ? domPathOf(live) : \"(unavailable)\";\n\n // The reporter's viewport, not the reader's: `comment.context.viewport`\n // was captured at report time (RF2) and travels with the comment. Live\n // `viewportWidth`/`viewportHeight` (the copying browser's window) is only\n // a fallback for legacy records persisted before RF1/RF2 had no context.\n const capturedViewport = comment.context?.viewport;\n const reportedViewportWidth = capturedViewport?.width ?? viewportWidth;\n const reportedViewportHeight = capturedViewport?.height ?? viewportHeight;\n\n const lines = [\n `Page: ${comment.page}`,\n `Viewport: ${reportedViewportWidth}x${reportedViewportHeight}`,\n `Anchor state: ${state}`,\n `Status: ${comment.status || \"open\"}`,\n `Selector: ${anchor?.selector || \"(none)\"}`,\n `Element: ${element}`,\n `DOM path: ${path}`,\n `Nearby text: \"${fingerprint?.textSnippet ?? \"\"}\"`,\n `Comment by ${comment.author} (${comment.createdAt}):`,\n `\"${comment.text}\"`,\n ];\n\n // Neutral classification fields are omitted rather than printed as\n // \"(none)\" \u2014 the agent shouldn't read noise for data nobody filled in.\n if (comment.type) lines.push(`Type: ${comment.type}`);\n if (comment.priority) lines.push(`Priority: ${comment.priority}`);\n if (comment.tags?.length) lines.push(`Tags: ${comment.tags.join(\", \")}`);\n\n const context = comment.context;\n if (context) {\n if (context.url) lines.push(`URL: ${context.url}`);\n if (context.screen) {\n lines.push(`Screen: ${context.screen.width}x${context.screen.height}`);\n }\n if (context.browser?.name) {\n lines.push(\n `Browser: ${`${context.browser.name} ${context.browser.version || \"\"}`.trim()}`\n );\n }\n if (context.os?.name) {\n lines.push(\n `OS: ${`${context.os.name} ${context.os.version || \"\"}`.trim()}`\n );\n }\n }\n\n if (strings && comment.status === \"resolved\" && comment.resolvedAt) {\n const elapsed = formatDuration(\n new Date(comment.resolvedAt).getTime() -\n new Date(comment.createdAt).getTime(),\n strings\n );\n if (elapsed) lines.push(`Resolution time: ${elapsed}`);\n }\n\n const replies = comment.replies || [];\n if (replies.length > 0) {\n lines.push(`Replies (${replies.length}):`);\n for (const reply of replies) {\n lines.push(`- ${reply.author}: \"${reply.text}\"`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n", "// RF2 \u2014 the environment a comment was reported from, plus the automatic\n// capture taken at that moment.\n//\n// Two surfaces render it: the inbox detail, where it is always expanded\n// because the detail view exists to show everything, and the thread popover,\n// where it is a disclosure collapsed by default so the popover stays a\n// conversation first and a bug report second.\n\nimport { CLASSES } from \"./constants.js\";\n\nconst CARET_ICON_SVG = `<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>`;\n\n/**\n * @param {any} comment\n * @param {{ strings: object, onShowLightbox: (src: string) => void,\n * collapsible?: boolean }} deps\n * @returns {HTMLElement | null} null for comments created before RF1/RF2\n */\nexport const createContextBlock = (\n comment,\n { strings, onShowLightbox, collapsible = false }\n) => {\n const { context, contextScreenshot } = comment;\n if (!context && !contextScreenshot) return null;\n\n const block = document.createElement(\"div\");\n block.className = CLASSES.CONTEXT_BLOCK;\n\n const body = document.createElement(\"div\");\n body.className = CLASSES.CONTEXT_BODY;\n\n if (collapsible) {\n const toggle = document.createElement(\"button\");\n toggle.type = \"button\";\n toggle.className = CLASSES.CONTEXT_TOGGLE;\n toggle.setAttribute(\"aria-expanded\", \"false\");\n toggle.innerHTML = `<span>${strings.contextSection}</span>${CARET_ICON_SVG}`;\n body.style.display = \"none\";\n toggle.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const expanded = toggle.getAttribute(\"aria-expanded\") === \"true\";\n toggle.setAttribute(\"aria-expanded\", String(!expanded));\n body.style.display = expanded ? \"none\" : \"\";\n });\n block.appendChild(toggle);\n } else {\n const title = document.createElement(\"div\");\n title.className = CLASSES.CONTEXT_TITLE;\n title.textContent = strings.contextSection;\n block.appendChild(title);\n }\n\n if (contextScreenshot) {\n const caption = document.createElement(\"div\");\n caption.className = CLASSES.CONTEXT_SCREENSHOT_CAPTION;\n caption.textContent = strings.autoScreenshotLabel;\n body.appendChild(caption);\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = contextScreenshot;\n img.alt = strings.autoScreenshotLabel;\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onShowLightbox(contextScreenshot);\n });\n body.appendChild(img);\n }\n\n if (context) {\n const addRow = (label, value) => {\n if (!value) return;\n const row = document.createElement(\"div\");\n row.className = CLASSES.CONTEXT_ROW;\n const key = document.createElement(\"span\");\n key.textContent = label;\n const val = document.createElement(\"span\");\n val.textContent = value;\n row.appendChild(key);\n row.appendChild(val);\n body.appendChild(row);\n };\n\n const size = (dimensions) =>\n dimensions ? `${dimensions.width}\u00D7${dimensions.height}` : \"\";\n const named = (entry) =>\n entry?.name ? `${entry.name} ${entry.version || \"\"}`.trim() : \"\";\n\n addRow(strings.contextUrl, context.url);\n addRow(strings.contextViewport, size(context.viewport));\n addRow(strings.contextScreen, size(context.screen));\n addRow(strings.contextBrowser, named(context.browser));\n addRow(strings.contextOs, named(context.os));\n }\n\n block.appendChild(body);\n return block;\n};\n", "// Right-side inbox sidebar: a filterable list of every comment and a detail\n// view with the full thread. Pure view layer \u2014 all data mutations flow back\n// to CommentOverlay through the callbacks contract passed to the\n// constructor, so this module never touches storage or the page markers.\n\nimport { CLASSES, COMMENT_TYPES, PRIORITIES, STATUSES } from \"./constants.js\";\nimport { buildAgentContext } from \"./agent-context.js\";\nimport { attachMenuToggle } from \"./menus.js\";\nimport { createContextBlock } from \"./context-block.js\";\nimport {\n createCommentActions,\n copyToClipboard,\n statusLabelOf,\n typeLabelOf,\n priorityLabelOf,\n} from \"./comment-actions.js\";\nimport {\n createMetaElement,\n createScreenshotsDisplay,\n createInputArea,\n createReplyElement,\n createBadgeRow,\n getShortcutText,\n} from \"./components.js\";\nimport { createInlineEditor, confirmDiscard } from \"./inline-editor.js\";\nimport { buildCommentLink } from \"./link.js\";\n\nconst CARET_ICON_SVG = `<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>`;\nconst CHEVRON_LEFT_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"/></svg>`;\nconst ARROW_UP_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"18 15 12 9 6 15\"/></svg>`;\nconst ARROW_DOWN_SVG = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>`;\n\nexport class InboxView {\n /**\n * @param {Object} deps\n * @param {ShadowRoot} deps.shadowRoot\n * @param {Object} deps.strings\n * @param {string} deps.locale\n * @param {string} deps.currentPage\n * @param {() => Array<Object>} deps.getComments\n * @param {{ shortcutKey?: string, shortcutModifier?: string, linkParam?: string }} [deps.options]\n * @param {{ onOpenDetailScroll: Function, onReply: Function, onDelete: Function, onDeleteReply: Function, onEditComment: Function, onEditReply: Function, onSetStatus: Function, onSetType: Function, onSetPriority: Function, onNavigateToPage: Function, onShowLightbox: Function, onActivateCommentMode: Function, onClose: Function }} deps.callbacks\n */\n constructor({\n shadowRoot,\n strings,\n locale,\n currentPage,\n getComments,\n callbacks,\n options = {},\n }) {\n this.shadowRoot = shadowRoot;\n this.strings = strings;\n this.locale = locale;\n this.currentPage = currentPage;\n this.getComments = getComments;\n this.callbacks = callbacks;\n // Only the shortcut config is read, and only to teach the chord in the\n // empty state.\n this.options = options;\n this.pageFilter = \"page\"; // \"all\" | \"page\"\n this.statusFilter = \"all\"; // \"all\" | STATUSES\n this.typeFilter = \"all\"; // \"all\" | COMMENT_TYPES\n this.priorityFilter = \"all\"; // \"all\" | PRIORITIES\n this.detailId = null;\n /**\n * The one open editor, as state rather than DOM.\n *\n * This panel re-renders from ten places, and the overlay refreshes it\n * from seven more. A draft living only in a textarea would be wiped by\n * any of them \u2014 changing a comment's priority mid-sentence would eat the\n * sentence. Keeping it here means every one of those rebuilds is\n * harmless, and leaves only the deliberate exits to ask about.\n * @type {{ commentId: any, replyId: any | null, draft: string } | null}\n */\n this.editing = null;\n /** @type {string | null} */\n this.notice = null;\n /** @type {HTMLElement | null} */\n this.el = null;\n /** @type {HTMLElement | null} */\n this._highlightedEl = null;\n /** @type {HTMLElement | null} */\n this._activeMarkerEl = null;\n }\n\n isOpen() {\n return Boolean(this.el);\n }\n\n open() {\n if (this.el) return;\n this.el = document.createElement(\"div\");\n this.el.className = CLASSES.INBOX_PANEL;\n this.el.setAttribute(\"role\", \"dialog\");\n this.el.setAttribute(\"aria-label\", this.strings.inboxAriaLabel);\n this.shadowRoot.appendChild(this.el);\n this.render();\n }\n\n close() {\n this._clearHighlight();\n this._setActiveMarker(null);\n this.el?.remove();\n this.el = null;\n this.detailId = null;\n // Every route to here already went through releaseEditor(), so anything\n // still sitting in the draft has been answered for.\n this.editing = null;\n this.notice = null;\n }\n\n refresh() {\n if (this.el) this.render();\n }\n\n /**\n * A line at the top of the list. Exists for one case: someone followed a\n * \"Copy link\" URL to a comment this page cannot show them. It stays until\n * the comment arrives (the overlay retries on every loadComments) or the\n * user navigates away from it.\n * @param {string} text\n */\n showNotice(text) {\n this.notice = text;\n this.refresh();\n }\n\n clearNotice() {\n if (!this.notice) return;\n this.notice = null;\n this.refresh();\n }\n\n /** True while an editor holds text the user has not saved. */\n isDirty() {\n if (!this.editing) return false;\n return this.editing.draft.trim() !== this._editingOriginalText().trim();\n }\n\n _editingOriginalText() {\n if (!this.editing) return \"\";\n const comment = this.getComments().find(\n (c) => String(c.id) === String(this.editing.commentId)\n );\n if (!comment) return \"\";\n if (this.editing.replyId == null) return comment.text || \"\";\n const reply = (comment.replies || []).find(\n (r) => String(r.id) === String(this.editing.replyId)\n );\n return reply?.text || \"\";\n }\n\n /**\n * Every path that would take the editor off screen funnels through here,\n * so the question is asked once and in one place instead of at each of the\n * exits (Cancel, Escape, the \u22EF of another comment, the close button, the\n * prev/next arrows, Back).\n * @returns {Promise<boolean>} true when the caller may proceed\n */\n async releaseEditor() {\n if (!this.editing) return true;\n if (this.isDirty()) {\n const host = /** @type {any} */ (this.el || this.shadowRoot);\n if (!(await confirmDiscard(host, this.strings))) return false;\n }\n this.editing = null;\n return true;\n }\n\n /**\n * The handlers every editor in this panel shares. Split out because the\n * comment body and a reply body are built by different components but must\n * behave identically \u2014 a draft that saved from one place and discarded\n * from the other would be two features wearing one look.\n */\n _editorHandlers() {\n return {\n draft: this.editing.draft,\n onInput: (text) => {\n this.editing.draft = text;\n },\n onSave: (text) => {\n const { commentId, replyId } = this.editing;\n if (replyId == null) this.callbacks.onEditComment(commentId, text);\n else this.callbacks.onEditReply(commentId, replyId, text);\n this.editing = null;\n this.render();\n },\n onCancel: async () => {\n if (await this.releaseEditor()) this.render();\n },\n };\n }\n\n _buildEditor() {\n const handlers = this._editorHandlers();\n return createInlineEditor({\n value: handlers.draft,\n strings: this.strings,\n onInput: handlers.onInput,\n onSave: handlers.onSave,\n onCancel: handlers.onCancel,\n });\n }\n\n /** Opens the editor on a comment body, or on one of its replies. */\n async startEditing(commentId, replyId = null) {\n if (!(await this.releaseEditor())) return;\n this.detailId = commentId;\n this.editing = { commentId, replyId, draft: \"\" };\n this.editing.draft = this._editingOriginalText();\n this.render();\n }\n\n /**\n * The on-page marker for a comment, when there is one to decorate.\n * Resolved, orphaned and hidden comments render no circle at all.\n * @param {any} comment\n * @returns {HTMLElement | null}\n */\n _markerFor(comment) {\n if (\n comment.anchorState !== \"anchored\" ||\n comment.hidden ||\n comment.status === \"resolved\"\n ) {\n return null;\n }\n return /** @type {any} */ (\n this.shadowRoot.querySelector(`[data-comment-id=\"${comment.id}\"]`)\n );\n }\n\n _highlight(comment) {\n this._clearHighlight();\n const circle = this._markerFor(comment);\n if (!circle) return;\n circle.classList.add(CLASSES.HIGHLIGHT);\n this._highlightedEl = circle;\n }\n\n _clearHighlight() {\n this._highlightedEl?.classList.remove(CLASSES.HIGHLIGHT);\n this._highlightedEl = null;\n }\n\n /**\n * Opening a comment's detail selects it just as clicking its marker does,\n * so the marker gets the same active state the thread popover gives it.\n * Passing null clears it \u2014 the list view has nothing selected.\n * @param {any} comment\n */\n _setActiveMarker(comment) {\n this._activeMarkerEl?.classList.remove(CLASSES.CIRCLE_ACTIVE);\n this._activeMarkerEl = null;\n if (!comment) return;\n const circle = this._markerFor(comment);\n if (!circle) return;\n circle.classList.add(CLASSES.CIRCLE_ACTIVE);\n this._activeMarkerEl = circle;\n }\n\n filteredComments() {\n let comments = this.getComments();\n if (this.pageFilter === \"page\") {\n comments = comments.filter(\n (comment) => comment.page === this.currentPage\n );\n }\n if (this.statusFilter !== \"all\") {\n // `open` is the implicit default: comments saved before RF09 have no\n // status at all and must still match the \"open\" chip.\n comments = comments.filter(\n (comment) => (comment.status || \"open\") === this.statusFilter\n );\n }\n if (this.typeFilter !== \"all\") {\n comments = comments.filter((comment) => comment.type === this.typeFilter);\n }\n if (this.priorityFilter !== \"all\") {\n comments = comments.filter(\n (comment) => comment.priority === this.priorityFilter\n );\n }\n // Resolved sink to the bottom; both partitions keep their original order.\n return [\n ...comments.filter((comment) => comment.status !== \"resolved\"),\n ...comments.filter((comment) => comment.status === \"resolved\"),\n ];\n }\n\n render() {\n if (!this.el) return;\n this._clearHighlight();\n this.el.innerHTML = \"\";\n const comments = this.filteredComments();\n const detail =\n this.detailId != null\n ? comments.find((comment) => comment.id === this.detailId)\n : null;\n // Set before rendering so a detail reached by any route \u2014 a card click,\n // the prev/next nav, the cross-page handoff \u2014 marks its marker.\n this._setActiveMarker(detail);\n if (detail) {\n this._renderDetail(detail, comments);\n } else {\n this.detailId = null;\n this._renderList(comments);\n }\n }\n\n /**\n * Opens the panel (if needed) directly on a comment's detail. Used by\n * the overlay for the cross-page handoff on startup.\n * @param {number} id\n */\n openDetail(id) {\n if (!this.el) this.open();\n const comment = this.getComments().find((c) => c.id === id);\n if (comment) this._openDetail(comment);\n }\n\n _openDetail(comment) {\n this.detailId = comment.id;\n if (comment.anchorState === \"anchored\" && !comment.hidden) {\n this.callbacks.onOpenDetailScroll(comment);\n }\n this.render();\n }\n\n _closeButton() {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_CLOSE;\n btn.setAttribute(\"aria-label\", this.strings.close);\n btn.innerHTML = \"×\";\n // `this.editing &&` short-circuits before the await, so with no editor\n // open this handler stays synchronous \u2014 closing the panel must not\n // become a microtask later just because editing exists as a feature.\n btn.addEventListener(\"click\", async () => {\n if (this.editing && !(await this.releaseEditor())) return;\n this.callbacks.onClose();\n });\n return btn;\n }\n\n _renderList(comments) {\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_HEADER;\n header.appendChild(this._buildFilter());\n header.appendChild(this._closeButton());\n this.el.appendChild(header);\n\n const list = document.createElement(\"div\");\n list.className = CLASSES.INBOX_LIST;\n this.el.appendChild(list);\n\n if (this.notice) {\n const notice = document.createElement(\"div\");\n notice.className = CLASSES.INBOX_NOTICE;\n notice.setAttribute(\"role\", \"status\");\n notice.textContent = this.notice;\n list.appendChild(notice);\n }\n\n if (comments.length === 0) {\n list.appendChild(this._buildEmptyState());\n return;\n }\n\n for (const comment of comments) {\n list.appendChild(this._buildCard(comment, { interactive: true }));\n }\n }\n\n /**\n * Two different nothings, and telling a user the wrong one wastes their\n * time: an inbox with no comments at all needs teaching (what the shortcut\n * is, how to place the first one), while an inbox whose filters happen to\n * exclude everything needs the filters relaxed. Offering \"turn on comment\n * mode\" to someone who already has twenty comments would be nonsense.\n */\n _buildEmptyState() {\n const empty = document.createElement(\"div\");\n empty.className = CLASSES.INBOX_EMPTY;\n\n // An outline of the marker the user is about to place, not a generic\n // placeholder \u2014 same teardrop silhouette the circles use.\n const icon = document.createElement(\"div\");\n icon.className = CLASSES.INBOX_EMPTY_ICON;\n icon.setAttribute(\"aria-hidden\", \"true\");\n empty.appendChild(icon);\n\n const hasAnyComment = this.getComments().length > 0;\n\n const title = document.createElement(\"div\");\n title.className = CLASSES.INBOX_EMPTY_TITLE;\n title.textContent = hasAnyComment\n ? this.strings.inboxNoMatches\n : this.strings.inboxEmptyTitle;\n empty.appendChild(title);\n\n if (hasAnyComment) {\n const clear = document.createElement(\"button\");\n clear.type = \"button\";\n clear.className = CLASSES.INBOX_EMPTY_ACTION;\n clear.textContent = this.strings.filterClear;\n clear.addEventListener(\"click\", () => {\n this.pageFilter = \"page\";\n this.statusFilter = \"all\";\n this.typeFilter = \"all\";\n this.priorityFilter = \"all\";\n this.render();\n });\n empty.appendChild(clear);\n return empty;\n }\n\n const text = document.createElement(\"div\");\n text.className = CLASSES.INBOX_EMPTY_TEXT;\n // Split on the placeholder so the chord can be a real <kbd> rather than\n // bare text, without taking the sentence apart in the locale files.\n const [before, after] = String(this.strings.inboxEmptyHintTemplate).split(\n \"{n}\"\n );\n const kbd = document.createElement(\"kbd\");\n kbd.className = CLASSES.INBOX_EMPTY_KBD;\n kbd.textContent = getShortcutText(this.options, this.strings);\n text.appendChild(document.createTextNode(before ?? \"\"));\n text.appendChild(kbd);\n text.appendChild(document.createTextNode(after ?? \"\"));\n empty.appendChild(text);\n\n const action = document.createElement(\"button\");\n action.type = \"button\";\n action.className = CLASSES.INBOX_EMPTY_ACTION;\n action.textContent = this.strings.inboxEmptyAction;\n action.addEventListener(\"click\", () =>\n this.callbacks.onActivateCommentMode()\n );\n empty.appendChild(action);\n\n return empty;\n }\n\n _pageFilterLabel(value) {\n return value === \"all\"\n ? this.strings.filterAll\n : this.strings.filterCurrentPage;\n }\n\n /**\n * Summary label for the collapsed filter button. The page filter always\n * contributes (it's either \"All pages\" or \"Current page\"); status, type,\n * and priority only join in when active, so an active filter is never\n * hidden from a user who hasn't opened the menu.\n */\n _filterSummaryLabel() {\n const parts = [this._pageFilterLabel(this.pageFilter)];\n if (this.statusFilter !== \"all\") {\n parts.push(statusLabelOf(this.statusFilter, this.strings));\n }\n if (this.typeFilter !== \"all\") {\n parts.push(typeLabelOf(this.typeFilter, this.strings));\n }\n if (this.priorityFilter !== \"all\") {\n parts.push(priorityLabelOf(this.priorityFilter, this.strings));\n }\n return parts.join(\" \u00B7 \");\n }\n\n _isFilterActive() {\n return (\n this.pageFilter !== \"page\" ||\n this.statusFilter !== \"all\" ||\n this.typeFilter !== \"all\" ||\n this.priorityFilter !== \"all\"\n );\n }\n\n /**\n * One chip group. Status, type and priority chips toggle: activating the\n * chip that is already on clears the group back to \"all\", which is why\n * they carry no explicit \"All\" chip. The page group does \u2014 it has no\n * neutral state, it's always one of two answers.\n *\n * @param {{ title: string, dataAttr: string, values: string[],\n * labelOf: (value: string) => string, selected: string,\n * toggles?: boolean, onSelect: (value: string) => void }} config\n */\n _buildFilterGroup({\n title,\n dataAttr,\n values,\n labelOf,\n selected,\n toggles = true,\n onSelect,\n }) {\n const group = document.createElement(\"div\");\n group.className = CLASSES.INBOX_FILTER_GROUP;\n\n const heading = document.createElement(\"div\");\n heading.className = CLASSES.INBOX_FILTER_SECTION;\n heading.textContent = title;\n group.appendChild(heading);\n\n const chips = document.createElement(\"div\");\n chips.className = CLASSES.INBOX_FILTER_CHIPS;\n\n for (const value of values) {\n const checked = selected === value;\n const chip = document.createElement(\"button\");\n chip.type = \"button\";\n chip.className = CLASSES.INBOX_FILTER_CHIP;\n chip.dataset[dataAttr] = value;\n chip.setAttribute(\"role\", toggles ? \"switch\" : \"radio\");\n chip.setAttribute(\"aria-checked\", String(checked));\n chip.textContent = labelOf(value);\n chip.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onSelect(toggles && checked ? \"all\" : value);\n this.render();\n });\n chips.appendChild(chip);\n }\n\n group.appendChild(chips);\n return group;\n }\n\n _buildFilter() {\n const wrapper = document.createElement(\"div\");\n wrapper.className = CLASSES.INBOX_FILTER + \"-wrapper\";\n\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_FILTER;\n btn.setAttribute(\"aria-haspopup\", \"true\");\n btn.setAttribute(\"aria-expanded\", \"false\");\n btn.innerHTML = `<span>${this._filterSummaryLabel()}</span>${CARET_ICON_SVG}`;\n\n const menu = document.createElement(\"div\");\n menu.className = CLASSES.INBOX_FILTER_MENU;\n menu.setAttribute(\"role\", \"group\");\n menu.setAttribute(\"aria-label\", this.strings.filterTitle);\n\n attachMenuToggle(btn, menu);\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_FILTER_MENU_HEADER;\n\n const title = document.createElement(\"span\");\n title.textContent = this.strings.filterTitle;\n header.appendChild(title);\n\n const clear = document.createElement(\"button\");\n clear.type = \"button\";\n clear.className = CLASSES.INBOX_FILTER_CLEAR;\n clear.textContent = this.strings.filterClear;\n clear.disabled = !this._isFilterActive();\n clear.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.pageFilter = \"page\";\n this.statusFilter = \"all\";\n this.typeFilter = \"all\";\n this.priorityFilter = \"all\";\n this.render();\n });\n header.appendChild(clear);\n menu.appendChild(header);\n\n menu.appendChild(\n this._buildFilterGroup({\n title: this.strings.filterByPage,\n dataAttr: \"filterPage\",\n values: [\"page\", \"all\"],\n labelOf: (value) => this._pageFilterLabel(value),\n selected: this.pageFilter,\n toggles: false,\n onSelect: (value) => (this.pageFilter = value),\n })\n );\n\n menu.appendChild(\n this._buildFilterGroup({\n title: this.strings.filterByStatus,\n dataAttr: \"filterStatus\",\n values: [...STATUSES],\n labelOf: (value) => statusLabelOf(value, this.strings),\n selected: this.statusFilter,\n onSelect: (value) => (this.statusFilter = value),\n })\n );\n\n menu.appendChild(\n this._buildFilterGroup({\n title: this.strings.filterByType,\n dataAttr: \"filterType\",\n values: [...COMMENT_TYPES],\n labelOf: (value) => typeLabelOf(value, this.strings),\n selected: this.typeFilter,\n onSelect: (value) => (this.typeFilter = value),\n })\n );\n\n menu.appendChild(\n this._buildFilterGroup({\n title: this.strings.filterByPriority,\n dataAttr: \"filterPriority\",\n values: [...PRIORITIES],\n labelOf: (value) => priorityLabelOf(value, this.strings),\n selected: this.priorityFilter,\n onSelect: (value) => (this.priorityFilter = value),\n })\n );\n\n wrapper.appendChild(btn);\n wrapper.appendChild(menu);\n return wrapper;\n }\n\n _buildCard(comment, { interactive }) {\n const card = document.createElement(\"div\");\n card.className = CLASSES.INBOX_CARD;\n if (comment.status === \"resolved\") {\n card.classList.add(`${CLASSES.INBOX_CARD}--resolved`);\n }\n card.dataset.commentId = comment.id;\n\n // Meta alone on its row, action strip on the next one \u2014 the same split\n // the thread popover makes. Sharing a row squeezed the author into\n // ~90px and wrapped the name onto two lines.\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_CARD_HEADER;\n header.appendChild(\n createMetaElement(\n comment.author,\n comment.createdAt,\n this.strings,\n this.locale,\n comment.editedAt\n )\n );\n card.appendChild(header);\n\n const actionsRow = document.createElement(\"div\");\n actionsRow.className = CLASSES.THREAD_ACTIONS_ROW;\n actionsRow.appendChild(this._buildCardActions(comment));\n card.appendChild(actionsRow);\n\n // The editor only ever replaces the body in the detail view. On a list\n // card it would sit inside a control that navigates on click, so the \u22EF\n // there routes through startEditing(), which opens the detail first.\n const editingThis =\n !interactive &&\n this.editing &&\n this.editing.replyId == null &&\n String(this.editing.commentId) === String(comment.id);\n\n if (editingThis) {\n card.appendChild(this._buildEditor());\n } else {\n const text = document.createElement(\"div\");\n text.className = CLASSES.INBOX_CARD_TEXT;\n text.textContent = comment.text;\n card.appendChild(text);\n }\n\n if (comment.screenshots?.length) {\n const shots = createScreenshotsDisplay(comment.screenshots, this.strings);\n shots\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.callbacks.onShowLightbox(img.src);\n });\n });\n card.appendChild(shots);\n }\n\n // Status, type and priority already sit in the action strip above as\n // labelled pickers; repeating them here was the same fact twice. Tags\n // and the resolution time have no control anywhere, so they remain \u2014\n // the row simply disappears when there is neither.\n const badges = createBadgeRow(comment, this.strings, {\n includeClassification: false,\n });\n if (badges) card.appendChild(badges);\n\n const tag = this._buildTag(comment);\n if (tag) card.appendChild(tag);\n\n if (interactive) {\n card.setAttribute(\"role\", \"button\");\n card.setAttribute(\"tabindex\", \"0\");\n\n // Inactive comments belong to another page: activating them hands\n // off to that page (the detail opens there after the redirect).\n const activate = () =>\n comment.anchorState === \"inactive\"\n ? this.callbacks.onNavigateToPage(comment)\n : this._openDetail(comment);\n\n const replyLink = document.createElement(\"button\");\n replyLink.type = \"button\";\n replyLink.className = CLASSES.INBOX_CARD_REPLY_LINK;\n replyLink.textContent = this.strings.replyLink;\n replyLink.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n activate();\n });\n card.appendChild(replyLink);\n\n card.addEventListener(\"click\", activate);\n card.addEventListener(\"keydown\", (/** @type {KeyboardEvent} */ e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n activate();\n }\n });\n\n // Hovering a card spotlights its marker on the page \u2014 only when the\n // marker is actually there (anchored, visible, not resolved).\n card.addEventListener(\"mouseenter\", () => this._highlight(comment));\n card.addEventListener(\"mouseleave\", () => this._clearHighlight());\n }\n\n return card;\n }\n\n _buildTag(comment) {\n let label = null;\n if (comment.anchorState === \"orphaned\") label = this.strings.orphanedBadge;\n else if (comment.hidden) label = this.strings.hiddenBadge;\n else if (comment.anchorState === \"inactive\") label = comment.page;\n if (!label) return null;\n\n const tag = document.createElement(\"span\");\n tag.className = CLASSES.INBOX_CARD_TAG;\n tag.textContent = label;\n return tag;\n }\n\n _buildCardActions(comment) {\n return createCommentActions(comment, {\n strings: this.strings,\n onCopy: (c) =>\n copyToClipboard(\n buildAgentContext(c, {\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n strings: this.strings,\n })\n ),\n onCopyLink: (c) =>\n copyToClipboard(buildCommentLink(c, this.options.linkParam)),\n // From a list card this opens the detail with the editor already up:\n // a textarea inside a card that navigates on click, and highlights a\n // marker on hover, would be fighting three behaviours at once.\n onEdit: (c) => this.startEditing(c.id),\n onSetStatus: (c, status) => this.callbacks.onSetStatus(c.id, status),\n onSetType: (c, type) => this.callbacks.onSetType(c.id, type),\n onSetPriority: (c, priority) =>\n this.callbacks.onSetPriority(c.id, priority),\n onDelete: (c) => {\n if (this.detailId === c.id) this.detailId = null;\n this.callbacks.onDelete(c.id);\n this.render();\n },\n });\n }\n\n _renderDetail(comment, comments) {\n const index = comments.indexOf(comment);\n\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_DETAIL_HEADER;\n\n const backBtn = document.createElement(\"button\");\n backBtn.type = \"button\";\n backBtn.className = CLASSES.INBOX_BACK;\n backBtn.innerHTML = `${CHEVRON_LEFT_SVG}<span>${this.strings.back}</span>`;\n backBtn.addEventListener(\"click\", async () => {\n if (this.editing && !(await this.releaseEditor())) return;\n this.detailId = null;\n this.render();\n });\n header.appendChild(backBtn);\n\n const nav = document.createElement(\"div\");\n nav.className = CLASSES.INBOX_CARD_ACTIONS;\n\n const navBtn = (svg, label, targetIndex) => {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = CLASSES.INBOX_NAV_BTN;\n btn.setAttribute(\"aria-label\", label);\n btn.title = label;\n btn.innerHTML = svg;\n const target = comments[targetIndex];\n btn.disabled = !target;\n if (target) {\n // Navigating to another comment takes the edited text off screen. A\n // draft that survived that invisibly and reappeared later would be\n // worse than being asked about it here.\n btn.addEventListener(\"click\", async () => {\n if (this.editing && !(await this.releaseEditor())) return;\n this._openDetail(target);\n });\n }\n return btn;\n };\n\n nav.appendChild(navBtn(ARROW_UP_SVG, this.strings.prevComment, index - 1));\n nav.appendChild(\n navBtn(ARROW_DOWN_SVG, this.strings.nextComment, index + 1)\n );\n nav.appendChild(this._closeButton());\n header.appendChild(nav);\n\n this.el.appendChild(header);\n\n const detail = document.createElement(\"div\");\n detail.className = CLASSES.INBOX_DETAIL;\n\n detail.appendChild(this._buildCard(comment, { interactive: false }));\n\n // Always expanded here: the detail view exists to show everything. The\n // thread popover renders the same block as a collapsed disclosure.\n const context = createContextBlock(comment, {\n strings: this.strings,\n onShowLightbox: (src) => this.callbacks.onShowLightbox(src),\n });\n if (context) detail.appendChild(context);\n\n const replies = document.createElement(\"div\");\n replies.className = CLASSES.INBOX_REPLIES;\n for (const reply of comment.replies || []) {\n const editingThisReply =\n this.editing &&\n String(this.editing.commentId) === String(comment.id) &&\n String(this.editing.replyId) === String(reply.id);\n\n const replyEl = createReplyElement(reply, this.strings, this.locale, {\n // Drops the row instead of re-rendering the detail: a full render\n // would also throw away whatever the user has half-typed in the\n // reply box below.\n onDelete: (r, el) => {\n if (this.callbacks.onDeleteReply(comment.id, r.id)) el.remove();\n },\n onEdit: (r) => this.startEditing(comment.id, r.id),\n editing: editingThisReply ? this._editorHandlers() : null,\n });\n replyEl\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.callbacks.onShowLightbox(img.src);\n });\n });\n replies.appendChild(replyEl);\n }\n detail.appendChild(replies);\n\n detail.appendChild(this._buildReplyInput(comment));\n this.el.appendChild(detail);\n }\n\n _buildReplyInput(comment) {\n const {\n container,\n inputEl,\n screenshotsContainer,\n attachBtn,\n fileInput,\n submitBtn,\n } = createInputArea(\n {\n areaClassName: CLASSES.THREAD_INPUT_AREA,\n inputTag: \"input\",\n inputClassName: CLASSES.THREAD_INPUT,\n inputPlaceholder: this.strings.replyPlaceholder,\n },\n this.strings\n );\n\n let pendingScreenshots = [];\n\n const updatePreview = () => {\n screenshotsContainer.innerHTML = \"\";\n screenshotsContainer.classList.toggle(\n CLASSES.ACTIVE,\n pendingScreenshots.length > 0\n );\n pendingScreenshots.forEach((dataUrl, i) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = dataUrl;\n img.alt = this.strings.attachedScreenshot;\n img.onclick = () => this.callbacks.onShowLightbox(dataUrl);\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = CLASSES.SCREENSHOT_REMOVE;\n removeBtn.setAttribute(\"aria-label\", this.strings.removeScreenshot);\n removeBtn.innerHTML = \"×\";\n removeBtn.onclick = (e) => {\n e.stopPropagation();\n pendingScreenshots.splice(i, 1);\n updatePreview();\n };\n item.appendChild(img);\n item.appendChild(removeBtn);\n screenshotsContainer.appendChild(item);\n });\n };\n\n attachBtn.addEventListener(\"click\", () => fileInput.click());\n fileInput.addEventListener(\"change\", (e) => {\n const file = /** @type {HTMLInputElement} */ (e.target).files[0];\n if (!file || pendingScreenshots.length >= 5) return;\n const reader = new FileReader();\n reader.onload = (ev) => {\n pendingScreenshots.push(ev.target.result);\n updatePreview();\n };\n reader.readAsDataURL(file);\n fileInput.value = \"\";\n });\n\n const submit = () => {\n const text = inputEl.value.trim();\n if (!text && pendingScreenshots.length === 0) return;\n this.callbacks.onReply(comment, text, [...pendingScreenshots]);\n pendingScreenshots = [];\n this.render();\n };\n\n submitBtn.addEventListener(\"click\", submit);\n inputEl.addEventListener(\"keydown\", (/** @type {KeyboardEvent} */ e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n });\n\n return container;\n }\n}\n", "import {\n renderPage,\n cropRegion,\n cropViewport,\n withHiddenOverlay,\n AUTO_SCALE,\n} from \"./capture.js\";\nimport { captureContext } from \"./metadata.js\";\nimport {\n CLASSES,\n IDS,\n SELECTORS,\n STATUSES,\n COMMENT_TYPES,\n PRIORITIES,\n} from \"./constants.js\";\nimport { getStyles, getGlobalStyles } from \"./styles.js\";\nimport { getShadowRoot, TAG_NAME } from \"./root-element.js\";\nimport { getStrings, detectLocale } from \"./i18n.js\";\nimport {\n createAnchor,\n resolveAnchor,\n generateElementSelector,\n} from \"./anchor.js\";\nimport {\n readStoredComments,\n writeStoredComments,\n mergeForStorage,\n PENDING_DETAIL_KEY,\n} from \"./storage.js\";\nimport { createId } from \"./id.js\";\nimport {\n buildCommentLink,\n readCommentLinkParam,\n DEFAULT_LINK_PARAM,\n} from \"./link.js\";\nimport {\n createToolbar,\n createCommentBox,\n createCommentCircle,\n createTooltip,\n createThreadPopover,\n createReplyElement,\n createEditedMark,\n} from \"./components.js\";\nimport { createInlineEditor, confirmDiscard } from \"./inline-editor.js\";\nimport { InboxView } from \"./inbox.js\";\nimport { createContextBlock } from \"./context-block.js\";\nimport { createCommentActions, copyToClipboard } from \"./comment-actions.js\";\nimport { buildAgentContext } from \"./agent-context.js\";\nimport { closeOpenMenus } from \"./menus.js\";\nimport { closeOpenConfirmDialogs } from \"./confirm-dialog.js\";\n\n// Tags are user-typed, so they arrive with stray case and whitespace.\n// Normalising here (rather than at each entry point) is what makes\n// \"Checkout\" and \"checkout \" the same tag for filtering.\nconst normalizeTags = (tags) => {\n const seen = new Set();\n for (const tag of tags) {\n const clean = String(tag).trim().toLowerCase();\n if (clean) seen.add(clean);\n }\n return [...seen];\n};\n\nclass CommentOverlay {\n /**\n * @param {import('./index.d.ts').CommentOverlayOptions} [options]\n */\n constructor(options = {}) {\n this.comments = [];\n this.commentMode = false;\n this.isMac = /Mac|iPod|iPhone|iPad/.test(navigator.userAgent);\n this.options = {\n shortcutKey: options.shortcutKey || (this.isMac ? \"c\" : \"C\"),\n shortcutModifier: options.shortcutModifier || \"alt\",\n autoScreenshot: options.autoScreenshot !== false,\n ...options,\n };\n this.locale = this.options.locale || detectLocale();\n this.strings = getStrings(this.locale);\n\n // Initialize resize observers and position validation\n this.resizeObservers = new Map();\n // Track mutation observers per comment\n this.mutationObservers = new Map();\n this.positionValidationEnabled = true;\n\n // rAF scheduling flag for bulk updates\n this._pendingRaf = null;\n this._pendingContextScreenshot = null;\n\n /**\n * The popover's open editor. Tracked as state even though the popover\n * mounts it straight into the DOM (it is built once and never\n * re-rendered): Escape, the close button and a click on the page all\n * need to know whether there is unsaved text before they act.\n * @type {{ commentId: any, replyId: any | null, draft: string } | null}\n */\n this._popoverEditing = null;\n /**\n * A comment someone asked to open \u2014 from a \"Copy link\" URL or the\n * cross-page handoff \u2014 that has not been found yet.\n * @type {string | null}\n */\n this._pendingDetailId = null;\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", () => this.initOverlay());\n } else {\n this.initOverlay();\n }\n }\n\n initOverlay() {\n // Mount inside a dedicated shadow root so widget styles/markup stay\n // isolated from the host page in both directions.\n this.shadowRoot = getShadowRoot();\n\n // Create and append UI elements\n this.toolbar = createToolbar(this.options, this.strings);\n this.commentBox = createCommentBox(this.strings);\n this.overlay = document.createElement(\"div\");\n this.overlay.className = CLASSES.COMMENT_OVERLAY;\n\n this.shadowRoot.appendChild(this.overlay);\n this.shadowRoot.appendChild(this.toolbar);\n this.shadowRoot.appendChild(this.commentBox);\n\n this.commentBtn = this.toolbar.querySelector(\n `.${CLASSES.TOOLBAR_COMMENT_BTN}`\n );\n this.inboxBtn = this.toolbar.querySelector(`.${CLASSES.TOOLBAR_MENU_BTN}`);\n /** @type {HTMLButtonElement} */\n this.submitButton = /** @type {any} */ (\n this.shadowRoot.getElementById(IDS.SUBMIT_COMMENT)\n );\n /** @type {HTMLTextAreaElement} */\n this.commentInput = /** @type {any} */ (\n this.shadowRoot.getElementById(IDS.COMMENT_INPUT)\n );\n this.attachImageBtn = this.commentBox.querySelector(\n `.${CLASSES.ATTACH_IMAGE_BTN}`\n );\n /** @type {HTMLInputElement} */\n this.attachImageInput = /** @type {any} */ (\n this.shadowRoot.getElementById(IDS.ATTACH_IMAGE_INPUT)\n );\n\n // Bind event listeners\n this.bindEventListeners();\n this.setupKeyboardShortcut();\n this.setupResizeHandlers();\n this.injectStyles();\n\n this._pendingDetailId = this._readPendingDetailId();\n\n if (this.options.persistence === \"localStorage\") {\n this.loadComments(readStoredComments());\n }\n // Also outside localStorage mode: a host that persists comments itself\n // still deserves to have the link honoured, and until its loadComments()\n // arrives the inbox is what tells the user the link was understood.\n this._openPendingDetail();\n }\n\n _navigateTo(url) {\n location.assign(url);\n }\n\n /**\n * Where a request to open one comment can come from. Two sources, one\n * slot: an inactive card clicked on the previous page (sessionStorage), or\n * a \"Copy link\" URL someone was sent. The URL wins when both are present \u2014\n * it is the one the user acted on just now.\n * @returns {string | null}\n */\n _readPendingDetailId() {\n const fromLink = readCommentLinkParam(this._linkParam());\n let fromHandoff = null;\n try {\n fromHandoff = sessionStorage.getItem(PENDING_DETAIL_KEY);\n // One-shot: read it and it is spent, whether or not it resolves.\n if (fromHandoff != null) sessionStorage.removeItem(PENDING_DETAIL_KEY);\n } catch {\n // A blocked sessionStorage only costs the handoff, not the link.\n }\n return fromLink ?? fromHandoff;\n }\n\n _linkParam() {\n return this.options.linkParam || DEFAULT_LINK_PARAM;\n }\n\n /**\n * Opens the inbox on the pending comment, if there is one.\n *\n * Deliberately does NOT give up when the id fails to resolve: a host that\n * fetches its comments from its own back end has not called loadComments()\n * yet at startup, and that is precisely the setup where a link is worth\n * sending to another person. The id is kept and this runs again after\n * every load, so the inbox switches from \"not on this page\" to the comment\n * the moment the data lands.\n */\n _openPendingDetail() {\n const id = this._pendingDetailId;\n if (!id) return;\n\n const comment = this.comments.find((c) => String(c.id) === String(id));\n if (!comment) {\n // Opening the inbox anyway is the point: clicking a link and having\n // nothing at all happen is indistinguishable from a broken widget.\n this.showInbox();\n this.inboxView?.showNotice(this.strings.commentNotFound);\n return;\n }\n\n this._pendingDetailId = null;\n this.showInbox();\n this.inboxView.clearNotice();\n this.inboxView.openDetail(comment.id);\n }\n\n /**\n * The body element of the root comment, or of one reply, inside the open\n * popover. Returns whatever is currently there \u2014 the text node or the\n * editor that replaced it.\n */\n _popoverBodyEl(replyId = null) {\n const popover = this.activeThreadPopover;\n if (!popover) return null;\n if (replyId == null) {\n return popover.querySelector(\n `.${CLASSES.THREAD_SCROLL} > .${CLASSES.THREAD_BODY}, .${CLASSES.THREAD_SCROLL} > .${CLASSES.EDITOR}`\n );\n }\n const row = popover.querySelector(\n `.${CLASSES.THREAD_REPLY}[data-reply-id=\"${replyId}\"]`\n );\n return (\n row?.querySelector(`.${CLASSES.THREAD_BODY}, .${CLASSES.EDITOR}`) || null\n );\n }\n\n /**\n * Puts the edited text back on screen where the panels do not rebuild\n * themselves: the open thread quotes the text that just changed, and the\n * hover tooltip is thrown away on mouseleave so it needs nothing.\n */\n _refreshCommentViews(id, replyId = null) {\n if (this.activeThreadPopover?.dataset.for !== String(id)) return;\n const comment = this.comments.find((c) => String(c.id) === String(id));\n if (!comment) return;\n\n const source =\n replyId == null\n ? comment\n : (comment.replies || []).find((r) => String(r.id) === String(replyId));\n if (!source) return;\n\n const body = document.createElement(\"div\");\n body.className = CLASSES.THREAD_BODY;\n body.textContent = source.text;\n this._popoverBodyEl(replyId)?.replaceWith(body);\n\n // The \"edited\" mark belongs to the same meta line the author and time\n // are on, and it is absent until the first edit.\n const meta =\n replyId == null\n ? this.activeThreadPopover.querySelector(`.${CLASSES.THREAD_META}`)\n : this.activeThreadPopover\n .querySelector(\n `.${CLASSES.THREAD_REPLY}[data-reply-id=\"${replyId}\"]`\n )\n ?.querySelector(`.${CLASSES.THREAD_META}`);\n if (\n meta &&\n source.editedAt &&\n !meta.querySelector(`.${CLASSES.THREAD_EDITED}`)\n ) {\n const editedEl = createEditedMark(\n source.editedAt,\n this.strings,\n this.locale\n );\n // Before the \u22EF, which `margin-left: auto` has pushed to the far right.\n const actions = meta.querySelector(`.${CLASSES.THREAD_REPLY_ACTIONS}`);\n if (actions) meta.insertBefore(editedEl, actions);\n else meta.appendChild(editedEl);\n }\n }\n\n /** True while the popover holds an editor with unsaved text. */\n _popoverEditorDirty() {\n if (!this._popoverEditing) return false;\n const { commentId, replyId, draft } = this._popoverEditing;\n const comment = this.comments.find(\n (c) => String(c.id) === String(commentId)\n );\n const source =\n replyId == null\n ? comment\n : (comment?.replies || []).find(\n (r) => String(r.id) === String(replyId)\n );\n return draft.trim() !== String(source?.text || \"\").trim();\n }\n\n /**\n * Single gate in front of everything that would take the popover's editor\n * off screen. Mirrors InboxView.releaseEditor so the two panels answer the\n * same question the same way.\n * @returns {Promise<boolean>} true when the caller may proceed\n */\n async _releasePopoverEditor() {\n if (!this._popoverEditing) return true;\n if (this._popoverEditorDirty()) {\n const host = /** @type {any} */ (this.shadowRoot);\n if (!(await confirmDiscard(host, this.strings))) return false;\n }\n const { replyId } = this._popoverEditing;\n this._popoverEditing = null;\n this._restorePopoverBody(replyId);\n return true;\n }\n\n _restorePopoverBody(replyId) {\n const comment = this.comments.find(\n (c) => String(c.id) === String(this.activeThreadPopover?.dataset.for)\n );\n if (!comment) return;\n const source =\n replyId == null\n ? comment\n : (comment.replies || []).find((r) => String(r.id) === String(replyId));\n if (!source) return;\n\n const body = document.createElement(\"div\");\n body.className = CLASSES.THREAD_BODY;\n body.textContent = source.text;\n this._popoverBodyEl(replyId)?.replaceWith(body);\n }\n\n /**\n * Opens the editor inside the popover. Unlike the inbox this mounts into\n * the DOM directly: the popover is built once and never re-rendered, so\n * there is nothing to survive. The draft is still tracked as state, because\n * Escape, the close button and a click outside all have to know whether\n * there is anything to lose.\n */\n async _startPopoverEditing(commentId, replyId = null) {\n if (!(await this._releasePopoverEditor())) return;\n\n const comment = this.comments.find(\n (c) => String(c.id) === String(commentId)\n );\n const source =\n replyId == null\n ? comment\n : (comment?.replies || []).find(\n (r) => String(r.id) === String(replyId)\n );\n if (!source) return;\n\n this._popoverEditing = { commentId, replyId, draft: source.text };\n\n const editor = createInlineEditor({\n value: source.text,\n strings: this.strings,\n onInput: (text) => {\n this._popoverEditing.draft = text;\n },\n onSave: (text) => {\n const saved =\n replyId == null\n ? this.editComment(commentId, text)\n : this.editReply(commentId, replyId, text);\n this._popoverEditing = null;\n if (saved) {\n this._refreshCommentViews(commentId, replyId);\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n } else {\n this._restorePopoverBody(replyId);\n }\n },\n onCancel: () => {\n this._releasePopoverEditor();\n },\n });\n\n this._popoverBodyEl(replyId)?.replaceWith(editor);\n }\n\n /** The shareable URL for a comment, as \"Copy link\" builds it. */\n commentLink(id) {\n const comment = this.comments.find((c) => String(c.id) === String(id));\n return comment ? buildCommentLink(comment, this._linkParam()) : null;\n }\n\n _syncStorage() {\n if (this.options.persistence !== \"localStorage\") return;\n writeStoredComments(\n mergeForStorage(\n readStoredComments(),\n this.serializeComments(),\n location.pathname\n )\n );\n }\n\n bindEventListeners() {\n this.commentBtn.addEventListener(\"click\", () => this.toggleCommentMode());\n this.inboxBtn.addEventListener(\"click\", () => this.toggleInbox());\n this.submitButton.addEventListener(\"click\", () => this.saveComment());\n\n this.commentInput.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n this.saveComment();\n }\n });\n\n this.attachImageBtn.addEventListener(\"click\", () => {\n this.attachImageInput.click();\n });\n\n this.attachImageInput.addEventListener(\"change\", (e) => {\n const file = /** @type {HTMLInputElement} */ (e.target).files[0];\n if (!file) return;\n if (!this._pendingScreenshots) this._pendingScreenshots = [];\n if (this._pendingScreenshots.length >= 5) return;\n\n const reader = new FileReader();\n reader.onload = (ev) => {\n this._pendingScreenshots.push(ev.target.result);\n this._updateScreenshotsPreview();\n };\n reader.readAsDataURL(file);\n this.attachImageInput.value = \"\";\n });\n\n this._handleDocumentClickBound = (e) => this.handleDocumentClick(e);\n document.addEventListener(\"mousedown\", this._handleDocumentClickBound);\n }\n\n setupKeyboardShortcut() {\n // Remove any existing event listeners\n if (this.keydownHandler) {\n document.removeEventListener(\"keydown\", this.keydownHandler);\n }\n\n // Create a new handler with proper binding\n this.keydownHandler = (e) => {\n if (e.key === \"Escape\") {\n if (this._activeLightbox) {\n this.closeLightbox();\n } else if (this.activeThreadPopover) {\n // An open editor answers Escape first, and closes only itself. The\n // editor's own textarea stops the event before it reaches here, so\n // this branch is for an Escape pressed with focus somewhere else in\n // the popover \u2014 which must not take the panel down either.\n if (this._popoverEditing) this._releasePopoverEditor();\n else this.closeThreadPopover();\n } else if (this.inboxView?.isOpen()) {\n if (this.inboxView.editing) {\n this.inboxView\n .releaseEditor()\n .then((released) => released && this.inboxView?.refresh());\n } else {\n this.closeInbox();\n }\n } else if (this.commentBox.style.display !== \"none\") {\n this.hideCommentBox();\n this.toggleCommentMode();\n } else if (this.commentMode) {\n this.toggleCommentMode();\n }\n return;\n }\n\n const isMacOptionC =\n this.isMac && e.altKey && (e.key === \"\u00E7\" || e.key === \"\u00C7\");\n const isWindowsAltC =\n !this.isMac && e.altKey && e.key.toLowerCase() === \"c\";\n const isCustomShortcut =\n e.key.toLowerCase() === this.options.shortcutKey.toLowerCase() &&\n ((this.options.shortcutModifier === \"alt\" && e.altKey) ||\n (this.options.shortcutModifier === \"ctrl\" &&\n (e.ctrlKey || e.metaKey)) ||\n (this.options.shortcutModifier === \"shift\" && e.shiftKey));\n\n if (isMacOptionC || isWindowsAltC || isCustomShortcut) {\n e.preventDefault();\n e.stopPropagation();\n this.toggleCommentMode();\n return false;\n }\n };\n\n // Add the event listener\n document.addEventListener(\"keydown\", this.keydownHandler);\n }\n\n handleDocumentClick(e) {\n if (!this.commentMode) return;\n\n // Listener is attached on `document`, outside the shadow boundary, so\n // `e.target` gets retargeted to the shadow host. Use composedPath() to\n // recover the real, deepest target inside the shadow tree.\n const target = e.composedPath()[0] || e.target;\n\n if (\n this.toolbar.contains(target) ||\n target?.closest?.(`.${CLASSES.CIRCLE}`) ||\n target?.closest?.(`.${CLASSES.TOOLTIP}`) ||\n target?.closest?.(`.${CLASSES.THREAD_POPOVER}`) ||\n target?.closest?.(`.${CLASSES.INBOX_PANEL}`) ||\n target?.closest?.(`.${CLASSES.LIGHTBOX}`)\n ) {\n return;\n }\n\n if (this.commentBox.contains(target)) {\n return;\n }\n\n if (this.commentBox.style.display !== \"none\") {\n this.hideCommentBox();\n this.toggleCommentMode();\n return;\n }\n\n if (e.button !== 0) return;\n e.preventDefault();\n\n this._dragStart = { x: e.clientX, y: e.clientY };\n this._isDragging = false;\n\n this._boundDragMove = (ev) => this._onDragMove(ev);\n this._boundDragEnd = (ev) => this._onDragEnd(ev);\n document.addEventListener(\"mousemove\", this._boundDragMove);\n document.addEventListener(\"mouseup\", this._boundDragEnd);\n }\n\n _onDragMove(e) {\n const dx = e.clientX - this._dragStart.x;\n const dy = e.clientY - this._dragStart.y;\n\n if (!this._isDragging && Math.hypot(dx, dy) < 5) return;\n\n this._isDragging = true;\n\n const left = Math.min(this._dragStart.x, e.clientX);\n const top = Math.min(this._dragStart.y, e.clientY);\n const width = Math.abs(dx);\n const height = Math.abs(dy);\n\n if (!this._selectionRect) {\n this._selectionRect = document.createElement(\"div\");\n this._selectionRect.className = CLASSES.SELECTION_RECT;\n this.shadowRoot.appendChild(this._selectionRect);\n }\n\n this._selectionRect.style.left = `${left}px`;\n this._selectionRect.style.top = `${top}px`;\n this._selectionRect.style.width = `${width}px`;\n this._selectionRect.style.height = `${height}px`;\n }\n\n async _onDragEnd(e) {\n document.removeEventListener(\"mousemove\", this._boundDragMove);\n document.removeEventListener(\"mouseup\", this._boundDragEnd);\n\n if (this._isDragging) {\n const left = Math.min(this._dragStart.x, e.clientX);\n const top = Math.min(this._dragStart.y, e.clientY);\n const width = Math.abs(e.clientX - this._dragStart.x);\n const height = Math.abs(e.clientY - this._dragStart.y);\n\n this._selectionRect?.remove();\n this._selectionRect = null;\n\n if (width > 10 && height > 10) {\n try {\n if (!this._pendingScreenshots) this._pendingScreenshots = [];\n // One render feeds both images: the PNG region the user selected\n // and the automatic JPEG context shot.\n const full = await withHiddenOverlay(() => renderPage({ scale: 1 }));\n const dataUrl = cropRegion(full, { left, top, width, height });\n if (dataUrl && this._pendingScreenshots.length < 5) {\n this._pendingScreenshots.push(dataUrl);\n }\n if (this.options.autoScreenshot) {\n this._pendingContextScreenshot = cropViewport(full, {\n sourceScale: 1,\n });\n }\n } catch (err) {\n console.warn(\"Screenshot capture failed:\", err);\n }\n }\n\n await this._placeCommentAtPoint(e.clientX, e.clientY);\n } else {\n await this._placeCommentAtPoint(this._dragStart.x, this._dragStart.y);\n }\n\n this._isDragging = false;\n this._dragStart = null;\n }\n\n async _placeCommentAtPoint(clientX, clientY) {\n // The no-drag path has no render yet. Half scale because the output is\n // half scale anyway \u2014 the render is the expensive part, and it costs\n // ~4x less here than at scale 1.\n if (this.options.autoScreenshot && !this._pendingContextScreenshot) {\n try {\n const full = await withHiddenOverlay(() =>\n renderPage({ scale: AUTO_SCALE })\n );\n this._pendingContextScreenshot = cropViewport(full, {\n sourceScale: AUTO_SCALE,\n });\n } catch (err) {\n console.warn(\"HellDots: automatic screenshot failed\", err);\n this._pendingContextScreenshot = null;\n }\n }\n\n const prevPointerEvents = this.overlay.style.pointerEvents;\n this.overlay.style.pointerEvents = \"none\";\n const underlying = document.elementFromPoint(clientX, clientY);\n this.overlay.style.pointerEvents = prevPointerEvents || \"\";\n\n const container =\n underlying?.closest?.(SELECTORS.CONTAINER) || document.body;\n const containerRect = container.getBoundingClientRect();\n\n // Zero-size containers (display:none, not yet laid out) would make the\n // division blow up to Infinity \u2014 which isn't JSON-serializable either.\n const relativeX =\n containerRect.width > 0\n ? (clientX - containerRect.left) / containerRect.width\n : 0;\n const relativeY =\n containerRect.height > 0\n ? (clientY - containerRect.top) / containerRect.height\n : 0;\n\n const anchor = createAnchor(\n /** @type {HTMLElement} */ (container),\n relativeX,\n relativeY\n );\n // The clicked element can disappear (responsive display:none) while the\n // coarse anchor container stays visible \u2014 track it separately so the\n // marker hides with what the user actually commented on.\n anchor.targetSelector =\n underlying && underlying !== container\n ? generateElementSelector(/** @type {HTMLElement} */ (underlying))\n : null;\n\n this.currentPosition = {\n container,\n relativeX,\n relativeY,\n anchor,\n target: /** @type {HTMLElement} */ (underlying || container),\n };\n\n this.createPreviewCircle(clientX, clientY);\n document.body.classList.remove(CLASSES.COMMENT_CURSOR);\n\n if (this._pendingScreenshots && this._pendingScreenshots.length > 0) {\n this._updateScreenshotsPreview();\n }\n\n this.showCommentBox(clientX, clientY);\n }\n\n _updateScreenshotsPreview() {\n const container = this.commentBox.querySelector(\n `.${CLASSES.SCREENSHOTS_CONTAINER}`\n );\n if (!container) return;\n container.innerHTML = \"\";\n\n if (!this._pendingScreenshots || this._pendingScreenshots.length === 0) {\n container.classList.remove(CLASSES.ACTIVE);\n return;\n }\n\n container.classList.add(CLASSES.ACTIVE);\n\n this._pendingScreenshots.forEach((dataUrl, i) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = dataUrl;\n img.alt = this.strings.attachedScreenshot;\n img.onclick = () => this.showLightbox(dataUrl);\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = CLASSES.SCREENSHOT_REMOVE;\n removeBtn.setAttribute(\"aria-label\", this.strings.removeScreenshot);\n removeBtn.innerHTML = \"×\";\n removeBtn.onclick = (e) => {\n e.stopPropagation();\n this._pendingScreenshots.splice(i, 1);\n this._updateScreenshotsPreview();\n };\n\n item.appendChild(img);\n item.appendChild(removeBtn);\n container.appendChild(item);\n });\n }\n\n _clearScreenshotPreview() {\n this._pendingScreenshots = [];\n const container = this.commentBox.querySelector(\n `.${CLASSES.SCREENSHOTS_CONTAINER}`\n );\n if (container) {\n container.innerHTML = \"\";\n container.classList.remove(CLASSES.ACTIVE);\n }\n }\n\n showCommentBox(x, y) {\n this.commentBox.style.display = \"block\";\n\n const circleBaseSize = 28;\n const circleRadius = circleBaseSize / 2;\n const offset = circleRadius + 10;\n const windowWidth = window.innerWidth;\n const windowHeight = window.innerHeight;\n\n // Measured, not assumed: the box is 400px wide on a roomy viewport but\n // narrows to `100vw - 24px` on a phone. A hardcoded width here is what\n // used to push it off the right edge on mobile.\n const boxRect = this.commentBox.getBoundingClientRect();\n const boxWidth = boxRect.width || 400;\n\n const centerX = x + circleRadius;\n const centerY = y + circleRadius;\n\n let adjustedX = centerX + offset;\n let adjustedY = centerY - circleRadius;\n\n if (adjustedX + boxWidth > windowWidth) {\n adjustedX = centerX - offset - boxWidth;\n }\n // Clamp both edges: on a viewport narrower than the box plus its\n // margins, flipping to the other side isn't enough on its own.\n adjustedX = Math.min(adjustedX, windowWidth - boxWidth - 10);\n adjustedX = Math.max(10, adjustedX);\n\n if (adjustedY + boxRect.height > windowHeight) {\n adjustedY = windowHeight - boxRect.height - 10;\n }\n adjustedY = Math.max(10, adjustedY);\n\n this.commentBox.style.left = `${adjustedX}px`;\n this.commentBox.style.top = `${adjustedY}px`;\n\n this.commentInput.value = \"\";\n setTimeout(() => this.commentInput.focus(), 50);\n }\n\n hideCommentBox() {\n this.commentBox.style.display = \"none\";\n this.commentInput.style.height = \"auto\";\n this.currentPosition = null;\n this.removePreviewCircle();\n this._clearScreenshotPreview();\n this._pendingContextScreenshot = null;\n /** @type {any} */ (this.commentBox).classify?.reset();\n\n if (this.commentMode) {\n document.body.classList.add(CLASSES.COMMENT_CURSOR);\n }\n }\n\n toggleCommentMode() {\n this.commentMode = !this.commentMode;\n // The inbox is a full-height panel over the page; leaving it open would\n // cover the very content the user now has to click on. Clicking the\n // toolbar button already closed it as an outside click \u2014 this is what\n // covers the keyboard shortcut and the empty state's own button.\n if (this.commentMode) this.closeInbox();\n this.commentBtn?.classList.toggle(CLASSES.ACTIVE, this.commentMode);\n this.commentBtn?.setAttribute(\"aria-pressed\", String(this.commentMode));\n this.overlay.classList.toggle(CLASSES.ACTIVE, this.commentMode);\n document.body.classList.toggle(CLASSES.COMMENT_CURSOR, this.commentMode);\n\n if (!this.commentMode) {\n this.hideCommentBox();\n }\n }\n\n saveComment() {\n if (!this.commentInput.value.trim() || !this.currentPosition) return;\n\n const comment = {\n text: this.commentInput.value,\n container: this.currentPosition.container,\n relativeX: this.currentPosition.relativeX,\n relativeY: this.currentPosition.relativeY,\n anchor: this.currentPosition.anchor,\n anchorState: \"anchored\",\n target: this.currentPosition.target,\n hidden: false,\n status: \"open\",\n page: location.pathname,\n id: createId(),\n replies: [],\n author: this.options.user?.name || this.strings.anonymous,\n createdAt: new Date().toISOString(),\n screenshots: this._pendingScreenshots\n ? [...this._pendingScreenshots]\n : [],\n type: /** @type {any} */ (this.commentBox).classify?.getType() ?? null,\n priority:\n /** @type {any} */ (this.commentBox).classify?.getPriority() ?? null,\n // No longer authored in the widget \u2014 kept on the model for\n // setCommentTags() and for comments imported through loadComments().\n tags: [],\n resolvedAt: null,\n context: captureContext(),\n contextScreenshot: this._pendingContextScreenshot,\n };\n\n this.comments.push(comment);\n this._syncStorage();\n this.options.onCommentCreated?.(this._serializeComment(comment));\n this.renderCommentCircle(comment);\n this.hideCommentBox();\n this.toggleCommentMode();\n\n const circle = this.shadowRoot.querySelector(\n `[data-comment-id=\"${comment.id}\"]`\n );\n if (circle) {\n this.showThreadPopover(circle, comment);\n }\n }\n\n renderCommentCircle(comment) {\n const circle = createCommentCircle(comment, this.strings);\n\n circle.addEventListener(\"mouseenter\", () =>\n this.showCommentTooltip(circle, comment)\n );\n circle.addEventListener(\"mouseleave\", () => {\n setTimeout(() => {\n const tooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (tooltip && !tooltip.matches(\":hover\")) {\n tooltip.remove();\n }\n }, 250);\n });\n\n circle.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const tooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (tooltip) tooltip.remove();\n // The marker toggles its own thread: clicking the active marker\n // closes it rather than tearing the popover down and rebuilding an\n // identical one. Only its own \u2014 clicking a different marker still\n // switches to that thread.\n if (this.activeThreadPopover?.dataset.for === String(comment.id)) {\n this.closeThreadPopover();\n return;\n }\n this.showThreadPopover(circle, comment);\n });\n\n // The circle is a <div role=\"button\">, so unlike a real <button> it\n // doesn't get Enter/Space-activates-click for free.\n circle.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n circle.click();\n }\n });\n\n this.overlay.appendChild(circle);\n this.updateCommentPosition(comment, circle);\n\n this.createResizeObserver(comment, circle);\n this.createMutationObserver(comment);\n }\n\n showCommentTooltip(circle, comment) {\n const existingPopover = this.shadowRoot.querySelector(\n `.${CLASSES.THREAD_POPOVER}[data-for=\"${comment.id}\"]`\n );\n if (existingPopover) return;\n\n const existingTooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (existingTooltip) return;\n\n const tooltip = createTooltip(comment, this.strings, this.locale);\n this.shadowRoot.appendChild(tooltip);\n\n tooltip\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.showLightbox(img.src);\n });\n });\n\n setTimeout(() => {\n this.positionPopoverAtCircle(tooltip, circle);\n }, 10);\n\n tooltip\n .querySelector(`.${CLASSES.CLOSE_TOOLTIP}`)\n .addEventListener(\"click\", (e) => {\n e.stopPropagation();\n tooltip.remove();\n });\n\n tooltip.addEventListener(\"mouseleave\", () => tooltip.remove());\n }\n\n toggleInbox() {\n if (this.inboxView?.isOpen()) {\n this.closeInbox();\n } else {\n this.showInbox();\n }\n }\n\n showInbox() {\n this.closeThreadPopover();\n\n if (!this.inboxView) {\n this.inboxView = new InboxView({\n shadowRoot: this.shadowRoot,\n strings: this.strings,\n locale: this.locale,\n currentPage: location.pathname,\n getComments: () => this.comments,\n options: this.options,\n callbacks: {\n onActivateCommentMode: () => {\n this.closeInbox();\n // Never a toggle: the button reads \"turn on comment mode\", so\n // pressing it while the mode is already on must not turn it off.\n if (!this.commentMode) this.toggleCommentMode();\n },\n onOpenDetailScroll: (comment) => this.scrollMarkerIntoView(comment),\n onReply: (comment, text, screenshots) =>\n this.addReply(comment, text, screenshots),\n onDelete: (id) => this.deleteComment(id),\n onDeleteReply: (commentId, replyId) =>\n this.deleteReply(commentId, replyId),\n onEditComment: (id, text) => {\n if (!this.editComment(id, text)) return;\n // The marker's hover tooltip and the open thread both quote the\n // text that just changed, and neither rebuilds on its own.\n this._refreshCommentViews(id);\n },\n onEditReply: (commentId, replyId, text) => {\n if (!this.editReply(commentId, replyId, text)) return;\n this._refreshCommentViews(commentId);\n },\n onSetStatus: (id, status) => this.setCommentStatus(id, status),\n onSetType: (id, type) => this.setCommentType(id, type),\n onSetPriority: (id, priority) =>\n this.setCommentPriority(id, priority),\n onNavigateToPage: (comment) => {\n try {\n sessionStorage.setItem(PENDING_DETAIL_KEY, String(comment.id));\n } catch {}\n this._navigateTo(comment.page);\n },\n onShowLightbox: (src) => this.showLightbox(src),\n onClose: () => this.closeInbox(),\n },\n });\n }\n this.inboxView.open();\n\n setTimeout(() => {\n this._inboxClickHandler = (e) => {\n const target = e.composedPath()[0] || e.target;\n if (\n !this.inboxView.el?.contains(target) &&\n !this.inboxBtn.contains(target) &&\n !this._isInsideLightbox(target)\n ) {\n // Same reasoning as the thread popover: an unsaved draft turns a\n // click outside into \"stay open\", not into a question.\n if (this.inboxView.isDirty()) return;\n this.closeInbox();\n }\n };\n document.addEventListener(\"mousedown\", this._inboxClickHandler);\n }, 0);\n }\n\n closeInbox() {\n this.inboxView?.close();\n if (this._inboxClickHandler) {\n document.removeEventListener(\"mousedown\", this._inboxClickHandler);\n this._inboxClickHandler = null;\n }\n }\n\n // `circle` may be null for orphaned comments (opened from the inbox):\n // the popover is centered in the viewport instead of pinned to a marker.\n showThreadPopover(circle, comment) {\n this.closeThreadPopover();\n\n const existingTooltip = this.shadowRoot.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`\n );\n if (existingTooltip) existingTooltip.remove();\n\n // Keeps the selected marker visibly picked out while its thread is open\n // \u2014 same growth as hover, plus a ring, so it survives the pointer\n // leaving the circle to reach the popover.\n circle?.classList.add(CLASSES.CIRCLE_ACTIVE);\n // Remembered so scrolling can keep the popover pinned to it. Null for\n // orphaned comments opened from the inbox \u2014 those get centered instead\n // and have no marker to follow.\n this._activePopoverCircle = circle || null;\n\n const onDeleteReply = (reply, replyEl) => {\n if (!this.deleteReply(comment.id, reply.id)) return;\n replyEl.remove();\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n };\n\n const popover = createThreadPopover(comment, this.strings, this.locale, {\n onDeleteReply,\n onEditReply: (reply) => this._startPopoverEditing(comment.id, reply.id),\n });\n this.shadowRoot.appendChild(popover);\n\n // Same action strip as the inbox cards: copy agent context, lifecycle\n // status picker (RF09) and the \u22EF menu.\n const headerEl = popover.querySelector(`.${CLASSES.THREAD_HEADER}`);\n const actionsEl = createCommentActions(comment, {\n strings: this.strings,\n onCopy: (c) =>\n copyToClipboard(\n buildAgentContext(c, {\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n strings: this.strings,\n })\n ),\n onCopyLink: (c) =>\n copyToClipboard(buildCommentLink(c, this._linkParam())),\n onEdit: (c) => this._startPopoverEditing(c.id),\n onSetStatus: (c, status) => this.setCommentStatus(c.id, status),\n onSetType: (c, type) => this.setCommentType(c.id, type),\n onSetPriority: (c, priority) => this.setCommentPriority(c.id, priority),\n onDelete: (c) => {\n this.closeThreadPopover();\n this.deleteComment(c.id);\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n },\n });\n // Its own row under the header, for the same reason the inbox card has\n // a footer: five controls sharing the header left the author ~90px and\n // truncated it mid-name.\n const actionsRow = document.createElement(\"div\");\n actionsRow.className = CLASSES.THREAD_ACTIONS_ROW;\n actionsRow.appendChild(actionsEl);\n headerEl.insertAdjacentElement(\"afterend\", actionsRow);\n\n // The root comment's gallery, not the reply box's pending previews.\n const mainScreenshotsContainer = popover.querySelector(\n `.${CLASSES.THREAD_SCROLL} > .${CLASSES.SCREENSHOTS_CONTAINER}`\n );\n if (mainScreenshotsContainer) {\n mainScreenshotsContainer\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.showLightbox(img.src);\n });\n });\n }\n\n // RF2 \u2014 the automatic capture used to be reachable only from the inbox\n // detail. Collapsed by default so the popover stays a conversation\n // first; built here because it needs the lightbox callback.\n const contextBlock = createContextBlock(comment, {\n strings: this.strings,\n onShowLightbox: (src) => this.showLightbox(src),\n collapsible: true,\n });\n if (contextBlock) {\n // `.before()` rather than popover.insertBefore(): the replies live\n // inside the scroll container, not directly under the popover.\n popover.querySelector(`.${CLASSES.THREAD_REPLIES}`).before(contextBlock);\n }\n\n setTimeout(() => {\n if (circle) {\n this.positionPopoverAtCircle(popover, circle);\n } else {\n this.centerPopover(popover);\n }\n }, 10);\n\n popover\n .querySelector(`.${CLASSES.CLOSE_TOOLTIP}`)\n .addEventListener(\"click\", async (e) => {\n e.stopPropagation();\n // Unlike a click on the page, pressing \u00D7 is an unambiguous request\n // to close, so an unsaved draft is worth one question. The guard\n // short-circuits before the await, keeping the no-editor path\n // synchronous.\n if (this._popoverEditing && !(await this._releasePopoverEditor()))\n return;\n this.closeThreadPopover();\n });\n\n /** @type {HTMLInputElement} */\n const input = /** @type {any} */ (\n popover.querySelector(`.${CLASSES.THREAD_INPUT}`)\n );\n const submitBtn = popover.querySelector(`.${CLASSES.THREAD_SUBMIT}`);\n const threadAttachBtn = popover.querySelector(\n `.${CLASSES.THREAD_INPUT_AREA} .${CLASSES.ATTACH_IMAGE_BTN}`\n );\n /** @type {HTMLInputElement} */\n const threadFileInput = /** @type {any} */ (\n popover.querySelector(`.${CLASSES.THREAD_INPUT_AREA} input[type=\"file\"]`)\n );\n const threadScreenshotsContainer = popover.querySelector(\n `.${CLASSES.THREAD_INPUT_AREA} .${CLASSES.SCREENSHOTS_CONTAINER}`\n );\n\n let pendingReplyScreenshots = [];\n\n const updateReplyScreenshotsPreview = () => {\n threadScreenshotsContainer.innerHTML = \"\";\n if (pendingReplyScreenshots.length === 0) {\n threadScreenshotsContainer.classList.remove(CLASSES.ACTIVE);\n return;\n }\n threadScreenshotsContainer.classList.add(CLASSES.ACTIVE);\n pendingReplyScreenshots.forEach((dataUrl, i) => {\n const item = document.createElement(\"div\");\n item.className = CLASSES.SCREENSHOT_ITEM;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.SCREENSHOT_IMG;\n img.src = dataUrl;\n img.alt = this.strings.attachedScreenshot;\n img.onclick = () => this.showLightbox(dataUrl);\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.className = CLASSES.SCREENSHOT_REMOVE;\n removeBtn.innerHTML = \"×\";\n removeBtn.onclick = (e) => {\n e.stopPropagation();\n pendingReplyScreenshots.splice(i, 1);\n updateReplyScreenshotsPreview();\n };\n\n item.appendChild(img);\n item.appendChild(removeBtn);\n threadScreenshotsContainer.appendChild(item);\n });\n };\n\n threadAttachBtn.addEventListener(\"click\", () => {\n threadFileInput.click();\n });\n\n threadFileInput.addEventListener(\"change\", (e) => {\n const file = /** @type {HTMLInputElement} */ (e.target).files[0];\n if (!file) return;\n if (pendingReplyScreenshots.length >= 5) return;\n\n const reader = new FileReader();\n reader.onload = (ev) => {\n pendingReplyScreenshots.push(ev.target.result);\n updateReplyScreenshotsPreview();\n };\n reader.readAsDataURL(file);\n threadFileInput.value = \"\";\n });\n\n const submitReply = () => {\n const text = input.value.trim();\n if (!text && pendingReplyScreenshots.length === 0) return;\n\n const reply = this.addReply(\n comment,\n text,\n pendingReplyScreenshots.length > 0 ? [...pendingReplyScreenshots] : []\n );\n\n const repliesContainer = popover.querySelector(\n `.${CLASSES.THREAD_REPLIES}`\n );\n const replyEl = createReplyElement(reply, this.strings, this.locale, {\n onDelete: onDeleteReply,\n });\n repliesContainer.appendChild(replyEl);\n\n replyEl\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.showLightbox(img.src);\n });\n });\n\n // Once the thread is taller than the popover the new reply lands below\n // the fold, so sending would look like nothing happened.\n const scrollEl = popover.querySelector(`.${CLASSES.THREAD_SCROLL}`);\n if (scrollEl) scrollEl.scrollTop = scrollEl.scrollHeight;\n\n input.value = \"\";\n pendingReplyScreenshots = [];\n updateReplyScreenshotsPreview();\n input.focus();\n };\n\n submitBtn.addEventListener(\"click\", submitReply);\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submitReply();\n }\n });\n\n this.activeThreadPopover = popover;\n\n // Sending a reply, expanding the context block or a screenshot finishing\n // its decode all change the popover's height after it was placed. Without\n // re-running the anchor decision the box keeps the top it was given and\n // grows straight off the bottom of the viewport. Watching the element is\n // the one hook that covers every cause; guarded because jsdom has no\n // ResizeObserver.\n if (typeof ResizeObserver !== \"undefined\") {\n this._popoverResizeObserver = new ResizeObserver(() =>\n this._repositionThreadPopover()\n );\n this._popoverResizeObserver.observe(popover);\n }\n\n setTimeout(() => input.focus(), 50);\n\n setTimeout(() => {\n this._threadClickHandler = (e) => {\n const target = e.composedPath()[0] || e.target;\n if (\n !popover.contains(target) &&\n !circle?.contains(target) &&\n !this._isInsideLightbox(target)\n ) {\n // A click on the page while text is unsaved is an ambiguous\n // gesture \u2014 maybe the user went to look at the thing they are\n // describing. Answering it with a modal would interrupt them, and\n // interrupting often teaches people to dismiss without reading. So\n // the panel simply stays put; the textarea still on screen says\n // everything the dialog would have.\n if (this._popoverEditorDirty()) return;\n this.closeThreadPopover();\n }\n };\n document.addEventListener(\"mousedown\", this._threadClickHandler);\n }, 0);\n }\n\n closeThreadPopover() {\n // Queried rather than remembered: the marker can be re-rendered while\n // its popover is open, and the stale reference would keep the class.\n // cleanup() reaches here before initOverlay() has run when the document\n // was still loading, so there may be no shadow root yet.\n this.shadowRoot\n ?.querySelectorAll(`.${CLASSES.CIRCLE_ACTIVE}`)\n .forEach((/** @type {HTMLElement} */ el) =>\n el.classList.remove(CLASSES.CIRCLE_ACTIVE)\n );\n this._popoverResizeObserver?.disconnect();\n this._popoverResizeObserver = null;\n // Every route here has already answered for the draft, and the DOM it\n // lived in is about to go.\n this._popoverEditing = null;\n if (this.activeThreadPopover) {\n this.activeThreadPopover.remove();\n this.activeThreadPopover = null;\n }\n this._activePopoverCircle = null;\n if (this._threadClickHandler) {\n document.removeEventListener(\"mousedown\", this._threadClickHandler);\n this._threadClickHandler = null;\n }\n }\n\n showLightbox(imageSrc) {\n this.closeLightbox();\n\n const lightbox = document.createElement(\"div\");\n lightbox.className = CLASSES.LIGHTBOX;\n\n const img = document.createElement(\"img\");\n img.className = CLASSES.LIGHTBOX_IMG;\n img.src = imageSrc;\n img.alt = this.strings.screenshotPreview;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = CLASSES.LIGHTBOX_CLOSE;\n closeBtn.setAttribute(\"aria-label\", this.strings.close);\n closeBtn.innerHTML = \"×\";\n closeBtn.addEventListener(\"click\", () => this.closeLightbox());\n\n lightbox.appendChild(img);\n lightbox.appendChild(closeBtn);\n\n lightbox.addEventListener(\"click\", (e) => {\n if (e.target === lightbox) this.closeLightbox();\n });\n\n this.shadowRoot.appendChild(lightbox);\n this._activeLightbox = lightbox;\n }\n\n closeLightbox() {\n this._activeLightbox?.remove();\n this._activeLightbox = null;\n }\n\n // The lightbox is opened *from* the inbox and the thread popover but lives\n // as their sibling in the shadow root, so a naive \"is this click outside my\n // element?\" test reads every click on it \u2014 including its own close button \u2014\n // as a click away from the panel, and tears the panel down behind it.\n _isInsideLightbox(target) {\n return Boolean(target?.closest?.(`.${CLASSES.LIGHTBOX}`));\n }\n\n addReply(comment, text, screenshots = []) {\n if (!comment.replies) comment.replies = [];\n const reply = {\n id: createId(),\n editedAt: null,\n text,\n author: this.options.user?.name || this.strings.anonymous,\n timestamp: new Date().toISOString(),\n screenshots,\n };\n comment.replies.push(reply);\n this._syncStorage();\n this.options.onReplyAdded?.(\n this._serializeComment(comment),\n this._serializeReply(reply)\n );\n return reply;\n }\n\n /**\n * Removes one reply from a thread. The root comment is untouched \u2014 deleting\n * the last reply leaves the comment itself standing, which is why this is\n * separate from deleteComment rather than a special case of it.\n *\n * @param {number} commentId\n * @param {number} replyId\n * @returns {boolean} false when either id does not resolve\n */\n deleteReply(commentId, replyId) {\n const comment = this.comments.find((c) => c.id === commentId);\n const index = comment?.replies?.findIndex((r) => r.id === replyId) ?? -1;\n if (index < 0) return false;\n\n const [reply] = comment.replies.splice(index, 1);\n this._syncStorage();\n this.options.onReplyDeleted?.(\n this._serializeComment(comment),\n this._serializeReply(reply)\n );\n return true;\n }\n\n /**\n * Rewrites a comment's text and stamps `editedAt`.\n *\n * Refuses an empty body: a comment with no text keeps its marker, its\n * replies and its inbox row while saying nothing, so blanking is not a\n * back door to deletion \u2014 deleting is its own action and it asks first.\n * Refuses a no-op too, so opening the editor and saving without typing\n * does not brand the comment as edited.\n *\n * @param {import('./index.d.ts').CommentId} id\n * @param {string} text\n * @returns {boolean} false when the id does not resolve, or nothing changed\n */\n editComment(id, text) {\n const comment = this.comments.find((c) => String(c.id) === String(id));\n const next = String(text ?? \"\").trim();\n if (!comment || !next || next === comment.text) return false;\n\n comment.text = next;\n comment.editedAt = new Date().toISOString();\n this._syncStorage();\n this.options.onCommentEdited?.(this._serializeComment(comment));\n return true;\n }\n\n /**\n * Same contract as editComment, one level down.\n *\n * @param {import('./index.d.ts').CommentId} commentId\n * @param {import('./index.d.ts').CommentId} replyId\n * @param {string} text\n * @returns {boolean} false when either id does not resolve, or nothing changed\n */\n editReply(commentId, replyId, text) {\n const comment = this.comments.find(\n (c) => String(c.id) === String(commentId)\n );\n const reply = comment?.replies?.find(\n (r) => String(r.id) === String(replyId)\n );\n const next = String(text ?? \"\").trim();\n if (!reply || !next || next === reply.text) return false;\n\n reply.text = next;\n reply.editedAt = new Date().toISOString();\n this._syncStorage();\n this.options.onReplyEdited?.(\n this._serializeComment(comment),\n this._serializeReply(reply)\n );\n return true;\n }\n\n _serializeReply({ id, text, author, timestamp, screenshots, editedAt }) {\n return {\n id,\n text,\n author,\n timestamp,\n screenshots: screenshots || [],\n editedAt: editedAt || null,\n };\n }\n\n /**\n * Serializable snapshot of one comment: the live `container` element is\n * replaced by its `anchor`. Screenshots (data-URLs) are included \u2014 the\n * localStorage mode and the inbox cards need them.\n */\n _serializeComment(comment) {\n return {\n id: comment.id,\n text: comment.text,\n editedAt: comment.editedAt || null,\n anchor: comment.anchor || null,\n page: comment.page || location.pathname,\n replies: (comment.replies || []).map((reply) =>\n this._serializeReply(reply)\n ),\n author: comment.author,\n createdAt: comment.createdAt,\n screenshots: comment.screenshots || [],\n status: comment.status || \"open\",\n type: comment.type || null,\n priority: comment.priority || null,\n // Copied rather than referenced: a host mutating serializeComments()\n // output must not be able to reach back into overlay internals.\n tags: comment.tags ? [...comment.tags] : [],\n resolvedAt: comment.resolvedAt || null,\n context: comment.context ? { ...comment.context } : null,\n contextScreenshot: comment.contextScreenshot || null,\n };\n }\n\n /**\n * RF09 \u2014 moves a comment through its lifecycle\n * (open \u2192 in_progress \u2192 resolved \u2192 closed, in any order).\n * @param {number} id\n * @param {import('./index.d.ts').CommentStatus} status\n * @returns {boolean} false when the id or status is unknown\n */\n setCommentStatus(id, status) {\n if (!STATUSES.includes(status)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n // No-op: picking the status the comment is already in must not re-stamp\n // resolvedAt (that would reset RF5's elapsed time to \"<1m\") or trigger a\n // storage write / inbox refresh / callback for nothing having changed.\n if (comment.status === status) return true;\n comment.status = status;\n // RF5 \u2014 the timestamp always describes the CURRENT resolution: a\n // reopened comment loses it, and resolving again re-stamps it.\n comment.resolvedAt =\n status === \"resolved\" ? new Date().toISOString() : null;\n // Resolving removes the on-page marker; reopening restores it.\n const circle = /** @type {HTMLElement} */ (\n this.shadowRoot?.querySelector(`[data-comment-id=\"${id}\"]`)\n );\n if (circle) this.updateCommentPosition(comment, circle);\n this._syncStorage();\n // Re-render the inbox so the card picks up the new status right away\n // (resolved styling + sink-to-bottom sorting) no matter where the\n // change came from \u2014 card, detail or thread popover.\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n this.options.onCommentStatusChanged?.(this._serializeComment(comment));\n return true;\n }\n\n /**\n * Shared tail of the classification setters: persist, re-render the\n * inbox if it's showing, and notify the host app.\n * @param {any} comment\n * @returns {true}\n */\n _commitUpdate(comment) {\n this._syncStorage();\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n this.options.onCommentUpdated?.(this._serializeComment(comment));\n return true;\n }\n\n /**\n * RF3 \u2014 categorises a comment. `null` returns it to the neutral state.\n * @param {number} id\n * @param {import('./index.d.ts').CommentType | null} type\n * @returns {boolean} false when the id or type is unknown\n */\n setCommentType(id, type) {\n if (type !== null && !COMMENT_TYPES.includes(type)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n comment.type = type;\n return this._commitUpdate(comment);\n }\n\n /**\n * RF4 \u2014 prioritises a comment. `null` returns it to the neutral state.\n * @param {number} id\n * @param {import('./index.d.ts').CommentPriority | null} priority\n * @returns {boolean} false when the id or priority is unknown\n */\n setCommentPriority(id, priority) {\n if (priority !== null && !PRIORITIES.includes(priority)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n comment.priority = priority;\n return this._commitUpdate(comment);\n }\n\n /**\n * RF3 \u2014 replaces a comment's free-form labels. Values are trimmed,\n * lowercased and de-duplicated.\n * @param {number} id\n * @param {string[]} tags\n * @returns {boolean} false when the id is unknown or tags isn't an array\n */\n setCommentTags(id, tags) {\n if (!Array.isArray(tags)) return false;\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return false;\n comment.tags = normalizeTags(tags);\n return this._commitUpdate(comment);\n }\n\n /**\n * @returns {import('./index.d.ts').SerializedComment[]}\n */\n serializeComments() {\n return this.comments.map((comment) => this._serializeComment(comment));\n }\n\n /**\n * Removes a comment everywhere: page marker, memory and (when the\n * localStorage mode is on) persisted storage.\n * @param {number} id\n * @returns {boolean} false when the id is unknown\n */\n deleteComment(id) {\n if (!this.comments.some((comment) => comment.id === id)) return false;\n this._removeComment(id);\n if (this.options.persistence === \"localStorage\") {\n // The merge preserves other-page entries missing from memory, which\n // would resurrect a deleted inactive comment \u2014 drop the id explicitly.\n writeStoredComments(\n mergeForStorage(\n readStoredComments().filter((comment) => comment.id !== id),\n this.serializeComments(),\n location.pathname\n )\n );\n }\n this.options.onCommentDeleted?.(id);\n return true;\n }\n\n _removeComment(id) {\n this.cleanupResizeObserver(id);\n if (this.mutationObservers.has(id)) {\n try {\n this.mutationObservers.get(id).disconnect();\n } catch {}\n this.mutationObservers.delete(id);\n }\n this.shadowRoot.querySelector(`[data-comment-id=\"${id}\"]`)?.remove();\n this.comments = this.comments.filter((comment) => comment.id !== id);\n }\n\n /**\n * Restores serialized comments: each anchor is resolved back to a live\n * element (circle re-rendered) or the comment is kept as an orphan \u2014\n * present in the list/inbox but never positioned over the wrong element.\n * Loading the same id again replaces the previous copy (idempotent).\n * @param {import('./index.d.ts').SerializedComment[]} data\n * @returns {{ anchored: number, orphaned: number, inactive: number }}\n */\n loadComments(data) {\n let anchored = 0;\n let orphaned = 0;\n let inactive = 0;\n if (!Array.isArray(data)) return { anchored, orphaned, inactive };\n\n for (const item of data) {\n if (!item || item.id == null || typeof item.text !== \"string\") {\n console.warn(\"HellDots: skipping malformed serialized comment\", item);\n continue;\n }\n this._removeComment(item.id);\n\n const comment = {\n id: item.id,\n text: item.text,\n editedAt: item.editedAt || null,\n anchor: item.anchor || null,\n anchorState: \"orphaned\",\n target: null,\n hidden: false,\n page: item.page || location.pathname,\n container: null,\n relativeX: 0,\n relativeY: 0,\n replies: Array.isArray(item.replies) ? [...item.replies] : [],\n author: item.author || this.strings.anonymous,\n createdAt: item.createdAt || new Date().toISOString(),\n screenshots: Array.isArray(item.screenshots)\n ? [...item.screenshots]\n : [],\n // \"closed\" existed briefly and was folded into \"resolved\".\n status:\n /** @type {string} */ (item.status) === \"closed\"\n ? \"resolved\"\n : STATUSES.includes(item.status)\n ? item.status\n : \"open\",\n // Records persisted before RF1-RF5 have none of these \u2014 every\n // reader downstream may assume they exist after this point.\n type: COMMENT_TYPES.includes(item.type) ? item.type : null,\n priority: PRIORITIES.includes(item.priority) ? item.priority : null,\n tags: Array.isArray(item.tags) ? [...item.tags] : [],\n resolvedAt: item.resolvedAt || null,\n context: item.context || null,\n contextScreenshot: item.contextScreenshot || null,\n };\n\n // Comments from other pages aren't broken \u2014 their elements just\n // don't exist here. They stay listed (inbox \"all\" filter) without a\n // marker and without an onAnchorLost false alarm.\n if (item.page && item.page !== location.pathname) {\n comment.anchorState = \"inactive\";\n this.comments.push(comment);\n inactive++;\n continue;\n }\n\n const resolved = item.anchor ? resolveAnchor(item.anchor) : null;\n if (resolved) {\n comment.container = resolved.element;\n comment.relativeX = item.anchor.relativeX;\n comment.relativeY = item.anchor.relativeY;\n comment.anchorState = \"anchored\";\n this.comments.push(comment);\n this.renderCommentCircle(comment);\n anchored++;\n } else {\n this.comments.push(comment);\n orphaned++;\n this.options.onAnchorLost?.(this._serializeComment(comment));\n }\n }\n\n // A link the host's data had not arrived for yet may be waiting on\n // exactly the comments that just landed.\n this._openPendingDetail();\n\n return { anchored, orphaned, inactive };\n }\n\n centerPopover(el) {\n const elRect = el.getBoundingClientRect();\n const x = Math.max(10, (window.innerWidth - (elRect.width || 400)) / 2);\n const y = Math.max(10, (window.innerHeight - elRect.height) / 2);\n el.style.left = `${x}px`;\n // Explicitly cleared: this element may have been bottom-anchored by\n // positionPopoverAtCircle, and `top` alone would not win over it.\n el.style.bottom = \"auto\";\n el.style.top = `${y}px`;\n }\n\n /**\n * Brings a comment's marker into view, centred vertically.\n *\n * Deliberately not `comment.container.scrollIntoView()`: the container is\n * the coarse anchor box (`section, div[class*=container|content]`), which\n * falls back to `<body>` whenever the commented element has no such\n * ancestor. Centring `<body>` lands halfway down the document \u2014 nowhere\n * near the marker, which is what made opening a comment from the inbox\n * jump to an unrelated section.\n *\n * @param {any} comment\n */\n scrollMarkerIntoView(comment) {\n const y = this._markerViewportY(comment);\n if (y == null) return;\n window.scrollTo({\n top: Math.max(0, window.scrollY + y - window.innerHeight / 2),\n });\n }\n\n /**\n * The marker's centre in viewport coordinates, derived from the anchor the\n * same way `updateCommentPosition` derives it \u2014 including the clamp that\n * keeps the circle inside its container.\n *\n * Deliberately not read off the rendered circle: the circle's coordinates\n * are only refreshed inside a rAF on scroll, so they are stale for any\n * caller that runs in the same tick as a scroll. The container's rect is\n * live, which makes this correct whenever it is asked.\n *\n * @param {any} comment\n * @returns {number | null} null when there is no anchor to resolve\n */\n _markerViewportY(comment) {\n const container = comment.container;\n if (!container?.isConnected) return null;\n const rect = container.getBoundingClientRect();\n if (rect.height <= 0) return null;\n\n const circleSize = 28;\n const offsetY = Math.max(\n 0,\n Math.min(comment.relativeY * rect.height, rect.height - circleSize)\n );\n return rect.top + offsetY + circleSize / 2;\n }\n\n /**\n * Keeps the open thread popover pinned beside its marker while the page\n * scrolls, and hides it while the marker is off-screen.\n *\n * Hidden, not closed: a half-typed reply must survive scrolling the marker\n * out of view and back. Closing here would also fight the outside-click\n * handler, which is the thing that legitimately dismisses the popover.\n */\n /**\n * Re-runs the open popover's placement against its current size. Split from\n * syncThreadPopoverToMarker because that one also decides visibility from\n * the marker, which is wrong here: a popover resizing while its marker is\n * off-screen must stay hidden, not reappear.\n */\n _repositionThreadPopover() {\n const popover = this.activeThreadPopover;\n if (!popover || popover.style.display === \"none\") return;\n\n if (this._activePopoverCircle) {\n this.positionPopoverAtCircle(popover, this._activePopoverCircle);\n } else {\n this.centerPopover(popover);\n }\n }\n\n syncThreadPopoverToMarker() {\n const popover = this.activeThreadPopover;\n const circle = this._activePopoverCircle;\n // A popover with no marker is the centered variant (orphaned comment\n // opened from the inbox); it has nothing to track.\n if (!popover || !circle) return;\n\n const rect = circle.getBoundingClientRect();\n const onScreen =\n circle.isConnected &&\n circle.style.display !== \"none\" &&\n rect.bottom > 0 &&\n rect.right > 0 &&\n rect.top < window.innerHeight &&\n rect.left < window.innerWidth;\n\n if (!onScreen) {\n popover.style.display = \"none\";\n return;\n }\n\n // Un-hide before measuring: a `display: none` element reports a zero\n // rect, and positionPopoverAtCircle sizes itself from that measurement.\n popover.style.display = \"\";\n this.positionPopoverAtCircle(popover, circle);\n }\n\n positionPopoverAtCircle(el, circle) {\n const circleRect = circle.getBoundingClientRect();\n const centerX = circleRect.left + circleRect.width / 2;\n const centerY = circleRect.top + circleRect.height / 2;\n const circleBaseSize = 28;\n const offset = circleBaseSize / 2 + 10;\n\n // Same reasoning as showCommentBox(): the tooltip and popover are\n // `min(400px, 100vw - 24px)` wide, so their real width has to be read.\n const elRect = el.getBoundingClientRect();\n const elWidth = elRect.width || 400;\n\n let x = centerX + offset;\n\n if (x + elWidth > window.innerWidth) {\n x = centerX - offset - elWidth;\n }\n x = Math.min(x, window.innerWidth - elWidth - 10);\n x = Math.max(10, x);\n el.style.left = `${x}px`;\n\n // Vertically the popover is anchored by whichever edge keeps it on\n // screen, and that choice is what makes it grow in the right direction.\n //\n // Pinning `top` alone was not enough: `max-height` caps the height but\n // says nothing about where the box starts, so a marker low on the page\n // put the top at, say, 600px and the popover simply ran off the bottom \u2014\n // taking the reply input with it, so you could not see what you were\n // typing. Re-clamping `top` on every growth would fight the user, since\n // each new reply would shift the whole thread upward under the cursor.\n //\n // Anchoring `bottom` instead makes the browser do it: the reply box stays\n // put and the thread extends upward until `max-height` takes over and\n // `.thread-scroll` starts scrolling.\n const margin = 10;\n const preferredTop = centerY - circleBaseSize / 2;\n const spaceBelow = window.innerHeight - margin - preferredTop;\n\n if (elRect.height > spaceBelow) {\n el.style.top = \"auto\";\n el.style.bottom = `${margin}px`;\n } else {\n el.style.bottom = \"auto\";\n el.style.top = `${Math.max(margin, preferredTop)}px`;\n }\n }\n\n createPreviewCircle(x, y) {\n this.removePreviewCircle();\n\n const circle = document.createElement(\"div\");\n circle.className = `${CLASSES.CIRCLE} ${CLASSES.PREVIEW_CIRCLE}`;\n circle.style.position = \"absolute\";\n const circleRadius = 14;\n circle.style.left = `${x + circleRadius}px`;\n circle.style.top = `${y + circleRadius}px`;\n circle.style.transform = \"translate(-50%, -50%)\";\n circle.style.pointerEvents = \"none\";\n\n this.overlay.appendChild(circle);\n this.previewCircle = circle;\n }\n\n removePreviewCircle() {\n this.previewCircle?.remove();\n this.previewCircle = null;\n }\n\n cleanupResizeObserver(commentId) {\n if (this.resizeObservers && this.resizeObservers.has(commentId)) {\n const { circle, observer } = this.resizeObservers.get(commentId);\n if (observer) {\n observer.disconnect();\n }\n if (circle && circle.parentNode) {\n circle.parentNode.removeChild(circle);\n }\n this.resizeObservers.delete(commentId);\n }\n }\n\n /**\n * Validates and recalculates comment position based on container dimensions\n * @param {Object} comment - The comment object with position data\n * @param {HTMLElement} circle - The comment circle element\n * @returns {Object} - Validated position data\n */\n validateAndCalculatePosition(comment, circle) {\n if (!comment.container || !circle) return null;\n\n const containerRect = comment.container.getBoundingClientRect();\n const containerWidth = containerRect.width;\n const containerHeight = containerRect.height;\n\n // Zero size isn't an anomaly: it's what display:none (e.g. responsive\n // media queries) looks like. The caller hides the marker until the\n // element gets its size back.\n if (containerWidth <= 0 || containerHeight <= 0) {\n return null;\n }\n\n // Use simple relative positioning for consistent results\n const absoluteX = comment.relativeX * containerWidth;\n const absoluteY = comment.relativeY * containerHeight;\n\n const circleSize = 28;\n const validatedX = Math.max(\n 0,\n Math.min(absoluteX, containerWidth - circleSize)\n );\n const validatedY = Math.max(\n 0,\n Math.min(absoluteY, containerHeight - circleSize)\n );\n\n // Recalculate relative position for future calculations\n const validatedRelativeX = validatedX / containerWidth;\n const validatedRelativeY = validatedY / containerHeight;\n\n return {\n absoluteX: validatedX,\n absoluteY: validatedY,\n relativeX: validatedRelativeX,\n relativeY: validatedRelativeY,\n containerWidth,\n containerHeight,\n containerLeft: containerRect.left,\n containerTop: containerRect.top,\n };\n }\n\n /**\n * Updates comment circle position based on validated calculations\n * @param {Object} comment - The comment object\n * @param {HTMLElement} circle - The comment circle element\n */\n /**\n * The exact element the user clicked on can vanish (responsive\n * display:none) while its coarse anchor container stays visible. When we\n * have a live target \u2014 or can re-derive one from the serialized\n * targetSelector \u2014 the marker follows ITS visibility too.\n */\n _isAnchorTargetVisible(comment) {\n let target = comment.target;\n if ((!target || !target.isConnected) && comment.anchor?.targetSelector) {\n try {\n target = document.querySelector(comment.anchor.targetSelector);\n } catch {\n target = null;\n }\n comment.target = target || null;\n }\n if (!target || !target.isConnected) return true; // no signal \u2014 assume visible\n const rect = target.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n }\n\n /**\n * A marker floats above the whole page (own shadow host, high z-index),\n * so a host-page modal overlay can never cover it with CSS alone. Hit-test\n * the marker's point instead: if the topmost page element there is\n * unrelated to the comment's anchor (neither ancestor nor descendant),\n * something like a modal backdrop is covering it and the marker should\n * hide with it.\n */\n _isMarkerOccluded(comment, x, y) {\n if (typeof document.elementsFromPoint !== \"function\") return false;\n // Off-viewport points can't be hit-tested; the marker isn't visible\n // there anyway, so keep the current (visible) state.\n if (x < 0 || y < 0 || x >= window.innerWidth || y >= window.innerHeight) {\n return false;\n }\n const stack = document.elementsFromPoint(x, y);\n // Our own shadow host shows up first (the marker itself, toolbar, \u2026).\n const top = stack.find(\n (el) => el.tagName.toLowerCase() !== TAG_NAME.toLowerCase()\n );\n if (!top) return false;\n\n const target = comment.target?.isConnected ? comment.target : null;\n // The precise element the comment was left on (or its subtree /\n // ancestors) is what should be under the marker \u2014 never an occluder.\n if (target && (target.contains(top) || top.contains(target))) {\n return false;\n }\n\n const container = comment.container;\n if (!container?.isConnected) return false;\n // Something entirely unrelated to the anchor sits on top of it.\n if (!container.contains(top) && !top.contains(container)) return true;\n\n // `top` lives inside the anchor container \u2014 usually normal content of\n // the anchored subtree. But broad containers (body, page wrappers) also\n // contain the page's modals, so walk the chain up to the container: if\n // it crosses a modal-looking layer the anchor doesn't belong to, the\n // marker is covered after all.\n if (container.contains(top) && top !== container) {\n for (let el = top; el && el !== container; el = el.parentElement) {\n if (this._looksLikeModalLayer(el, target)) return true;\n }\n }\n return false;\n }\n\n /**\n * Heuristic for \"this element is a modal/backdrop layer\": explicit dialog\n * semantics, or a hit-testable fixed element covering most of the\n * viewport. Elements that contain the comment's own target are the layer\n * the comment lives in, never an occluder.\n */\n _looksLikeModalLayer(el, target) {\n if (target && el.contains(target)) return false;\n if (el.matches?.('dialog, [aria-modal=\"true\"], [role=\"dialog\"]')) {\n return true;\n }\n if (getComputedStyle(el).position !== \"fixed\") return false;\n const rect = el.getBoundingClientRect();\n return (\n rect.width >= window.innerWidth * 0.5 &&\n rect.height >= window.innerHeight * 0.5\n );\n }\n\n // A marker that just went away must not leave its hover tooltip or its\n // open thread popover floating on the page (e.g. above the modal that\n // now covers the marker).\n _dismissMarkerUi(comment) {\n this.shadowRoot\n .querySelector(`.${CLASSES.TOOLTIP}[data-for=\"${comment.id}\"]`)\n ?.remove();\n if (this.activeThreadPopover?.dataset.for === String(comment.id)) {\n this.closeThreadPopover();\n }\n }\n\n updateCommentPosition(comment, circle) {\n // Resolved comments have no on-page marker (RF09). This is not the\n // \"hidden\" state \u2014 the anchor is fine, the issue is just done.\n if (comment.status === \"resolved\") {\n if (circle) circle.style.display = \"none\";\n return;\n }\n\n let positionData = this.validateAndCalculatePosition(comment, circle);\n if (positionData && !this._isAnchorTargetVisible(comment)) {\n positionData = null;\n }\n const wasHidden = comment.hidden === true;\n\n if (!positionData) {\n // Anchor element currently invisible (zero-size container): hide the\n // marker; it comes back automatically when the observers fire again.\n if (circle && comment.container) {\n comment.hidden = true;\n circle.style.display = \"none\";\n this._dismissMarkerUi(comment);\n if (!wasHidden && this.inboxView?.isOpen()) this.inboxView.refresh();\n }\n return;\n }\n\n // Offset so the circle's top-left tip (sharp corner) aligns with the stored position\n const circleRadius = 14;\n const viewportX =\n positionData.containerLeft + positionData.absoluteX + circleRadius;\n const viewportY =\n positionData.containerTop + positionData.absoluteY + circleRadius;\n\n // A host-page modal (or any unrelated overlay) covering the anchor also\n // hides the marker \u2014 it must not float above the modal's backdrop.\n if (this._isMarkerOccluded(comment, viewportX, viewportY)) {\n comment.hidden = true;\n circle.style.display = \"none\";\n this._dismissMarkerUi(comment);\n if (!wasHidden && this.inboxView?.isOpen()) this.inboxView.refresh();\n return;\n }\n\n comment.hidden = false;\n circle.style.display = \"\";\n if (wasHidden && this.inboxView?.isOpen()) this.inboxView.refresh();\n\n circle.style.left = `${viewportX}px`;\n circle.style.top = `${viewportY}px`;\n circle.style.transform = \"translate(-50%, -50%)\";\n circle.style.position = \"absolute\";\n\n comment.relativeX = positionData.relativeX;\n comment.relativeY = positionData.relativeY;\n }\n\n /**\n * Sets up resize observers and window resize handlers\n */\n setupResizeHandlers() {\n // Throttled updater\n this.scheduleUpdatePositions = () => {\n if (this._pendingRaf) return;\n this._pendingRaf = requestAnimationFrame(() => {\n this._pendingRaf = null;\n if (this.positionValidationEnabled) {\n this.comments.forEach((comment) => {\n /** @type {HTMLElement} */\n const circle = /** @type {any} */ (\n this.shadowRoot.querySelector(`[data-comment-id=\"${comment.id}\"]`)\n );\n if (circle) this.updateCommentPosition(comment, circle);\n });\n }\n // Runs even with position validation off: the markers are placed in\n // viewport coordinates either way, so the popover has to follow.\n this.syncThreadPopoverToMarker();\n });\n };\n\n // Window resize handler for viewport changes\n this.windowResizeHandler = () => {\n this.scheduleUpdatePositions();\n };\n window.addEventListener(\"resize\", this.windowResizeHandler, {\n passive: true,\n });\n\n // Capture scroll on any scrolling ancestor\n this.scrollHandler = () => {\n this.scheduleUpdatePositions();\n };\n window.addEventListener(\"scroll\", this.scrollHandler, {\n capture: true,\n passive: true,\n });\n\n // Update after resources load (images, fonts)\n this.loadHandler = () => {\n this.scheduleUpdatePositions();\n };\n window.addEventListener(\"load\", this.loadHandler);\n\n // Modals open/close outside any comment's container (backdrops are\n // usually appended to <body> or toggled via style/class), so the\n // per-comment observers never see them. One page-wide observer keeps\n // the occlusion check honest; shadow-root internals don't bubble into\n // it, so our own marker updates can't retrigger it.\n if (window.MutationObserver) {\n this._globalMutationObserver = new MutationObserver(() => {\n this.scheduleUpdatePositions();\n });\n this._globalMutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"style\", \"class\", \"hidden\", \"open\"],\n });\n }\n }\n\n /**\n * Debug function to log position information\n * @param {Object} comment - The comment object\n * @param {HTMLElement} circle - The comment circle element\n */\n debugPosition(comment, circle) {\n if (!comment || !circle) return;\n\n const containerRect = comment.container.getBoundingClientRect();\n const circleRect = circle.getBoundingClientRect();\n\n // Calculate expected position from relative coordinates\n const expectedX = comment.relativeX * containerRect.width;\n const expectedY = comment.relativeY * containerRect.height;\n\n console.log(\"Position Debug:\", {\n commentId: comment.id,\n relativePosition: { x: comment.relativeX, y: comment.relativeY },\n containerRect: {\n left: containerRect.left,\n top: containerRect.top,\n width: containerRect.width,\n height: containerRect.height,\n },\n circlePosition: {\n left: circleRect.left,\n top: circleRect.top,\n centerX: circleRect.left + circleRect.width / 2,\n centerY: circleRect.top + circleRect.height / 2,\n },\n expectedPosition: {\n x: expectedX,\n y: expectedY,\n },\n offset: {\n x:\n circleRect.left +\n circleRect.width / 2 -\n (containerRect.left + expectedX),\n y:\n circleRect.top +\n circleRect.height / 2 -\n (containerRect.top + expectedY),\n },\n });\n }\n\n /**\n * Creates a ResizeObserver for a specific comment container\n * @param {Object} comment - The comment object\n * @param {HTMLElement} circle - The comment circle element\n */\n createResizeObserver(comment, circle) {\n if (!window.ResizeObserver) {\n console.warn(\n \"ResizeObserver not supported, position validation will be limited\"\n );\n return;\n }\n\n const observer = new ResizeObserver((entries) => {\n if (!this.positionValidationEnabled) return;\n\n for (const entry of entries) {\n // Only update if the container size actually changed\n if (entry.target === comment.container) {\n this.updateCommentPosition(comment, circle);\n }\n }\n });\n\n // Start observing the container\n observer.observe(comment.container);\n\n // Store the observer for cleanup\n this.resizeObservers.set(comment.id, {\n circle,\n observer,\n container: comment.container,\n });\n }\n\n /**\n * Creates a MutationObserver to react to layout-affecting DOM changes\n * @param {Object} comment\n */\n createMutationObserver(comment) {\n if (!window.MutationObserver) return;\n\n // Disconnect existing for this comment if any\n if (this.mutationObservers.has(comment.id)) {\n try {\n this.mutationObservers.get(comment.id).disconnect();\n } catch {}\n this.mutationObservers.delete(comment.id);\n }\n\n const observer = new MutationObserver(() => {\n this.scheduleUpdatePositions();\n });\n\n observer.observe(comment.container, {\n attributes: true,\n attributeFilter: undefined,\n childList: true,\n subtree: true,\n });\n\n this.mutationObservers.set(comment.id, observer);\n }\n\n /**\n * Cleanup method to remove all event listeners and observers\n */\n cleanup() {\n // Dropping the panels leaves their dropdowns detached-but-open, which\n // would keep the menu registry's document listener alive until the next\n // stray mousedown.\n closeOpenMenus();\n // Same reasoning: an unanswered confirmation holds a capture-phase\n // keydown listener on document, which would go on eating Escape for the\n // whole page after the widget is gone.\n closeOpenConfirmDialogs();\n this.closeThreadPopover();\n this.closeInbox();\n this.closeLightbox();\n this.removePreviewCircle();\n this._selectionRect?.remove();\n this._pendingScreenshots = [];\n\n if (this._handleDocumentClickBound) {\n document.removeEventListener(\"mousedown\", this._handleDocumentClickBound);\n }\n\n if (this.windowResizeHandler) {\n window.removeEventListener(\"resize\", this.windowResizeHandler);\n }\n\n if (this.scrollHandler) {\n window.removeEventListener(\"scroll\", this.scrollHandler, {\n capture: true,\n });\n }\n\n if (this.loadHandler) {\n window.removeEventListener(\"load\", this.loadHandler);\n }\n\n if (this._globalMutationObserver) {\n this._globalMutationObserver.disconnect();\n this._globalMutationObserver = null;\n }\n\n // Cleanup all resize observers\n if (this.resizeObservers) {\n this.resizeObservers.forEach(({ observer }) => {\n if (observer) {\n observer.disconnect();\n }\n });\n this.resizeObservers.clear();\n }\n\n // Cleanup mutation observers\n if (this.mutationObservers) {\n this.mutationObservers.forEach((observer) => {\n try {\n observer.disconnect();\n } catch {}\n });\n this.mutationObservers.clear();\n }\n\n // Remove keyboard shortcut handler\n if (this.keydownHandler) {\n document.removeEventListener(\"keydown\", this.keydownHandler);\n }\n\n // Remove DOM elements\n if (this.toolbar && this.toolbar.parentNode) {\n this.toolbar.parentNode.removeChild(this.toolbar);\n }\n if (this.commentBox && this.commentBox.parentNode) {\n this.commentBox.parentNode.removeChild(this.commentBox);\n }\n if (this.overlay && this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n\n document.body.classList.remove(CLASSES.COMMENT_CURSOR);\n document.getElementById(IDS.GLOBAL_STYLES)?.remove();\n\n // Remove all comment circles\n this.comments.forEach((comment) => {\n const circle = this.shadowRoot.querySelector(\n `[data-comment-id=\"${comment.id}\"]`\n );\n if (circle && circle.parentNode) {\n circle.parentNode.removeChild(circle);\n }\n });\n }\n\n injectStyles() {\n const existingStyle = this.shadowRoot.getElementById(IDS.STYLES);\n if (existingStyle) {\n existingStyle.remove();\n }\n\n const style = document.createElement(\"style\");\n style.id = IDS.STYLES;\n style.textContent = getStyles();\n this.shadowRoot.appendChild(style);\n\n // A few rules (e.g. the comment-mode cursor on document.body) target\n // the host page itself, which a shadow root's stylesheet can't reach \u2014\n // those live in a separate <style> in document.head instead.\n const existingGlobalStyle = document.getElementById(IDS.GLOBAL_STYLES);\n if (existingGlobalStyle) {\n existingGlobalStyle.remove();\n }\n\n const globalStyle = document.createElement(\"style\");\n globalStyle.id = IDS.GLOBAL_STYLES;\n globalStyle.textContent = getGlobalStyles();\n document.head.appendChild(globalStyle);\n }\n}\n\nexport default CommentOverlay;\n", "import CommentOverlay from \"./overlay.js\";\n\n/**\n * Creates a CommentOverlay.\n *\n * Safe to call before the document is ready: `CommentOverlay`'s constructor\n * already defers its own DOM work to `DOMContentLoaded`, so callers always\n * get a real instance back and never have to branch on `readyState`. An\n * earlier version duplicated that same check here and, while the document\n * was loading, both registered a listener AND returned the uninvoked\n * initializer \u2014 so a caller who invoked it (reasonably, since the type said\n * it might be a function) ended up with two overlays, and the one the\n * listener built had no handle to call `cleanup()` on.\n *\n * @overload\n * @param {import('./index.d.ts').CommentOverlayOptions & { autoInit?: true }} [options]\n * @returns {CommentOverlay}\n */\n/**\n * @overload\n * @param {import('./index.d.ts').CommentOverlayOptions & { autoInit: false }} options\n * @returns {() => CommentOverlay}\n */\n/**\n * @param {import('./index.d.ts').CommentOverlayOptions} [options]\n * @returns {CommentOverlay | (() => CommentOverlay)}\n */\nexport function createCommentOverlay(options = {}) {\n const { autoInit = true, ...overlayOptions } = options;\n const initialize = () => new CommentOverlay(overlayOptions);\n return autoInit ? initialize() : initialize;\n}\n\n// Export the class for advanced usage\nexport { CommentOverlay };\n\n// Export a default instance creator for simple usage\nexport default createCommentOverlay;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["domToCanvas", "TAG_NAME", "ensureDefined", "getShadowRoot", "host", "AUTO_SCALE", "AUTO_QUALITY", "isUnpainted", "color", "effectiveBackgroundColor", "htmlBg", "bodyBg", "renderPage", "scale", "domToCanvas", "withHiddenOverlay", "fn", "host", "TAG_NAME", "previousDisplay", "cropRegion", "canvas", "left", "top", "width", "height", "out", "ctx", "cropViewport", "sourceScale", "outputScale", "quality", "BROWSERS", "OPERATING_SYSTEMS", "UNKNOWN", "isGreaseBrand", "brand", "matchFirst", "table", "ua", "name", "re", "match", "captureContext", "win", "nav", "uaData", "browser", "b", "os", "CLASSES", "HOST_PAGE_CLASSES", "IDS", "STATUSES", "STATUS_COLORS", "COMMENT_TYPES", "TYPE_COLORS", "PRIORITIES", "PRIORITY_COLORS", "SELECTORS", "Z_INDEX", "CURSOR_SVG", "CURSOR_HOTSPOT", "SCROLLBAR", "webkitScrollbar", "selectors", "selector", "getStyles", "IDS", "Z_INDEX", "CLASSES", "getGlobalStyles", "CURSOR_SVG", "CURSOR_HOTSPOT", "en_default", "es_default", "LOCALES", "en_default", "es_default", "DEFAULT_LOCALE", "detectLocale", "lang", "getStrings", "localeCode", "formatTemplate", "template", "n", "MINUTE_MS", "formatDuration", "ms", "strings", "totalMinutes", "totalHours", "minutes", "hours", "totalDays", "days", "TEXT_SNIPPET_MAX", "MAX_CLASS_PATH_DEPTH", "MAX_STRUCTURAL_DEPTH", "SELECTOR_THRESHOLD", "RESCUE_THRESHOLD", "GENERATED_CLASS_PREFIX_RE", "FRAMEWORK_DATA_ATTR_RE", "STABLE_ATTR_NAMES", "SELECTOR_ATTR_NAMES", "escapeCss", "value", "escapeAttrValue", "isUnique", "selector", "doc", "normalizeText", "text", "isStableClass", "cls", "HOST_PAGE_CLASSES", "part", "stableClassesOf", "element", "stableAttributes", "attrs", "name", "siblingPosition", "parent", "sameTag", "child", "idSelector", "attributeSelector", "tag", "classPathSelector", "segments", "current", "depth", "classes", "structuralSelector", "rooted", "index", "generateSelector", "generateElementSelector", "createAnchor", "relativeX", "relativeY", "count", "textSimilarity", "a", "b", "tokensA", "tokensB", "common", "token", "attributeSimilarity", "names", "matched", "positionSimilarity", "fingerprint", "delta", "span", "scoreElement", "hasText", "hasAttrs", "textWeight", "attrWeight", "posWeight", "bestMatch", "candidates", "best", "confidence", "resolveAnchor", "anchor", "STORAGE_KEY", "PENDING_DETAIL_KEY", "readStoredComments", "raw", "parsed", "err", "tryWriteStoredComments", "comments", "writeStoredComments", "shedOrder", "comment", "index", "a", "b", "timeA", "timeB", "working", "shed", "mergeForStorage", "stored", "current", "currentPage", "currentIds", "c", "urlAlphabet", "nanoid", "size", "id", "bytes", "urlAlphabet", "createId", "nanoid", "DEFAULT_LINK_PARAM", "buildCommentLink", "comment", "param", "href", "current", "page", "url", "readCommentLinkParam", "openMenus", "outsideListener", "eventHits", "el", "startWatching", "e", "entry", "stopWatching", "closeOpenMenus", "attachMenuToggle", "button", "menu", "open", "isOpen", "wasOpen", "openDialogs", "closeOpenConfirmDialogs", "dismiss", "confirmDialog", "host", "title", "message", "confirmLabel", "cancelLabel", "resolve", "backdrop", "CLASSES", "Z_INDEX", "panel", "titleEl", "messageEl", "actions", "cancelBtn", "acceptBtn", "previouslyFocused", "settled", "settle", "result", "onKeydown", "e", "focusables", "active", "index", "pressedBackdrop", "COPY_ICON_SVG", "CHECK_ICON_SVG", "DOTS_ICON_SVG", "copyToClipboard", "text", "textarea", "statusLabelOf", "status", "strings", "typeLabelOf", "type", "priorityLabelOf", "priority", "createPicker", "action", "options", "value", "colorOf", "labelOf", "tooltipLabel", "onSelect", "showLabel", "wrapper", "btn", "CLASSES", "dot", "labelEl", "menu", "toggle", "attachMenuToggle", "current", "syncUi", "label", "item", "raw", "option", "itemDot", "e", "createMoreMenu", "tooltip", "items", "entry", "host", "confirmDialog", "createCommentActions", "comment", "onCopy", "onCopyLink", "onEdit", "onSetStatus", "onSetType", "onSetPriority", "onDelete", "actions", "copyBtn", "STATUSES", "STATUS_COLORS", "COMMENT_TYPES", "TYPE_COLORS", "PRIORITIES", "PRIORITY_COLORS", "createInlineEditor", "value", "strings", "onInput", "onSave", "onCancel", "wrapper", "CLASSES", "input", "actions", "cancel", "save", "syncSave", "e", "confirmDiscard", "host", "confirmDialog", "formatRelativeTime", "date", "strings", "diff", "minutes", "hours", "days", "formatTemplate", "formatFullDate", "locale", "createMetaElement", "author", "createdAt", "editedAt", "meta", "CLASSES", "authorEl", "timeEl", "createEditedMark", "editedEl", "getShortcutText", "options", "isMac", "modifierMap", "modifier", "key", "ATTACH_ICON_SVG", "SEND_ICON_SVG", "COMMENT_BUBBLE_SVG", "MENU_ICON_SVG", "createInputArea", "areaClassName", "inputTag", "inputClassName", "inputId", "inputPlaceholder", "submitBtnId", "fileInputId", "container", "inputEl", "screenshotsContainer", "actionsBar", "attachBtn", "fileInput", "submitBtn", "createActionWithTooltip", "btnClass", "btnSvg", "tooltipContent", "label", "wrapper", "tooltip", "el", "btn", "createToolbar", "en_default", "toolbar", "IDS", "actions", "commentLabel", "shortcutKey", "commentWrapper", "inboxLabel", "inboxWrapper", "createBadgeRow", "comment", "includeStatus", "includeClassification", "row", "addBadge", "text", "color", "badge", "status", "statusLabelOf", "STATUS_COLORS", "typeLabelOf", "TYPE_COLORS", "priorityLabelOf", "PRIORITY_COLORS", "tag", "elapsed", "formatDuration", "createClassifyRow", "type", "priority", "mount", "createPicker", "COMMENT_TYPES", "value", "PRIORITIES", "createCommentBox", "commentBox", "inputArea", "classify", "createCommentCircle", "circle", "createScreenshotsDisplay", "screenshots", "src", "item", "img", "createTooltip", "header", "closeButton", "body", "badges", "tooltipScreenshots", "replyCount", "replies", "createReplyElement", "reply", "onDelete", "onEdit", "editing", "replyEl", "items", "createMoreMenu", "createInlineEditor", "replyScreenshots", "createThreadPopover", "onDeleteReply", "onEditReply", "popover", "scroll", "popoverScreenshots", "openingTagOf", "element", "attrs", "name", "value", "openingTagFromFingerprint", "fingerprint", "domPathOf", "segments", "current", "tag", "id", "classes", "cls", "buildAgentContext", "comment", "viewportWidth", "viewportHeight", "strings", "anchor", "live", "state", "path", "capturedViewport", "reportedViewportWidth", "reportedViewportHeight", "lines", "context", "elapsed", "formatDuration", "replies", "reply", "CARET_ICON_SVG", "createContextBlock", "comment", "strings", "onShowLightbox", "collapsible", "context", "contextScreenshot", "block", "CLASSES", "body", "toggle", "e", "expanded", "title", "caption", "img", "addRow", "label", "value", "row", "key", "val", "size", "dimensions", "named", "entry", "CARET_ICON_SVG", "CHEVRON_LEFT_SVG", "ARROW_UP_SVG", "ARROW_DOWN_SVG", "InboxView", "shadowRoot", "strings", "locale", "currentPage", "getComments", "callbacks", "options", "CLASSES", "text", "comment", "c", "r", "host", "confirmDiscard", "commentId", "replyId", "handlers", "createInlineEditor", "circle", "comments", "detail", "id", "btn", "header", "list", "notice", "empty", "icon", "hasAnyComment", "title", "clear", "before", "after", "kbd", "getShortcutText", "action", "value", "parts", "statusLabelOf", "typeLabelOf", "priorityLabelOf", "dataAttr", "values", "labelOf", "selected", "toggles", "onSelect", "group", "heading", "chips", "checked", "chip", "e", "wrapper", "menu", "attachMenuToggle", "STATUSES", "COMMENT_TYPES", "PRIORITIES", "interactive", "card", "createMetaElement", "actionsRow", "shots", "createScreenshotsDisplay", "img", "badges", "createBadgeRow", "tag", "activate", "replyLink", "label", "createCommentActions", "copyToClipboard", "buildAgentContext", "buildCommentLink", "status", "type", "priority", "index", "backBtn", "nav", "navBtn", "svg", "targetIndex", "target", "context", "createContextBlock", "src", "replies", "reply", "editingThisReply", "replyEl", "createReplyElement", "el", "container", "inputEl", "screenshotsContainer", "attachBtn", "fileInput", "submitBtn", "createInputArea", "pendingScreenshots", "updatePreview", "dataUrl", "i", "item", "removeBtn", "file", "reader", "ev", "submit", "normalizeTags", "tags", "seen", "tag", "clean", "CommentOverlay", "options", "detectLocale", "getStrings", "getShadowRoot", "createToolbar", "createCommentBox", "CLASSES", "IDS", "readStoredComments", "url", "fromLink", "readCommentLinkParam", "fromHandoff", "PENDING_DETAIL_KEY", "DEFAULT_LINK_PARAM", "id", "comment", "c", "replyId", "popover", "source", "r", "body", "meta", "editedEl", "createEditedMark", "actions", "commentId", "draft", "host", "confirmDiscard", "editor", "createInlineEditor", "text", "saved", "buildCommentLink", "writeStoredComments", "mergeForStorage", "file", "reader", "ev", "released", "isMacOptionC", "isWindowsAltC", "isCustomShortcut", "target", "dx", "dy", "left", "top", "width", "height", "full", "withHiddenOverlay", "renderPage", "dataUrl", "cropRegion", "cropViewport", "err", "clientX", "clientY", "AUTO_SCALE", "prevPointerEvents", "underlying", "container", "SELECTORS", "containerRect", "relativeX", "relativeY", "anchor", "createAnchor", "generateElementSelector", "item", "img", "removeBtn", "e", "x", "y", "circleRadius", "offset", "windowWidth", "windowHeight", "boxRect", "boxWidth", "centerX", "centerY", "adjustedX", "adjustedY", "createId", "captureContext", "circle", "createCommentCircle", "tooltip", "createTooltip", "InboxView", "screenshots", "status", "type", "priority", "src", "existingTooltip", "onDeleteReply", "reply", "replyEl", "createThreadPopover", "headerEl", "actionsEl", "createCommentActions", "copyToClipboard", "buildAgentContext", "actionsRow", "mainScreenshotsContainer", "contextBlock", "createContextBlock", "input", "submitBtn", "threadAttachBtn", "threadFileInput", "threadScreenshotsContainer", "pendingReplyScreenshots", "updateReplyScreenshotsPreview", "i", "submitReply", "repliesContainer", "createReplyElement", "scrollEl", "el", "imageSrc", "lightbox", "closeBtn", "index", "next", "author", "timestamp", "editedAt", "STATUSES", "COMMENT_TYPES", "PRIORITIES", "data", "anchored", "orphaned", "inactive", "resolved", "resolveAnchor", "elRect", "rect", "circleSize", "offsetY", "circleRect", "circleBaseSize", "elWidth", "margin", "preferredTop", "spaceBelow", "observer", "containerWidth", "containerHeight", "absoluteX", "absoluteY", "validatedX", "validatedY", "validatedRelativeX", "validatedRelativeY", "TAG_NAME", "positionData", "wasHidden", "viewportX", "viewportY", "expectedX", "expectedY", "entries", "entry", "closeOpenMenus", "closeOpenConfirmDialogs", "existingStyle", "style", "getStyles", "existingGlobalStyle", "globalStyle", "getGlobalStyles", "overlay_default", "createCommentOverlay", "options", "autoInit", "overlayOptions", "initialize", "overlay_default", "index_default", "createCommentOverlay"]
|
|
7
7
|
}
|