helldots 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,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 = \"&times;\";\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 = \"&times;\";\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 = \"&times;\";\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 = \"&times;\";\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 = \"&times;\";\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 = \"&times;\";\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 = \"&times;\";\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 = \"&times;\";\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/constants.js", "../src/capture-flow.js", "../src/metadata.js", "../src/styles.js", "../src/style-mount.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/context-block.js", "../src/agent-context.js", "../src/popover-controller.js", "../src/marker-engine.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;\nconst 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 * Whether `modern-screenshot` can embed the page's web fonts.\n *\n * It reads `@font-face` rules by parking a `<style>` in a detached document\n * and reading back `.sheet`. That element inherits the host page's CSP, so a\n * policy with a strict `style-src` refuses to parse it, `.sheet` comes back\n * null, and the render dies on `null.cssRules` \u2014 taking the entire capture\n * with it, not just the fonts. Probing costs one detached document; the\n * render it guards costs orders of magnitude more.\n * @returns {boolean}\n */\nexport const canEmbedWebFonts = () => {\n try {\n const probe = document.implementation.createHTMLDocument(\"\");\n const style = probe.createElement(\"style\");\n probe.head.appendChild(style);\n return style.sheet !== null;\n } catch {\n return false;\n }\n};\n\n/**\n * Pulls the `@font-face` blocks out of a stylesheet's source text.\n *\n * Only those: the sheet is a third party's, and appending the whole thing to\n * the host's `<head>` would put its layout rules last in the cascade and\n * restyle the page for the duration of the capture.\n * @param {string} css\n * @returns {string}\n */\nexport const extractFontFaceRules = (css) => {\n const blocks = [];\n let at = css.indexOf(\"@font-face\");\n while (at !== -1) {\n const open = css.indexOf(\"{\", at);\n const close = open === -1 ? -1 : css.indexOf(\"}\", open);\n if (close === -1) break; // truncated sheet \u2014 keep what parsed cleanly\n blocks.push(css.slice(at, close + 1));\n at = css.indexOf(\"@font-face\", close);\n }\n return blocks.join(\"\\n\");\n};\n\n/** Same sheet, same session, one request. @type {Map<string, Promise<string>>} */\nconst fontRuleCache = new Map();\n\nconst fetchFontRules = (href) => {\n if (!fontRuleCache.has(href)) {\n fontRuleCache.set(\n href,\n fetch(href, { mode: \"cors\", credentials: \"omit\" })\n .then((res) => (res.ok ? res.text() : \"\"))\n .then(extractFontFaceRules)\n .catch(() => \"\")\n );\n }\n return fontRuleCache.get(href);\n};\n\nconst isReadable = (sheet) => {\n try {\n return Boolean(sheet.cssRules);\n } catch {\n return false;\n }\n};\n\n/**\n * Makes a cross-origin stylesheet's web fonts reachable by the renderer, and\n * returns the undo.\n *\n * `cssRules` throws `SecurityError` on a cross-origin sheet, so the renderer\n * never finds its `@font-face` rules and never inlines the font files. What\n * it produces is an SVG rendered as an image \u2014 an isolated document with no\n * network of its own \u2014 so a font that was not inlined is simply absent and\n * the text reflows into a fallback. Fallback metrics differ, which moves\n * every glyph sideways: the page still *looks* about right, but a drag crop\n * taken at live coordinates comes back holding the wrong glyphs.\n *\n * `fetch` succeeds where `cssRules` does not \u2014 font CDNs serve\n * `Access-Control-Allow-Origin: *` \u2014 and a same-origin `<style>` carrying\n * those rules is readable, so the renderer inlines the binaries itself\n * rather than us reimplementing that. A host that refuses the fetch (no\n * CORS, a `connect-src` policy) lands exactly where it is today.\n *\n * Off unless asked for: requesting a third party's stylesheet is network a\n * host did not sign up for by mounting a comment widget, so that call is\n * theirs to make.\n * @param {boolean} enabled\n * @returns {Promise<() => void>}\n */\nconst shimUnreadableFontRules = async (enabled) => {\n const noop = () => {};\n if (!enabled || !canEmbedWebFonts()) return noop;\n\n const hrefs = Array.from(document.styleSheets)\n .filter((sheet) => sheet.href && !isReadable(sheet))\n .map((sheet) => sheet.href);\n if (!hrefs.length) return noop;\n\n const css = (await Promise.all(hrefs.map(fetchFontRules)))\n .filter(Boolean)\n .join(\"\\n\");\n if (!css) return noop;\n\n const style = document.createElement(\"style\");\n style.textContent = css;\n document.head.appendChild(style);\n return () => style.remove();\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 *\n * The widget must never render into its own screenshot: the host node is\n * filtered out of the clone. Filtering replaced the old hide-during-render\n * approach (withHiddenOverlay), which took the whole UI off screen for the\n * duration of the render and therefore forced callers to await the capture\n * before showing anything \u2014 with the filter, a capture can run in the\n * background while the comment box is already on screen.\n * @param {{ scale?: number, embedCrossOriginFonts?: boolean }} [options]\n * scale 1 keeps the canvas in CSS pixels so crop rects map 1:1 to page\n * coordinates. `embedCrossOriginFonts` opts into fetching stylesheets the\n * renderer cannot read, so their web fonts survive into the capture.\n * @returns {Promise<any>}\n */\nexport async function renderPage({\n scale = 1,\n embedCrossOriginFonts = false,\n} = {}) {\n const unshim = await shimUnreadableFontRules(embedCrossOriginFonts);\n try {\n // documentElement, not body. The clone is re-parented into a document\n // where the UA's `body { margin: 8px }` applies again, even on a page\n // that zeroed it \u2014 so rendering <body> pushed every flow element 8px\n // right and down inside a canvas that did not grow, losing 8px off the\n // right edge and putting every crop 8px out. <html> carries no such\n // margin, so page coordinates and canvas pixels line up 1:1, which is\n // exactly what the crops below assume.\n return await domToCanvas(document.documentElement, {\n scale,\n backgroundColor: effectiveBackgroundColor(),\n // Dropping web fonts costs one font substitution inside the image;\n // keeping them where they cannot be read costs the image entirely.\n ...(canEmbedWebFonts() ? {} : { font: false }),\n // nodeName, not tagName: the filter also receives text nodes, which\n // must be kept (and have no tagName).\n filter: (node) => node.nodeName?.toLowerCase() !== TAG_NAME,\n });\n } finally {\n unshim();\n }\n}\n\n/**\n * Lays the page's background down across the whole output before the render\n * goes on top.\n *\n * The render covers the BODY's box, which on a page shorter than the\n * viewport is shorter than the crop. Whatever the render does not reach\n * keeps the canvas's initial transparent black \u2014 invisible in a PNG, and a\n * solid black band once JPEG flattens it. The browser paints html/body\n * across the entire viewport, so the background is what is really there.\n * @param {CanvasRenderingContext2D} ctx\n * @param {number} width\n * @param {number} height\n */\nconst paintBackdrop = (ctx, width, height) => {\n ctx.fillStyle = effectiveBackgroundColor();\n ctx.fillRect(0, 0, width, height);\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 paintBackdrop(ctx, width, height);\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 paintBackdrop(ctx, out.width, out.height);\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", "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", "export const CLASSES = {\n CIRCLE: \"comment-circle\",\n CIRCLE_ACTIVE: \"comment-circle--active\",\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 // Set by menus.js when a dropdown has to open upward to stay unclipped.\n INBOX_MENU_UP: \"inbox-menu--up\",\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.\n// Marker circle size in px. The stylesheet's .comment-circle rule and the\n// positioning math (center offsets, edge clamps) must agree on this number.\nexport const MARKER_SIZE = 28;\n\n// Cap on user-attached screenshots per comment or reply \u2014 enforced by every\n// attachment surface (comment box, thread popover, inbox reply input).\nexport const MAX_SCREENSHOTS = 5;\n\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", "// Drag-selection and screenshot-capture orchestration.\n//\n// Extracted from CommentOverlay as part of splitting the god object\n// (DECISIONS.md, Fase 5). This module owns the drag rectangle, the one\n// render each gesture pays for, and the pending automatic capture the\n// click path kicks off in the background. Placement \u2014 anchoring, the\n// comment box \u2014 stays with the overlay and is reached through `onPlace`;\n// the pending-attachments array stays with the comment box that previews\n// it and is fed through `onRegionCaptured`.\n\nimport { renderPage, cropRegion, cropViewport, AUTO_SCALE } from \"./capture.js\";\nimport { CLASSES } from \"./constants.js\";\n\nexport class CaptureFlow {\n /**\n * @param {{\n * host: ShadowRoot,\n * autoScreenshot: boolean,\n * embedCrossOriginFonts?: boolean,\n * onRegionCaptured: (dataUrl: string) => void,\n * onPlace: (x: number, y: number) => Promise<void>,\n * }} deps `host` is where the selection rectangle mounts.\n */\n constructor({\n host,\n autoScreenshot,\n embedCrossOriginFonts = false,\n onRegionCaptured,\n onPlace,\n }) {\n this.host = host;\n this.autoScreenshot = autoScreenshot;\n this.embedCrossOriginFonts = embedCrossOriginFonts;\n this.onRegionCaptured = onRegionCaptured;\n this.onPlace = onPlace;\n\n /**\n * The in-flight automatic capture, resolving to a JPEG data-URL or\n * null. A promise rather than the value: the render kicks off when the\n * comment box opens and the save path awaits it, so the render never\n * gates the box.\n * @type {Promise<string | null> | null}\n */\n this.pendingCapture = null;\n\n /** @type {{ x: number, y: number } | null} */\n this._dragStart = null;\n this._isDragging = false;\n /** @type {HTMLElement | null} */\n this._selectionRect = null;\n this._boundDragMove = (/** @type {MouseEvent} */ e) => this.onDragMove(e);\n this._boundDragEnd = (/** @type {MouseEvent} */ e) => this.onDragEnd(e);\n }\n\n /** Starts tracking a possible drag from a mousedown in comment mode. */\n beginDrag(/** @type {MouseEvent} */ e) {\n this._dragStart = { x: e.clientX, y: e.clientY };\n this._isDragging = false;\n document.addEventListener(\"mousemove\", this._boundDragMove);\n document.addEventListener(\"mouseup\", this._boundDragEnd);\n }\n\n onDragMove(/** @type {MouseEvent} */ 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.host.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(/** @type {MouseEvent} */ 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 // One render feeds both images: the PNG region the user selected\n // and the automatic JPEG context shot. Unlike the click path this\n // one awaits \u2014 the region crop IS what the user asked for, and\n // the box should open with its preview already attached.\n const full = await renderPage({\n scale: 1,\n embedCrossOriginFonts: this.embedCrossOriginFonts,\n });\n const dataUrl = cropRegion(full, { left, top, width, height });\n if (dataUrl) this.onRegionCaptured(dataUrl);\n if (this.autoScreenshot) {\n this.pendingCapture = Promise.resolve(\n cropViewport(full, { sourceScale: 1 })\n );\n }\n } catch (err) {\n console.warn(\"HellDots: screenshot capture failed:\", err);\n }\n }\n\n await this.onPlace(e.clientX, e.clientY);\n } else {\n await this.onPlace(this._dragStart.x, this._dragStart.y);\n }\n\n this._isDragging = false;\n this._dragStart = null;\n }\n\n /**\n * Kicks off the click path's background capture. Half scale because the\n * output is half scale anyway \u2014 the render is the expensive part, and it\n * costs ~4x less here than at scale 1. Deliberately NOT awaited: on heavy\n * pages the render takes hundreds of ms, and gating the comment box on it\n * made every click feel broken. The save path awaits the promise, by\n * which time it has almost always resolved.\n */\n armClickCapture() {\n if (!this.autoScreenshot || this.pendingCapture) return;\n this.pendingCapture = renderPage({\n scale: AUTO_SCALE,\n embedCrossOriginFonts: this.embedCrossOriginFonts,\n })\n .then((full) => cropViewport(full, { sourceScale: AUTO_SCALE }))\n .catch((err) => {\n console.warn(\"HellDots: automatic screenshot failed\", err);\n return null;\n });\n }\n\n /**\n * The capture the save path attaches \u2014 null when none is in flight.\n * @returns {Promise<string | null>}\n */\n async consumePending() {\n return this.pendingCapture ? await this.pendingCapture : null;\n }\n\n /** Dismissing the comment box must not leak its capture into the next. */\n clearPending() {\n this.pendingCapture = null;\n }\n\n /** Drops listeners and the selection rectangle, even mid-gesture. */\n destroy() {\n document.removeEventListener(\"mousemove\", this._boundDragMove);\n document.removeEventListener(\"mouseup\", this._boundDragEnd);\n this._selectionRect?.remove();\n this._selectionRect = null;\n this.pendingCapture = null;\n this._dragStart = null;\n this._isDragging = false;\n }\n}\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", "import {\n CLASSES,\n IDS,\n Z_INDEX,\n CURSOR_SVG,\n CURSOR_HOTSPOT,\n MARKER_SIZE,\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 = ` scrollbar-width:thin;scrollbar-color:rgba(255,255,255,0.22) transparent;`;\n\nconst webkitScrollbar = (...selectors) =>\n selectors\n .map(\n (selector) => ` ${selector}::-webkit-scrollbar{width:8px;height:8px;}${selector}::-webkit-scrollbar-track{background:transparent;}${selector}::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.22);border-radius:4px;}${selector}::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,0.35);}`\n )\n .join(\"\");\n\nexport const getStyles = () => ` :host{all:initial;display:block;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif;line-height:1.5;color-scheme:light;}:host *,:host *::before,:host *::after{box-sizing:border-box;font-family:inherit;}button{padding:0;font:inherit;color:inherit;}#${IDS.TOOLBAR}{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:${Z_INDEX.TOOLBAR};}.${CLASSES.TOOLBAR_ACTION_WRAPPER}{position:relative;}.${CLASSES.TOOLBAR_ACTION_TOOLTIP}{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translateX(-50%) translateY(4px);display:flex;align-items:center;gap:8px;background:rgba(20,20,23,0.95);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);padding:8px 12px;border-radius:10px;border:1px solid rgba(255,255,255,0.08);box-shadow:0 4px 24px rgba(0,0,0,0.35);color:white;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 0.15s ease,transform 0.15s ease;}.${CLASSES.TOOLBAR_ACTION_WRAPPER}:hover .${\n CLASSES.TOOLBAR_ACTION_TOOLTIP\n }{opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0);}.${CLASSES.TOOLBAR_TEXT}{font-size:13px;font-weight:500;letter-spacing:-0.01em;}.${CLASSES.SHORTCUT_HINT}{font-size:11px;font-weight:500;color:rgba(255,255,255,0.5);background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);padding:2px 6px;border-radius:5px;line-height:1;white-space:nowrap;}.${CLASSES.TOOLBAR_ACTIONS}{display:flex;flex-direction:row;background:rgba(20,20,23,0.95);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,0.35);}.${CLASSES.TOOLBAR_ACTION_BTN}{width:42px;height:42px;display:flex;align-items:center;justify-content:center;background:none;border:none;outline:none;color:rgba(255,255,255,0.65);cursor:pointer;transition:background 0.2s,color 0.2s;padding:0;}.${CLASSES.TOOLBAR_ACTION_WRAPPER}:first-child .${\n CLASSES.TOOLBAR_ACTION_BTN\n }{border-radius:12px 0 0 12px;}.${CLASSES.TOOLBAR_ACTION_WRAPPER}:last-child .${\n CLASSES.TOOLBAR_ACTION_BTN\n }{border-radius:0 12px 12px 0;}.${CLASSES.TOOLBAR_ACTION_BTN}:hover{background:rgba(255,255,255,0.08);color:white;}.${CLASSES.TOOLBAR_COMMENT_BTN}.${CLASSES.ACTIVE}{color:#2E90FA;background:rgba(46,144,250,0.1);}#${IDS.COMMENT_BOX}{position:fixed;background:#1C1C1E;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,0.4);padding:16px;z-index:${Z_INDEX.COMMENT_BOX};width:min(400px,calc(100vw - 24px));display:none;box-sizing:border-box;}#${IDS.COMMENT_BOX} .${CLASSES.COMMENT_INPUT_AREA}{display:flex;flex-direction:column;gap:0;}.${CLASSES.CLASSIFY_ROW}{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:0 0 10px;border-bottom:1px solid rgba(255,255,255,0.08);}.${CLASSES.CLASSIFY_ROW} .${CLASSES.INBOX_ACTION_BTN}{height:26px;border:1px solid rgba(255,255,255,0.12);border-radius:8px;}.${CLASSES.CLASSIFY_ROW} .${CLASSES.INBOX_ACTION_BTN}:hover{border-color:rgba(255,255,255,0.28);}#${IDS.COMMENT_INPUT}{flex:1;min-height:20px;background:#1C1C1E;border:none;resize:none;font-family:inherit;color:white;font-size:14px;line-height:1.4;box-sizing:border-box;field-sizing:content;padding:8px 0;}#${IDS.COMMENT_INPUT}::placeholder{color:rgba(255,255,255,0.5);}#${IDS.COMMENT_INPUT}:focus{outline:none;box-shadow:none;}.${CLASSES.COMMENT_ACTIONS_BAR}{display:flex;align-items:center;justify-content:space-between;padding-top:12px;}.${CLASSES.ATTACH_IMAGE_BTN}{background:none;border:none;color:rgba(255,255,255,0.5);cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:6px;transition:background 0.2s,color 0.2s;}.${CLASSES.ATTACH_IMAGE_BTN}:hover{background:rgba(255,255,255,0.1);color:rgba(255,255,255,0.8);}.${CLASSES.CIRCLE}{position:absolute;width:${MARKER_SIZE}px;height:${MARKER_SIZE}px;background:#2E90FA;border-radius:0% 100% 100% 100%;border:2px solid #FFF;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,0.2);transition:transform 0.2s,background 0.2s;z-index:${Z_INDEX.CIRCLE};transform:translate(-50%,-50%);}.${CLASSES.CIRCLE}:hover{transform:translate(-50%,-50%) scale(1.2) !important;background:rgb(0,123,255);}.${CLASSES.CIRCLE}.${CLASSES.HIGHLIGHT}{transform:translate(-50%,-50%) scale(1.2) !important;background:rgb(0,123,255);box-shadow:0 0 0 4px rgba(46,144,250,0.35),0 1px 5px rgba(0,0,0,0.2);}.${CLASSES.CIRCLE}.${CLASSES.CIRCLE_ACTIVE}{transform:translate(-50%,-50%) scale(1.2) !important;background:rgb(0,123,255);box-shadow:0 0 0 5px rgba(46,144,250,0.5),0 1px 5px rgba(0,0,0,0.2);}.${CLASSES.TOOLTIP}{position:fixed;background:#1C1C1E;border-radius:12px;padding:16px;box-shadow:0 4px 20px rgba(0,0,0,0.4);width:min(400px,calc(100vw - 24px));max-height:calc(100vh - 20px);overflow-y:auto;overscroll-behavior:contain;${SCROLLBAR} z-index:${Z_INDEX.TOOLTIP};color:white;font-size:14px;line-height:1.5;box-sizing:border-box;}.${CLASSES.TOOLTIP} .${CLASSES.THREAD_BODY}{padding:8px 0;}.${CLASSES.THREAD_POPOVER}{position:fixed;background:#1C1C1E;border-radius:12px;padding:16px;box-shadow:0 4px 20px rgba(0,0,0,0.4);width:min(400px,calc(100vw - 24px));max-height:calc(100vh - 20px);display:flex;flex-direction:column;z-index:${Z_INDEX.TOOLTIP};color:white;font-size:14px;line-height:1.5;box-sizing:border-box;}.${CLASSES.THREAD_POPOVER}>.${CLASSES.THREAD_HEADER},.${CLASSES.THREAD_POPOVER}>.${CLASSES.THREAD_ACTIONS_ROW},.${CLASSES.THREAD_POPOVER}>.${CLASSES.THREAD_INPUT_AREA}{flex:none;}.${CLASSES.THREAD_SCROLL}{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;${SCROLLBAR}}${webkitScrollbar(\n `.${CLASSES.THREAD_SCROLL}`,\n `.${CLASSES.TOOLTIP}`,\n `.${CLASSES.INBOX_LIST}`,\n `.${CLASSES.INBOX_DETAIL}`,\n `.${CLASSES.EDITOR_INPUT}`\n)} .${CLASSES.INBOX_PANEL}{position:fixed;top:16px;right:16px;bottom:16px;width:380px;display:flex;flex-direction:column;background:#1C1C1E;border:1px solid rgba(255,255,255,0.08);border-radius:14px;box-shadow:0 8px 32px rgba(0,0,0,0.5);z-index:${Z_INDEX.COMMENT_BOX};color:white;font-size:14px;line-height:1.5;box-sizing:border-box;overflow:hidden;}@media (max-width:480px){.${CLASSES.INBOX_PANEL}{left:16px;width:auto;}}.${CLASSES.INBOX_HEADER},.${CLASSES.INBOX_DETAIL_HEADER}{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 14px;border-bottom:1px solid rgba(255,255,255,0.08);flex:none;}.${CLASSES.INBOX_FILTER}-wrapper{position:relative;}.${CLASSES.INBOX_FILTER}{display:flex;align-items:center;gap:6px;background:transparent;border:none;color:white;font-size:13px;font-weight:600;cursor:pointer;padding:4px 6px;border-radius:6px;}.${CLASSES.INBOX_FILTER}:hover{background:rgba(255,255,255,0.08);}.${CLASSES.INBOX_FILTER_MENU}{position:absolute;top:calc(100% + 6px);left:0;background:#2C2C2E;border:1px solid rgba(255,255,255,0.1);border-radius:12px;padding:14px;width:300px;max-width:calc(100vw - 40px);z-index:1;box-shadow:0 8px 28px rgba(0,0,0,0.5);}.${CLASSES.INBOX_FILTER_MENU_HEADER}{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-size:13px;font-weight:600;margin-bottom:12px;}.${CLASSES.INBOX_FILTER_CLEAR}{background:transparent;border:none;color:rgba(255,255,255,0.55);font-size:12px;font-weight:500;cursor:pointer;padding:0;}.${CLASSES.INBOX_FILTER_CLEAR}:hover:not(:disabled){color:white;}.${CLASSES.INBOX_FILTER_CLEAR}:disabled{opacity:0.35;cursor:default;}.${CLASSES.INBOX_FILTER_GROUP} + .${CLASSES.INBOX_FILTER_GROUP}{margin-top:14px;}.${CLASSES.INBOX_FILTER_SECTION}{font-size:11px;font-weight:600;color:rgba(255,255,255,0.45);margin-bottom:8px;}.${CLASSES.INBOX_FILTER_CHIPS}{display:flex;flex-wrap:wrap;gap:6px;}.${CLASSES.INBOX_FILTER_CHIP}{background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:999px;color:rgba(255,255,255,0.75);font-size:12px;line-height:1;padding:7px 12px;cursor:pointer;transition:background 0.15s,border-color 0.15s,color 0.15s;}.${CLASSES.INBOX_FILTER_CHIP}:hover{border-color:rgba(255,255,255,0.4);color:white;}.${CLASSES.INBOX_FILTER_CHIP}[aria-checked=\"true\"]{background:rgba(255,255,255,0.92);border-color:rgba(255,255,255,0.92);color:#1C1C1E;font-weight:600;}.${CLASSES.INBOX_MENU_ITEM}{display:block;width:100%;text-align:left;background:transparent;border:none;color:white;font-size:13px;padding:7px 10px;border-radius:6px;cursor:pointer;}.${CLASSES.INBOX_MENU_ITEM}:hover{background:rgba(255,255,255,0.08);}.${CLASSES.INBOX_CLOSE},.${CLASSES.INBOX_NAV_BTN},.${CLASSES.INBOX_BACK}{display:flex;align-items:center;gap:4px;background:transparent;border:none;color:rgba(255,255,255,0.75);cursor:pointer;padding:4px 6px;border-radius:6px;font-size:14px;}.${CLASSES.INBOX_CLOSE}{font-size:20px;line-height:1;}.${CLASSES.INBOX_CLOSE}:hover,.${CLASSES.INBOX_NAV_BTN}:not(:disabled):hover,.${CLASSES.INBOX_BACK}:hover{background:rgba(255,255,255,0.08);color:white;}.${CLASSES.INBOX_NAV_BTN}:disabled{opacity:0.35;cursor:default;}.${CLASSES.INBOX_LIST},.${CLASSES.INBOX_DETAIL}{flex:1;overflow-y:auto;${SCROLLBAR} padding:12px;display:flex;flex-direction:column;gap:12px;}.${CLASSES.INBOX_CARD}{border:1px solid rgba(255,255,255,0.1);border-radius:10px;padding:12px;display:flex;flex-direction:column;gap:8px;}.${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD}{cursor:pointer;}.${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD}:hover{border-color:rgba(255,255,255,0.22);}.${CLASSES.INBOX_CARD}--resolved{border-color:rgba(48,209,88,0.4);opacity:0.75;}.${CLASSES.INBOX_LIST} .${CLASSES.INBOX_CARD}--resolved:hover{border-color:rgba(48,209,88,0.7);opacity:1;}.${CLASSES.INBOX_CARD_HEADER}{display:flex;align-items:center;justify-content:space-between;gap:8px;}.${CLASSES.INBOX_CARD_ACTIONS}{display:flex;align-items:center;flex-wrap:wrap;justify-content:flex-end;gap:6px;}.${CLASSES.INBOX_ACTION_BTN}{display:flex;align-items:center;justify-content:center;width:24px;height:24px;background:transparent;border:none;border-radius:6px;color:rgba(255,255,255,0.65);cursor:pointer;}.${CLASSES.INBOX_ACTION_BTN}:hover{background:rgba(255,255,255,0.08);color:white;}.${CLASSES.INBOX_ACTION_BTN_LABELED}{width:auto;padding:0 8px 0 6px;gap:5px;justify-content:flex-start;flex:none;}.${CLASSES.INBOX_ACTION_LABEL}{font-size:11px;line-height:1;white-space:nowrap;}.${CLASSES.INBOX_STATUS_DOT}{width:12px;height:12px;border-radius:50%;border:1.5px solid rgba(255,255,255,0.45);display:inline-block;flex:none;}.${CLASSES.INBOX_MENU_ITEM} .${CLASSES.INBOX_STATUS_DOT}{width:9px;height:9px;border:none;margin-right:8px;vertical-align:baseline;}.${CLASSES.INBOX_MENU_ITEM}[aria-checked=\"true\"]{background:rgba(255,255,255,0.08);}.${CLASSES.THREAD_ACTIONS_ROW}{display:flex;justify-content:flex-end;}.${CLASSES.THREAD_POPOVER}>.${CLASSES.THREAD_ACTIONS_ROW}{padding-top:8px;}[data-hd-tooltip]{position:relative;}[data-hd-tooltip]::after{content:attr(data-hd-tooltip);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#000;color:white;padding:4px 8px;border-radius:6px;font-size:11px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 0.12s ease;z-index:2;}[data-hd-tooltip]:hover::after{opacity:1;}.${CLASSES.INBOX_MENU}{position:absolute;top:calc(100% + 4px);right:0;background:#2C2C2E;border:1px solid rgba(255,255,255,0.1);border-radius:8px;padding:4px;min-width:130px;z-index:1;box-shadow:0 4px 16px rgba(0,0,0,0.4);}.${CLASSES.INBOX_MENU}.${CLASSES.INBOX_MENU_UP}{top:auto;bottom:calc(100% + 4px);}.${CLASSES.INBOX_CARD_TEXT}{white-space:pre-wrap;word-break:break-word;}.${CLASSES.INBOX_CARD_TAG}{align-self:flex-start;padding:1px 8px;border-radius:999px;background:rgba(255,159,10,0.2);color:#FF9F0A;font-size:11px;font-weight:600;}.${CLASSES.INBOX_BADGES}{display:flex;flex-wrap:wrap;gap:4px;}.${CLASSES.INBOX_CARD} .${CLASSES.INBOX_BADGES}{margin-top:6px;}.${CLASSES.TOOLTIP} .${CLASSES.INBOX_BADGES}{margin-bottom:4px;}.${CLASSES.BADGE}{display:inline-flex;align-items:center;padding:1px 6px;border:1px solid rgba(255,255,255,0.18);border-radius:10px;font-size:10px;line-height:1.6;letter-spacing:0.01em;white-space:nowrap;}.${CLASSES.BADGE_STATUS},.${CLASSES.BADGE_TYPE},.${CLASSES.BADGE_PRIORITY}{font-weight:600;}.${CLASSES.BADGE_TAG}{opacity:0.75;}.${CLASSES.BADGE_DURATION}{opacity:0.75;border-style:dashed;}.${CLASSES.CONTEXT_BLOCK}{display:flex;flex-direction:column;padding:10px 12px;border-top:1px solid rgba(255,255,255,0.08);font-size:11px;}.${CLASSES.CONTEXT_BODY}{display:flex;flex-direction:column;gap:4px;}.${CLASSES.CONTEXT_TITLE}{font-size:11px;font-weight:600;color:rgba(255,255,255,0.45);padding-bottom:4px;}.${CLASSES.CONTEXT_TOGGLE}{display:flex;align-items:center;justify-content:space-between;width:100%;background:transparent;border:none;color:rgba(255,255,255,0.45);font-size:11px;font-weight:600;cursor:pointer;padding:2px 0;}.${CLASSES.CONTEXT_TOGGLE}:hover{color:rgba(255,255,255,0.75);}.${CLASSES.CONTEXT_TOGGLE} svg{flex:none;transition:transform 0.15s ease;}.${CLASSES.CONTEXT_TOGGLE}[aria-expanded=\"true\"] svg{transform:rotate(180deg);}.${CLASSES.CONTEXT_TOGGLE}[aria-expanded=\"true\"] + .${CLASSES.CONTEXT_BODY}{padding-top:8px;}.${CLASSES.CONTEXT_BLOCK} img{width:100%;border-radius:6px;margin-bottom:6px;cursor:zoom-in;}.${CLASSES.CONTEXT_SCREENSHOT_CAPTION}{opacity:0.75;}.${CLASSES.CONTEXT_ROW}{display:flex;justify-content:space-between;gap:12px;opacity:0.75;}.${CLASSES.CONTEXT_ROW} span:last-child{text-align:right;word-break:break-all;}.${CLASSES.INBOX_CARD_REPLY_LINK}{align-self:flex-start;background:transparent;border:none;color:rgba(255,255,255,0.55);font-size:13px;cursor:pointer;padding:0;}.${CLASSES.INBOX_CARD_REPLY_LINK}:hover{color:white;}.${CLASSES.INBOX_REPLIES}{display:flex;flex-direction:column;gap:10px;padding:0 4px;}.${CLASSES.TOOLTIP_REPLY_COUNT}{font-size:12px;color:rgba(255,255,255,0.45);padding-top:4px;}.${CLASSES.INBOX_EMPTY}{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:32px 24px;color:rgba(255,255,255,0.55);text-align:center;}.${CLASSES.INBOX_EMPTY_ICON}{width:44px;height:44px;margin-bottom:6px;border:2px dashed rgba(255,255,255,0.25);border-radius:0% 100% 100% 100%;flex:none;}.${CLASSES.INBOX_EMPTY_TITLE}{color:white;font-size:14px;font-weight:600;}.${CLASSES.INBOX_EMPTY_TEXT}{font-size:13px;line-height:1.5;max-width:30ch;}.${CLASSES.INBOX_EMPTY_KBD}{font-family:inherit;font-size:12px;font-weight:500;color:rgba(255,255,255,0.8);background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.14);border-radius:5px;padding:1px 5px;white-space:nowrap;}.${CLASSES.INBOX_EMPTY_ACTION}{margin-top:6px;background:transparent;border:1px solid rgba(255,255,255,0.2);border-radius:8px;color:white;font-size:13px;font-weight:500;padding:8px 16px;cursor:pointer;transition:background 0.15s,border-color 0.15s;}.${CLASSES.INBOX_EMPTY_ACTION}:hover{background:rgba(255,255,255,0.08);border-color:rgba(255,255,255,0.35);}.${CLASSES.THREAD_HEADER}{display:flex;justify-content:space-between;align-items:center;padding:0;}.${CLASSES.THREAD_META}{display:flex;align-items:center;gap:6px;flex:1;min-width:0;}.${CLASSES.THREAD_AUTHOR}{font-weight:600;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.${CLASSES.THREAD_TIME}{font-size:12px;color:rgba(255,255,255,0.5);cursor:default;position:relative;flex:none;}.${CLASSES.THREAD_TIME}::after{content:attr(data-full-date);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#000;color:white;padding:4px 8px;border-radius:6px;font-size:11px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 0.15s ease;z-index:1;}.${CLASSES.THREAD_TIME}:hover::after{opacity:1;}.${CLASSES.THREAD_BODY}{padding:8px 0;white-space:pre-wrap;word-break:break-word;}.${CLASSES.THREAD_REPLIES}{padding:0;}.${CLASSES.THREAD_REPLIES}:empty{display:none;}.${CLASSES.THREAD_REPLY}{padding:16px 0 0 0;border-top:1px solid rgba(255,255,255,0.1);white-space:pre-wrap;word-break:break-word;font-size:13px;color:rgba(255,255,255,0.85);}.${CLASSES.THREAD_REPLY} .${CLASSES.THREAD_META}{margin-bottom:2px;}.${CLASSES.THREAD_REPLY_ACTIONS}{margin-left:auto;flex:none;}.${CLASSES.THREAD_REPLY} .${CLASSES.SCREENSHOT_IMG}{width:144px;height:100px;object-fit:cover;border-radius:8px;margin-top:4px;cursor:pointer;display:block;}.${CLASSES.THREAD_INPUT_AREA}{display:flex;flex-direction:column;gap:0;padding:12px 0 0;border-top:1px solid rgba(255,255,255,0.1);}.${CLASSES.THREAD_INPUT}{width:100%;background:transparent;border:none;padding:0;color:white;font-size:14px;font-family:inherit;outline:none;box-sizing:border-box;}.${CLASSES.THREAD_INPUT}::placeholder{color:rgba(255,255,255,0.5);}.${CLASSES.THREAD_INPUT}:focus{outline:none;box-shadow:none;}.${CLASSES.THREAD_SUBMIT}{background:none;border:none;color:#2E90FA;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:6px;transition:background 0.2s;}.${CLASSES.THREAD_SUBMIT}:hover{background:rgba(46,144,250,0.15);color:#1570D6;}.${CLASSES.CLOSE_TOOLTIP}{background:none;border:none;font-size:18px;cursor:pointer;color:rgba(255,255,255,0.5);line-height:1;}.${CLASSES.CLOSE_TOOLTIP}:hover{color:white;}.${CLASSES.PREVIEW_CIRCLE}{animation:helldots-pulse 1.5s ease-in-out infinite;}@keyframes helldots-pulse{0%,100%{box-shadow:0 0 0 0 rgba(46,144,250,0.4),0 1px 5px rgba(0,0,0,0.2);}50%{box-shadow:0 0 0 8px rgba(46,144,250,0),0 1px 5px rgba(0,0,0,0.2);}}.${CLASSES.COMMENT_OVERLAY}{position:fixed;top:0;left:0;right:0;bottom:0;background:transparent;pointer-events:none;z-index:${Z_INDEX.TOOLBAR - 1};}.${CLASSES.COMMENT_OVERLAY}.${CLASSES.ACTIVE}{pointer-events:auto;background:rgba(0,0,0,0.1);}.${CLASSES.SELECTION_RECT}{position:fixed;border:2px solid #2E90FA;background:rgba(46,144,250,0.1);pointer-events:none;z-index:${Z_INDEX.TOOLTIP};box-sizing:border-box;}.${CLASSES.SCREENSHOTS_CONTAINER}{display:none;overflow-x:auto;gap:8px;margin-top:4px;padding:4px 0;scrollbar-width:none;-ms-overflow-style:none;margin-bottom:8px;}.${CLASSES.SCREENSHOTS_CONTAINER}::-webkit-scrollbar{display:none;}.${CLASSES.SCREENSHOTS_CONTAINER}.${CLASSES.ACTIVE}{display:flex;}.${CLASSES.SCREENSHOT_ITEM}{position:relative;flex-shrink:0;}.${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_IMG}{width:50px;height:50px;object-fit:cover;border-radius:8px;cursor:pointer;display:block;margin:0;}.${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_IMG}:hover{opacity:0.85;}.${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_REMOVE}{position:absolute;top:-5px;right:-5px;width:18px;height:18px;background:rgba(0,0,0,0.7);border:none;border-radius:50%;color:white;font-size:12px;line-height:1;cursor:pointer;display:none;align-items:center;justify-content:center;z-index:1;}.${CLASSES.SCREENSHOT_ITEM}:hover .${CLASSES.SCREENSHOT_REMOVE}{display:flex;}.${CLASSES.SCREENSHOT_ITEM} .${CLASSES.SCREENSHOT_REMOVE}:hover{background:rgba(0,0,0,0.9);}.${CLASSES.TOOLTIP}>.${CLASSES.SCREENSHOTS_CONTAINER} .${\n CLASSES.SCREENSHOT_ITEM\n } .${CLASSES.SCREENSHOT_IMG},.${CLASSES.THREAD_SCROLL}>.${CLASSES.SCREENSHOTS_CONTAINER} .${\n CLASSES.SCREENSHOT_ITEM\n } .${CLASSES.SCREENSHOT_IMG},.${CLASSES.THREAD_REPLY} .${CLASSES.SCREENSHOT_ITEM} .${\n CLASSES.SCREENSHOT_IMG\n }{width:144px;height:100px;}.${CLASSES.CONFIRM}{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;padding:16px;animation:helldots-fade-in 0.15s ease;}.${CLASSES.CONFIRM_PANEL}{width:min(360px,100%);background:#1C1C1E;border:1px solid rgba(255,255,255,0.1);border-radius:14px;box-shadow:0 8px 32px rgba(0,0,0,0.5);padding:20px;color:white;font-size:14px;line-height:1.5;box-sizing:border-box;}.${CLASSES.CONFIRM_TITLE}{margin:0 0 8px;font-size:15px;font-weight:600;}.${CLASSES.CONFIRM_MESSAGE}{margin:0 0 20px;font-size:13px;color:rgba(255,255,255,0.65);}.${CLASSES.CONFIRM_ACTIONS}{display:flex;justify-content:flex-end;gap:8px;}.${CLASSES.CONFIRM_CANCEL},.${CLASSES.CONFIRM_ACCEPT}{padding:7px 14px;border:1px solid transparent;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;}.${CLASSES.CONFIRM_CANCEL}:focus-visible,.${CLASSES.CONFIRM_ACCEPT}:focus-visible{outline:2px solid #2E90FA;outline-offset:2px;}.${CLASSES.CONFIRM_CANCEL}{background:rgba(255,255,255,0.08);border-color:rgba(255,255,255,0.12);color:white;}.${CLASSES.CONFIRM_CANCEL}:hover{background:rgba(255,255,255,0.14);}.${CLASSES.CONFIRM_ACCEPT}{background:#FF453A;color:white;}.${CLASSES.CONFIRM_ACCEPT}:hover{background:#FF6961;}.${CLASSES.LIGHTBOX}{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.92);z-index:${Z_INDEX.LIGHTBOX};display:flex;align-items:center;justify-content:center;animation:helldots-fade-in 0.2s ease;}@keyframes helldots-fade-in{from{opacity:0;}to{opacity:1;}}.${CLASSES.LIGHTBOX_IMG}{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:8px;}.${CLASSES.LIGHTBOX_CLOSE}{position:absolute;top:16px;right:16px;background:rgba(255,255,255,0.15);border:none;color:white;font-size:24px;width:40px;height:40px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background 0.2s;}.${CLASSES.LIGHTBOX_CLOSE}:hover{background:rgba(255,255,255,0.3);}.${CLASSES.EDITOR}{display:flex;flex-direction:column;gap:8px;margin:4px 0 2px;}.${CLASSES.EDITOR_INPUT}{width:100%;box-sizing:border-box;resize:vertical;min-height:60px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.14);border-radius:8px;padding:8px 10px;color:white;font-size:14px;font-family:inherit;line-height:1.45;outline:none;${SCROLLBAR}}.${CLASSES.EDITOR_INPUT}:focus{border-color:rgba(46,144,250,0.7);}.${CLASSES.EDITOR_ACTIONS}{display:flex;justify-content:flex-end;gap:8px;}.${CLASSES.EDITOR_CANCEL},.${CLASSES.EDITOR_SAVE}{padding:5px 12px;border:1px solid transparent;border-radius:7px;font-size:13px;font-weight:600;font-family:inherit;cursor:pointer;}.${CLASSES.EDITOR_CANCEL}{background:rgba(255,255,255,0.08);border-color:rgba(255,255,255,0.12);color:white;}.${CLASSES.EDITOR_CANCEL}:hover{background:rgba(255,255,255,0.14);}.${CLASSES.EDITOR_SAVE}{background:#2E90FA;color:white;}.${CLASSES.EDITOR_SAVE}:hover:not(:disabled){background:#57A6FB;}.${CLASSES.EDITOR_SAVE}:disabled{background:rgba(255,255,255,0.10);color:rgba(255,255,255,0.4);cursor:not-allowed;}.${CLASSES.THREAD_EDITED}{font-size:12px;color:rgba(255,255,255,0.4);cursor:default;position:relative;flex:none;}.${CLASSES.THREAD_EDITED}::before{content:\"\u00B7\";margin-right:4px;}.${CLASSES.THREAD_EDITED}::after{content:attr(data-full-date);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#000;color:white;padding:4px 8px;border-radius:6px;font-size:11px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 0.15s ease;}.${CLASSES.THREAD_EDITED}:hover::after{opacity:1;}.${CLASSES.INBOX_NOTICE}{margin:0 0 10px;padding:9px 11px;border-radius:8px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.12);color:rgba(255,255,255,0.75);font-size:13px;line-height:1.4;}`;\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 = () => ` .${CLASSES.COMMENT_CURSOR},.${CLASSES.COMMENT_CURSOR} *{cursor:url('${CURSOR_SVG}') ${CURSOR_HOTSPOT},auto !important;}`;\n", "// How the widget's CSS reaches the page.\n//\n// Injecting a <style> element is what a strict `style-src` Content Security\n// Policy blocks \u2014 and a blocked stylesheet is not a cosmetic problem here:\n// markers are positioned by CSS, so the widget becomes unusable rather than\n// merely ugly. Constructed stylesheets (`new CSSStyleSheet` + `replaceSync`\n// + `adoptedStyleSheets`) are not subject to style-src, because nothing is\n// parsed from document markup.\n//\n// The <style> path stays as the fallback for platforms without constructed\n// sheets (and for jsdom, where the whole test suite runs).\n\n/**\n * Adopts or injects `css` into `target`.\n *\n * @param {ShadowRoot | Document} target where the styles apply \u2014 a shadow\n * root for the widget's own UI, the document for the few rules that\n * target the host page (the comment-mode cursor on <body>).\n * @param {string} css\n * @param {string} fallbackId id given to the injected <style>, so the\n * fallback path stays inspectable and idempotent.\n * @returns {() => void} detaches exactly what this call mounted\n */\nexport function mountStyles(target, css, fallbackId) {\n const sheet = constructSheet(css, target);\n if (sheet) {\n // Appended, never assigned over: a host app (Lit, or anything else\n // using constructed sheets) adopts onto the document too, and\n // replacing the array would delete its styles.\n target.adoptedStyleSheets = [...(target.adoptedStyleSheets ?? []), sheet];\n return () => {\n target.adoptedStyleSheets = (target.adoptedStyleSheets ?? []).filter(\n (candidate) => candidate !== sheet\n );\n };\n }\n\n // A Document mounts into <head>; a shadow root takes the element itself.\n const parent = /** @type {any} */ (target).head ?? target;\n /** @type {any} */ (parent).querySelector?.(`#${fallbackId}`)?.remove();\n\n const style = document.createElement(\"style\");\n style.id = fallbackId;\n style.textContent = css;\n parent.appendChild(style);\n return () => style.remove();\n}\n\n/**\n * A constructed stylesheet, or null where the platform cannot provide one.\n *\n * Both halves are checked: Safari shipped `CSSStyleSheet` for years without\n * making it constructible, and jsdom constructs sheets happily while not\n * implementing `adoptedStyleSheets` at all \u2014 so a sheet nobody can adopt\n * would silently style nothing.\n */\nfunction constructSheet(css, target) {\n if (typeof CSSStyleSheet !== \"function\") return null;\n if (!(\"adoptedStyleSheets\" in target)) return null;\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(css);\n return sheet;\n } catch {\n return null;\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 modifierShift: \"Shift\",\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 modifierShift: \"May\u00FAs\",\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. An unknown\n * code falls back to English wholesale; a known locale falls back to\n * English PER KEY, so a translation added to en.js but not yet to a sibling\n * locale degrades to English instead of rendering literal \"undefined\".\n * @param {string} [localeCode]\n * @returns {typeof en}\n */\nexport function getStrings(localeCode) {\n const selected = LOCALES[localeCode];\n if (!selected || localeCode === DEFAULT_LOCALE) {\n return LOCALES[DEFAULT_LOCALE];\n }\n return { ...LOCALES[DEFAULT_LOCALE], ...selected };\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 // An anchor written by a newer schema than this code understands is\n // treated as orphaned rather than half-interpreted: the comment stays\n // listed but is never positioned over a guessed element.\n if (anchor?.version != null && anchor.version > 1) return null;\n\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 // Ids are compared on their string form (see id.js) \u2014 a numeric legacy id\n // and its string spelling are the same comment, never two entries.\n const currentIds = new Set(current.map((c) => String(c.id)));\n const kept = stored.filter(\n (c) => !currentIds.has(String(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\n/**\n * The one way to compare ids. New ids are strings, but Date.now()-era\n * records still hold numbers, and either spelling may have crossed a JSON\n * or URL boundary since \u2014 so equality is defined on the string form, as\n * `index.d.ts` promises callers.\n *\n * @param {string | number} a\n * @param {string | number} b\n * @returns {boolean}\n */\nexport const sameId = (a, b) => String(a) === String(b);\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\nimport { CLASSES } from \"./constants.js\";\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/** @type {((e: KeyboardEvent) => void) | null} */\nlet keyListener = null;\n\nconst menuItems = (menu) => [\n ...menu.querySelectorAll('[role=\"menuitem\"], [role=\"menuitemradio\"]'),\n];\n\n/** Vertical gap between a menu and the button it belongs to (matches the CSS). */\nconst MENU_GAP = 4;\n\n/**\n * The nearest ancestor that would clip the menu, or null when only the\n * viewport bounds it. Anything with a non-visible overflow clips: the thread's\n * scroll container and the inbox list are the two that matter, and both are\n * exactly where a dropdown near the bottom edge used to become unreachable.\n *\n * Walking up stops at the shadow root, whose overflow is not ours to read.\n *\n * @param {HTMLElement} menu\n * @returns {DOMRect | null}\n */\nconst clipperRectOf = (menu) => {\n for (let el = menu.parentElement; el; el = el.parentElement) {\n const { overflowX, overflowY } = getComputedStyle(el);\n if (overflowX !== \"visible\" || overflowY !== \"visible\") {\n return el.getBoundingClientRect();\n }\n }\n return null;\n};\n\n/**\n * Opens the menu upward when it would otherwise be clipped below \u2014 and only\n * when it actually fits up there. Flipping a menu taller than its container\n * would just clip the other end while also reversing the position the user\n * reaches for, so in that case it stays put.\n *\n * Measured on every open, never cached: a menu on a row that scrolled since\n * last time has different room than it did.\n *\n * @param {HTMLElement} button\n * @param {HTMLElement} menu the menu, already displayed and placed downward\n */\nconst placeMenu = (button, menu) => {\n menu.classList.remove(CLASSES.INBOX_MENU_UP);\n\n const clipper = clipperRectOf(menu);\n const floor = Math.min(clipper?.bottom ?? Infinity, window.innerHeight);\n const ceiling = Math.max(clipper?.top ?? 0, 0);\n\n const { height } = menu.getBoundingClientRect();\n const anchor = button.getBoundingClientRect();\n const bottomIfDown = anchor.bottom + MENU_GAP + height;\n const topIfUp = anchor.top - MENU_GAP - height;\n\n if (bottomIfDown > floor && topIfUp >= ceiling) {\n menu.classList.add(CLASSES.INBOX_MENU_UP);\n }\n};\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 // role=\"menu\"/\"menuitem\" promises keyboard behavior (ARIA menu pattern):\n // Escape closes THIS layer only \u2014 never the popover behind it, which is\n // why the listener runs in the capture phase and stops propagation \u2014 and\n // the arrow keys walk the items. Registered while a menu is open, exactly\n // like the mousedown watcher.\n keyListener = (e) => {\n const entry = [...openMenus].pop();\n if (!entry) return;\n\n if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n entry.close();\n entry.button.focus();\n return;\n }\n\n if ([\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"].includes(e.key)) {\n const items = menuItems(entry.menu);\n if (items.length === 0) return;\n e.preventDefault();\n e.stopPropagation();\n // Menus live in a shadow root, where document.activeElement reports\n // the host \u2014 the root's own activeElement is the real one.\n const root = /** @type {Document | ShadowRoot} */ (\n entry.menu.getRootNode()\n );\n const index = items.indexOf(root.activeElement);\n let next;\n if (e.key === \"Home\") next = 0;\n else if (e.key === \"End\") next = items.length - 1;\n else if (index === -1)\n next = e.key === \"ArrowDown\" ? 0 : items.length - 1;\n else {\n const step = e.key === \"ArrowDown\" ? 1 : -1;\n next = (index + step + items.length) % items.length;\n }\n items[next].focus();\n }\n };\n document.addEventListener(\"keydown\", keyListener, true);\n};\n\nconst stopWatching = () => {\n if (!outsideListener) return;\n document.removeEventListener(\"mousedown\", outsideListener, true);\n outsideListener = null;\n document.removeEventListener(\"keydown\", keyListener, true);\n keyListener = 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 // Displayed before measuring: a menu still at display:none has no box to\n // measure, so its height would read as zero and it would never flip.\n placeMenu(button, menu);\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 // The title alone doesn't say \"cannot be undone\" \u2014 the message does,\n // and some AT won't announce it without describedby.\n messageEl.id = `hd-confirm-message-${Math.random().toString(36).slice(2, 9)}`;\n panel.setAttribute(\"aria-describedby\", messageEl.id);\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 MAX_SCREENSHOTS,\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\nexport const isMacPlatform = () =>\n /Mac|iPod|iPhone|iPad/.test(navigator.userAgent);\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 = isMacPlatform();\n const modifierMap = {\n alt: isMac ? \"\u2325\" : strings.modifierAlt,\n ctrl: isMac ? \"\u2318\" : strings.modifierCtrl,\n shift: isMac ? \"\u21E7\" : strings.modifierShift,\n };\n\n const modifier = modifierMap[options.shortcutModifier] || modifierMap.alt;\n const key = options.shortcutKey?.toUpperCase() || \"C\";\n\n return `${modifier} + ${key}`;\n};\n\n// Shared with the inbox and the context block \u2014 one caret, not three copies.\nexport const 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\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\n/**\n * Escapes a value for interpolation inside a double-quoted CSS attribute\n * selector. loadComments accepts arbitrary host ids, so a quote or backslash\n * in one would otherwise make every querySelector throw. Escaped by hand\n * because jsdom (where the whole test suite runs) does not implement\n * CSS.escape.\n * @param {string | number} value\n * @returns {string}\n */\nexport const cssAttrValue = (value) => String(value).replace(/[\\\\\"]/g, \"\\\\$&\");\n\n/**\n * Attribute selector for a comment's marker circle.\n * @param {string | number} id\n * @returns {string}\n */\nexport const circleSelector = (id) => `[data-comment-id=\"${cssAttrValue(id)}\"]`;\n\n/**\n * A comment's (or reply's) attached screenshots, tolerating the singular\n * `screenshot` field records persisted before the array existed still carry.\n * @param {{ screenshots?: string[], screenshot?: string }} entry\n * @returns {string[]}\n */\nexport const screenshotsOf = (entry) =>\n entry.screenshots || (entry.screenshot ? [entry.screenshot] : []);\n\n/**\n * The pending-attachment preview strip. One builder for the three surfaces\n * that show it \u2014 comment box, thread popover reply, inbox reply \u2014 which had\n * drifted apart once already (the popover copy lost its remove button's\n * aria-label and type).\n * @param {Element} container\n * @param {string[]} screenshots the pending array; remove splices it in place\n * @param {{ strings: typeof defaultStrings, onShow: (dataUrl: string) => void,\n * rerender: () => void }} deps\n */\nexport const renderScreenshotsPreview = (\n container,\n screenshots,\n { strings, onShow, rerender }\n) => {\n container.innerHTML = \"\";\n container.classList.toggle(CLASSES.ACTIVE, screenshots.length > 0);\n\n screenshots.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 = strings.attachedScreenshot;\n makeThumbnailOperable(img, () => onShow(dataUrl));\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = CLASSES.SCREENSHOT_REMOVE;\n removeBtn.setAttribute(\"aria-label\", strings.removeScreenshot);\n removeBtn.innerHTML = \"&times;\";\n removeBtn.onclick = (e) => {\n e.stopPropagation();\n screenshots.splice(i, 1);\n rerender();\n };\n\n item.appendChild(img);\n item.appendChild(removeBtn);\n container.appendChild(item);\n });\n};\n\n/**\n * Wires the hidden file input that feeds a pending-screenshots array,\n * enforcing MAX_SCREENSHOTS the same way on every attachment surface.\n * @param {HTMLInputElement} input\n * @param {() => string[]} getScreenshots\n * @param {() => void} rerender\n */\nexport const wireScreenshotInput = (input, getScreenshots, rerender) => {\n input.addEventListener(\"change\", (e) => {\n const file = /** @type {HTMLInputElement} */ (e.target).files[0];\n if (!file) return;\n // A non-image read into a data URL renders a broken <img> and bloats\n // the stored payload for nothing.\n if (file.type && !file.type.startsWith(\"image/\")) return;\n const screenshots = getScreenshots();\n if (screenshots.length >= MAX_SCREENSHOTS) return;\n\n const reader = new FileReader();\n reader.onload = (ev) => {\n screenshots.push(/** @type {string} */ (ev.target.result));\n rerender();\n };\n reader.readAsDataURL(file);\n input.value = \"\";\n });\n};\n\n/**\n * Every rendered screenshot thumbnail opens the lightbox the same way;\n * wired in one place so the five surfaces that render them cannot drift.\n * @param {ParentNode} root\n * @param {(src: string) => void} onShow\n */\nexport const wireScreenshotLightbox = (root, onShow) => {\n root\n .querySelectorAll(`.${CLASSES.SCREENSHOT_IMG}`)\n .forEach((/** @type {HTMLImageElement} */ img) => {\n makeThumbnailOperable(img, () => onShow(img.src));\n });\n};\n\n/**\n * A thumbnail that opens the lightbox is a control, not decoration: same\n * role=\"button\" + tabindex + Enter/Space pattern the marker circles use\n * (see DECISIONS.md, Accessibility). The img's alt is its accessible name.\n * @param {HTMLImageElement} img\n * @param {() => void} activate\n */\nconst makeThumbnailOperable = (img, activate) => {\n img.setAttribute(\"role\", \"button\");\n img.setAttribute(\"tabindex\", \"0\");\n img.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n activate();\n });\n img.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n activate();\n }\n });\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.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 = \"&times;\";\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 = screenshotsOf(comment);\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 = screenshotsOf(reply);\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 = \"&times;\";\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 = screenshotsOf(comment);\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", "// RF2 \u2014 the environment a comment was reported from, plus the automatic\n// capture taken at that moment.\n//\n// Two surfaces render it as a disclosure, differing only in where it starts:\n// the thread popover collapses it so the popover stays a conversation first\n// and a bug report second, while the inbox detail opens expanded because that\n// view is the one you go to in order to read everything.\n//\n// The caller owns the open/closed state \u2014 `expanded` in, `onToggle` out \u2014\n// because the inbox rebuilds its detail from scratch on every refresh, and a\n// block that remembered its own state would spring back open on the next\n// mutation.\n\nimport { CLASSES } from \"./constants.js\";\nimport { CARET_ICON_SVG, wireScreenshotLightbox } from \"./components.js\";\n\n/**\n * @param {any} comment\n * @param {{ strings: object, onShowLightbox: (src: string) => void,\n * collapsible?: boolean, expanded?: boolean,\n * onToggle?: (expanded: boolean) => void }} deps\n * @returns {HTMLElement | null} null for comments created before RF1/RF2\n */\nexport const createContextBlock = (\n comment,\n { strings, onShowLightbox, collapsible = false, expanded = false, onToggle }\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\", String(expanded));\n toggle.innerHTML = `<span>${strings.contextSection}</span>${CARET_ICON_SVG}`;\n body.style.display = expanded ? \"\" : \"none\";\n toggle.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const isOpen = toggle.getAttribute(\"aria-expanded\") === \"true\";\n toggle.setAttribute(\"aria-expanded\", String(!isOpen));\n body.style.display = isOpen ? \"none\" : \"\";\n onToggle?.(!isOpen);\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 body.appendChild(img);\n\n // Through the shared helper rather than a click listener of its own:\n // this thumbnail opens the lightbox like every other one, so it needs\n // the same role=\"button\" + tabindex + Enter/Space treatment. Wiring it\n // by hand here is exactly the drift the helper exists to prevent.\n wireScreenshotLightbox(body, onShowLightbox);\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", "// 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", "// The thread popover: lifecycle, in-place editing state, and the placement\n// math that keeps a floating panel pinned beside its marker.\n//\n// Extracted from CommentOverlay as part of splitting the god object\n// (DECISIONS.md, Fase 5). Pure view-controller: every data mutation flows\n// back through the `actions` contract, so this module never touches\n// storage, callbacks to the host app, or the markers themselves.\n\nimport { CLASSES, MARKER_SIZE } from \"./constants.js\";\nimport {\n createThreadPopover,\n createReplyElement,\n createEditedMark,\n cssAttrValue,\n renderScreenshotsPreview,\n wireScreenshotInput,\n wireScreenshotLightbox,\n} from \"./components.js\";\nimport { createCommentActions, copyToClipboard } from \"./comment-actions.js\";\nimport { createContextBlock } from \"./context-block.js\";\nimport { buildAgentContext } from \"./agent-context.js\";\nimport { buildCommentLink } from \"./link.js\";\nimport { createInlineEditor, confirmDiscard } from \"./inline-editor.js\";\nimport { sameId } from \"./id.js\";\n\n/**\n * Places a floating panel (tooltip or thread popover) beside a marker,\n * clamped to the viewport. Exported on its own because the hover tooltip\n * shares this exact placement without going through the controller.\n * @param {HTMLElement} el\n * @param {HTMLElement} circle\n */\nexport const 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 = MARKER_SIZE;\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/**\n * Centers a panel in the viewport \u2014 the placement for popovers with no\n * marker to pin to (orphaned comments opened from the inbox).\n * @param {HTMLElement} el\n */\nexport const 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\nexport class PopoverController {\n /**\n * @param {{\n * shadowRoot: ShadowRoot,\n * strings: Object,\n * locale: string,\n * findComment: (id: any) => any,\n * removeTooltip: (id: any) => void,\n * onShowLightbox: (src: string) => void,\n * isInsideLightbox: (target: any) => boolean,\n * linkParam: () => string,\n * refreshInbox: () => void,\n * actions: {\n * addReply: Function, deleteReply: Function,\n * editComment: Function, editReply: Function,\n * setStatus: Function, setType: Function, setPriority: Function,\n * deleteComment: Function,\n * },\n * }} deps\n */\n constructor(deps) {\n this.deps = deps;\n /** The open popover element, or null. @type {HTMLElement | null} */\n this.active = null;\n /**\n * The marker the popover follows on scroll. Null for orphaned comments\n * opened from the inbox \u2014 those get centered instead.\n * @type {HTMLElement | null}\n */\n this._activeCircle = null;\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.editing = null;\n /** @type {ResizeObserver | null} */\n this._resizeObserver = null;\n /** @type {((e: MouseEvent) => void) | null} */\n this._clickHandler = null;\n /**\n * Pending arm of `_clickHandler`. Held so `close()` can cancel it: the\n * listener goes on `document`, so a timer that outlives teardown installs\n * one nothing is left to remove.\n * @type {ReturnType<typeof setTimeout> | null}\n */\n this._armClickTimer = null;\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 _bodyEl(replyId = null) {\n const popover = this.active;\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=\"${cssAttrValue(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.active?.dataset.for !== String(id)) return;\n const comment = this.deps.findComment(id);\n if (!comment) return;\n\n const source =\n replyId == null\n ? comment\n : (comment.replies || []).find((r) => sameId(r.id, 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._bodyEl(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.active.querySelector(`.${CLASSES.THREAD_META}`)\n : this.active\n .querySelector(\n `.${CLASSES.THREAD_REPLY}[data-reply-id=\"${cssAttrValue(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.deps.strings,\n this.deps.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 at all. */\n isEditing() {\n return this.editing != null;\n }\n\n /** True while the popover holds an editor with unsaved text. */\n editorDirty() {\n if (!this.editing) return false;\n const { commentId, replyId, draft } = this.editing;\n const comment = this.deps.findComment(commentId);\n const source =\n replyId == null\n ? comment\n : (comment?.replies || []).find((r) => sameId(r.id, replyId));\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 releaseEditor() {\n if (!this.editing) return true;\n if (this.editorDirty()) {\n const host = /** @type {any} */ (this.deps.shadowRoot);\n if (!(await confirmDiscard(host, this.deps.strings))) return false;\n }\n const { replyId } = this.editing;\n this.editing = null;\n this._restoreBody(replyId);\n return true;\n }\n\n _restoreBody(replyId) {\n const comment = this.deps.findComment(this.active?.dataset.for);\n if (!comment) return;\n const source =\n replyId == null\n ? comment\n : (comment.replies || []).find((r) => sameId(r.id, 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._bodyEl(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 startEditing(commentId, replyId = null) {\n if (!(await this.releaseEditor())) return;\n\n const comment = this.deps.findComment(commentId);\n const source =\n replyId == null\n ? comment\n : (comment?.replies || []).find((r) => sameId(r.id, replyId));\n if (!source) return;\n\n this.editing = { commentId, replyId, draft: source.text };\n\n const editor = createInlineEditor({\n value: source.text,\n strings: this.deps.strings,\n onInput: (text) => {\n this.editing.draft = text;\n },\n onSave: (text) => {\n const saved =\n replyId == null\n ? this.deps.actions.editComment(commentId, text)\n : this.deps.actions.editReply(commentId, replyId, text);\n this.editing = null;\n if (saved) {\n this.refreshCommentViews(commentId, replyId);\n this.deps.refreshInbox();\n } else {\n this._restoreBody(replyId);\n }\n },\n onCancel: () => {\n this.releaseEditor();\n },\n });\n\n this._bodyEl(replyId)?.replaceWith(editor);\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 show(circle, comment) {\n this.close();\n\n const { strings, locale } = this.deps;\n this.deps.removeTooltip(comment.id);\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 this._activeCircle = circle || null;\n\n const onDeleteReply = (reply, replyEl) => {\n if (!this.deps.actions.deleteReply(comment.id, reply.id)) return;\n replyEl.remove();\n this.deps.refreshInbox();\n };\n\n // Named rather than inlined below: `submitReply` builds a reply row too,\n // and when this handler lived only in the call site there, the row the\n // user had just created came out with a \u22EF menu that could delete but not\n // edit \u2014 until the popover was reopened and the full render wired both.\n const onEditReply = (reply) => this.startEditing(comment.id, reply.id);\n\n const popover = createThreadPopover(comment, strings, locale, {\n onDeleteReply,\n onEditReply,\n });\n this.deps.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,\n onCopy: (c) =>\n copyToClipboard(\n buildAgentContext(c, {\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n strings,\n })\n ),\n onCopyLink: (c) =>\n copyToClipboard(buildCommentLink(c, this.deps.linkParam())),\n onEdit: (c) => this.startEditing(c.id),\n onSetStatus: (c, status) => this.deps.actions.setStatus(c.id, status),\n onSetType: (c, type) => this.deps.actions.setType(c.id, type),\n onSetPriority: (c, priority) =>\n this.deps.actions.setPriority(c.id, priority),\n onDelete: (c) => {\n this.close();\n this.deps.actions.deleteComment(c.id);\n this.deps.refreshInbox();\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 wireScreenshotLightbox(mainScreenshotsContainer, (src) =>\n this.deps.onShowLightbox(src)\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,\n onShowLightbox: (src) => this.deps.onShowLightbox(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 positionPopoverAtCircle(popover, circle);\n } else {\n 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.editing && !(await this.releaseEditor())) return;\n this.close();\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 renderScreenshotsPreview(\n threadScreenshotsContainer,\n pendingReplyScreenshots,\n {\n strings,\n onShow: (dataUrl) => this.deps.onShowLightbox(dataUrl),\n rerender: () => updateReplyScreenshotsPreview(),\n }\n );\n };\n\n threadAttachBtn.addEventListener(\"click\", () => {\n threadFileInput.click();\n });\n\n wireScreenshotInput(\n threadFileInput,\n () => pendingReplyScreenshots,\n updateReplyScreenshotsPreview\n );\n\n const submitReply = () => {\n const text = input.value.trim();\n if (!text && pendingReplyScreenshots.length === 0) return;\n\n const reply = this.deps.actions.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, strings, locale, {\n onDelete: onDeleteReply,\n onEdit: onEditReply,\n });\n repliesContainer.appendChild(replyEl);\n\n wireScreenshotLightbox(replyEl, (src) => this.deps.onShowLightbox(src));\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.active = 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._resizeObserver = new ResizeObserver(() => this.reposition());\n this._resizeObserver.observe(popover);\n }\n\n setTimeout(() => input.focus(), 50);\n\n // Deferred so the gesture that opened this popover cannot immediately\n // close it. Cancellable, because `close()` may run first.\n this._armClickTimer = setTimeout(() => {\n this._armClickTimer = null;\n this._clickHandler = (e) => {\n const target = /** @type {Node} */ (e.composedPath()[0] || e.target);\n if (\n !popover.contains(target) &&\n !circle?.contains(target) &&\n !this.deps.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.editorDirty()) return;\n this.close();\n }\n };\n document.addEventListener(\"mousedown\", this._clickHandler);\n }, 0);\n }\n\n close() {\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 this.deps.shadowRoot\n ?.querySelectorAll(`.${CLASSES.CIRCLE_ACTIVE}`)\n .forEach((/** @type {HTMLElement} */ el) =>\n el.classList.remove(CLASSES.CIRCLE_ACTIVE)\n );\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n // Every route here has already answered for the draft, and the DOM it\n // lived in is about to go.\n this.editing = null;\n if (this.active) {\n this.active.remove();\n this.active = null;\n }\n this._activeCircle = null;\n if (this._armClickTimer) {\n clearTimeout(this._armClickTimer);\n this._armClickTimer = null;\n }\n if (this._clickHandler) {\n document.removeEventListener(\"mousedown\", this._clickHandler);\n this._clickHandler = null;\n }\n }\n\n /**\n * Re-runs the open popover's placement against its current size. Split\n * from syncToMarker because that one also decides visibility from the\n * marker, which is wrong here: a popover resizing while its marker is\n * off-screen must stay hidden, not reappear.\n */\n reposition() {\n const popover = this.active;\n if (!popover || popover.style.display === \"none\") return;\n\n if (this._activeCircle) {\n positionPopoverAtCircle(popover, this._activeCircle);\n } else {\n centerPopover(popover);\n }\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 syncToMarker() {\n const popover = this.active;\n const circle = this._activeCircle;\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 positionPopoverAtCircle(popover, circle);\n }\n}\n", "// The marker engine: circle rendering, position math, occlusion hit-testing,\n// the batched rAF update loop, and every observer/listener that feeds it.\n//\n// Extracted from CommentOverlay as part of splitting the god object\n// (DECISIONS.md, Fase 5). The engine owns WHERE markers are and WHETHER they\n// are visible; what a marker click or hover opens (tooltip, thread popover)\n// belongs to the overlay and comes in through `wireMarker`, so this module\n// never learns about panels, storage or callbacks to the host app.\n\nimport { MARKER_SIZE } from \"./constants.js\";\nimport { createCommentCircle } from \"./components.js\";\nimport { TAG_NAME } from \"./root-element.js\";\n\n// How long a batched position pass may reuse the previous occlusion verdict\n// before hit-testing again. Scrolling schedules a pass per frame; occlusion\n// rarely changes mid-scroll, and the trailing pass settles the end state.\nconst OCCLUSION_INTERVAL_MS = 150;\n\n/**\n * Keeps a marker's point inside its container's box.\n *\n * Clamped to the box itself, not to `box - MARKER_SIZE`: reserving room for\n * the whole marker moved the point the user clicked whenever the container\n * was shorter or narrower than the marker (a 36px navbar row pulled every\n * marker up to 8px from its top). Only the marker's tip carries meaning, so\n * the point stays put and the marker's body overhangs instead.\n *\n * The one place this math lives \u2014 `updatePosition` and `scrollMarkerIntoView`\n * both derive from it, and they must never disagree about where a marker is.\n *\n * @param {number} offset position along one axis, in px from the box's edge\n * @param {number} size the box's length along that axis\n */\nconst clampToBox = (offset, size) => Math.max(0, Math.min(offset, size));\n\nexport class MarkerEngine {\n /**\n * @param {{\n * container: HTMLElement,\n * strings: Object,\n * getComments: () => any[],\n * wireMarker: (circle: HTMLElement, comment: any) => void,\n * onMarkerHidden: (comment: any) => void,\n * onVisibilityFlip: () => void,\n * onAfterPass: () => void,\n * }} deps `container` is the overlay element the circles mount into;\n * `wireMarker` is where the overlay attaches its tooltip/popover\n * handlers; `onMarkerHidden` dismisses UI floating over a marker that\n * just went away; `onAfterPass` runs after every rAF pass (the thread\n * popover follows its marker there).\n */\n constructor(deps) {\n this.deps = deps;\n\n /**\n * Marker circles by String(comment.id). The per-frame position loop\n * used to querySelector each one \u2014 a full shadow-tree scan per comment\n * per frame, O(n\u00B2) on scroll.\n * @type {Map<string, HTMLElement>}\n */\n this.circles = new Map();\n /** @type {Map<string, { circle: HTMLElement, observer: any, container: HTMLElement }>} */\n this.resizeObservers = new Map();\n /** Position validation gate \u2014 off means passes only sync the popover. */\n this.enabled = true;\n\n // Occlusion hit-testing (elementsFromPoint + getComputedStyle per\n // marker) is the expensive part of a position pass, and scrolling is\n // when passes are hottest \u2014 so batched passes run it at most once per\n // OCCLUSION_INTERVAL_MS, with a trailing pass to settle the final state.\n this._lastOcclusionPass = 0;\n this._occlusionTrailingTimer = null;\n\n // rAF scheduling flag for bulk updates\n this._pendingRaf = null;\n\n this._globalMutationObserver = null;\n this._resizeHandler = null;\n this._scrollHandler = null;\n this._loadHandler = null;\n }\n\n /** Attaches the window listeners and the page-wide mutation observer. */\n start() {\n this._resizeHandler = () => this.scheduleUpdate();\n window.addEventListener(\"resize\", this._resizeHandler, { passive: true });\n\n // Capture scroll on any scrolling ancestor\n this._scrollHandler = () => this.scheduleUpdate();\n window.addEventListener(\"scroll\", this._scrollHandler, {\n capture: true,\n passive: true,\n });\n\n // Update after resources load (images, fonts)\n this._loadHandler = () => this.scheduleUpdate();\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.scheduleUpdate();\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 /** Creates, mounts, positions and observes one comment's marker. */\n render(comment) {\n const circle = createCommentCircle(comment, this.deps.strings);\n this.deps.wireMarker(circle, comment);\n\n this.deps.container.appendChild(circle);\n this.circles.set(String(comment.id), circle);\n this.updatePosition(comment, circle);\n\n // Size changes of the container are watched per comment (ResizeObserver\n // below); DOM mutations are watched once for the whole page by the\n // global observer in start() \u2014 a per-comment MutationObserver here\n // would fire N redundant callbacks per mutation batch on top of it.\n this.createResizeObserver(comment, circle);\n }\n\n /** The marker circle for a comment id, however the caller spells it. */\n circleOf(id) {\n return this.circles.get(String(id)) ?? null;\n }\n\n /** Removes one comment's marker and its observer. */\n remove(id) {\n this.cleanupResizeObserver(id);\n this.circles.get(String(id))?.remove();\n this.circles.delete(String(id));\n }\n\n /** Removes every marker and observer at once (bulk reset). */\n clear() {\n this.resizeObservers.forEach(({ observer }) => observer?.disconnect());\n this.resizeObservers.clear();\n this.circles.forEach((circle) => circle.remove());\n this.circles.clear();\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 validatedX = clampToBox(absoluteX, containerWidth);\n const validatedY = clampToBox(absoluteY, containerHeight);\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 * 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 /**\n * Read half of a position update: layout reads only, no DOM writes, so a\n * batched pass can measure every marker before touching any style (an\n * interleaved read-write loop forces a fresh layout per marker).\n * @param {Object} comment\n * @param {HTMLElement} circle\n * @param {{ checkOcclusion?: boolean }} [options] when false, the pass\n * reuses the marker's previous occlusion verdict instead of hit-testing.\n * @returns {{ kind: \"resolved\" } | { kind: \"noop\" } | { kind: \"hidden\" }\n * | { kind: \"visible\", viewportX: number, viewportY: number,\n * relativeX: number, relativeY: number }}\n */\n _computeMarkerState(comment, circle, { checkOcclusion = true } = {}) {\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 return { kind: \"resolved\" };\n }\n\n let positionData = this.validateAndCalculatePosition(comment, circle);\n if (positionData && !this._isAnchorTargetVisible(comment)) {\n positionData = null;\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 return comment.container ? { kind: \"hidden\" } : { kind: \"noop\" };\n }\n\n // Offset so the circle's top-left tip (sharp corner) aligns with the stored position\n const circleRadius = MARKER_SIZE / 2;\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 (checkOcclusion) {\n comment._occluded = this._isMarkerOccluded(comment, viewportX, viewportY);\n }\n if (comment._occluded) {\n return { kind: \"hidden\" };\n }\n\n return {\n kind: \"visible\",\n viewportX,\n viewportY,\n relativeX: positionData.relativeX,\n relativeY: positionData.relativeY,\n };\n }\n\n /**\n * Write half of a position update: styles and state only, no layout\n * reads.\n * @param {Object} comment\n * @param {HTMLElement} circle\n * @param {ReturnType<MarkerEngine[\"_computeMarkerState\"]>} state\n * @returns {boolean} true when the marker's hidden flag flipped \u2014 the\n * caller decides how to refresh the inbox (once per batch in the rAF\n * loop, immediately on direct calls).\n */\n _applyMarkerState(comment, circle, state) {\n if (state.kind === \"resolved\") {\n if (circle) circle.style.display = \"none\";\n return false;\n }\n if (state.kind === \"noop\") return false;\n\n const wasHidden = comment.hidden === true;\n if (state.kind === \"hidden\") {\n comment.hidden = true;\n circle.style.display = \"none\";\n // A marker that just went away must not leave its hover tooltip or\n // its open thread popover floating on the page.\n this.deps.onMarkerHidden(comment);\n return !wasHidden;\n }\n\n comment.hidden = false;\n circle.style.display = \"\";\n circle.style.left = `${state.viewportX}px`;\n circle.style.top = `${state.viewportY}px`;\n circle.style.transform = \"translate(-50%, -50%)\";\n circle.style.position = \"absolute\";\n\n comment.relativeX = state.relativeX;\n comment.relativeY = state.relativeY;\n return wasHidden;\n }\n\n /** One marker's position, refreshed now. */\n updatePosition(comment, circle = this.circles.get(String(comment.id))) {\n if (!circle) return;\n const state = this._computeMarkerState(comment, circle);\n const flipped = this._applyMarkerState(comment, circle, state);\n if (flipped) this.deps.onVisibilityFlip();\n }\n\n /**\n * One batched pass over every marker: measure everything, then write\n * everything, then refresh the inbox at most once \u2014 flipping N markers in\n * one frame used to rebuild the inbox N times from inside the loop.\n */\n _updateAllPositions() {\n const now = Date.now();\n const checkOcclusion =\n now - this._lastOcclusionPass >= OCCLUSION_INTERVAL_MS;\n if (checkOcclusion) {\n this._lastOcclusionPass = now;\n } else {\n // This pass reuses stale occlusion verdicts; make sure one more pass\n // runs after the burst settles so the end state is honest.\n this._armOcclusionTrailingPass();\n }\n\n const plans = [];\n for (const comment of this.deps.getComments()) {\n const circle = this.circles.get(String(comment.id));\n if (!circle) continue;\n plans.push([\n comment,\n circle,\n this._computeMarkerState(comment, circle, { checkOcclusion }),\n ]);\n }\n\n let anyFlipped = false;\n for (const [comment, circle, state] of plans) {\n if (this._applyMarkerState(comment, circle, state)) anyFlipped = true;\n }\n if (anyFlipped) this.deps.onVisibilityFlip();\n }\n\n _armOcclusionTrailingPass() {\n if (this._occlusionTrailingTimer) {\n clearTimeout(this._occlusionTrailingTimer);\n }\n this._occlusionTrailingTimer = setTimeout(() => {\n this._occlusionTrailingTimer = null;\n this.scheduleUpdate();\n }, OCCLUSION_INTERVAL_MS);\n }\n\n /** Coalesces any number of triggers into one rAF pass. */\n scheduleUpdate() {\n if (this._pendingRaf) return;\n this._pendingRaf = requestAnimationFrame(() => {\n this._pendingRaf = null;\n if (this.enabled) {\n this._updateAllPositions();\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.deps.onAfterPass();\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.enabled) 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.updatePosition(comment, circle);\n }\n }\n });\n\n // Start observing the container\n observer.observe(comment.container);\n\n // Store the observer for cleanup, keyed by String(id) like circles.\n this.resizeObservers.set(String(comment.id), {\n circle,\n observer,\n container: comment.container,\n });\n }\n\n cleanupResizeObserver(commentId) {\n // Keyed by String(id), exactly like the circles map: the caller may\n // hold the other spelling of a legacy numeric id, and a missed lookup\n // here leaks a live observer pointed at a detached circle.\n const key = String(commentId);\n if (this.resizeObservers.has(key)) {\n const { circle, observer } = this.resizeObservers.get(key);\n if (observer) {\n observer.disconnect();\n }\n if (circle && circle.parentNode) {\n circle.parentNode.removeChild(circle);\n }\n this.resizeObservers.delete(key);\n }\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 `updatePosition` derives it \u2014 through the same `clampToBox`, so\n * the two cannot disagree about where a marker sits.\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 offsetY = clampToBox(comment.relativeY * rect.height, rect.height);\n return rect.top + offsetY + MARKER_SIZE / 2;\n }\n\n /** Cancels every scheduled pass, listener and observer. */\n destroy() {\n // A pass scheduled before teardown must not run against a destroyed\n // widget \u2014 cancel the pending frame and the trailing occlusion timer.\n if (this._pendingRaf) {\n cancelAnimationFrame(this._pendingRaf);\n this._pendingRaf = null;\n }\n if (this._occlusionTrailingTimer) {\n clearTimeout(this._occlusionTrailingTimer);\n this._occlusionTrailingTimer = null;\n }\n if (this._resizeHandler) {\n window.removeEventListener(\"resize\", this._resizeHandler);\n this._resizeHandler = null;\n }\n if (this._scrollHandler) {\n window.removeEventListener(\"scroll\", this._scrollHandler, {\n capture: true,\n });\n this._scrollHandler = null;\n }\n if (this._loadHandler) {\n window.removeEventListener(\"load\", this._loadHandler);\n this._loadHandler = null;\n }\n if (this._globalMutationObserver) {\n this._globalMutationObserver.disconnect();\n this._globalMutationObserver = null;\n }\n this.clear();\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 { 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 CARET_ICON_SVG,\n circleSelector,\n createMetaElement,\n renderScreenshotsPreview,\n wireScreenshotInput,\n wireScreenshotLightbox,\n createScreenshotsDisplay,\n createInputArea,\n createReplyElement,\n createBadgeRow,\n getShortcutText,\n} from \"./components.js\";\nimport { sameId } from \"./id.js\";\nimport { createInlineEditor, confirmDiscard } from \"./inline-editor.js\";\nimport { buildCommentLink } from \"./link.js\";\n\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 * Whether the detail view's context disclosure is open. State rather than\n * DOM for the same reason as the editor below \u2014 the detail is rebuilt on\n * every refresh \u2014 and one flag for the panel rather than one per comment,\n * so folding it away stays folded while stepping through comments with\n * prev/next.\n */\n this.contextExpanded = true;\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 /**\n * Keyed card cache: String(id) \u2192 the live comment object, the\n * fingerprint of what its card renders, and the card node itself. A\n * refresh reuses a node whose comment and fingerprint both still match\n * \u2014 which is what keeps thumbnails decoded and the list's scroll\n * position intact across the many refreshes this panel receives.\n * @type {Map<string, { comment: any, fingerprint: string, card: HTMLElement }>}\n */\n this._cardBindings = new Map();\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 // Announced as a dialog, so keyboard focus has to arrive with it \u2014\n // otherwise the user tabs across the whole page to reach the panel.\n this.el.setAttribute(\"tabindex\", \"-1\");\n this.shadowRoot.appendChild(this.el);\n this.render();\n this.el.focus();\n }\n\n close() {\n this._clearHighlight();\n this._setActiveMarker(null);\n this.el?.remove();\n this.el = null;\n this._cardBindings.clear();\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 /** Back to the default view: current page, no status/type/priority. */\n _resetFilters() {\n this.pageFilter = \"page\";\n this.statusFilter = \"all\";\n this.typeFilter = \"all\";\n this.priorityFilter = \"all\";\n 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((c) =>\n sameId(c.id, this.editing.commentId)\n );\n if (!comment) return \"\";\n if (this.editing.replyId == null) return comment.text || \"\";\n const reply = (comment.replies || []).find((r) =>\n sameId(r.id, 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(circleSelector(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 const comments = this.filteredComments();\n const detail =\n this.detailId != null\n ? comments.find((comment) => sameId(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 // The detail shows one comment: a full rebuild is cheap and keeps the\n // editing and reply wiring simple. Leaving the list view drops its\n // keyed state; the skeleton is rebuilt on the way back.\n this._cardBindings.clear();\n this.el.innerHTML = \"\";\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 {import('./index.d.ts').CommentId} id\n */\n openDetail(id) {\n if (!this.el) this.open();\n const comment = this.getComments().find((c) => sameId(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 = \"&times;\";\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 // Persistent skeleton: the header and the scrolling list are built once\n // and survive every refresh. Replacing the list wholesale (the old\n // innerHTML = \"\" render) reset its scroll position and re-decoded every\n // thumbnail whenever anything anywhere changed.\n let list = [...this.el.children].find((el) =>\n el.classList.contains(CLASSES.INBOX_LIST)\n );\n if (!list) {\n this.el.innerHTML = \"\";\n this._cardBindings.clear();\n const header = document.createElement(\"div\");\n header.className = CLASSES.INBOX_HEADER;\n this.el.appendChild(header);\n list = document.createElement(\"div\");\n list.className = CLASSES.INBOX_LIST;\n this.el.appendChild(list);\n }\n\n // The header is label-driven (the filter summary changes with every\n // selection) and holds no scroll or image state \u2014 rebuilt each pass.\n const header = [...this.el.children].find((el) =>\n el.classList.contains(CLASSES.INBOX_HEADER)\n );\n header.replaceChildren(this._buildFilter(), this._closeButton());\n\n this._reconcileCards(list, comments);\n }\n\n /**\n * Everything a list card renders, captured as a comparable string. An\n * equal fingerprint for the same live comment object means the existing\n * node can be reused as-is \u2014 listeners, decoded thumbnails and all. The\n * object identity check matters because loadComments REPLACES comment\n * objects: a reused card whose closures held the stale object would\n * mutate a comment the overlay no longer owns.\n */\n _cardFingerprint(comment) {\n return JSON.stringify([\n comment.text,\n comment.editedAt ?? null,\n comment.status ?? \"open\",\n comment.type ?? null,\n comment.priority ?? null,\n comment.tags ?? [],\n comment.resolvedAt ?? null,\n comment.anchorState,\n comment.hidden === true,\n comment.page,\n comment.screenshots?.length ?? 0,\n ]);\n }\n\n _reconcileCards(list, comments) {\n // The notice and the empty state are stateless one-offs \u2014 always\n // rebuilt, and removed up front so they never count as \"out of place\"\n // cards during the ordering walk below.\n for (const el of [...list.children]) {\n if (\n el.classList.contains(CLASSES.INBOX_NOTICE) ||\n el.classList.contains(CLASSES.INBOX_EMPTY)\n ) {\n el.remove();\n }\n }\n\n const desired = [];\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 desired.push(notice);\n }\n\n const seen = new Set();\n if (comments.length === 0) {\n desired.push(this._buildEmptyState());\n } else {\n for (const comment of comments) {\n const key = String(comment.id);\n const fingerprint = this._cardFingerprint(comment);\n const binding = this._cardBindings.get(key);\n let card;\n if (\n binding &&\n binding.comment === comment &&\n binding.fingerprint === fingerprint\n ) {\n card = binding.card;\n } else {\n card = this._buildCard(comment, { interactive: true });\n this._cardBindings.set(key, { comment, fingerprint, card });\n }\n seen.add(key);\n desired.push(card);\n }\n }\n\n for (const key of [...this._cardBindings.keys()]) {\n if (!seen.has(key)) this._cardBindings.delete(key);\n }\n\n // Minimal-move ordering: only nodes that are out of place are touched,\n // so an untouched tail keeps its position \u2014 and the container, which is\n // never replaced, keeps its scroll.\n desired.forEach((node, index) => {\n if (list.children[index] !== node) {\n list.insertBefore(node, list.children[index] ?? null);\n }\n });\n while (list.children.length > desired.length) {\n list.lastElementChild.remove();\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._resetFilters();\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 // The page chips are role=\"radio\" (exactly one active) and radios must\n // sit in a radiogroup; the toggling groups are switches, plain group.\n chips.setAttribute(\"role\", toggles ? \"group\" : \"radiogroup\");\n chips.setAttribute(\"aria-label\", title);\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._resetFilters();\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 wireScreenshotLightbox(shots, (src) =>\n this.callbacks.onShowLightbox(src)\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 != null && sameId(this.detailId, c.id)) {\n this.detailId = null;\n }\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 // Open on arrival \u2014 the detail view is where you go to read everything \u2014\n // but foldable, and the choice outlives the rebuilds this view does on\n // every refresh.\n const context = createContextBlock(comment, {\n strings: this.strings,\n onShowLightbox: (src) => this.callbacks.onShowLightbox(src),\n collapsible: true,\n expanded: this.contextExpanded,\n onToggle: (expanded) => {\n this.contextExpanded = expanded;\n },\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 wireScreenshotLightbox(replyEl, (src) =>\n this.callbacks.onShowLightbox(src)\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 renderScreenshotsPreview(screenshotsContainer, pendingScreenshots, {\n strings: this.strings,\n onShow: (dataUrl) => this.callbacks.onShowLightbox(dataUrl),\n rerender: () => updatePreview(),\n });\n };\n\n attachBtn.addEventListener(\"click\", () => fileInput.click());\n wireScreenshotInput(fileInput, () => pendingScreenshots, updatePreview);\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 { CaptureFlow } from \"./capture-flow.js\";\nimport { captureContext } from \"./metadata.js\";\nimport {\n CLASSES,\n IDS,\n SELECTORS,\n STATUSES,\n COMMENT_TYPES,\n PRIORITIES,\n MARKER_SIZE,\n MAX_SCREENSHOTS,\n} from \"./constants.js\";\nimport { getStyles, getGlobalStyles } from \"./styles.js\";\nimport { mountStyles } from \"./style-mount.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 STORAGE_KEY,\n PENDING_DETAIL_KEY,\n} from \"./storage.js\";\nimport { createId, sameId } from \"./id.js\";\nimport {\n buildCommentLink,\n readCommentLinkParam,\n DEFAULT_LINK_PARAM,\n} from \"./link.js\";\nimport {\n createToolbar,\n cssAttrValue,\n isMacPlatform,\n renderScreenshotsPreview,\n wireScreenshotInput,\n wireScreenshotLightbox,\n createCommentBox,\n createTooltip,\n} from \"./components.js\";\nimport {\n PopoverController,\n positionPopoverAtCircle,\n} from \"./popover-controller.js\";\nimport { MarkerEngine } from \"./marker-engine.js\";\nimport { InboxView } from \"./inbox.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\n// Screenshots are data-URLs rendered straight into <img src>; anything else\n// in a persisted array is a silently broken thumbnail waiting to happen.\nconst onlyStrings = (values) => values.filter((v) => typeof v === \"string\");\n\n// Every change the host can hear about, as `type` \u2192 the specific callback\n// that has always carried it. One table so a new event cannot be added to\n// the stream while forgetting the callback (or the other way round), and so\n// the two can never disagree about when they fire.\nconst CHANGE_CALLBACKS = {\n \"comment:created\": \"onCommentCreated\",\n \"comment:edited\": \"onCommentEdited\",\n \"comment:deleted\": \"onCommentDeleted\",\n \"comment:status-changed\": \"onCommentStatusChanged\",\n \"comment:updated\": \"onCommentUpdated\",\n \"comment:anchor-lost\": \"onAnchorLost\",\n \"reply:added\": \"onReplyAdded\",\n \"reply:deleted\": \"onReplyDeleted\",\n \"reply:edited\": \"onReplyEdited\",\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 = isMacPlatform();\n this.options = {\n shortcutKey: options.shortcutKey || (this.isMac ? \"c\" : \"C\"),\n shortcutModifier: options.shortcutModifier || \"alt\",\n autoScreenshot: options.autoScreenshot !== false,\n embedCrossOriginFonts: options.embedCrossOriginFonts === true,\n ...options,\n };\n this.locale = this.options.locale || detectLocale();\n this.strings = getStrings(this.locale);\n\n /**\n * Marker positioning, occlusion and observers (see marker-engine.js).\n * Created in initOverlay \u2014 its circles mount into the overlay element.\n * @type {MarkerEngine | null}\n */\n this.markers = null;\n /**\n * Drag selection + screenshot orchestration (see capture-flow.js).\n * Created in initOverlay \u2014 it mounts the selection rect into the\n * shadow root. @type {CaptureFlow | null}\n */\n this._captureFlow = null;\n\n /**\n * Parsed cross-page corpus, so every mutation does not pay a full\n * getItem + JSON.parse (megabytes once screenshots accumulate). Kept in\n * step with what this instance writes; dropped when another tab writes\n * (the `storage` listener in initOverlay).\n * @type {import('./index.d.ts').SerializedComment[] | null}\n */\n this._storedCache = null;\n\n /**\n * Thread-popover lifecycle and editing state (see\n * popover-controller.js). Created in initOverlay \u2014 it mounts into the\n * shadow root. @type {PopoverController | null}\n */\n this._popover = 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 // Kept on the instance so cleanup() can cancel it \u2014 an instance\n // destroyed while the document is still loading must not mount a\n // zombie UI when DOMContentLoaded fires.\n this._onDomReady = () => this.initOverlay();\n document.addEventListener(\"DOMContentLoaded\", this._onDomReady);\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 this._captureFlow = new CaptureFlow({\n host: this.shadowRoot,\n autoScreenshot: this.options.autoScreenshot,\n embedCrossOriginFonts: this.options.embedCrossOriginFonts,\n // The pending-attachments array stays here, next to the comment box\n // that previews it \u2014 the flow only reports what a drag captured.\n onRegionCaptured: (dataUrl) => {\n if (!this._pendingScreenshots) this._pendingScreenshots = [];\n if (this._pendingScreenshots.length < MAX_SCREENSHOTS) {\n this._pendingScreenshots.push(dataUrl);\n }\n },\n onPlace: (x, y) => this._placeCommentAtPoint(x, y),\n });\n\n this._popover = new PopoverController({\n shadowRoot: this.shadowRoot,\n strings: this.strings,\n locale: this.locale,\n findComment: (id) => this._findComment(id),\n removeTooltip: (id) => this._tooltipEl(id)?.remove(),\n onShowLightbox: (src) => this.showLightbox(src),\n isInsideLightbox: (target) => this._isInsideLightbox(target),\n linkParam: () => this._linkParam(),\n refreshInbox: () => {\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n },\n actions: {\n addReply: (comment, text, screenshots) =>\n this.addReply(comment, text, screenshots),\n deleteReply: (commentId, replyId) =>\n this.deleteReply(commentId, replyId),\n editComment: (id, text) => this.editComment(id, text),\n editReply: (commentId, replyId, text) =>\n this.editReply(commentId, replyId, text),\n setStatus: (id, status) => this.setCommentStatus(id, status),\n setType: (id, type) => this.setCommentType(id, type),\n setPriority: (id, priority) => this.setCommentPriority(id, priority),\n deleteComment: (id) => this.deleteComment(id),\n },\n });\n\n this.markers = new MarkerEngine({\n container: this.overlay,\n strings: this.strings,\n getComments: () => this.comments,\n wireMarker: (circle, comment) => this._wireMarker(circle, comment),\n onMarkerHidden: (comment) => this._dismissMarkerUi(comment),\n onVisibilityFlip: () => {\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n },\n // Runs after every rAF pass: the markers are placed in viewport\n // coordinates, so the open thread popover has to follow.\n onAfterPass: () => this.syncThreadPopoverToMarker(),\n });\n this.markers.start();\n\n // Bind event listeners\n this.bindEventListeners();\n this.setupKeyboardShortcut();\n this.injectStyles();\n\n this._pendingDetailId = this._readPendingDetailId();\n\n if (this.options.persistence === \"localStorage\") {\n this._storedCache = readStoredComments();\n this.loadComments(this._storedCache);\n // Another tab writing the key makes this instance's parsed copy\n // stale \u2014 drop it so the next sync re-reads before merging, instead\n // of clobbering what the other tab persisted.\n this._storageHandler = (e) => {\n if (e.key === STORAGE_KEY || e.key === null) this._storedCache = null;\n };\n window.addEventListener(\"storage\", this._storageHandler);\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 // Opt-in, never default: popstate only covers back/forward, and MPA\n // hosts should not inherit listeners for navigations they don't do.\n // pushState routing still needs an explicit notifyNavigation() call.\n if (this.options.autoDetectNavigation) {\n this._popstateHandler = () => this.notifyNavigation();\n window.addEventListener(\"popstate\", this._popstateHandler);\n }\n }\n\n _navigateTo(url) {\n // A host router can take over (SPA): a full-page load throws away the\n // app's state just to show another route it could render itself.\n if (typeof this.options.navigate === \"function\") {\n this.options.navigate(url);\n return;\n }\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._findComment(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 one place a change leaves the widget. Fires the specific callback\n * that has always carried this event and then the onChange stream, so a\n * host can subscribe either way \u2014 or both \u2014 and never sees the two\n * disagree about what happened or when.\n *\n * Host handlers are isolated: a subscriber that throws must not roll back\n * a mutation that already happened, and must not stop its sibling from\n * hearing about it either.\n *\n * @param {keyof typeof CHANGE_CALLBACKS} type\n * @param {any[]} callbackArgs arguments for the specific callback, in the\n * order it has always taken them\n * @param {Object} payload the event's own fields, minus `type`\n */\n _emit(type, callbackArgs, payload) {\n const name = CHANGE_CALLBACKS[type];\n const callback = this.options[name];\n if (typeof callback === \"function\") {\n try {\n callback(...callbackArgs);\n } catch (err) {\n console.warn(`HellDots: ${name} handler threw`, err);\n }\n }\n if (typeof this.options.onChange === \"function\") {\n try {\n this.options.onChange({ type, ...payload });\n } catch (err) {\n console.warn(\"HellDots: onChange handler threw\", err);\n }\n }\n }\n\n /** The shareable URL for a comment, as \"Copy link\" builds it. */\n commentLink(id) {\n const comment = this._findComment(id);\n return comment ? buildCommentLink(comment, this._linkParam()) : null;\n }\n\n /** The parsed corpus, re-read only after another tab invalidated it. */\n _readStoredCached() {\n if (!this._storedCache) this._storedCache = readStoredComments();\n return this._storedCache;\n }\n\n _syncStorage() {\n if (this.options.persistence !== \"localStorage\") return;\n const merged = mergeForStorage(\n this._readStoredCached(),\n this.serializeComments(),\n location.pathname\n );\n writeStoredComments(merged);\n // The merge IS the new stored state (quota shedding only nulls\n // contextScreenshot in the written copy, which the next merge would\n // reattempt from memory anyway \u2014 same as before the cache existed).\n this._storedCache = merged;\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 wireScreenshotInput(\n this.attachImageInput,\n () => {\n if (!this._pendingScreenshots) this._pendingScreenshots = [];\n return this._pendingScreenshots;\n },\n () => this._updateScreenshotsPreview()\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._popover.isEditing()) this._popover.releaseEditor();\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 // One matcher for default and custom chords alike \u2014 the old hardcoded\n // Alt+C fallbacks fired unconditionally, so a host that configured its\n // own shortcut got Alt+C on top of it with no way to turn it off.\n const key = this.options.shortcutKey.toLowerCase();\n const keyMatches =\n e.key.toLowerCase() === key ||\n // Option+letter on macOS (and AltGr layouts) types a dead or special\n // character (\"\u00E7\" for Option+C, \"\u02DA\" for Option+K), so e.key never\n // spells the configured letter there. e.code names the physical key\n // and is what makes Alt chords matchable at all.\n (e.altKey &&\n /^[a-z]$/.test(key) &&\n e.code === `Key${key.toUpperCase()}`);\n const modifierMatches =\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 (keyMatches && modifierMatches) {\n e.preventDefault();\n e.stopPropagation();\n this.toggleCommentMode();\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._captureFlow.beginDrag(e);\n }\n\n async _placeCommentAtPoint(clientX, clientY) {\n // The no-drag path has no render yet \u2014 kick the background capture off\n // now so it resolves while the user types (see capture-flow.js).\n this._captureFlow.armClickCapture();\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 renderScreenshotsPreview(container, this._pendingScreenshots || [], {\n strings: this.strings,\n onShow: (dataUrl) => this.showLightbox(dataUrl),\n rerender: () => this._updateScreenshotsPreview(),\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 = MARKER_SIZE;\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._captureFlow?.clearPending();\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 async saveComment() {\n // Two Enters while the capture resolves must not save twice.\n if (this._saving) return;\n if (!this.commentInput.value.trim() || !this.currentPosition) return;\n this._saving = true;\n try {\n await this._saveCommentNow();\n } finally {\n this._saving = false;\n }\n }\n\n async _saveCommentNow() {\n // The capture kicked off when the box opened; by save time it has\n // usually resolved and this await costs nothing.\n const contextScreenshot = await this._captureFlow.consumePending();\n // The box may have been dismissed (Escape) while awaiting \u2014 a save that\n // lands after that would contradict what the user sees on screen.\n if (!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,\n };\n\n this.comments.push(comment);\n this._syncStorage();\n const created = this._serializeComment(comment);\n this._emit(\"comment:created\", [created], { comment: created });\n this.renderCommentCircle(comment);\n this.hideCommentBox();\n this.toggleCommentMode();\n\n const circle = this._circles.get(String(comment.id));\n if (circle) {\n this.showThreadPopover(circle, comment);\n }\n }\n\n renderCommentCircle(comment) {\n this.markers.render(comment);\n }\n\n /**\n * What a marker opens when interacted with \u2014 tooltip on hover, thread\n * popover on activation. UI wiring only; the engine calls this once per\n * circle it creates and owns everything about position and visibility.\n */\n _wireMarker(circle, comment) {\n circle.addEventListener(\"mouseenter\", () =>\n this.showCommentTooltip(circle, comment)\n );\n circle.addEventListener(\"mouseleave\", () => {\n setTimeout(() => {\n const tooltip = this._tooltipEl(comment.id);\n if (tooltip && !tooltip.matches(\":hover\")) {\n tooltip.remove();\n }\n }, 250);\n });\n\n circle.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this._tooltipEl(comment.id)?.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\n showCommentTooltip(circle, comment) {\n const existingPopover = this.shadowRoot.querySelector(\n `.${CLASSES.THREAD_POPOVER}[data-for=\"${cssAttrValue(comment.id)}\"]`\n );\n if (existingPopover) return;\n\n if (this._tooltipEl(comment.id)) return;\n\n const tooltip = createTooltip(comment, this.strings, this.locale);\n this.shadowRoot.appendChild(tooltip);\n\n wireScreenshotLightbox(tooltip, (src) => this.showLightbox(src));\n\n setTimeout(() => {\n 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._popover.refreshCommentViews(id);\n },\n onEditReply: (commentId, replyId, text) => {\n if (!this.editReply(commentId, replyId, text)) return;\n this._popover.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 // Both fields hold one thing, so whatever is already in them has to go\n // first: re-opening an open inbox (notifyNavigation re-reads the deep\n // link on every route change) otherwise orphaned the previous timer and\n // handler where closeInbox could no longer reach them.\n this._disarmInboxOutsideClick();\n\n // Deferred like the thread popover's, and cancellable for the same\n // reason: closeInbox() (via cleanup(), or a host that opens and unmounts\n // in one tick) may run before the timer, and a listener installed after\n // that has nothing left to remove it.\n this._inboxClickTimer = setTimeout(() => {\n this._inboxClickTimer = null;\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 this._disarmInboxOutsideClick();\n }\n\n /**\n * Drops the inbox's outside-click listener, whether it is already on\n * `document` or still sitting in a pending timer. One place, so re-opening\n * and closing cannot each remember a different half of it.\n */\n _disarmInboxOutsideClick() {\n if (this._inboxClickTimer) {\n clearTimeout(this._inboxClickTimer);\n this._inboxClickTimer = null;\n }\n if (this._inboxClickHandler) {\n document.removeEventListener(\"mousedown\", this._inboxClickHandler);\n this._inboxClickHandler = null;\n }\n }\n\n /**\n * The open thread popover element, or null. Lives on the controller;\n * surfaced under its historical name because the inbox, the marker\n * engine paths and the test suite all read it here.\n */\n get activeThreadPopover() {\n return this._popover?.active ?? null;\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._popover.show(circle, comment);\n }\n\n closeThreadPopover() {\n // cleanup() reaches here before initOverlay() has run when the document\n // was still loading, so there may be no controller yet.\n this._popover?.close();\n }\n\n syncThreadPopoverToMarker() {\n this._popover?.syncToMarker();\n }\n\n showLightbox(imageSrc) {\n this.closeLightbox();\n\n // Whoever opened the lightbox (a thumbnail in the shadow tree, or a\n // host-page element) gets focus back when it closes.\n this._lightboxReturnFocus =\n this.shadowRoot.activeElement || document.activeElement;\n\n const lightbox = document.createElement(\"div\");\n lightbox.className = CLASSES.LIGHTBOX;\n lightbox.setAttribute(\"role\", \"dialog\");\n lightbox.setAttribute(\"aria-modal\", \"true\");\n lightbox.setAttribute(\"aria-label\", this.strings.screenshotPreview);\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 = \"&times;\";\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 // aria-modal is a promise about focus: the page behind the backdrop must\n // be unreachable. The close button is the only stop, so the trap is a\n // re-focus rather than a ring walk \u2014 same reasoning as confirm-dialog,\n // and on document in the capture phase for the same reason.\n this._lightboxKeydownHandler = (e) => {\n if (e.key !== \"Tab\") return;\n e.preventDefault();\n closeBtn.focus();\n };\n document.addEventListener(\"keydown\", this._lightboxKeydownHandler, true);\n\n closeBtn.focus();\n }\n\n closeLightbox() {\n if (!this._activeLightbox) return;\n if (this._lightboxKeydownHandler) {\n document.removeEventListener(\n \"keydown\",\n this._lightboxKeydownHandler,\n true\n );\n this._lightboxKeydownHandler = null;\n }\n this._activeLightbox.remove();\n this._activeLightbox = null;\n const returnFocus = /** @type {HTMLElement | null} */ (\n this._lightboxReturnFocus\n );\n this._lightboxReturnFocus = null;\n if (returnFocus?.isConnected) returnFocus.focus?.();\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 /**\n * The one lookup every id-taking method goes through. Uses sameId so a\n * legacy numeric id resolves no matter which spelling the caller holds \u2014\n * index.d.ts promises exactly that.\n * @param {import('./index.d.ts').CommentId} id\n */\n _findComment(id) {\n return this.comments.find((c) => sameId(c.id, id));\n }\n\n /**\n * The hover tooltip currently open for a comment, if any. The one place\n * the `[data-for]` selector is built, so a host id carrying a quote is\n * escaped once instead of at five call sites.\n * @param {import('./index.d.ts').CommentId} id\n * @returns {HTMLElement | null}\n */\n _tooltipEl(id) {\n return (\n this.shadowRoot?.querySelector(\n `.${CLASSES.TOOLTIP}[data-for=\"${cssAttrValue(id)}\"]`\n ) ?? null\n );\n }\n\n /**\n * @param {import('./index.d.ts').Comment | import('./index.d.ts').CommentId} commentOrId\n * the live comment, or its id \u2014 every sibling mutator takes an id, so\n * this one stopped being the exception.\n * @param {string} text\n * @param {string[]} [screenshots]\n * @returns {import('./index.d.ts').CommentReply | null} null when an id\n * does not resolve\n */\n addReply(commentOrId, text, screenshots = []) {\n const comment =\n typeof commentOrId === \"object\" && commentOrId !== null\n ? commentOrId\n : this._findComment(\n /** @type {import('./index.d.ts').CommentId} */ (commentOrId)\n );\n if (!comment) return null;\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 const serialized = this._serializeComment(comment);\n const serializedReply = this._serializeReply(reply);\n this._emit(\"reply:added\", [serialized, serializedReply], {\n comment: serialized,\n reply: serializedReply,\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 {import('./index.d.ts').CommentId} commentId\n * @param {import('./index.d.ts').CommentId} replyId\n * @returns {boolean} false when either id does not resolve\n */\n deleteReply(commentId, replyId) {\n const comment = this._findComment(commentId);\n const index =\n comment?.replies?.findIndex((r) => sameId(r.id, replyId)) ?? -1;\n if (index < 0) return false;\n\n const [reply] = comment.replies.splice(index, 1);\n this._syncStorage();\n const serialized = this._serializeComment(comment);\n const serializedReply = this._serializeReply(reply);\n this._emit(\"reply:deleted\", [serialized, serializedReply], {\n comment: serialized,\n reply: serializedReply,\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._findComment(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 // The marker's accessible name is the comment text \u2014 a screen-reader\n // user tabbing to it must hear the current sentence, not the old one.\n this._circles\n .get(String(comment.id))\n ?.setAttribute(\n \"aria-label\",\n `${this.strings.commentAriaLabelPrefix}${comment.text}`\n );\n this._syncStorage();\n const edited = this._serializeComment(comment);\n this._emit(\"comment:edited\", [edited], { comment: edited });\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._findComment(commentId);\n const reply = comment?.replies?.find((r) => sameId(r.id, replyId));\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 const serialized = this._serializeComment(comment);\n const serializedReply = this._serializeReply(reply);\n this._emit(\"reply:edited\", [serialized, serializedReply], {\n comment: serialized,\n reply: serializedReply,\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 // The anchor and context sub-objects always carried a version; the\n // comment gets one too so future breaking changes have a hinge \u2014\n // purely additive, loadComments ignores it today.\n schemaVersion: 1,\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 {import('./index.d.ts').CommentId} 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._findComment(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. The\n // lookup goes through the comment's own id, not the caller's spelling.\n const circle = this._circles.get(String(comment.id));\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 const changed = this._serializeComment(comment);\n this._emit(\"comment:status-changed\", [changed], { comment: changed });\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 const updated = this._serializeComment(comment);\n this._emit(\"comment:updated\", [updated], { comment: updated });\n return true;\n }\n\n /**\n * RF3 \u2014 categorises a comment. `null` returns it to the neutral state.\n * @param {import('./index.d.ts').CommentId} 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._findComment(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 {import('./index.d.ts').CommentId} 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._findComment(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 {import('./index.d.ts').CommentId} 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._findComment(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 {import('./index.d.ts').CommentId} id\n * @returns {boolean} false when the id is unknown\n */\n deleteComment(id) {\n if (!this._findComment(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 const merged = mergeForStorage(\n this._readStoredCached().filter((comment) => !sameId(comment.id, id)),\n this.serializeComments(),\n location.pathname\n );\n writeStoredComments(merged);\n this._storedCache = merged;\n }\n this._emit(\"comment:deleted\", [id], { id });\n return true;\n }\n\n _removeComment(id) {\n this.markers.remove(id);\n this.comments = this.comments.filter((comment) => !sameId(comment.id, id));\n }\n\n /**\n * Removes every comment at once \u2014 markers, memory and (in localStorage\n * mode) their persisted entries. This is the bulk reset a host needs to\n * reconcile against its backend before a fresh loadComments, so it\n * deliberately fires no per-comment onCommentDeleted callbacks: the host\n * initiated it and would only hear its own action echoed back.\n */\n clearComments() {\n this.closeThreadPopover();\n const cleared = this.comments;\n this.markers.clear();\n this.comments = [];\n\n if (this.options.persistence === \"localStorage\" && cleared.length > 0) {\n const clearedIds = new Set(cleared.map((comment) => String(comment.id)));\n const merged = this._readStoredCached().filter(\n (comment) => !clearedIds.has(String(comment.id))\n );\n writeStoredComments(merged);\n this._storedCache = merged;\n }\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\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 // Same minimal gate the top-level comment passes (id + text):\n // a malformed reply would otherwise flow into every renderer.\n replies: Array.isArray(item.replies)\n ? item.replies\n .filter(\n (reply) =>\n reply &&\n typeof reply === \"object\" &&\n reply.id != null &&\n typeof reply.text === \"string\"\n )\n .map((reply) =>\n Array.isArray(reply.screenshots)\n ? {\n ...reply,\n screenshots: onlyStrings(reply.screenshots),\n }\n : reply\n )\n : [],\n author: item.author || this.strings.anonymous,\n createdAt: item.createdAt || new Date().toISOString(),\n // Screenshots land in <img src> as-is \u2014 a non-string entry renders\n // a silently broken thumbnail.\n screenshots: Array.isArray(item.screenshots)\n ? onlyStrings(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 const lost = this._serializeComment(comment);\n this._emit(\"comment:anchor-lost\", [lost], { comment: lost });\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 /**\n * Re-syncs the widget after a client-side navigation: reclassifies every\n * comment against the new `location.pathname`, re-resolves anchors\n * against the new DOM, rebuilds markers, and moves the inbox onto the\n * new page. Call it from the router's \"after navigation\" hook; with\n * `autoDetectNavigation` it also runs on popstate (back/forward).\n *\n * Same-path calls are useful too: an SPA that re-rendered its route\n * swapped every node, and this is the \"re-anchor now\" primitive.\n *\n * @returns {{ anchored: number, orphaned: number, inactive: number }}\n */\n notifyNavigation() {\n const page = location.pathname;\n let anchored = 0;\n let orphaned = 0;\n let inactive = 0;\n\n // Panels pinned to the old DOM don't survive a route change; the inbox\n // does \u2014 it is cross-page by design and refreshes below.\n this.closeThreadPopover();\n this.hideCommentBox();\n if (this.inboxView) this.inboxView.currentPage = page;\n\n for (const comment of this.comments) {\n this.markers.remove(comment.id);\n comment.hidden = false;\n comment.target = null;\n comment._occluded = false;\n\n if (comment.page && comment.page !== page) {\n comment.anchorState = \"inactive\";\n comment.container = null;\n inactive++;\n continue;\n }\n\n const resolved = comment.anchor ? resolveAnchor(comment.anchor) : null;\n if (resolved) {\n comment.container = resolved.element;\n comment.relativeX = comment.anchor.relativeX;\n comment.relativeY = comment.anchor.relativeY;\n comment.anchorState = \"anchored\";\n this.renderCommentCircle(comment);\n anchored++;\n } else {\n // Same contract as loadComments: kept and listed, never positioned\n // over a guessed element \u2014 and the host is told, each time, because\n // \"the element is gone on this visit\" is fresh information.\n comment.container = null;\n comment.anchorState = \"orphaned\";\n orphaned++;\n const lost = this._serializeComment(comment);\n this._emit(\"comment:anchor-lost\", [lost], { comment: lost });\n }\n }\n\n // The new URL may itself carry a deep link (a copy-link opened through\n // the SPA's router) or the cross-page handoff written just before the\n // host navigated.\n this._pendingDetailId = this._readPendingDetailId();\n if (this.inboxView?.isOpen()) this.inboxView.refresh();\n this._openPendingDetail();\n\n return { anchored, orphaned, inactive };\n }\n\n scrollMarkerIntoView(comment) {\n this.markers.scrollMarkerIntoView(comment);\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 = MARKER_SIZE / 2;\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 // ------------------------------------------------------------------\n // Marker facade \u2014 the engine owns the logic (marker-engine.js); these\n // keep the overlay-level names every internal caller and the test suite\n // grew around. State fields surface as accessors for the same reason.\n // ------------------------------------------------------------------\n\n cleanupResizeObserver(commentId) {\n this.markers.cleanupResizeObserver(commentId);\n }\n\n validateAndCalculatePosition(comment, circle) {\n return this.markers.validateAndCalculatePosition(comment, circle);\n }\n\n updateCommentPosition(comment, circle) {\n this.markers.updatePosition(comment, circle);\n }\n\n scheduleUpdatePositions() {\n this.markers.scheduleUpdate();\n }\n\n /** Marker circles by String(id) \u2014 lives on the engine. */\n get _circles() {\n return this.markers?.circles;\n }\n\n /** Per-comment ResizeObservers \u2014 live on the engine. */\n get resizeObservers() {\n return this.markers?.resizeObservers;\n }\n\n get positionValidationEnabled() {\n return this.markers?.enabled ?? true;\n }\n\n set positionValidationEnabled(value) {\n if (this.markers) this.markers.enabled = value;\n }\n\n get _globalMutationObserver() {\n return this.markers?._globalMutationObserver ?? null;\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._tooltipEl(comment.id)?.remove();\n if (this.activeThreadPopover?.dataset.for === String(comment.id)) {\n this.closeThreadPopover();\n }\n }\n\n /**\n * Cleanup method to remove all event listeners and observers\n */\n cleanup() {\n // An instance destroyed while the document is still loading must not\n // mount when DOMContentLoaded eventually fires.\n if (this._onDomReady) {\n document.removeEventListener(\"DOMContentLoaded\", this._onDomReady);\n this._onDomReady = null;\n }\n // Cancels every scheduled pass, listener, observer and circle the\n // marker engine owns \u2014 including a rAF armed before teardown.\n this.markers?.destroy();\n if (this._storageHandler) {\n window.removeEventListener(\"storage\", this._storageHandler);\n this._storageHandler = null;\n }\n if (this._popstateHandler) {\n window.removeEventListener(\"popstate\", this._popstateHandler);\n this._popstateHandler = null;\n }\n this._storedCache = null;\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 // Covers the selection rect, the drag listeners and the pending capture.\n this._captureFlow?.destroy();\n this._pendingScreenshots = [];\n\n if (this._handleDocumentClickBound) {\n document.removeEventListener(\"mousedown\", this._handleDocumentClickBound);\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 // Covers both paths: removes the injected <style> or drops our adopted\n // sheet from the document. Leaving the latter behind would keep styling\n // the host page \u2014 the comment-mode cursor included \u2014 after teardown.\n this._detachStyles();\n\n // The now-empty shadow host itself: leaving <helldots-root> dangling\n // from <body> is half a cleanup. getShadowRoot() recreates it if a new\n // instance mounts later.\n document.querySelector(TAG_NAME)?.remove();\n }\n\n injectStyles() {\n // Re-injecting replaces rather than accumulates, whichever path is in\n // use \u2014 mountStyles hands back the undo for exactly what it mounted.\n this._detachStyles();\n this._styleDetachers = [\n mountStyles(this.shadowRoot, getStyles(), IDS.STYLES),\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 cannot\n // reach \u2014 those go on the document instead.\n mountStyles(document, getGlobalStyles(), IDS.GLOBAL_STYLES),\n ];\n }\n\n _detachStyles() {\n for (const detach of this._styleDetachers ?? []) detach();\n this._styleDetachers = [];\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", "canEmbedWebFonts", "probe", "style", "extractFontFaceRules", "css", "blocks", "at", "open", "close", "fontRuleCache", "fetchFontRules", "href", "res", "isReadable", "sheet", "shimUnreadableFontRules", "enabled", "noop", "hrefs", "renderPage", "scale", "embedCrossOriginFonts", "unshim", "domToCanvas", "node", "TAG_NAME", "paintBackdrop", "ctx", "width", "height", "cropRegion", "canvas", "left", "top", "out", "cropViewport", "sourceScale", "outputScale", "quality", "CLASSES", "HOST_PAGE_CLASSES", "IDS", "MARKER_SIZE", "MAX_SCREENSHOTS", "STATUSES", "STATUS_COLORS", "COMMENT_TYPES", "TYPE_COLORS", "PRIORITIES", "PRIORITY_COLORS", "SELECTORS", "Z_INDEX", "CURSOR_SVG", "CURSOR_HOTSPOT", "CaptureFlow", "host", "autoScreenshot", "embedCrossOriginFonts", "onRegionCaptured", "onPlace", "e", "dx", "dy", "left", "top", "width", "height", "CLASSES", "full", "renderPage", "dataUrl", "cropRegion", "cropViewport", "err", "AUTO_SCALE", "BROWSERS", "OPERATING_SYSTEMS", "UNKNOWN", "isGreaseBrand", "brand", "matchFirst", "table", "ua", "name", "re", "match", "captureContext", "win", "nav", "uaData", "browser", "b", "os", "SCROLLBAR", "webkitScrollbar", "selectors", "selector", "getStyles", "IDS", "Z_INDEX", "CLASSES", "MARKER_SIZE", "getGlobalStyles", "CURSOR_SVG", "CURSOR_HOTSPOT", "mountStyles", "target", "css", "fallbackId", "sheet", "constructSheet", "candidate", "parent", "style", "en_default", "es_default", "LOCALES", "en_default", "es_default", "DEFAULT_LOCALE", "detectLocale", "lang", "getStrings", "localeCode", "selected", "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", "sameId", "a", "b", "DEFAULT_LINK_PARAM", "buildCommentLink", "comment", "param", "href", "current", "page", "url", "readCommentLinkParam", "openMenus", "outsideListener", "keyListener", "menuItems", "menu", "MENU_GAP", "clipperRectOf", "el", "overflowX", "overflowY", "placeMenu", "button", "CLASSES", "clipper", "floor", "ceiling", "height", "anchor", "bottomIfDown", "topIfUp", "eventHits", "startWatching", "e", "entry", "items", "root", "index", "next", "step", "stopWatching", "closeOpenMenus", "attachMenuToggle", "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", "isMacPlatform", "getShortcutText", "options", "isMac", "modifierMap", "modifier", "key", "CARET_ICON_SVG", "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", "cssAttrValue", "circleSelector", "id", "screenshotsOf", "entry", "renderScreenshotsPreview", "screenshots", "onShow", "rerender", "dataUrl", "i", "item", "img", "makeThumbnailOperable", "removeBtn", "e", "wireScreenshotInput", "input", "getScreenshots", "file", "MAX_SCREENSHOTS", "reader", "ev", "wireScreenshotLightbox", "root", "activate", "createCommentCircle", "circle", "createScreenshotsDisplay", "src", "createTooltip", "header", "closeButton", "body", "badges", "tooltipScreenshots", "replyCount", "replies", "createReplyElement", "reply", "onDelete", "onEdit", "editing", "replyEl", "items", "createMoreMenu", "createInlineEditor", "replyScreenshots", "createThreadPopover", "onDeleteReply", "onEditReply", "popover", "scroll", "popoverScreenshots", "createContextBlock", "comment", "strings", "onShowLightbox", "collapsible", "expanded", "onToggle", "context", "contextScreenshot", "block", "CLASSES", "body", "toggle", "CARET_ICON_SVG", "e", "isOpen", "title", "caption", "img", "wireScreenshotLightbox", "addRow", "label", "value", "row", "key", "val", "size", "dimensions", "named", "entry", "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", "positionPopoverAtCircle", "el", "circle", "circleRect", "centerX", "centerY", "circleBaseSize", "MARKER_SIZE", "offset", "elRect", "elWidth", "x", "margin", "preferredTop", "spaceBelow", "centerPopover", "y", "PopoverController", "deps", "replyId", "popover", "CLASSES", "cssAttrValue", "id", "comment", "source", "r", "sameId", "body", "meta", "editedEl", "createEditedMark", "actions", "commentId", "draft", "host", "confirmDiscard", "editor", "createInlineEditor", "text", "saved", "strings", "locale", "onDeleteReply", "reply", "replyEl", "onEditReply", "createThreadPopover", "headerEl", "actionsEl", "createCommentActions", "c", "copyToClipboard", "buildAgentContext", "buildCommentLink", "status", "type", "priority", "actionsRow", "mainScreenshotsContainer", "wireScreenshotLightbox", "src", "contextBlock", "createContextBlock", "e", "input", "submitBtn", "threadAttachBtn", "threadFileInput", "threadScreenshotsContainer", "pendingReplyScreenshots", "updateReplyScreenshotsPreview", "renderScreenshotsPreview", "dataUrl", "wireScreenshotInput", "submitReply", "repliesContainer", "createReplyElement", "scrollEl", "target", "rect", "OCCLUSION_INTERVAL_MS", "clampToBox", "offset", "size", "MarkerEngine", "deps", "comment", "circle", "createCommentCircle", "id", "observer", "containerRect", "containerWidth", "containerHeight", "absoluteX", "absoluteY", "validatedX", "validatedY", "validatedRelativeX", "validatedRelativeY", "target", "rect", "x", "y", "top", "el", "TAG_NAME", "container", "checkOcclusion", "positionData", "circleRadius", "MARKER_SIZE", "viewportX", "viewportY", "state", "wasHidden", "now", "plans", "anyFlipped", "entries", "entry", "commentId", "key", "offsetY", "CHEVRON_LEFT_SVG", "ARROW_UP_SVG", "ARROW_DOWN_SVG", "InboxView", "shadowRoot", "strings", "locale", "currentPage", "getComments", "callbacks", "options", "CLASSES", "text", "comment", "c", "sameId", "r", "host", "confirmDiscard", "commentId", "replyId", "handlers", "createInlineEditor", "circleSelector", "circle", "comments", "detail", "id", "btn", "list", "el", "header", "desired", "notice", "seen", "key", "fingerprint", "binding", "card", "node", "index", "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", "CARET_ICON_SVG", "menu", "attachMenuToggle", "STATUSES", "COMMENT_TYPES", "PRIORITIES", "interactive", "createMetaElement", "actionsRow", "shots", "createScreenshotsDisplay", "wireScreenshotLightbox", "src", "badges", "createBadgeRow", "tag", "activate", "replyLink", "label", "createCommentActions", "copyToClipboard", "buildAgentContext", "buildCommentLink", "status", "type", "priority", "backBtn", "nav", "navBtn", "svg", "targetIndex", "target", "context", "createContextBlock", "expanded", "replies", "reply", "editingThisReply", "replyEl", "createReplyElement", "container", "inputEl", "screenshotsContainer", "attachBtn", "fileInput", "submitBtn", "createInputArea", "pendingScreenshots", "updatePreview", "renderScreenshotsPreview", "dataUrl", "wireScreenshotInput", "submit", "normalizeTags", "tags", "seen", "tag", "clean", "onlyStrings", "values", "v", "CHANGE_CALLBACKS", "CommentOverlay", "options", "isMacPlatform", "detectLocale", "getStrings", "getShadowRoot", "createToolbar", "createCommentBox", "CLASSES", "IDS", "CaptureFlow", "dataUrl", "MAX_SCREENSHOTS", "x", "y", "PopoverController", "id", "src", "target", "comment", "text", "screenshots", "commentId", "replyId", "status", "type", "priority", "MarkerEngine", "circle", "readStoredComments", "STORAGE_KEY", "url", "fromLink", "readCommentLinkParam", "fromHandoff", "PENDING_DETAIL_KEY", "DEFAULT_LINK_PARAM", "callbackArgs", "payload", "name", "callback", "err", "buildCommentLink", "merged", "mergeForStorage", "writeStoredComments", "wireScreenshotInput", "released", "key", "keyMatches", "modifierMatches", "clientX", "clientY", "prevPointerEvents", "underlying", "container", "SELECTORS", "containerRect", "relativeX", "relativeY", "anchor", "createAnchor", "generateElementSelector", "renderScreenshotsPreview", "circleRadius", "MARKER_SIZE", "offset", "windowWidth", "windowHeight", "boxRect", "boxWidth", "centerX", "centerY", "adjustedX", "adjustedY", "contextScreenshot", "createId", "captureContext", "created", "tooltip", "e", "cssAttrValue", "createTooltip", "wireScreenshotLightbox", "positionPopoverAtCircle", "InboxView", "imageSrc", "lightbox", "img", "closeBtn", "returnFocus", "c", "sameId", "commentOrId", "reply", "serialized", "serializedReply", "index", "r", "next", "edited", "author", "timestamp", "editedAt", "STATUSES", "changed", "updated", "COMMENT_TYPES", "PRIORITIES", "cleared", "clearedIds", "data", "anchored", "orphaned", "inactive", "item", "resolved", "resolveAnchor", "lost", "page", "value", "closeOpenMenus", "closeOpenConfirmDialogs", "TAG_NAME", "mountStyles", "getStyles", "getGlobalStyles", "detach", "overlay_default", "createCommentOverlay", "options", "autoInit", "overlayOptions", "initialize", "overlay_default", "index_default", "createCommentOverlay"]
7
7
  }