@qaiddev/thumbs-embed 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/icons.ts","../src/styles.ts","../src/a11y.ts","../src/modal-positioning.ts","../src/console-capture.ts","../src/dom-utils.ts","../src/quest-launcher.ts","../src/embed.ts","../src/bootstrap.ts","../src/network-capture.ts","../src/video-orientation.ts","../src/video-redaction.ts","../src/video-capture.ts","../src/screenshot-dom.ts","../src/annotate.ts","../src/index.ts","../src/dom.ts","../src/recording.ts","../src/screenshot.ts","../src/modal.ts","../src/element-selector.ts","../src/targeting.ts"],"sourcesContent":["/**\n * SVG icon strings for thumbs up/down buttons\n */\n\nexport const THUMBS_UP_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5\"/>\n</svg>`;\n\nexport const THUMBS_DOWN_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.737 3h4.018a2 2 0 01.485.06l3.76.94m-7 10v5a2 2 0 002 2h.095c.5 0 .905-.405.905-.905 0-.714.211-1.412.608-2.006L17 13V4m-7 10h2m5-10h2a2 2 0 012 2v6a2 2 0 01-2 2h-2.5\"/>\n</svg>`;\n\nexport const RECORD_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z\"/>\n</svg>`;\n\n/** Chat-bubble icon for the single, sentiment-free \"Feedback\" button. */\nexport const FEEDBACK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8 10h8M8 14h5m-9 6l3.5-3.5H18a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v14z\"/>\n</svg>`;\n\n/* ---------------------------------------------------------------------------\n * Screenshot annotation toolbar icons. Same 24×24 stroked style as the thumbs\n * icons above so they inherit `.qaid-icon` sizing and `currentColor`.\n * ------------------------------------------------------------------------- */\n\nexport const RECTANGLE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"4\" y=\"6\" width=\"16\" height=\"12\" rx=\"1\" stroke-linejoin=\"round\"/>\n</svg>`;\n\nexport const ARROW_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 19L19 5m0 0h-8m8 0v8\"/>\n</svg>`;\n\nexport const PENCIL_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 20h9M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z\"/>\n</svg>`;\n\nexport const BLUR_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 3s6 6.4 6 10a6 6 0 01-12 0c0-3.6 6-10 6-10z\"/>\n</svg>`;\n\nexport const UNDO_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-4\"/>\n</svg>`;\n\nexport const DONE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20 6L9 17l-5-5\"/>\n</svg>`;\n\n/** Exclamation mark for the \"message not sent\" screen. */\nexport const ERROR_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 6v8m0 4h.01\"/>\n</svg>`;\n\nexport const SKIP_ICON =`<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 6l12 12M18 6L6 18\"/>\n</svg>`;\n","/**\n * CSS styles for the feedback embed\n * Uses .qaid- prefix to avoid conflicts\n */\n\n// Import CSS as strings (vite handles minification in production)\nimport shadowStyles from \"./styles-shadow.css?inline\";\nimport lightStyles from \"./styles-light.css?inline\";\n\nlet instanceCount = 0;\n\n/** Default button styles included in shadow root */\nconst DEFAULT_BUTTON_STYLES = `button.qaid-btn{width:var(--qaid-btn-size);height:var(--qaid-btn-size);border-radius:50%;border:none;background:#f3f4f6;color:#374151;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);transition:background-color .2s,color .2s,transform .2s;-webkit-appearance:none;appearance:none;--qaid-hover-up-bg:var(--qaid-positive);--qaid-hover-up-color:#fff;--qaid-hover-down-bg:var(--qaid-negative);--qaid-hover-down-color:#fff}button.qaid-btn:hover{transform:scale(1.05)}button.qaid-btn-up:hover{background:var(--qaid-hover-up-bg);color:var(--qaid-hover-up-color)}button.qaid-btn-down:hover{background:var(--qaid-hover-down-bg);color:var(--qaid-hover-down-color)}button.qaid-btn-feedback:hover{background:var(--qaid-marker,#6365f1);color:var(--qaid-marker-text,#fff)}button.qaid-btn svg{width:var(--qaid-icon-size);height:var(--qaid-icon-size)}`;\n\nexport interface BuildCssVarsOptions {\n positiveColor?: string;\n negativeColor?: string;\n /** Color for selected element marker */\n markerColor?: string;\n /** Button size: small (36px), medium (48px), large (64px) */\n buttonSize?: \"small\" | \"medium\" | \"large\";\n /** Modal width in pixels */\n modalWidth?: number;\n /** Backdrop opacity (0-1) */\n backdropOpacity?: number;\n /** Font family */\n fontFamily?: string;\n /** Base font size in pixels */\n fontSize?: number;\n}\n\nconst BUTTON_SIZES = {\n small: 36,\n medium: 48,\n large: 64,\n} as const;\n\nconst ICON_SIZES = {\n small: 18,\n medium: 24,\n large: 32,\n} as const;\n\n/**\n * Calculate relative luminance of a color\n * Returns value between 0 (black) and 1 (white)\n */\nfunction getLuminance(r: number, g: number, b: number): number {\n const [rs, gs, bs] = [r, g, b].map((c) => {\n c = c / 255;\n return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n });\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n}\n\n/**\n * Parse a color string (hex or rgb) and return RGB values\n */\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n // Hex format\n if (color.startsWith(\"#\")) {\n const hex = color.slice(1);\n const fullHex = hex.length === 3\n ? hex.split(\"\").map((c) => c + c).join(\"\")\n : hex;\n const num = parseInt(fullHex, 16);\n return {\n r: (num >> 16) & 255,\n g: (num >> 8) & 255,\n b: num & 255,\n };\n }\n // RGB format\n const match = color.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/);\n if (match) {\n return {\n r: parseInt(match[1]),\n g: parseInt(match[2]),\n b: parseInt(match[3]),\n };\n }\n return null;\n}\n\n/**\n * Get the best contrasting text color (black or white) for a background\n */\nfunction getContrastTextColor(bgColor: string): string {\n const rgb = parseColor(bgColor);\n if (!rgb) return \"white\";\n const luminance = getLuminance(rgb.r, rgb.g, rgb.b);\n // Use white text on dark backgrounds, black on light\n return luminance > 0.4 ? \"black\" : \"white\";\n}\n\n/**\n * Build a map of CSS variable names to values for per-instance scoping\n */\nexport function buildCssVars(options: BuildCssVarsOptions = {}): Record<string, string> {\n const {\n positiveColor = \"rgb(0, 200, 83)\",\n negativeColor = \"rgb(255, 0, 0)\",\n markerColor = \"#6365f1\",\n buttonSize = \"medium\",\n modalWidth = 400,\n backdropOpacity = 0.3,\n fontFamily = \"system-ui, -apple-system, sans-serif\",\n fontSize = 16,\n } = options;\n\n const btnSize = BUTTON_SIZES[buttonSize];\n const iconSize = ICON_SIZES[buttonSize];\n const markerTextColor = getContrastTextColor(markerColor);\n\n return {\n \"--qaid-positive\": positiveColor,\n \"--qaid-negative\": negativeColor,\n \"--qaid-marker\": markerColor,\n \"--qaid-marker-text\": markerTextColor,\n \"--qaid-btn-size\": `${btnSize}px`,\n \"--qaid-icon-size\": `${iconSize}px`,\n \"--qaid-modal-width\": `${modalWidth}px`,\n \"--qaid-backdrop-opacity\": String(backdropOpacity),\n \"--qaid-font-family\": fontFamily,\n \"--qaid-font-size\": `${fontSize}px`,\n };\n}\n\n/**\n * Apply CSS variables to an element via inline style properties\n */\nexport function applyCssVars(el: HTMLElement, vars: Record<string, string>): void {\n for (const [name, value] of Object.entries(vars)) {\n el.style.setProperty(name, value);\n }\n}\n\n/**\n * Get the CSS string to inject into a shadow root\n * Includes base shadow styles + default button styles\n */\nexport function getEmbedStyles(): string {\n return shadowStyles + DEFAULT_BUTTON_STYLES;\n}\n\n/**\n * Inject light DOM styles (cursor override + highlight)\n * Reference-counted across instances\n */\nexport function injectStyles(): void {\n instanceCount++;\n if (instanceCount > 1) return;\n\n const style = document.createElement(\"style\");\n style.id = \"qaid-styles\";\n style.textContent = lightStyles;\n document.head.appendChild(style);\n}\n\n/**\n * Remove light DOM styles when last instance is destroyed\n */\nexport function removeStyles(): void {\n if (instanceCount <= 0) return;\n instanceCount--;\n if (instanceCount === 0) {\n // Optional: a client-side router (Astro's view transitions, any SPA that\n // swaps <head>) can remove this element out from under us, so by the time\n // a later destroy() runs there may be nothing to remove. Asserting it here\n // threw and took the rest of destroy() — and any re-mount after it — down.\n document.getElementById(\"qaid-styles\")?.remove();\n }\n}\n\n/** Reset internal state — for tests only */\nexport function _resetStylesState(): void {\n instanceCount = 0;\n const style = document.getElementById(\"qaid-styles\");\n if (style) {\n style.remove();\n }\n}\n","/**\n * Shared accessibility primitives for the qaid embeds.\n *\n * These helpers are intentionally framework-free and self-contained (no CSS\n * dependency) so they can be dropped into any open shadow root. They cover the\n * cross-cutting WCAG 2.2 AA infrastructure called for in the embed\n * accessibility plan: live-region announcements, focus management (trap,\n * save/restore), dialog semantics, and background isolation.\n */\n\n/** Host for the live regions: either a shadow root or a plain element. */\nexport type AnnounceRoot = ShadowRoot | HTMLElement;\n\n/** Options accepted by {@link announce}. */\nexport interface AnnounceOptions {\n /** Route the message through the assertive (role=\"alert\") region. */\n assertive?: boolean;\n}\n\n/** Options accepted by {@link applyDialog}. */\nexport interface ApplyDialogOptions {\n /** id of the element that labels the dialog (wired via aria-labelledby). */\n labelledbyId?: string;\n /** id of the element that describes the dialog (wired via aria-describedby). */\n describedbyId?: string;\n /** Fallback accessible name when no labelling element exists. */\n label?: string;\n}\n\n/** Handle returned by {@link createFocusTrap}. */\nexport interface FocusTrap {\n /** Remove the trap's key handler and clean up any tabindex it added. */\n release(): void;\n}\n\nconst POLITE_ATTR = \"data-qaid-a11y-live\";\n\nconst VISUALLY_HIDDEN_CSS =\n \"position:absolute;width:1px;height:1px;margin:-1px;padding:0;\" +\n \"border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);\" +\n \"white-space:nowrap;\";\n\n/**\n * Selector for natively focusable / author-focusable elements. Elements that\n * opt out with tabindex=\"-1\" are excluded below in {@link getFocusable}.\n */\nconst FOCUSABLE_SELECTOR = [\n \"a[href]\",\n \"button\",\n \"input\",\n \"textarea\",\n \"select\",\n \"[tabindex]\",\n].join(\",\");\n\nfunction ownerDoc(root: AnnounceRoot): Document {\n return root.ownerDocument || document;\n}\n\nfunction getLiveRegion(root: AnnounceRoot, assertive: boolean): HTMLElement {\n const kind = assertive ? \"assertive\" : \"polite\";\n const existing = root.querySelector<HTMLElement>(\n `[${POLITE_ATTR}=\"${kind}\"]`\n );\n if (existing) return existing;\n\n const doc = ownerDoc(root);\n const region = doc.createElement(\"div\");\n region.setAttribute(POLITE_ATTR, kind);\n region.setAttribute(\"role\", assertive ? \"alert\" : \"status\");\n region.setAttribute(\"aria-live\", assertive ? \"assertive\" : \"polite\");\n region.setAttribute(\"aria-atomic\", \"true\");\n region.style.cssText = VISUALLY_HIDDEN_CSS;\n root.appendChild(region);\n return region;\n}\n\n/**\n * Announce a message to assistive technology via a visually-hidden live region\n * mounted inside `root`. The two regions (polite role=\"status\" and assertive\n * role=\"alert\") are created lazily on first use and reused thereafter.\n *\n * The target region is cleared before the new text is written so that repeated\n * identical messages are re-announced rather than coalesced.\n */\nexport function announce(\n root: AnnounceRoot,\n message: string,\n opts: AnnounceOptions = {}\n): void {\n const region = getLiveRegion(root, !!opts.assertive);\n // Clear-then-set so an identical repeat is still perceived as a change.\n region.textContent = \"\";\n region.textContent = message;\n}\n\nfunction isDisabled(el: HTMLElement): boolean {\n if (el.hasAttribute(\"disabled\")) return true;\n const asInput = el as HTMLElement & { disabled?: boolean };\n return asInput.disabled === true;\n}\n\nfunction isHidden(el: HTMLElement): boolean {\n let node: HTMLElement | null = el;\n while (node) {\n if (node.hasAttribute(\"hidden\")) return true;\n const style = node.style;\n if (\n style &&\n (style.display === \"none\" ||\n style.visibility === \"hidden\" ||\n style.visibility === \"collapse\")\n ) {\n return true;\n }\n node = node.parentElement;\n }\n return false;\n}\n\n/**\n * Collect the focusable descendants of `container` in DOM order, excluding\n * disabled controls, elements opted out with tabindex=\"-1\", and elements that\n * are hidden (via the hidden attribute or an inline display/visibility rule on\n * themselves or an ancestor up to the document root).\n */\nexport function getFocusable(container: HTMLElement): HTMLElement[] {\n const nodes = Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n );\n return nodes.filter((el) => {\n if (el.getAttribute(\"tabindex\") === \"-1\") return false;\n if (el instanceof HTMLInputElement && el.type === \"hidden\") return false;\n if (el instanceof HTMLAnchorElement && !el.getAttribute(\"href\")) return false;\n if (isDisabled(el)) return false;\n if (isHidden(el)) return false;\n return true;\n });\n}\n\n/** Resolve the deepest active element, drilling through open shadow roots. */\nfunction getDeepActiveElement(doc: Document): HTMLElement | null {\n let active: Element | null = doc.activeElement;\n while (active && active.shadowRoot && active.shadowRoot.activeElement) {\n active = active.shadowRoot.activeElement;\n }\n return active instanceof HTMLElement ? active : null;\n}\n\n/**\n * Trap keyboard focus inside `container`: Tab / Shift+Tab cycle between the\n * first and last focusable descendants and can never leave the container. On\n * creation focus moves to the first focusable element, or to the container\n * itself (made programmatically focusable) when it has no focusable children.\n *\n * Call {@link FocusTrap.release} to remove the handler; pair it with\n * {@link restoreFocus} to return focus to the invoking control.\n */\nexport function createFocusTrap(container: HTMLElement): FocusTrap {\n const doc = container.ownerDocument || document;\n let addedTabindex = false;\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key !== \"Tab\") return;\n\n const focusable = getFocusable(container);\n if (focusable.length === 0) {\n // Nothing to move to — keep focus pinned on the container.\n event.preventDefault();\n container.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = getDeepActiveElement(doc);\n const withinTrap = active ? focusable.indexOf(active) !== -1 : false;\n\n if (event.shiftKey) {\n if (!withinTrap || active === first) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (!withinTrap || active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n container.addEventListener(\"keydown\", onKeydown);\n\n const initial = getFocusable(container);\n if (initial.length > 0) {\n initial[0].focus();\n } else {\n if (!container.hasAttribute(\"tabindex\")) {\n container.setAttribute(\"tabindex\", \"-1\");\n addedTabindex = true;\n }\n container.focus();\n }\n\n return {\n release(): void {\n container.removeEventListener(\"keydown\", onKeydown);\n if (addedTabindex) {\n container.removeAttribute(\"tabindex\");\n addedTabindex = false;\n }\n },\n };\n}\n\n/**\n * Capture the currently focused element (drilling through shadow roots) so it\n * can be restored later with {@link restoreFocus}. Returns null when focus is\n * on nothing focusable.\n */\nexport function saveFocus(): HTMLElement | null {\n return getDeepActiveElement(document);\n}\n\n/**\n * Restore focus to a previously saved element. Safe to call with null or an\n * element that has since been detached (a missing/throwing focus is ignored).\n */\nexport function restoreFocus(el: HTMLElement | null): void {\n if (!el || typeof el.focus !== \"function\") return;\n try {\n el.focus();\n } catch {\n // The element may have been removed; nothing more to do.\n }\n}\n\n/**\n * Apply dialog semantics to `el`: role=\"dialog\", aria-modal=\"true\", and the\n * naming/description wiring described by `opts`. Prefer aria-labelledby /\n * aria-describedby pointing at visible title/subtitle elements; fall back to\n * aria-label when no visible label element exists.\n */\nexport function applyDialog(el: HTMLElement, opts: ApplyDialogOptions = {}): void {\n el.setAttribute(\"role\", \"dialog\");\n el.setAttribute(\"aria-modal\", \"true\");\n if (opts.labelledbyId) {\n el.setAttribute(\"aria-labelledby\", opts.labelledbyId);\n }\n if (opts.describedbyId) {\n el.setAttribute(\"aria-describedby\", opts.describedbyId);\n }\n if (opts.label) {\n el.setAttribute(\"aria-label\", opts.label);\n }\n}\n\n/** Walk up to the body-level ancestor of `el`, crossing shadow boundaries. */\nfunction topLevelAncestor(el: HTMLElement, body: HTMLElement): HTMLElement | null {\n let node: Node | null = el;\n while (node && node !== body) {\n const parent: Node | null = node.parentNode;\n if (parent === body) return node as HTMLElement;\n if (parent && (parent as ShadowRoot).host) {\n node = (parent as ShadowRoot).host;\n continue;\n }\n if (!parent) return null;\n node = parent;\n }\n return null;\n}\n\n/**\n * Isolate the background from assistive technology while a dialog is open by\n * marking every top-level sibling of the dialog inert (with an aria-hidden\n * fallback). `except` is the open dialog (or any element inside it); the\n * top-level element that contains it is left interactive.\n *\n * Returns a restore function that reverts every attribute this call changed,\n * leaving elements that were already inert/hidden untouched.\n */\nexport function setBackgroundInert(except: HTMLElement): () => void {\n const doc = except.ownerDocument || document;\n const body = doc.body;\n if (!body) return () => {};\n\n const keep = topLevelAncestor(except, body);\n const changed: Array<{\n el: HTMLElement;\n prevInert: boolean;\n prevAriaHidden: string | null;\n }> = [];\n\n Array.from(body.children).forEach((child) => {\n if (!(child instanceof HTMLElement)) return;\n if (keep && child === keep) return;\n\n const prevInert = child.inert === true;\n const prevAriaHidden = child.getAttribute(\"aria-hidden\");\n // Only record (and later revert) elements we actually change.\n if (prevInert && prevAriaHidden === \"true\") return;\n\n child.inert = true;\n child.setAttribute(\"aria-hidden\", \"true\");\n changed.push({ el: child, prevInert, prevAriaHidden });\n });\n\n return function restore(): void {\n while (changed.length) {\n // The loop guard guarantees pop() returns an entry.\n const entry = changed.pop()!;\n entry.el.inert = entry.prevInert;\n if (entry.prevAriaHidden === null) {\n entry.el.removeAttribute(\"aria-hidden\");\n } else {\n entry.el.setAttribute(\"aria-hidden\", entry.prevAriaHidden);\n }\n }\n };\n}\n","/**\n * Modal positioning utilities\n */\n\nimport type { SelectedBounds } from \"./types\";\n\nexport interface ModalPosition {\n top: number;\n left: number;\n position: \"above\" | \"below\";\n}\n\nexport interface ArrowPosition {\n left: number;\n}\n\nexport interface ModalDimensions {\n width: number;\n height: number;\n arrowHeight: number;\n gap: number;\n viewportPadding: number;\n}\n\nconst DEFAULT_DIMENSIONS: ModalDimensions = {\n width: 400,\n height: 280,\n arrowHeight: 12,\n gap: 8,\n viewportPadding: 16,\n};\n\n/**\n * Calculate whether modal should go above or below the target element\n */\nexport function calculateVerticalPosition(\n bounds: SelectedBounds,\n viewportHeight: number,\n totalHeight: number,\n gap: number,\n viewportPadding: number\n): { top: number; position: \"above\" | \"below\" } {\n const spaceAbove = bounds.y;\n const spaceBelow = viewportHeight - (bounds.y + bounds.height);\n\n // Prefer below if there's enough space\n if (spaceBelow >= totalHeight + gap) {\n return {\n top: bounds.y + bounds.height + gap,\n position: \"below\",\n };\n }\n\n // Try above if there's enough space\n if (spaceAbove >= totalHeight + gap) {\n return {\n top: bounds.y - totalHeight - gap,\n position: \"above\",\n };\n }\n\n // Neither fits perfectly - clamp to viewport\n const position = spaceBelow > spaceAbove ? \"below\" : \"above\";\n let top: number;\n\n if (position === \"below\") {\n top = Math.min(\n bounds.y + bounds.height + gap,\n viewportHeight - totalHeight - viewportPadding\n );\n } else {\n top = Math.max(viewportPadding, bounds.y - totalHeight - gap);\n }\n\n return { top, position };\n}\n\n/**\n * Calculate horizontal position of modal, clamped to viewport\n */\nexport function calculateHorizontalPosition(\n bounds: SelectedBounds,\n viewportWidth: number,\n modalWidth: number,\n viewportPadding: number\n): number {\n const centerX = bounds.x + bounds.width / 2;\n let left = centerX - modalWidth / 2;\n\n // Clamp to viewport\n left = Math.max(viewportPadding, Math.min(left, viewportWidth - modalWidth - viewportPadding));\n\n return left;\n}\n\n/**\n * Calculate arrow position relative to modal\n * Points at the click position, not the element center\n */\nexport function calculateArrowPosition(\n bounds: SelectedBounds,\n modalLeft: number,\n modalWidth: number,\n minOffset = 24,\n arrowWidth = 24\n): number {\n // Use click position for arrow, not element center\n const targetX = bounds.clickX;\n // Subtract half the arrow width to center it on the target\n const arrowLeft = targetX - modalLeft - arrowWidth / 2;\n\n // Clamp arrow to stay within modal bounds\n return Math.max(minOffset, Math.min(arrowLeft, modalWidth - minOffset - arrowWidth / 2));\n}\n\n/**\n * Calculate complete modal position based on selected element bounds\n */\nexport function calculateModalPosition(\n bounds: SelectedBounds,\n viewportWidth: number,\n viewportHeight: number,\n dimensions: ModalDimensions = DEFAULT_DIMENSIONS\n): ModalPosition {\n const totalHeight = dimensions.height + dimensions.arrowHeight;\n\n const vertical = calculateVerticalPosition(\n bounds,\n viewportHeight,\n totalHeight,\n dimensions.gap,\n dimensions.viewportPadding\n );\n\n const left = calculateHorizontalPosition(\n bounds,\n viewportWidth,\n dimensions.width,\n dimensions.viewportPadding\n );\n\n return {\n top: vertical.top,\n left,\n position: vertical.position,\n };\n}\n\nexport interface TooltipPositionOptions {\n /** Gap in pixels between anchor and tooltip, and from viewport edges */\n gap?: number;\n}\n\n/**\n * Calculate tooltip position relative to an anchor, clamped to the viewport.\n * Pure function: no DOM access — caller passes measured rects and viewport size.\n *\n * Strategy:\n * - Place below the anchor by default\n * - Flip above if below would overflow vertically\n * - Clamp horizontally to viewport, then vertically to viewport\n */\nexport function calculateTooltipPosition(\n anchor: { top: number; bottom: number; left: number },\n tooltip: { width: number; height: number },\n viewport: { width: number; height: number },\n options: TooltipPositionOptions = {}\n): { top: number; left: number } {\n const gap = options.gap ?? 8;\n\n let top = anchor.bottom + gap;\n\n // Flip above if below overflows\n if (top + tooltip.height > viewport.height - gap) {\n top = anchor.top - tooltip.height - gap;\n }\n\n // Clamp horizontally\n let left = anchor.left;\n if (left < gap) {\n left = gap;\n } else if (left + tooltip.width > viewport.width - gap) {\n left = viewport.width - tooltip.width - gap;\n }\n\n // Clamp vertically\n if (top < gap) {\n top = gap;\n } else if (top + tooltip.height > viewport.height - gap) {\n top = viewport.height - tooltip.height - gap;\n }\n\n return { top, left };\n}\n\n/**\n * Calculate both modal and arrow positions\n */\nexport function calculateModalAndArrowPosition(\n bounds: SelectedBounds,\n viewportWidth: number,\n viewportHeight: number,\n dimensions: ModalDimensions = DEFAULT_DIMENSIONS\n): { modal: ModalPosition; arrow: ArrowPosition } {\n const modal = calculateModalPosition(bounds, viewportWidth, viewportHeight, dimensions);\n\n const arrowLeft = calculateArrowPosition(\n bounds,\n modal.left,\n dimensions.width\n );\n\n return {\n modal,\n arrow: { left: arrowLeft },\n };\n}\n","/**\n * Console capture utilities\n */\n\nimport type { ConsoleError } from \"./types\";\n\nconst MAX_MESSAGES = 20;\n\nexport interface ConsoleCapture {\n errors: ConsoleError[];\n restore: () => void;\n}\n\ntype ConsoleLevel = \"error\" | \"warn\" | \"log\";\n\n/**\n * Wrap console methods to capture messages\n */\nexport function captureConsoleErrors(\n onError?: (error: ConsoleError) => void\n): ConsoleCapture {\n const errors: ConsoleError[] = [];\n const originalError = console.error;\n const originalWarn = console.warn;\n const originalLog = console.log;\n\n const capture = (level: ConsoleLevel, args: unknown[]) => {\n const error: ConsoleError = {\n message: args.map((arg) => String(arg)).join(\" \"),\n timestamp: Date.now(),\n level,\n };\n\n // Keep max messages\n if (errors.length >= MAX_MESSAGES) {\n errors.shift();\n }\n errors.push(error);\n\n // Call callback if provided\n if (onError) {\n onError(error);\n }\n };\n\n console.error = function (...args: unknown[]) {\n capture(\"error\", args);\n originalError.apply(console, args);\n };\n\n console.warn = function (...args: unknown[]) {\n capture(\"warn\", args);\n originalWarn.apply(console, args);\n };\n\n console.log = function (...args: unknown[]) {\n capture(\"log\", args);\n originalLog.apply(console, args);\n };\n\n return {\n errors,\n restore: () => {\n console.error = originalError;\n console.warn = originalWarn;\n console.log = originalLog;\n },\n };\n}\n\n/**\n * Add an error to the captured errors array\n */\nexport function addError(errors: ConsoleError[], error: ConsoleError): void {\n if (errors.length >= MAX_MESSAGES) {\n errors.shift();\n }\n errors.push(error);\n}\n\n/**\n * Format console error for display\n */\nexport function formatError(error: ConsoleError): string {\n const date = new Date(error.timestamp);\n const time = date.toLocaleTimeString();\n const levelPrefix = error.level === \"error\" ? \"ERR\" : error.level === \"warn\" ? \"WRN\" : \"LOG\";\n return `[${time}] [${levelPrefix}] ${error.message}`;\n}\n","/**\n * DOM utility functions\n */\n\n/**\n * Create an element with attributes and optional children\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string)[]\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"className\") {\n el.className = value;\n } else {\n el.setAttribute(key, value);\n }\n }\n }\n\n if (children) {\n for (const child of children) {\n if (typeof child === \"string\") {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n }\n\n return el;\n}\n\n/**\n * Check if viewport width indicates mobile device\n */\nexport function isMobileViewport(breakpoint = 640): boolean {\n return typeof window !== \"undefined\" && window.innerWidth < breakpoint;\n}\n\n/**\n * Get element at point, temporarily hiding shadow hosts so elementFromPoint\n * sees through to the page elements underneath.\n * Uses visibility:hidden which skips the element in elementFromPoint without\n * triggering reflow.\n */\nexport function getElementAtPointUnderOverlay(\n x: number,\n y: number,\n hosts: HTMLElement[]\n): Element | null {\n const saved = hosts.map((h) => h.style.visibility);\n hosts.forEach((h) => (h.style.visibility = \"hidden\"));\n const element = document.elementFromPoint(x, y);\n hosts.forEach((h, i) => (h.style.visibility = saved[i]));\n return element;\n}\n\n/**\n * Check if an element is part of the feedback embed\n * With shadow DOM, embed elements are inside shadow hosts marked with\n * data-qaid-embed or data-qaid-embed-overlay\n */\nexport function isEmbedElement(element: Element | null): boolean {\n if (!element) return false;\n // Check if the element itself is a shadow host\n if (element.hasAttribute(\"data-qaid-embed\") || element.hasAttribute(\"data-qaid-embed-overlay\")) return true;\n // Check ancestors for shadow host\n return !!element.closest(\"[data-qaid-embed], [data-qaid-embed-overlay]\");\n}\n\n/**\n * Get element bounds with optional padding\n */\nexport function getElementBounds(\n element: Element,\n padding = 0\n): { x: number; y: number; width: number; height: number } {\n const rect = element.getBoundingClientRect();\n return {\n x: rect.left - padding,\n y: rect.top - padding,\n width: rect.width + padding * 2,\n height: rect.height + padding * 2,\n };\n}\n\n/**\n * Remove all elements with a specific class\n */\nexport function removeAllByClass(className: string): void {\n document.querySelectorAll(`.${className}`).forEach((el) => {\n el.classList.remove(className);\n });\n}\n","/**\n * Lazy loader + launcher for `@qaiddev/quests-embed`.\n *\n * `thumbs-embed` links a button to a quest but owns no questionnaire code\n * and takes no build-time dependency on the quests package. Instead the\n * quests widget is imported from a CDN the first time a quest is triggered,\n * so the shipped bundle stays zero-dependency and small.\n *\n * Only the tiny slice of the quests public API that we actually use is\n * modelled here structurally, so a version bump of the quests package that\n * keeps this surface stable needs no change in thumbs-embed.\n */\n\n/** Default ES-module URL, pinned to a compatible major on unpkg. */\nexport const DEFAULT_QUESTS_MODULE_URL =\n \"https://unpkg.com/@qaiddev/quests-embed@1/dist/qaid-quests.js\";\n\n/** The bit of a QaidQuests instance we hold onto. */\nexport interface QuestInstance {\n destroy(): void;\n}\n\n/** The bit of QaidQuests config we pass. Mirrors QuestsConfig loosely. */\nexport interface LaunchedQuestConfig {\n endpoint: string;\n configUrl: string;\n apiKey?: string;\n metadata?: Record<string, unknown>;\n onComplete?: (answers: Record<string, unknown>) => void;\n onClose?: () => void;\n}\n\ntype QuestsConstructor = new (config: LaunchedQuestConfig) => QuestInstance;\n\n/** Shape of the quests module's default/entry exports. */\nexport interface QuestsModule {\n QaidQuests: QuestsConstructor;\n}\n\ntype Importer = (url: string) => Promise<unknown>;\n\nconst defaultImporter: Importer = (url) =>\n // The URL is a runtime value, not a static specifier — keep Vite from\n // trying to analyze/bundle it.\n import(/* @vite-ignore */ url);\n\nlet importer: Importer = defaultImporter;\nlet modulePromise: Promise<QuestsModule> | null = null;\nlet cachedUrl: string | null = null;\n\n/**\n * @internal Test seam: swap the dynamic importer and clear the cache.\n * Pass `null` to restore the real dynamic `import()`. Not re-exported\n * from the package entry, so it isn't public API.\n */\nexport function _setQuestsImporter(fn: Importer | null): void {\n importer = fn ?? defaultImporter;\n modulePromise = null;\n cachedUrl = null;\n}\n\n/**\n * Import the quests module, caching the promise per URL. A failed load\n * clears the cache so a later trigger can retry (e.g. after a transient\n * network error) rather than being stuck with a rejected promise.\n */\nexport function loadQuestsModule(moduleUrl: string): Promise<QuestsModule> {\n if (modulePromise && cachedUrl === moduleUrl) return modulePromise;\n cachedUrl = moduleUrl;\n modulePromise = Promise.resolve(importer(moduleUrl))\n .then((mod) => {\n const m = mod as Partial<QuestsModule>;\n if (!m || typeof m.QaidQuests !== \"function\") {\n throw new Error(\"quests module has no QaidQuests export\");\n }\n return m as QuestsModule;\n })\n .catch((err) => {\n modulePromise = null;\n cachedUrl = null;\n throw err;\n });\n return modulePromise;\n}\n\nexport interface LaunchQuestOptions {\n /** Quest id to launch (the `[id]` in `{base}/{id}/definition`). */\n questId: string;\n /** Quest-service base URL, e.g. \"https://qaid.dev/api/quests\". */\n base: string;\n /** API key for the quest service (optional). */\n apiKey?: string;\n /** ES-module URL to load the quests widget from. */\n moduleUrl: string;\n /** Feedback record id to correlate the response with (optional). */\n feedbackId?: string | number | null;\n /** Called when the quest embed is torn down. */\n onClose?: () => void;\n}\n\n/**\n * Load the quests widget (if not already loaded) and open the given quest\n * as its own centered modal. Rejects if the module can't be loaded — the\n * caller should fall back to its normal UI.\n */\nexport async function launchQuest(opts: LaunchQuestOptions): Promise<QuestInstance> {\n const mod = await loadQuestsModule(opts.moduleUrl);\n const base = opts.base.replace(/\\/+$/, \"\");\n const metadata =\n opts.feedbackId != null ? { feedbackId: opts.feedbackId } : undefined;\n return new mod.QaidQuests({\n endpoint: `${base}/responses`,\n configUrl: `${base}/${encodeURIComponent(opts.questId)}/definition`,\n apiKey: opts.apiKey || undefined,\n metadata,\n onClose: opts.onClose,\n });\n}\n","import type {\n FeedbackConfig,\n ResolvedFeedbackConfig,\n FeedbackData,\n FeedbackType,\n SelectedBounds,\n EmbedState,\n FeedbackPayload,\n FeedbackResponse,\n} from \"./types\";\nimport { THUMBS_UP_ICON, THUMBS_DOWN_ICON, RECORD_ICON, FEEDBACK_ICON } from \"./icons\";\nimport { injectStyles, removeStyles, buildCssVars, applyCssVars, getEmbedStyles } from \"./styles\";\n// The element-targeting subsystem (with element-selector, ~14 KB) lives in a\n// lazily-loaded chunk (targeting.ts), pulled in the first time the user targets.\nimport type { TargetingController, TargetingHost } from \"./targeting\";\nimport {\n announce,\n createFocusTrap,\n saveFocus,\n restoreFocus,\n applyDialog,\n setBackgroundInert,\n type FocusTrap,\n} from \"./a11y\";\nimport { calculateTooltipPosition } from \"./modal-positioning\";\nimport type { ModalController, ModalHost } from \"./modal\";\nimport { captureConsoleErrors, type ConsoleCapture } from \"./console-capture\";\n// The whole video-recording subsystem lives in a lazily-loaded chunk\n// (recording.ts), pulled in the first time the record button is used. Only the\n// types are imported eagerly (erased at build time).\nimport type { RecordingController, RecordingHost } from \"./recording\";\nimport { isMobileViewport } from \"./dom-utils\";\n// Screenshot capture and the annotation editor are loaded on demand the first\n// time a screenshot is taken — see submitFeedback() / openAnnotationEditor().\nimport {\n launchQuest,\n DEFAULT_QUESTS_MODULE_URL,\n type QuestInstance,\n} from \"./quest-launcher\";\n\nconst VISITOR_ID_KEY = \"qaid_visitor_id\";\nconst HIDE_FEEDBACK_KEY = \"qaid_hide_feedback\";\n/** Default annotation swatches: red, amber, green, blue, near-black, white. */\nconst DEFAULT_ANNOTATION_PALETTE = [\n \"#ef4444\",\n \"#f59e0b\",\n \"#22c55e\",\n \"#3b82f6\",\n \"#111827\",\n \"#ffffff\",\n];\n\n// True when the primary pointer is touch (phone/tablet). On these devices the\n// Screen Capture API (getDisplayMedia) shows an intrusive \"start capturing\"\n// system prompt, dims the screen, and on iOS returns a rotated frame — so the\n// permission-free DOM/canvas screenshot is the right default there.\nfunction isTouchPrimaryDevice(): boolean {\n return (\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(pointer: coarse)\").matches\n );\n}\n\nfunction getHideKey(apiKey?: string): string {\n return apiKey ? `${HIDE_FEEDBACK_KEY}_${apiKey}` : HIDE_FEEDBACK_KEY;\n}\n\nexport function isHiddenByUser(apiKey?: string): boolean {\n try {\n return localStorage.getItem(getHideKey(apiKey)) === \"1\";\n } catch {\n return false;\n }\n}\n\nexport function setHiddenByUser(apiKey?: string, hidden = true): void {\n try {\n if (hidden) {\n localStorage.setItem(getHideKey(apiKey), \"1\");\n } else {\n localStorage.removeItem(getHideKey(apiKey));\n }\n } catch {\n // localStorage not available\n }\n}\n\n/**\n * A random v4 UUID.\n *\n * `crypto.randomUUID` exists only in secure contexts (HTTPS and localhost). On\n * a plain http:// page it is undefined, and calling it threw inside the\n * constructor, so the widget never appeared. `crypto.getRandomValues` has no\n * such restriction; `Math.random` covers a runtime with no `crypto` at all.\n * The id only groups one browser's feedback, so it needs to be unique, not\n * secret.\n */\nexport function randomId(): string {\n const c = typeof crypto !== \"undefined\" ? crypto : undefined;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n\n const bytes = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n // RFC 4122 version 4 / variant 1 bits, so the result reads as a real UUID.\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Get or create a visitor ID stored in localStorage\n */\nexport function getOrCreateVisitorId(): string {\n try {\n let visitorId = localStorage.getItem(VISITOR_ID_KEY);\n if (!visitorId) {\n visitorId = randomId();\n localStorage.setItem(VISITOR_ID_KEY, visitorId);\n }\n return visitorId;\n } catch {\n // localStorage not available, generate a session-only ID\n return randomId();\n }\n}\n\n/**\n * QaidFeedback - Standalone feedback collection embed\n */\nexport class QaidFeedback {\n private config: ResolvedFeedbackConfig;\n private state: EmbedState = \"IDLE\";\n private feedbackData: FeedbackData = {\n feedbackType: null,\n elementSelector: null,\n elementText: null,\n consoleErrors: [],\n };\n private selectedBounds: SelectedBounds = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n clickX: 0,\n clickY: 0,\n visible: false,\n };\n private feedbackId: number | null = null;\n // A quest launched from a button (in place of the message box), if any.\n private activeQuest: QuestInstance | null = null;\n private isMobile = false;\n private visitorId: string;\n\n // Console capture\n private consoleCapture: ConsoleCapture | null = null;\n\n // Video recording — the whole subsystem lives in a lazily-loaded chunk\n // (recording.ts); a thumbs-only visitor never downloads it.\n private recording: RecordingController | null = null;\n // Element targeting — also a lazily-loaded chunk (targeting.ts).\n private targeting: TargetingController | null = null;\n private targetingPrewarmed = false;\n // Preload-on-intent: warm the lazily-split feature chunks before first use.\n private prewarmHandle: number | null = null;\n private prewarmIsTimeout = false;\n private videoPrewarmed = false;\n private screenshotPrewarmed = false;\n\n // Shadow DOM\n private shadowHost: HTMLDivElement | null = null;\n private shadowRoot: ShadowRoot | null = null;\n\n // Overlay shadow DOM (always on document.body for full-page coverage)\n private overlayShadowHost: HTMLDivElement | null = null;\n private overlayShadowRoot: ShadowRoot | null = null;\n\n // DOM elements (inside shadow root)\n private buttonsContainer: HTMLDivElement | null = null;\n private isUserProvidedContainer = false;\n // The message modal lives in a lazily-loaded chunk (modal.ts).\n private modal: ModalController | null = null;\n private modalPrewarmed = false;\n private dismissBtn: HTMLButtonElement | null = null;\n\n // Per-instance CSS variables\n private cssVars: Record<string, string> = {};\n\n private activeThumbBtn: HTMLElement | null = null;\n // Tracks whether the pending activation came from the keyboard (Enter/Space)\n // rather than a pointer, so targeting can avoid elementFromPoint(0,0).\n private keyboardActivation = false;\n\n // Accessibility: dialog focus management\n private dialogTrigger: HTMLElement | null = null;\n private dialogTrap: FocusTrap | null = null;\n private dialogRestoreInert: (() => void) | null = null;\n\n // Unique id suffix for aria-labelledby/describedby references\n private readonly uid = Math.random().toString(36).slice(2, 9);\n\n // Bound event handlers\n private boundKeyDown: (e: KeyboardEvent) => void;\n private boundResize: () => void;\n\n // Start with buttons slid off-screen (localStorage dismiss, no animation)\n private _startDismissed = false;\n\n // DOM persistence (survives framework client-side navigation)\n private destroyed = false;\n private domObserver: MutationObserver | null = null;\n private boundBeforeSwap: ((e: Event) => void) | null = null;\n\n constructor(config: FeedbackConfig) {\n this.config = {\n endpoint: config.endpoint,\n apiKey: config.apiKey ?? \"\",\n container: config.container ?? \"\",\n buttonClass: config.buttonClass ?? \"\",\n direction: config.direction ?? \"horizontal\",\n position: config.position ?? \"bottom-right\",\n offset: {\n x: config.offset?.x ?? 16,\n y: config.offset?.y ?? 16,\n },\n zIndex: config.zIndex ?? 50,\n skipTargeting: config.skipTargeting ?? false,\n singleButton: config.singleButton ?? false,\n feedbackMode: config.feedbackMode ?? \"target\",\n colors: {\n positive: config.colors?.positive ?? \"rgb(0, 200, 83)\",\n negative: config.colors?.negative ?? \"rgb(255, 0, 0)\",\n marker: config.colors?.marker ?? \"#6365f1\",\n },\n buttonSize: config.buttonSize ?? \"medium\",\n text: {\n tooltip: config.text?.tooltip ?? \"\",\n modalTitle: config.text?.modalTitle ?? \"Thank you for your feedback!\",\n modalSubtitle: config.text?.modalSubtitle ?? \"Would you like to add a message to help us understand your feedback better?\",\n placeholder: config.text?.placeholder ?? \"Optional: Tell us more about your experience...\",\n submitButton: config.text?.submitButton ?? \"Submit\",\n skipButton: config.text?.skipButton ?? \"Skip\",\n positiveLabel: config.text?.positiveLabel ?? \"Send positive feedback\",\n negativeLabel: config.text?.negativeLabel ?? \"Send negative feedback\",\n recordLabel: config.text?.recordLabel ?? \"Record a screen recording\",\n dismissLabel: config.text?.dismissLabel ?? \"Hide Feedback\",\n feedbackLabel: config.text?.feedbackLabel ?? \"Send feedback\",\n confirmationTitle: config.text?.confirmationTitle ?? \"Thank you!\",\n confirmationMessage:\n config.text?.confirmationMessage ?? \"Your feedback has been received.\",\n confirmationClose: config.text?.confirmationClose ?? \"Close\",\n errorTitle: config.text?.errorTitle ?? \"Message not sent\",\n errorMessage:\n config.text?.errorMessage ??\n \"Something went wrong, so we did not get your message. Please try again later.\",\n },\n hideConfirmation: config.hideConfirmation ?? false,\n modalWidth: config.modalWidth ?? 400,\n backdropOpacity: config.backdropOpacity ?? 0.3,\n fontFamily: config.fontFamily ?? \"system-ui, -apple-system, sans-serif\",\n fontSize: config.fontSize ?? 16,\n captureScreenshot: config.captureScreenshot ?? false,\n annotate: config.annotate ?? true,\n annotationColor: config.annotationColor ?? config.colors?.marker ?? \"#6365f1\",\n annotationPalette: config.annotationPalette ?? DEFAULT_ANNOTATION_PALETTE,\n screenshotMethod: config.screenshotMethod ?? \"permission\",\n screenshotOptions: {\n quality: config.screenshotOptions?.quality ?? 0.8,\n maxWidth: config.screenshotOptions?.maxWidth ?? 1280,\n maxHeight: config.screenshotOptions?.maxHeight ?? 800,\n },\n incognito: config.incognito ?? false,\n hideDismiss: config.hideDismiss ?? false,\n positiveIcon: config.positiveIcon ?? \"\",\n negativeIcon: config.negativeIcon ?? \"\",\n feedbackIcon: config.feedbackIcon ?? \"\",\n hideThumbs: config.hideThumbs ?? false,\n css: config.css ?? \"\",\n captureVideo: config.captureVideo ?? false,\n videoOptions: {\n maxDuration: config.videoOptions?.maxDuration ?? 15,\n redaction: config.videoOptions?.redaction ?? false,\n },\n recordIcon: config.recordIcon ?? \"\",\n quests: {\n base: config.quests?.base ?? \"\",\n up: config.quests?.up ?? \"\",\n down: config.quests?.down ?? \"\",\n video: config.quests?.video ?? \"\",\n // Quest service reuses the feedback API key unless overridden.\n apiKey: config.quests?.apiKey ?? config.apiKey ?? \"\",\n moduleUrl: config.quests?.moduleUrl ?? DEFAULT_QUESTS_MODULE_URL,\n },\n };\n\n // Bind event handlers\n this.boundKeyDown = this.handleKeyDown.bind(this);\n this.boundResize = this.handleResize.bind(this);\n\n // Get or create visitor ID for anonymous feedback tracking\n this.visitorId = getOrCreateVisitorId();\n\n this.init();\n }\n\n private applyVars(el: HTMLElement): void {\n applyCssVars(el, this.cssVars);\n }\n\n /**\n * Announce a message via the shared visually-hidden live regions.\n * Prefer the overlay shadow root (which hosts every transient surface and\n * is never inerted by its own dialogs) so announcements are not suppressed\n * while a dialog aria-hides the main button host.\n */\n private announceMsg(message: string, assertive = false): void {\n const root = this.overlayShadowRoot ?? this.shadowRoot;\n if (root) announce(root, message, { assertive });\n }\n\n /**\n * Turn a transient surface into an accessible modal dialog: save the\n * invoking control, apply dialog semantics, trap focus, and inert the\n * background. Paired with closeDialogA11y() on every close path.\n */\n private openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void {\n this.dialogTrigger = saveFocus();\n applyDialog(container, opts);\n this.dialogTrap = createFocusTrap(container);\n this.dialogRestoreInert = setBackgroundInert(container);\n }\n\n private closeDialogA11y(): void {\n this.dialogTrap?.release();\n this.dialogTrap = null;\n if (this.dialogRestoreInert) {\n this.dialogRestoreInert();\n this.dialogRestoreInert = null;\n }\n restoreFocus(this.dialogTrigger);\n this.dialogTrigger = null;\n }\n\n private clearActiveThumb(): void {\n if (this.activeThumbBtn) {\n this.activeThumbBtn.setAttribute(\"aria-pressed\", \"false\");\n this.activeThumbBtn = null;\n }\n }\n\n private init(): void {\n // Inject shared light DOM styles (cursor + highlight, reference counted)\n injectStyles();\n\n // Build per-instance CSS variables\n this.cssVars = buildCssVars({\n positiveColor: this.config.colors.positive,\n negativeColor: this.config.colors.negative,\n markerColor: this.config.colors.marker,\n buttonSize: this.config.buttonSize,\n modalWidth: this.config.modalWidth,\n backdropOpacity: this.config.backdropOpacity,\n fontFamily: this.config.fontFamily,\n fontSize: this.config.fontSize,\n });\n\n // Restore dismiss preference from localStorage (skip if hideDismiss)\n if (!this.config.hideDismiss && isHiddenByUser(this.config.apiKey)) {\n this._startDismissed = true;\n }\n\n // Check mobile\n this.checkMobile();\n window.addEventListener(\"resize\", this.boundResize);\n\n // Create shadow host and embed\n this.createEmbed();\n\n // Capture console errors\n this.consoleCapture = captureConsoleErrors((_error) => {\n this.feedbackData.consoleErrors = this.consoleCapture?.errors ?? [];\n });\n this.feedbackData.consoleErrors = this.consoleCapture.errors;\n\n // Persist across client-side navigations (Astro View Transitions, etc.)\n this.observeDom();\n\n // Preload split feature chunks on idle when their config flags are set, so\n // the first screenshot / recording doesn't wait on a chunk fetch. Hovering\n // the record button also warms the video chunk (see createEmbed).\n if (\n (this.config.captureVideo && this.videoSupported()) ||\n this.config.captureScreenshot\n ) {\n this.schedulePrewarm(() => {\n if (this.config.captureVideo && this.videoSupported()) this.prewarmVideo();\n if (this.config.captureScreenshot) this.prewarmScreenshot();\n });\n }\n }\n\n /**\n * Best-effort preload of a lazily-split feature chunk so its first use is\n * instant. Warming fetches + compiles (and defines) the module; the feature\n * modules have no load-time side effects, so this is safe. Errors are\n * swallowed — a failed preload just falls back to an on-demand load.\n */\n private prewarmVideo(): void {\n if (this.videoPrewarmed) return;\n this.videoPrewarmed = true;\n // Warm both the recording controller chunk and the capture chunk it pulls\n // in on start, so the first recording begins without waiting on either.\n import(\"./recording\").catch(() => {});\n import(\"./video-capture\").catch(() => {});\n }\n\n private prewarmScreenshot(): void {\n if (this.screenshotPrewarmed) return;\n this.screenshotPrewarmed = true;\n (this.shouldCaptureViaDom()\n ? import(\"./screenshot-dom\")\n : import(\"./screenshot\")\n ).catch(() => {});\n if (this.config.annotate) import(\"./annotate\").catch(() => {});\n }\n\n /** Run fn when the main thread is idle; cancelled by destroy(). */\n private schedulePrewarm(fn: () => void): void {\n const w = window as unknown as {\n requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;\n };\n const guarded = (): void => {\n if (!this.destroyed) fn();\n };\n if (typeof w.requestIdleCallback === \"function\") {\n this.prewarmIsTimeout = false;\n this.prewarmHandle = w.requestIdleCallback(guarded, { timeout: 2000 });\n } else {\n this.prewarmIsTimeout = true;\n this.prewarmHandle = window.setTimeout(guarded, 1200);\n }\n }\n\n /**\n * Watch for the shadow hosts being removed from the DOM by framework\n * client-side navigation (e.g. Astro View Transitions swapping <body>\n * contents, or any SPA router that replaces DOM subtrees). If the host\n * is disconnected and destroy() wasn't called, re-append it.\n *\n * Also hooks into Astro's `astro:before-swap` when available, which\n * lets us carry elements into the new document before the swap happens\n * (avoids a flash of the widget disappearing and reappearing).\n */\n private observeDom(): void {\n // Astro View Transitions: carry hosts into the new document pre-swap\n this.boundBeforeSwap = (e: Event) => {\n const newDoc = (e as any).newDocument as Document | undefined;\n if (!newDoc || this.destroyed) return;\n if (this.shadowHost) newDoc.body.appendChild(this.shadowHost);\n if (this.overlayShadowHost) newDoc.body.appendChild(this.overlayShadowHost);\n };\n document.addEventListener(\"astro:before-swap\", this.boundBeforeSwap);\n\n // Generic fallback: MutationObserver to catch any framework removing our hosts\n this.domObserver = new MutationObserver(() => {\n if (this.destroyed) return;\n if (this.shadowHost && !this.shadowHost.isConnected) {\n document.body.appendChild(this.shadowHost);\n }\n if (this.overlayShadowHost && !this.overlayShadowHost.isConnected) {\n document.body.appendChild(this.overlayShadowHost);\n }\n });\n this.domObserver.observe(document.body, { childList: true });\n }\n\n private checkMobile(): void {\n this.isMobile = isMobileViewport();\n }\n\n private handleResize(): void {\n this.checkMobile();\n }\n\n private createEmbed(): void {\n // Create the shadow host element\n this.shadowHost = document.createElement(\"div\");\n this.shadowHost.setAttribute(\"data-qaid-embed\", \"\");\n this.shadowHost.style.position = \"static\";\n this.shadowHost.style.display = \"contents\";\n\n // Check if user provided a container selector\n let userContainer: HTMLElement | null = null;\n if (this.config.container) {\n userContainer = document.querySelector(this.config.container);\n }\n\n if (userContainer) {\n userContainer.appendChild(this.shadowHost);\n this.isUserProvidedContainer = true;\n // Container mode never needs a dismiss button\n this.config.hideDismiss = true;\n } else {\n // Set shadow host to fixed positioning for auto-created containers\n this.shadowHost.style.position = \"fixed\";\n this.shadowHost.style.display = \"block\";\n this.shadowHost.style.inset = \"0\";\n this.shadowHost.style.pointerEvents = \"none\";\n this.shadowHost.style.zIndex = String(this.config.zIndex);\n document.body.appendChild(this.shadowHost);\n }\n\n // Attach shadow root\n this.shadowRoot = this.shadowHost.attachShadow({ mode: \"open\" });\n\n // Inject base styles into shadow root\n const baseStyle = document.createElement(\"style\");\n baseStyle.textContent = getEmbedStyles();\n this.shadowRoot.appendChild(baseStyle);\n\n // Inject theme CSS if provided\n if (this.config.css) {\n const themeStyle = document.createElement(\"style\");\n themeStyle.textContent = this.config.css;\n this.shadowRoot.appendChild(themeStyle);\n }\n\n // Create buttons container inside shadow root\n const dirClass = this.config.direction === \"vertical\" ? \"qaid-vertical\" : \"\";\n if (this.isUserProvidedContainer) {\n this.buttonsContainer = document.createElement(\"div\");\n this.buttonsContainer.className = `qaid-buttons qaid-${this.config.position} ${dirClass}`.trim();\n if (this.config.incognito) {\n this.buttonsContainer.classList.add(\"qaid-incognito\");\n }\n } else {\n this.buttonsContainer = document.createElement(\"div\");\n this.buttonsContainer.className = `qaid-buttons qaid-auto-container qaid-${this.config.position} ${dirClass}${this.config.incognito ? \" qaid-incognito\" : \"\"}`.trim();\n this.buttonsContainer.style.pointerEvents = \"auto\";\n // Apply custom offset\n const { x: offsetX, y: offsetY } = this.config.offset;\n if (this.config.position.includes(\"right\")) {\n this.buttonsContainer.style.right = `${offsetX}px`;\n } else {\n this.buttonsContainer.style.left = `${offsetX}px`;\n }\n if (this.config.position.includes(\"bottom\")) {\n this.buttonsContainer.style.bottom = `${offsetY}px`;\n } else {\n this.buttonsContainer.style.top = `${offsetY}px`;\n }\n }\n\n // Apply per-instance CSS variables to buttons container\n this.applyVars(this.buttonsContainer);\n this.shadowRoot.appendChild(this.buttonsContainer);\n\n // Track input modality so a keyboard-activated control (Enter/Space) can be\n // distinguished from a pointer click. A keyboard-fired synthetic click has\n // clientX/clientY===0, which would break elementFromPoint targeting.\n this.buttonsContainer.addEventListener(\"keydown\", (ev) => {\n if (ev.key === \"Enter\" || ev.key === \" \" || ev.key === \"Spacebar\") {\n this.keyboardActivation = true;\n }\n });\n this.buttonsContainer.addEventListener(\"mousedown\", () => {\n this.keyboardActivation = false;\n });\n this.buttonsContainer.addEventListener(\"pointerdown\", () => {\n this.keyboardActivation = false;\n });\n\n // Determine button classes\n const useCustomClass = !!this.config.buttonClass;\n const btnBaseClass = useCustomClass\n ? `qaid-btn-structural ${this.config.buttonClass}`\n : \"qaid-btn\";\n\n // Determine tooltip text\n const defaultTooltip = \"Feedback for us?\";\n const tooltipText = this.config.text.tooltip || defaultTooltip;\n\n // Create shared tooltip element (inside shadow root)\n const tooltip = document.createElement(\"div\");\n tooltip.className = \"qaid-tooltip-text\";\n tooltip.textContent = tooltipText;\n this.applyVars(tooltip);\n this.shadowRoot.appendChild(tooltip);\n this.tooltipElement = tooltip;\n\n // Single sentiment-free \"Feedback\" button, or the thumbs up/down pair.\n if (this.config.singleButton) {\n const fbWrapper = document.createElement(\"div\");\n fbWrapper.className = \"qaid-tooltip-wrapper\";\n\n const fbBtn = document.createElement(\"button\");\n fbBtn.type = \"button\";\n fbBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-feedback` : \"qaid-btn qaid-btn-feedback\";\n fbBtn.setAttribute(\"aria-label\", this.config.text.feedbackLabel);\n fbBtn.innerHTML = this.config.feedbackIcon || FEEDBACK_ICON;\n fbBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"neutral\", e.currentTarget as HTMLElement, e));\n fbBtn.addEventListener(\"mouseenter\", () => {\n this.prewarmTargeting();\n this.showTooltip(fbBtn);\n });\n fbBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n fbWrapper.appendChild(fbBtn);\n\n this.buttonsContainer.appendChild(fbWrapper);\n } else if (!this.config.hideThumbs) {\n // Thumbs up button\n const upWrapper = document.createElement(\"div\");\n upWrapper.className = \"qaid-tooltip-wrapper\";\n\n const upBtn = document.createElement(\"button\");\n upBtn.type = \"button\";\n upBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-up` : \"qaid-btn qaid-btn-up\";\n upBtn.setAttribute(\"aria-label\", this.config.text.positiveLabel);\n upBtn.innerHTML = this.config.positiveIcon || THUMBS_UP_ICON;\n upBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"up\", e.currentTarget as HTMLElement, e));\n upBtn.addEventListener(\"mouseenter\", () => {\n this.prewarmTargeting();\n this.showTooltip(upBtn);\n });\n upBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n upWrapper.appendChild(upBtn);\n\n // Thumbs down button\n const downWrapper = document.createElement(\"div\");\n downWrapper.className = \"qaid-tooltip-wrapper\";\n\n const downBtn = document.createElement(\"button\");\n downBtn.type = \"button\";\n downBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-down` : \"qaid-btn qaid-btn-down\";\n downBtn.setAttribute(\"aria-label\", this.config.text.negativeLabel);\n downBtn.innerHTML = this.config.negativeIcon || THUMBS_DOWN_ICON;\n downBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"down\", e.currentTarget as HTMLElement, e));\n downBtn.addEventListener(\"mouseenter\", () => {\n this.prewarmTargeting();\n this.showTooltip(downBtn);\n });\n downBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n downWrapper.appendChild(downBtn);\n\n this.buttonsContainer.appendChild(upWrapper);\n this.buttonsContainer.appendChild(downWrapper);\n }\n\n // Record button (only when captureVideo is true and browser supports it)\n if (this.config.captureVideo && this.videoSupported()) {\n const recordWrapper = document.createElement(\"div\");\n recordWrapper.className = \"qaid-tooltip-wrapper\";\n\n const recordBtn = document.createElement(\"button\");\n recordBtn.type = \"button\";\n recordBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-record` : \"qaid-btn qaid-btn-record\";\n recordBtn.setAttribute(\"aria-label\", this.config.text.recordLabel);\n recordBtn.innerHTML = this.config.recordIcon || RECORD_ICON;\n recordBtn.addEventListener(\"click\", () => {\n void this.ensureRecording().then((rec) =>\n this.config.videoOptions.redaction ? rec.startPicking() : rec.startRecording()\n );\n });\n // Warm the video chunk on hover/focus/touch — the ~200ms before a click —\n // so recording starts without waiting on the chunk fetch.\n const warmVideo = (): void => this.prewarmVideo();\n recordBtn.addEventListener(\"mouseenter\", () => {\n warmVideo();\n this.showTooltip(recordBtn);\n });\n recordBtn.addEventListener(\"focus\", warmVideo);\n recordBtn.addEventListener(\"touchstart\", warmVideo, { passive: true });\n recordBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n recordWrapper.appendChild(recordBtn);\n\n this.buttonsContainer.appendChild(recordWrapper);\n }\n\n // Dismiss (X) button — only in body/fixed mode (hidden for container embeds)\n if (!this.config.hideDismiss) {\n this.dismissBtn = document.createElement(\"button\");\n this.dismissBtn.type = \"button\";\n this.dismissBtn.className = \"qaid-dismiss-btn\";\n this.dismissBtn.setAttribute(\"aria-label\", this.config.text.dismissLabel);\n this.dismissBtn.title = this.config.text.dismissLabel;\n this.dismissBtn.innerHTML = `<svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 2l8 8M10 2l-8 8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`;\n this.dismissBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.handleDismiss();\n });\n // Insert as first child so it sits to the left of thumb buttons\n this.buttonsContainer.insertBefore(this.dismissBtn, this.buttonsContainer.firstChild);\n\n // If previously dismissed via localStorage, start fully hidden so it\n // doesn't flash back on hover — matching the dismiss behavior.\n if (this._startDismissed) {\n this.buttonsContainer.classList.add(\"qaid-dismissed\");\n }\n }\n }\n\n /**\n * Lazily create a separate overlay shadow host on document.body.\n * This host contains all full-page elements (targeting overlay, marker,\n * backdrop, modal, recording indicator, video preview) so they escape\n * clip-path / transform containing blocks in user containers.\n */\n private ensureOverlayHost(): ShadowRoot {\n if (this.overlayShadowRoot) return this.overlayShadowRoot;\n\n this.overlayShadowHost = document.createElement(\"div\");\n this.overlayShadowHost.setAttribute(\"data-qaid-embed-overlay\", \"\");\n this.overlayShadowHost.style.position = \"fixed\";\n this.overlayShadowHost.style.inset = \"0\";\n this.overlayShadowHost.style.pointerEvents = \"none\";\n this.overlayShadowHost.style.zIndex = String(this.config.zIndex);\n document.body.appendChild(this.overlayShadowHost);\n\n this.overlayShadowRoot = this.overlayShadowHost.attachShadow({ mode: \"open\" });\n\n // Inject base styles into overlay shadow root\n const baseStyle = document.createElement(\"style\");\n baseStyle.textContent = getEmbedStyles();\n this.overlayShadowRoot.appendChild(baseStyle);\n\n // Inject theme CSS if provided\n if (this.config.css) {\n const themeStyle = document.createElement(\"style\");\n themeStyle.textContent = this.config.css;\n this.overlayShadowRoot.appendChild(themeStyle);\n }\n\n return this.overlayShadowRoot;\n }\n\n private tooltipElement: HTMLElement | null = null;\n\n private showTooltip(anchor: HTMLElement): void {\n const tooltip = this.tooltipElement!;\n\n // Make visible to measure\n tooltip.style.visibility = \"hidden\";\n tooltip.classList.add(\"qaid-tooltip-visible\");\n\n const anchorRect = anchor.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const { top, left } = calculateTooltipPosition(\n anchorRect,\n tooltipRect,\n { width: window.innerWidth, height: window.innerHeight }\n );\n\n tooltip.style.top = `${top}px`;\n tooltip.style.left = `${left}px`;\n tooltip.style.visibility = \"visible\";\n }\n\n private hideTooltip(): void {\n this.tooltipElement!.classList.remove(\"qaid-tooltip-visible\");\n }\n\n private handleDismiss(): void {\n // Fully remove the widget. It does not linger as a hover-reveal ghost\n // (that's `qaid-incognito`); once dismissed it stays gone until the\n // visitor clears the stored preference.\n this.buttonsContainer!.classList.add(\"qaid-dismissed\");\n setHiddenByUser(this.config.apiKey, true);\n }\n\n private handleThumbClick(type: FeedbackType, buttonEl: HTMLElement, e: MouseEvent): void {\n // If user clicks a thumb while in incognito mode, they want it back.\n // (Dismissed widgets are `display:none`, so this can't fire for them.)\n if (this.buttonsContainer?.classList.contains(\"qaid-incognito\")) {\n this.buttonsContainer.classList.remove(\"qaid-incognito\");\n setHiddenByUser(this.config.apiKey, false);\n }\n // A message modal follows this feedback flow — warm its chunk now.\n this.prewarmModal();\n // \"annotate\" mode and skipTargeting both bypass element targeting and go\n // straight to the (screenshot →) modal flow.\n if (this.config.skipTargeting || this.config.feedbackMode === \"annotate\") {\n this.submitDirectFeedback(type, buttonEl);\n } else {\n // Expose targeting on/off state on the trigger\n this.activeThumbBtn = buttonEl;\n buttonEl.setAttribute(\"aria-pressed\", \"true\");\n // A keyboard-activated <button> fires a synthetic click whose clientX/\n // clientY are (0,0). Route those through the keyboard path instead of\n // elementFromPoint(0,0). `keyboardActivation` is set by a preceding\n // Enter/Space keydown and cleared by any pointerdown/mousedown.\n const keyboard = this.keyboardActivation;\n this.keyboardActivation = false;\n // Load the targeting chunk (pre-warmed on hover) then start it.\n void this.ensureTargeting().then((t) => {\n if (this.destroyed) return;\n if (keyboard) t.startKeyboard(type);\n else t.startPointer(type, e);\n });\n }\n }\n\n private submitDirectFeedback(type: FeedbackType, buttonEl: HTMLElement): void {\n this.feedbackData.feedbackType = type;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n\n // Use the button's position for modal placement\n const rect = buttonEl.getBoundingClientRect();\n this.selectedBounds = {\n x: rect.left,\n y: rect.top,\n width: rect.width,\n height: rect.height,\n clickX: rect.left + rect.width / 2,\n clickY: rect.top + rect.height / 2,\n visible: false,\n };\n this.submitFeedback();\n }\n\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (e.key === \"Escape\") {\n // The recording controller owns Escape while recording / in preview /\n // picking; let it consume the key first.\n if (this.recording?.handleEscape()) return;\n if (this.state === \"TARGETING\") {\n this.targeting?.cancel();\n } else if (this.state === \"MODAL_OPEN\") {\n this.modal?.close();\n }\n }\n }\n\n\n /** Whether to capture the screenshot with the DOM/canvas method (html2canvas)\n * instead of the permission-based Screen Capture API. Explicit \"dom\" wins;\n * otherwise DOM is used on touch devices to avoid the getDisplayMedia prompt. */\n private shouldCaptureViaDom(): boolean {\n return this.config.screenshotMethod === \"dom\" || isTouchPrimaryDevice();\n }\n\n /**\n * Open the full-screen annotation editor over the captured screenshot,\n * reusing the overlay shadow host and the shared dialog a11y helpers.\n * Resolves with the composited WebP data URL, or null when the user skips\n * (caller keeps the original). Pointer events on the overlay host are\n * enabled while the editor is open and restored on close.\n */\n private async openAnnotationEditor(screenshot: string): Promise<string | null> {\n const root = this.ensureOverlayHost();\n const host = this.overlayShadowHost!;\n const prevPointerEvents = host.style.pointerEvents;\n host.style.pointerEvents = \"auto\";\n try {\n const { openAnnotationEditor } = await import(\"./annotate\");\n return await openAnnotationEditor({\n dataUrl: screenshot,\n root,\n quality: this.config.screenshotOptions.quality,\n color: this.config.annotationColor,\n palette: this.config.annotationPalette,\n applyVars: (el) => this.applyVars(el),\n announce: (msg, assertive) => this.announceMsg(msg, assertive),\n openDialog: (container, opts) => this.openDialogA11y(container, opts),\n closeDialog: () => this.closeDialogA11y(),\n });\n } catch (err) {\n // The annotate chunk failed to load / the editor threw — keep the\n // original screenshot rather than aborting the whole submission.\n console.error(\"Annotation editor failed to open:\", err);\n return null;\n } finally {\n host.style.pointerEvents = prevPointerEvents;\n }\n }\n\n private async submitFeedback(): Promise<void> {\n // Capture screenshot if enabled (server will gate by plan). \"annotate\" mode\n // always captures one to mark up, even without captureScreenshot set.\n let screenshot: string | null = null;\n const wantScreenshot =\n this.config.captureScreenshot || this.config.feedbackMode === \"annotate\";\n if (wantScreenshot) {\n // Loaded on demand so visitors who never submit a screenshot never\n // download the capture + annotation code. A capture failure (denied\n // permission, chunk load error) must never abort the feedback — we just\n // submit without a screenshot so the user still reaches the modal.\n try {\n if (this.shouldCaptureViaDom()) {\n const { captureDomScreenshot } = await import(\"./screenshot-dom\");\n screenshot = await captureDomScreenshot(this.config.screenshotOptions);\n } else {\n const { captureScreenshot } = await import(\"./screenshot\");\n screenshot = await captureScreenshot(this.config.screenshotOptions);\n }\n if (screenshot) this.announceMsg(\"Screenshot captured\");\n } catch (err) {\n console.error(\"Screenshot capture failed:\", err);\n screenshot = null;\n }\n }\n\n // Let the user mark up / redact the screenshot before it is submitted.\n // The message modal only opens after the POST below, so the composited\n // result lands in the same request — no backend change needed. On skip\n // the editor resolves null and the original screenshot is kept.\n if (screenshot && this.config.annotate) {\n screenshot = (await this.openAnnotationEditor(screenshot)) ?? screenshot;\n }\n\n // Include element bounds for server-side screenshot fallback\n // Only include if we have an element selector (element was targeted)\n const elementBounds = this.feedbackData.elementSelector\n ? {\n x: this.selectedBounds.x,\n y: this.selectedBounds.y,\n width: this.selectedBounds.width,\n height: this.selectedBounds.height,\n }\n : null;\n\n const payload: FeedbackPayload = {\n feedbackType: this.feedbackData.feedbackType!,\n pageUrl: window.location.href,\n apiKey: this.config.apiKey || undefined,\n elementSelector: this.feedbackData.elementSelector,\n elementText: this.feedbackData.elementText,\n consoleErrors: [...this.feedbackData.consoleErrors],\n screenWidth: window.innerWidth,\n screenHeight: window.innerHeight,\n clickX: this.selectedBounds.clickX,\n clickY: this.selectedBounds.clickY,\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n screenshot,\n visitorId: this.visitorId,\n // Include element bounds and user agent for server-side screenshot fallback\n elementBounds,\n userAgent: navigator.userAgent,\n };\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (response.ok) {\n const data: FeedbackResponse = await response.json();\n this.feedbackId = data.id;\n this.announceMsg(\"Feedback sent\");\n }\n } catch (error) {\n console.error(\"Failed to submit feedback:\", error);\n this.announceMsg(\"Failed to send feedback\", true);\n }\n\n // If this button is linked to a quest, launch it in place of the\n // message box. The feedback record already exists, so on success we\n // just tear down the targeting UI and hand off. On any load failure we\n // fall through to the classic message box so the user can still leave\n // an optional message.\n const type = this.feedbackData.feedbackType;\n // Quest links are keyed by up/down/video; neutral single-button feedback\n // has no quest mapping, so it always falls through to the message box.\n if (type && type !== \"neutral\" && (await this.tryLaunchQuest(type, this.feedbackId))) {\n this.resetFeedbackUi();\n return;\n }\n\n // Always show the message modal as the terminal state (even if the API\n // failed — useful for demos).\n this.state = \"MODAL_OPEN\";\n try {\n // The modal chunk loads async; bail if the embed was destroyed meanwhile,\n // so a raced modal never opens (and never leaks a listener) post-destroy.\n const modal = await this.ensureModal();\n if (this.destroyed) return;\n modal.open();\n\n // Enable pointer events on overlay host for modal\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n } catch (err) {\n // The modal chunk failed to load/open. Never leave the user stuck in a\n // re-targetable limbo: thank them and reset to IDLE so a fresh click\n // starts a clean flow (the feedback itself was already POSTed above).\n console.error(\"Failed to open the feedback modal:\", err);\n this.announceMsg(\"Thank you for your feedback!\", true);\n this.resetFeedbackUi();\n }\n }\n\n /**\n * Quest id linked to `type`, or \"\" when quest launching is disabled\n * (no `base`) or this button has no quest configured.\n */\n private questIdFor(type: \"up\" | \"down\" | \"video\"): string {\n const q = this.config.quests;\n return q.base ? q[type] || \"\" : \"\";\n }\n\n /**\n * Launch the quest linked to `type`, if any. Resolves `true` when a quest\n * was configured and the widget launched; `false` when no quest is\n * configured or the widget failed to load (caller falls back to its\n * normal UI). The created feedback record id is passed through so the\n * quest response can be joined back to it server-side.\n */\n private async tryLaunchQuest(\n type: \"up\" | \"down\" | \"video\",\n feedbackId: number | string | null,\n ): Promise<boolean> {\n const questId = this.questIdFor(type);\n if (!questId) return false;\n try {\n this.activeQuest?.destroy();\n this.activeQuest = await launchQuest({\n questId,\n base: this.config.quests.base,\n apiKey: this.config.quests.apiKey || undefined,\n moduleUrl: this.config.quests.moduleUrl,\n feedbackId,\n onClose: () => {\n this.activeQuest = null;\n },\n });\n return true;\n } catch (error) {\n console.error(\"Failed to launch quest:\", error);\n this.activeQuest = null;\n return false;\n }\n }\n\n /**\n * Reset the thumbs targeting/marker UI back to idle without opening or\n * closing the message modal. Shared by closeModal() and the quest-launch\n * path (which bypasses the modal entirely).\n */\n private resetFeedbackUi(): void {\n this.targeting?.hideMarker();\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"none\";\n }\n this.state = \"IDLE\";\n this.feedbackId = null;\n this.feedbackData.feedbackType = null;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n this.selectedBounds.visible = false;\n }\n\n\n /**\n * Lazily load and instantiate the modal controller. The message modal lives\n * in a separate chunk, fetched the first time it opens (and pre-warmed while\n * the user targets an element — see prewarmModal).\n */\n private async ensureModal(): Promise<ModalController> {\n if (!this.modal) {\n const { ModalController } = await import(\"./modal\");\n this.modal = new ModalController(this.makeModalHost());\n }\n return this.modal;\n }\n\n /** Narrow view of the embed the modal controller talks back through. */\n private makeModalHost(): ModalHost {\n const self = this;\n return {\n get config() {\n return self.config;\n },\n get uid() {\n return self.uid;\n },\n get isMobile() {\n return self.isMobile;\n },\n get state() {\n return self.state;\n },\n get boundKeyDown() {\n return self.boundKeyDown;\n },\n get feedbackData() {\n return self.feedbackData;\n },\n get selectedBounds() {\n return self.selectedBounds;\n },\n get feedbackId() {\n return self.feedbackId;\n },\n setFeedbackId: (id) => {\n self.feedbackId = id;\n },\n ensureOverlayHost: () => self.ensureOverlayHost(),\n applyVars: (el) => self.applyVars(el),\n announceMsg: (message, assertive) => self.announceMsg(message, assertive),\n openDialogA11y: (container, opts) => self.openDialogA11y(container, opts),\n closeDialogA11y: () => self.closeDialogA11y(),\n resetFeedbackUi: () => self.resetFeedbackUi(),\n };\n }\n\n /** Warm the modal chunk while the user is targeting, so it opens instantly. */\n private prewarmModal(): void {\n if (this.modalPrewarmed) return;\n this.modalPrewarmed = true;\n import(\"./modal\").catch(() => {});\n }\n\n /**\n * Lazily load and instantiate the targeting controller. The subsystem (with\n * element-selector) lives in a separate chunk, fetched the first time the\n * user targets — pre-warmed on thumb-button hover (see prewarmTargeting).\n */\n private async ensureTargeting(): Promise<TargetingController> {\n if (!this.targeting) {\n const { TargetingController } = await import(\"./targeting\");\n this.targeting = new TargetingController(this.makeTargetingHost());\n }\n return this.targeting;\n }\n\n /** Narrow view of the embed the targeting controller talks back through. */\n private makeTargetingHost(): TargetingHost {\n const self = this;\n return {\n get config() {\n return self.config;\n },\n get cssVars() {\n return self.cssVars;\n },\n get state() {\n return self.state;\n },\n get shadowHost() {\n return self.shadowHost;\n },\n get overlayShadowHost() {\n return self.overlayShadowHost;\n },\n get boundKeyDown() {\n return self.boundKeyDown;\n },\n get feedbackData() {\n return self.feedbackData;\n },\n get selectedBounds() {\n return self.selectedBounds;\n },\n setState: (state) => {\n self.state = state;\n },\n ensureOverlayHost: () => self.ensureOverlayHost(),\n applyVars: (el) => self.applyVars(el),\n announceMsg: (message, assertive) => self.announceMsg(message, assertive),\n clearActiveThumb: () => self.clearActiveThumb(),\n submitFeedback: () => self.submitFeedback(),\n };\n }\n\n /** Warm the targeting chunk on thumb-button hover, before the click. */\n private prewarmTargeting(): void {\n if (this.targetingPrewarmed || this.config.skipTargeting) return;\n this.targetingPrewarmed = true;\n import(\"./targeting\").catch(() => {});\n }\n\n // ==================== Video Recording ====================\n\n /**\n * Cheap synchronous capability check so the record button can render without\n * pulling in the (lazily-loaded) video subsystem. Mirrors\n * isVideoRecordingSupported() in video-capture.ts.\n */\n private videoSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n !!navigator.mediaDevices &&\n typeof navigator.mediaDevices.getDisplayMedia === \"function\" &&\n typeof MediaRecorder !== \"undefined\"\n );\n }\n\n\n /**\n * Lazily load and instantiate the recording controller. The whole recording\n * subsystem lives in a separate chunk, fetched only the first time the record\n * button is used (and pre-warmed on hover — see prewarmVideo).\n */\n private async ensureRecording(): Promise<RecordingController> {\n if (!this.recording) {\n const { RecordingController } = await import(\"./recording\");\n this.recording = new RecordingController(this.makeRecordingHost());\n }\n return this.recording;\n }\n\n /** Narrow view of the embed the recording controller talks back through. */\n private makeRecordingHost(): RecordingHost {\n const self = this;\n return {\n get config() {\n return self.config;\n },\n get visitorId() {\n return self.visitorId;\n },\n get uid() {\n return self.uid;\n },\n get overlayShadowHost() {\n return self.overlayShadowHost;\n },\n get consoleCapture() {\n return self.consoleCapture;\n },\n get boundKeyDown() {\n return self.boundKeyDown;\n },\n get state() {\n return self.state;\n },\n setState: (state) => {\n self.state = state;\n },\n ensureOverlayHost: () => self.ensureOverlayHost(),\n applyVars: (el) => self.applyVars(el),\n announceMsg: (message, assertive) => self.announceMsg(message, assertive),\n openDialogA11y: (container, opts) => self.openDialogA11y(container, opts),\n closeDialogA11y: () => self.closeDialogA11y(),\n setButtonsDisabled: (disabled) => self.setButtonsDisabled(disabled),\n tryLaunchQuest: (type, feedbackId) => self.tryLaunchQuest(type, feedbackId),\n };\n }\n\n private setButtonsDisabled(disabled: boolean): void {\n if (!this.buttonsContainer) return;\n const buttons = this.buttonsContainer.querySelectorAll<HTMLButtonElement>(\"button.qaid-btn, button.qaid-btn-structural\");\n buttons.forEach((btn) => {\n if (disabled) {\n // Don't disable the record button itself (it has its own state)\n if (!btn.classList.contains(\"qaid-btn-record\")) {\n btn.disabled = true;\n btn.style.opacity = \"0.5\";\n }\n } else {\n btn.disabled = false;\n btn.style.opacity = \"\";\n }\n });\n }\n\n /**\n * Destroy the embed and clean up all resources\n */\n public destroy(): void {\n // Flag as destroyed so the DOM observer doesn't re-attach hosts\n this.destroyed = true;\n\n // Disconnect DOM persistence observers\n if (this.domObserver) {\n this.domObserver.disconnect();\n this.domObserver = null;\n }\n if (this.boundBeforeSwap) {\n document.removeEventListener(\"astro:before-swap\", this.boundBeforeSwap);\n this.boundBeforeSwap = null;\n }\n\n // Tear down the targeting subsystem + release any open dialog focus state\n this.targeting?.destroy();\n this.targeting = null;\n this.clearActiveThumb();\n this.closeDialogA11y();\n\n // Cancel any pending idle preload\n if (this.prewarmHandle !== null) {\n const w = window as unknown as { cancelIdleCallback?: (h: number) => void };\n if (this.prewarmIsTimeout) clearTimeout(this.prewarmHandle);\n else w.cancelIdleCallback?.(this.prewarmHandle);\n this.prewarmHandle = null;\n }\n\n // Tear down the recording subsystem (recorder, picker, preview) if loaded.\n this.recording?.destroy();\n this.recording = null;\n // Tear down the message modal if it was loaded/open.\n this.modal?.destroy();\n this.modal = null;\n\n // Tear down any quest launched from a button\n this.activeQuest?.destroy();\n this.activeQuest = null;\n\n // Restore console.error\n if (this.consoleCapture) {\n this.consoleCapture.restore();\n this.consoleCapture = null;\n }\n\n // Remove event listeners\n window.removeEventListener(\"resize\", this.boundResize);\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n\n // Remove shadow hosts from DOM (this removes shadow roots and all their contents)\n if (this.shadowHost) {\n this.shadowHost.remove();\n this.shadowHost = null;\n this.shadowRoot = null;\n }\n if (this.overlayShadowHost) {\n this.overlayShadowHost.remove();\n this.overlayShadowHost = null;\n this.overlayShadowRoot = null;\n }\n this.buttonsContainer = null;\n this.dismissBtn = null;\n this.tooltipElement = null;\n\n // Remove light DOM styles (reference counted)\n removeStyles();\n }\n}\n","/**\n * Auto-initialization from a `<script>` tag.\n *\n * Shared by both delivery entries: the legacy UMD/API entry (`index.ts`) and\n * the minimal ESM loader (`loader.ts`). Kept out of `index.ts` so the loader\n * can auto-init without pulling in `index.ts`'s feature re-exports — that\n * re-export graph is what anchors screenshot/video/annotate into the main\n * chunk and defeats code-splitting.\n */\n\nimport { QaidFeedback } from \"./embed\";\nimport type { FeedbackConfig } from \"./types\";\n\n/**\n * Find theme CSS from an element matched by a CSS selector.\n * Used by data-css-selector attribute and JSON cssSelector property.\n */\nfunction findCssFromSelector(selector: string): string {\n const el = document.querySelector(selector);\n return el?.textContent?.trim() ?? \"\";\n}\n\n/**\n * Parse JSON config from a separate script tag with type=\"application/json\"\n * Looks for: <script type=\"application/json\" data-feedback-config>{ ... }</script>\n */\nfunction parseJsonConfig(): Partial<FeedbackConfig> | null {\n const configScript = document.querySelector(\n 'script[type=\"application/json\"][data-feedback-config]'\n );\n if (!configScript) return null;\n\n const textContent = configScript.textContent?.trim();\n if (!textContent) return null;\n\n try {\n const parsed = JSON.parse(textContent);\n // Resolve cssSelector → css\n if (parsed.cssSelector && !parsed.css) {\n parsed.css = findCssFromSelector(parsed.cssSelector);\n delete parsed.cssSelector;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse config from data-* attributes (backward compatibility)\n */\n/** Exported for tests: index.ts does not re-export it, so this is not public API. */\nexport function parseDataAttributes(script: HTMLScriptElement): Partial<FeedbackConfig> | null {\n const endpoint = script.getAttribute(\"data-endpoint\");\n if (!endpoint) return null;\n\n const position = script.getAttribute(\"data-position\") as\n | \"bottom-right\"\n | \"bottom-left\"\n | \"top-right\"\n | \"top-left\"\n | null;\n const zIndex = script.getAttribute(\"data-zindex\");\n const positiveColor = script.getAttribute(\"data-positive-color\");\n const negativeColor = script.getAttribute(\"data-negative-color\");\n const markerColor = script.getAttribute(\"data-marker-color\");\n const container = script.getAttribute(\"data-container\");\n const buttonClass = script.getAttribute(\"data-button-class\");\n const skipTargeting = script.getAttribute(\"data-skip-targeting\");\n const hideConfirmation = script.getAttribute(\"data-hide-confirmation\");\n const singleButton = script.getAttribute(\"data-single-button\");\n const feedbackMode = script.getAttribute(\"data-feedback-mode\") as\n | \"target\"\n | \"annotate\"\n | null;\n const incognito = script.getAttribute(\"data-incognito\");\n const buttonSize = script.getAttribute(\"data-button-size\") as\n | \"small\"\n | \"medium\"\n | \"large\"\n | null;\n const offsetX = script.getAttribute(\"data-offset-x\");\n const offsetY = script.getAttribute(\"data-offset-y\");\n const modalWidth = script.getAttribute(\"data-modal-width\");\n const backdropOpacity = script.getAttribute(\"data-backdrop-opacity\");\n const fontFamily = script.getAttribute(\"data-font-family\");\n const fontSize = script.getAttribute(\"data-font-size\");\n const tooltip = script.getAttribute(\"data-tooltip\");\n const modalTitle = script.getAttribute(\"data-modal-title\");\n const confirmationTitle = script.getAttribute(\"data-confirmation-title\");\n const confirmationMessage = script.getAttribute(\"data-confirmation-message\");\n const confirmationClose = script.getAttribute(\"data-confirmation-close\");\n const errorTitle = script.getAttribute(\"data-error-title\");\n const errorMessage = script.getAttribute(\"data-error-message\");\n const modalSubtitle = script.getAttribute(\"data-modal-subtitle\");\n const placeholder = script.getAttribute(\"data-placeholder\");\n const submitButton = script.getAttribute(\"data-submit-button\");\n const skipButton = script.getAttribute(\"data-skip-button\");\n const positiveIcon = script.getAttribute(\"data-positive-icon\");\n const negativeIcon = script.getAttribute(\"data-negative-icon\");\n const feedbackIcon = script.getAttribute(\"data-feedback-icon\");\n const feedbackLabel = script.getAttribute(\"data-feedback-label\");\n const apiKey = script.getAttribute(\"data-api-key\");\n const captureScreenshot = script.getAttribute(\"data-capture-screenshot\");\n const annotate = script.getAttribute(\"data-annotate\");\n const annotationColor = script.getAttribute(\"data-annotation-color\");\n const screenshotQuality = script.getAttribute(\"data-screenshot-quality\");\n const screenshotMaxWidth = script.getAttribute(\"data-screenshot-max-width\");\n const screenshotMaxHeight = script.getAttribute(\"data-screenshot-max-height\");\n const captureVideo = script.getAttribute(\"data-capture-video\");\n const redaction = script.getAttribute(\"data-video-redaction\");\n const hideThumbs = script.getAttribute(\"data-hide-thumbs\");\n const hideDismiss = script.getAttribute(\"data-hide-dismiss\");\n const videoMaxDuration = script.getAttribute(\"data-video-max-duration\");\n const screenshotMethod = script.getAttribute(\"data-screenshot-method\") as \"dom\" | \"permission\" | null;\n const direction = script.getAttribute(\"data-direction\") as \"horizontal\" | \"vertical\" | null;\n const cssSelector = script.getAttribute(\"data-css-selector\");\n const questBase = script.getAttribute(\"data-quest-base\");\n const questUp = script.getAttribute(\"data-quest-up\");\n const questDown = script.getAttribute(\"data-quest-down\");\n const questVideo = script.getAttribute(\"data-quest-video\");\n const questApiKey = script.getAttribute(\"data-quest-api-key\");\n const questModuleUrl = script.getAttribute(\"data-quest-module-url\");\n\n return {\n endpoint,\n css: cssSelector ? findCssFromSelector(cssSelector) : undefined,\n apiKey: apiKey ?? undefined,\n captureScreenshot: captureScreenshot === \"true\" ? true : undefined,\n // Annotation is on by default; only an explicit \"false\" disables it.\n annotate: annotate === \"false\" ? false : undefined,\n annotationColor: annotationColor ?? undefined,\n screenshotOptions: (screenshotQuality || screenshotMaxWidth || screenshotMaxHeight) ? {\n quality: screenshotQuality ? parseFloat(screenshotQuality) : undefined,\n maxWidth: screenshotMaxWidth ? parseInt(screenshotMaxWidth, 10) : undefined,\n maxHeight: screenshotMaxHeight ? parseInt(screenshotMaxHeight, 10) : undefined,\n } : undefined,\n container: container ?? undefined,\n buttonClass: buttonClass ?? undefined,\n direction: direction ?? undefined,\n position: position ?? undefined,\n zIndex: zIndex ? parseInt(zIndex, 10) : undefined,\n skipTargeting: skipTargeting === \"true\" ? true : undefined,\n hideConfirmation: hideConfirmation === \"true\" ? true : undefined,\n singleButton: singleButton === \"true\" ? true : undefined,\n feedbackMode: feedbackMode ?? undefined,\n incognito: incognito === \"true\" ? true : undefined,\n buttonSize: buttonSize ?? undefined,\n offset: (offsetX || offsetY) ? {\n x: offsetX ? parseInt(offsetX, 10) : undefined,\n y: offsetY ? parseInt(offsetY, 10) : undefined,\n } : undefined,\n modalWidth: modalWidth ? parseInt(modalWidth, 10) : undefined,\n backdropOpacity: backdropOpacity ? parseFloat(backdropOpacity) : undefined,\n fontFamily: fontFamily ?? undefined,\n fontSize: fontSize ? parseInt(fontSize, 10) : undefined,\n colors: {\n positive: positiveColor ?? undefined,\n negative: negativeColor ?? undefined,\n marker: markerColor ?? undefined,\n },\n text: (tooltip || modalTitle || modalSubtitle || placeholder || submitButton || skipButton || feedbackLabel || confirmationTitle || confirmationMessage || confirmationClose || errorTitle || errorMessage) ? {\n tooltip: tooltip ?? undefined,\n modalTitle: modalTitle ?? undefined,\n modalSubtitle: modalSubtitle ?? undefined,\n placeholder: placeholder ?? undefined,\n submitButton: submitButton ?? undefined,\n skipButton: skipButton ?? undefined,\n feedbackLabel: feedbackLabel ?? undefined,\n confirmationTitle: confirmationTitle ?? undefined,\n confirmationMessage: confirmationMessage ?? undefined,\n confirmationClose: confirmationClose ?? undefined,\n errorTitle: errorTitle ?? undefined,\n errorMessage: errorMessage ?? undefined,\n } : undefined,\n positiveIcon: positiveIcon ?? undefined,\n negativeIcon: negativeIcon ?? undefined,\n feedbackIcon: feedbackIcon ?? undefined,\n screenshotMethod: screenshotMethod ?? undefined,\n captureVideo: captureVideo === \"true\" ? true : undefined,\n hideThumbs: hideThumbs === \"true\" ? true : undefined,\n hideDismiss: hideDismiss === \"true\" ? true : undefined,\n videoOptions: (videoMaxDuration || redaction) ? {\n maxDuration: videoMaxDuration ? parseInt(videoMaxDuration, 10) : undefined,\n redaction: redaction === \"true\" ? true : undefined,\n } : undefined,\n quests: questBase ? {\n base: questBase,\n up: questUp ?? undefined,\n down: questDown ?? undefined,\n video: questVideo ?? undefined,\n apiKey: questApiKey ?? undefined,\n moduleUrl: questModuleUrl ?? undefined,\n } : undefined,\n };\n}\n\n/**\n * Initialize a QaidFeedback instance from the current `<script>` tag's JSON\n * config block or data-* attributes. Safe to call in any environment — it\n * no-ops when there is no document or no endpoint config.\n */\nexport function autoInit(): void {\n if (typeof document === \"undefined\") return;\n\n const run = (): void => {\n // `document.currentScript` is null inside an ES module (the loader entry),\n // so fall back to locating the embed's own script tag by its data-endpoint.\n const script =\n (document.currentScript as HTMLScriptElement | null) ??\n document.querySelector<HTMLScriptElement>(\"script[data-endpoint]\");\n // Try JSON config from a separate script tag first, fall back to data-* attrs.\n const jsonConfig = parseJsonConfig();\n const dataConfig = script ? parseDataAttributes(script) : null;\n const config = jsonConfig ?? dataConfig;\n\n if (config?.endpoint) {\n new QaidFeedback(config as FeedbackConfig);\n }\n };\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", run);\n } else {\n run();\n }\n}\n","/**\n * Network capture utilities\n * Intercepts fetch() and XMLHttpRequest to capture 4xx/5xx errors\n */\n\nimport type { NetworkError } from \"./types\";\n\nconst MAX_ENTRIES = 20;\nconst MAX_BODY_SIZE = 4096; // 4KB\n\nexport interface NetworkCapture {\n errors: NetworkError[];\n restore: () => void;\n}\n\n/**\n * Truncate a body to max size. Callers must guard against null/undefined.\n */\nexport function truncateBody(body: unknown): string {\n const str = typeof body === \"string\" ? body : JSON.stringify(body);\n if (str.length > MAX_BODY_SIZE) {\n return str.slice(0, MAX_BODY_SIZE) + \"…[truncated]\";\n }\n return str;\n}\n\n/**\n * Add an error to the buffer, evicting oldest if at capacity\n */\nexport function addEntry(errors: NetworkError[], entry: NetworkError): void {\n if (errors.length >= MAX_ENTRIES) {\n errors.shift();\n }\n errors.push(entry);\n}\n\n/**\n * Read response body as text, with size limit\n */\nasync function safeReadBody(response: Response): Promise<string | undefined> {\n try {\n const text = await response.clone().text();\n return truncateBody(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Intercept fetch() and XMLHttpRequest to capture network errors (4xx/5xx)\n */\nexport function captureNetworkErrors(): NetworkCapture {\n const errors: NetworkError[] = [];\n const originalFetch = window.fetch;\n\n // Monkey-patch fetch\n window.fetch = async function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url;\n const method = init?.method ?? (typeof input === \"object\" && \"method\" in input ? input.method : \"GET\");\n let requestBody: string | undefined;\n if (init?.body) {\n requestBody = truncateBody(init.body);\n }\n\n const response = await originalFetch.apply(window, [input, init]);\n\n if (response.status >= 400) {\n const responseBody = await safeReadBody(response);\n addEntry(errors, {\n url,\n method: method.toUpperCase(),\n status: response.status,\n statusText: response.statusText,\n requestBody,\n responseBody,\n timestamp: Date.now(),\n });\n }\n\n return response;\n };\n\n // Monkey-patch XMLHttpRequest\n const originalXHROpen = XMLHttpRequest.prototype.open;\n const originalXHRSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (method: string, url: string | URL, ...rest: unknown[]) {\n (this as XMLHttpRequest & { _qaid_method: string; _qaid_url: string })._qaid_method = method;\n (this as XMLHttpRequest & { _qaid_url: string })._qaid_url = typeof url === \"string\" ? url : url.toString();\n return originalXHROpen.apply(this, [method, url, ...rest] as Parameters<typeof originalXHROpen>);\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const xhr = this as XMLHttpRequest & { _qaid_method: string; _qaid_url: string };\n const requestBody = body ? truncateBody(body) : undefined;\n\n xhr.addEventListener(\"load\", function () {\n if (xhr.status >= 400) {\n addEntry(errors, {\n url: xhr._qaid_url,\n method: xhr._qaid_method.toUpperCase(),\n status: xhr.status,\n statusText: xhr.statusText,\n requestBody,\n responseBody: truncateBody(xhr.responseText),\n timestamp: Date.now(),\n });\n }\n });\n\n return originalXHRSend.apply(this, [body]);\n };\n\n return {\n errors,\n restore: () => {\n window.fetch = originalFetch;\n XMLHttpRequest.prototype.open = originalXHROpen;\n XMLHttpRequest.prototype.send = originalXHRSend;\n },\n };\n}\n","/**\n * iOS/iPadOS screen-recording orientation fix.\n *\n * On iOS/iPadOS, `getDisplayMedia` + `MediaRecorder` writes a device-orientation\n * rotation into the recorded file (even for screen captures). Players disagree\n * on whether to honour it — the widget's own preview `<video>` renders it\n * sideways, and so does the dashboard. Re-encoding the *live* decoded frames\n * (which carry no container rotation) through a canvas produces a file whose\n * pixels are already upright with no rotation metadata, so it renders the same\n * everywhere.\n *\n * If the live frames themselves come back with swapped dimensions (portrait\n * frame while the viewport is landscape, or vice versa), we additionally rotate\n * the canvas 90° to match the viewport. That branch only triggers on a clear\n * orientation mismatch, so a correctly-oriented recording is never rotated.\n */\n\nexport interface OrientationTransform {\n /** Output canvas width in px. */\n width: number;\n /** Output canvas height in px. */\n height: number;\n /** Clockwise rotation to apply while drawing: 0, 90, or -90. */\n rotate: 0 | 90 | -90;\n}\n\nexport interface OrientationCorrectedStream {\n /** Canvas-sourced stream to hand to MediaRecorder. */\n stream: MediaStream;\n /** Stop drawing and release the canvas stream + source video. */\n stop: () => void;\n}\n\n/** True on iPhone/iPad/iPod, including iPadOS 13+ which reports as desktop Mac. */\nexport function isIOSDevice(): boolean {\n const ua = navigator.userAgent || \"\";\n return (\n /iP(hone|ad|od)/.test(ua) ||\n // iPadOS 13+ masquerades as \"MacIntel\" but is a multi-touch device.\n (navigator.platform === \"MacIntel\" && (navigator.maxTouchPoints || 0) > 1)\n );\n}\n\n/**\n * Decide the output canvas size and rotation needed to render a `frameW×frameH`\n * capture upright for a `viewportW×viewportH` viewport. When the frame and\n * viewport share an orientation, no rotation is applied (a straight re-encode,\n * which still strips the bad container rotation). Otherwise rotate 90°, its\n * direction chosen from the screen orientation angle.\n */\nexport function computeOrientationTransform(\n frameW: number,\n frameH: number,\n viewportW: number,\n viewportH: number,\n screenAngle: number\n): OrientationTransform {\n const frameLandscape = frameW >= frameH;\n const viewportLandscape = viewportW >= viewportH;\n\n if (frameLandscape === viewportLandscape) {\n return { width: frameW, height: frameH, rotate: 0 };\n }\n\n // Landscape-secondary (angle 270) is turned the opposite way to primary (90).\n const rotate: 90 | -90 = screenAngle === 270 ? -90 : 90;\n return { width: frameH, height: frameW, rotate };\n}\n\n/** Draw one video frame onto the canvas context, applying the transform. */\nexport function drawRotatedFrame(\n ctx: CanvasRenderingContext2D,\n source: CanvasImageSource,\n t: OrientationTransform\n): void {\n if (t.rotate === 0) {\n ctx.drawImage(source, 0, 0, t.width, t.height);\n return;\n }\n\n ctx.save();\n if (t.rotate === 90) {\n ctx.translate(t.width, 0);\n ctx.rotate(Math.PI / 2);\n } else {\n ctx.translate(0, t.height);\n ctx.rotate(-Math.PI / 2);\n }\n // The source is drawn at its natural (unrotated) extents, which are the\n // output dimensions swapped back.\n ctx.drawImage(source, 0, 0, t.height, t.width);\n ctx.restore();\n}\n\nfunction getScreenAngle(): number {\n const orientation =\n typeof screen !== \"undefined\" ? screen.orientation : undefined;\n return orientation && typeof orientation.angle === \"number\"\n ? orientation.angle\n : 0;\n}\n\nexport function waitForVideoDimensions(video: HTMLVideoElement): Promise<void> {\n return new Promise<void>((resolve) => {\n if (video.videoWidth > 0) {\n resolve();\n return;\n }\n video.addEventListener(\"loadedmetadata\", () => resolve(), { once: true });\n });\n}\n\nexport async function playSilently(video: HTMLVideoElement): Promise<void> {\n try {\n await video.play();\n } catch {\n // Autoplay can reject without a gesture; the element still produces frames\n // for the canvas, so this is non-fatal.\n }\n}\n\n/**\n * Build a canvas-backed, orientation-normalised copy of a display-capture\n * stream. Returns null when the browser can't support the canvas pipeline, in\n * which case the caller should record the original stream directly.\n */\nexport async function buildOrientationCorrectedStream(\n displayStream: MediaStream,\n frameRate = 15\n): Promise<OrientationCorrectedStream | null> {\n const canvas = document.createElement(\"canvas\");\n if (typeof canvas.captureStream !== \"function\") return null;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n\n const video = document.createElement(\"video\");\n video.muted = true;\n video.playsInline = true;\n video.srcObject = displayStream;\n\n await waitForVideoDimensions(video);\n\n const transform = computeOrientationTransform(\n video.videoWidth,\n video.videoHeight,\n window.innerWidth,\n window.innerHeight,\n getScreenAngle()\n );\n canvas.width = transform.width;\n canvas.height = transform.height;\n\n await playSilently(video);\n\n let rafId = 0;\n const draw = (): void => {\n drawRotatedFrame(ctx, video, transform);\n rafId = requestAnimationFrame(draw);\n };\n draw();\n\n const stream = canvas.captureStream(frameRate);\n\n return {\n stream,\n stop: (): void => {\n if (rafId) cancelAnimationFrame(rafId);\n rafId = 0;\n stream.getTracks().forEach((t) => t.stop());\n video.pause();\n video.srcObject = null;\n },\n };\n}\n","/**\n * Live video redaction.\n *\n * Blurs the on-screen bounding boxes of chosen elements while the current tab\n * is recorded. Every frame re-reads each element's `getBoundingClientRect()`,\n * so the blur tracks the content as the page scrolls or reflows — no pixel\n * tracking and no post-recording re-encode. The captured frame is the tab's\n * viewport (video-capture restricts capture to the current tab), so viewport\n * CSS coordinates map onto the frame with a single scale factor.\n *\n * The display stream is drawn to a canvas, the tracked regions are blurred on\n * top, and MediaRecorder records the canvas stream instead of the raw capture —\n * mirroring the iOS orientation pipeline in `video-orientation.ts`.\n */\n\nimport { waitForVideoDimensions, playSilently } from \"./video-orientation\";\n\nexport interface RedactedStream {\n /** Canvas-sourced stream to hand to MediaRecorder. */\n stream: MediaStream;\n /** Stop drawing and release the canvas stream + source video. */\n stop: () => void;\n}\n\nexport interface RedactionOptions {\n /** Output frame rate of the redacted stream. Default: 15. */\n frameRate?: number;\n /** Gaussian blur radius in px applied to each region. Default: 12. */\n blurRadius?: number;\n}\n\ninterface Box {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/**\n * Map a viewport rect (CSS px, from `getBoundingClientRect()`) onto the\n * captured frame, clamped to the frame bounds. Returns null when the rect is\n * fully outside the viewport (the element scrolled away) or has no area.\n */\nexport function mapRectToFrame(\n rect: { left: number; top: number; width: number; height: number },\n scaleX: number,\n scaleY: number,\n frameW: number,\n frameH: number\n): Box | null {\n const x = Math.max(0, Math.min(frameW, rect.left * scaleX));\n const y = Math.max(0, Math.min(frameH, rect.top * scaleY));\n const right = Math.max(0, Math.min(frameW, (rect.left + rect.width) * scaleX));\n const bottom = Math.max(0, Math.min(frameH, (rect.top + rect.height) * scaleY));\n const w = right - x;\n const h = bottom - y;\n if (w <= 0 || h <= 0) return null;\n return { x, y, w, h };\n}\n\n/**\n * Draw one frame: the full capture, then each element's live bounding box\n * blurred on top. Exported for unit testing the per-frame logic without a real\n * capture stream. When the 2D context can't apply a blur filter, the region is\n * filled solid instead so sensitive content is still hidden.\n */\nexport function drawRedactedFrame(\n ctx: CanvasRenderingContext2D,\n video: CanvasImageSource,\n elements: Element[],\n frameW: number,\n frameH: number,\n blurRadius: number,\n supportsFilter: boolean\n): void {\n ctx.drawImage(video, 0, 0, frameW, frameH);\n\n // The current-tab capture is the viewport, so this scale is ~devicePixelRatio;\n // deriving it live also absorbs viewport resizes mid-recording.\n const scaleX = frameW / Math.max(1, window.innerWidth);\n const scaleY = frameH / Math.max(1, window.innerHeight);\n\n for (const el of elements) {\n const box = mapRectToFrame(\n el.getBoundingClientRect(),\n scaleX,\n scaleY,\n frameW,\n frameH\n );\n if (!box) continue; // scrolled out of view\n\n ctx.save();\n if (supportsFilter) {\n ctx.filter = `blur(${blurRadius}px)`;\n // Re-sample just this region from the live frame so the blur source is\n // the region itself.\n ctx.drawImage(video, box.x, box.y, box.w, box.h, box.x, box.y, box.w, box.h);\n } else {\n // No canvas filter support — hide the region with a solid fill.\n ctx.fillStyle = \"#0b0b0b\";\n ctx.fillRect(box.x, box.y, box.w, box.h);\n }\n ctx.restore();\n }\n}\n\n/**\n * Build a canvas-backed copy of a display-capture stream that blurs the live\n * bounding boxes of `elements`. Returns null when the browser can't support the\n * canvas pipeline (no `captureStream` / 2D context), in which case the caller\n * should record the original stream unredacted.\n */\nexport async function buildRedactedStream(\n displayStream: MediaStream,\n elements: Element[],\n options: RedactionOptions = {}\n): Promise<RedactedStream | null> {\n const frameRate = options.frameRate ?? 15;\n const blurRadius = options.blurRadius ?? 12;\n\n const canvas = document.createElement(\"canvas\");\n if (typeof canvas.captureStream !== \"function\") return null;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n\n // Feature-detect canvas blur; fall back to solid fill where unsupported.\n const supportsFilter = \"filter\" in ctx;\n\n const video = document.createElement(\"video\");\n video.muted = true;\n video.playsInline = true;\n video.srcObject = displayStream;\n\n await waitForVideoDimensions(video);\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n await playSilently(video);\n\n let rafId = 0;\n const draw = (): void => {\n drawRedactedFrame(\n ctx,\n video,\n elements,\n canvas.width,\n canvas.height,\n blurRadius,\n supportsFilter\n );\n rafId = requestAnimationFrame(draw);\n };\n draw();\n\n const stream = canvas.captureStream(frameRate);\n\n return {\n stream,\n stop: (): void => {\n if (rafId) cancelAnimationFrame(rafId);\n rafId = 0;\n stream.getTracks().forEach((t) => t.stop());\n video.pause();\n video.srcObject = null;\n },\n };\n}\n","/**\n * Video capture utilities\n * Uses getDisplayMedia + MediaRecorder for screen recording\n */\n\nimport {\n isIOSDevice,\n buildOrientationCorrectedStream,\n type OrientationCorrectedStream,\n} from \"./video-orientation\";\nimport { buildRedactedStream, type RedactedStream } from \"./video-redaction\";\n\n/**\n * Thrown by `start()` when areas were picked for redaction but this browser\n * cannot run the canvas pipeline that blurs them. The recording is refused\n * rather than made without the blur.\n */\nexport const REDACTION_UNAVAILABLE =\n \"This browser cannot blur the areas you picked, so the recording was not started.\";\n\nexport interface VideoRecorderOptions {\n /** Max recording duration in seconds. Default: 15 */\n maxDuration?: number;\n /** Video bitrate in bps. Default: 800000 (800kbps) */\n videoBitsPerSecond?: number;\n /**\n * Elements whose live bounding boxes are blurred out of the recording. The\n * blur tracks each element as the page scrolls. Empty/omitted = no redaction.\n */\n redactionElements?: Element[];\n /** Blur radius in px for redacted regions. Default: 12. */\n redactionBlurRadius?: number;\n}\n\nexport interface VideoRecorder {\n /** Start recording. Requests getDisplayMedia if not already started. */\n start(): Promise<void>;\n /** Stop recording and return the video blob. */\n stop(): Promise<Blob>;\n /** Register a callback for each second tick (receives seconds elapsed). */\n onTick(callback: (elapsed: number) => void): void;\n /** Register a callback for when recording stops (e.g., browser stop button, max duration). */\n onStop(callback: (blob: Blob | null) => void): void;\n /** Clean up all resources. */\n destroy(): void;\n}\n\n/**\n * Check if video recording is supported in this browser\n */\nexport function isVideoRecordingSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n !!navigator.mediaDevices &&\n typeof navigator.mediaDevices.getDisplayMedia === \"function\" &&\n typeof MediaRecorder !== \"undefined\"\n );\n}\n\n/**\n * Detect the best supported MIME type for recording\n */\nexport function getSupportedMimeType(): string {\n if (typeof MediaRecorder === \"undefined\") return \"\";\n\n const candidates = [\n \"video/webm;codecs=vp9\",\n \"video/webm;codecs=vp8\",\n \"video/webm\",\n \"video/mp4\",\n ];\n\n for (const type of candidates) {\n if (MediaRecorder.isTypeSupported(type)) {\n return type;\n }\n }\n\n return \"\";\n}\n\n/**\n * Create a video recorder that captures the screen\n */\nexport function createVideoRecorder(options: VideoRecorderOptions = {}): VideoRecorder {\n const maxDuration = options.maxDuration ?? 15;\n const videoBitsPerSecond = options.videoBitsPerSecond ?? 800_000;\n const redactionElements = options.redactionElements ?? [];\n const redactionBlurRadius = options.redactionBlurRadius;\n\n let stream: MediaStream | null = null;\n let orientationCorrection: OrientationCorrectedStream | null = null;\n let redaction: RedactedStream | null = null;\n let recorder: MediaRecorder | null = null;\n let chunks: Blob[] = [];\n let tickCallback: ((elapsed: number) => void) | null = null;\n let stopCallback: ((blob: Blob | null) => void) | null = null;\n let tickInterval: ReturnType<typeof setInterval> | null = null;\n let startTime = 0;\n let resolveStop: ((blob: Blob) => void) | null = null;\n let rejectStop: ((err: Error) => void) | null = null;\n let stopped = false;\n let autoStopTimeout: ReturnType<typeof setTimeout> | null = null;\n\n function cleanup(): void {\n if (tickInterval !== null) {\n clearInterval(tickInterval);\n tickInterval = null;\n }\n if (autoStopTimeout !== null) {\n clearTimeout(autoStopTimeout);\n autoStopTimeout = null;\n }\n if (orientationCorrection) {\n orientationCorrection.stop();\n orientationCorrection = null;\n }\n if (redaction) {\n redaction.stop();\n redaction = null;\n }\n if (stream) {\n stream.getTracks().forEach((t) => t.stop());\n stream = null;\n }\n recorder = null;\n chunks = [];\n tickCallback = null;\n stopCallback = null;\n resolveStop = null;\n rejectStop = null;\n }\n\n function finishRecording(): void {\n if (stopped) return;\n stopped = true;\n\n if (recorder && recorder.state !== \"inactive\") {\n recorder.stop();\n }\n }\n\n return {\n async start(): Promise<void> {\n stopped = false;\n chunks = [];\n\n const mimeType = getSupportedMimeType();\n if (!mimeType) {\n throw new Error(\"No supported video MIME type found\");\n }\n\n // Request screen capture, restricted to the current tab (the page the\n // embed is running on). `preferCurrentTab` collapses the Chromium picker\n // to a single \"share this tab\" choice; the other hints drop window and\n // whole-screen options where the browser honours them. These keys are\n // not in the DOM types yet.\n stream = await navigator.mediaDevices.getDisplayMedia({\n video: {\n frameRate: 15,\n displaySurface: \"browser\",\n },\n audio: false,\n // @ts-expect-error preferCurrentTab is not in the TS types yet\n preferCurrentTab: true,\n selfBrowserSurface: \"include\",\n monitorTypeSurfaces: \"exclude\",\n surfaceSwitching: \"exclude\",\n });\n\n // Enforce tab-only capture. In browsers that ignore the hints above and\n // still let the user share a window or the whole screen, reject it — qaid\n // records only the tab it is embedded in, never other windows or the\n // desktop.\n const videoTrack = stream.getVideoTracks()[0];\n const surface = videoTrack?.getSettings?.()?.displaySurface;\n if (surface === \"monitor\" || surface === \"window\") {\n stream.getTracks().forEach((t) => t.stop());\n stream = null;\n throw new Error(\n \"qaid records only the current tab — please share this tab, not a window or your whole screen.\",\n );\n }\n\n // Handle browser \"Stop sharing\" button\n if (videoTrack) {\n videoTrack.addEventListener(\"ended\", () => {\n finishRecording();\n });\n }\n\n // Route the raw capture through canvas pipelines when needed. On\n // iOS/iPadOS the captured file is rotated by the OS, so first re-encode\n // the live frames upright (falling back to the raw stream if the canvas\n // pipeline isn't available). Then, if the user marked regions to redact,\n // blur their live bounding boxes into whatever the first step produced.\n //\n // Redaction used to sit in an `else` of the iOS branch, so on an iPhone\n // or iPad the picker drew its outlines and the recording went out\n // unblurred. The upright frame matches the viewport, which is what the\n // redaction mapping assumes, so the two chain cleanly.\n let recordStream: MediaStream = stream;\n if (isIOSDevice()) {\n const corrected = await buildOrientationCorrectedStream(stream);\n if (corrected) {\n orientationCorrection = corrected;\n recordStream = corrected.stream;\n }\n }\n if (redactionElements.length > 0) {\n const redacted = await buildRedactedStream(recordStream, redactionElements, {\n blurRadius: redactionBlurRadius,\n });\n if (!redacted) {\n // The visitor was promised a blur. Recording the areas in the clear\n // would break that promise, so refuse to record instead.\n if (orientationCorrection) {\n orientationCorrection.stop();\n orientationCorrection = null;\n }\n stream.getTracks().forEach((t) => t.stop());\n stream = null;\n throw new Error(REDACTION_UNAVAILABLE);\n }\n redaction = redacted;\n recordStream = redacted.stream;\n }\n\n recorder = new MediaRecorder(recordStream, {\n mimeType,\n videoBitsPerSecond,\n });\n\n recorder.ondataavailable = (e: BlobEvent) => {\n if (e.data.size > 0) {\n chunks.push(e.data);\n }\n };\n\n recorder.onstop = () => {\n const blob = new Blob(chunks, { type: mimeType });\n if (resolveStop) {\n resolveStop(blob);\n }\n // Notify via callback (for unexpected stops like browser stop button)\n if (stopCallback) {\n stopCallback(blob);\n }\n // Stop the canvas pipeline (iOS orientation / redaction) and the\n // display stream tracks.\n if (orientationCorrection) {\n orientationCorrection.stop();\n orientationCorrection = null;\n }\n if (redaction) {\n redaction.stop();\n redaction = null;\n }\n if (stream) {\n stream.getTracks().forEach((t) => t.stop());\n }\n };\n\n recorder.onerror = () => {\n if (rejectStop) {\n rejectStop(new Error(\"MediaRecorder error\"));\n }\n };\n\n recorder.start(1000); // collect data every second\n startTime = Date.now();\n\n // Start tick interval\n tickInterval = setInterval(() => {\n const elapsed = Math.floor((Date.now() - startTime) / 1000);\n if (tickCallback) {\n tickCallback(elapsed);\n }\n }, 1000);\n\n // Auto-stop at max duration\n autoStopTimeout = setTimeout(() => {\n finishRecording();\n }, maxDuration * 1000);\n },\n\n stop(): Promise<Blob> {\n return new Promise<Blob>((resolve, reject) => {\n resolveStop = resolve;\n rejectStop = reject;\n finishRecording();\n });\n },\n\n onTick(callback: (elapsed: number) => void): void {\n tickCallback = callback;\n },\n\n onStop(callback: (blob: Blob | null) => void): void {\n stopCallback = callback;\n },\n\n destroy(): void {\n finishRecording();\n cleanup();\n },\n };\n}\n","/**\n * DOM-based screenshot capture using html2canvas\n * No browser permission dialog required - reconstructs the page from the DOM\n */\n\nexport interface DomScreenshotOptions {\n /** Quality of WebP compression (0-1). Default: 0.8 */\n quality?: number;\n /** Max width of the screenshot. Default: 1280 */\n maxWidth?: number;\n /** Max height of the screenshot. Default: 800 */\n maxHeight?: number;\n}\n\ndeclare global {\n interface Window {\n html2canvas?: (\n element: HTMLElement,\n options?: Record<string, unknown>\n ) => Promise<HTMLCanvasElement>;\n }\n}\n\nconst SCRIPT_URL = \"https://qaid.dev/lib/html2canvas.min.js\";\nconst LOAD_TIMEOUT = 10_000;\nconst POLL_INTERVAL = 50;\n\nlet loadPromise: Promise<boolean> | null = null;\n\n/**\n * Check if DOM screenshot capture is supported\n * Requires canvas and basic DOM APIs\n */\nexport function isDomScreenshotSupported(): boolean {\n if (typeof document === \"undefined\" || typeof window === \"undefined\") {\n return false;\n }\n // Check canvas support\n const canvas = document.createElement(\"canvas\");\n return typeof canvas.getContext === \"function\" && !!canvas.getContext(\"2d\");\n}\n\n/**\n * Load html2canvas script if not already loaded\n */\nfunction loadHtml2Canvas(): Promise<boolean> {\n if (window.html2canvas) return Promise.resolve(true);\n\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<boolean>((resolve) => {\n // Check if script tag already exists\n const existing = document.querySelector(\n `script[src=\"${SCRIPT_URL}\"]`\n ) as HTMLScriptElement | null;\n\n if (!existing) {\n const script = document.createElement(\"script\");\n script.src = SCRIPT_URL;\n script.async = true;\n document.head.appendChild(script);\n }\n\n // Poll for window.html2canvas availability\n const start = Date.now();\n const poll = () => {\n if (window.html2canvas) {\n resolve(true);\n return;\n }\n if (Date.now() - start > LOAD_TIMEOUT) {\n loadPromise = null;\n resolve(false);\n return;\n }\n setTimeout(poll, POLL_INTERVAL);\n };\n poll();\n });\n\n return loadPromise;\n}\n\n/**\n * Capture a screenshot of the current page using html2canvas\n * Returns a base64-encoded WebP data URL, or null on failure\n */\nexport async function captureDomScreenshot(\n options: DomScreenshotOptions = {}\n): Promise<string | null> {\n const { quality = 0.8, maxWidth = 1280, maxHeight = 800 } = options;\n\n try {\n const loaded = await loadHtml2Canvas();\n if (!loaded || !window.html2canvas) {\n console.warn(\"html2canvas failed to load\");\n return null;\n }\n\n const canvas = await window.html2canvas(document.body, {\n useCORS: true,\n allowTaint: false,\n logging: false,\n windowWidth: document.documentElement.clientWidth,\n windowHeight: document.documentElement.clientHeight,\n width: document.documentElement.clientWidth,\n height: document.documentElement.clientHeight,\n x: window.scrollX,\n y: window.scrollY,\n ignoreElements: (el: Element) => {\n // Hide all qaid elements from the screenshot\n if (el instanceof HTMLElement) {\n return (\n el.classList.contains(\"qaid-buttons\") ||\n el.classList.contains(\"qaid-targeting-overlay\") ||\n el.classList.contains(\"qaid-modal-container\") ||\n el.classList.contains(\"qaid-bottom-sheet\") ||\n el.classList.contains(\"qaid-backdrop\") ||\n el.classList.contains(\"qaid-selected-marker\") ||\n el.classList.contains(\"qaid-tooltip-text\") ||\n el.classList.contains(\"qaid-recording-indicator\") ||\n el.classList.contains(\"qaid-video-preview\") ||\n el.className?.toString().startsWith?.(\"qaid-\")\n );\n }\n return false;\n },\n });\n\n // Scale down if needed\n const srcWidth = canvas.width;\n const srcHeight = canvas.height;\n const scale = Math.min(maxWidth / srcWidth, maxHeight / srcHeight, 1);\n\n if (scale < 1) {\n const scaledWidth = Math.round(srcWidth * scale);\n const scaledHeight = Math.round(srcHeight * scale);\n\n const scaledCanvas = document.createElement(\"canvas\");\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n\n const ctx = scaledCanvas.getContext(\"2d\");\n if (!ctx) return null;\n\n ctx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n return scaledCanvas.toDataURL(\"image/webp\", quality);\n }\n\n return canvas.toDataURL(\"image/webp\", quality);\n } catch (error) {\n console.warn(\"DOM screenshot capture failed:\", error);\n return null;\n }\n}\n","/**\n * Screenshot annotation / markup editor for the thumbs embed.\n *\n * After a screenshot is captured the user can draw on it — rectangles, arrows,\n * a freehand pen, and a blur/redact box — in a full-screen, focus-trapped,\n * WCAG 2.2 AA dialog rendered inside the embed's overlay shadow root. On \"Done\"\n * the base image and the vector shapes are composited into a fresh WebP data\n * URL (so downstream code is unchanged); on \"Skip\"/Escape the original is kept.\n *\n * The geometry and compositing are kept as pure-ish exported functions\n * (`drawShape`, `rectFromPoints`, `compositeAnnotations`) so they can be tested\n * without a real rendering engine — happy-dom has no raster, so tests assert on\n * spied canvas-context calls rather than pixels. The {@link AnnotationEditor}\n * controller keeps the DOM wiring thin and its interactive methods are public\n * so they can be driven directly in tests.\n */\n\nimport { createElement } from \"./dom-utils\";\nimport {\n RECTANGLE_ICON,\n ARROW_ICON,\n PENCIL_ICON,\n BLUR_ICON,\n UNDO_ICON,\n DONE_ICON,\n SKIP_ICON,\n} from \"./icons\";\n\n/** The four drawing tools. */\nexport type ShapeType = \"rect\" | \"arrow\" | \"pen\" | \"blur\";\n\n/** A point in the screenshot's natural-pixel coordinate space. */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * A single vector annotation. `rect`, `arrow` and `blur` use `points[0]` as the\n * drag start and `points[1]` as the drag end; `pen` uses every point in order.\n */\nexport interface Shape {\n type: ShapeType;\n color: string;\n strokeWidth: number;\n points: Point[];\n}\n\n/** Tile size (natural px) for the mosaic drawn by the blur/redact tool. */\nconst BLUR_TILE = 12;\n\n/** Default stroke colour: a high-visibility red that reads on most screenshots. */\nconst DEFAULT_STROKE = \"#ef4444\";\n/** Default swatch palette when the embed doesn't supply one. */\nconst DEFAULT_PALETTE = [\"#ef4444\", \"#f59e0b\", \"#22c55e\", \"#3b82f6\", \"#111827\", \"#ffffff\"];\n/** Friendly names for common swatches (for accessible labels). */\nconst COLOR_NAMES: Record<string, string> = {\n \"#ef4444\": \"red\",\n \"#f59e0b\": \"amber\",\n \"#22c55e\": \"green\",\n \"#3b82f6\": \"blue\",\n \"#111827\": \"black\",\n \"#ffffff\": \"white\",\n // The default marker (nudged from #6366f1 so white text on it clears 4.5:1);\n // the old value keeps its name for sites that pass it explicitly.\n \"#6365f1\": \"indigo\",\n \"#6366f1\": \"indigo\",\n};\n/** Accessible name for a swatch colour — a friendly name, else the hex. */\nfunction colorName(hex: string): string {\n return COLOR_NAMES[hex.toLowerCase()] ?? hex;\n}\n/** Default stroke width in natural pixels. */\nconst DEFAULT_STROKE_WIDTH = 4;\n/** Default WebP quality for the re-encoded composite. */\nconst DEFAULT_QUALITY = 0.8;\n\n/** Human-readable labels for the toolbar / announcements (all overridable). */\nexport interface AnnotationLabels {\n title: string;\n instructions: string;\n rectangle: string;\n arrow: string;\n pen: string;\n blur: string;\n undo: string;\n skip: string;\n done: string;\n colors: string;\n}\n\nconst DEFAULT_LABELS: AnnotationLabels = {\n title: \"Annotate screenshot\",\n instructions:\n \"Draw on the screenshot to highlight or hide details, then choose Done to attach it or Skip to send the original.\",\n rectangle: \"Rectangle\",\n arrow: \"Arrow\",\n pen: \"Pen\",\n blur: \"Blur or redact\",\n undo: \"Undo last\",\n skip: \"Skip annotation\",\n done: \"Done\",\n colors: \"Drawing colour\",\n};\n\n/**\n * Normalise two drag corners into a top-left origin plus positive width/height,\n * regardless of the direction the user dragged.\n */\nexport function rectFromPoints(\n a: Point,\n b: Point\n): { x: number; y: number; w: number; h: number } {\n return {\n x: Math.min(a.x, b.x),\n y: Math.min(a.y, b.y),\n w: Math.abs(a.x - b.x),\n h: Math.abs(a.y - b.y),\n };\n}\n\n/** A drag/shape too small to be a deliberate mark (a click, not a drag). */\nfunction isDegenerate(shape: Shape): boolean {\n if (shape.type === \"pen\") return shape.points.length < 2;\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n return r.w < 3 && r.h < 3;\n}\n\nfunction drawArrow(\n ctx: CanvasRenderingContext2D,\n from: Point,\n to: Point,\n width: number\n): void {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n\n const angle = Math.atan2(to.y - from.y, to.x - from.x);\n const head = Math.max(10, width * 3);\n ctx.beginPath();\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle - Math.PI / 6),\n to.y - head * Math.sin(angle - Math.PI / 6)\n );\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle + Math.PI / 6),\n to.y - head * Math.sin(angle + Math.PI / 6)\n );\n ctx.stroke();\n}\n\nfunction drawPen(ctx: CanvasRenderingContext2D, points: Point[]): void {\n if (points.length === 0) return;\n ctx.beginPath();\n ctx.moveTo(points[0].x, points[0].y);\n for (let i = 1; i < points.length; i++) {\n ctx.lineTo(points[i].x, points[i].y);\n }\n ctx.stroke();\n}\n\n/**\n * Redact a region so the underlying content is GONE from the composite — not\n * merely dimmed. Both fills are fully opaque (no alpha), so nothing bleeds\n * through; the two-tone mosaic just gives it a recognisable \"redacted\" texture.\n * There is no real Gaussian blur (it can't be verified without a raster), and a\n * translucent wash would leave sensitive text faintly legible — so this paints\n * solid.\n */\nfunction drawBlur(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number\n): void {\n ctx.fillStyle = \"rgb(15, 23, 42)\";\n ctx.fillRect(x, y, w, h);\n ctx.fillStyle = \"rgb(37, 47, 63)\";\n for (let ty = y; ty < y + h; ty += BLUR_TILE) {\n for (let tx = x; tx < x + w; tx += BLUR_TILE) {\n const checker =\n (Math.floor((tx - x) / BLUR_TILE) + Math.floor((ty - y) / BLUR_TILE)) %\n 2 ===\n 0;\n if (checker) {\n ctx.fillRect(\n tx,\n ty,\n Math.min(BLUR_TILE, x + w - tx),\n Math.min(BLUR_TILE, y + h - ty)\n );\n }\n }\n }\n}\n\n/**\n * Draw a single shape onto a 2D context in the shape's own (natural-pixel)\n * coordinates. Used both for the live editing preview and for the final\n * composite, so what the user sees is exactly what is submitted.\n */\nexport function drawShape(ctx: CanvasRenderingContext2D, shape: Shape): void {\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = \"round\";\n ctx.lineCap = \"round\";\n\n switch (shape.type) {\n case \"rect\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n ctx.strokeRect(r.x, r.y, r.w, r.h);\n break;\n }\n case \"arrow\":\n drawArrow(ctx, shape.points[0], shape.points[1], shape.strokeWidth);\n break;\n case \"pen\":\n drawPen(ctx, shape.points);\n break;\n case \"blur\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n drawBlur(ctx, r.x, r.y, r.w, r.h);\n break;\n }\n }\n\n ctx.restore();\n}\n\n/** Load an image from a data URL. Resolves with the element once decoded. */\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = (): void => resolve(img);\n img.onerror = (): void => reject(new Error(\"Failed to load screenshot\"));\n img.src = src;\n });\n}\n\n/**\n * Composite the base screenshot and the drawn shapes into a new WebP data URL\n * at the image's natural size. Falls back to the original data URL if the image\n * can't be loaded or a 2D context isn't available (so a submission is never\n * lost). This is intentionally standalone so it can be unit-tested in isolation.\n */\nexport async function compositeAnnotations(\n dataUrl: string,\n shapes: Shape[],\n opts: { quality?: number } = {}\n): Promise<string> {\n const quality = opts.quality ?? DEFAULT_QUALITY;\n\n let img: HTMLImageElement;\n try {\n img = await loadImage(dataUrl);\n } catch {\n return dataUrl;\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.naturalWidth || img.width || 1;\n canvas.height = img.naturalHeight || img.height || 1;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return dataUrl;\n\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n for (const shape of shapes) {\n drawShape(ctx, shape);\n }\n\n return canvas.toDataURL(\"image/webp\", quality);\n}\n\n/** Options for {@link AnnotationEditor} / {@link openAnnotationEditor}. */\nexport interface AnnotationEditorOptions {\n /** The captured screenshot as a WebP data URL. */\n dataUrl: string;\n /** Shadow root (or element) the editor is mounted into. */\n root: ShadowRoot | HTMLElement;\n /** WebP quality for the re-encoded composite. */\n quality?: number;\n /** Default stroke colour for rectangle/arrow/pen. */\n color?: string;\n /** Colour swatches offered in the toolbar. The active `color` is ensured present. */\n palette?: string[];\n /** Default stroke width. */\n strokeWidth?: number;\n /** Label overrides. */\n labels?: Partial<AnnotationLabels>;\n /** Apply the embed's per-instance `--qaid-*` CSS variables to the container. */\n applyVars?: (el: HTMLElement) => void;\n /** Announce a message via the embed's live regions. */\n announce?: (message: string, assertive?: boolean) => void;\n /** Turn the container into an accessible modal dialog (focus trap + inert). */\n openDialog?: (\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string }\n ) => void;\n /** Tear down the dialog semantics and restore focus. */\n closeDialog?: () => void;\n}\n\nlet editorSeq = 0;\n\n/**\n * Full-screen screenshot annotation editor. Construct it, call {@link open}\n * (which returns a promise resolving to the composited WebP, or `null` when the\n * user skips), and the controller manages its own DOM and teardown.\n */\nexport class AnnotationEditor {\n readonly shapes: Shape[] = [];\n tool: ShapeType = \"rect\";\n\n private readonly opts: AnnotationEditorOptions;\n private readonly labels: AnnotationLabels;\n private color: string;\n private readonly palette: string[];\n private readonly strokeWidth: number;\n private readonly uid = `qaid-annotate-${++editorSeq}`;\n\n private container!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D | null = null;\n private toolbar!: HTMLDivElement;\n private undoBtn!: HTMLButtonElement;\n private img: HTMLImageElement | null = null;\n\n private drawing = false;\n private current: Shape | null = null;\n private settled = false;\n // Assigned in open() before any settle() is reachable (done/skip only fire\n // after the editor is mounted).\n private resolveResult!: (value: string | null) => void;\n\n constructor(opts: AnnotationEditorOptions) {\n this.opts = opts;\n this.labels = { ...DEFAULT_LABELS, ...(opts.labels ?? {}) };\n this.color = opts.color ?? DEFAULT_STROKE;\n this.palette = (opts.palette && opts.palette.length ? opts.palette : DEFAULT_PALETTE).slice();\n // Ensure the active colour is offered as a swatch so it stays selectable.\n if (!this.palette.some((c) => c.toLowerCase() === this.color.toLowerCase())) {\n this.palette.unshift(this.color);\n }\n this.strokeWidth = opts.strokeWidth ?? DEFAULT_STROKE_WIDTH;\n }\n\n /** Mount the editor and resolve when the user commits (Done) or skips. */\n open(): Promise<string | null> {\n const promise = new Promise<string | null>((resolve) => {\n this.resolveResult = resolve;\n });\n this.mount();\n return promise;\n }\n\n private toolButton(tool: ShapeType, icon: string, label: string): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-tool\",\n \"data-qaid-tool\": tool,\n \"aria-pressed\": tool === this.tool ? \"true\" : \"false\",\n \"aria-label\": `${label} tool`,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n /** A round colour swatch button for the toolbar's colour group. */\n private swatchButton(color: string): HTMLButtonElement {\n const name = colorName(color);\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-swatch\",\n \"data-qaid-color\": color,\n \"aria-pressed\": color.toLowerCase() === this.color.toLowerCase() ? \"true\" : \"false\",\n \"aria-label\": `Draw in ${name}`,\n title: name,\n });\n btn.style.setProperty(\"--qaid-swatch\", color);\n return btn;\n }\n\n private actionButton(\n action: string,\n icon: string,\n label: string,\n extraClass: string\n ): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: `qaid-annotate-action ${extraClass}`,\n \"data-qaid-action\": action,\n \"aria-label\": label,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n private mount(): void {\n const titleId = `${this.uid}-title`;\n const descId = `${this.uid}-desc`;\n\n this.container = createElement(\"div\", { class: \"qaid-annotate\" });\n this.opts.applyVars?.(this.container);\n\n const title = createElement(\"h2\", {\n id: titleId,\n class: \"qaid-annotate-title\",\n });\n title.textContent = this.labels.title;\n\n const desc = createElement(\"p\", {\n id: descId,\n class: \"qaid-annotate-desc\",\n });\n desc.textContent = this.labels.instructions;\n\n const stage = createElement(\"div\", { class: \"qaid-annotate-stage\" });\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"qaid-annotate-canvas\";\n this.canvas.width = 1;\n this.canvas.height = 1;\n this.canvas.setAttribute(\"role\", \"img\");\n this.canvas.setAttribute(\"aria-label\", this.labels.title);\n this.ctx = this.canvas.getContext(\"2d\");\n stage.appendChild(this.canvas);\n\n this.toolbar = createElement(\"div\", {\n class: \"qaid-annotate-toolbar\",\n role: \"toolbar\",\n \"aria-label\": this.labels.title,\n });\n\n const tools = createElement(\"div\", { class: \"qaid-annotate-tools\" });\n tools.appendChild(this.toolButton(\"rect\", RECTANGLE_ICON, this.labels.rectangle));\n tools.appendChild(this.toolButton(\"arrow\", ARROW_ICON, this.labels.arrow));\n tools.appendChild(this.toolButton(\"pen\", PENCIL_ICON, this.labels.pen));\n tools.appendChild(this.toolButton(\"blur\", BLUR_ICON, this.labels.blur));\n\n const colors = createElement(\"div\", {\n class: \"qaid-annotate-colors\",\n role: \"group\",\n \"aria-label\": this.labels.colors,\n });\n for (const c of this.palette) colors.appendChild(this.swatchButton(c));\n\n this.undoBtn = this.actionButton(\"undo\", UNDO_ICON, this.labels.undo, \"qaid-annotate-undo\");\n this.undoBtn.disabled = true;\n\n const skipBtn = this.actionButton(\"skip\", SKIP_ICON, this.labels.skip, \"qaid-annotate-skip\");\n const doneBtn = this.actionButton(\"done\", DONE_ICON, this.labels.done, \"qaid-annotate-done\");\n\n const actions = createElement(\"div\", { class: \"qaid-annotate-actions\" });\n actions.appendChild(this.undoBtn);\n actions.appendChild(skipBtn);\n actions.appendChild(doneBtn);\n\n this.toolbar.appendChild(tools);\n this.toolbar.appendChild(colors);\n this.toolbar.appendChild(actions);\n\n this.container.appendChild(title);\n this.container.appendChild(desc);\n this.container.appendChild(stage);\n this.container.appendChild(this.toolbar);\n this.opts.root.appendChild(this.container);\n\n this.toolbar.addEventListener(\"click\", this.onToolbarClick);\n this.canvas.addEventListener(\"pointerdown\", this.onPointerDown);\n this.container.addEventListener(\"pointermove\", this.onPointerMove);\n this.container.addEventListener(\"pointerup\", this.onPointerUp);\n this.container.addEventListener(\"keydown\", this.onKeyDown);\n\n this.opts.openDialog?.(this.container, {\n labelledbyId: titleId,\n describedbyId: descId,\n });\n this.opts.announce?.(this.labels.instructions);\n\n loadImage(this.opts.dataUrl)\n .then((img) => {\n this.img = img;\n this.canvas.width = img.naturalWidth || img.width || 1;\n this.canvas.height = img.naturalHeight || img.height || 1;\n this.redraw();\n })\n .catch(() => {\n this.img = null;\n this.redraw();\n });\n }\n\n /** Convert a pointer event's client coordinates into natural image pixels. */\n private toImageCoords(e: { clientX: number; clientY: number }): Point {\n const rect = this.canvas.getBoundingClientRect();\n const scaleX = this.canvas.width / (rect.width || this.canvas.width);\n const scaleY = this.canvas.height / (rect.height || this.canvas.height);\n return {\n x: (e.clientX - rect.left) * scaleX,\n y: (e.clientY - rect.top) * scaleY,\n };\n }\n\n /** Repaint the display canvas: base image (if loaded) then every shape. */\n redraw(): void {\n const ctx = this.ctx;\n if (!ctx) return;\n ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n if (this.img) {\n ctx.drawImage(this.img, 0, 0, this.canvas.width, this.canvas.height);\n }\n for (const shape of this.shapes) {\n drawShape(ctx, shape);\n }\n if (this.current) {\n drawShape(ctx, this.current);\n }\n }\n\n /** Select the active drawing tool and reflect it on the toolbar. */\n selectTool(tool: ShapeType): void {\n this.tool = tool;\n const buttons = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-tool]\");\n buttons.forEach((btn) => {\n btn.setAttribute(\n \"aria-pressed\",\n btn.getAttribute(\"data-qaid-tool\") === tool ? \"true\" : \"false\"\n );\n });\n const label = this.labels[LABEL_KEY[tool]];\n this.opts.announce?.(`${label} tool selected`);\n }\n\n /** Set the active drawing colour; subsequent shapes use it. */\n selectColor(color: string): void {\n this.color = color;\n const swatches = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-color]\");\n swatches.forEach((s) => {\n s.setAttribute(\n \"aria-pressed\",\n (s.getAttribute(\"data-qaid-color\") ?? \"\").toLowerCase() === color.toLowerCase()\n ? \"true\"\n : \"false\"\n );\n });\n this.opts.announce?.(`${colorName(color)} colour selected`);\n }\n\n private onToolbarClick = (e: Event): void => {\n const target = e.target as HTMLElement | null;\n const btn = target?.closest<HTMLElement>(\n \"[data-qaid-tool],[data-qaid-action],[data-qaid-color]\"\n );\n if (!btn) return;\n const color = btn.getAttribute(\"data-qaid-color\");\n if (color) {\n this.selectColor(color);\n return;\n }\n const tool = btn.getAttribute(\"data-qaid-tool\");\n if (tool) {\n this.selectTool(tool as ShapeType);\n return;\n }\n const action = btn.getAttribute(\"data-qaid-action\");\n if (action === \"undo\") this.undo();\n else if (action === \"skip\") this.skip();\n else if (action === \"done\") void this.done();\n };\n\n onPointerDown = (e: PointerEvent): void => {\n e.preventDefault?.();\n this.drawing = true;\n const p = this.toImageCoords(e);\n this.current = {\n type: this.tool,\n color: this.color,\n strokeWidth: this.strokeWidth,\n points: this.tool === \"pen\" ? [p] : [p, { ...p }],\n };\n this.redraw();\n };\n\n onPointerMove = (e: PointerEvent): void => {\n if (!this.drawing || !this.current) return;\n const p = this.toImageCoords(e);\n if (this.current.type === \"pen\") {\n this.current.points.push(p);\n } else {\n this.current.points[1] = p;\n }\n this.redraw();\n };\n\n onPointerUp = (): void => {\n if (!this.drawing || !this.current) return;\n this.drawing = false;\n const shape = this.current;\n this.current = null;\n if (isDegenerate(shape)) {\n this.redraw();\n return;\n }\n this.shapes.push(shape);\n this.undoBtn.disabled = false;\n this.opts.announce?.(\"Annotation added\");\n this.redraw();\n };\n\n onKeyDown = (e: KeyboardEvent): void => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n this.skip();\n return;\n }\n if (e.key === \"Enter\") {\n const target = e.target as HTMLElement | null;\n // Let Enter activate a focused tool/action button natively.\n if (target?.getAttribute(\"data-qaid-tool\") || target?.getAttribute(\"data-qaid-action\")) {\n return;\n }\n e.preventDefault();\n void this.done();\n }\n };\n\n /** Remove the most recently added shape. */\n undo(): void {\n if (this.shapes.length === 0) {\n this.opts.announce?.(\"Nothing to undo\");\n return;\n }\n this.shapes.pop();\n this.undoBtn.disabled = this.shapes.length === 0;\n this.opts.announce?.(\"Removed last annotation\");\n this.redraw();\n }\n\n /** Composite the annotations and resolve with the new WebP data URL. */\n async done(): Promise<void> {\n if (this.settled) return;\n const out = await compositeAnnotations(this.opts.dataUrl, this.shapes, {\n quality: this.opts.quality,\n });\n this.opts.announce?.(\"Annotated screenshot attached\");\n this.settle(out);\n }\n\n /** Discard annotations and resolve with `null` (caller keeps the original). */\n skip(): void {\n if (this.settled) return;\n this.opts.announce?.(\"Annotation skipped\");\n this.settle(null);\n }\n\n private settle(value: string | null): void {\n this.settled = true;\n this.teardown();\n this.resolveResult(value);\n }\n\n private teardown(): void {\n this.toolbar.removeEventListener(\"click\", this.onToolbarClick);\n this.canvas.removeEventListener(\"pointerdown\", this.onPointerDown);\n this.container.removeEventListener(\"pointermove\", this.onPointerMove);\n this.container.removeEventListener(\"pointerup\", this.onPointerUp);\n this.container.removeEventListener(\"keydown\", this.onKeyDown);\n this.opts.closeDialog?.();\n this.container.remove();\n }\n}\n\n/** Maps a tool id to its label key, for announcements. */\nconst LABEL_KEY: Record<ShapeType, keyof AnnotationLabels> = {\n rect: \"rectangle\",\n arrow: \"arrow\",\n pen: \"pen\",\n blur: \"blur\",\n};\n\n/**\n * Open the annotation editor and resolve with the composited WebP data URL, or\n * `null` if the user skipped. Thin entry point used by the embed.\n */\nexport function openAnnotationEditor(\n opts: AnnotationEditorOptions\n): Promise<string | null> {\n return new AnnotationEditor(opts).open();\n}\n","/**\n * @qaiddev/thumbs-embed - Standalone Feedback Embed\n *\n * A zero-dependency feedback embed with element targeting.\n *\n * Usage via ES module:\n * ```typescript\n * import { QaidFeedback } from '@qaiddev/thumbs-embed';\n *\n * const embed = new QaidFeedback({ endpoint: '/api/feedback' });\n * ```\n *\n * Usage via script tag (full bundle):\n * ```html\n * <script src=\"qaid.umd.cjs\" data-endpoint=\"/api/feedback\"></script>\n * ```\n *\n * For progressive loading (small core + on-demand screenshot/video/redaction\n * chunks), load the ESM loader instead:\n * ```html\n * <script type=\"module\" src=\"…/loader.js\" data-endpoint=\"/api/feedback\"></script>\n * ```\n *\n * This entry re-exports the full public API. Because those re-exports pull the\n * feature modules into this chunk, this entry is the \"full\" bundle; the\n * `loader` entry stays lean so its features split into on-demand chunks.\n */\n\nimport { QaidFeedback } from \"./embed\";\nimport { autoInit } from \"./bootstrap\";\n\nexport { QaidFeedback };\nexport type {\n FeedbackConfig,\n QuestsLaunchConfig,\n ResolvedFeedbackConfig,\n ConsoleError,\n NetworkError,\n FeedbackData,\n SelectedBounds,\n FeedbackPayload,\n FeedbackResponse,\n FeedbackMessagePayload,\n} from \"./types\";\nexport { captureNetworkErrors } from \"./network-capture\";\nexport type { NetworkCapture } from \"./network-capture\";\nexport { createVideoRecorder, isVideoRecordingSupported, getSupportedMimeType } from \"./video-capture\";\nexport type { VideoRecorder, VideoRecorderOptions } from \"./video-capture\";\nexport { captureDomScreenshot, isDomScreenshotSupported } from \"./screenshot-dom\";\nexport type { DomScreenshotOptions } from \"./screenshot-dom\";\nexport {\n openAnnotationEditor,\n compositeAnnotations,\n drawShape,\n rectFromPoints,\n AnnotationEditor,\n} from \"./annotate\";\nexport type {\n Shape,\n ShapeType,\n Point,\n AnnotationLabels,\n AnnotationEditorOptions,\n} from \"./annotate\";\n\n// Auto-initialize from a script tag (legacy UMD / full-bundle path).\nautoInit();\n","/**\n * Nano DOM factory — a purpose-built, construction-only helper for this embed.\n *\n * The embed builds all of its UI imperatively inside a Shadow DOM. That means\n * the same create → set class/style/attrs → wire events → append dance repeats\n * across every button, overlay, modal, and recording control. `h()` collapses\n * that dance into a single declarative expression.\n *\n * It is deliberately NOT a framework: no reactivity, no virtual DOM, no runtime\n * state. It only constructs. That fits what the embed actually needs (one-shot\n * DOM construction plus a few imperative updates), adds no dependency, and\n * keeps the package zero-dependency. Aliasing `document` here also lets the\n * minifier mangle the call site, which the global `document.createElement`\n * can't be.\n */\n\nconst d = document;\n\nexport interface ElProps {\n /** className */\n class?: string;\n /** Inline styles as a cssText string. */\n style?: string;\n /** innerHTML — for icons / trusted SVG markup. */\n html?: string;\n /** textContent. */\n text?: string;\n /** Attributes set via setAttribute (aria-*, data-*, type, role, …). */\n attrs?: Record<string, string>;\n /** Event listeners keyed by type. */\n on?: Record<string, (e: Event) => void>;\n}\n\ntype Child = Node | string | null | undefined | false;\n\n/**\n * Create an element, apply props, and append children. Falsy children are\n * skipped so conditional content reads cleanly (`cond && h(...)`).\n */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props?: ElProps | null,\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const el = d.createElement(tag);\n if (props) {\n if (props.class) el.className = props.class;\n if (props.style) el.style.cssText = props.style;\n if (props.html != null) el.innerHTML = props.html;\n if (props.text != null) el.textContent = props.text;\n if (props.attrs) {\n for (const k in props.attrs) el.setAttribute(k, props.attrs[k]);\n }\n if (props.on) {\n for (const k in props.on) el.addEventListener(k, props.on[k]);\n }\n }\n for (const child of children) {\n if (child != null && child !== false) el.append(child);\n }\n return el;\n}\n","/**\n * Video-recording subsystem — extracted from embed.ts so it lives in its own\n * lazily-loaded chunk. A thumbs-only visitor never downloads any of this; the\n * embed dynamically imports it the first time the record button is used (and\n * warms it on hover — see embed.ts prewarmVideo).\n *\n * The controller owns all recording state and UI (recorder, redaction picker,\n * indicator, preview, submit). It talks back to the embed through the narrow\n * `RecordingHost` interface, so embed internals stay private and the coupling\n * is explicit.\n */\n\nimport { h } from \"./dom\";\nimport { isEmbedElement } from \"./dom-utils\";\nimport { captureNetworkErrors, type NetworkCapture } from \"./network-capture\";\nimport type { VideoRecorder } from \"./video-capture\";\nimport type { ResolvedFeedbackConfig, EmbedState } from \"./types\";\nimport type { ConsoleCapture } from \"./console-capture\";\n\n/** The slice of the embed the recording subsystem needs. */\nexport interface RecordingHost {\n readonly config: ResolvedFeedbackConfig;\n readonly visitorId: string;\n readonly uid: string;\n readonly overlayShadowHost: HTMLDivElement | null;\n readonly consoleCapture: ConsoleCapture | null;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n readonly state: EmbedState;\n setState(state: EmbedState): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void;\n closeDialogA11y(): void;\n setButtonsDisabled(disabled: boolean): void;\n tryLaunchQuest(\n type: \"up\" | \"down\" | \"video\",\n feedbackId: number | string | null\n ): Promise<boolean>;\n}\n\nexport class RecordingController {\n private videoRecorder: VideoRecorder | null = null;\n private networkCapture: NetworkCapture | null = null;\n private recordedBlob: Blob | null = null;\n private recordingIndicator: HTMLDivElement | null = null;\n private videoPreview: HTMLDivElement | null = null;\n private isRecording = false;\n private isSendingVideo = false;\n\n // Pre-recording redaction picking.\n private redactPicks: Element[] = [];\n private redactPickerRoot: HTMLDivElement | null = null;\n private redactRafId = 0;\n private boundRedactClick = (e: MouseEvent): void => this.handleRedactPickClick(e);\n\n constructor(private host: RecordingHost) {}\n\n /** Escape-key handler; returns true when it consumed the key. */\n handleEscape(): boolean {\n if (this.isRecording) {\n void this.stopRecording();\n return true;\n }\n if (this.videoPreview) {\n this.cancelRecordingPreview();\n return true;\n }\n if (this.redactPickerRoot) {\n this.finishRedactionPicking(false);\n return true;\n }\n return false;\n }\n\n destroy(): void {\n if (this.redactRafId) cancelAnimationFrame(this.redactRafId);\n this.redactRafId = 0;\n document.removeEventListener(\"click\", this.boundRedactClick, true);\n if (this.redactPickerRoot) {\n this.redactPickerRoot.remove();\n this.redactPickerRoot = null;\n }\n this.redactPicks = [];\n this.cleanupRecording();\n this.removeVideoPreview();\n }\n\n async startRecording(redactionElements: Element[] = []): Promise<void> {\n // Don't start if already recording or in a targeting/modal flow.\n if (this.isRecording || this.host.state !== \"IDLE\") return;\n\n try {\n this.networkCapture = captureNetworkErrors();\n\n const { createVideoRecorder } = await import(\"./video-capture\");\n this.videoRecorder = createVideoRecorder({\n maxDuration: this.host.config.videoOptions.maxDuration,\n redactionElements,\n });\n\n this.videoRecorder.onTick((elapsed) => {\n this.updateRecordingTimer(elapsed);\n });\n\n // Handle unexpected stops (browser stop button, max duration).\n this.videoRecorder.onStop((blob) => {\n if (this.isRecording) {\n this.recordedBlob = blob;\n this.isRecording = false;\n this.host.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n this.host.setButtonsDisabled(false);\n\n if (blob && blob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n });\n\n await this.videoRecorder.start();\n this.isRecording = true;\n this.host.announceMsg(\"Recording started\");\n this.host.setButtonsDisabled(true);\n this.showRecordingIndicator();\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n } catch (error) {\n // User denied screen share, shared a disallowed surface (window / whole\n // screen), the browser cannot blur the picked areas, or another error\n // occurred. Surface the tab-only and no-blur messages so neither is a\n // silent no-op. (Matched by text: video-capture is a separate lazy\n // chunk, so importing its constant here would merge the two.)\n if (error instanceof Error && /current tab|cannot blur/i.test(error.message)) {\n this.host.announceMsg(error.message);\n }\n this.cleanupRecording();\n }\n }\n\n private async stopRecording(): Promise<void> {\n try {\n this.recordedBlob = await this.videoRecorder!.stop();\n } catch {\n this.recordedBlob = null;\n }\n\n this.isRecording = false;\n this.host.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n\n if (this.recordedBlob && this.recordedBlob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n\n /**\n * Pre-recording redaction picker. The user clicks page areas to blur; each\n * gets an outline that tracks its position, then \"Start recording\" hands the\n * picks to the recorder, which blurs their live bounding boxes so the\n * redaction follows the content as the page scrolls. \"Cancel\"/Escape aborts.\n */\n startPicking(): void {\n if (this.isRecording || this.host.state !== \"IDLE\") return;\n this.host.setState(\"REDACT_PICKING\");\n this.redactPicks = [];\n\n const root = this.host.ensureOverlayHost();\n\n // Layer holding the per-pick outlines (repositioned every frame).\n const boxHost = h(\"div\", { style: \"position:absolute;inset:0;\" });\n\n const container = h(\n \"div\",\n { class: \"qaid-redact-picker\", style: \"position:fixed;inset:0;pointer-events:none;\" },\n boxHost,\n // Control bar — the only pointer-interactive part of the overlay.\n h(\n \"div\",\n {\n style:\n \"position:fixed;left:50%;bottom:24px;transform:translateX(-50%);\" +\n \"display:flex;gap:12px;align-items:center;pointer-events:auto;\" +\n \"background:#0b1220;color:#fff;border:1px solid #ff6b6b;border-radius:9999px;\" +\n \"padding:10px 16px;font:600 13px system-ui,sans-serif;box-shadow:0 8px 30px rgba(0,0,0,.5);\",\n },\n h(\"span\", {\n text: \"Click sensitive areas to blur\",\n attrs: { \"data-qaid-redact-label\": \"\" },\n }),\n h(\"button\", {\n text: \"Start recording\",\n attrs: { type: \"button\" },\n style:\n \"cursor:pointer;border:0;border-radius:9999px;padding:8px 14px;background:#ff6b6b;color:#0b1220;font:inherit;\",\n on: { click: () => this.finishRedactionPicking(true) },\n }),\n h(\"button\", {\n text: \"Cancel\",\n attrs: { type: \"button\" },\n style: \"cursor:pointer;border:0;background:transparent;color:#9fb3c8;font:inherit;\",\n on: { click: () => this.finishRedactionPicking(false) },\n }),\n ),\n );\n\n root.appendChild(container);\n this.redactPickerRoot = container;\n\n // Keep the outlines glued to their elements while the user scrolls/picks.\n const tick = (): void => {\n this.renderRedactPicks(boxHost);\n this.redactRafId = requestAnimationFrame(tick);\n };\n tick();\n\n document.addEventListener(\"click\", this.boundRedactClick, true);\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n this.host.announceMsg(\"Click sensitive areas to blur, then start recording\");\n }\n\n private handleRedactPickClick(e: MouseEvent): void {\n const target = e.target instanceof Element ? e.target : null;\n // Ignore clicks on the embed's own UI. isEmbedElement catches the retargeted\n // shadow host (real browsers compose click events to it); the contains()\n // check catches the picker's own control-bar buttons when the event isn't\n // retargeted, so their handlers still fire instead of being swallowed here.\n if (!target || isEmbedElement(target) || this.redactPickerRoot?.contains(target)) return;\n e.preventDefault();\n e.stopPropagation();\n\n const idx = this.redactPicks.indexOf(target);\n if (idx >= 0) {\n this.redactPicks.splice(idx, 1);\n } else {\n this.redactPicks.push(target);\n }\n\n const label = this.redactPickerRoot?.querySelector(\"[data-qaid-redact-label]\");\n if (label) {\n const n = this.redactPicks.length;\n label.textContent =\n n === 0 ? \"Click sensitive areas to blur\" : `${n} area${n === 1 ? \"\" : \"s\"} will be blurred`;\n }\n }\n\n private renderRedactPicks(layer: HTMLElement): void {\n // Rebuild the outline boxes to match current picks and their live positions.\n layer.textContent = \"\";\n for (const el of this.redactPicks) {\n const r = el.getBoundingClientRect();\n if (r.width <= 0 || r.height <= 0) continue;\n layer.appendChild(\n h(\"div\", {\n style:\n `position:fixed;left:${r.left}px;top:${r.top}px;width:${r.width}px;height:${r.height}px;` +\n \"border:2px solid #ff6b6b;border-radius:4px;background:rgba(255,107,107,.18);pointer-events:none;\",\n }),\n );\n }\n }\n\n private finishRedactionPicking(record: boolean): void {\n if (this.redactRafId) cancelAnimationFrame(this.redactRafId);\n this.redactRafId = 0;\n document.removeEventListener(\"click\", this.boundRedactClick, true);\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n if (this.redactPickerRoot) {\n this.redactPickerRoot.remove();\n this.redactPickerRoot = null;\n }\n const picks = this.redactPicks;\n this.redactPicks = [];\n this.host.setState(\"IDLE\");\n if (record) {\n void this.startRecording(picks);\n }\n }\n\n private showRecordingIndicator(): void {\n const root = this.host.ensureOverlayHost();\n\n this.recordingIndicator = h(\n \"div\",\n { class: \"qaid-recording-indicator\", style: `z-index:${this.host.config.zIndex + 100}` },\n h(\"div\", { class: \"qaid-recording-dot\" }),\n h(\"span\", {\n class: \"qaid-recording-time\",\n text: this.formatTime(this.host.config.videoOptions.maxDuration),\n }),\n h(\"button\", {\n class: \"qaid-recording-stop\",\n text: \"Stop\",\n attrs: { type: \"button\" },\n on: { click: () => this.stopRecording() },\n }),\n );\n\n this.host.applyVars(this.recordingIndicator);\n\n // Enable pointer events on the overlay host for the recording indicator.\n if (this.host.overlayShadowHost) {\n this.host.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n\n root.appendChild(this.recordingIndicator);\n }\n\n private updateRecordingTimer(elapsed: number): void {\n if (!this.recordingIndicator) return;\n const timer = this.recordingIndicator.querySelector<HTMLSpanElement>(\".qaid-recording-time\");\n if (timer) {\n const remaining = Math.max(0, this.host.config.videoOptions.maxDuration - elapsed);\n timer.textContent = this.formatTime(remaining);\n // Announce the final countdown so it is perceivable non-visually.\n if (remaining > 0 && remaining <= 5) {\n this.host.announceMsg(`${remaining} second${remaining === 1 ? \"\" : \"s\"} remaining`);\n }\n }\n }\n\n private formatTime(seconds: number): string {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${m}:${s.toString().padStart(2, \"0\")}`;\n }\n\n private removeRecordingIndicator(): void {\n if (this.recordingIndicator) {\n this.recordingIndicator.remove();\n this.recordingIndicator = null;\n }\n }\n\n private showRecordingPreview(): void {\n const root = this.host.ensureOverlayHost();\n\n const videoUrl = URL.createObjectURL(this.recordedBlob!);\n const titleId = `qaid-video-title-${this.host.uid}`;\n\n const title = h(\"h3\", { text: \"Review your recording\", attrs: { id: titleId } });\n\n // Boolean media props must be set as properties (the `muted` attribute is\n // unreliable for autoplay), so build the element then assign them.\n const videoEl = h(\"video\");\n videoEl.src = videoUrl;\n videoEl.controls = true;\n videoEl.autoplay = true;\n videoEl.muted = true;\n\n const textarea = h(\"textarea\", {\n attrs: {\n placeholder: \"Optional: Describe the issue you recorded...\",\n \"aria-label\": \"Describe the issue you recorded\",\n },\n });\n\n const sendBtn = h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-send\",\n text: \"Send\",\n attrs: { type: \"button\" },\n on: { click: () => this.submitVideoFeedback(textarea.value.trim() || null, sendBtn) },\n });\n\n const box = h(\n \"div\",\n { class: \"qaid-video-preview-box\" },\n title,\n videoEl,\n textarea,\n h(\n \"div\",\n { class: \"qaid-video-preview-actions\" },\n h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-cancel\",\n text: \"Cancel\",\n attrs: { type: \"button\" },\n on: { click: () => this.cancelRecordingPreview() },\n }),\n h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-rerecord\",\n text: \"Re-record\",\n attrs: { type: \"button\" },\n on: {\n click: () => {\n this.cancelRecordingPreview();\n this.startRecording();\n },\n },\n }),\n sendBtn,\n ),\n );\n\n this.videoPreview = h(\n \"div\",\n { class: \"qaid-video-preview\", style: `z-index:${this.host.config.zIndex + 100}` },\n box,\n );\n\n this.host.applyVars(this.videoPreview);\n\n // Enable pointer events on the overlay host for the video preview.\n if (this.host.overlayShadowHost) {\n this.host.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n\n root.appendChild(this.videoPreview);\n\n // Dialog semantics + focus trap + background inert.\n this.host.openDialogA11y(box, { labelledbyId: title.id });\n\n // Listen for the escape key.\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private cancelRecordingPreview(): void {\n this.removeVideoPreview();\n this.cleanupRecording();\n }\n\n private removeVideoPreview(): void {\n // Release focus trap / inert and restore focus before removing the DOM.\n this.host.closeDialogA11y();\n if (this.videoPreview) {\n const videoEl = this.videoPreview.querySelector<HTMLVideoElement>(\"video\");\n if (videoEl?.src) {\n URL.revokeObjectURL(videoEl.src);\n }\n this.videoPreview.remove();\n this.videoPreview = null;\n }\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private async submitVideoFeedback(message: string | null, sendBtn: HTMLButtonElement): Promise<void> {\n if (!this.recordedBlob || this.isSendingVideo) return;\n\n this.isSendingVideo = true;\n sendBtn.disabled = true;\n sendBtn.textContent = \"Sending...\";\n\n const formData = new FormData();\n formData.append(\"video\", this.recordedBlob, `recording.${this.recordedBlob.type.includes(\"mp4\") ? \"mp4\" : \"webm\"}`);\n formData.append(\"pageUrl\", window.location.href);\n formData.append(\"visitorId\", this.host.visitorId);\n\n if (this.host.config.apiKey) {\n formData.append(\"apiKey\", this.host.config.apiKey);\n }\n if (message) {\n formData.append(\"message\", message);\n }\n if (this.host.consoleCapture) {\n formData.append(\"consoleErrors\", JSON.stringify(this.host.consoleCapture.errors));\n }\n if (this.networkCapture) {\n formData.append(\"networkErrors\", JSON.stringify(this.networkCapture.errors));\n }\n\n let videoFeedbackId: string | number | null = null;\n // Set when the upload failed: the reason shown to the visitor.\n let failure: string | null = null;\n try {\n const response = await fetch(`${this.host.config.endpoint}/video`, {\n method: \"POST\",\n body: formData,\n });\n\n if (response.ok) {\n this.host.announceMsg(\"Recording sent\");\n try {\n const data = (await response.json()) as { id?: string | number };\n videoFeedbackId = data?.id ?? null;\n } catch {\n // Non-JSON / no id — the quest can still launch, just unlinked.\n }\n } else {\n // A refusal (403 on a plan without video, 400, 413 too large, 429…)\n // used to close the preview exactly like a success.\n console.error(\"Failed to submit video feedback:\", await response.text());\n failure =\n response.status === 413\n ? \"This recording is too large to send. Try a shorter one.\"\n : \"Your recording could not be sent.\";\n }\n } catch (error) {\n console.error(\"Failed to submit video feedback:\", error);\n failure = \"Your recording could not be sent. Check your connection and try again.\";\n }\n\n this.isSendingVideo = false;\n\n if (failure) {\n this.showSendError(failure, sendBtn);\n return;\n }\n\n this.removeVideoPreview();\n this.cleanupRecording();\n\n // Once the recording is safely sent, launch the linked quest (if any),\n // passing the new feedback record id so its response is joinable.\n await this.host.tryLaunchQuest(\"video\", videoFeedbackId);\n }\n\n /**\n * Keep the preview open and say the upload failed. Closing it, as a success\n * does, told the visitor their recording had arrived when it had not. Send\n * is re-enabled for another try; Cancel and Re-record still work.\n */\n private showSendError(message: string, sendBtn: HTMLButtonElement): void {\n this.host.announceMsg(`Failed to send recording. ${message}`, true);\n sendBtn.disabled = false;\n sendBtn.textContent = \"Send\";\n\n const actions = this.videoPreview?.querySelector(\".qaid-video-preview-actions\");\n if (actions) {\n let note = this.videoPreview!.querySelector<HTMLElement>(\".qaid-video-error\");\n if (!note) {\n note = h(\"p\", { class: \"qaid-video-error\" });\n actions.before(note);\n }\n note.textContent = message;\n }\n }\n\n private cleanupRecording(): void {\n this.isRecording = false;\n this.removeRecordingIndicator();\n\n if (this.videoRecorder) {\n this.videoRecorder.destroy();\n this.videoRecorder = null;\n }\n if (this.networkCapture) {\n this.networkCapture.restore();\n this.networkCapture = null;\n }\n if (this.recordedBlob) {\n this.recordedBlob = null;\n }\n\n this.host.setButtonsDisabled(false);\n\n // Reset overlay host pointer events if nothing else needs them.\n if (this.host.overlayShadowHost && this.host.state === \"IDLE\") {\n this.host.overlayShadowHost.style.pointerEvents = \"none\";\n }\n }\n}\n","/**\n * Screenshot capture utility using native Screen Capture API\n */\n\nexport interface ScreenshotOptions {\n /** Quality of WebP compression (0-1). Default: 0.8 */\n quality?: number;\n /** Max width of the screenshot. Default: 1280 */\n maxWidth?: number;\n /** Max height of the screenshot. Default: 800 */\n maxHeight?: number;\n}\n\n/**\n * Captures a screenshot using the native Screen Capture API\n * Requires user permission but produces accurate results\n * Returns a base64-encoded WebP data URL\n */\nexport async function captureScreenshot(\n options: ScreenshotOptions = {}\n): Promise<string | null> {\n const { quality = 0.8, maxWidth = 1280, maxHeight = 800 } = options;\n\n try {\n // Check if API is available\n if (!navigator.mediaDevices?.getDisplayMedia) {\n console.warn('Screen Capture API not available');\n return null;\n }\n\n // Request screen capture - preferCurrentTab auto-selects current tab in supported browsers\n const stream = await navigator.mediaDevices.getDisplayMedia({\n preferCurrentTab: true,\n video: {\n displaySurface: 'browser',\n },\n } as DisplayMediaStreamOptions);\n\n // Get video track\n const track = stream.getVideoTracks()[0];\n const settings = track.getSettings();\n\n // Create video element\n const video = document.createElement('video');\n video.srcObject = stream;\n video.muted = true;\n\n await new Promise<void>((resolve) => {\n video.onloadedmetadata = () => {\n video.play();\n resolve();\n };\n });\n\n // Wait for video to have actual frame data\n await new Promise<void>((resolve) => {\n const checkFrame = () => {\n if (video.readyState >= 2) {\n resolve();\n } else {\n requestAnimationFrame(checkFrame);\n }\n };\n checkFrame();\n });\n\n // Extra delay to ensure frame is rendered\n await new Promise((resolve) => setTimeout(resolve, 100));\n\n // Calculate dimensions\n const srcWidth = settings.width || video.videoWidth;\n const srcHeight = settings.height || video.videoHeight;\n const scale = Math.min(maxWidth / srcWidth, maxHeight / srcHeight, 1);\n const width = Math.round(srcWidth * scale);\n const height = Math.round(srcHeight * scale);\n\n // Draw to canvas\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n track.stop();\n return null;\n }\n\n ctx.drawImage(video, 0, 0, width, height);\n\n // Stop the stream\n track.stop();\n\n return canvas.toDataURL('image/webp', quality);\n } catch (error) {\n console.warn('Screenshot capture failed:', error);\n return null;\n }\n}\n","/**\n * Feedback message modal — extracted from embed.ts into its own lazily-loaded\n * chunk. It's fetched the first time a message modal opens (after feedback is\n * submitted), and pre-warmed while the user is targeting an element.\n *\n * The controller owns the modal/backdrop DOM and the message PATCH flow, and\n * talks back to the embed through the narrow `ModalHost` interface.\n */\n\nimport { calculateModalAndArrowPosition } from \"./modal-positioning\";\nimport { h } from \"./dom\";\nimport {\n THUMBS_UP_ICON,\n THUMBS_DOWN_ICON,\n FEEDBACK_ICON,\n DONE_ICON,\n ERROR_ICON,\n} from \"./icons\";\nimport type {\n ResolvedFeedbackConfig,\n EmbedState,\n FeedbackData,\n SelectedBounds,\n} from \"./types\";\n\n/** The slice of the embed the modal needs. */\nexport interface ModalHost {\n readonly config: ResolvedFeedbackConfig;\n readonly uid: string;\n readonly isMobile: boolean;\n readonly state: EmbedState;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n /** Live references — the toggle mutates feedbackType; positioning reads bounds. */\n readonly feedbackData: FeedbackData;\n readonly selectedBounds: SelectedBounds;\n readonly feedbackId: number | null;\n setFeedbackId(id: number | null): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void;\n closeDialogA11y(): void;\n resetFeedbackUi(): void;\n}\n\nexport class ModalController {\n private modalContainer: HTMLDivElement | null = null;\n private backdrop: HTMLDivElement | null = null;\n\n constructor(private host: ModalHost) {}\n\n open(): void {\n const root = this.host.ensureOverlayHost();\n\n // Create backdrop\n this.backdrop = document.createElement(\"div\");\n this.backdrop.className = \"qaid-backdrop\";\n this.backdrop.style.zIndex = String(this.host.config.zIndex + 2);\n this.backdrop.style.background = `rgba(0, 0, 0, ${this.host.config.backdropOpacity})`;\n this.backdrop.addEventListener(\"click\", () => this.close());\n this.host.applyVars(this.backdrop);\n\n if (this.host.isMobile) {\n this.showBottomSheet();\n } else {\n this.showPositionedModal();\n }\n\n root.appendChild(this.backdrop);\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n /** Escape / external close. Runs the finalize-PATCH + teardown. */\n close(): void {\n // If the modal is open and we're closing without submitting, still finalize.\n if (this.host.state === \"MODAL_OPEN\" && this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message: null }),\n }).catch((err) => console.error(\"Failed to finalize feedback:\", err));\n }\n\n this.teardown();\n this.host.resetFeedbackUi();\n }\n\n /** DOM/listener teardown only — no PATCH, no state reset (for embed destroy). */\n destroy(): void {\n this.teardown();\n }\n\n private teardown(): void {\n this.host.closeDialogA11y();\n if (this.modalContainer) {\n this.modalContainer.remove();\n this.modalContainer = null;\n }\n if (this.backdrop) {\n this.backdrop.remove();\n this.backdrop = null;\n }\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private showBottomSheet(): void {\n const root = this.host.ensureOverlayHost();\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"qaid-bottom-sheet\";\n sheet.style.zIndex = String(this.host.config.zIndex + 3);\n\n sheet.appendChild(\n h(\n \"div\",\n { class: \"qaid-bottom-sheet-content\" },\n h(\"div\", { class: \"qaid-bottom-sheet-handle\" }),\n this.buildModalContent()\n )\n );\n\n this.host.applyVars(sheet);\n root.appendChild(sheet);\n this.modalContainer = sheet;\n\n this.setupModalInteractions();\n }\n\n private showPositionedModal(): void {\n const root = this.host.ensureOverlayHost();\n\n const { modal, arrow } = calculateModalAndArrowPosition(\n this.host.selectedBounds,\n window.innerWidth,\n window.innerHeight,\n {\n width: this.host.config.modalWidth,\n height: 280,\n arrowHeight: 12,\n gap: 8,\n viewportPadding: 16,\n }\n );\n\n this.modalContainer = document.createElement(\"div\");\n this.modalContainer.className = `qaid-modal-container qaid-${modal.position}`;\n this.modalContainer.style.top = `${modal.top}px`;\n this.modalContainer.style.left = `${modal.left}px`;\n this.modalContainer.style.zIndex = String(this.host.config.zIndex + 3);\n\n const arrowEl = document.createElement(\"div\");\n arrowEl.className = \"qaid-modal-arrow\";\n arrowEl.style.left = `${arrow.left}px`;\n\n const box = document.createElement(\"div\");\n box.className = \"qaid-modal-box\";\n box.appendChild(this.buildModalContent());\n\n this.modalContainer.appendChild(arrowEl);\n this.modalContainer.appendChild(box);\n this.host.applyVars(this.modalContainer);\n root.appendChild(this.modalContainer);\n\n this.setupModalInteractions();\n }\n\n /**\n * The modal's contents, built as nodes rather than an HTML string.\n *\n * Every piece of copy (title, subtitle, placeholder, button) goes in as\n * text, so a `<`, `&` or `\"` in a translation shows as written instead of\n * being parsed as markup or cutting an attribute short. Only the icons are\n * markup, because they are documented as SVG/HTML strings.\n */\n private buildModalContent(): DocumentFragment {\n const { config, uid } = this.host;\n const text = config.text;\n const type = this.host.feedbackData.feedbackType;\n const isUp = type === \"up\";\n const positiveIcon = config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = config.negativeIcon || THUMBS_DOWN_ICON;\n const toggleClass = config.buttonClass\n ? `qaid-type-toggle qaid-type-toggle-custom ${config.buttonClass} ${isUp ? \"qaid-btn-up\" : \"qaid-btn-down\"}`\n : `qaid-type-toggle ${isUp ? \"qaid-type-up\" : \"qaid-type-down\"}`;\n const toggleLabel = isUp ? \"Feedback type: positive\" : \"Feedback type: negative\";\n // Neutral (single-button) feedback has no sentiment to toggle — show a\n // static feedback icon in place of the up/down toggle.\n const header =\n type === \"neutral\"\n ? h(\"span\", {\n class: \"qaid-type-static\",\n html: config.feedbackIcon || FEEDBACK_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n })\n : h(\"button\", {\n class: toggleClass,\n html: isUp ? positiveIcon : negativeIcon,\n attrs: {\n type: \"button\",\n title: \"Click to switch\",\n \"aria-pressed\": String(isUp),\n \"aria-label\": toggleLabel,\n },\n });\n\n const fragment = document.createDocumentFragment();\n fragment.append(\n h(\n \"div\",\n { class: \"qaid-modal-header\" },\n header,\n h(\n \"div\",\n { class: \"qaid-modal-header-text\" },\n h(\"h3\", {\n class: \"qaid-modal-title\",\n text: text.modalTitle,\n attrs: { id: `qaid-modal-title-${uid}` },\n }),\n h(\"p\", {\n class: \"qaid-modal-subtitle\",\n text: text.modalSubtitle,\n attrs: { id: `qaid-modal-subtitle-${uid}` },\n })\n )\n ),\n h(\"textarea\", {\n class: \"qaid-textarea\",\n attrs: { \"aria-label\": text.modalSubtitle, placeholder: text.placeholder },\n }),\n h(\n \"div\",\n { class: \"qaid-btn-row\" },\n h(\"button\", {\n class: \"qaid-btn-submit\",\n text: text.skipButton,\n attrs: { type: \"button\" },\n })\n )\n );\n return fragment;\n }\n\n private setupModalInteractions(): void {\n // Dialog semantics + focus trap + background inert\n this.host.openDialogA11y(this.modalContainer!, {\n labelledbyId: `qaid-modal-title-${this.host.uid}`,\n describedbyId: `qaid-modal-subtitle-${this.host.uid}`,\n });\n\n const textarea =\n this.modalContainer!.querySelector<HTMLTextAreaElement>(\".qaid-textarea\");\n const submitBtn =\n this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-btn-submit\");\n if (textarea) {\n // Auto-focus\n setTimeout(() => textarea.focus(), 100);\n\n // Update button text based on content\n textarea.addEventListener(\"input\", () => {\n if (submitBtn) {\n submitBtn.textContent = textarea.value.trim()\n ? this.host.config.text.submitButton\n : this.host.config.text.skipButton;\n }\n });\n }\n\n if (submitBtn) {\n submitBtn.addEventListener(\"click\", () => {\n const message = textarea?.value.trim() || null;\n this.submitMessage(message);\n });\n }\n\n // Handle feedback type toggle\n const typeToggle = this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-type-toggle\");\n if (typeToggle) {\n typeToggle.addEventListener(\"click\", () => {\n const newType = this.host.feedbackData.feedbackType === \"up\" ? \"down\" : \"up\";\n this.host.feedbackData.feedbackType = newType;\n\n // Update UI — use different classes depending on custom buttonClass\n if (this.host.config.buttonClass) {\n typeToggle.classList.toggle(\"qaid-btn-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-btn-down\", newType === \"down\");\n } else {\n typeToggle.classList.toggle(\"qaid-type-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-type-down\", newType === \"down\");\n }\n const positiveIcon = this.host.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.host.config.negativeIcon || THUMBS_DOWN_ICON;\n typeToggle.innerHTML = newType === \"up\" ? positiveIcon : negativeIcon;\n\n // Update accessible state and announce the change\n const toggleLabel = newType === \"up\" ? \"Feedback type: positive\" : \"Feedback type: negative\";\n typeToggle.setAttribute(\"aria-pressed\", String(newType === \"up\"));\n typeToggle.setAttribute(\"aria-label\", toggleLabel);\n this.host.announceMsg(toggleLabel);\n\n // Update feedback on server\n if (this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ feedbackType: newType }),\n }).catch((err) => console.error(\"Failed to update feedback type:\", err));\n }\n });\n }\n }\n\n private async submitMessage(message: string | null): Promise<void> {\n // null = nothing was attempted (the initial POST never produced an id);\n // otherwise whether the server accepted the PATCH.\n let delivered: boolean | null = null;\n\n if (this.host.feedbackId) {\n try {\n const response = await fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n // fetch resolves on any HTTP reply. A 4xx/5xx means the server\n // refused or lost the message, so it is not a success.\n delivered = response.ok;\n if (!response.ok) {\n console.error(\"Failed to submit feedback message: HTTP\", response.status);\n }\n } catch (error) {\n console.error(\"Failed to submit feedback message:\", error);\n delivered = false;\n }\n // Clear feedbackId so close() doesn't send another PATCH.\n this.host.setFeedbackId(null);\n }\n\n // The message was lost. Closing quietly reads as success, so say so —\n // even with hideConfirmation, which only opts out of the success screen.\n if (delivered === false) {\n this.showResult(false);\n return;\n }\n\n // A submit that simply removes the modal reads as a failure — nothing\n // acknowledges that the message was sent. Show a success screen unless the\n // host opted out.\n //\n // With no feedbackId the initial POST never landed and there was nothing\n // to send the message to; that path closes exactly as it did before.\n if (delivered && !this.host.config.hideConfirmation) {\n this.showResult(true);\n return;\n }\n this.close();\n }\n\n /**\n * Replace the modal's contents with the outcome: a checkmark and a short\n * acknowledgement, or an error mark and a \"not sent\" notice.\n *\n * The submit button that had focus is gone by this point, so focus moves to\n * the heading (WCAG 2.4.3) and the message is announced. Mirrors the quests\n * embed's thank-you screen so the two products confirm the same way. Copy\n * goes in as text, never markup.\n */\n private showResult(delivered: boolean): void {\n const box = this.modalContainer?.querySelector(\".qaid-modal-box\")\n ?? this.modalContainer?.querySelector(\".qaid-bottom-sheet-content\")\n ?? this.modalContainer;\n\n if (!box) {\n // Nothing to draw into — never strand the user with an open modal.\n this.close();\n return;\n }\n\n const text = this.host.config.text;\n const heading = delivered ? text.confirmationTitle : text.errorTitle;\n const body = delivered ? text.confirmationMessage : text.errorMessage;\n\n const title = h(\"h3\", {\n class: \"qaid-confirm-title\",\n text: heading,\n attrs: { id: `qaid-confirm-title-${this.host.uid}`, tabindex: \"-1\" },\n });\n\n box.textContent = \"\";\n box.appendChild(\n h(\n \"div\",\n { class: delivered ? \"qaid-confirm\" : \"qaid-confirm qaid-confirm-error\" },\n h(\"span\", {\n class: \"qaid-confirm-icon\",\n html: delivered ? DONE_ICON : ERROR_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n }),\n title,\n h(\"p\", { class: \"qaid-confirm-message\", text: body }),\n h(\"button\", {\n class: \"qaid-btn-submit qaid-confirm-close\",\n text: text.confirmationClose,\n attrs: { type: \"button\" },\n on: { click: () => this.close() },\n })\n )\n );\n\n // Focus after paint, matching the quests embed.\n requestAnimationFrame(() => title.focus());\n this.host.announceMsg(`${heading} ${body}`, true);\n }\n}\n","/**\n * Element selector generation utilities\n */\n\n/** Data attributes to look for (in priority order) */\nexport const DATA_ATTRS = [\"data-comp\", \"data-qa\", \"data-testid\", \"data-id\"];\n\nexport interface ElementInfo {\n selector: string;\n text: string;\n dataAttr: string | null;\n}\n\n/**\n * Find the first matching data attribute on an element or its ancestors\n */\nexport function findDataAttribute(\n element: Element,\n stopAt: Element = document.body\n): string | null {\n let current: Element | null = element;\n\n while (current && current !== stopAt) {\n for (const attr of DATA_ATTRS) {\n const value = current.getAttribute(attr);\n if (value) {\n return `${attr}=\"${value}\"`;\n }\n }\n current = current.parentElement;\n }\n\n return null;\n}\n\n/**\n * Get truncated text content from an element\n */\nexport function getTruncatedText(element: Element, maxLength = 100): string {\n const textContent = element.textContent?.trim().slice(0, maxLength) || \"\";\n return textContent.length === maxLength ? textContent + \"...\" : textContent;\n}\n\n/**\n * Get the nth-child index of an element among its siblings\n */\nfunction getNthChildIndex(element: Element): number {\n let index = 1;\n let sibling = element.previousElementSibling;\n while (sibling) {\n index++;\n sibling = sibling.previousElementSibling;\n }\n return index;\n}\n\n/**\n * Generate an nth-child path from body to the element\n * This is always a valid CSS selector and uniquely identifies the element\n * Exported for testing\n */\nexport function generateNthChildPath(element: Element): string {\n const path: string[] = [];\n let current: Element | null = element;\n\n while (current && current !== document.body && current !== document.documentElement) {\n const tag = current.tagName.toLowerCase();\n const index = getNthChildIndex(current);\n path.unshift(`${tag}:nth-child(${index})`);\n current = current.parentElement;\n }\n\n return path.length > 0 ? `body > ${path.join(\" > \")}` : \"body\";\n}\n\n/**\n * Escape special CSS characters in a string (for IDs with special chars)\n */\nfunction escapeCSSIdentifier(str: string): string {\n return str.replace(/([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^`{|}~])/g, \"\\\\$1\");\n}\n\n/**\n * Generate a selector string for an element\n * Priority: data attribute > ID > nth-child path\n */\nexport function generateSelector(element: Element): string {\n // Try data attribute first (on element or ancestors) - most stable\n const dataAttr = findDataAttribute(element);\n if (dataAttr) {\n return `[${dataAttr}]`;\n }\n\n // Try ID (escaped for special characters)\n if (element.id) {\n return `#${escapeCSSIdentifier(element.id)}`;\n }\n\n // Fall back to nth-child path - always valid and unique\n return generateNthChildPath(element);\n}\n\n/**\n * Generate complete element info for feedback\n */\nexport function generateElementInfo(element: Element): ElementInfo {\n const text = getTruncatedText(element);\n const dataAttr = findDataAttribute(element);\n const selector = generateSelector(element);\n\n return { selector, text, dataAttr };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Keyboard element targeting */\n/* */\n/* The mouse targeting path in embed.ts resolves the target with */\n/* elementFromPoint(clientX, clientY). A keyboard-activated thumb produces a */\n/* synthetic click at (0, 0), so a keyboard-only user can never point at a */\n/* real element. This controller provides a coordinate-free alternative: */\n/* Tab / arrow keys move a highlight between candidate elements (seeded from */\n/* document.activeElement), Enter / Space selects, Escape cancels. It never */\n/* hides the cursor — it drives the *native* focus ring instead, so both */\n/* sighted keyboard users and screen readers can follow the highlight. */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Default set of \"targetable\" elements for keyboard navigation: focusable\n * controls, ARIA-role'd nodes, common semantic content, and anything already\n * carrying one of the stable data attributes we key selectors off of.\n */\nexport const TARGETABLE_SELECTOR: string = [\n \"a[href]\",\n \"button\",\n \"input:not([type=hidden])\",\n \"select\",\n \"textarea\",\n \"summary\",\n \"[tabindex]\",\n \"[role]\",\n \"img\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"p\",\n \"li\",\n \"label\",\n ...DATA_ATTRS.map((attr) => `[${attr}]`),\n].join(\",\");\n\n/** Tag names that already take keyboard focus without an added tabindex. */\nconst NATIVELY_FOCUSABLE = /^(?:A|BUTTON|INPUT|SELECT|TEXTAREA|SUMMARY)$/;\n\n/**\n * Best-effort visibility test used to keep invisible nodes out of the keyboard\n * candidate list. Deliberately layout-free (no getBoundingClientRect) so it is\n * stable in headless/test environments: it only rejects nodes that are\n * explicitly hidden via the `hidden` attribute, `aria-hidden`, or a computed\n * `display:none` / `visibility:hidden`.\n */\nexport function isElementVisible(element: Element): boolean {\n if (element.hasAttribute(\"hidden\")) return false;\n if (element.getAttribute(\"aria-hidden\") === \"true\") return false;\n const view = element.ownerDocument?.defaultView;\n if (view && typeof view.getComputedStyle === \"function\") {\n const style = view.getComputedStyle(element);\n if (style.display === \"none\" || style.visibility === \"hidden\") return false;\n }\n return true;\n}\n\nexport interface CollectTargetableOptions {\n /** Where to search for candidates (default `document.body`). */\n root?: ParentNode;\n /** CSS selector for candidate elements (default {@link TARGETABLE_SELECTOR}). */\n selector?: string;\n /** Return true to drop an element (e.g. the embed's own shadow hosts). */\n isExcluded?: (element: Element) => boolean;\n /** Visibility predicate (default {@link isElementVisible}). */\n isVisible?: (element: Element) => boolean;\n}\n\n/**\n * Collect the ordered list of elements a keyboard user can target. Document\n * order matches the natural Tab order closely enough for feedback targeting.\n */\nexport function collectTargetableElements(\n options: CollectTargetableOptions = {}\n): Element[] {\n const root = options.root ?? document.body;\n const selector = options.selector ?? TARGETABLE_SELECTOR;\n const isExcluded = options.isExcluded ?? (() => false);\n const isVisible = options.isVisible ?? isElementVisible;\n\n return Array.from(root.querySelectorAll(selector)).filter(\n (element) => !isExcluded(element) && isVisible(element)\n );\n}\n\nexport interface KeyboardTargetingOptions {\n /**\n * Explicit candidate list. When omitted, candidates are collected via\n * {@link collectTargetableElements} using `root` / `selector` / `isExcluded`\n * / `isVisible`.\n */\n candidates?: Element[];\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n root?: ParentNode;\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n selector?: string;\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n isExcluded?: (element: Element) => boolean;\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n isVisible?: (element: Element) => boolean;\n /**\n * Element to start the highlight on. Defaults to `document.activeElement`\n * when it is one of the candidates, otherwise the first candidate.\n */\n initial?: Element | null;\n /**\n * Move native focus to the highlighted element (default true). This is what\n * keeps a screen reader / focus ring on the highlight; non-focusable nodes\n * get a temporary `tabindex=\"-1\"` that is removed on {@link stop}. The cursor\n * is never hidden.\n */\n moveFocus?: boolean;\n /** Where the keydown listener is attached (default `document`). */\n eventTarget?: Document | HTMLElement;\n /** Fired whenever the highlighted candidate changes (and once on start). */\n onHighlight?: (element: Element, index: number) => void;\n /** Fired when the user commits a selection (Enter / Space). Auto-stops first. */\n onSelect: (element: Element) => void;\n /** Fired when the user cancels (Escape). Auto-stops first. */\n onCancel?: () => void;\n}\n\nexport interface KeyboardTargetingController {\n /** The (frozen) candidate list being navigated. */\n readonly candidates: readonly Element[];\n /** Index of the highlighted candidate, or -1 when there are none. */\n getIndex(): number;\n /** The highlighted candidate, or null when there are none. */\n getCurrent(): Element | null;\n /** Highlight the next candidate (wraps to the first). */\n next(): void;\n /** Highlight the previous candidate (wraps to the last). */\n prev(): void;\n /** Highlight a specific index (wraps out-of-range values). */\n moveTo(index: number): void;\n /** Commit the current candidate (fires onSelect) and stop. */\n select(): void;\n /** Cancel (fires onCancel) and stop. */\n cancel(): void;\n /**\n * Handle a keydown. Wired automatically to `eventTarget`; exposed so a host\n * can forward events from another surface (e.g. the embed's shadow root).\n */\n handleKey(event: KeyboardEvent): void;\n /** Remove the listener and restore any temporary tabindex. Idempotent. */\n stop(): void;\n}\n\n/**\n * Start a keyboard element-targeting session. The returned controller wires its\n * own capture-phase keydown listener on `eventTarget` and emits the initial\n * highlight synchronously before returning.\n */\nexport function startKeyboardTargeting(\n options: KeyboardTargetingOptions\n): KeyboardTargetingController {\n const candidates =\n options.candidates ??\n collectTargetableElements({\n root: options.root,\n selector: options.selector,\n isExcluded: options.isExcluded,\n isVisible: options.isVisible,\n });\n\n const moveFocus = options.moveFocus ?? true;\n const target: EventTarget = options.eventTarget ?? document;\n const addedTabindex = new Set<Element>();\n\n let index = -1;\n let stopped = false;\n\n // Seed the highlight from the currently focused element when it is a\n // candidate; this makes \"Tab to a control, then hit the thumb\" land on that\n // control rather than the top of the page.\n const seed =\n options.initial !== undefined\n ? options.initial\n : typeof document !== \"undefined\"\n ? document.activeElement\n : null;\n if (seed) {\n const seedIndex = candidates.indexOf(seed);\n if (seedIndex >= 0) index = seedIndex;\n }\n if (index < 0 && candidates.length > 0) index = 0;\n\n function focusCurrent(element: Element): void {\n if (!moveFocus) return;\n const focusable = element as HTMLElement;\n if (typeof focusable.focus !== \"function\") return;\n // Make non-focusable content (p, img, li, …) programmatically focusable so\n // the native focus ring and AT follow the highlight. Never hide the cursor.\n if (\n element.getAttribute(\"tabindex\") === null &&\n !NATIVELY_FOCUSABLE.test(element.tagName)\n ) {\n element.setAttribute(\"tabindex\", \"-1\");\n addedTabindex.add(element);\n }\n try {\n focusable.focus();\n } catch {\n /* focus can throw in detached/headless cases — highlight still works */\n }\n }\n\n function emitHighlight(): void {\n const element = candidates[index];\n if (!element) return;\n focusCurrent(element);\n options.onHighlight?.(element, index);\n }\n\n function moveTo(nextIndex: number): void {\n if (stopped || candidates.length === 0) return;\n const len = candidates.length;\n index = ((nextIndex % len) + len) % len; // wrap in both directions\n emitHighlight();\n }\n\n function next(): void {\n moveTo(index + 1);\n }\n\n function prev(): void {\n moveTo(index - 1);\n }\n\n function getCurrent(): Element | null {\n return candidates[index] ?? null;\n }\n\n function stop(): void {\n if (stopped) return;\n stopped = true;\n target.removeEventListener(\"keydown\", handleKey as EventListener, true);\n addedTabindex.forEach((element) => element.removeAttribute(\"tabindex\"));\n addedTabindex.clear();\n }\n\n function select(): void {\n if (stopped) return;\n const element = getCurrent();\n stop();\n if (element) options.onSelect(element);\n }\n\n function cancel(): void {\n if (stopped) return;\n stop();\n options.onCancel?.();\n }\n\n function handleKey(event: KeyboardEvent): void {\n if (stopped) return;\n switch (event.key) {\n case \"Tab\":\n event.preventDefault();\n event.stopPropagation();\n if (event.shiftKey) prev();\n else next();\n break;\n case \"ArrowDown\":\n case \"ArrowRight\":\n event.preventDefault();\n event.stopPropagation();\n next();\n break;\n case \"ArrowUp\":\n case \"ArrowLeft\":\n event.preventDefault();\n event.stopPropagation();\n prev();\n break;\n case \"Enter\":\n case \" \":\n case \"Spacebar\": // legacy key value\n event.preventDefault();\n event.stopPropagation();\n select();\n break;\n case \"Escape\":\n case \"Esc\": // legacy key value\n event.preventDefault();\n event.stopPropagation();\n cancel();\n break;\n default:\n break;\n }\n }\n\n target.addEventListener(\"keydown\", handleKey as EventListener, true);\n\n // Emit the initial highlight so the caller can position its highlight box.\n if (index >= 0) emitHighlight();\n\n return {\n candidates,\n getIndex: () => index,\n getCurrent,\n next,\n prev,\n moveTo,\n select,\n cancel,\n handleKey,\n stop,\n };\n}\n","/**\n * Element-targeting subsystem — extracted from embed.ts into its own lazily\n * loaded chunk (it also carries `element-selector`, ~14 KB). It's fetched the\n * first time the user starts targeting (and pre-warmed on thumb-button hover,\n * so the reticle appears without a perceptible wait).\n *\n * The controller owns the reticle/overlay, the mouse/touch/keyboard targeting\n * flows, and the selected-element marker; it talks back to the embed through\n * the narrow `TargetingHost` interface.\n */\n\nimport {\n generateElementInfo,\n startKeyboardTargeting,\n type KeyboardTargetingController,\n} from \"./element-selector\";\nimport {\n getElementAtPointUnderOverlay,\n isEmbedElement,\n getElementBounds,\n} from \"./dom-utils\";\nimport type {\n ResolvedFeedbackConfig,\n EmbedState,\n FeedbackData,\n FeedbackType,\n SelectedBounds,\n} from \"./types\";\n\n/** A touch that moves more than this (px) is a scroll, not a tap-to-select. */\nconst TOUCH_TAP_SLOP = 12;\n\n/** The slice of the embed the targeting subsystem needs. */\nexport interface TargetingHost {\n readonly config: ResolvedFeedbackConfig;\n readonly cssVars: Record<string, string>;\n readonly state: EmbedState;\n readonly shadowHost: HTMLDivElement | null;\n readonly overlayShadowHost: HTMLDivElement | null;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n /** Live references, mutated in place. */\n readonly feedbackData: FeedbackData;\n readonly selectedBounds: SelectedBounds;\n setState(state: EmbedState): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n clearActiveThumb(): void;\n submitFeedback(): Promise<void>;\n}\n\nexport class TargetingController {\n private mousePos = { x: 0, y: 0 };\n private touchStartPos: { x: number; y: number } | null = null;\n private overlayContainer: HTMLDivElement | null = null;\n private captureLayer: HTMLDivElement | null = null;\n private crosshairH: HTMLDivElement | null = null;\n private crosshairV: HTMLDivElement | null = null;\n private scope: HTMLDivElement | null = null;\n private highlightBox: HTMLDivElement | null = null;\n private marker: HTMLDivElement | null = null;\n private keyboardController: KeyboardTargetingController | null = null;\n\n private boundMouseMove = (e: MouseEvent): void => this.handleMouseMove(e);\n private boundClick = (e: MouseEvent): void => this.handleClick(e);\n private boundTouchStart = (e: TouchEvent): void => this.handleTouchStart(e);\n private boundTouchEnd = (e: TouchEvent): void => this.handleTouchEnd(e);\n\n constructor(private host: TargetingHost) {}\n\n startPointer(type: FeedbackType, e: MouseEvent): void {\n this.host.setState(\"TARGETING\");\n this.host.feedbackData.feedbackType = type;\n this.host.feedbackData.elementSelector = null;\n this.host.feedbackData.elementText = null;\n this.host.selectedBounds.visible = false;\n\n // Track initial mouse position from the click event\n this.mousePos.x = e.clientX;\n this.mousePos.y = e.clientY;\n\n // Add class to body (light DOM — for cursor override)\n document.body.classList.add(\"qaid-targeting\");\n if (type === \"up\") {\n document.body.classList.add(\"qaid-type-up\");\n } else {\n document.body.classList.remove(\"qaid-type-up\");\n }\n\n // Set targeting colors on body so .qaid-highlight rules can resolve them\n document.body.style.setProperty(\"--qaid-positive\", this.host.cssVars[\"--qaid-positive\"]);\n document.body.style.setProperty(\"--qaid-negative\", this.host.cssVars[\"--qaid-negative\"]);\n\n this.createTargetingOverlay();\n\n // Add event listeners on document — overlay is pointer-events:none so scroll works naturally\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n document.addEventListener(\"mousemove\", this.boundMouseMove);\n document.addEventListener(\"click\", this.boundClick, true);\n // Touch: tap to select (scrolling stays enabled). touchstart is passive\n // (we only read the point); touchend is non-passive so a tap can\n // preventDefault the synthesised click that would otherwise activate the\n // targeted element.\n document.addEventListener(\"touchstart\", this.boundTouchStart, { passive: true });\n document.addEventListener(\"touchend\", this.boundTouchEnd, { passive: false });\n }\n\n /**\n * Keyboard-driven targeting. Mirrors startPointer minus the mouse plumbing:\n * no `qaid-targeting` body class (keeps the cursor visible for keyboard\n * users), no mouse reticle, and no document mouse/click listeners. The\n * KeyboardTargetingController owns Tab/Arrow/Enter/Space/Escape.\n */\n startKeyboard(type: FeedbackType): void {\n this.host.setState(\"TARGETING\");\n this.host.feedbackData.feedbackType = type;\n this.host.feedbackData.elementSelector = null;\n this.host.feedbackData.elementText = null;\n this.host.selectedBounds.visible = false;\n\n document.body.style.setProperty(\"--qaid-positive\", this.host.cssVars[\"--qaid-positive\"]);\n document.body.style.setProperty(\"--qaid-negative\", this.host.cssVars[\"--qaid-negative\"]);\n\n // Build the highlight box, then hide the mouse reticle (meaningless here)\n this.createTargetingOverlay();\n if (this.crosshairH) this.crosshairH.style.display = \"none\";\n if (this.crosshairV) this.crosshairV.style.display = \"none\";\n if (this.scope) this.scope.style.display = \"none\";\n\n this.keyboardController = startKeyboardTargeting({\n isExcluded: (el) => isEmbedElement(el),\n onHighlight: (el) => {\n const rect = el.getBoundingClientRect();\n const b = this.highlightBox;\n if (b) {\n b.style.transform = `translate(${rect.left}px, ${rect.top}px)`;\n b.style.width = `${rect.width}px`;\n b.style.height = `${rect.height}px`;\n b.style.display = \"block\";\n }\n const { text } = generateElementInfo(el);\n this.host.announceMsg(`Targeting ${text || el.tagName.toLowerCase()}`);\n },\n onSelect: (el) => this.selectKeyboardTarget(el),\n onCancel: () => this.cancel(),\n });\n }\n\n private selectKeyboardTarget(el: Element): void {\n const bounds = getElementBounds(el, 8);\n Object.assign(this.host.selectedBounds, bounds, {\n clickX: bounds.x + bounds.width / 2,\n clickY: bounds.y + bounds.height / 2,\n visible: true,\n });\n\n const { selector, text } = generateElementInfo(el);\n this.host.feedbackData.elementSelector = selector;\n this.host.feedbackData.elementText = text;\n\n // Teardown (controller has already auto-stopped before onSelect fired)\n this.removeTargetingOverlay();\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n this.keyboardController = null;\n this.host.clearActiveThumb();\n\n this.host.setState(\"SELECTED\");\n this.showSelectedMarker();\n void this.host.submitFeedback();\n }\n\n private createTargetingOverlay(): void {\n const root = this.host.ensureOverlayHost();\n\n this.overlayContainer = document.createElement(\"div\");\n this.overlayContainer.className = `qaid-targeting-overlay qaid-type-${this.host.feedbackData.feedbackType}`;\n\n // Capture layer (pointer-events:none — scroll passes through naturally)\n this.captureLayer = document.createElement(\"div\");\n this.captureLayer.className = \"qaid-capture-layer\";\n\n // Vignette\n const vignette = document.createElement(\"div\");\n vignette.className = \"qaid-vignette\";\n\n // Crosshairs\n this.crosshairH = document.createElement(\"div\");\n this.crosshairH.className = \"qaid-crosshair-h\";\n\n this.crosshairV = document.createElement(\"div\");\n this.crosshairV.className = \"qaid-crosshair-v\";\n\n // Scope\n this.scope = document.createElement(\"div\");\n this.scope.className = \"qaid-scope\";\n this.scope.innerHTML = `\n <div class=\"qaid-scope-ring\"></div>\n <div class=\"qaid-scope-ring-inner\"></div>\n <div class=\"qaid-scope-dot\"></div>\n `;\n\n // Highlight box overlay (positioned over hovered elements)\n this.highlightBox = document.createElement(\"div\");\n this.highlightBox.className = \"qaid-highlight-box\";\n\n this.overlayContainer.appendChild(this.captureLayer);\n this.overlayContainer.appendChild(vignette);\n this.overlayContainer.appendChild(this.highlightBox);\n this.overlayContainer.appendChild(this.crosshairH);\n this.overlayContainer.appendChild(this.crosshairV);\n this.overlayContainer.appendChild(this.scope);\n\n // Position crosshairs immediately at current mouse position\n this.crosshairH.style.top = `${this.mousePos.y}px`;\n this.crosshairV.style.left = `${this.mousePos.x}px`;\n this.scope.style.left = `${this.mousePos.x}px`;\n this.scope.style.top = `${this.mousePos.y}px`;\n\n this.host.applyVars(this.overlayContainer);\n root.appendChild(this.overlayContainer);\n }\n\n private handleMouseMove(e: MouseEvent): void {\n this.updateReticleAt(e.clientX, e.clientY);\n }\n\n /** Move the crosshair/scope reticle and highlight the element under (x, y). */\n private updateReticleAt(x: number, y: number): void {\n this.mousePos.x = x;\n this.mousePos.y = y;\n\n if (this.crosshairH) this.crosshairH.style.top = `${y}px`;\n if (this.crosshairV) this.crosshairV.style.left = `${x}px`;\n if (this.scope) {\n this.scope.style.left = `${x}px`;\n this.scope.style.top = `${y}px`;\n }\n\n // Find element underneath — hide both shadow hosts\n if (this.captureLayer && this.host.shadowHost) {\n const hosts = [this.host.shadowHost, this.host.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const elementUnder = getElementAtPointUnderOverlay(x, y, hosts);\n\n if (elementUnder && !isEmbedElement(elementUnder)) {\n if (this.highlightBox) {\n const rect = elementUnder.getBoundingClientRect();\n this.highlightBox.style.transform = `translate(${rect.left}px, ${rect.top}px)`;\n this.highlightBox.style.width = `${rect.width}px`;\n this.highlightBox.style.height = `${rect.height}px`;\n this.highlightBox.style.display = \"block\";\n }\n } else if (this.highlightBox) {\n this.highlightBox.style.display = \"none\";\n }\n }\n }\n\n private handleClick(e: MouseEvent): void {\n e.preventDefault();\n e.stopPropagation();\n this.selectAt(e.clientX, e.clientY);\n }\n\n // ---- Touch targeting (iOS/iPadOS) ----\n private handleTouchStart(e: TouchEvent): void {\n const t = e.touches[0];\n if (!t) return;\n this.touchStartPos = { x: t.clientX, y: t.clientY };\n // Preview what's under the finger; don't preventDefault so scrolling works.\n this.updateReticleAt(t.clientX, t.clientY);\n }\n\n private handleTouchEnd(e: TouchEvent): void {\n // touches is empty on touchend; the lifted point is in changedTouches.\n const t = e.changedTouches[0];\n const start = this.touchStartPos;\n this.touchStartPos = null;\n if (!t || !start) return;\n // A drag beyond the slop threshold is a scroll, not a selection — ignore it.\n const moved = Math.hypot(t.clientX - start.x, t.clientY - start.y);\n if (moved > TOUCH_TAP_SLOP) return;\n e.preventDefault(); // suppress the tap's synthesised click\n this.selectAt(t.clientX, t.clientY);\n }\n\n /** Select the element under (x, y) and tear down targeting. */\n private selectAt(x: number, y: number): void {\n const hosts = [this.host.shadowHost, this.host.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const target = getElementAtPointUnderOverlay(x, y, hosts);\n\n if (!target || isEmbedElement(target)) {\n return;\n }\n\n const bounds = getElementBounds(target, 8);\n Object.assign(this.host.selectedBounds, bounds, { clickX: x, clickY: y, visible: true });\n\n const { selector, text } = generateElementInfo(target);\n this.host.feedbackData.elementSelector = selector;\n this.host.feedbackData.elementText = text;\n\n // Remove targeting overlay and document listeners\n this.stopTargetingListeners();\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n\n this.host.setState(\"SELECTED\");\n this.host.clearActiveThumb();\n\n this.showSelectedMarker();\n void this.host.submitFeedback();\n }\n\n private stopTargetingListeners(): void {\n this.removeTargetingOverlay();\n this.touchStartPos = null;\n document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\n document.removeEventListener(\"touchstart\", this.boundTouchStart);\n document.removeEventListener(\"touchend\", this.boundTouchEnd);\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n cancel(): void {\n this.keyboardController?.stop();\n this.keyboardController = null;\n this.stopTargetingListeners();\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n this.host.clearActiveThumb();\n\n this.host.setState(\"IDLE\");\n this.host.feedbackData.feedbackType = null;\n this.host.feedbackData.elementSelector = null;\n this.host.feedbackData.elementText = null;\n this.host.selectedBounds.visible = false;\n }\n\n private removeTargetingOverlay(): void {\n if (this.overlayContainer) {\n this.overlayContainer.remove();\n this.overlayContainer = null;\n }\n this.captureLayer = null;\n this.crosshairH = null;\n this.crosshairV = null;\n this.scope = null;\n }\n\n private showSelectedMarker(): void {\n const root = this.host.ensureOverlayHost();\n\n this.marker = document.createElement(\"div\");\n this.marker.className = \"qaid-selected-marker\";\n this.marker.style.left = `${this.host.selectedBounds.x}px`;\n this.marker.style.top = `${this.host.selectedBounds.y}px`;\n this.marker.style.width = `${this.host.selectedBounds.width}px`;\n this.marker.style.height = `${this.host.selectedBounds.height}px`;\n this.marker.style.zIndex = String(this.host.config.zIndex + 1);\n\n this.host.applyVars(this.marker);\n root.appendChild(this.marker);\n }\n\n hideMarker(): void {\n if (this.marker) {\n this.marker.remove();\n this.marker = null;\n }\n }\n\n /** Full teardown for embed.destroy(). */\n destroy(): void {\n this.keyboardController?.stop();\n this.keyboardController = null;\n this.stopTargetingListeners();\n this.hideMarker();\n }\n}\n"],"names":["THUMBS_UP_ICON","THUMBS_DOWN_ICON","RECORD_ICON","FEEDBACK_ICON","RECTANGLE_ICON","ARROW_ICON","PENCIL_ICON","BLUR_ICON","UNDO_ICON","DONE_ICON","ERROR_ICON","SKIP_ICON","instanceCount","DEFAULT_BUTTON_STYLES","BUTTON_SIZES","ICON_SIZES","getLuminance","r","g","b","rs","gs","bs","c","parseColor","color","hex","fullHex","num","match","getContrastTextColor","bgColor","rgb","buildCssVars","options","positiveColor","negativeColor","markerColor","buttonSize","modalWidth","backdropOpacity","fontFamily","fontSize","btnSize","iconSize","markerTextColor","applyCssVars","el","vars","name","value","getEmbedStyles","shadowStyles","injectStyles","style","lightStyles","removeStyles","POLITE_ATTR","VISUALLY_HIDDEN_CSS","FOCUSABLE_SELECTOR","ownerDoc","root","getLiveRegion","assertive","kind","existing","region","announce","message","opts","isDisabled","isHidden","node","getFocusable","container","getDeepActiveElement","doc","active","createFocusTrap","addedTabindex","onKeydown","event","focusable","first","last","withinTrap","initial","saveFocus","restoreFocus","applyDialog","topLevelAncestor","body","parent","setBackgroundInert","except","keep","changed","child","prevInert","prevAriaHidden","entry","DEFAULT_DIMENSIONS","calculateVerticalPosition","bounds","viewportHeight","totalHeight","gap","viewportPadding","spaceAbove","spaceBelow","position","top","calculateHorizontalPosition","viewportWidth","left","calculateArrowPosition","modalLeft","minOffset","arrowWidth","arrowLeft","calculateModalPosition","dimensions","vertical","calculateTooltipPosition","anchor","tooltip","viewport","calculateModalAndArrowPosition","modal","MAX_MESSAGES","captureConsoleErrors","onError","errors","originalError","originalWarn","originalLog","capture","level","args","error","arg","createElement","tag","attrs","children","key","isMobileViewport","breakpoint","getElementAtPointUnderOverlay","x","y","hosts","saved","h","element","i","isEmbedElement","getElementBounds","padding","rect","DEFAULT_QUESTS_MODULE_URL","defaultImporter","url","importer","modulePromise","cachedUrl","loadQuestsModule","moduleUrl","mod","m","err","launchQuest","base","metadata","VISITOR_ID_KEY","HIDE_FEEDBACK_KEY","DEFAULT_ANNOTATION_PALETTE","isTouchPrimaryDevice","getHideKey","apiKey","isHiddenByUser","setHiddenByUser","hidden","randomId","bytes","getOrCreateVisitorId","visitorId","QaidFeedback","config","_error","recording","videoCapture","screenshotDom","screenshot","fn","w","guarded","e","newDoc","userContainer","baseStyle","themeStyle","dirClass","offsetX","offsetY","ev","useCustomClass","btnBaseClass","tooltipText","fbWrapper","fbBtn","upWrapper","upBtn","downWrapper","downBtn","recordWrapper","recordBtn","rec","warmVideo","anchorRect","tooltipRect","type","buttonEl","keyboard","t","host","prevPointerEvents","openAnnotationEditor","annotate","msg","captureDomScreenshot","captureScreenshot","elementBounds","payload","response","data","q","feedbackId","questId","ModalController","self","id","TargetingController","targeting","state","RecordingController","disabled","btn","findCssFromSelector","selector","parseJsonConfig","configScript","textContent","parsed","parseDataAttributes","script","endpoint","zIndex","buttonClass","skipTargeting","hideConfirmation","singleButton","feedbackMode","incognito","modalTitle","confirmationTitle","confirmationMessage","confirmationClose","errorTitle","errorMessage","modalSubtitle","placeholder","submitButton","skipButton","positiveIcon","negativeIcon","feedbackIcon","feedbackLabel","annotationColor","screenshotQuality","screenshotMaxWidth","screenshotMaxHeight","captureVideo","redaction","hideThumbs","hideDismiss","videoMaxDuration","screenshotMethod","direction","cssSelector","questBase","questUp","questDown","questVideo","questApiKey","questModuleUrl","autoInit","run","jsonConfig","dataConfig","MAX_ENTRIES","MAX_BODY_SIZE","truncateBody","str","addEntry","safeReadBody","text","captureNetworkErrors","originalFetch","input","init","method","requestBody","responseBody","originalXHROpen","originalXHRSend","rest","xhr","isIOSDevice","ua","computeOrientationTransform","frameW","frameH","viewportW","viewportH","screenAngle","frameLandscape","viewportLandscape","drawRotatedFrame","ctx","source","getScreenAngle","orientation","waitForVideoDimensions","video","resolve","playSilently","buildOrientationCorrectedStream","displayStream","frameRate","canvas","transform","rafId","draw","stream","mapRectToFrame","scaleX","scaleY","right","bottom","drawRedactedFrame","elements","blurRadius","supportsFilter","box","buildRedactedStream","REDACTION_UNAVAILABLE","isVideoRecordingSupported","getSupportedMimeType","candidates","createVideoRecorder","maxDuration","videoBitsPerSecond","redactionElements","redactionBlurRadius","orientationCorrection","recorder","chunks","tickCallback","stopCallback","tickInterval","startTime","resolveStop","rejectStop","stopped","autoStopTimeout","cleanup","finishRecording","mimeType","videoTrack","surface","recordStream","corrected","redacted","blob","elapsed","reject","callback","SCRIPT_URL","LOAD_TIMEOUT","POLL_INTERVAL","loadPromise","isDomScreenshotSupported","loadHtml2Canvas","start","poll","quality","maxWidth","maxHeight","srcWidth","srcHeight","scale","scaledWidth","scaledHeight","scaledCanvas","BLUR_TILE","DEFAULT_STROKE","DEFAULT_PALETTE","COLOR_NAMES","colorName","DEFAULT_STROKE_WIDTH","DEFAULT_QUALITY","DEFAULT_LABELS","rectFromPoints","a","isDegenerate","shape","drawArrow","from","to","width","angle","head","drawPen","points","drawBlur","ty","tx","drawShape","loadImage","src","img","compositeAnnotations","dataUrl","shapes","editorSeq","AnnotationEditor","promise","tool","icon","label","action","extraClass","titleId","descId","title","desc","stage","tools","colors","skipBtn","doneBtn","actions","LABEL_KEY","s","p","target","out","d","props","k","boxHost","tick","idx","n","layer","record","picks","timer","remaining","seconds","videoUrl","videoEl","textarea","sendBtn","formData","videoFeedbackId","failure","note","track","settings","checkFrame","height","sheet","arrow","arrowEl","uid","isUp","toggleClass","toggleLabel","header","fragment","submitBtn","typeToggle","newType","delivered","heading","DATA_ATTRS","findDataAttribute","stopAt","current","attr","getTruncatedText","maxLength","getNthChildIndex","index","sibling","generateNthChildPath","path","escapeCSSIdentifier","generateSelector","dataAttr","generateElementInfo","TARGETABLE_SELECTOR","NATIVELY_FOCUSABLE","isElementVisible","view","collectTargetableElements","isExcluded","isVisible","startKeyboardTargeting","moveFocus","seed","seedIndex","focusCurrent","emitHighlight","moveTo","nextIndex","len","next","prev","getCurrent","stop","handleKey","select","cancel","TOUCH_TAP_SLOP","vignette","elementUnder"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/icons.ts","../src/styles.ts","../src/a11y.ts","../src/modal-positioning.ts","../src/console-capture.ts","../src/dom-utils.ts","../src/quest-launcher.ts","../src/embed.ts","../src/bootstrap.ts","../src/network-capture.ts","../src/video-orientation.ts","../src/video-redaction.ts","../src/video-capture.ts","../src/screenshot-dom.ts","../src/annotate.ts","../src/index.ts","../src/dom.ts","../src/recording.ts","../src/screenshot.ts","../src/modal.ts","../src/element-selector.ts","../src/targeting.ts"],"sourcesContent":["/**\n * SVG icon strings for thumbs up/down buttons\n */\n\nexport const THUMBS_UP_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5\"/>\n</svg>`;\n\nexport const THUMBS_DOWN_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.737 3h4.018a2 2 0 01.485.06l3.76.94m-7 10v5a2 2 0 002 2h.095c.5 0 .905-.405.905-.905 0-.714.211-1.412.608-2.006L17 13V4m-7 10h2m5-10h2a2 2 0 012 2v6a2 2 0 01-2 2h-2.5\"/>\n</svg>`;\n\nexport const RECORD_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z\"/>\n</svg>`;\n\n/** Chat-bubble icon for the single, sentiment-free \"Feedback\" button. */\nexport const FEEDBACK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8 10h8M8 14h5m-9 6l3.5-3.5H18a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v14z\"/>\n</svg>`;\n\n/* ---------------------------------------------------------------------------\n * Screenshot annotation toolbar icons. Same 24×24 stroked style as the thumbs\n * icons above so they inherit `.qaid-icon` sizing and `currentColor`.\n * ------------------------------------------------------------------------- */\n\nexport const RECTANGLE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"4\" y=\"6\" width=\"16\" height=\"12\" rx=\"1\" stroke-linejoin=\"round\"/>\n</svg>`;\n\nexport const ARROW_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 19L19 5m0 0h-8m8 0v8\"/>\n</svg>`;\n\nexport const PENCIL_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 20h9M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z\"/>\n</svg>`;\n\nexport const BLUR_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 3s6 6.4 6 10a6 6 0 01-12 0c0-3.6 6-10 6-10z\"/>\n</svg>`;\n\nexport const UNDO_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-4\"/>\n</svg>`;\n\nexport const DONE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20 6L9 17l-5-5\"/>\n</svg>`;\n\n/** Exclamation mark for the \"message not sent\" screen. */\nexport const ERROR_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 6v8m0 4h.01\"/>\n</svg>`;\n\nexport const SKIP_ICON =`<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"qaid-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 6l12 12M18 6L6 18\"/>\n</svg>`;\n","/**\n * CSS styles for the feedback embed\n * Uses .qaid- prefix to avoid conflicts\n */\n\n// Import CSS as strings (vite handles minification in production)\nimport shadowStyles from \"./styles-shadow.css?inline\";\nimport lightStyles from \"./styles-light.css?inline\";\n\nlet instanceCount = 0;\n\n/** Default button styles included in shadow root */\nconst DEFAULT_BUTTON_STYLES = `button.qaid-btn{width:var(--qaid-btn-size);height:var(--qaid-btn-size);border-radius:50%;border:none;background:#f3f4f6;color:#374151;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);transition:background-color .2s,color .2s,transform .2s;-webkit-appearance:none;appearance:none;--qaid-hover-up-bg:var(--qaid-positive);--qaid-hover-up-color:#fff;--qaid-hover-down-bg:var(--qaid-negative);--qaid-hover-down-color:#fff}button.qaid-btn:hover{transform:scale(1.05)}button.qaid-btn-up:hover{background:var(--qaid-hover-up-bg);color:var(--qaid-hover-up-color)}button.qaid-btn-down:hover{background:var(--qaid-hover-down-bg);color:var(--qaid-hover-down-color)}button.qaid-btn-feedback:hover{background:var(--qaid-marker,#6365f1);color:var(--qaid-marker-text,#fff)}button.qaid-btn svg{width:var(--qaid-icon-size);height:var(--qaid-icon-size)}`;\n\nexport interface BuildCssVarsOptions {\n positiveColor?: string;\n negativeColor?: string;\n /** Color for selected element marker */\n markerColor?: string;\n /** Button size: small (36px), medium (48px), large (64px) */\n buttonSize?: \"small\" | \"medium\" | \"large\";\n /** Modal width in pixels */\n modalWidth?: number;\n /** Backdrop opacity (0-1) */\n backdropOpacity?: number;\n /** Font family */\n fontFamily?: string;\n /** Base font size in pixels */\n fontSize?: number;\n}\n\nconst BUTTON_SIZES = {\n small: 36,\n medium: 48,\n large: 64,\n} as const;\n\nconst ICON_SIZES = {\n small: 18,\n medium: 24,\n large: 32,\n} as const;\n\n/**\n * Calculate relative luminance of a color\n * Returns value between 0 (black) and 1 (white)\n */\nfunction getLuminance(r: number, g: number, b: number): number {\n const [rs, gs, bs] = [r, g, b].map((c) => {\n c = c / 255;\n return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n });\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n}\n\n/**\n * Parse a color string (hex or rgb) and return RGB values\n */\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n // Hex format\n if (color.startsWith(\"#\")) {\n const hex = color.slice(1);\n const fullHex = hex.length === 3\n ? hex.split(\"\").map((c) => c + c).join(\"\")\n : hex;\n const num = parseInt(fullHex, 16);\n return {\n r: (num >> 16) & 255,\n g: (num >> 8) & 255,\n b: num & 255,\n };\n }\n // RGB format\n const match = color.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/);\n if (match) {\n return {\n r: parseInt(match[1]),\n g: parseInt(match[2]),\n b: parseInt(match[3]),\n };\n }\n return null;\n}\n\n/**\n * Get the best contrasting text color (black or white) for a background\n */\nfunction getContrastTextColor(bgColor: string): string {\n const rgb = parseColor(bgColor);\n if (!rgb) return \"white\";\n const luminance = getLuminance(rgb.r, rgb.g, rgb.b);\n // Use white text on dark backgrounds, black on light\n return luminance > 0.4 ? \"black\" : \"white\";\n}\n\n/**\n * Build a map of CSS variable names to values for per-instance scoping\n */\nexport function buildCssVars(options: BuildCssVarsOptions = {}): Record<string, string> {\n const {\n positiveColor = \"rgb(0, 200, 83)\",\n negativeColor = \"rgb(255, 0, 0)\",\n markerColor = \"#6365f1\",\n buttonSize = \"medium\",\n modalWidth = 400,\n backdropOpacity = 0.3,\n fontFamily = \"system-ui, -apple-system, sans-serif\",\n fontSize = 16,\n } = options;\n\n const btnSize = BUTTON_SIZES[buttonSize];\n const iconSize = ICON_SIZES[buttonSize];\n const markerTextColor = getContrastTextColor(markerColor);\n\n return {\n \"--qaid-positive\": positiveColor,\n \"--qaid-negative\": negativeColor,\n \"--qaid-marker\": markerColor,\n \"--qaid-marker-text\": markerTextColor,\n \"--qaid-btn-size\": `${btnSize}px`,\n \"--qaid-icon-size\": `${iconSize}px`,\n \"--qaid-modal-width\": `${modalWidth}px`,\n \"--qaid-backdrop-opacity\": String(backdropOpacity),\n \"--qaid-font-family\": fontFamily,\n \"--qaid-font-size\": `${fontSize}px`,\n };\n}\n\n/**\n * Apply CSS variables to an element via inline style properties\n */\nexport function applyCssVars(el: HTMLElement, vars: Record<string, string>): void {\n for (const [name, value] of Object.entries(vars)) {\n el.style.setProperty(name, value);\n }\n}\n\n/**\n * Get the CSS string to inject into a shadow root\n * Includes base shadow styles + default button styles\n */\nexport function getEmbedStyles(): string {\n return shadowStyles + DEFAULT_BUTTON_STYLES;\n}\n\n/**\n * Inject light DOM styles (cursor override + highlight)\n * Reference-counted across instances\n */\nexport function injectStyles(): void {\n instanceCount++;\n if (instanceCount > 1) return;\n\n const style = document.createElement(\"style\");\n style.id = \"qaid-styles\";\n style.textContent = lightStyles;\n document.head.appendChild(style);\n}\n\n/**\n * Remove light DOM styles when last instance is destroyed\n */\nexport function removeStyles(): void {\n if (instanceCount <= 0) return;\n instanceCount--;\n if (instanceCount === 0) {\n // Optional: a client-side router (Astro's view transitions, any SPA that\n // swaps <head>) can remove this element out from under us, so by the time\n // a later destroy() runs there may be nothing to remove. Asserting it here\n // threw and took the rest of destroy() — and any re-mount after it — down.\n document.getElementById(\"qaid-styles\")?.remove();\n }\n}\n\n/** Reset internal state — for tests only */\nexport function _resetStylesState(): void {\n instanceCount = 0;\n const style = document.getElementById(\"qaid-styles\");\n if (style) {\n style.remove();\n }\n}\n","/**\n * Shared accessibility primitives for the qaid embeds.\n *\n * These helpers are intentionally framework-free and self-contained (no CSS\n * dependency) so they can be dropped into any open shadow root. They cover the\n * cross-cutting WCAG 2.2 AA infrastructure called for in the embed\n * accessibility plan: live-region announcements, focus management (trap,\n * save/restore), dialog semantics, and background isolation.\n */\n\n/** Host for the live regions: either a shadow root or a plain element. */\nexport type AnnounceRoot = ShadowRoot | HTMLElement;\n\n/** Options accepted by {@link announce}. */\nexport interface AnnounceOptions {\n /** Route the message through the assertive (role=\"alert\") region. */\n assertive?: boolean;\n}\n\n/** Options accepted by {@link applyDialog}. */\nexport interface ApplyDialogOptions {\n /** id of the element that labels the dialog (wired via aria-labelledby). */\n labelledbyId?: string;\n /** id of the element that describes the dialog (wired via aria-describedby). */\n describedbyId?: string;\n /** Fallback accessible name when no labelling element exists. */\n label?: string;\n}\n\n/** Handle returned by {@link createFocusTrap}. */\nexport interface FocusTrap {\n /** Remove the trap's key handler and clean up any tabindex it added. */\n release(): void;\n}\n\nconst POLITE_ATTR = \"data-qaid-a11y-live\";\n\nconst VISUALLY_HIDDEN_CSS =\n \"position:absolute;width:1px;height:1px;margin:-1px;padding:0;\" +\n \"border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);\" +\n \"white-space:nowrap;\";\n\n/**\n * Selector for natively focusable / author-focusable elements. Elements that\n * opt out with tabindex=\"-1\" are excluded below in {@link getFocusable}.\n */\nconst FOCUSABLE_SELECTOR = [\n \"a[href]\",\n \"button\",\n \"input\",\n \"textarea\",\n \"select\",\n \"[tabindex]\",\n].join(\",\");\n\nfunction ownerDoc(root: AnnounceRoot): Document {\n return root.ownerDocument || document;\n}\n\nfunction getLiveRegion(root: AnnounceRoot, assertive: boolean): HTMLElement {\n const kind = assertive ? \"assertive\" : \"polite\";\n const existing = root.querySelector<HTMLElement>(\n `[${POLITE_ATTR}=\"${kind}\"]`\n );\n if (existing) return existing;\n\n const doc = ownerDoc(root);\n const region = doc.createElement(\"div\");\n region.setAttribute(POLITE_ATTR, kind);\n region.setAttribute(\"role\", assertive ? \"alert\" : \"status\");\n region.setAttribute(\"aria-live\", assertive ? \"assertive\" : \"polite\");\n region.setAttribute(\"aria-atomic\", \"true\");\n region.style.cssText = VISUALLY_HIDDEN_CSS;\n root.appendChild(region);\n return region;\n}\n\n/**\n * Announce a message to assistive technology via a visually-hidden live region\n * mounted inside `root`. The two regions (polite role=\"status\" and assertive\n * role=\"alert\") are created lazily on first use and reused thereafter.\n *\n * The target region is cleared before the new text is written so that repeated\n * identical messages are re-announced rather than coalesced.\n */\nexport function announce(\n root: AnnounceRoot,\n message: string,\n opts: AnnounceOptions = {}\n): void {\n const region = getLiveRegion(root, !!opts.assertive);\n // Clear-then-set so an identical repeat is still perceived as a change.\n region.textContent = \"\";\n region.textContent = message;\n}\n\nfunction isDisabled(el: HTMLElement): boolean {\n if (el.hasAttribute(\"disabled\")) return true;\n const asInput = el as HTMLElement & { disabled?: boolean };\n return asInput.disabled === true;\n}\n\nfunction isHidden(el: HTMLElement): boolean {\n let node: HTMLElement | null = el;\n while (node) {\n if (node.hasAttribute(\"hidden\")) return true;\n const style = node.style;\n if (\n style &&\n (style.display === \"none\" ||\n style.visibility === \"hidden\" ||\n style.visibility === \"collapse\")\n ) {\n return true;\n }\n node = node.parentElement;\n }\n return false;\n}\n\n/**\n * Collect the focusable descendants of `container` in DOM order, excluding\n * disabled controls, elements opted out with tabindex=\"-1\", and elements that\n * are hidden (via the hidden attribute or an inline display/visibility rule on\n * themselves or an ancestor up to the document root).\n */\nexport function getFocusable(container: HTMLElement): HTMLElement[] {\n const nodes = Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n );\n return nodes.filter((el) => {\n if (el.getAttribute(\"tabindex\") === \"-1\") return false;\n if (el instanceof HTMLInputElement && el.type === \"hidden\") return false;\n if (el instanceof HTMLAnchorElement && !el.getAttribute(\"href\")) return false;\n if (isDisabled(el)) return false;\n if (isHidden(el)) return false;\n return true;\n });\n}\n\n/** Resolve the deepest active element, drilling through open shadow roots. */\nfunction getDeepActiveElement(doc: Document): HTMLElement | null {\n let active: Element | null = doc.activeElement;\n while (active && active.shadowRoot && active.shadowRoot.activeElement) {\n active = active.shadowRoot.activeElement;\n }\n return active instanceof HTMLElement ? active : null;\n}\n\n/**\n * Trap keyboard focus inside `container`: Tab / Shift+Tab cycle between the\n * first and last focusable descendants and can never leave the container. On\n * creation focus moves to the first focusable element, or to the container\n * itself (made programmatically focusable) when it has no focusable children.\n *\n * Call {@link FocusTrap.release} to remove the handler; pair it with\n * {@link restoreFocus} to return focus to the invoking control.\n */\nexport function createFocusTrap(container: HTMLElement): FocusTrap {\n const doc = container.ownerDocument || document;\n let addedTabindex = false;\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key !== \"Tab\") return;\n\n const focusable = getFocusable(container);\n if (focusable.length === 0) {\n // Nothing to move to — keep focus pinned on the container.\n event.preventDefault();\n container.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = getDeepActiveElement(doc);\n const withinTrap = active ? focusable.indexOf(active) !== -1 : false;\n\n if (event.shiftKey) {\n if (!withinTrap || active === first) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (!withinTrap || active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n container.addEventListener(\"keydown\", onKeydown);\n\n const initial = getFocusable(container);\n if (initial.length > 0) {\n initial[0].focus();\n } else {\n if (!container.hasAttribute(\"tabindex\")) {\n container.setAttribute(\"tabindex\", \"-1\");\n addedTabindex = true;\n }\n container.focus();\n }\n\n return {\n release(): void {\n container.removeEventListener(\"keydown\", onKeydown);\n if (addedTabindex) {\n container.removeAttribute(\"tabindex\");\n addedTabindex = false;\n }\n },\n };\n}\n\n/**\n * Capture the currently focused element (drilling through shadow roots) so it\n * can be restored later with {@link restoreFocus}. Returns null when focus is\n * on nothing focusable.\n */\nexport function saveFocus(): HTMLElement | null {\n return getDeepActiveElement(document);\n}\n\n/**\n * Restore focus to a previously saved element. Safe to call with null or an\n * element that has since been detached (a missing/throwing focus is ignored).\n */\nexport function restoreFocus(el: HTMLElement | null): void {\n if (!el || typeof el.focus !== \"function\") return;\n try {\n el.focus();\n } catch {\n // The element may have been removed; nothing more to do.\n }\n}\n\n/**\n * Apply dialog semantics to `el`: role=\"dialog\", aria-modal=\"true\", and the\n * naming/description wiring described by `opts`. Prefer aria-labelledby /\n * aria-describedby pointing at visible title/subtitle elements; fall back to\n * aria-label when no visible label element exists.\n */\nexport function applyDialog(el: HTMLElement, opts: ApplyDialogOptions = {}): void {\n el.setAttribute(\"role\", \"dialog\");\n el.setAttribute(\"aria-modal\", \"true\");\n if (opts.labelledbyId) {\n el.setAttribute(\"aria-labelledby\", opts.labelledbyId);\n }\n if (opts.describedbyId) {\n el.setAttribute(\"aria-describedby\", opts.describedbyId);\n }\n if (opts.label) {\n el.setAttribute(\"aria-label\", opts.label);\n }\n}\n\n/** Walk up to the body-level ancestor of `el`, crossing shadow boundaries. */\nfunction topLevelAncestor(el: HTMLElement, body: HTMLElement): HTMLElement | null {\n let node: Node | null = el;\n while (node && node !== body) {\n const parent: Node | null = node.parentNode;\n if (parent === body) return node as HTMLElement;\n if (parent && (parent as ShadowRoot).host) {\n node = (parent as ShadowRoot).host;\n continue;\n }\n if (!parent) return null;\n node = parent;\n }\n return null;\n}\n\n/**\n * Isolate the background from assistive technology while a dialog is open by\n * marking every top-level sibling of the dialog inert (with an aria-hidden\n * fallback). `except` is the open dialog (or any element inside it); the\n * top-level element that contains it is left interactive.\n *\n * Returns a restore function that reverts every attribute this call changed,\n * leaving elements that were already inert/hidden untouched.\n */\nexport function setBackgroundInert(except: HTMLElement): () => void {\n const doc = except.ownerDocument || document;\n const body = doc.body;\n if (!body) return () => {};\n\n const keep = topLevelAncestor(except, body);\n const changed: Array<{\n el: HTMLElement;\n prevInert: boolean;\n prevAriaHidden: string | null;\n }> = [];\n\n Array.from(body.children).forEach((child) => {\n if (!(child instanceof HTMLElement)) return;\n if (keep && child === keep) return;\n\n const prevInert = child.inert === true;\n const prevAriaHidden = child.getAttribute(\"aria-hidden\");\n // Only record (and later revert) elements we actually change.\n if (prevInert && prevAriaHidden === \"true\") return;\n\n child.inert = true;\n child.setAttribute(\"aria-hidden\", \"true\");\n changed.push({ el: child, prevInert, prevAriaHidden });\n });\n\n return function restore(): void {\n while (changed.length) {\n // The loop guard guarantees pop() returns an entry.\n const entry = changed.pop()!;\n entry.el.inert = entry.prevInert;\n if (entry.prevAriaHidden === null) {\n entry.el.removeAttribute(\"aria-hidden\");\n } else {\n entry.el.setAttribute(\"aria-hidden\", entry.prevAriaHidden);\n }\n }\n };\n}\n","/**\n * Modal positioning utilities\n */\n\nimport type { SelectedBounds } from \"./types\";\n\nexport interface ModalPosition {\n top: number;\n left: number;\n position: \"above\" | \"below\";\n}\n\nexport interface ArrowPosition {\n left: number;\n}\n\nexport interface ModalDimensions {\n width: number;\n height: number;\n arrowHeight: number;\n gap: number;\n viewportPadding: number;\n}\n\nconst DEFAULT_DIMENSIONS: ModalDimensions = {\n width: 400,\n height: 280,\n arrowHeight: 12,\n gap: 8,\n viewportPadding: 16,\n};\n\n/**\n * Calculate whether modal should go above or below the target element\n */\nexport function calculateVerticalPosition(\n bounds: SelectedBounds,\n viewportHeight: number,\n totalHeight: number,\n gap: number,\n viewportPadding: number\n): { top: number; position: \"above\" | \"below\" } {\n const spaceAbove = bounds.y;\n const spaceBelow = viewportHeight - (bounds.y + bounds.height);\n\n // Prefer below if there's enough space\n if (spaceBelow >= totalHeight + gap) {\n return {\n top: bounds.y + bounds.height + gap,\n position: \"below\",\n };\n }\n\n // Try above if there's enough space\n if (spaceAbove >= totalHeight + gap) {\n return {\n top: bounds.y - totalHeight - gap,\n position: \"above\",\n };\n }\n\n // Neither fits perfectly - clamp to viewport\n const position = spaceBelow > spaceAbove ? \"below\" : \"above\";\n let top: number;\n\n if (position === \"below\") {\n top = Math.min(\n bounds.y + bounds.height + gap,\n viewportHeight - totalHeight - viewportPadding\n );\n } else {\n top = Math.max(viewportPadding, bounds.y - totalHeight - gap);\n }\n\n return { top, position };\n}\n\n/**\n * Calculate horizontal position of modal, clamped to viewport\n */\nexport function calculateHorizontalPosition(\n bounds: SelectedBounds,\n viewportWidth: number,\n modalWidth: number,\n viewportPadding: number\n): number {\n const centerX = bounds.x + bounds.width / 2;\n let left = centerX - modalWidth / 2;\n\n // Clamp to viewport\n left = Math.max(viewportPadding, Math.min(left, viewportWidth - modalWidth - viewportPadding));\n\n return left;\n}\n\n/**\n * Calculate arrow position relative to modal\n * Points at the click position, not the element center\n */\nexport function calculateArrowPosition(\n bounds: SelectedBounds,\n modalLeft: number,\n modalWidth: number,\n minOffset = 24,\n arrowWidth = 24\n): number {\n // Use click position for arrow, not element center\n const targetX = bounds.clickX;\n // Subtract half the arrow width to center it on the target\n const arrowLeft = targetX - modalLeft - arrowWidth / 2;\n\n // Clamp arrow to stay within modal bounds\n return Math.max(minOffset, Math.min(arrowLeft, modalWidth - minOffset - arrowWidth / 2));\n}\n\n/**\n * Calculate complete modal position based on selected element bounds\n */\nexport function calculateModalPosition(\n bounds: SelectedBounds,\n viewportWidth: number,\n viewportHeight: number,\n dimensions: ModalDimensions = DEFAULT_DIMENSIONS\n): ModalPosition {\n const totalHeight = dimensions.height + dimensions.arrowHeight;\n\n const vertical = calculateVerticalPosition(\n bounds,\n viewportHeight,\n totalHeight,\n dimensions.gap,\n dimensions.viewportPadding\n );\n\n const left = calculateHorizontalPosition(\n bounds,\n viewportWidth,\n dimensions.width,\n dimensions.viewportPadding\n );\n\n return {\n top: vertical.top,\n left,\n position: vertical.position,\n };\n}\n\nexport interface TooltipPositionOptions {\n /** Gap in pixels between anchor and tooltip, and from viewport edges */\n gap?: number;\n}\n\n/**\n * Calculate tooltip position relative to an anchor, clamped to the viewport.\n * Pure function: no DOM access — caller passes measured rects and viewport size.\n *\n * Strategy:\n * - Place below the anchor by default\n * - Flip above if below would overflow vertically\n * - Clamp horizontally to viewport, then vertically to viewport\n */\nexport function calculateTooltipPosition(\n anchor: { top: number; bottom: number; left: number },\n tooltip: { width: number; height: number },\n viewport: { width: number; height: number },\n options: TooltipPositionOptions = {}\n): { top: number; left: number } {\n const gap = options.gap ?? 8;\n\n let top = anchor.bottom + gap;\n\n // Flip above if below overflows\n if (top + tooltip.height > viewport.height - gap) {\n top = anchor.top - tooltip.height - gap;\n }\n\n // Clamp horizontally\n let left = anchor.left;\n if (left < gap) {\n left = gap;\n } else if (left + tooltip.width > viewport.width - gap) {\n left = viewport.width - tooltip.width - gap;\n }\n\n // Clamp vertically\n if (top < gap) {\n top = gap;\n } else if (top + tooltip.height > viewport.height - gap) {\n top = viewport.height - tooltip.height - gap;\n }\n\n return { top, left };\n}\n\n/**\n * Calculate both modal and arrow positions\n */\nexport function calculateModalAndArrowPosition(\n bounds: SelectedBounds,\n viewportWidth: number,\n viewportHeight: number,\n dimensions: ModalDimensions = DEFAULT_DIMENSIONS\n): { modal: ModalPosition; arrow: ArrowPosition } {\n const modal = calculateModalPosition(bounds, viewportWidth, viewportHeight, dimensions);\n\n const arrowLeft = calculateArrowPosition(\n bounds,\n modal.left,\n dimensions.width\n );\n\n return {\n modal,\n arrow: { left: arrowLeft },\n };\n}\n","/**\n * Console capture utilities\n */\n\nimport type { ConsoleError } from \"./types\";\n\nconst MAX_MESSAGES = 20;\n\nexport interface ConsoleCapture {\n errors: ConsoleError[];\n restore: () => void;\n}\n\ntype ConsoleLevel = \"error\" | \"warn\" | \"log\";\n\n/**\n * Wrap console methods to capture messages\n */\nexport function captureConsoleErrors(\n onError?: (error: ConsoleError) => void\n): ConsoleCapture {\n const errors: ConsoleError[] = [];\n const originalError = console.error;\n const originalWarn = console.warn;\n const originalLog = console.log;\n\n const capture = (level: ConsoleLevel, args: unknown[]) => {\n const error: ConsoleError = {\n message: args.map((arg) => String(arg)).join(\" \"),\n timestamp: Date.now(),\n level,\n };\n\n // Keep max messages\n if (errors.length >= MAX_MESSAGES) {\n errors.shift();\n }\n errors.push(error);\n\n // Call callback if provided\n if (onError) {\n onError(error);\n }\n };\n\n console.error = function (...args: unknown[]) {\n capture(\"error\", args);\n originalError.apply(console, args);\n };\n\n console.warn = function (...args: unknown[]) {\n capture(\"warn\", args);\n originalWarn.apply(console, args);\n };\n\n console.log = function (...args: unknown[]) {\n capture(\"log\", args);\n originalLog.apply(console, args);\n };\n\n return {\n errors,\n restore: () => {\n console.error = originalError;\n console.warn = originalWarn;\n console.log = originalLog;\n },\n };\n}\n\n/**\n * Add an error to the captured errors array\n */\nexport function addError(errors: ConsoleError[], error: ConsoleError): void {\n if (errors.length >= MAX_MESSAGES) {\n errors.shift();\n }\n errors.push(error);\n}\n\n/**\n * Format console error for display\n */\nexport function formatError(error: ConsoleError): string {\n const date = new Date(error.timestamp);\n const time = date.toLocaleTimeString();\n const levelPrefix = error.level === \"error\" ? \"ERR\" : error.level === \"warn\" ? \"WRN\" : \"LOG\";\n return `[${time}] [${levelPrefix}] ${error.message}`;\n}\n","/**\n * DOM utility functions\n */\n\n/**\n * Create an element with attributes and optional children\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string)[]\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (key === \"className\") {\n el.className = value;\n } else {\n el.setAttribute(key, value);\n }\n }\n }\n\n if (children) {\n for (const child of children) {\n if (typeof child === \"string\") {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n }\n\n return el;\n}\n\n/**\n * Check if viewport width indicates mobile device\n */\nexport function isMobileViewport(breakpoint = 640): boolean {\n return typeof window !== \"undefined\" && window.innerWidth < breakpoint;\n}\n\n/**\n * Get element at point, temporarily hiding shadow hosts so elementFromPoint\n * sees through to the page elements underneath.\n * Uses visibility:hidden which skips the element in elementFromPoint without\n * triggering reflow.\n */\nexport function getElementAtPointUnderOverlay(\n x: number,\n y: number,\n hosts: HTMLElement[]\n): Element | null {\n const saved = hosts.map((h) => h.style.visibility);\n hosts.forEach((h) => (h.style.visibility = \"hidden\"));\n const element = document.elementFromPoint(x, y);\n hosts.forEach((h, i) => (h.style.visibility = saved[i]));\n return element;\n}\n\n/**\n * Check if an element is part of the feedback embed\n * With shadow DOM, embed elements are inside shadow hosts marked with\n * data-qaid-embed or data-qaid-embed-overlay\n */\nexport function isEmbedElement(element: Element | null): boolean {\n if (!element) return false;\n // Check if the element itself is a shadow host\n if (element.hasAttribute(\"data-qaid-embed\") || element.hasAttribute(\"data-qaid-embed-overlay\")) return true;\n // Check ancestors for shadow host\n return !!element.closest(\"[data-qaid-embed], [data-qaid-embed-overlay]\");\n}\n\n/**\n * Get element bounds with optional padding\n */\nexport function getElementBounds(\n element: Element,\n padding = 0\n): { x: number; y: number; width: number; height: number } {\n const rect = element.getBoundingClientRect();\n return {\n x: rect.left - padding,\n y: rect.top - padding,\n width: rect.width + padding * 2,\n height: rect.height + padding * 2,\n };\n}\n\n/**\n * Remove all elements with a specific class\n */\nexport function removeAllByClass(className: string): void {\n document.querySelectorAll(`.${className}`).forEach((el) => {\n el.classList.remove(className);\n });\n}\n","/**\n * Lazy loader + launcher for `@qaiddev/quests-embed`.\n *\n * `thumbs-embed` links a button to a quest but owns no questionnaire code\n * and takes no build-time dependency on the quests package. Instead the\n * quests widget is imported from a CDN the first time a quest is triggered,\n * so the shipped bundle stays zero-dependency and small.\n *\n * Only the tiny slice of the quests public API that we actually use is\n * modelled here structurally, so a version bump of the quests package that\n * keeps this surface stable needs no change in thumbs-embed.\n */\n\n/** Default ES-module URL, pinned to a compatible major on unpkg. */\nexport const DEFAULT_QUESTS_MODULE_URL =\n \"https://unpkg.com/@qaiddev/quests-embed@1/dist/qaid-quests.js\";\n\n/** The bit of a QaidQuests instance we hold onto. */\nexport interface QuestInstance {\n destroy(): void;\n}\n\n/** The bit of QaidQuests config we pass. Mirrors QuestsConfig loosely. */\nexport interface LaunchedQuestConfig {\n endpoint: string;\n configUrl: string;\n apiKey?: string;\n metadata?: Record<string, unknown>;\n onComplete?: (answers: Record<string, unknown>) => void;\n onClose?: () => void;\n}\n\ntype QuestsConstructor = new (config: LaunchedQuestConfig) => QuestInstance;\n\n/** Shape of the quests module's default/entry exports. */\nexport interface QuestsModule {\n QaidQuests: QuestsConstructor;\n}\n\ntype Importer = (url: string) => Promise<unknown>;\n\nconst defaultImporter: Importer = (url) =>\n // The URL is a runtime value, not a static specifier — keep Vite from\n // trying to analyze/bundle it.\n import(/* @vite-ignore */ url);\n\nlet importer: Importer = defaultImporter;\nlet modulePromise: Promise<QuestsModule> | null = null;\nlet cachedUrl: string | null = null;\n\n/**\n * @internal Test seam: swap the dynamic importer and clear the cache.\n * Pass `null` to restore the real dynamic `import()`. Not re-exported\n * from the package entry, so it isn't public API.\n */\nexport function _setQuestsImporter(fn: Importer | null): void {\n importer = fn ?? defaultImporter;\n modulePromise = null;\n cachedUrl = null;\n}\n\n/**\n * Import the quests module, caching the promise per URL. A failed load\n * clears the cache so a later trigger can retry (e.g. after a transient\n * network error) rather than being stuck with a rejected promise.\n */\nexport function loadQuestsModule(moduleUrl: string): Promise<QuestsModule> {\n if (modulePromise && cachedUrl === moduleUrl) return modulePromise;\n cachedUrl = moduleUrl;\n modulePromise = Promise.resolve(importer(moduleUrl))\n .then((mod) => {\n const m = mod as Partial<QuestsModule>;\n if (!m || typeof m.QaidQuests !== \"function\") {\n throw new Error(\"quests module has no QaidQuests export\");\n }\n return m as QuestsModule;\n })\n .catch((err) => {\n modulePromise = null;\n cachedUrl = null;\n throw err;\n });\n return modulePromise;\n}\n\nexport interface LaunchQuestOptions {\n /** Quest id to launch (the `[id]` in `{base}/{id}/definition`). */\n questId: string;\n /** Quest-service base URL, e.g. \"https://qaid.dev/api/quests\". */\n base: string;\n /** API key for the quest service (optional). */\n apiKey?: string;\n /** ES-module URL to load the quests widget from. */\n moduleUrl: string;\n /** Feedback record id to correlate the response with (optional). */\n feedbackId?: string | number | null;\n /** Called when the quest embed is torn down. */\n onClose?: () => void;\n}\n\n/**\n * Load the quests widget (if not already loaded) and open the given quest\n * as its own centered modal. Rejects if the module can't be loaded — the\n * caller should fall back to its normal UI.\n */\nexport async function launchQuest(opts: LaunchQuestOptions): Promise<QuestInstance> {\n const mod = await loadQuestsModule(opts.moduleUrl);\n const base = opts.base.replace(/\\/+$/, \"\");\n const metadata =\n opts.feedbackId != null ? { feedbackId: opts.feedbackId } : undefined;\n return new mod.QaidQuests({\n endpoint: `${base}/responses`,\n configUrl: `${base}/${encodeURIComponent(opts.questId)}/definition`,\n apiKey: opts.apiKey || undefined,\n metadata,\n onClose: opts.onClose,\n });\n}\n","import type {\n FeedbackConfig,\n ResolvedFeedbackConfig,\n FeedbackData,\n FeedbackType,\n SelectedBounds,\n EmbedState,\n FeedbackPayload,\n FeedbackResponse,\n} from \"./types\";\nimport { THUMBS_UP_ICON, THUMBS_DOWN_ICON, RECORD_ICON, FEEDBACK_ICON } from \"./icons\";\nimport { injectStyles, removeStyles, buildCssVars, applyCssVars, getEmbedStyles } from \"./styles\";\n// The element-targeting subsystem (with element-selector, ~14 KB) lives in a\n// lazily-loaded chunk (targeting.ts), pulled in the first time the user targets.\nimport type { TargetingController, TargetingHost } from \"./targeting\";\nimport {\n announce,\n createFocusTrap,\n saveFocus,\n restoreFocus,\n applyDialog,\n setBackgroundInert,\n type FocusTrap,\n} from \"./a11y\";\nimport { calculateTooltipPosition } from \"./modal-positioning\";\nimport type { ModalController, ModalHost } from \"./modal\";\nimport { captureConsoleErrors, type ConsoleCapture } from \"./console-capture\";\n// The whole video-recording subsystem lives in a lazily-loaded chunk\n// (recording.ts), pulled in the first time the record button is used. Only the\n// types are imported eagerly (erased at build time).\nimport type { RecordingController, RecordingHost } from \"./recording\";\nimport { isMobileViewport } from \"./dom-utils\";\n// Screenshot capture and the annotation editor are loaded on demand the first\n// time a screenshot is taken — see submitFeedback() / openAnnotationEditor().\nimport {\n launchQuest,\n DEFAULT_QUESTS_MODULE_URL,\n type QuestInstance,\n} from \"./quest-launcher\";\n\nconst VISITOR_ID_KEY = \"qaid_visitor_id\";\nconst HIDE_FEEDBACK_KEY = \"qaid_hide_feedback\";\n/** Default annotation swatches: red, amber, green, blue, near-black, white. */\nconst DEFAULT_ANNOTATION_PALETTE = [\n \"#ef4444\",\n \"#f59e0b\",\n \"#22c55e\",\n \"#3b82f6\",\n \"#111827\",\n \"#ffffff\",\n];\n\n// True when the primary pointer is touch (phone/tablet). On these devices the\n// Screen Capture API (getDisplayMedia) shows an intrusive \"start capturing\"\n// system prompt, dims the screen, and on iOS returns a rotated frame — so the\n// permission-free DOM/canvas screenshot is the right default there.\nfunction isTouchPrimaryDevice(): boolean {\n return (\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(pointer: coarse)\").matches\n );\n}\n\nfunction getHideKey(apiKey?: string): string {\n return apiKey ? `${HIDE_FEEDBACK_KEY}_${apiKey}` : HIDE_FEEDBACK_KEY;\n}\n\nexport function isHiddenByUser(apiKey?: string): boolean {\n try {\n return localStorage.getItem(getHideKey(apiKey)) === \"1\";\n } catch {\n return false;\n }\n}\n\nexport function setHiddenByUser(apiKey?: string, hidden = true): void {\n try {\n if (hidden) {\n localStorage.setItem(getHideKey(apiKey), \"1\");\n } else {\n localStorage.removeItem(getHideKey(apiKey));\n }\n } catch {\n // localStorage not available\n }\n}\n\n/**\n * A random v4 UUID.\n *\n * `crypto.randomUUID` exists only in secure contexts (HTTPS and localhost). On\n * a plain http:// page it is undefined, and calling it threw inside the\n * constructor, so the widget never appeared. `crypto.getRandomValues` has no\n * such restriction; `Math.random` covers a runtime with no `crypto` at all.\n * The id only groups one browser's feedback, so it needs to be unique, not\n * secret.\n */\nexport function randomId(): string {\n const c = typeof crypto !== \"undefined\" ? crypto : undefined;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n\n const bytes = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n // RFC 4122 version 4 / variant 1 bits, so the result reads as a real UUID.\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Get or create a visitor ID stored in localStorage\n */\nexport function getOrCreateVisitorId(): string {\n try {\n let visitorId = localStorage.getItem(VISITOR_ID_KEY);\n if (!visitorId) {\n visitorId = randomId();\n localStorage.setItem(VISITOR_ID_KEY, visitorId);\n }\n return visitorId;\n } catch {\n // localStorage not available, generate a session-only ID\n return randomId();\n }\n}\n\n/**\n * QaidFeedback - Standalone feedback collection embed\n */\nexport class QaidFeedback {\n private config: ResolvedFeedbackConfig;\n private state: EmbedState = \"IDLE\";\n private feedbackData: FeedbackData = {\n feedbackType: null,\n elementSelector: null,\n elementText: null,\n consoleErrors: [],\n };\n private selectedBounds: SelectedBounds = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n clickX: 0,\n clickY: 0,\n visible: false,\n };\n private feedbackId: number | null = null;\n // A quest launched from a button (in place of the message box), if any.\n private activeQuest: QuestInstance | null = null;\n private isMobile = false;\n private visitorId: string;\n\n // Console capture\n private consoleCapture: ConsoleCapture | null = null;\n\n // Video recording — the whole subsystem lives in a lazily-loaded chunk\n // (recording.ts); a thumbs-only visitor never downloads it.\n private recording: RecordingController | null = null;\n // Element targeting — also a lazily-loaded chunk (targeting.ts).\n private targeting: TargetingController | null = null;\n private targetingPrewarmed = false;\n // Preload-on-intent: warm the lazily-split feature chunks before first use.\n private prewarmHandle: number | null = null;\n private prewarmIsTimeout = false;\n private videoPrewarmed = false;\n private screenshotPrewarmed = false;\n\n // Shadow DOM\n private shadowHost: HTMLDivElement | null = null;\n private shadowRoot: ShadowRoot | null = null;\n\n // Overlay shadow DOM (always on document.body for full-page coverage)\n private overlayShadowHost: HTMLDivElement | null = null;\n private overlayShadowRoot: ShadowRoot | null = null;\n\n // DOM elements (inside shadow root)\n private buttonsContainer: HTMLDivElement | null = null;\n private isUserProvidedContainer = false;\n // The message modal lives in a lazily-loaded chunk (modal.ts).\n private modal: ModalController | null = null;\n private modalPrewarmed = false;\n private dismissBtn: HTMLButtonElement | null = null;\n\n // Per-instance CSS variables\n private cssVars: Record<string, string> = {};\n\n private activeThumbBtn: HTMLElement | null = null;\n // Tracks whether the pending activation came from the keyboard (Enter/Space)\n // rather than a pointer, so targeting can avoid elementFromPoint(0,0).\n private keyboardActivation = false;\n\n // Accessibility: dialog focus management\n private dialogTrigger: HTMLElement | null = null;\n private dialogTrap: FocusTrap | null = null;\n private dialogRestoreInert: (() => void) | null = null;\n\n // Unique id suffix for aria-labelledby/describedby references\n private readonly uid = Math.random().toString(36).slice(2, 9);\n\n // Bound event handlers\n private boundKeyDown: (e: KeyboardEvent) => void;\n private boundResize: () => void;\n\n // Start with buttons slid off-screen (localStorage dismiss, no animation)\n private _startDismissed = false;\n\n // DOM persistence (survives framework client-side navigation)\n private destroyed = false;\n private domObserver: MutationObserver | null = null;\n private boundBeforeSwap: ((e: Event) => void) | null = null;\n\n constructor(config: FeedbackConfig) {\n this.config = {\n endpoint: config.endpoint,\n apiKey: config.apiKey ?? \"\",\n container: config.container ?? \"\",\n buttonClass: config.buttonClass ?? \"\",\n direction: config.direction ?? \"horizontal\",\n position: config.position ?? \"bottom-right\",\n offset: {\n x: config.offset?.x ?? 16,\n y: config.offset?.y ?? 16,\n },\n zIndex: config.zIndex ?? 50,\n skipTargeting: config.skipTargeting ?? false,\n singleButton: config.singleButton ?? false,\n feedbackMode: config.feedbackMode ?? \"target\",\n colors: {\n positive: config.colors?.positive ?? \"rgb(0, 200, 83)\",\n negative: config.colors?.negative ?? \"rgb(255, 0, 0)\",\n marker: config.colors?.marker ?? \"#6365f1\",\n },\n buttonSize: config.buttonSize ?? \"medium\",\n text: {\n tooltip: config.text?.tooltip ?? \"\",\n modalTitle: config.text?.modalTitle ?? \"Thank you for your feedback!\",\n modalSubtitle: config.text?.modalSubtitle ?? \"Would you like to add a message to help us understand your feedback better?\",\n placeholder: config.text?.placeholder ?? \"Optional: Tell us more about your experience...\",\n submitButton: config.text?.submitButton ?? \"Submit\",\n skipButton: config.text?.skipButton ?? \"Skip\",\n positiveLabel: config.text?.positiveLabel ?? \"Send positive feedback\",\n negativeLabel: config.text?.negativeLabel ?? \"Send negative feedback\",\n recordLabel: config.text?.recordLabel ?? \"Record a screen recording\",\n dismissLabel: config.text?.dismissLabel ?? \"Hide Feedback\",\n feedbackLabel: config.text?.feedbackLabel ?? \"Send feedback\",\n confirmationTitle: config.text?.confirmationTitle ?? \"Thank you!\",\n confirmationMessage:\n config.text?.confirmationMessage ?? \"Your feedback has been received.\",\n confirmationClose: config.text?.confirmationClose ?? \"Close\",\n errorTitle: config.text?.errorTitle ?? \"Message not sent\",\n errorMessage:\n config.text?.errorMessage ??\n \"Something went wrong, so we did not get your message. Please try again later.\",\n },\n hideConfirmation: config.hideConfirmation ?? false,\n modalWidth: config.modalWidth ?? 400,\n backdropOpacity: config.backdropOpacity ?? 0.3,\n fontFamily: config.fontFamily ?? \"system-ui, -apple-system, sans-serif\",\n fontSize: config.fontSize ?? 16,\n captureScreenshot: config.captureScreenshot ?? false,\n annotate: config.annotate ?? true,\n annotationColor: config.annotationColor ?? config.colors?.marker ?? \"#6365f1\",\n annotationPalette: config.annotationPalette ?? DEFAULT_ANNOTATION_PALETTE,\n screenshotMethod: config.screenshotMethod ?? \"permission\",\n screenshotOptions: {\n quality: config.screenshotOptions?.quality ?? 0.8,\n maxWidth: config.screenshotOptions?.maxWidth ?? 1280,\n maxHeight: config.screenshotOptions?.maxHeight ?? 800,\n },\n incognito: config.incognito ?? false,\n hideDismiss: config.hideDismiss ?? false,\n positiveIcon: config.positiveIcon ?? \"\",\n negativeIcon: config.negativeIcon ?? \"\",\n feedbackIcon: config.feedbackIcon ?? \"\",\n hideThumbs: config.hideThumbs ?? false,\n css: config.css ?? \"\",\n captureVideo: config.captureVideo ?? false,\n videoOptions: {\n maxDuration: config.videoOptions?.maxDuration ?? 15,\n redaction: config.videoOptions?.redaction ?? false,\n },\n recordIcon: config.recordIcon ?? \"\",\n quests: {\n base: config.quests?.base ?? \"\",\n up: config.quests?.up ?? \"\",\n down: config.quests?.down ?? \"\",\n video: config.quests?.video ?? \"\",\n // Quest service reuses the feedback API key unless overridden.\n apiKey: config.quests?.apiKey ?? config.apiKey ?? \"\",\n moduleUrl: config.quests?.moduleUrl ?? DEFAULT_QUESTS_MODULE_URL,\n },\n };\n\n // Bind event handlers\n this.boundKeyDown = this.handleKeyDown.bind(this);\n this.boundResize = this.handleResize.bind(this);\n\n // Get or create visitor ID for anonymous feedback tracking\n this.visitorId = getOrCreateVisitorId();\n\n this.init();\n }\n\n private applyVars(el: HTMLElement): void {\n applyCssVars(el, this.cssVars);\n }\n\n /**\n * Announce a message via the shared visually-hidden live regions.\n * Prefer the overlay shadow root (which hosts every transient surface and\n * is never inerted by its own dialogs) so announcements are not suppressed\n * while a dialog aria-hides the main button host.\n */\n private announceMsg(message: string, assertive = false): void {\n const root = this.overlayShadowRoot ?? this.shadowRoot;\n if (root) announce(root, message, { assertive });\n }\n\n /**\n * Turn a transient surface into an accessible modal dialog: save the\n * invoking control, apply dialog semantics, trap focus, and inert the\n * background. Paired with closeDialogA11y() on every close path.\n */\n private openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void {\n this.dialogTrigger = saveFocus();\n applyDialog(container, opts);\n this.dialogTrap = createFocusTrap(container);\n this.dialogRestoreInert = setBackgroundInert(container);\n }\n\n private closeDialogA11y(): void {\n this.dialogTrap?.release();\n this.dialogTrap = null;\n if (this.dialogRestoreInert) {\n this.dialogRestoreInert();\n this.dialogRestoreInert = null;\n }\n restoreFocus(this.dialogTrigger);\n this.dialogTrigger = null;\n }\n\n private clearActiveThumb(): void {\n if (this.activeThumbBtn) {\n this.activeThumbBtn.setAttribute(\"aria-pressed\", \"false\");\n this.activeThumbBtn = null;\n }\n }\n\n private init(): void {\n // Inject shared light DOM styles (cursor + highlight, reference counted)\n injectStyles();\n\n // Build per-instance CSS variables\n this.cssVars = buildCssVars({\n positiveColor: this.config.colors.positive,\n negativeColor: this.config.colors.negative,\n markerColor: this.config.colors.marker,\n buttonSize: this.config.buttonSize,\n modalWidth: this.config.modalWidth,\n backdropOpacity: this.config.backdropOpacity,\n fontFamily: this.config.fontFamily,\n fontSize: this.config.fontSize,\n });\n\n // Restore dismiss preference from localStorage (skip if hideDismiss)\n if (!this.config.hideDismiss && isHiddenByUser(this.config.apiKey)) {\n this._startDismissed = true;\n }\n\n // Check mobile\n this.checkMobile();\n window.addEventListener(\"resize\", this.boundResize);\n\n // Create shadow host and embed\n this.createEmbed();\n\n // Capture console errors\n this.consoleCapture = captureConsoleErrors((_error) => {\n this.feedbackData.consoleErrors = this.consoleCapture?.errors ?? [];\n });\n this.feedbackData.consoleErrors = this.consoleCapture.errors;\n\n // Persist across client-side navigations (Astro View Transitions, etc.)\n this.observeDom();\n\n // Preload split feature chunks on idle when their config flags are set, so\n // the first screenshot / recording doesn't wait on a chunk fetch. Hovering\n // the record button also warms the video chunk (see createEmbed).\n if (\n (this.config.captureVideo && this.videoSupported()) ||\n this.config.captureScreenshot\n ) {\n this.schedulePrewarm(() => {\n if (this.config.captureVideo && this.videoSupported()) this.prewarmVideo();\n if (this.config.captureScreenshot) this.prewarmScreenshot();\n });\n }\n }\n\n /**\n * Best-effort preload of a lazily-split feature chunk so its first use is\n * instant. Warming fetches + compiles (and defines) the module; the feature\n * modules have no load-time side effects, so this is safe. Errors are\n * swallowed — a failed preload just falls back to an on-demand load.\n */\n private prewarmVideo(): void {\n if (this.videoPrewarmed) return;\n this.videoPrewarmed = true;\n // Warm both the recording controller chunk and the capture chunk it pulls\n // in on start, so the first recording begins without waiting on either.\n import(\"./recording\").catch(() => {});\n import(\"./video-capture\").catch(() => {});\n }\n\n private prewarmScreenshot(): void {\n if (this.screenshotPrewarmed) return;\n this.screenshotPrewarmed = true;\n (this.shouldCaptureViaDom()\n ? import(\"./screenshot-dom\")\n : import(\"./screenshot\")\n ).catch(() => {});\n if (this.config.annotate) import(\"./annotate\").catch(() => {});\n }\n\n /** Run fn when the main thread is idle; cancelled by destroy(). */\n private schedulePrewarm(fn: () => void): void {\n const w = window as unknown as {\n requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;\n };\n const guarded = (): void => {\n if (!this.destroyed) fn();\n };\n if (typeof w.requestIdleCallback === \"function\") {\n this.prewarmIsTimeout = false;\n this.prewarmHandle = w.requestIdleCallback(guarded, { timeout: 2000 });\n } else {\n this.prewarmIsTimeout = true;\n this.prewarmHandle = window.setTimeout(guarded, 1200);\n }\n }\n\n /**\n * Watch for the shadow hosts being removed from the DOM by framework\n * client-side navigation (e.g. Astro View Transitions swapping <body>\n * contents, or any SPA router that replaces DOM subtrees). If the host\n * is disconnected and destroy() wasn't called, re-append it.\n *\n * Also hooks into Astro's `astro:before-swap` when available, which\n * lets us carry elements into the new document before the swap happens\n * (avoids a flash of the widget disappearing and reappearing).\n */\n private observeDom(): void {\n // Astro View Transitions: carry hosts into the new document pre-swap\n this.boundBeforeSwap = (e: Event) => {\n const newDoc = (e as any).newDocument as Document | undefined;\n if (!newDoc || this.destroyed) return;\n if (this.shadowHost) newDoc.body.appendChild(this.shadowHost);\n if (this.overlayShadowHost) newDoc.body.appendChild(this.overlayShadowHost);\n };\n document.addEventListener(\"astro:before-swap\", this.boundBeforeSwap);\n\n // Generic fallback: MutationObserver to catch any framework removing our hosts\n this.domObserver = new MutationObserver(() => {\n if (this.destroyed) return;\n if (this.shadowHost && !this.shadowHost.isConnected) {\n document.body.appendChild(this.shadowHost);\n }\n if (this.overlayShadowHost && !this.overlayShadowHost.isConnected) {\n document.body.appendChild(this.overlayShadowHost);\n }\n });\n this.domObserver.observe(document.body, { childList: true });\n }\n\n private checkMobile(): void {\n this.isMobile = isMobileViewport();\n }\n\n private handleResize(): void {\n this.checkMobile();\n }\n\n private createEmbed(): void {\n // Create the shadow host element\n this.shadowHost = document.createElement(\"div\");\n this.shadowHost.setAttribute(\"data-qaid-embed\", \"\");\n this.shadowHost.style.position = \"static\";\n this.shadowHost.style.display = \"contents\";\n\n // Check if user provided a container selector\n let userContainer: HTMLElement | null = null;\n if (this.config.container) {\n userContainer = document.querySelector(this.config.container);\n }\n\n if (userContainer) {\n userContainer.appendChild(this.shadowHost);\n this.isUserProvidedContainer = true;\n // Container mode never needs a dismiss button\n this.config.hideDismiss = true;\n } else {\n // Set shadow host to fixed positioning for auto-created containers\n this.shadowHost.style.position = \"fixed\";\n this.shadowHost.style.display = \"block\";\n this.shadowHost.style.inset = \"0\";\n this.shadowHost.style.pointerEvents = \"none\";\n this.shadowHost.style.zIndex = String(this.config.zIndex);\n document.body.appendChild(this.shadowHost);\n }\n\n // Attach shadow root\n this.shadowRoot = this.shadowHost.attachShadow({ mode: \"open\" });\n\n // Inject base styles into shadow root\n const baseStyle = document.createElement(\"style\");\n baseStyle.textContent = getEmbedStyles();\n this.shadowRoot.appendChild(baseStyle);\n\n // Inject theme CSS if provided\n if (this.config.css) {\n const themeStyle = document.createElement(\"style\");\n themeStyle.textContent = this.config.css;\n this.shadowRoot.appendChild(themeStyle);\n }\n\n // Create buttons container inside shadow root\n const dirClass = this.config.direction === \"vertical\" ? \"qaid-vertical\" : \"\";\n if (this.isUserProvidedContainer) {\n this.buttonsContainer = document.createElement(\"div\");\n this.buttonsContainer.className = `qaid-buttons qaid-${this.config.position} ${dirClass}`.trim();\n if (this.config.incognito) {\n this.buttonsContainer.classList.add(\"qaid-incognito\");\n }\n } else {\n this.buttonsContainer = document.createElement(\"div\");\n this.buttonsContainer.className = `qaid-buttons qaid-auto-container qaid-${this.config.position} ${dirClass}${this.config.incognito ? \" qaid-incognito\" : \"\"}`.trim();\n this.buttonsContainer.style.pointerEvents = \"auto\";\n // Apply custom offset\n const { x: offsetX, y: offsetY } = this.config.offset;\n if (this.config.position.includes(\"right\")) {\n this.buttonsContainer.style.right = `${offsetX}px`;\n } else {\n this.buttonsContainer.style.left = `${offsetX}px`;\n }\n if (this.config.position.includes(\"bottom\")) {\n this.buttonsContainer.style.bottom = `${offsetY}px`;\n } else {\n this.buttonsContainer.style.top = `${offsetY}px`;\n }\n }\n\n // Apply per-instance CSS variables to buttons container\n this.applyVars(this.buttonsContainer);\n this.shadowRoot.appendChild(this.buttonsContainer);\n\n // Track input modality so a keyboard-activated control (Enter/Space) can be\n // distinguished from a pointer click. A keyboard-fired synthetic click has\n // clientX/clientY===0, which would break elementFromPoint targeting.\n this.buttonsContainer.addEventListener(\"keydown\", (ev) => {\n if (ev.key === \"Enter\" || ev.key === \" \" || ev.key === \"Spacebar\") {\n this.keyboardActivation = true;\n }\n });\n this.buttonsContainer.addEventListener(\"mousedown\", () => {\n this.keyboardActivation = false;\n });\n this.buttonsContainer.addEventListener(\"pointerdown\", () => {\n this.keyboardActivation = false;\n });\n\n // Determine button classes\n const useCustomClass = !!this.config.buttonClass;\n const btnBaseClass = useCustomClass\n ? `qaid-btn-structural ${this.config.buttonClass}`\n : \"qaid-btn\";\n\n // Determine tooltip text\n const defaultTooltip = \"Feedback for us?\";\n const tooltipText = this.config.text.tooltip || defaultTooltip;\n\n // Create shared tooltip element (inside shadow root)\n const tooltip = document.createElement(\"div\");\n tooltip.className = \"qaid-tooltip-text\";\n tooltip.textContent = tooltipText;\n this.applyVars(tooltip);\n this.shadowRoot.appendChild(tooltip);\n this.tooltipElement = tooltip;\n\n // Single sentiment-free \"Feedback\" button, or the thumbs up/down pair.\n if (this.config.singleButton) {\n const fbWrapper = document.createElement(\"div\");\n fbWrapper.className = \"qaid-tooltip-wrapper\";\n\n const fbBtn = document.createElement(\"button\");\n fbBtn.type = \"button\";\n fbBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-feedback` : \"qaid-btn qaid-btn-feedback\";\n fbBtn.setAttribute(\"aria-label\", this.config.text.feedbackLabel);\n fbBtn.innerHTML = this.config.feedbackIcon || FEEDBACK_ICON;\n fbBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"neutral\", e.currentTarget as HTMLElement, e));\n fbBtn.addEventListener(\"mouseenter\", () => {\n this.prewarmTargeting();\n this.showTooltip(fbBtn);\n });\n fbBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n fbWrapper.appendChild(fbBtn);\n\n this.buttonsContainer.appendChild(fbWrapper);\n } else if (!this.config.hideThumbs) {\n // Thumbs up button\n const upWrapper = document.createElement(\"div\");\n upWrapper.className = \"qaid-tooltip-wrapper\";\n\n const upBtn = document.createElement(\"button\");\n upBtn.type = \"button\";\n upBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-up` : \"qaid-btn qaid-btn-up\";\n upBtn.setAttribute(\"aria-label\", this.config.text.positiveLabel);\n upBtn.innerHTML = this.config.positiveIcon || THUMBS_UP_ICON;\n upBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"up\", e.currentTarget as HTMLElement, e));\n upBtn.addEventListener(\"mouseenter\", () => {\n this.prewarmTargeting();\n this.showTooltip(upBtn);\n });\n upBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n upWrapper.appendChild(upBtn);\n\n // Thumbs down button\n const downWrapper = document.createElement(\"div\");\n downWrapper.className = \"qaid-tooltip-wrapper\";\n\n const downBtn = document.createElement(\"button\");\n downBtn.type = \"button\";\n downBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-down` : \"qaid-btn qaid-btn-down\";\n downBtn.setAttribute(\"aria-label\", this.config.text.negativeLabel);\n downBtn.innerHTML = this.config.negativeIcon || THUMBS_DOWN_ICON;\n downBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"down\", e.currentTarget as HTMLElement, e));\n downBtn.addEventListener(\"mouseenter\", () => {\n this.prewarmTargeting();\n this.showTooltip(downBtn);\n });\n downBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n downWrapper.appendChild(downBtn);\n\n this.buttonsContainer.appendChild(upWrapper);\n this.buttonsContainer.appendChild(downWrapper);\n }\n\n // Record button (only when captureVideo is true and browser supports it)\n if (this.config.captureVideo && this.videoSupported()) {\n const recordWrapper = document.createElement(\"div\");\n recordWrapper.className = \"qaid-tooltip-wrapper\";\n\n const recordBtn = document.createElement(\"button\");\n recordBtn.type = \"button\";\n recordBtn.className = useCustomClass ? `${btnBaseClass} qaid-btn-record` : \"qaid-btn qaid-btn-record\";\n recordBtn.setAttribute(\"aria-label\", this.config.text.recordLabel);\n recordBtn.innerHTML = this.config.recordIcon || RECORD_ICON;\n recordBtn.addEventListener(\"click\", () => {\n void this.ensureRecording().then((rec) =>\n this.config.videoOptions.redaction ? rec.startPicking() : rec.startRecording()\n );\n });\n // Warm the video chunk on hover/focus/touch — the ~200ms before a click —\n // so recording starts without waiting on the chunk fetch.\n const warmVideo = (): void => this.prewarmVideo();\n recordBtn.addEventListener(\"mouseenter\", () => {\n warmVideo();\n this.showTooltip(recordBtn);\n });\n recordBtn.addEventListener(\"focus\", warmVideo);\n recordBtn.addEventListener(\"touchstart\", warmVideo, { passive: true });\n recordBtn.addEventListener(\"mouseleave\", () => this.hideTooltip());\n recordWrapper.appendChild(recordBtn);\n\n this.buttonsContainer.appendChild(recordWrapper);\n }\n\n // Dismiss (X) button — only in body/fixed mode (hidden for container embeds)\n if (!this.config.hideDismiss) {\n this.dismissBtn = document.createElement(\"button\");\n this.dismissBtn.type = \"button\";\n this.dismissBtn.className = \"qaid-dismiss-btn\";\n this.dismissBtn.setAttribute(\"aria-label\", this.config.text.dismissLabel);\n this.dismissBtn.title = this.config.text.dismissLabel;\n this.dismissBtn.innerHTML = `<svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 2l8 8M10 2l-8 8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>`;\n this.dismissBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.handleDismiss();\n });\n // Insert as first child so it sits to the left of thumb buttons\n this.buttonsContainer.insertBefore(this.dismissBtn, this.buttonsContainer.firstChild);\n\n // If previously dismissed via localStorage, start fully hidden so it\n // doesn't flash back on hover — matching the dismiss behavior.\n if (this._startDismissed) {\n this.buttonsContainer.classList.add(\"qaid-dismissed\");\n }\n }\n }\n\n /**\n * Lazily create a separate overlay shadow host on document.body.\n * This host contains all full-page elements (targeting overlay, marker,\n * backdrop, modal, recording indicator, video preview) so they escape\n * clip-path / transform containing blocks in user containers.\n */\n private ensureOverlayHost(): ShadowRoot {\n if (this.overlayShadowRoot) return this.overlayShadowRoot;\n\n this.overlayShadowHost = document.createElement(\"div\");\n this.overlayShadowHost.setAttribute(\"data-qaid-embed-overlay\", \"\");\n this.overlayShadowHost.style.position = \"fixed\";\n this.overlayShadowHost.style.inset = \"0\";\n this.overlayShadowHost.style.pointerEvents = \"none\";\n this.overlayShadowHost.style.zIndex = String(this.config.zIndex);\n document.body.appendChild(this.overlayShadowHost);\n\n this.overlayShadowRoot = this.overlayShadowHost.attachShadow({ mode: \"open\" });\n\n // Inject base styles into overlay shadow root\n const baseStyle = document.createElement(\"style\");\n baseStyle.textContent = getEmbedStyles();\n this.overlayShadowRoot.appendChild(baseStyle);\n\n // Inject theme CSS if provided\n if (this.config.css) {\n const themeStyle = document.createElement(\"style\");\n themeStyle.textContent = this.config.css;\n this.overlayShadowRoot.appendChild(themeStyle);\n }\n\n return this.overlayShadowRoot;\n }\n\n private tooltipElement: HTMLElement | null = null;\n\n private showTooltip(anchor: HTMLElement): void {\n const tooltip = this.tooltipElement!;\n\n // Make visible to measure\n tooltip.style.visibility = \"hidden\";\n tooltip.classList.add(\"qaid-tooltip-visible\");\n\n const anchorRect = anchor.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const { top, left } = calculateTooltipPosition(\n anchorRect,\n tooltipRect,\n { width: window.innerWidth, height: window.innerHeight }\n );\n\n tooltip.style.top = `${top}px`;\n tooltip.style.left = `${left}px`;\n tooltip.style.visibility = \"visible\";\n }\n\n private hideTooltip(): void {\n this.tooltipElement!.classList.remove(\"qaid-tooltip-visible\");\n }\n\n private handleDismiss(): void {\n // Fully remove the widget. It does not linger as a hover-reveal ghost\n // (that's `qaid-incognito`); once dismissed it stays gone until the\n // visitor clears the stored preference.\n this.buttonsContainer!.classList.add(\"qaid-dismissed\");\n setHiddenByUser(this.config.apiKey, true);\n }\n\n private handleThumbClick(type: FeedbackType, buttonEl: HTMLElement, e: MouseEvent): void {\n // If user clicks a thumb while in incognito mode, they want it back.\n // (Dismissed widgets are `display:none`, so this can't fire for them.)\n if (this.buttonsContainer?.classList.contains(\"qaid-incognito\")) {\n this.buttonsContainer.classList.remove(\"qaid-incognito\");\n setHiddenByUser(this.config.apiKey, false);\n }\n // A message modal follows this feedback flow — warm its chunk now.\n this.prewarmModal();\n // \"annotate\" mode and skipTargeting both bypass element targeting and go\n // straight to the (screenshot →) modal flow.\n if (this.config.skipTargeting || this.config.feedbackMode === \"annotate\") {\n this.submitDirectFeedback(type, buttonEl);\n } else {\n // Expose targeting on/off state on the trigger\n this.activeThumbBtn = buttonEl;\n buttonEl.setAttribute(\"aria-pressed\", \"true\");\n // A keyboard-activated <button> fires a synthetic click whose clientX/\n // clientY are (0,0). Route those through the keyboard path instead of\n // elementFromPoint(0,0). `keyboardActivation` is set by a preceding\n // Enter/Space keydown and cleared by any pointerdown/mousedown.\n const keyboard = this.keyboardActivation;\n this.keyboardActivation = false;\n // Load the targeting chunk (pre-warmed on hover) then start it.\n void this.ensureTargeting().then((t) => {\n if (this.destroyed) return;\n if (keyboard) t.startKeyboard(type);\n else t.startPointer(type, e);\n });\n }\n }\n\n private submitDirectFeedback(type: FeedbackType, buttonEl: HTMLElement): void {\n this.feedbackData.feedbackType = type;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n\n // Use the button's position for modal placement\n const rect = buttonEl.getBoundingClientRect();\n this.selectedBounds = {\n x: rect.left,\n y: rect.top,\n width: rect.width,\n height: rect.height,\n clickX: rect.left + rect.width / 2,\n clickY: rect.top + rect.height / 2,\n visible: false,\n };\n this.submitFeedback();\n }\n\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (e.key === \"Escape\") {\n // The recording controller owns Escape while recording / in preview /\n // picking; let it consume the key first.\n if (this.recording?.handleEscape()) return;\n if (this.state === \"TARGETING\") {\n this.targeting?.cancel();\n } else if (this.state === \"MODAL_OPEN\") {\n this.modal?.close();\n }\n }\n }\n\n\n /** Whether to capture the screenshot with the DOM/canvas method (html2canvas)\n * instead of the permission-based Screen Capture API. Explicit \"dom\" wins;\n * otherwise DOM is used on touch devices to avoid the getDisplayMedia prompt. */\n private shouldCaptureViaDom(): boolean {\n return this.config.screenshotMethod === \"dom\" || isTouchPrimaryDevice();\n }\n\n /**\n * Open the full-screen annotation editor over the captured screenshot,\n * reusing the overlay shadow host and the shared dialog a11y helpers.\n * Resolves with the composited WebP data URL, or null when the user skips\n * (caller keeps the original). Pointer events on the overlay host are\n * enabled while the editor is open and restored on close.\n */\n private async openAnnotationEditor(screenshot: string): Promise<string | null> {\n const root = this.ensureOverlayHost();\n const host = this.overlayShadowHost!;\n const prevPointerEvents = host.style.pointerEvents;\n host.style.pointerEvents = \"auto\";\n try {\n const { openAnnotationEditor } = await import(\"./annotate\");\n return await openAnnotationEditor({\n dataUrl: screenshot,\n root,\n quality: this.config.screenshotOptions.quality,\n color: this.config.annotationColor,\n palette: this.config.annotationPalette,\n applyVars: (el) => this.applyVars(el),\n announce: (msg, assertive) => this.announceMsg(msg, assertive),\n openDialog: (container, opts) => this.openDialogA11y(container, opts),\n closeDialog: () => this.closeDialogA11y(),\n });\n } catch (err) {\n // The annotate chunk failed to load / the editor threw — keep the\n // original screenshot rather than aborting the whole submission.\n console.error(\"Annotation editor failed to open:\", err);\n return null;\n } finally {\n host.style.pointerEvents = prevPointerEvents;\n }\n }\n\n private async submitFeedback(): Promise<void> {\n // Capture screenshot if enabled (server will gate by plan). \"annotate\" mode\n // always captures one to mark up, even without captureScreenshot set.\n let screenshot: string | null = null;\n const wantScreenshot =\n this.config.captureScreenshot || this.config.feedbackMode === \"annotate\";\n if (wantScreenshot) {\n // Loaded on demand so visitors who never submit a screenshot never\n // download the capture + annotation code. A capture failure (denied\n // permission, chunk load error) must never abort the feedback — we just\n // submit without a screenshot so the user still reaches the modal.\n try {\n if (this.shouldCaptureViaDom()) {\n const { captureDomScreenshot } = await import(\"./screenshot-dom\");\n screenshot = await captureDomScreenshot(this.config.screenshotOptions);\n } else {\n const { captureScreenshot } = await import(\"./screenshot\");\n screenshot = await captureScreenshot(this.config.screenshotOptions);\n }\n if (screenshot) this.announceMsg(\"Screenshot captured\");\n } catch (err) {\n console.error(\"Screenshot capture failed:\", err);\n screenshot = null;\n }\n }\n\n // Let the user mark up / redact the screenshot before it is submitted.\n // The message modal only opens after the POST below, so the composited\n // result lands in the same request — no backend change needed. On skip\n // the editor resolves null and the original screenshot is kept.\n if (screenshot && this.config.annotate) {\n screenshot = (await this.openAnnotationEditor(screenshot)) ?? screenshot;\n }\n\n // Include element bounds for server-side screenshot fallback\n // Only include if we have an element selector (element was targeted)\n const elementBounds = this.feedbackData.elementSelector\n ? {\n x: this.selectedBounds.x,\n y: this.selectedBounds.y,\n width: this.selectedBounds.width,\n height: this.selectedBounds.height,\n }\n : null;\n\n const payload: FeedbackPayload = {\n feedbackType: this.feedbackData.feedbackType!,\n pageUrl: window.location.href,\n apiKey: this.config.apiKey || undefined,\n elementSelector: this.feedbackData.elementSelector,\n elementText: this.feedbackData.elementText,\n consoleErrors: [...this.feedbackData.consoleErrors],\n screenWidth: window.innerWidth,\n screenHeight: window.innerHeight,\n clickX: this.selectedBounds.clickX,\n clickY: this.selectedBounds.clickY,\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n screenshot,\n visitorId: this.visitorId,\n // Include element bounds and user agent for server-side screenshot fallback\n elementBounds,\n userAgent: navigator.userAgent,\n };\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (response.ok) {\n const data: FeedbackResponse = await response.json();\n this.feedbackId = data.id;\n this.announceMsg(\"Feedback sent\");\n }\n } catch (error) {\n console.error(\"Failed to submit feedback:\", error);\n this.announceMsg(\"Failed to send feedback\", true);\n }\n\n // If this button is linked to a quest, launch it in place of the\n // message box. The feedback record already exists, so on success we\n // just tear down the targeting UI and hand off. On any load failure we\n // fall through to the classic message box so the user can still leave\n // an optional message.\n const type = this.feedbackData.feedbackType;\n // Quest links are keyed by up/down/video; neutral single-button feedback\n // has no quest mapping, so it always falls through to the message box.\n if (type && type !== \"neutral\" && (await this.tryLaunchQuest(type, this.feedbackId))) {\n this.resetFeedbackUi();\n return;\n }\n\n // Always show the message modal as the terminal state (even if the API\n // failed — useful for demos).\n this.state = \"MODAL_OPEN\";\n try {\n // The modal chunk loads async; bail if the embed was destroyed meanwhile,\n // so a raced modal never opens (and never leaks a listener) post-destroy.\n const modal = await this.ensureModal();\n if (this.destroyed) return;\n modal.open();\n\n // Enable pointer events on overlay host for modal\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n } catch (err) {\n // The modal chunk failed to load/open. Never leave the user stuck in a\n // re-targetable limbo: thank them and reset to IDLE so a fresh click\n // starts a clean flow (the feedback itself was already POSTed above).\n console.error(\"Failed to open the feedback modal:\", err);\n this.announceMsg(\"Thank you for your feedback!\", true);\n this.resetFeedbackUi();\n }\n }\n\n /**\n * Quest id linked to `type`, or \"\" when quest launching is disabled\n * (no `base`) or this button has no quest configured.\n */\n private questIdFor(type: \"up\" | \"down\" | \"video\"): string {\n const q = this.config.quests;\n return q.base ? q[type] || \"\" : \"\";\n }\n\n /**\n * Launch the quest linked to `type`, if any. Resolves `true` when a quest\n * was configured and the widget launched; `false` when no quest is\n * configured or the widget failed to load (caller falls back to its\n * normal UI). The created feedback record id is passed through so the\n * quest response can be joined back to it server-side.\n */\n private async tryLaunchQuest(\n type: \"up\" | \"down\" | \"video\",\n feedbackId: number | string | null,\n ): Promise<boolean> {\n const questId = this.questIdFor(type);\n if (!questId) return false;\n try {\n this.activeQuest?.destroy();\n this.activeQuest = await launchQuest({\n questId,\n base: this.config.quests.base,\n apiKey: this.config.quests.apiKey || undefined,\n moduleUrl: this.config.quests.moduleUrl,\n feedbackId,\n onClose: () => {\n this.activeQuest = null;\n },\n });\n return true;\n } catch (error) {\n console.error(\"Failed to launch quest:\", error);\n this.activeQuest = null;\n return false;\n }\n }\n\n /**\n * Reset the thumbs targeting/marker UI back to idle without opening or\n * closing the message modal. Shared by closeModal() and the quest-launch\n * path (which bypasses the modal entirely).\n */\n private resetFeedbackUi(): void {\n this.targeting?.hideMarker();\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"none\";\n }\n this.state = \"IDLE\";\n this.feedbackId = null;\n this.feedbackData.feedbackType = null;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n this.selectedBounds.visible = false;\n }\n\n\n /**\n * Lazily load and instantiate the modal controller. The message modal lives\n * in a separate chunk, fetched the first time it opens (and pre-warmed while\n * the user targets an element — see prewarmModal).\n */\n private async ensureModal(): Promise<ModalController> {\n if (!this.modal) {\n const { ModalController } = await import(\"./modal\");\n this.modal = new ModalController(this.makeModalHost());\n }\n return this.modal;\n }\n\n /** Narrow view of the embed the modal controller talks back through. */\n private makeModalHost(): ModalHost {\n const self = this;\n return {\n get config() {\n return self.config;\n },\n get uid() {\n return self.uid;\n },\n get isMobile() {\n return self.isMobile;\n },\n get state() {\n return self.state;\n },\n get boundKeyDown() {\n return self.boundKeyDown;\n },\n get feedbackData() {\n return self.feedbackData;\n },\n get selectedBounds() {\n return self.selectedBounds;\n },\n get feedbackId() {\n return self.feedbackId;\n },\n setFeedbackId: (id) => {\n self.feedbackId = id;\n },\n ensureOverlayHost: () => self.ensureOverlayHost(),\n applyVars: (el) => self.applyVars(el),\n announceMsg: (message, assertive) => self.announceMsg(message, assertive),\n openDialogA11y: (container, opts) => self.openDialogA11y(container, opts),\n closeDialogA11y: () => self.closeDialogA11y(),\n resetFeedbackUi: () => self.resetFeedbackUi(),\n };\n }\n\n /** Warm the modal chunk while the user is targeting, so it opens instantly. */\n private prewarmModal(): void {\n if (this.modalPrewarmed) return;\n this.modalPrewarmed = true;\n import(\"./modal\").catch(() => {});\n }\n\n /**\n * Lazily load and instantiate the targeting controller. The subsystem (with\n * element-selector) lives in a separate chunk, fetched the first time the\n * user targets — pre-warmed on thumb-button hover (see prewarmTargeting).\n */\n private async ensureTargeting(): Promise<TargetingController> {\n if (!this.targeting) {\n const { TargetingController } = await import(\"./targeting\");\n this.targeting = new TargetingController(this.makeTargetingHost());\n }\n return this.targeting;\n }\n\n /** Narrow view of the embed the targeting controller talks back through. */\n private makeTargetingHost(): TargetingHost {\n const self = this;\n return {\n get config() {\n return self.config;\n },\n get cssVars() {\n return self.cssVars;\n },\n get state() {\n return self.state;\n },\n get shadowHost() {\n return self.shadowHost;\n },\n get overlayShadowHost() {\n return self.overlayShadowHost;\n },\n get boundKeyDown() {\n return self.boundKeyDown;\n },\n get feedbackData() {\n return self.feedbackData;\n },\n get selectedBounds() {\n return self.selectedBounds;\n },\n setState: (state) => {\n self.state = state;\n },\n ensureOverlayHost: () => self.ensureOverlayHost(),\n applyVars: (el) => self.applyVars(el),\n announceMsg: (message, assertive) => self.announceMsg(message, assertive),\n clearActiveThumb: () => self.clearActiveThumb(),\n submitFeedback: () => self.submitFeedback(),\n };\n }\n\n /** Warm the targeting chunk on thumb-button hover, before the click. */\n private prewarmTargeting(): void {\n if (this.targetingPrewarmed || this.config.skipTargeting) return;\n this.targetingPrewarmed = true;\n import(\"./targeting\").catch(() => {});\n }\n\n // ==================== Video Recording ====================\n\n /**\n * Cheap synchronous capability check so the record button can render without\n * pulling in the (lazily-loaded) video subsystem. Mirrors\n * isVideoRecordingSupported() in video-capture.ts.\n */\n private videoSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n !!navigator.mediaDevices &&\n typeof navigator.mediaDevices.getDisplayMedia === \"function\" &&\n typeof MediaRecorder !== \"undefined\"\n );\n }\n\n\n /**\n * Lazily load and instantiate the recording controller. The whole recording\n * subsystem lives in a separate chunk, fetched only the first time the record\n * button is used (and pre-warmed on hover — see prewarmVideo).\n */\n private async ensureRecording(): Promise<RecordingController> {\n if (!this.recording) {\n const { RecordingController } = await import(\"./recording\");\n this.recording = new RecordingController(this.makeRecordingHost());\n }\n return this.recording;\n }\n\n /** Narrow view of the embed the recording controller talks back through. */\n private makeRecordingHost(): RecordingHost {\n const self = this;\n return {\n get config() {\n return self.config;\n },\n get visitorId() {\n return self.visitorId;\n },\n get uid() {\n return self.uid;\n },\n get overlayShadowHost() {\n return self.overlayShadowHost;\n },\n get consoleCapture() {\n return self.consoleCapture;\n },\n get boundKeyDown() {\n return self.boundKeyDown;\n },\n get state() {\n return self.state;\n },\n setState: (state) => {\n self.state = state;\n },\n ensureOverlayHost: () => self.ensureOverlayHost(),\n applyVars: (el) => self.applyVars(el),\n announceMsg: (message, assertive) => self.announceMsg(message, assertive),\n openDialogA11y: (container, opts) => self.openDialogA11y(container, opts),\n closeDialogA11y: () => self.closeDialogA11y(),\n setButtonsDisabled: (disabled) => self.setButtonsDisabled(disabled),\n tryLaunchQuest: (type, feedbackId) => self.tryLaunchQuest(type, feedbackId),\n };\n }\n\n private setButtonsDisabled(disabled: boolean): void {\n if (!this.buttonsContainer) return;\n const buttons = this.buttonsContainer.querySelectorAll<HTMLButtonElement>(\"button.qaid-btn, button.qaid-btn-structural\");\n buttons.forEach((btn) => {\n if (disabled) {\n // Don't disable the record button itself (it has its own state)\n if (!btn.classList.contains(\"qaid-btn-record\")) {\n btn.disabled = true;\n btn.style.opacity = \"0.5\";\n }\n } else {\n btn.disabled = false;\n btn.style.opacity = \"\";\n }\n });\n }\n\n /**\n * Destroy the embed and clean up all resources\n */\n public destroy(): void {\n // Flag as destroyed so the DOM observer doesn't re-attach hosts\n this.destroyed = true;\n\n // Disconnect DOM persistence observers\n if (this.domObserver) {\n this.domObserver.disconnect();\n this.domObserver = null;\n }\n if (this.boundBeforeSwap) {\n document.removeEventListener(\"astro:before-swap\", this.boundBeforeSwap);\n this.boundBeforeSwap = null;\n }\n\n // Tear down the targeting subsystem + release any open dialog focus state\n this.targeting?.destroy();\n this.targeting = null;\n this.clearActiveThumb();\n this.closeDialogA11y();\n\n // Cancel any pending idle preload\n if (this.prewarmHandle !== null) {\n const w = window as unknown as { cancelIdleCallback?: (h: number) => void };\n if (this.prewarmIsTimeout) clearTimeout(this.prewarmHandle);\n else w.cancelIdleCallback?.(this.prewarmHandle);\n this.prewarmHandle = null;\n }\n\n // Tear down the recording subsystem (recorder, picker, preview) if loaded.\n this.recording?.destroy();\n this.recording = null;\n // Tear down the message modal if it was loaded/open.\n this.modal?.destroy();\n this.modal = null;\n\n // Tear down any quest launched from a button\n this.activeQuest?.destroy();\n this.activeQuest = null;\n\n // Restore console.error\n if (this.consoleCapture) {\n this.consoleCapture.restore();\n this.consoleCapture = null;\n }\n\n // Remove event listeners\n window.removeEventListener(\"resize\", this.boundResize);\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n\n // Remove shadow hosts from DOM (this removes shadow roots and all their contents)\n if (this.shadowHost) {\n this.shadowHost.remove();\n this.shadowHost = null;\n this.shadowRoot = null;\n }\n if (this.overlayShadowHost) {\n this.overlayShadowHost.remove();\n this.overlayShadowHost = null;\n this.overlayShadowRoot = null;\n }\n this.buttonsContainer = null;\n this.dismissBtn = null;\n this.tooltipElement = null;\n\n // Remove light DOM styles (reference counted)\n removeStyles();\n }\n}\n","/**\n * Auto-initialization from a `<script>` tag.\n *\n * Shared by both delivery entries: the legacy UMD/API entry (`index.ts`) and\n * the minimal ESM loader (`loader.ts`). Kept out of `index.ts` so the loader\n * can auto-init without pulling in `index.ts`'s feature re-exports — that\n * re-export graph is what anchors screenshot/video/annotate into the main\n * chunk and defeats code-splitting.\n */\n\nimport { QaidFeedback } from \"./embed\";\nimport type { FeedbackConfig } from \"./types\";\n\n/**\n * Find theme CSS from an element matched by a CSS selector.\n * Used by data-css-selector attribute and JSON cssSelector property.\n */\nfunction findCssFromSelector(selector: string): string {\n const el = document.querySelector(selector);\n return el?.textContent?.trim() ?? \"\";\n}\n\n/**\n * Parse JSON config from a separate script tag with type=\"application/json\"\n * Looks for: <script type=\"application/json\" data-feedback-config>{ ... }</script>\n */\nfunction parseJsonConfig(): Partial<FeedbackConfig> | null {\n const configScript = document.querySelector(\n 'script[type=\"application/json\"][data-feedback-config]'\n );\n if (!configScript) return null;\n\n const textContent = configScript.textContent?.trim();\n if (!textContent) return null;\n\n try {\n const parsed = JSON.parse(textContent);\n // Resolve cssSelector → css\n if (parsed.cssSelector && !parsed.css) {\n parsed.css = findCssFromSelector(parsed.cssSelector);\n delete parsed.cssSelector;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Say, once per attribute, that a value was ignored. The embed never throws\n * over a bad attribute: it keeps its default and tells the developer why.\n */\nfunction warnIgnored(attr: string, value: string, expected: string): void {\n console.warn(`[thumbs-embed] Ignoring ${attr}=\"${value}\": expected ${expected}.`);\n}\n\n/**\n * Split a comma-separated list, but not on commas inside parentheses, so\n * `rgb(0, 128, 0)` stays one colour.\n */\nfunction splitTopLevel(value: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let current = \"\";\n for (const ch of value) {\n if (ch === \"(\") depth++;\n if (ch === \")\") depth = Math.max(0, depth - 1);\n if (ch === \",\" && depth === 0) {\n parts.push(current.trim());\n current = \"\";\n } else {\n current += ch;\n }\n }\n parts.push(current.trim());\n return parts;\n}\n\n/**\n * A list of colours, written as a JSON array (`[\"#f00\",\"#0f0\"]`) or as a\n * comma-separated list (`#f00, rgb(0, 128, 0)`). Any empty entry, a\n * non-string entry, an empty list or malformed JSON rejects the whole value.\n */\nfunction parseColorList(attr: string, raw: string | null): string[] | undefined {\n if (raw === null) return undefined;\n const value = raw.trim();\n let list: unknown = null;\n // Anything that looks like JSON is read as JSON, so an object is rejected\n // rather than taken as a one-colour list.\n if (value.startsWith(\"[\") || value.startsWith(\"{\")) {\n try {\n list = JSON.parse(value);\n } catch {\n list = null;\n }\n } else {\n list = splitTopLevel(value);\n }\n if (\n Array.isArray(list) &&\n list.length > 0 &&\n list.every((c) => typeof c === \"string\" && c.trim() !== \"\")\n ) {\n return (list as string[]).map((c) => c.trim());\n }\n warnIgnored(attr, raw, \"a JSON array of colours or a comma-separated list\");\n return undefined;\n}\n\n/**\n * A screen-reader label. An empty one would leave an icon-only button with\n * no accessible name, so it is ignored in favour of the default.\n */\nfunction parseLabel(attr: string, raw: string | null): string | undefined {\n if (raw === null) return undefined;\n if (raw.trim() === \"\") {\n warnIgnored(attr, raw, \"a non-empty label\");\n return undefined;\n }\n return raw;\n}\n\n/**\n * Parse config from data-* attributes. Every option that can be written as\n * text has one, so the JSON config block is a choice, not a requirement.\n *\n * Exported for tests: index.ts does not re-export it, so this is not public API.\n */\nexport function parseDataAttributes(script: HTMLScriptElement): Partial<FeedbackConfig> | null {\n const endpoint = script.getAttribute(\"data-endpoint\");\n if (!endpoint) return null;\n\n const position = script.getAttribute(\"data-position\") as\n | \"bottom-right\"\n | \"bottom-left\"\n | \"top-right\"\n | \"top-left\"\n | null;\n const zIndex = script.getAttribute(\"data-zindex\");\n const positiveColor = script.getAttribute(\"data-positive-color\");\n const negativeColor = script.getAttribute(\"data-negative-color\");\n const markerColor = script.getAttribute(\"data-marker-color\");\n const container = script.getAttribute(\"data-container\");\n const buttonClass = script.getAttribute(\"data-button-class\");\n const skipTargeting = script.getAttribute(\"data-skip-targeting\");\n const hideConfirmation = script.getAttribute(\"data-hide-confirmation\");\n const singleButton = script.getAttribute(\"data-single-button\");\n const feedbackMode = script.getAttribute(\"data-feedback-mode\") as\n | \"target\"\n | \"annotate\"\n | null;\n const incognito = script.getAttribute(\"data-incognito\");\n const buttonSize = script.getAttribute(\"data-button-size\") as\n | \"small\"\n | \"medium\"\n | \"large\"\n | null;\n const offsetX = script.getAttribute(\"data-offset-x\");\n const offsetY = script.getAttribute(\"data-offset-y\");\n const modalWidth = script.getAttribute(\"data-modal-width\");\n const backdropOpacity = script.getAttribute(\"data-backdrop-opacity\");\n const fontFamily = script.getAttribute(\"data-font-family\");\n const fontSize = script.getAttribute(\"data-font-size\");\n const tooltip = script.getAttribute(\"data-tooltip\");\n const modalTitle = script.getAttribute(\"data-modal-title\");\n const confirmationTitle = script.getAttribute(\"data-confirmation-title\");\n const confirmationMessage = script.getAttribute(\"data-confirmation-message\");\n const confirmationClose = script.getAttribute(\"data-confirmation-close\");\n const errorTitle = script.getAttribute(\"data-error-title\");\n const errorMessage = script.getAttribute(\"data-error-message\");\n const modalSubtitle = script.getAttribute(\"data-modal-subtitle\");\n const placeholder = script.getAttribute(\"data-placeholder\");\n const submitButton = script.getAttribute(\"data-submit-button\");\n const skipButton = script.getAttribute(\"data-skip-button\");\n const positiveIcon = script.getAttribute(\"data-positive-icon\");\n const negativeIcon = script.getAttribute(\"data-negative-icon\");\n const feedbackIcon = script.getAttribute(\"data-feedback-icon\");\n const feedbackLabel = script.getAttribute(\"data-feedback-label\");\n const apiKey = script.getAttribute(\"data-api-key\");\n const captureScreenshot = script.getAttribute(\"data-capture-screenshot\");\n const annotate = script.getAttribute(\"data-annotate\");\n const annotationColor = script.getAttribute(\"data-annotation-color\");\n const screenshotQuality = script.getAttribute(\"data-screenshot-quality\");\n const screenshotMaxWidth = script.getAttribute(\"data-screenshot-max-width\");\n const screenshotMaxHeight = script.getAttribute(\"data-screenshot-max-height\");\n const captureVideo = script.getAttribute(\"data-capture-video\");\n const redaction = script.getAttribute(\"data-video-redaction\");\n const hideThumbs = script.getAttribute(\"data-hide-thumbs\");\n const hideDismiss = script.getAttribute(\"data-hide-dismiss\");\n const videoMaxDuration = script.getAttribute(\"data-video-max-duration\");\n const screenshotMethod = script.getAttribute(\"data-screenshot-method\") as \"dom\" | \"permission\" | null;\n const direction = script.getAttribute(\"data-direction\") as \"horizontal\" | \"vertical\" | null;\n const cssSelector = script.getAttribute(\"data-css-selector\");\n const css = script.getAttribute(\"data-css\");\n const positiveLabel = parseLabel(\"data-positive-label\", script.getAttribute(\"data-positive-label\"));\n const negativeLabel = parseLabel(\"data-negative-label\", script.getAttribute(\"data-negative-label\"));\n const recordLabel = parseLabel(\"data-record-label\", script.getAttribute(\"data-record-label\"));\n const dismissLabel = parseLabel(\"data-dismiss-label\", script.getAttribute(\"data-dismiss-label\"));\n const annotationPalette = parseColorList(\n \"data-annotation-palette\",\n script.getAttribute(\"data-annotation-palette\")\n );\n const recordIcon = script.getAttribute(\"data-record-icon\");\n const questBase = script.getAttribute(\"data-quest-base\");\n const questUp = script.getAttribute(\"data-quest-up\");\n const questDown = script.getAttribute(\"data-quest-down\");\n const questVideo = script.getAttribute(\"data-quest-video\");\n const questApiKey = script.getAttribute(\"data-quest-api-key\");\n const questModuleUrl = script.getAttribute(\"data-quest-module-url\");\n\n return {\n endpoint,\n // Inline CSS wins over a selector, as `css` wins over `cssSelector` in\n // the JSON block.\n css: css || (cssSelector ? findCssFromSelector(cssSelector) : undefined),\n apiKey: apiKey ?? undefined,\n captureScreenshot: captureScreenshot === \"true\" ? true : undefined,\n // Annotation is on by default; only an explicit \"false\" disables it.\n annotate: annotate === \"false\" ? false : undefined,\n annotationColor: annotationColor ?? undefined,\n annotationPalette,\n screenshotOptions: (screenshotQuality || screenshotMaxWidth || screenshotMaxHeight) ? {\n quality: screenshotQuality ? parseFloat(screenshotQuality) : undefined,\n maxWidth: screenshotMaxWidth ? parseInt(screenshotMaxWidth, 10) : undefined,\n maxHeight: screenshotMaxHeight ? parseInt(screenshotMaxHeight, 10) : undefined,\n } : undefined,\n container: container ?? undefined,\n buttonClass: buttonClass ?? undefined,\n direction: direction ?? undefined,\n position: position ?? undefined,\n zIndex: zIndex ? parseInt(zIndex, 10) : undefined,\n skipTargeting: skipTargeting === \"true\" ? true : undefined,\n hideConfirmation: hideConfirmation === \"true\" ? true : undefined,\n singleButton: singleButton === \"true\" ? true : undefined,\n feedbackMode: feedbackMode ?? undefined,\n incognito: incognito === \"true\" ? true : undefined,\n buttonSize: buttonSize ?? undefined,\n offset: (offsetX || offsetY) ? {\n x: offsetX ? parseInt(offsetX, 10) : undefined,\n y: offsetY ? parseInt(offsetY, 10) : undefined,\n } : undefined,\n modalWidth: modalWidth ? parseInt(modalWidth, 10) : undefined,\n backdropOpacity: backdropOpacity ? parseFloat(backdropOpacity) : undefined,\n fontFamily: fontFamily ?? undefined,\n fontSize: fontSize ? parseInt(fontSize, 10) : undefined,\n colors: {\n positive: positiveColor ?? undefined,\n negative: negativeColor ?? undefined,\n marker: markerColor ?? undefined,\n },\n text: (tooltip || modalTitle || modalSubtitle || placeholder || submitButton || skipButton || feedbackLabel || confirmationTitle || confirmationMessage || confirmationClose || errorTitle || errorMessage || positiveLabel || negativeLabel || recordLabel || dismissLabel) ? {\n tooltip: tooltip ?? undefined,\n modalTitle: modalTitle ?? undefined,\n modalSubtitle: modalSubtitle ?? undefined,\n placeholder: placeholder ?? undefined,\n submitButton: submitButton ?? undefined,\n skipButton: skipButton ?? undefined,\n feedbackLabel: feedbackLabel ?? undefined,\n confirmationTitle: confirmationTitle ?? undefined,\n confirmationMessage: confirmationMessage ?? undefined,\n confirmationClose: confirmationClose ?? undefined,\n errorTitle: errorTitle ?? undefined,\n errorMessage: errorMessage ?? undefined,\n positiveLabel,\n negativeLabel,\n recordLabel,\n dismissLabel,\n } : undefined,\n positiveIcon: positiveIcon ?? undefined,\n recordIcon: recordIcon ?? undefined,\n negativeIcon: negativeIcon ?? undefined,\n feedbackIcon: feedbackIcon ?? undefined,\n screenshotMethod: screenshotMethod ?? undefined,\n captureVideo: captureVideo === \"true\" ? true : undefined,\n hideThumbs: hideThumbs === \"true\" ? true : undefined,\n hideDismiss: hideDismiss === \"true\" ? true : undefined,\n videoOptions: (videoMaxDuration || redaction) ? {\n maxDuration: videoMaxDuration ? parseInt(videoMaxDuration, 10) : undefined,\n redaction: redaction === \"true\" ? true : undefined,\n } : undefined,\n quests: questBase ? {\n base: questBase,\n up: questUp ?? undefined,\n down: questDown ?? undefined,\n video: questVideo ?? undefined,\n apiKey: questApiKey ?? undefined,\n moduleUrl: questModuleUrl ?? undefined,\n } : undefined,\n };\n}\n\n/**\n * Initialize a QaidFeedback instance from the current `<script>` tag's JSON\n * config block or data-* attributes. Safe to call in any environment — it\n * no-ops when there is no document or no endpoint config.\n */\nexport function autoInit(): void {\n if (typeof document === \"undefined\") return;\n\n const run = (): void => {\n // `document.currentScript` is null inside an ES module (the loader entry),\n // so fall back to locating the embed's own script tag by its data-endpoint.\n const script =\n (document.currentScript as HTMLScriptElement | null) ??\n document.querySelector<HTMLScriptElement>(\"script[data-endpoint]\");\n // Try JSON config from a separate script tag first, fall back to data-* attrs.\n // The JSON block wins. The attributes are only read without one, so a\n // bad attribute beside a block doesn't warn about a value never used.\n const config = parseJsonConfig() ?? (script ? parseDataAttributes(script) : null);\n\n if (config?.endpoint) {\n new QaidFeedback(config as FeedbackConfig);\n }\n };\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", run);\n } else {\n run();\n }\n}\n","/**\n * Network capture utilities\n * Intercepts fetch() and XMLHttpRequest to capture 4xx/5xx errors\n */\n\nimport type { NetworkError } from \"./types\";\n\nconst MAX_ENTRIES = 20;\nconst MAX_BODY_SIZE = 4096; // 4KB\n\nexport interface NetworkCapture {\n errors: NetworkError[];\n restore: () => void;\n}\n\n/**\n * Truncate a body to max size. Callers must guard against null/undefined.\n */\nexport function truncateBody(body: unknown): string {\n const str = typeof body === \"string\" ? body : JSON.stringify(body);\n if (str.length > MAX_BODY_SIZE) {\n return str.slice(0, MAX_BODY_SIZE) + \"…[truncated]\";\n }\n return str;\n}\n\n/**\n * Add an error to the buffer, evicting oldest if at capacity\n */\nexport function addEntry(errors: NetworkError[], entry: NetworkError): void {\n if (errors.length >= MAX_ENTRIES) {\n errors.shift();\n }\n errors.push(entry);\n}\n\n/**\n * Read response body as text, with size limit\n */\nasync function safeReadBody(response: Response): Promise<string | undefined> {\n try {\n const text = await response.clone().text();\n return truncateBody(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Intercept fetch() and XMLHttpRequest to capture network errors (4xx/5xx)\n */\nexport function captureNetworkErrors(): NetworkCapture {\n const errors: NetworkError[] = [];\n const originalFetch = window.fetch;\n\n // Monkey-patch fetch\n window.fetch = async function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url;\n const method = init?.method ?? (typeof input === \"object\" && \"method\" in input ? input.method : \"GET\");\n let requestBody: string | undefined;\n if (init?.body) {\n requestBody = truncateBody(init.body);\n }\n\n const response = await originalFetch.apply(window, [input, init]);\n\n if (response.status >= 400) {\n const responseBody = await safeReadBody(response);\n addEntry(errors, {\n url,\n method: method.toUpperCase(),\n status: response.status,\n statusText: response.statusText,\n requestBody,\n responseBody,\n timestamp: Date.now(),\n });\n }\n\n return response;\n };\n\n // Monkey-patch XMLHttpRequest\n const originalXHROpen = XMLHttpRequest.prototype.open;\n const originalXHRSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (method: string, url: string | URL, ...rest: unknown[]) {\n (this as XMLHttpRequest & { _qaid_method: string; _qaid_url: string })._qaid_method = method;\n (this as XMLHttpRequest & { _qaid_url: string })._qaid_url = typeof url === \"string\" ? url : url.toString();\n return originalXHROpen.apply(this, [method, url, ...rest] as Parameters<typeof originalXHROpen>);\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const xhr = this as XMLHttpRequest & { _qaid_method: string; _qaid_url: string };\n const requestBody = body ? truncateBody(body) : undefined;\n\n xhr.addEventListener(\"load\", function () {\n if (xhr.status >= 400) {\n addEntry(errors, {\n url: xhr._qaid_url,\n method: xhr._qaid_method.toUpperCase(),\n status: xhr.status,\n statusText: xhr.statusText,\n requestBody,\n responseBody: truncateBody(xhr.responseText),\n timestamp: Date.now(),\n });\n }\n });\n\n return originalXHRSend.apply(this, [body]);\n };\n\n return {\n errors,\n restore: () => {\n window.fetch = originalFetch;\n XMLHttpRequest.prototype.open = originalXHROpen;\n XMLHttpRequest.prototype.send = originalXHRSend;\n },\n };\n}\n","/**\n * iOS/iPadOS screen-recording orientation fix.\n *\n * On iOS/iPadOS, `getDisplayMedia` + `MediaRecorder` writes a device-orientation\n * rotation into the recorded file (even for screen captures). Players disagree\n * on whether to honour it — the widget's own preview `<video>` renders it\n * sideways, and so does the dashboard. Re-encoding the *live* decoded frames\n * (which carry no container rotation) through a canvas produces a file whose\n * pixels are already upright with no rotation metadata, so it renders the same\n * everywhere.\n *\n * If the live frames themselves come back with swapped dimensions (portrait\n * frame while the viewport is landscape, or vice versa), we additionally rotate\n * the canvas 90° to match the viewport. That branch only triggers on a clear\n * orientation mismatch, so a correctly-oriented recording is never rotated.\n */\n\nexport interface OrientationTransform {\n /** Output canvas width in px. */\n width: number;\n /** Output canvas height in px. */\n height: number;\n /** Clockwise rotation to apply while drawing: 0, 90, or -90. */\n rotate: 0 | 90 | -90;\n}\n\nexport interface OrientationCorrectedStream {\n /** Canvas-sourced stream to hand to MediaRecorder. */\n stream: MediaStream;\n /** Stop drawing and release the canvas stream + source video. */\n stop: () => void;\n}\n\n/** True on iPhone/iPad/iPod, including iPadOS 13+ which reports as desktop Mac. */\nexport function isIOSDevice(): boolean {\n const ua = navigator.userAgent || \"\";\n return (\n /iP(hone|ad|od)/.test(ua) ||\n // iPadOS 13+ masquerades as \"MacIntel\" but is a multi-touch device.\n (navigator.platform === \"MacIntel\" && (navigator.maxTouchPoints || 0) > 1)\n );\n}\n\n/**\n * Decide the output canvas size and rotation needed to render a `frameW×frameH`\n * capture upright for a `viewportW×viewportH` viewport. When the frame and\n * viewport share an orientation, no rotation is applied (a straight re-encode,\n * which still strips the bad container rotation). Otherwise rotate 90°, its\n * direction chosen from the screen orientation angle.\n */\nexport function computeOrientationTransform(\n frameW: number,\n frameH: number,\n viewportW: number,\n viewportH: number,\n screenAngle: number\n): OrientationTransform {\n const frameLandscape = frameW >= frameH;\n const viewportLandscape = viewportW >= viewportH;\n\n if (frameLandscape === viewportLandscape) {\n return { width: frameW, height: frameH, rotate: 0 };\n }\n\n // Landscape-secondary (angle 270) is turned the opposite way to primary (90).\n const rotate: 90 | -90 = screenAngle === 270 ? -90 : 90;\n return { width: frameH, height: frameW, rotate };\n}\n\n/** Draw one video frame onto the canvas context, applying the transform. */\nexport function drawRotatedFrame(\n ctx: CanvasRenderingContext2D,\n source: CanvasImageSource,\n t: OrientationTransform\n): void {\n if (t.rotate === 0) {\n ctx.drawImage(source, 0, 0, t.width, t.height);\n return;\n }\n\n ctx.save();\n if (t.rotate === 90) {\n ctx.translate(t.width, 0);\n ctx.rotate(Math.PI / 2);\n } else {\n ctx.translate(0, t.height);\n ctx.rotate(-Math.PI / 2);\n }\n // The source is drawn at its natural (unrotated) extents, which are the\n // output dimensions swapped back.\n ctx.drawImage(source, 0, 0, t.height, t.width);\n ctx.restore();\n}\n\nfunction getScreenAngle(): number {\n const orientation =\n typeof screen !== \"undefined\" ? screen.orientation : undefined;\n return orientation && typeof orientation.angle === \"number\"\n ? orientation.angle\n : 0;\n}\n\nexport function waitForVideoDimensions(video: HTMLVideoElement): Promise<void> {\n return new Promise<void>((resolve) => {\n if (video.videoWidth > 0) {\n resolve();\n return;\n }\n video.addEventListener(\"loadedmetadata\", () => resolve(), { once: true });\n });\n}\n\nexport async function playSilently(video: HTMLVideoElement): Promise<void> {\n try {\n await video.play();\n } catch {\n // Autoplay can reject without a gesture; the element still produces frames\n // for the canvas, so this is non-fatal.\n }\n}\n\n/**\n * Build a canvas-backed, orientation-normalised copy of a display-capture\n * stream. Returns null when the browser can't support the canvas pipeline, in\n * which case the caller should record the original stream directly.\n */\nexport async function buildOrientationCorrectedStream(\n displayStream: MediaStream,\n frameRate = 15\n): Promise<OrientationCorrectedStream | null> {\n const canvas = document.createElement(\"canvas\");\n if (typeof canvas.captureStream !== \"function\") return null;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n\n const video = document.createElement(\"video\");\n video.muted = true;\n video.playsInline = true;\n video.srcObject = displayStream;\n\n await waitForVideoDimensions(video);\n\n const transform = computeOrientationTransform(\n video.videoWidth,\n video.videoHeight,\n window.innerWidth,\n window.innerHeight,\n getScreenAngle()\n );\n canvas.width = transform.width;\n canvas.height = transform.height;\n\n await playSilently(video);\n\n let rafId = 0;\n const draw = (): void => {\n drawRotatedFrame(ctx, video, transform);\n rafId = requestAnimationFrame(draw);\n };\n draw();\n\n const stream = canvas.captureStream(frameRate);\n\n return {\n stream,\n stop: (): void => {\n if (rafId) cancelAnimationFrame(rafId);\n rafId = 0;\n stream.getTracks().forEach((t) => t.stop());\n video.pause();\n video.srcObject = null;\n },\n };\n}\n","/**\n * Live video redaction.\n *\n * Blurs the on-screen bounding boxes of chosen elements while the current tab\n * is recorded. Every frame re-reads each element's `getBoundingClientRect()`,\n * so the blur tracks the content as the page scrolls or reflows — no pixel\n * tracking and no post-recording re-encode. The captured frame is the tab's\n * viewport (video-capture restricts capture to the current tab), so viewport\n * CSS coordinates map onto the frame with a single scale factor.\n *\n * The display stream is drawn to a canvas, the tracked regions are blurred on\n * top, and MediaRecorder records the canvas stream instead of the raw capture —\n * mirroring the iOS orientation pipeline in `video-orientation.ts`.\n */\n\nimport { waitForVideoDimensions, playSilently } from \"./video-orientation\";\n\nexport interface RedactedStream {\n /** Canvas-sourced stream to hand to MediaRecorder. */\n stream: MediaStream;\n /** Stop drawing and release the canvas stream + source video. */\n stop: () => void;\n}\n\nexport interface RedactionOptions {\n /** Output frame rate of the redacted stream. Default: 15. */\n frameRate?: number;\n /** Gaussian blur radius in px applied to each region. Default: 12. */\n blurRadius?: number;\n}\n\ninterface Box {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/**\n * Map a viewport rect (CSS px, from `getBoundingClientRect()`) onto the\n * captured frame, clamped to the frame bounds. Returns null when the rect is\n * fully outside the viewport (the element scrolled away) or has no area.\n */\nexport function mapRectToFrame(\n rect: { left: number; top: number; width: number; height: number },\n scaleX: number,\n scaleY: number,\n frameW: number,\n frameH: number\n): Box | null {\n const x = Math.max(0, Math.min(frameW, rect.left * scaleX));\n const y = Math.max(0, Math.min(frameH, rect.top * scaleY));\n const right = Math.max(0, Math.min(frameW, (rect.left + rect.width) * scaleX));\n const bottom = Math.max(0, Math.min(frameH, (rect.top + rect.height) * scaleY));\n const w = right - x;\n const h = bottom - y;\n if (w <= 0 || h <= 0) return null;\n return { x, y, w, h };\n}\n\n/**\n * Draw one frame: the full capture, then each element's live bounding box\n * blurred on top. Exported for unit testing the per-frame logic without a real\n * capture stream. When the 2D context can't apply a blur filter, the region is\n * filled solid instead so sensitive content is still hidden.\n */\nexport function drawRedactedFrame(\n ctx: CanvasRenderingContext2D,\n video: CanvasImageSource,\n elements: Element[],\n frameW: number,\n frameH: number,\n blurRadius: number,\n supportsFilter: boolean\n): void {\n ctx.drawImage(video, 0, 0, frameW, frameH);\n\n // The current-tab capture is the viewport, so this scale is ~devicePixelRatio;\n // deriving it live also absorbs viewport resizes mid-recording.\n const scaleX = frameW / Math.max(1, window.innerWidth);\n const scaleY = frameH / Math.max(1, window.innerHeight);\n\n for (const el of elements) {\n const box = mapRectToFrame(\n el.getBoundingClientRect(),\n scaleX,\n scaleY,\n frameW,\n frameH\n );\n if (!box) continue; // scrolled out of view\n\n ctx.save();\n if (supportsFilter) {\n ctx.filter = `blur(${blurRadius}px)`;\n // Re-sample just this region from the live frame so the blur source is\n // the region itself.\n ctx.drawImage(video, box.x, box.y, box.w, box.h, box.x, box.y, box.w, box.h);\n } else {\n // No canvas filter support — hide the region with a solid fill.\n ctx.fillStyle = \"#0b0b0b\";\n ctx.fillRect(box.x, box.y, box.w, box.h);\n }\n ctx.restore();\n }\n}\n\n/**\n * Build a canvas-backed copy of a display-capture stream that blurs the live\n * bounding boxes of `elements`. Returns null when the browser can't support the\n * canvas pipeline (no `captureStream` / 2D context), in which case the caller\n * should record the original stream unredacted.\n */\nexport async function buildRedactedStream(\n displayStream: MediaStream,\n elements: Element[],\n options: RedactionOptions = {}\n): Promise<RedactedStream | null> {\n const frameRate = options.frameRate ?? 15;\n const blurRadius = options.blurRadius ?? 12;\n\n const canvas = document.createElement(\"canvas\");\n if (typeof canvas.captureStream !== \"function\") return null;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n\n // Feature-detect canvas blur; fall back to solid fill where unsupported.\n const supportsFilter = \"filter\" in ctx;\n\n const video = document.createElement(\"video\");\n video.muted = true;\n video.playsInline = true;\n video.srcObject = displayStream;\n\n await waitForVideoDimensions(video);\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n await playSilently(video);\n\n let rafId = 0;\n const draw = (): void => {\n drawRedactedFrame(\n ctx,\n video,\n elements,\n canvas.width,\n canvas.height,\n blurRadius,\n supportsFilter\n );\n rafId = requestAnimationFrame(draw);\n };\n draw();\n\n const stream = canvas.captureStream(frameRate);\n\n return {\n stream,\n stop: (): void => {\n if (rafId) cancelAnimationFrame(rafId);\n rafId = 0;\n stream.getTracks().forEach((t) => t.stop());\n video.pause();\n video.srcObject = null;\n },\n };\n}\n","/**\n * Video capture utilities\n * Uses getDisplayMedia + MediaRecorder for screen recording\n */\n\nimport {\n isIOSDevice,\n buildOrientationCorrectedStream,\n type OrientationCorrectedStream,\n} from \"./video-orientation\";\nimport { buildRedactedStream, type RedactedStream } from \"./video-redaction\";\n\n/**\n * Thrown by `start()` when areas were picked for redaction but this browser\n * cannot run the canvas pipeline that blurs them. The recording is refused\n * rather than made without the blur.\n */\nexport const REDACTION_UNAVAILABLE =\n \"This browser cannot blur the areas you picked, so the recording was not started.\";\n\nexport interface VideoRecorderOptions {\n /** Max recording duration in seconds. Default: 15 */\n maxDuration?: number;\n /** Video bitrate in bps. Default: 800000 (800kbps) */\n videoBitsPerSecond?: number;\n /**\n * Elements whose live bounding boxes are blurred out of the recording. The\n * blur tracks each element as the page scrolls. Empty/omitted = no redaction.\n */\n redactionElements?: Element[];\n /** Blur radius in px for redacted regions. Default: 12. */\n redactionBlurRadius?: number;\n}\n\nexport interface VideoRecorder {\n /** Start recording. Requests getDisplayMedia if not already started. */\n start(): Promise<void>;\n /** Stop recording and return the video blob. */\n stop(): Promise<Blob>;\n /** Register a callback for each second tick (receives seconds elapsed). */\n onTick(callback: (elapsed: number) => void): void;\n /** Register a callback for when recording stops (e.g., browser stop button, max duration). */\n onStop(callback: (blob: Blob | null) => void): void;\n /** Clean up all resources. */\n destroy(): void;\n}\n\n/**\n * Check if video recording is supported in this browser\n */\nexport function isVideoRecordingSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n !!navigator.mediaDevices &&\n typeof navigator.mediaDevices.getDisplayMedia === \"function\" &&\n typeof MediaRecorder !== \"undefined\"\n );\n}\n\n/**\n * Detect the best supported MIME type for recording\n */\nexport function getSupportedMimeType(): string {\n if (typeof MediaRecorder === \"undefined\") return \"\";\n\n const candidates = [\n \"video/webm;codecs=vp9\",\n \"video/webm;codecs=vp8\",\n \"video/webm\",\n \"video/mp4\",\n ];\n\n for (const type of candidates) {\n if (MediaRecorder.isTypeSupported(type)) {\n return type;\n }\n }\n\n return \"\";\n}\n\n/**\n * Create a video recorder that captures the screen\n */\nexport function createVideoRecorder(options: VideoRecorderOptions = {}): VideoRecorder {\n const maxDuration = options.maxDuration ?? 15;\n const videoBitsPerSecond = options.videoBitsPerSecond ?? 800_000;\n const redactionElements = options.redactionElements ?? [];\n const redactionBlurRadius = options.redactionBlurRadius;\n\n let stream: MediaStream | null = null;\n let orientationCorrection: OrientationCorrectedStream | null = null;\n let redaction: RedactedStream | null = null;\n let recorder: MediaRecorder | null = null;\n let chunks: Blob[] = [];\n let tickCallback: ((elapsed: number) => void) | null = null;\n let stopCallback: ((blob: Blob | null) => void) | null = null;\n let tickInterval: ReturnType<typeof setInterval> | null = null;\n let startTime = 0;\n let resolveStop: ((blob: Blob) => void) | null = null;\n let rejectStop: ((err: Error) => void) | null = null;\n let stopped = false;\n let autoStopTimeout: ReturnType<typeof setTimeout> | null = null;\n\n function cleanup(): void {\n if (tickInterval !== null) {\n clearInterval(tickInterval);\n tickInterval = null;\n }\n if (autoStopTimeout !== null) {\n clearTimeout(autoStopTimeout);\n autoStopTimeout = null;\n }\n if (orientationCorrection) {\n orientationCorrection.stop();\n orientationCorrection = null;\n }\n if (redaction) {\n redaction.stop();\n redaction = null;\n }\n if (stream) {\n stream.getTracks().forEach((t) => t.stop());\n stream = null;\n }\n recorder = null;\n chunks = [];\n tickCallback = null;\n stopCallback = null;\n resolveStop = null;\n rejectStop = null;\n }\n\n function finishRecording(): void {\n if (stopped) return;\n stopped = true;\n\n if (recorder && recorder.state !== \"inactive\") {\n recorder.stop();\n }\n }\n\n return {\n async start(): Promise<void> {\n stopped = false;\n chunks = [];\n\n const mimeType = getSupportedMimeType();\n if (!mimeType) {\n throw new Error(\"No supported video MIME type found\");\n }\n\n // Request screen capture, restricted to the current tab (the page the\n // embed is running on). `preferCurrentTab` collapses the Chromium picker\n // to a single \"share this tab\" choice; the other hints drop window and\n // whole-screen options where the browser honours them. These keys are\n // not in the DOM types yet.\n stream = await navigator.mediaDevices.getDisplayMedia({\n video: {\n frameRate: 15,\n displaySurface: \"browser\",\n },\n audio: false,\n // @ts-expect-error preferCurrentTab is not in the TS types yet\n preferCurrentTab: true,\n selfBrowserSurface: \"include\",\n monitorTypeSurfaces: \"exclude\",\n surfaceSwitching: \"exclude\",\n });\n\n // Enforce tab-only capture. In browsers that ignore the hints above and\n // still let the user share a window or the whole screen, reject it — qaid\n // records only the tab it is embedded in, never other windows or the\n // desktop.\n const videoTrack = stream.getVideoTracks()[0];\n const surface = videoTrack?.getSettings?.()?.displaySurface;\n if (surface === \"monitor\" || surface === \"window\") {\n stream.getTracks().forEach((t) => t.stop());\n stream = null;\n throw new Error(\n \"qaid records only the current tab — please share this tab, not a window or your whole screen.\",\n );\n }\n\n // Handle browser \"Stop sharing\" button\n if (videoTrack) {\n videoTrack.addEventListener(\"ended\", () => {\n finishRecording();\n });\n }\n\n // Route the raw capture through canvas pipelines when needed. On\n // iOS/iPadOS the captured file is rotated by the OS, so first re-encode\n // the live frames upright (falling back to the raw stream if the canvas\n // pipeline isn't available). Then, if the user marked regions to redact,\n // blur their live bounding boxes into whatever the first step produced.\n //\n // Redaction used to sit in an `else` of the iOS branch, so on an iPhone\n // or iPad the picker drew its outlines and the recording went out\n // unblurred. The upright frame matches the viewport, which is what the\n // redaction mapping assumes, so the two chain cleanly.\n let recordStream: MediaStream = stream;\n if (isIOSDevice()) {\n const corrected = await buildOrientationCorrectedStream(stream);\n if (corrected) {\n orientationCorrection = corrected;\n recordStream = corrected.stream;\n }\n }\n if (redactionElements.length > 0) {\n const redacted = await buildRedactedStream(recordStream, redactionElements, {\n blurRadius: redactionBlurRadius,\n });\n if (!redacted) {\n // The visitor was promised a blur. Recording the areas in the clear\n // would break that promise, so refuse to record instead.\n if (orientationCorrection) {\n orientationCorrection.stop();\n orientationCorrection = null;\n }\n stream.getTracks().forEach((t) => t.stop());\n stream = null;\n throw new Error(REDACTION_UNAVAILABLE);\n }\n redaction = redacted;\n recordStream = redacted.stream;\n }\n\n recorder = new MediaRecorder(recordStream, {\n mimeType,\n videoBitsPerSecond,\n });\n\n recorder.ondataavailable = (e: BlobEvent) => {\n if (e.data.size > 0) {\n chunks.push(e.data);\n }\n };\n\n recorder.onstop = () => {\n const blob = new Blob(chunks, { type: mimeType });\n if (resolveStop) {\n resolveStop(blob);\n }\n // Notify via callback (for unexpected stops like browser stop button)\n if (stopCallback) {\n stopCallback(blob);\n }\n // Stop the canvas pipeline (iOS orientation / redaction) and the\n // display stream tracks.\n if (orientationCorrection) {\n orientationCorrection.stop();\n orientationCorrection = null;\n }\n if (redaction) {\n redaction.stop();\n redaction = null;\n }\n if (stream) {\n stream.getTracks().forEach((t) => t.stop());\n }\n };\n\n recorder.onerror = () => {\n if (rejectStop) {\n rejectStop(new Error(\"MediaRecorder error\"));\n }\n };\n\n recorder.start(1000); // collect data every second\n startTime = Date.now();\n\n // Start tick interval\n tickInterval = setInterval(() => {\n const elapsed = Math.floor((Date.now() - startTime) / 1000);\n if (tickCallback) {\n tickCallback(elapsed);\n }\n }, 1000);\n\n // Auto-stop at max duration\n autoStopTimeout = setTimeout(() => {\n finishRecording();\n }, maxDuration * 1000);\n },\n\n stop(): Promise<Blob> {\n return new Promise<Blob>((resolve, reject) => {\n resolveStop = resolve;\n rejectStop = reject;\n finishRecording();\n });\n },\n\n onTick(callback: (elapsed: number) => void): void {\n tickCallback = callback;\n },\n\n onStop(callback: (blob: Blob | null) => void): void {\n stopCallback = callback;\n },\n\n destroy(): void {\n finishRecording();\n cleanup();\n },\n };\n}\n","/**\n * DOM-based screenshot capture using html2canvas\n * No browser permission dialog required - reconstructs the page from the DOM\n */\n\nexport interface DomScreenshotOptions {\n /** Quality of WebP compression (0-1). Default: 0.8 */\n quality?: number;\n /** Max width of the screenshot. Default: 1280 */\n maxWidth?: number;\n /** Max height of the screenshot. Default: 800 */\n maxHeight?: number;\n}\n\ndeclare global {\n interface Window {\n html2canvas?: (\n element: HTMLElement,\n options?: Record<string, unknown>\n ) => Promise<HTMLCanvasElement>;\n }\n}\n\nconst SCRIPT_URL = \"https://qaid.dev/lib/html2canvas.min.js\";\nconst LOAD_TIMEOUT = 10_000;\nconst POLL_INTERVAL = 50;\n\nlet loadPromise: Promise<boolean> | null = null;\n\n/**\n * Check if DOM screenshot capture is supported\n * Requires canvas and basic DOM APIs\n */\nexport function isDomScreenshotSupported(): boolean {\n if (typeof document === \"undefined\" || typeof window === \"undefined\") {\n return false;\n }\n // Check canvas support\n const canvas = document.createElement(\"canvas\");\n return typeof canvas.getContext === \"function\" && !!canvas.getContext(\"2d\");\n}\n\n/**\n * Load html2canvas script if not already loaded\n */\nfunction loadHtml2Canvas(): Promise<boolean> {\n if (window.html2canvas) return Promise.resolve(true);\n\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<boolean>((resolve) => {\n // Check if script tag already exists\n const existing = document.querySelector(\n `script[src=\"${SCRIPT_URL}\"]`\n ) as HTMLScriptElement | null;\n\n if (!existing) {\n const script = document.createElement(\"script\");\n script.src = SCRIPT_URL;\n script.async = true;\n document.head.appendChild(script);\n }\n\n // Poll for window.html2canvas availability\n const start = Date.now();\n const poll = () => {\n if (window.html2canvas) {\n resolve(true);\n return;\n }\n if (Date.now() - start > LOAD_TIMEOUT) {\n loadPromise = null;\n resolve(false);\n return;\n }\n setTimeout(poll, POLL_INTERVAL);\n };\n poll();\n });\n\n return loadPromise;\n}\n\n/**\n * Capture a screenshot of the current page using html2canvas\n * Returns a base64-encoded WebP data URL, or null on failure\n */\nexport async function captureDomScreenshot(\n options: DomScreenshotOptions = {}\n): Promise<string | null> {\n const { quality = 0.8, maxWidth = 1280, maxHeight = 800 } = options;\n\n try {\n const loaded = await loadHtml2Canvas();\n if (!loaded || !window.html2canvas) {\n console.warn(\"html2canvas failed to load\");\n return null;\n }\n\n const canvas = await window.html2canvas(document.body, {\n useCORS: true,\n allowTaint: false,\n logging: false,\n windowWidth: document.documentElement.clientWidth,\n windowHeight: document.documentElement.clientHeight,\n width: document.documentElement.clientWidth,\n height: document.documentElement.clientHeight,\n x: window.scrollX,\n y: window.scrollY,\n ignoreElements: (el: Element) => {\n // Hide all qaid elements from the screenshot\n if (el instanceof HTMLElement) {\n return (\n el.classList.contains(\"qaid-buttons\") ||\n el.classList.contains(\"qaid-targeting-overlay\") ||\n el.classList.contains(\"qaid-modal-container\") ||\n el.classList.contains(\"qaid-bottom-sheet\") ||\n el.classList.contains(\"qaid-backdrop\") ||\n el.classList.contains(\"qaid-selected-marker\") ||\n el.classList.contains(\"qaid-tooltip-text\") ||\n el.classList.contains(\"qaid-recording-indicator\") ||\n el.classList.contains(\"qaid-video-preview\") ||\n el.className?.toString().startsWith?.(\"qaid-\")\n );\n }\n return false;\n },\n });\n\n // Scale down if needed\n const srcWidth = canvas.width;\n const srcHeight = canvas.height;\n const scale = Math.min(maxWidth / srcWidth, maxHeight / srcHeight, 1);\n\n if (scale < 1) {\n const scaledWidth = Math.round(srcWidth * scale);\n const scaledHeight = Math.round(srcHeight * scale);\n\n const scaledCanvas = document.createElement(\"canvas\");\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n\n const ctx = scaledCanvas.getContext(\"2d\");\n if (!ctx) return null;\n\n ctx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n return scaledCanvas.toDataURL(\"image/webp\", quality);\n }\n\n return canvas.toDataURL(\"image/webp\", quality);\n } catch (error) {\n console.warn(\"DOM screenshot capture failed:\", error);\n return null;\n }\n}\n","/**\n * Screenshot annotation / markup editor for the thumbs embed.\n *\n * After a screenshot is captured the user can draw on it — rectangles, arrows,\n * a freehand pen, and a blur/redact box — in a full-screen, focus-trapped,\n * WCAG 2.2 AA dialog rendered inside the embed's overlay shadow root. On \"Done\"\n * the base image and the vector shapes are composited into a fresh WebP data\n * URL (so downstream code is unchanged); on \"Skip\"/Escape the original is kept.\n *\n * The geometry and compositing are kept as pure-ish exported functions\n * (`drawShape`, `rectFromPoints`, `compositeAnnotations`) so they can be tested\n * without a real rendering engine — happy-dom has no raster, so tests assert on\n * spied canvas-context calls rather than pixels. The {@link AnnotationEditor}\n * controller keeps the DOM wiring thin and its interactive methods are public\n * so they can be driven directly in tests.\n */\n\nimport { createElement } from \"./dom-utils\";\nimport {\n RECTANGLE_ICON,\n ARROW_ICON,\n PENCIL_ICON,\n BLUR_ICON,\n UNDO_ICON,\n DONE_ICON,\n SKIP_ICON,\n} from \"./icons\";\n\n/** The four drawing tools. */\nexport type ShapeType = \"rect\" | \"arrow\" | \"pen\" | \"blur\";\n\n/** A point in the screenshot's natural-pixel coordinate space. */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * A single vector annotation. `rect`, `arrow` and `blur` use `points[0]` as the\n * drag start and `points[1]` as the drag end; `pen` uses every point in order.\n */\nexport interface Shape {\n type: ShapeType;\n color: string;\n strokeWidth: number;\n points: Point[];\n}\n\n/** Tile size (natural px) for the mosaic drawn by the blur/redact tool. */\nconst BLUR_TILE = 12;\n\n/** Default stroke colour: a high-visibility red that reads on most screenshots. */\nconst DEFAULT_STROKE = \"#ef4444\";\n/** Default swatch palette when the embed doesn't supply one. */\nconst DEFAULT_PALETTE = [\"#ef4444\", \"#f59e0b\", \"#22c55e\", \"#3b82f6\", \"#111827\", \"#ffffff\"];\n/** Friendly names for common swatches (for accessible labels). */\nconst COLOR_NAMES: Record<string, string> = {\n \"#ef4444\": \"red\",\n \"#f59e0b\": \"amber\",\n \"#22c55e\": \"green\",\n \"#3b82f6\": \"blue\",\n \"#111827\": \"black\",\n \"#ffffff\": \"white\",\n // The default marker (nudged from #6366f1 so white text on it clears 4.5:1);\n // the old value keeps its name for sites that pass it explicitly.\n \"#6365f1\": \"indigo\",\n \"#6366f1\": \"indigo\",\n};\n/** Accessible name for a swatch colour — a friendly name, else the hex. */\nfunction colorName(hex: string): string {\n return COLOR_NAMES[hex.toLowerCase()] ?? hex;\n}\n/** Default stroke width in natural pixels. */\nconst DEFAULT_STROKE_WIDTH = 4;\n/** Default WebP quality for the re-encoded composite. */\nconst DEFAULT_QUALITY = 0.8;\n\n/** Human-readable labels for the toolbar / announcements (all overridable). */\nexport interface AnnotationLabels {\n title: string;\n instructions: string;\n rectangle: string;\n arrow: string;\n pen: string;\n blur: string;\n undo: string;\n skip: string;\n done: string;\n colors: string;\n}\n\nconst DEFAULT_LABELS: AnnotationLabels = {\n title: \"Annotate screenshot\",\n instructions:\n \"Draw on the screenshot to highlight or hide details, then choose Done to attach it or Skip to send the original.\",\n rectangle: \"Rectangle\",\n arrow: \"Arrow\",\n pen: \"Pen\",\n blur: \"Blur or redact\",\n undo: \"Undo last\",\n skip: \"Skip annotation\",\n done: \"Done\",\n colors: \"Drawing colour\",\n};\n\n/**\n * Normalise two drag corners into a top-left origin plus positive width/height,\n * regardless of the direction the user dragged.\n */\nexport function rectFromPoints(\n a: Point,\n b: Point\n): { x: number; y: number; w: number; h: number } {\n return {\n x: Math.min(a.x, b.x),\n y: Math.min(a.y, b.y),\n w: Math.abs(a.x - b.x),\n h: Math.abs(a.y - b.y),\n };\n}\n\n/** A drag/shape too small to be a deliberate mark (a click, not a drag). */\nfunction isDegenerate(shape: Shape): boolean {\n if (shape.type === \"pen\") return shape.points.length < 2;\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n return r.w < 3 && r.h < 3;\n}\n\nfunction drawArrow(\n ctx: CanvasRenderingContext2D,\n from: Point,\n to: Point,\n width: number\n): void {\n ctx.beginPath();\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n\n const angle = Math.atan2(to.y - from.y, to.x - from.x);\n const head = Math.max(10, width * 3);\n ctx.beginPath();\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle - Math.PI / 6),\n to.y - head * Math.sin(angle - Math.PI / 6)\n );\n ctx.moveTo(to.x, to.y);\n ctx.lineTo(\n to.x - head * Math.cos(angle + Math.PI / 6),\n to.y - head * Math.sin(angle + Math.PI / 6)\n );\n ctx.stroke();\n}\n\nfunction drawPen(ctx: CanvasRenderingContext2D, points: Point[]): void {\n if (points.length === 0) return;\n ctx.beginPath();\n ctx.moveTo(points[0].x, points[0].y);\n for (let i = 1; i < points.length; i++) {\n ctx.lineTo(points[i].x, points[i].y);\n }\n ctx.stroke();\n}\n\n/**\n * Redact a region so the underlying content is GONE from the composite — not\n * merely dimmed. Both fills are fully opaque (no alpha), so nothing bleeds\n * through; the two-tone mosaic just gives it a recognisable \"redacted\" texture.\n * There is no real Gaussian blur (it can't be verified without a raster), and a\n * translucent wash would leave sensitive text faintly legible — so this paints\n * solid.\n */\nfunction drawBlur(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number\n): void {\n ctx.fillStyle = \"rgb(15, 23, 42)\";\n ctx.fillRect(x, y, w, h);\n ctx.fillStyle = \"rgb(37, 47, 63)\";\n for (let ty = y; ty < y + h; ty += BLUR_TILE) {\n for (let tx = x; tx < x + w; tx += BLUR_TILE) {\n const checker =\n (Math.floor((tx - x) / BLUR_TILE) + Math.floor((ty - y) / BLUR_TILE)) %\n 2 ===\n 0;\n if (checker) {\n ctx.fillRect(\n tx,\n ty,\n Math.min(BLUR_TILE, x + w - tx),\n Math.min(BLUR_TILE, y + h - ty)\n );\n }\n }\n }\n}\n\n/**\n * Draw a single shape onto a 2D context in the shape's own (natural-pixel)\n * coordinates. Used both for the live editing preview and for the final\n * composite, so what the user sees is exactly what is submitted.\n */\nexport function drawShape(ctx: CanvasRenderingContext2D, shape: Shape): void {\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = \"round\";\n ctx.lineCap = \"round\";\n\n switch (shape.type) {\n case \"rect\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n ctx.strokeRect(r.x, r.y, r.w, r.h);\n break;\n }\n case \"arrow\":\n drawArrow(ctx, shape.points[0], shape.points[1], shape.strokeWidth);\n break;\n case \"pen\":\n drawPen(ctx, shape.points);\n break;\n case \"blur\": {\n const r = rectFromPoints(shape.points[0], shape.points[1]);\n drawBlur(ctx, r.x, r.y, r.w, r.h);\n break;\n }\n }\n\n ctx.restore();\n}\n\n/** Load an image from a data URL. Resolves with the element once decoded. */\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = (): void => resolve(img);\n img.onerror = (): void => reject(new Error(\"Failed to load screenshot\"));\n img.src = src;\n });\n}\n\n/**\n * Composite the base screenshot and the drawn shapes into a new WebP data URL\n * at the image's natural size. Falls back to the original data URL if the image\n * can't be loaded or a 2D context isn't available (so a submission is never\n * lost). This is intentionally standalone so it can be unit-tested in isolation.\n */\nexport async function compositeAnnotations(\n dataUrl: string,\n shapes: Shape[],\n opts: { quality?: number } = {}\n): Promise<string> {\n const quality = opts.quality ?? DEFAULT_QUALITY;\n\n let img: HTMLImageElement;\n try {\n img = await loadImage(dataUrl);\n } catch {\n return dataUrl;\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.naturalWidth || img.width || 1;\n canvas.height = img.naturalHeight || img.height || 1;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return dataUrl;\n\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n for (const shape of shapes) {\n drawShape(ctx, shape);\n }\n\n return canvas.toDataURL(\"image/webp\", quality);\n}\n\n/** Options for {@link AnnotationEditor} / {@link openAnnotationEditor}. */\nexport interface AnnotationEditorOptions {\n /** The captured screenshot as a WebP data URL. */\n dataUrl: string;\n /** Shadow root (or element) the editor is mounted into. */\n root: ShadowRoot | HTMLElement;\n /** WebP quality for the re-encoded composite. */\n quality?: number;\n /** Default stroke colour for rectangle/arrow/pen. */\n color?: string;\n /** Colour swatches offered in the toolbar. The active `color` is ensured present. */\n palette?: string[];\n /** Default stroke width. */\n strokeWidth?: number;\n /** Label overrides. */\n labels?: Partial<AnnotationLabels>;\n /** Apply the embed's per-instance `--qaid-*` CSS variables to the container. */\n applyVars?: (el: HTMLElement) => void;\n /** Announce a message via the embed's live regions. */\n announce?: (message: string, assertive?: boolean) => void;\n /** Turn the container into an accessible modal dialog (focus trap + inert). */\n openDialog?: (\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string }\n ) => void;\n /** Tear down the dialog semantics and restore focus. */\n closeDialog?: () => void;\n}\n\nlet editorSeq = 0;\n\n/**\n * Full-screen screenshot annotation editor. Construct it, call {@link open}\n * (which returns a promise resolving to the composited WebP, or `null` when the\n * user skips), and the controller manages its own DOM and teardown.\n */\nexport class AnnotationEditor {\n readonly shapes: Shape[] = [];\n tool: ShapeType = \"rect\";\n\n private readonly opts: AnnotationEditorOptions;\n private readonly labels: AnnotationLabels;\n private color: string;\n private readonly palette: string[];\n private readonly strokeWidth: number;\n private readonly uid = `qaid-annotate-${++editorSeq}`;\n\n private container!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D | null = null;\n private toolbar!: HTMLDivElement;\n private undoBtn!: HTMLButtonElement;\n private img: HTMLImageElement | null = null;\n\n private drawing = false;\n private current: Shape | null = null;\n private settled = false;\n // Assigned in open() before any settle() is reachable (done/skip only fire\n // after the editor is mounted).\n private resolveResult!: (value: string | null) => void;\n\n constructor(opts: AnnotationEditorOptions) {\n this.opts = opts;\n this.labels = { ...DEFAULT_LABELS, ...(opts.labels ?? {}) };\n this.color = opts.color ?? DEFAULT_STROKE;\n this.palette = (opts.palette && opts.palette.length ? opts.palette : DEFAULT_PALETTE).slice();\n // Ensure the active colour is offered as a swatch so it stays selectable.\n if (!this.palette.some((c) => c.toLowerCase() === this.color.toLowerCase())) {\n this.palette.unshift(this.color);\n }\n this.strokeWidth = opts.strokeWidth ?? DEFAULT_STROKE_WIDTH;\n }\n\n /** Mount the editor and resolve when the user commits (Done) or skips. */\n open(): Promise<string | null> {\n const promise = new Promise<string | null>((resolve) => {\n this.resolveResult = resolve;\n });\n this.mount();\n return promise;\n }\n\n private toolButton(tool: ShapeType, icon: string, label: string): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-tool\",\n \"data-qaid-tool\": tool,\n \"aria-pressed\": tool === this.tool ? \"true\" : \"false\",\n \"aria-label\": `${label} tool`,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n /** A round colour swatch button for the toolbar's colour group. */\n private swatchButton(color: string): HTMLButtonElement {\n const name = colorName(color);\n const btn = createElement(\"button\", {\n type: \"button\",\n class: \"qaid-annotate-swatch\",\n \"data-qaid-color\": color,\n \"aria-pressed\": color.toLowerCase() === this.color.toLowerCase() ? \"true\" : \"false\",\n \"aria-label\": `Draw in ${name}`,\n title: name,\n });\n btn.style.setProperty(\"--qaid-swatch\", color);\n return btn;\n }\n\n private actionButton(\n action: string,\n icon: string,\n label: string,\n extraClass: string\n ): HTMLButtonElement {\n const btn = createElement(\"button\", {\n type: \"button\",\n class: `qaid-annotate-action ${extraClass}`,\n \"data-qaid-action\": action,\n \"aria-label\": label,\n title: label,\n });\n btn.innerHTML = `${icon}<span class=\"qaid-annotate-btn-text\">${label}</span>`;\n return btn;\n }\n\n private mount(): void {\n const titleId = `${this.uid}-title`;\n const descId = `${this.uid}-desc`;\n\n this.container = createElement(\"div\", { class: \"qaid-annotate\" });\n this.opts.applyVars?.(this.container);\n\n const title = createElement(\"h2\", {\n id: titleId,\n class: \"qaid-annotate-title\",\n });\n title.textContent = this.labels.title;\n\n const desc = createElement(\"p\", {\n id: descId,\n class: \"qaid-annotate-desc\",\n });\n desc.textContent = this.labels.instructions;\n\n const stage = createElement(\"div\", { class: \"qaid-annotate-stage\" });\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"qaid-annotate-canvas\";\n this.canvas.width = 1;\n this.canvas.height = 1;\n this.canvas.setAttribute(\"role\", \"img\");\n this.canvas.setAttribute(\"aria-label\", this.labels.title);\n this.ctx = this.canvas.getContext(\"2d\");\n stage.appendChild(this.canvas);\n\n this.toolbar = createElement(\"div\", {\n class: \"qaid-annotate-toolbar\",\n role: \"toolbar\",\n \"aria-label\": this.labels.title,\n });\n\n const tools = createElement(\"div\", { class: \"qaid-annotate-tools\" });\n tools.appendChild(this.toolButton(\"rect\", RECTANGLE_ICON, this.labels.rectangle));\n tools.appendChild(this.toolButton(\"arrow\", ARROW_ICON, this.labels.arrow));\n tools.appendChild(this.toolButton(\"pen\", PENCIL_ICON, this.labels.pen));\n tools.appendChild(this.toolButton(\"blur\", BLUR_ICON, this.labels.blur));\n\n const colors = createElement(\"div\", {\n class: \"qaid-annotate-colors\",\n role: \"group\",\n \"aria-label\": this.labels.colors,\n });\n for (const c of this.palette) colors.appendChild(this.swatchButton(c));\n\n this.undoBtn = this.actionButton(\"undo\", UNDO_ICON, this.labels.undo, \"qaid-annotate-undo\");\n this.undoBtn.disabled = true;\n\n const skipBtn = this.actionButton(\"skip\", SKIP_ICON, this.labels.skip, \"qaid-annotate-skip\");\n const doneBtn = this.actionButton(\"done\", DONE_ICON, this.labels.done, \"qaid-annotate-done\");\n\n const actions = createElement(\"div\", { class: \"qaid-annotate-actions\" });\n actions.appendChild(this.undoBtn);\n actions.appendChild(skipBtn);\n actions.appendChild(doneBtn);\n\n this.toolbar.appendChild(tools);\n this.toolbar.appendChild(colors);\n this.toolbar.appendChild(actions);\n\n this.container.appendChild(title);\n this.container.appendChild(desc);\n this.container.appendChild(stage);\n this.container.appendChild(this.toolbar);\n this.opts.root.appendChild(this.container);\n\n this.toolbar.addEventListener(\"click\", this.onToolbarClick);\n this.canvas.addEventListener(\"pointerdown\", this.onPointerDown);\n this.container.addEventListener(\"pointermove\", this.onPointerMove);\n this.container.addEventListener(\"pointerup\", this.onPointerUp);\n this.container.addEventListener(\"keydown\", this.onKeyDown);\n\n this.opts.openDialog?.(this.container, {\n labelledbyId: titleId,\n describedbyId: descId,\n });\n this.opts.announce?.(this.labels.instructions);\n\n loadImage(this.opts.dataUrl)\n .then((img) => {\n this.img = img;\n this.canvas.width = img.naturalWidth || img.width || 1;\n this.canvas.height = img.naturalHeight || img.height || 1;\n this.redraw();\n })\n .catch(() => {\n this.img = null;\n this.redraw();\n });\n }\n\n /** Convert a pointer event's client coordinates into natural image pixels. */\n private toImageCoords(e: { clientX: number; clientY: number }): Point {\n const rect = this.canvas.getBoundingClientRect();\n const scaleX = this.canvas.width / (rect.width || this.canvas.width);\n const scaleY = this.canvas.height / (rect.height || this.canvas.height);\n return {\n x: (e.clientX - rect.left) * scaleX,\n y: (e.clientY - rect.top) * scaleY,\n };\n }\n\n /** Repaint the display canvas: base image (if loaded) then every shape. */\n redraw(): void {\n const ctx = this.ctx;\n if (!ctx) return;\n ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n if (this.img) {\n ctx.drawImage(this.img, 0, 0, this.canvas.width, this.canvas.height);\n }\n for (const shape of this.shapes) {\n drawShape(ctx, shape);\n }\n if (this.current) {\n drawShape(ctx, this.current);\n }\n }\n\n /** Select the active drawing tool and reflect it on the toolbar. */\n selectTool(tool: ShapeType): void {\n this.tool = tool;\n const buttons = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-tool]\");\n buttons.forEach((btn) => {\n btn.setAttribute(\n \"aria-pressed\",\n btn.getAttribute(\"data-qaid-tool\") === tool ? \"true\" : \"false\"\n );\n });\n const label = this.labels[LABEL_KEY[tool]];\n this.opts.announce?.(`${label} tool selected`);\n }\n\n /** Set the active drawing colour; subsequent shapes use it. */\n selectColor(color: string): void {\n this.color = color;\n const swatches = this.toolbar.querySelectorAll<HTMLButtonElement>(\"[data-qaid-color]\");\n swatches.forEach((s) => {\n s.setAttribute(\n \"aria-pressed\",\n (s.getAttribute(\"data-qaid-color\") ?? \"\").toLowerCase() === color.toLowerCase()\n ? \"true\"\n : \"false\"\n );\n });\n this.opts.announce?.(`${colorName(color)} colour selected`);\n }\n\n private onToolbarClick = (e: Event): void => {\n const target = e.target as HTMLElement | null;\n const btn = target?.closest<HTMLElement>(\n \"[data-qaid-tool],[data-qaid-action],[data-qaid-color]\"\n );\n if (!btn) return;\n const color = btn.getAttribute(\"data-qaid-color\");\n if (color) {\n this.selectColor(color);\n return;\n }\n const tool = btn.getAttribute(\"data-qaid-tool\");\n if (tool) {\n this.selectTool(tool as ShapeType);\n return;\n }\n const action = btn.getAttribute(\"data-qaid-action\");\n if (action === \"undo\") this.undo();\n else if (action === \"skip\") this.skip();\n else if (action === \"done\") void this.done();\n };\n\n onPointerDown = (e: PointerEvent): void => {\n e.preventDefault?.();\n this.drawing = true;\n const p = this.toImageCoords(e);\n this.current = {\n type: this.tool,\n color: this.color,\n strokeWidth: this.strokeWidth,\n points: this.tool === \"pen\" ? [p] : [p, { ...p }],\n };\n this.redraw();\n };\n\n onPointerMove = (e: PointerEvent): void => {\n if (!this.drawing || !this.current) return;\n const p = this.toImageCoords(e);\n if (this.current.type === \"pen\") {\n this.current.points.push(p);\n } else {\n this.current.points[1] = p;\n }\n this.redraw();\n };\n\n onPointerUp = (): void => {\n if (!this.drawing || !this.current) return;\n this.drawing = false;\n const shape = this.current;\n this.current = null;\n if (isDegenerate(shape)) {\n this.redraw();\n return;\n }\n this.shapes.push(shape);\n this.undoBtn.disabled = false;\n this.opts.announce?.(\"Annotation added\");\n this.redraw();\n };\n\n onKeyDown = (e: KeyboardEvent): void => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n this.skip();\n return;\n }\n if (e.key === \"Enter\") {\n const target = e.target as HTMLElement | null;\n // Let Enter activate a focused tool/action button natively.\n if (target?.getAttribute(\"data-qaid-tool\") || target?.getAttribute(\"data-qaid-action\")) {\n return;\n }\n e.preventDefault();\n void this.done();\n }\n };\n\n /** Remove the most recently added shape. */\n undo(): void {\n if (this.shapes.length === 0) {\n this.opts.announce?.(\"Nothing to undo\");\n return;\n }\n this.shapes.pop();\n this.undoBtn.disabled = this.shapes.length === 0;\n this.opts.announce?.(\"Removed last annotation\");\n this.redraw();\n }\n\n /** Composite the annotations and resolve with the new WebP data URL. */\n async done(): Promise<void> {\n if (this.settled) return;\n const out = await compositeAnnotations(this.opts.dataUrl, this.shapes, {\n quality: this.opts.quality,\n });\n this.opts.announce?.(\"Annotated screenshot attached\");\n this.settle(out);\n }\n\n /** Discard annotations and resolve with `null` (caller keeps the original). */\n skip(): void {\n if (this.settled) return;\n this.opts.announce?.(\"Annotation skipped\");\n this.settle(null);\n }\n\n private settle(value: string | null): void {\n this.settled = true;\n this.teardown();\n this.resolveResult(value);\n }\n\n private teardown(): void {\n this.toolbar.removeEventListener(\"click\", this.onToolbarClick);\n this.canvas.removeEventListener(\"pointerdown\", this.onPointerDown);\n this.container.removeEventListener(\"pointermove\", this.onPointerMove);\n this.container.removeEventListener(\"pointerup\", this.onPointerUp);\n this.container.removeEventListener(\"keydown\", this.onKeyDown);\n this.opts.closeDialog?.();\n this.container.remove();\n }\n}\n\n/** Maps a tool id to its label key, for announcements. */\nconst LABEL_KEY: Record<ShapeType, keyof AnnotationLabels> = {\n rect: \"rectangle\",\n arrow: \"arrow\",\n pen: \"pen\",\n blur: \"blur\",\n};\n\n/**\n * Open the annotation editor and resolve with the composited WebP data URL, or\n * `null` if the user skipped. Thin entry point used by the embed.\n */\nexport function openAnnotationEditor(\n opts: AnnotationEditorOptions\n): Promise<string | null> {\n return new AnnotationEditor(opts).open();\n}\n","/**\n * @qaiddev/thumbs-embed - Standalone Feedback Embed\n *\n * A zero-dependency feedback embed with element targeting.\n *\n * Usage via ES module:\n * ```typescript\n * import { QaidFeedback } from '@qaiddev/thumbs-embed';\n *\n * const embed = new QaidFeedback({ endpoint: '/api/feedback' });\n * ```\n *\n * Usage via script tag (full bundle):\n * ```html\n * <script src=\"qaid.umd.cjs\" data-endpoint=\"/api/feedback\"></script>\n * ```\n *\n * For progressive loading (small core + on-demand screenshot/video/redaction\n * chunks), load the ESM loader instead:\n * ```html\n * <script type=\"module\" src=\"…/loader.js\" data-endpoint=\"/api/feedback\"></script>\n * ```\n *\n * This entry re-exports the full public API. Because those re-exports pull the\n * feature modules into this chunk, this entry is the \"full\" bundle; the\n * `loader` entry stays lean so its features split into on-demand chunks.\n */\n\nimport { QaidFeedback } from \"./embed\";\nimport { autoInit } from \"./bootstrap\";\n\nexport { QaidFeedback };\nexport type {\n FeedbackConfig,\n QuestsLaunchConfig,\n ResolvedFeedbackConfig,\n ConsoleError,\n NetworkError,\n FeedbackData,\n SelectedBounds,\n FeedbackPayload,\n FeedbackResponse,\n FeedbackMessagePayload,\n} from \"./types\";\nexport { captureNetworkErrors } from \"./network-capture\";\nexport type { NetworkCapture } from \"./network-capture\";\nexport { createVideoRecorder, isVideoRecordingSupported, getSupportedMimeType } from \"./video-capture\";\nexport type { VideoRecorder, VideoRecorderOptions } from \"./video-capture\";\nexport { captureDomScreenshot, isDomScreenshotSupported } from \"./screenshot-dom\";\nexport type { DomScreenshotOptions } from \"./screenshot-dom\";\nexport {\n openAnnotationEditor,\n compositeAnnotations,\n drawShape,\n rectFromPoints,\n AnnotationEditor,\n} from \"./annotate\";\nexport type {\n Shape,\n ShapeType,\n Point,\n AnnotationLabels,\n AnnotationEditorOptions,\n} from \"./annotate\";\n\n// Auto-initialize from a script tag (legacy UMD / full-bundle path).\nautoInit();\n","/**\n * Nano DOM factory — a purpose-built, construction-only helper for this embed.\n *\n * The embed builds all of its UI imperatively inside a Shadow DOM. That means\n * the same create → set class/style/attrs → wire events → append dance repeats\n * across every button, overlay, modal, and recording control. `h()` collapses\n * that dance into a single declarative expression.\n *\n * It is deliberately NOT a framework: no reactivity, no virtual DOM, no runtime\n * state. It only constructs. That fits what the embed actually needs (one-shot\n * DOM construction plus a few imperative updates), adds no dependency, and\n * keeps the package zero-dependency. Aliasing `document` here also lets the\n * minifier mangle the call site, which the global `document.createElement`\n * can't be.\n */\n\nconst d = document;\n\nexport interface ElProps {\n /** className */\n class?: string;\n /** Inline styles as a cssText string. */\n style?: string;\n /** innerHTML — for icons / trusted SVG markup. */\n html?: string;\n /** textContent. */\n text?: string;\n /** Attributes set via setAttribute (aria-*, data-*, type, role, …). */\n attrs?: Record<string, string>;\n /** Event listeners keyed by type. */\n on?: Record<string, (e: Event) => void>;\n}\n\ntype Child = Node | string | null | undefined | false;\n\n/**\n * Create an element, apply props, and append children. Falsy children are\n * skipped so conditional content reads cleanly (`cond && h(...)`).\n */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props?: ElProps | null,\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const el = d.createElement(tag);\n if (props) {\n if (props.class) el.className = props.class;\n if (props.style) el.style.cssText = props.style;\n if (props.html != null) el.innerHTML = props.html;\n if (props.text != null) el.textContent = props.text;\n if (props.attrs) {\n for (const k in props.attrs) el.setAttribute(k, props.attrs[k]);\n }\n if (props.on) {\n for (const k in props.on) el.addEventListener(k, props.on[k]);\n }\n }\n for (const child of children) {\n if (child != null && child !== false) el.append(child);\n }\n return el;\n}\n","/**\n * Video-recording subsystem — extracted from embed.ts so it lives in its own\n * lazily-loaded chunk. A thumbs-only visitor never downloads any of this; the\n * embed dynamically imports it the first time the record button is used (and\n * warms it on hover — see embed.ts prewarmVideo).\n *\n * The controller owns all recording state and UI (recorder, redaction picker,\n * indicator, preview, submit). It talks back to the embed through the narrow\n * `RecordingHost` interface, so embed internals stay private and the coupling\n * is explicit.\n */\n\nimport { h } from \"./dom\";\nimport { isEmbedElement } from \"./dom-utils\";\nimport { captureNetworkErrors, type NetworkCapture } from \"./network-capture\";\nimport type { VideoRecorder } from \"./video-capture\";\nimport type { ResolvedFeedbackConfig, EmbedState } from \"./types\";\nimport type { ConsoleCapture } from \"./console-capture\";\n\n/** The slice of the embed the recording subsystem needs. */\nexport interface RecordingHost {\n readonly config: ResolvedFeedbackConfig;\n readonly visitorId: string;\n readonly uid: string;\n readonly overlayShadowHost: HTMLDivElement | null;\n readonly consoleCapture: ConsoleCapture | null;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n readonly state: EmbedState;\n setState(state: EmbedState): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void;\n closeDialogA11y(): void;\n setButtonsDisabled(disabled: boolean): void;\n tryLaunchQuest(\n type: \"up\" | \"down\" | \"video\",\n feedbackId: number | string | null\n ): Promise<boolean>;\n}\n\nexport class RecordingController {\n private videoRecorder: VideoRecorder | null = null;\n private networkCapture: NetworkCapture | null = null;\n private recordedBlob: Blob | null = null;\n private recordingIndicator: HTMLDivElement | null = null;\n private videoPreview: HTMLDivElement | null = null;\n private isRecording = false;\n private isSendingVideo = false;\n\n // Pre-recording redaction picking.\n private redactPicks: Element[] = [];\n private redactPickerRoot: HTMLDivElement | null = null;\n private redactRafId = 0;\n private boundRedactClick = (e: MouseEvent): void => this.handleRedactPickClick(e);\n\n constructor(private host: RecordingHost) {}\n\n /** Escape-key handler; returns true when it consumed the key. */\n handleEscape(): boolean {\n if (this.isRecording) {\n void this.stopRecording();\n return true;\n }\n if (this.videoPreview) {\n this.cancelRecordingPreview();\n return true;\n }\n if (this.redactPickerRoot) {\n this.finishRedactionPicking(false);\n return true;\n }\n return false;\n }\n\n destroy(): void {\n if (this.redactRafId) cancelAnimationFrame(this.redactRafId);\n this.redactRafId = 0;\n document.removeEventListener(\"click\", this.boundRedactClick, true);\n if (this.redactPickerRoot) {\n this.redactPickerRoot.remove();\n this.redactPickerRoot = null;\n }\n this.redactPicks = [];\n this.cleanupRecording();\n this.removeVideoPreview();\n }\n\n async startRecording(redactionElements: Element[] = []): Promise<void> {\n // Don't start if already recording or in a targeting/modal flow.\n if (this.isRecording || this.host.state !== \"IDLE\") return;\n\n try {\n this.networkCapture = captureNetworkErrors();\n\n const { createVideoRecorder } = await import(\"./video-capture\");\n this.videoRecorder = createVideoRecorder({\n maxDuration: this.host.config.videoOptions.maxDuration,\n redactionElements,\n });\n\n this.videoRecorder.onTick((elapsed) => {\n this.updateRecordingTimer(elapsed);\n });\n\n // Handle unexpected stops (browser stop button, max duration).\n this.videoRecorder.onStop((blob) => {\n if (this.isRecording) {\n this.recordedBlob = blob;\n this.isRecording = false;\n this.host.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n this.host.setButtonsDisabled(false);\n\n if (blob && blob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n });\n\n await this.videoRecorder.start();\n this.isRecording = true;\n this.host.announceMsg(\"Recording started\");\n this.host.setButtonsDisabled(true);\n this.showRecordingIndicator();\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n } catch (error) {\n // User denied screen share, shared a disallowed surface (window / whole\n // screen), the browser cannot blur the picked areas, or another error\n // occurred. Surface the tab-only and no-blur messages so neither is a\n // silent no-op. (Matched by text: video-capture is a separate lazy\n // chunk, so importing its constant here would merge the two.)\n if (error instanceof Error && /current tab|cannot blur/i.test(error.message)) {\n this.host.announceMsg(error.message);\n }\n this.cleanupRecording();\n }\n }\n\n private async stopRecording(): Promise<void> {\n try {\n this.recordedBlob = await this.videoRecorder!.stop();\n } catch {\n this.recordedBlob = null;\n }\n\n this.isRecording = false;\n this.host.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n\n if (this.recordedBlob && this.recordedBlob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n\n /**\n * Pre-recording redaction picker. The user clicks page areas to blur; each\n * gets an outline that tracks its position, then \"Start recording\" hands the\n * picks to the recorder, which blurs their live bounding boxes so the\n * redaction follows the content as the page scrolls. \"Cancel\"/Escape aborts.\n */\n startPicking(): void {\n if (this.isRecording || this.host.state !== \"IDLE\") return;\n this.host.setState(\"REDACT_PICKING\");\n this.redactPicks = [];\n\n const root = this.host.ensureOverlayHost();\n\n // Layer holding the per-pick outlines (repositioned every frame).\n const boxHost = h(\"div\", { style: \"position:absolute;inset:0;\" });\n\n const container = h(\n \"div\",\n { class: \"qaid-redact-picker\", style: \"position:fixed;inset:0;pointer-events:none;\" },\n boxHost,\n // Control bar — the only pointer-interactive part of the overlay.\n h(\n \"div\",\n {\n style:\n \"position:fixed;left:50%;bottom:24px;transform:translateX(-50%);\" +\n \"display:flex;gap:12px;align-items:center;pointer-events:auto;\" +\n \"background:#0b1220;color:#fff;border:1px solid #ff6b6b;border-radius:9999px;\" +\n \"padding:10px 16px;font:600 13px system-ui,sans-serif;box-shadow:0 8px 30px rgba(0,0,0,.5);\",\n },\n h(\"span\", {\n text: \"Click sensitive areas to blur\",\n attrs: { \"data-qaid-redact-label\": \"\" },\n }),\n h(\"button\", {\n text: \"Start recording\",\n attrs: { type: \"button\" },\n style:\n \"cursor:pointer;border:0;border-radius:9999px;padding:8px 14px;background:#ff6b6b;color:#0b1220;font:inherit;\",\n on: { click: () => this.finishRedactionPicking(true) },\n }),\n h(\"button\", {\n text: \"Cancel\",\n attrs: { type: \"button\" },\n style: \"cursor:pointer;border:0;background:transparent;color:#9fb3c8;font:inherit;\",\n on: { click: () => this.finishRedactionPicking(false) },\n }),\n ),\n );\n\n root.appendChild(container);\n this.redactPickerRoot = container;\n\n // Keep the outlines glued to their elements while the user scrolls/picks.\n const tick = (): void => {\n this.renderRedactPicks(boxHost);\n this.redactRafId = requestAnimationFrame(tick);\n };\n tick();\n\n document.addEventListener(\"click\", this.boundRedactClick, true);\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n this.host.announceMsg(\"Click sensitive areas to blur, then start recording\");\n }\n\n private handleRedactPickClick(e: MouseEvent): void {\n const target = e.target instanceof Element ? e.target : null;\n // Ignore clicks on the embed's own UI. isEmbedElement catches the retargeted\n // shadow host (real browsers compose click events to it); the contains()\n // check catches the picker's own control-bar buttons when the event isn't\n // retargeted, so their handlers still fire instead of being swallowed here.\n if (!target || isEmbedElement(target) || this.redactPickerRoot?.contains(target)) return;\n e.preventDefault();\n e.stopPropagation();\n\n const idx = this.redactPicks.indexOf(target);\n if (idx >= 0) {\n this.redactPicks.splice(idx, 1);\n } else {\n this.redactPicks.push(target);\n }\n\n const label = this.redactPickerRoot?.querySelector(\"[data-qaid-redact-label]\");\n if (label) {\n const n = this.redactPicks.length;\n label.textContent =\n n === 0 ? \"Click sensitive areas to blur\" : `${n} area${n === 1 ? \"\" : \"s\"} will be blurred`;\n }\n }\n\n private renderRedactPicks(layer: HTMLElement): void {\n // Rebuild the outline boxes to match current picks and their live positions.\n layer.textContent = \"\";\n for (const el of this.redactPicks) {\n const r = el.getBoundingClientRect();\n if (r.width <= 0 || r.height <= 0) continue;\n layer.appendChild(\n h(\"div\", {\n style:\n `position:fixed;left:${r.left}px;top:${r.top}px;width:${r.width}px;height:${r.height}px;` +\n \"border:2px solid #ff6b6b;border-radius:4px;background:rgba(255,107,107,.18);pointer-events:none;\",\n }),\n );\n }\n }\n\n private finishRedactionPicking(record: boolean): void {\n if (this.redactRafId) cancelAnimationFrame(this.redactRafId);\n this.redactRafId = 0;\n document.removeEventListener(\"click\", this.boundRedactClick, true);\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n if (this.redactPickerRoot) {\n this.redactPickerRoot.remove();\n this.redactPickerRoot = null;\n }\n const picks = this.redactPicks;\n this.redactPicks = [];\n this.host.setState(\"IDLE\");\n if (record) {\n void this.startRecording(picks);\n }\n }\n\n private showRecordingIndicator(): void {\n const root = this.host.ensureOverlayHost();\n\n this.recordingIndicator = h(\n \"div\",\n { class: \"qaid-recording-indicator\", style: `z-index:${this.host.config.zIndex + 100}` },\n h(\"div\", { class: \"qaid-recording-dot\" }),\n h(\"span\", {\n class: \"qaid-recording-time\",\n text: this.formatTime(this.host.config.videoOptions.maxDuration),\n }),\n h(\"button\", {\n class: \"qaid-recording-stop\",\n text: \"Stop\",\n attrs: { type: \"button\" },\n on: { click: () => this.stopRecording() },\n }),\n );\n\n this.host.applyVars(this.recordingIndicator);\n\n // Enable pointer events on the overlay host for the recording indicator.\n if (this.host.overlayShadowHost) {\n this.host.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n\n root.appendChild(this.recordingIndicator);\n }\n\n private updateRecordingTimer(elapsed: number): void {\n if (!this.recordingIndicator) return;\n const timer = this.recordingIndicator.querySelector<HTMLSpanElement>(\".qaid-recording-time\");\n if (timer) {\n const remaining = Math.max(0, this.host.config.videoOptions.maxDuration - elapsed);\n timer.textContent = this.formatTime(remaining);\n // Announce the final countdown so it is perceivable non-visually.\n if (remaining > 0 && remaining <= 5) {\n this.host.announceMsg(`${remaining} second${remaining === 1 ? \"\" : \"s\"} remaining`);\n }\n }\n }\n\n private formatTime(seconds: number): string {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${m}:${s.toString().padStart(2, \"0\")}`;\n }\n\n private removeRecordingIndicator(): void {\n if (this.recordingIndicator) {\n this.recordingIndicator.remove();\n this.recordingIndicator = null;\n }\n }\n\n private showRecordingPreview(): void {\n const root = this.host.ensureOverlayHost();\n\n const videoUrl = URL.createObjectURL(this.recordedBlob!);\n const titleId = `qaid-video-title-${this.host.uid}`;\n\n const title = h(\"h3\", { text: \"Review your recording\", attrs: { id: titleId } });\n\n // Boolean media props must be set as properties (the `muted` attribute is\n // unreliable for autoplay), so build the element then assign them.\n const videoEl = h(\"video\");\n videoEl.src = videoUrl;\n videoEl.controls = true;\n videoEl.autoplay = true;\n videoEl.muted = true;\n\n const textarea = h(\"textarea\", {\n attrs: {\n placeholder: \"Optional: Describe the issue you recorded...\",\n \"aria-label\": \"Describe the issue you recorded\",\n },\n });\n\n const sendBtn = h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-send\",\n text: \"Send\",\n attrs: { type: \"button\" },\n on: { click: () => this.submitVideoFeedback(textarea.value.trim() || null, sendBtn) },\n });\n\n const box = h(\n \"div\",\n { class: \"qaid-video-preview-box\" },\n title,\n videoEl,\n textarea,\n h(\n \"div\",\n { class: \"qaid-video-preview-actions\" },\n h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-cancel\",\n text: \"Cancel\",\n attrs: { type: \"button\" },\n on: { click: () => this.cancelRecordingPreview() },\n }),\n h(\"button\", {\n class: \"qaid-video-btn qaid-video-btn-rerecord\",\n text: \"Re-record\",\n attrs: { type: \"button\" },\n on: {\n click: () => {\n this.cancelRecordingPreview();\n this.startRecording();\n },\n },\n }),\n sendBtn,\n ),\n );\n\n this.videoPreview = h(\n \"div\",\n { class: \"qaid-video-preview\", style: `z-index:${this.host.config.zIndex + 100}` },\n box,\n );\n\n this.host.applyVars(this.videoPreview);\n\n // Enable pointer events on the overlay host for the video preview.\n if (this.host.overlayShadowHost) {\n this.host.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n\n root.appendChild(this.videoPreview);\n\n // Dialog semantics + focus trap + background inert.\n this.host.openDialogA11y(box, { labelledbyId: title.id });\n\n // Listen for the escape key.\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private cancelRecordingPreview(): void {\n this.removeVideoPreview();\n this.cleanupRecording();\n }\n\n private removeVideoPreview(): void {\n // Release focus trap / inert and restore focus before removing the DOM.\n this.host.closeDialogA11y();\n if (this.videoPreview) {\n const videoEl = this.videoPreview.querySelector<HTMLVideoElement>(\"video\");\n if (videoEl?.src) {\n URL.revokeObjectURL(videoEl.src);\n }\n this.videoPreview.remove();\n this.videoPreview = null;\n }\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private async submitVideoFeedback(message: string | null, sendBtn: HTMLButtonElement): Promise<void> {\n if (!this.recordedBlob || this.isSendingVideo) return;\n\n this.isSendingVideo = true;\n sendBtn.disabled = true;\n sendBtn.textContent = \"Sending...\";\n\n const formData = new FormData();\n formData.append(\"video\", this.recordedBlob, `recording.${this.recordedBlob.type.includes(\"mp4\") ? \"mp4\" : \"webm\"}`);\n formData.append(\"pageUrl\", window.location.href);\n formData.append(\"visitorId\", this.host.visitorId);\n\n if (this.host.config.apiKey) {\n formData.append(\"apiKey\", this.host.config.apiKey);\n }\n if (message) {\n formData.append(\"message\", message);\n }\n if (this.host.consoleCapture) {\n formData.append(\"consoleErrors\", JSON.stringify(this.host.consoleCapture.errors));\n }\n if (this.networkCapture) {\n formData.append(\"networkErrors\", JSON.stringify(this.networkCapture.errors));\n }\n\n let videoFeedbackId: string | number | null = null;\n // Set when the upload failed: the reason shown to the visitor.\n let failure: string | null = null;\n try {\n const response = await fetch(`${this.host.config.endpoint}/video`, {\n method: \"POST\",\n body: formData,\n });\n\n if (response.ok) {\n this.host.announceMsg(\"Recording sent\");\n try {\n const data = (await response.json()) as { id?: string | number };\n videoFeedbackId = data?.id ?? null;\n } catch {\n // Non-JSON / no id — the quest can still launch, just unlinked.\n }\n } else {\n // A refusal (403 on a plan without video, 400, 413 too large, 429…)\n // used to close the preview exactly like a success.\n console.error(\"Failed to submit video feedback:\", await response.text());\n failure =\n response.status === 413\n ? \"This recording is too large to send. Try a shorter one.\"\n : \"Your recording could not be sent.\";\n }\n } catch (error) {\n console.error(\"Failed to submit video feedback:\", error);\n failure = \"Your recording could not be sent. Check your connection and try again.\";\n }\n\n this.isSendingVideo = false;\n\n if (failure) {\n this.showSendError(failure, sendBtn);\n return;\n }\n\n this.removeVideoPreview();\n this.cleanupRecording();\n\n // Once the recording is safely sent, launch the linked quest (if any),\n // passing the new feedback record id so its response is joinable.\n await this.host.tryLaunchQuest(\"video\", videoFeedbackId);\n }\n\n /**\n * Keep the preview open and say the upload failed. Closing it, as a success\n * does, told the visitor their recording had arrived when it had not. Send\n * is re-enabled for another try; Cancel and Re-record still work.\n */\n private showSendError(message: string, sendBtn: HTMLButtonElement): void {\n this.host.announceMsg(`Failed to send recording. ${message}`, true);\n sendBtn.disabled = false;\n sendBtn.textContent = \"Send\";\n\n const actions = this.videoPreview?.querySelector(\".qaid-video-preview-actions\");\n if (actions) {\n let note = this.videoPreview!.querySelector<HTMLElement>(\".qaid-video-error\");\n if (!note) {\n note = h(\"p\", { class: \"qaid-video-error\" });\n actions.before(note);\n }\n note.textContent = message;\n }\n }\n\n private cleanupRecording(): void {\n this.isRecording = false;\n this.removeRecordingIndicator();\n\n if (this.videoRecorder) {\n this.videoRecorder.destroy();\n this.videoRecorder = null;\n }\n if (this.networkCapture) {\n this.networkCapture.restore();\n this.networkCapture = null;\n }\n if (this.recordedBlob) {\n this.recordedBlob = null;\n }\n\n this.host.setButtonsDisabled(false);\n\n // Reset overlay host pointer events if nothing else needs them.\n if (this.host.overlayShadowHost && this.host.state === \"IDLE\") {\n this.host.overlayShadowHost.style.pointerEvents = \"none\";\n }\n }\n}\n","/**\n * Screenshot capture utility using native Screen Capture API\n */\n\nexport interface ScreenshotOptions {\n /** Quality of WebP compression (0-1). Default: 0.8 */\n quality?: number;\n /** Max width of the screenshot. Default: 1280 */\n maxWidth?: number;\n /** Max height of the screenshot. Default: 800 */\n maxHeight?: number;\n}\n\n/**\n * Captures a screenshot using the native Screen Capture API\n * Requires user permission but produces accurate results\n * Returns a base64-encoded WebP data URL\n */\nexport async function captureScreenshot(\n options: ScreenshotOptions = {}\n): Promise<string | null> {\n const { quality = 0.8, maxWidth = 1280, maxHeight = 800 } = options;\n\n try {\n // Check if API is available\n if (!navigator.mediaDevices?.getDisplayMedia) {\n console.warn('Screen Capture API not available');\n return null;\n }\n\n // Request screen capture - preferCurrentTab auto-selects current tab in supported browsers\n const stream = await navigator.mediaDevices.getDisplayMedia({\n preferCurrentTab: true,\n video: {\n displaySurface: 'browser',\n },\n } as DisplayMediaStreamOptions);\n\n // Get video track\n const track = stream.getVideoTracks()[0];\n const settings = track.getSettings();\n\n // Create video element\n const video = document.createElement('video');\n video.srcObject = stream;\n video.muted = true;\n\n await new Promise<void>((resolve) => {\n video.onloadedmetadata = () => {\n video.play();\n resolve();\n };\n });\n\n // Wait for video to have actual frame data\n await new Promise<void>((resolve) => {\n const checkFrame = () => {\n if (video.readyState >= 2) {\n resolve();\n } else {\n requestAnimationFrame(checkFrame);\n }\n };\n checkFrame();\n });\n\n // Extra delay to ensure frame is rendered\n await new Promise((resolve) => setTimeout(resolve, 100));\n\n // Calculate dimensions\n const srcWidth = settings.width || video.videoWidth;\n const srcHeight = settings.height || video.videoHeight;\n const scale = Math.min(maxWidth / srcWidth, maxHeight / srcHeight, 1);\n const width = Math.round(srcWidth * scale);\n const height = Math.round(srcHeight * scale);\n\n // Draw to canvas\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n track.stop();\n return null;\n }\n\n ctx.drawImage(video, 0, 0, width, height);\n\n // Stop the stream\n track.stop();\n\n return canvas.toDataURL('image/webp', quality);\n } catch (error) {\n console.warn('Screenshot capture failed:', error);\n return null;\n }\n}\n","/**\n * Feedback message modal — extracted from embed.ts into its own lazily-loaded\n * chunk. It's fetched the first time a message modal opens (after feedback is\n * submitted), and pre-warmed while the user is targeting an element.\n *\n * The controller owns the modal/backdrop DOM and the message PATCH flow, and\n * talks back to the embed through the narrow `ModalHost` interface.\n */\n\nimport { calculateModalAndArrowPosition } from \"./modal-positioning\";\nimport { h } from \"./dom\";\nimport {\n THUMBS_UP_ICON,\n THUMBS_DOWN_ICON,\n FEEDBACK_ICON,\n DONE_ICON,\n ERROR_ICON,\n} from \"./icons\";\nimport type {\n ResolvedFeedbackConfig,\n EmbedState,\n FeedbackData,\n SelectedBounds,\n} from \"./types\";\n\n/** The slice of the embed the modal needs. */\nexport interface ModalHost {\n readonly config: ResolvedFeedbackConfig;\n readonly uid: string;\n readonly isMobile: boolean;\n readonly state: EmbedState;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n /** Live references — the toggle mutates feedbackType; positioning reads bounds. */\n readonly feedbackData: FeedbackData;\n readonly selectedBounds: SelectedBounds;\n readonly feedbackId: number | null;\n setFeedbackId(id: number | null): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n openDialogA11y(\n container: HTMLElement,\n opts: { labelledbyId?: string; describedbyId?: string; label?: string }\n ): void;\n closeDialogA11y(): void;\n resetFeedbackUi(): void;\n}\n\nexport class ModalController {\n private modalContainer: HTMLDivElement | null = null;\n private backdrop: HTMLDivElement | null = null;\n\n constructor(private host: ModalHost) {}\n\n open(): void {\n const root = this.host.ensureOverlayHost();\n\n // Create backdrop\n this.backdrop = document.createElement(\"div\");\n this.backdrop.className = \"qaid-backdrop\";\n this.backdrop.style.zIndex = String(this.host.config.zIndex + 2);\n this.backdrop.style.background = `rgba(0, 0, 0, ${this.host.config.backdropOpacity})`;\n this.backdrop.addEventListener(\"click\", () => this.close());\n this.host.applyVars(this.backdrop);\n\n if (this.host.isMobile) {\n this.showBottomSheet();\n } else {\n this.showPositionedModal();\n }\n\n root.appendChild(this.backdrop);\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n /** Escape / external close. Runs the finalize-PATCH + teardown. */\n close(): void {\n // If the modal is open and we're closing without submitting, still finalize.\n if (this.host.state === \"MODAL_OPEN\" && this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message: null }),\n }).catch((err) => console.error(\"Failed to finalize feedback:\", err));\n }\n\n this.teardown();\n this.host.resetFeedbackUi();\n }\n\n /** DOM/listener teardown only — no PATCH, no state reset (for embed destroy). */\n destroy(): void {\n this.teardown();\n }\n\n private teardown(): void {\n this.host.closeDialogA11y();\n if (this.modalContainer) {\n this.modalContainer.remove();\n this.modalContainer = null;\n }\n if (this.backdrop) {\n this.backdrop.remove();\n this.backdrop = null;\n }\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n private showBottomSheet(): void {\n const root = this.host.ensureOverlayHost();\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"qaid-bottom-sheet\";\n sheet.style.zIndex = String(this.host.config.zIndex + 3);\n\n sheet.appendChild(\n h(\n \"div\",\n { class: \"qaid-bottom-sheet-content\" },\n h(\"div\", { class: \"qaid-bottom-sheet-handle\" }),\n this.buildModalContent()\n )\n );\n\n this.host.applyVars(sheet);\n root.appendChild(sheet);\n this.modalContainer = sheet;\n\n this.setupModalInteractions();\n }\n\n private showPositionedModal(): void {\n const root = this.host.ensureOverlayHost();\n\n const { modal, arrow } = calculateModalAndArrowPosition(\n this.host.selectedBounds,\n window.innerWidth,\n window.innerHeight,\n {\n width: this.host.config.modalWidth,\n height: 280,\n arrowHeight: 12,\n gap: 8,\n viewportPadding: 16,\n }\n );\n\n this.modalContainer = document.createElement(\"div\");\n this.modalContainer.className = `qaid-modal-container qaid-${modal.position}`;\n this.modalContainer.style.top = `${modal.top}px`;\n this.modalContainer.style.left = `${modal.left}px`;\n this.modalContainer.style.zIndex = String(this.host.config.zIndex + 3);\n\n const arrowEl = document.createElement(\"div\");\n arrowEl.className = \"qaid-modal-arrow\";\n arrowEl.style.left = `${arrow.left}px`;\n\n const box = document.createElement(\"div\");\n box.className = \"qaid-modal-box\";\n box.appendChild(this.buildModalContent());\n\n this.modalContainer.appendChild(arrowEl);\n this.modalContainer.appendChild(box);\n this.host.applyVars(this.modalContainer);\n root.appendChild(this.modalContainer);\n\n this.setupModalInteractions();\n }\n\n /**\n * The modal's contents, built as nodes rather than an HTML string.\n *\n * Every piece of copy (title, subtitle, placeholder, button) goes in as\n * text, so a `<`, `&` or `\"` in a translation shows as written instead of\n * being parsed as markup or cutting an attribute short. Only the icons are\n * markup, because they are documented as SVG/HTML strings.\n */\n private buildModalContent(): DocumentFragment {\n const { config, uid } = this.host;\n const text = config.text;\n const type = this.host.feedbackData.feedbackType;\n const isUp = type === \"up\";\n const positiveIcon = config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = config.negativeIcon || THUMBS_DOWN_ICON;\n const toggleClass = config.buttonClass\n ? `qaid-type-toggle qaid-type-toggle-custom ${config.buttonClass} ${isUp ? \"qaid-btn-up\" : \"qaid-btn-down\"}`\n : `qaid-type-toggle ${isUp ? \"qaid-type-up\" : \"qaid-type-down\"}`;\n const toggleLabel = isUp ? \"Feedback type: positive\" : \"Feedback type: negative\";\n // Neutral (single-button) feedback has no sentiment to toggle — show a\n // static feedback icon in place of the up/down toggle.\n const header =\n type === \"neutral\"\n ? h(\"span\", {\n class: \"qaid-type-static\",\n html: config.feedbackIcon || FEEDBACK_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n })\n : h(\"button\", {\n class: toggleClass,\n html: isUp ? positiveIcon : negativeIcon,\n attrs: {\n type: \"button\",\n title: \"Click to switch\",\n \"aria-pressed\": String(isUp),\n \"aria-label\": toggleLabel,\n },\n });\n\n const fragment = document.createDocumentFragment();\n fragment.append(\n h(\n \"div\",\n { class: \"qaid-modal-header\" },\n header,\n h(\n \"div\",\n { class: \"qaid-modal-header-text\" },\n h(\"h3\", {\n class: \"qaid-modal-title\",\n text: text.modalTitle,\n attrs: { id: `qaid-modal-title-${uid}` },\n }),\n h(\"p\", {\n class: \"qaid-modal-subtitle\",\n text: text.modalSubtitle,\n attrs: { id: `qaid-modal-subtitle-${uid}` },\n })\n )\n ),\n h(\"textarea\", {\n class: \"qaid-textarea\",\n attrs: { \"aria-label\": text.modalSubtitle, placeholder: text.placeholder },\n }),\n h(\n \"div\",\n { class: \"qaid-btn-row\" },\n h(\"button\", {\n class: \"qaid-btn-submit\",\n text: text.skipButton,\n attrs: { type: \"button\" },\n })\n )\n );\n return fragment;\n }\n\n private setupModalInteractions(): void {\n // Dialog semantics + focus trap + background inert\n this.host.openDialogA11y(this.modalContainer!, {\n labelledbyId: `qaid-modal-title-${this.host.uid}`,\n describedbyId: `qaid-modal-subtitle-${this.host.uid}`,\n });\n\n const textarea =\n this.modalContainer!.querySelector<HTMLTextAreaElement>(\".qaid-textarea\");\n const submitBtn =\n this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-btn-submit\");\n if (textarea) {\n // Auto-focus\n setTimeout(() => textarea.focus(), 100);\n\n // Update button text based on content\n textarea.addEventListener(\"input\", () => {\n if (submitBtn) {\n submitBtn.textContent = textarea.value.trim()\n ? this.host.config.text.submitButton\n : this.host.config.text.skipButton;\n }\n });\n }\n\n if (submitBtn) {\n submitBtn.addEventListener(\"click\", () => {\n const message = textarea?.value.trim() || null;\n this.submitMessage(message);\n });\n }\n\n // Handle feedback type toggle\n const typeToggle = this.modalContainer!.querySelector<HTMLButtonElement>(\".qaid-type-toggle\");\n if (typeToggle) {\n typeToggle.addEventListener(\"click\", () => {\n const newType = this.host.feedbackData.feedbackType === \"up\" ? \"down\" : \"up\";\n this.host.feedbackData.feedbackType = newType;\n\n // Update UI — use different classes depending on custom buttonClass\n if (this.host.config.buttonClass) {\n typeToggle.classList.toggle(\"qaid-btn-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-btn-down\", newType === \"down\");\n } else {\n typeToggle.classList.toggle(\"qaid-type-up\", newType === \"up\");\n typeToggle.classList.toggle(\"qaid-type-down\", newType === \"down\");\n }\n const positiveIcon = this.host.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.host.config.negativeIcon || THUMBS_DOWN_ICON;\n typeToggle.innerHTML = newType === \"up\" ? positiveIcon : negativeIcon;\n\n // Update accessible state and announce the change\n const toggleLabel = newType === \"up\" ? \"Feedback type: positive\" : \"Feedback type: negative\";\n typeToggle.setAttribute(\"aria-pressed\", String(newType === \"up\"));\n typeToggle.setAttribute(\"aria-label\", toggleLabel);\n this.host.announceMsg(toggleLabel);\n\n // Update feedback on server\n if (this.host.feedbackId) {\n fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ feedbackType: newType }),\n }).catch((err) => console.error(\"Failed to update feedback type:\", err));\n }\n });\n }\n }\n\n private async submitMessage(message: string | null): Promise<void> {\n // null = nothing was attempted (the initial POST never produced an id);\n // otherwise whether the server accepted the PATCH.\n let delivered: boolean | null = null;\n\n if (this.host.feedbackId) {\n try {\n const response = await fetch(`${this.host.config.endpoint}/${this.host.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n // fetch resolves on any HTTP reply. A 4xx/5xx means the server\n // refused or lost the message, so it is not a success.\n delivered = response.ok;\n if (!response.ok) {\n console.error(\"Failed to submit feedback message: HTTP\", response.status);\n }\n } catch (error) {\n console.error(\"Failed to submit feedback message:\", error);\n delivered = false;\n }\n // Clear feedbackId so close() doesn't send another PATCH.\n this.host.setFeedbackId(null);\n }\n\n // The message was lost. Closing quietly reads as success, so say so —\n // even with hideConfirmation, which only opts out of the success screen.\n if (delivered === false) {\n this.showResult(false);\n return;\n }\n\n // A submit that simply removes the modal reads as a failure — nothing\n // acknowledges that the message was sent. Show a success screen unless the\n // host opted out.\n //\n // With no feedbackId the initial POST never landed and there was nothing\n // to send the message to; that path closes exactly as it did before.\n if (delivered && !this.host.config.hideConfirmation) {\n this.showResult(true);\n return;\n }\n this.close();\n }\n\n /**\n * Replace the modal's contents with the outcome: a checkmark and a short\n * acknowledgement, or an error mark and a \"not sent\" notice.\n *\n * The submit button that had focus is gone by this point, so focus moves to\n * the heading (WCAG 2.4.3) and the message is announced. Mirrors the quests\n * embed's thank-you screen so the two products confirm the same way. Copy\n * goes in as text, never markup.\n */\n private showResult(delivered: boolean): void {\n const box = this.modalContainer?.querySelector(\".qaid-modal-box\")\n ?? this.modalContainer?.querySelector(\".qaid-bottom-sheet-content\")\n ?? this.modalContainer;\n\n if (!box) {\n // Nothing to draw into — never strand the user with an open modal.\n this.close();\n return;\n }\n\n const text = this.host.config.text;\n const heading = delivered ? text.confirmationTitle : text.errorTitle;\n const body = delivered ? text.confirmationMessage : text.errorMessage;\n\n const title = h(\"h3\", {\n class: \"qaid-confirm-title\",\n text: heading,\n attrs: { id: `qaid-confirm-title-${this.host.uid}`, tabindex: \"-1\" },\n });\n\n box.textContent = \"\";\n box.appendChild(\n h(\n \"div\",\n { class: delivered ? \"qaid-confirm\" : \"qaid-confirm qaid-confirm-error\" },\n h(\"span\", {\n class: \"qaid-confirm-icon\",\n html: delivered ? DONE_ICON : ERROR_ICON,\n attrs: { \"aria-hidden\": \"true\" },\n }),\n title,\n h(\"p\", { class: \"qaid-confirm-message\", text: body }),\n h(\"button\", {\n class: \"qaid-btn-submit qaid-confirm-close\",\n text: text.confirmationClose,\n attrs: { type: \"button\" },\n on: { click: () => this.close() },\n })\n )\n );\n\n // Focus after paint, matching the quests embed.\n requestAnimationFrame(() => title.focus());\n this.host.announceMsg(`${heading} ${body}`, true);\n }\n}\n","/**\n * Element selector generation utilities\n */\n\n/** Data attributes to look for (in priority order) */\nexport const DATA_ATTRS = [\"data-comp\", \"data-qa\", \"data-testid\", \"data-id\"];\n\nexport interface ElementInfo {\n selector: string;\n text: string;\n dataAttr: string | null;\n}\n\n/**\n * Find the first matching data attribute on an element or its ancestors\n */\nexport function findDataAttribute(\n element: Element,\n stopAt: Element = document.body\n): string | null {\n let current: Element | null = element;\n\n while (current && current !== stopAt) {\n for (const attr of DATA_ATTRS) {\n const value = current.getAttribute(attr);\n if (value) {\n return `${attr}=\"${value}\"`;\n }\n }\n current = current.parentElement;\n }\n\n return null;\n}\n\n/**\n * Get truncated text content from an element\n */\nexport function getTruncatedText(element: Element, maxLength = 100): string {\n const textContent = element.textContent?.trim().slice(0, maxLength) || \"\";\n return textContent.length === maxLength ? textContent + \"...\" : textContent;\n}\n\n/**\n * Get the nth-child index of an element among its siblings\n */\nfunction getNthChildIndex(element: Element): number {\n let index = 1;\n let sibling = element.previousElementSibling;\n while (sibling) {\n index++;\n sibling = sibling.previousElementSibling;\n }\n return index;\n}\n\n/**\n * Generate an nth-child path from body to the element\n * This is always a valid CSS selector and uniquely identifies the element\n * Exported for testing\n */\nexport function generateNthChildPath(element: Element): string {\n const path: string[] = [];\n let current: Element | null = element;\n\n while (current && current !== document.body && current !== document.documentElement) {\n const tag = current.tagName.toLowerCase();\n const index = getNthChildIndex(current);\n path.unshift(`${tag}:nth-child(${index})`);\n current = current.parentElement;\n }\n\n return path.length > 0 ? `body > ${path.join(\" > \")}` : \"body\";\n}\n\n/**\n * Escape special CSS characters in a string (for IDs with special chars)\n */\nfunction escapeCSSIdentifier(str: string): string {\n return str.replace(/([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^`{|}~])/g, \"\\\\$1\");\n}\n\n/**\n * Generate a selector string for an element\n * Priority: data attribute > ID > nth-child path\n */\nexport function generateSelector(element: Element): string {\n // Try data attribute first (on element or ancestors) - most stable\n const dataAttr = findDataAttribute(element);\n if (dataAttr) {\n return `[${dataAttr}]`;\n }\n\n // Try ID (escaped for special characters)\n if (element.id) {\n return `#${escapeCSSIdentifier(element.id)}`;\n }\n\n // Fall back to nth-child path - always valid and unique\n return generateNthChildPath(element);\n}\n\n/**\n * Generate complete element info for feedback\n */\nexport function generateElementInfo(element: Element): ElementInfo {\n const text = getTruncatedText(element);\n const dataAttr = findDataAttribute(element);\n const selector = generateSelector(element);\n\n return { selector, text, dataAttr };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Keyboard element targeting */\n/* */\n/* The mouse targeting path in embed.ts resolves the target with */\n/* elementFromPoint(clientX, clientY). A keyboard-activated thumb produces a */\n/* synthetic click at (0, 0), so a keyboard-only user can never point at a */\n/* real element. This controller provides a coordinate-free alternative: */\n/* Tab / arrow keys move a highlight between candidate elements (seeded from */\n/* document.activeElement), Enter / Space selects, Escape cancels. It never */\n/* hides the cursor — it drives the *native* focus ring instead, so both */\n/* sighted keyboard users and screen readers can follow the highlight. */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Default set of \"targetable\" elements for keyboard navigation: focusable\n * controls, ARIA-role'd nodes, common semantic content, and anything already\n * carrying one of the stable data attributes we key selectors off of.\n */\nexport const TARGETABLE_SELECTOR: string = [\n \"a[href]\",\n \"button\",\n \"input:not([type=hidden])\",\n \"select\",\n \"textarea\",\n \"summary\",\n \"[tabindex]\",\n \"[role]\",\n \"img\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"p\",\n \"li\",\n \"label\",\n ...DATA_ATTRS.map((attr) => `[${attr}]`),\n].join(\",\");\n\n/** Tag names that already take keyboard focus without an added tabindex. */\nconst NATIVELY_FOCUSABLE = /^(?:A|BUTTON|INPUT|SELECT|TEXTAREA|SUMMARY)$/;\n\n/**\n * Best-effort visibility test used to keep invisible nodes out of the keyboard\n * candidate list. Deliberately layout-free (no getBoundingClientRect) so it is\n * stable in headless/test environments: it only rejects nodes that are\n * explicitly hidden via the `hidden` attribute, `aria-hidden`, or a computed\n * `display:none` / `visibility:hidden`.\n */\nexport function isElementVisible(element: Element): boolean {\n if (element.hasAttribute(\"hidden\")) return false;\n if (element.getAttribute(\"aria-hidden\") === \"true\") return false;\n const view = element.ownerDocument?.defaultView;\n if (view && typeof view.getComputedStyle === \"function\") {\n const style = view.getComputedStyle(element);\n if (style.display === \"none\" || style.visibility === \"hidden\") return false;\n }\n return true;\n}\n\nexport interface CollectTargetableOptions {\n /** Where to search for candidates (default `document.body`). */\n root?: ParentNode;\n /** CSS selector for candidate elements (default {@link TARGETABLE_SELECTOR}). */\n selector?: string;\n /** Return true to drop an element (e.g. the embed's own shadow hosts). */\n isExcluded?: (element: Element) => boolean;\n /** Visibility predicate (default {@link isElementVisible}). */\n isVisible?: (element: Element) => boolean;\n}\n\n/**\n * Collect the ordered list of elements a keyboard user can target. Document\n * order matches the natural Tab order closely enough for feedback targeting.\n */\nexport function collectTargetableElements(\n options: CollectTargetableOptions = {}\n): Element[] {\n const root = options.root ?? document.body;\n const selector = options.selector ?? TARGETABLE_SELECTOR;\n const isExcluded = options.isExcluded ?? (() => false);\n const isVisible = options.isVisible ?? isElementVisible;\n\n return Array.from(root.querySelectorAll(selector)).filter(\n (element) => !isExcluded(element) && isVisible(element)\n );\n}\n\nexport interface KeyboardTargetingOptions {\n /**\n * Explicit candidate list. When omitted, candidates are collected via\n * {@link collectTargetableElements} using `root` / `selector` / `isExcluded`\n * / `isVisible`.\n */\n candidates?: Element[];\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n root?: ParentNode;\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n selector?: string;\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n isExcluded?: (element: Element) => boolean;\n /** Passed through to {@link collectTargetableElements} when `candidates` is omitted. */\n isVisible?: (element: Element) => boolean;\n /**\n * Element to start the highlight on. Defaults to `document.activeElement`\n * when it is one of the candidates, otherwise the first candidate.\n */\n initial?: Element | null;\n /**\n * Move native focus to the highlighted element (default true). This is what\n * keeps a screen reader / focus ring on the highlight; non-focusable nodes\n * get a temporary `tabindex=\"-1\"` that is removed on {@link stop}. The cursor\n * is never hidden.\n */\n moveFocus?: boolean;\n /** Where the keydown listener is attached (default `document`). */\n eventTarget?: Document | HTMLElement;\n /** Fired whenever the highlighted candidate changes (and once on start). */\n onHighlight?: (element: Element, index: number) => void;\n /** Fired when the user commits a selection (Enter / Space). Auto-stops first. */\n onSelect: (element: Element) => void;\n /** Fired when the user cancels (Escape). Auto-stops first. */\n onCancel?: () => void;\n}\n\nexport interface KeyboardTargetingController {\n /** The (frozen) candidate list being navigated. */\n readonly candidates: readonly Element[];\n /** Index of the highlighted candidate, or -1 when there are none. */\n getIndex(): number;\n /** The highlighted candidate, or null when there are none. */\n getCurrent(): Element | null;\n /** Highlight the next candidate (wraps to the first). */\n next(): void;\n /** Highlight the previous candidate (wraps to the last). */\n prev(): void;\n /** Highlight a specific index (wraps out-of-range values). */\n moveTo(index: number): void;\n /** Commit the current candidate (fires onSelect) and stop. */\n select(): void;\n /** Cancel (fires onCancel) and stop. */\n cancel(): void;\n /**\n * Handle a keydown. Wired automatically to `eventTarget`; exposed so a host\n * can forward events from another surface (e.g. the embed's shadow root).\n */\n handleKey(event: KeyboardEvent): void;\n /** Remove the listener and restore any temporary tabindex. Idempotent. */\n stop(): void;\n}\n\n/**\n * Start a keyboard element-targeting session. The returned controller wires its\n * own capture-phase keydown listener on `eventTarget` and emits the initial\n * highlight synchronously before returning.\n */\nexport function startKeyboardTargeting(\n options: KeyboardTargetingOptions\n): KeyboardTargetingController {\n const candidates =\n options.candidates ??\n collectTargetableElements({\n root: options.root,\n selector: options.selector,\n isExcluded: options.isExcluded,\n isVisible: options.isVisible,\n });\n\n const moveFocus = options.moveFocus ?? true;\n const target: EventTarget = options.eventTarget ?? document;\n const addedTabindex = new Set<Element>();\n\n let index = -1;\n let stopped = false;\n\n // Seed the highlight from the currently focused element when it is a\n // candidate; this makes \"Tab to a control, then hit the thumb\" land on that\n // control rather than the top of the page.\n const seed =\n options.initial !== undefined\n ? options.initial\n : typeof document !== \"undefined\"\n ? document.activeElement\n : null;\n if (seed) {\n const seedIndex = candidates.indexOf(seed);\n if (seedIndex >= 0) index = seedIndex;\n }\n if (index < 0 && candidates.length > 0) index = 0;\n\n function focusCurrent(element: Element): void {\n if (!moveFocus) return;\n const focusable = element as HTMLElement;\n if (typeof focusable.focus !== \"function\") return;\n // Make non-focusable content (p, img, li, …) programmatically focusable so\n // the native focus ring and AT follow the highlight. Never hide the cursor.\n if (\n element.getAttribute(\"tabindex\") === null &&\n !NATIVELY_FOCUSABLE.test(element.tagName)\n ) {\n element.setAttribute(\"tabindex\", \"-1\");\n addedTabindex.add(element);\n }\n try {\n focusable.focus();\n } catch {\n /* focus can throw in detached/headless cases — highlight still works */\n }\n }\n\n function emitHighlight(): void {\n const element = candidates[index];\n if (!element) return;\n focusCurrent(element);\n options.onHighlight?.(element, index);\n }\n\n function moveTo(nextIndex: number): void {\n if (stopped || candidates.length === 0) return;\n const len = candidates.length;\n index = ((nextIndex % len) + len) % len; // wrap in both directions\n emitHighlight();\n }\n\n function next(): void {\n moveTo(index + 1);\n }\n\n function prev(): void {\n moveTo(index - 1);\n }\n\n function getCurrent(): Element | null {\n return candidates[index] ?? null;\n }\n\n function stop(): void {\n if (stopped) return;\n stopped = true;\n target.removeEventListener(\"keydown\", handleKey as EventListener, true);\n addedTabindex.forEach((element) => element.removeAttribute(\"tabindex\"));\n addedTabindex.clear();\n }\n\n function select(): void {\n if (stopped) return;\n const element = getCurrent();\n stop();\n if (element) options.onSelect(element);\n }\n\n function cancel(): void {\n if (stopped) return;\n stop();\n options.onCancel?.();\n }\n\n function handleKey(event: KeyboardEvent): void {\n if (stopped) return;\n switch (event.key) {\n case \"Tab\":\n event.preventDefault();\n event.stopPropagation();\n if (event.shiftKey) prev();\n else next();\n break;\n case \"ArrowDown\":\n case \"ArrowRight\":\n event.preventDefault();\n event.stopPropagation();\n next();\n break;\n case \"ArrowUp\":\n case \"ArrowLeft\":\n event.preventDefault();\n event.stopPropagation();\n prev();\n break;\n case \"Enter\":\n case \" \":\n case \"Spacebar\": // legacy key value\n event.preventDefault();\n event.stopPropagation();\n select();\n break;\n case \"Escape\":\n case \"Esc\": // legacy key value\n event.preventDefault();\n event.stopPropagation();\n cancel();\n break;\n default:\n break;\n }\n }\n\n target.addEventListener(\"keydown\", handleKey as EventListener, true);\n\n // Emit the initial highlight so the caller can position its highlight box.\n if (index >= 0) emitHighlight();\n\n return {\n candidates,\n getIndex: () => index,\n getCurrent,\n next,\n prev,\n moveTo,\n select,\n cancel,\n handleKey,\n stop,\n };\n}\n","/**\n * Element-targeting subsystem — extracted from embed.ts into its own lazily\n * loaded chunk (it also carries `element-selector`, ~14 KB). It's fetched the\n * first time the user starts targeting (and pre-warmed on thumb-button hover,\n * so the reticle appears without a perceptible wait).\n *\n * The controller owns the reticle/overlay, the mouse/touch/keyboard targeting\n * flows, and the selected-element marker; it talks back to the embed through\n * the narrow `TargetingHost` interface.\n */\n\nimport {\n generateElementInfo,\n startKeyboardTargeting,\n type KeyboardTargetingController,\n} from \"./element-selector\";\nimport {\n getElementAtPointUnderOverlay,\n isEmbedElement,\n getElementBounds,\n} from \"./dom-utils\";\nimport type {\n ResolvedFeedbackConfig,\n EmbedState,\n FeedbackData,\n FeedbackType,\n SelectedBounds,\n} from \"./types\";\n\n/** A touch that moves more than this (px) is a scroll, not a tap-to-select. */\nconst TOUCH_TAP_SLOP = 12;\n\n/** The slice of the embed the targeting subsystem needs. */\nexport interface TargetingHost {\n readonly config: ResolvedFeedbackConfig;\n readonly cssVars: Record<string, string>;\n readonly state: EmbedState;\n readonly shadowHost: HTMLDivElement | null;\n readonly overlayShadowHost: HTMLDivElement | null;\n readonly boundKeyDown: (e: KeyboardEvent) => void;\n /** Live references, mutated in place. */\n readonly feedbackData: FeedbackData;\n readonly selectedBounds: SelectedBounds;\n setState(state: EmbedState): void;\n ensureOverlayHost(): ShadowRoot;\n applyVars(el: HTMLElement): void;\n announceMsg(message: string, assertive?: boolean): void;\n clearActiveThumb(): void;\n submitFeedback(): Promise<void>;\n}\n\nexport class TargetingController {\n private mousePos = { x: 0, y: 0 };\n private touchStartPos: { x: number; y: number } | null = null;\n private overlayContainer: HTMLDivElement | null = null;\n private captureLayer: HTMLDivElement | null = null;\n private crosshairH: HTMLDivElement | null = null;\n private crosshairV: HTMLDivElement | null = null;\n private scope: HTMLDivElement | null = null;\n private highlightBox: HTMLDivElement | null = null;\n private marker: HTMLDivElement | null = null;\n private keyboardController: KeyboardTargetingController | null = null;\n\n private boundMouseMove = (e: MouseEvent): void => this.handleMouseMove(e);\n private boundClick = (e: MouseEvent): void => this.handleClick(e);\n private boundTouchStart = (e: TouchEvent): void => this.handleTouchStart(e);\n private boundTouchEnd = (e: TouchEvent): void => this.handleTouchEnd(e);\n\n constructor(private host: TargetingHost) {}\n\n startPointer(type: FeedbackType, e: MouseEvent): void {\n this.host.setState(\"TARGETING\");\n this.host.feedbackData.feedbackType = type;\n this.host.feedbackData.elementSelector = null;\n this.host.feedbackData.elementText = null;\n this.host.selectedBounds.visible = false;\n\n // Track initial mouse position from the click event\n this.mousePos.x = e.clientX;\n this.mousePos.y = e.clientY;\n\n // Add class to body (light DOM — for cursor override)\n document.body.classList.add(\"qaid-targeting\");\n if (type === \"up\") {\n document.body.classList.add(\"qaid-type-up\");\n } else {\n document.body.classList.remove(\"qaid-type-up\");\n }\n\n // Set targeting colors on body so .qaid-highlight rules can resolve them\n document.body.style.setProperty(\"--qaid-positive\", this.host.cssVars[\"--qaid-positive\"]);\n document.body.style.setProperty(\"--qaid-negative\", this.host.cssVars[\"--qaid-negative\"]);\n\n this.createTargetingOverlay();\n\n // Add event listeners on document — overlay is pointer-events:none so scroll works naturally\n document.addEventListener(\"keydown\", this.host.boundKeyDown);\n document.addEventListener(\"mousemove\", this.boundMouseMove);\n document.addEventListener(\"click\", this.boundClick, true);\n // Touch: tap to select (scrolling stays enabled). touchstart is passive\n // (we only read the point); touchend is non-passive so a tap can\n // preventDefault the synthesised click that would otherwise activate the\n // targeted element.\n document.addEventListener(\"touchstart\", this.boundTouchStart, { passive: true });\n document.addEventListener(\"touchend\", this.boundTouchEnd, { passive: false });\n }\n\n /**\n * Keyboard-driven targeting. Mirrors startPointer minus the mouse plumbing:\n * no `qaid-targeting` body class (keeps the cursor visible for keyboard\n * users), no mouse reticle, and no document mouse/click listeners. The\n * KeyboardTargetingController owns Tab/Arrow/Enter/Space/Escape.\n */\n startKeyboard(type: FeedbackType): void {\n this.host.setState(\"TARGETING\");\n this.host.feedbackData.feedbackType = type;\n this.host.feedbackData.elementSelector = null;\n this.host.feedbackData.elementText = null;\n this.host.selectedBounds.visible = false;\n\n document.body.style.setProperty(\"--qaid-positive\", this.host.cssVars[\"--qaid-positive\"]);\n document.body.style.setProperty(\"--qaid-negative\", this.host.cssVars[\"--qaid-negative\"]);\n\n // Build the highlight box, then hide the mouse reticle (meaningless here)\n this.createTargetingOverlay();\n if (this.crosshairH) this.crosshairH.style.display = \"none\";\n if (this.crosshairV) this.crosshairV.style.display = \"none\";\n if (this.scope) this.scope.style.display = \"none\";\n\n this.keyboardController = startKeyboardTargeting({\n isExcluded: (el) => isEmbedElement(el),\n onHighlight: (el) => {\n const rect = el.getBoundingClientRect();\n const b = this.highlightBox;\n if (b) {\n b.style.transform = `translate(${rect.left}px, ${rect.top}px)`;\n b.style.width = `${rect.width}px`;\n b.style.height = `${rect.height}px`;\n b.style.display = \"block\";\n }\n const { text } = generateElementInfo(el);\n this.host.announceMsg(`Targeting ${text || el.tagName.toLowerCase()}`);\n },\n onSelect: (el) => this.selectKeyboardTarget(el),\n onCancel: () => this.cancel(),\n });\n }\n\n private selectKeyboardTarget(el: Element): void {\n const bounds = getElementBounds(el, 8);\n Object.assign(this.host.selectedBounds, bounds, {\n clickX: bounds.x + bounds.width / 2,\n clickY: bounds.y + bounds.height / 2,\n visible: true,\n });\n\n const { selector, text } = generateElementInfo(el);\n this.host.feedbackData.elementSelector = selector;\n this.host.feedbackData.elementText = text;\n\n // Teardown (controller has already auto-stopped before onSelect fired)\n this.removeTargetingOverlay();\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n this.keyboardController = null;\n this.host.clearActiveThumb();\n\n this.host.setState(\"SELECTED\");\n this.showSelectedMarker();\n void this.host.submitFeedback();\n }\n\n private createTargetingOverlay(): void {\n const root = this.host.ensureOverlayHost();\n\n this.overlayContainer = document.createElement(\"div\");\n this.overlayContainer.className = `qaid-targeting-overlay qaid-type-${this.host.feedbackData.feedbackType}`;\n\n // Capture layer (pointer-events:none — scroll passes through naturally)\n this.captureLayer = document.createElement(\"div\");\n this.captureLayer.className = \"qaid-capture-layer\";\n\n // Vignette\n const vignette = document.createElement(\"div\");\n vignette.className = \"qaid-vignette\";\n\n // Crosshairs\n this.crosshairH = document.createElement(\"div\");\n this.crosshairH.className = \"qaid-crosshair-h\";\n\n this.crosshairV = document.createElement(\"div\");\n this.crosshairV.className = \"qaid-crosshair-v\";\n\n // Scope\n this.scope = document.createElement(\"div\");\n this.scope.className = \"qaid-scope\";\n this.scope.innerHTML = `\n <div class=\"qaid-scope-ring\"></div>\n <div class=\"qaid-scope-ring-inner\"></div>\n <div class=\"qaid-scope-dot\"></div>\n `;\n\n // Highlight box overlay (positioned over hovered elements)\n this.highlightBox = document.createElement(\"div\");\n this.highlightBox.className = \"qaid-highlight-box\";\n\n this.overlayContainer.appendChild(this.captureLayer);\n this.overlayContainer.appendChild(vignette);\n this.overlayContainer.appendChild(this.highlightBox);\n this.overlayContainer.appendChild(this.crosshairH);\n this.overlayContainer.appendChild(this.crosshairV);\n this.overlayContainer.appendChild(this.scope);\n\n // Position crosshairs immediately at current mouse position\n this.crosshairH.style.top = `${this.mousePos.y}px`;\n this.crosshairV.style.left = `${this.mousePos.x}px`;\n this.scope.style.left = `${this.mousePos.x}px`;\n this.scope.style.top = `${this.mousePos.y}px`;\n\n this.host.applyVars(this.overlayContainer);\n root.appendChild(this.overlayContainer);\n }\n\n private handleMouseMove(e: MouseEvent): void {\n this.updateReticleAt(e.clientX, e.clientY);\n }\n\n /** Move the crosshair/scope reticle and highlight the element under (x, y). */\n private updateReticleAt(x: number, y: number): void {\n this.mousePos.x = x;\n this.mousePos.y = y;\n\n if (this.crosshairH) this.crosshairH.style.top = `${y}px`;\n if (this.crosshairV) this.crosshairV.style.left = `${x}px`;\n if (this.scope) {\n this.scope.style.left = `${x}px`;\n this.scope.style.top = `${y}px`;\n }\n\n // Find element underneath — hide both shadow hosts\n if (this.captureLayer && this.host.shadowHost) {\n const hosts = [this.host.shadowHost, this.host.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const elementUnder = getElementAtPointUnderOverlay(x, y, hosts);\n\n if (elementUnder && !isEmbedElement(elementUnder)) {\n if (this.highlightBox) {\n const rect = elementUnder.getBoundingClientRect();\n this.highlightBox.style.transform = `translate(${rect.left}px, ${rect.top}px)`;\n this.highlightBox.style.width = `${rect.width}px`;\n this.highlightBox.style.height = `${rect.height}px`;\n this.highlightBox.style.display = \"block\";\n }\n } else if (this.highlightBox) {\n this.highlightBox.style.display = \"none\";\n }\n }\n }\n\n private handleClick(e: MouseEvent): void {\n e.preventDefault();\n e.stopPropagation();\n this.selectAt(e.clientX, e.clientY);\n }\n\n // ---- Touch targeting (iOS/iPadOS) ----\n private handleTouchStart(e: TouchEvent): void {\n const t = e.touches[0];\n if (!t) return;\n this.touchStartPos = { x: t.clientX, y: t.clientY };\n // Preview what's under the finger; don't preventDefault so scrolling works.\n this.updateReticleAt(t.clientX, t.clientY);\n }\n\n private handleTouchEnd(e: TouchEvent): void {\n // touches is empty on touchend; the lifted point is in changedTouches.\n const t = e.changedTouches[0];\n const start = this.touchStartPos;\n this.touchStartPos = null;\n if (!t || !start) return;\n // A drag beyond the slop threshold is a scroll, not a selection — ignore it.\n const moved = Math.hypot(t.clientX - start.x, t.clientY - start.y);\n if (moved > TOUCH_TAP_SLOP) return;\n e.preventDefault(); // suppress the tap's synthesised click\n this.selectAt(t.clientX, t.clientY);\n }\n\n /** Select the element under (x, y) and tear down targeting. */\n private selectAt(x: number, y: number): void {\n const hosts = [this.host.shadowHost, this.host.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const target = getElementAtPointUnderOverlay(x, y, hosts);\n\n if (!target || isEmbedElement(target)) {\n return;\n }\n\n const bounds = getElementBounds(target, 8);\n Object.assign(this.host.selectedBounds, bounds, { clickX: x, clickY: y, visible: true });\n\n const { selector, text } = generateElementInfo(target);\n this.host.feedbackData.elementSelector = selector;\n this.host.feedbackData.elementText = text;\n\n // Remove targeting overlay and document listeners\n this.stopTargetingListeners();\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n\n this.host.setState(\"SELECTED\");\n this.host.clearActiveThumb();\n\n this.showSelectedMarker();\n void this.host.submitFeedback();\n }\n\n private stopTargetingListeners(): void {\n this.removeTargetingOverlay();\n this.touchStartPos = null;\n document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\n document.removeEventListener(\"touchstart\", this.boundTouchStart);\n document.removeEventListener(\"touchend\", this.boundTouchEnd);\n document.removeEventListener(\"keydown\", this.host.boundKeyDown);\n }\n\n cancel(): void {\n this.keyboardController?.stop();\n this.keyboardController = null;\n this.stopTargetingListeners();\n document.body.classList.remove(\"qaid-targeting\", \"qaid-type-up\");\n document.body.style.removeProperty(\"--qaid-positive\");\n document.body.style.removeProperty(\"--qaid-negative\");\n this.host.clearActiveThumb();\n\n this.host.setState(\"IDLE\");\n this.host.feedbackData.feedbackType = null;\n this.host.feedbackData.elementSelector = null;\n this.host.feedbackData.elementText = null;\n this.host.selectedBounds.visible = false;\n }\n\n private removeTargetingOverlay(): void {\n if (this.overlayContainer) {\n this.overlayContainer.remove();\n this.overlayContainer = null;\n }\n this.captureLayer = null;\n this.crosshairH = null;\n this.crosshairV = null;\n this.scope = null;\n }\n\n private showSelectedMarker(): void {\n const root = this.host.ensureOverlayHost();\n\n this.marker = document.createElement(\"div\");\n this.marker.className = \"qaid-selected-marker\";\n this.marker.style.left = `${this.host.selectedBounds.x}px`;\n this.marker.style.top = `${this.host.selectedBounds.y}px`;\n this.marker.style.width = `${this.host.selectedBounds.width}px`;\n this.marker.style.height = `${this.host.selectedBounds.height}px`;\n this.marker.style.zIndex = String(this.host.config.zIndex + 1);\n\n this.host.applyVars(this.marker);\n root.appendChild(this.marker);\n }\n\n hideMarker(): void {\n if (this.marker) {\n this.marker.remove();\n this.marker = null;\n }\n }\n\n /** Full teardown for embed.destroy(). */\n destroy(): void {\n this.keyboardController?.stop();\n this.keyboardController = null;\n this.stopTargetingListeners();\n this.hideMarker();\n }\n}\n"],"names":["THUMBS_UP_ICON","THUMBS_DOWN_ICON","RECORD_ICON","FEEDBACK_ICON","RECTANGLE_ICON","ARROW_ICON","PENCIL_ICON","BLUR_ICON","UNDO_ICON","DONE_ICON","ERROR_ICON","SKIP_ICON","instanceCount","DEFAULT_BUTTON_STYLES","BUTTON_SIZES","ICON_SIZES","getLuminance","r","g","b","rs","gs","bs","c","parseColor","color","hex","fullHex","num","match","getContrastTextColor","bgColor","rgb","buildCssVars","options","positiveColor","negativeColor","markerColor","buttonSize","modalWidth","backdropOpacity","fontFamily","fontSize","btnSize","iconSize","markerTextColor","applyCssVars","el","vars","name","value","getEmbedStyles","shadowStyles","injectStyles","style","lightStyles","removeStyles","POLITE_ATTR","VISUALLY_HIDDEN_CSS","FOCUSABLE_SELECTOR","ownerDoc","root","getLiveRegion","assertive","kind","existing","region","announce","message","opts","isDisabled","isHidden","node","getFocusable","container","getDeepActiveElement","doc","active","createFocusTrap","addedTabindex","onKeydown","event","focusable","first","last","withinTrap","initial","saveFocus","restoreFocus","applyDialog","topLevelAncestor","body","parent","setBackgroundInert","except","keep","changed","child","prevInert","prevAriaHidden","entry","DEFAULT_DIMENSIONS","calculateVerticalPosition","bounds","viewportHeight","totalHeight","gap","viewportPadding","spaceAbove","spaceBelow","position","top","calculateHorizontalPosition","viewportWidth","left","calculateArrowPosition","modalLeft","minOffset","arrowWidth","arrowLeft","calculateModalPosition","dimensions","vertical","calculateTooltipPosition","anchor","tooltip","viewport","calculateModalAndArrowPosition","modal","MAX_MESSAGES","captureConsoleErrors","onError","errors","originalError","originalWarn","originalLog","capture","level","args","error","arg","createElement","tag","attrs","children","key","isMobileViewport","breakpoint","getElementAtPointUnderOverlay","x","y","hosts","saved","h","element","i","isEmbedElement","getElementBounds","padding","rect","DEFAULT_QUESTS_MODULE_URL","defaultImporter","url","importer","modulePromise","cachedUrl","loadQuestsModule","moduleUrl","mod","m","err","launchQuest","base","metadata","VISITOR_ID_KEY","HIDE_FEEDBACK_KEY","DEFAULT_ANNOTATION_PALETTE","isTouchPrimaryDevice","getHideKey","apiKey","isHiddenByUser","setHiddenByUser","hidden","randomId","bytes","getOrCreateVisitorId","visitorId","QaidFeedback","config","_error","recording","videoCapture","screenshotDom","screenshot","fn","w","guarded","e","newDoc","userContainer","baseStyle","themeStyle","dirClass","offsetX","offsetY","ev","useCustomClass","btnBaseClass","tooltipText","fbWrapper","fbBtn","upWrapper","upBtn","downWrapper","downBtn","recordWrapper","recordBtn","rec","warmVideo","anchorRect","tooltipRect","type","buttonEl","keyboard","t","host","prevPointerEvents","openAnnotationEditor","annotate","msg","captureDomScreenshot","captureScreenshot","elementBounds","payload","response","data","q","feedbackId","questId","ModalController","self","id","TargetingController","targeting","state","RecordingController","disabled","btn","findCssFromSelector","selector","parseJsonConfig","configScript","textContent","parsed","warnIgnored","attr","expected","splitTopLevel","parts","depth","current","ch","parseColorList","raw","list","parseLabel","parseDataAttributes","script","endpoint","zIndex","buttonClass","skipTargeting","hideConfirmation","singleButton","feedbackMode","incognito","modalTitle","confirmationTitle","confirmationMessage","confirmationClose","errorTitle","errorMessage","modalSubtitle","placeholder","submitButton","skipButton","positiveIcon","negativeIcon","feedbackIcon","feedbackLabel","annotationColor","screenshotQuality","screenshotMaxWidth","screenshotMaxHeight","captureVideo","redaction","hideThumbs","hideDismiss","videoMaxDuration","screenshotMethod","direction","cssSelector","css","positiveLabel","negativeLabel","recordLabel","dismissLabel","annotationPalette","recordIcon","questBase","questUp","questDown","questVideo","questApiKey","questModuleUrl","autoInit","run","MAX_ENTRIES","MAX_BODY_SIZE","truncateBody","str","addEntry","safeReadBody","text","captureNetworkErrors","originalFetch","input","init","method","requestBody","responseBody","originalXHROpen","originalXHRSend","rest","xhr","isIOSDevice","ua","computeOrientationTransform","frameW","frameH","viewportW","viewportH","screenAngle","frameLandscape","viewportLandscape","drawRotatedFrame","ctx","source","getScreenAngle","orientation","waitForVideoDimensions","video","resolve","playSilently","buildOrientationCorrectedStream","displayStream","frameRate","canvas","transform","rafId","draw","stream","mapRectToFrame","scaleX","scaleY","right","bottom","drawRedactedFrame","elements","blurRadius","supportsFilter","box","buildRedactedStream","REDACTION_UNAVAILABLE","isVideoRecordingSupported","getSupportedMimeType","candidates","createVideoRecorder","maxDuration","videoBitsPerSecond","redactionElements","redactionBlurRadius","orientationCorrection","recorder","chunks","tickCallback","stopCallback","tickInterval","startTime","resolveStop","rejectStop","stopped","autoStopTimeout","cleanup","finishRecording","mimeType","videoTrack","surface","recordStream","corrected","redacted","blob","elapsed","reject","callback","SCRIPT_URL","LOAD_TIMEOUT","POLL_INTERVAL","loadPromise","isDomScreenshotSupported","loadHtml2Canvas","start","poll","quality","maxWidth","maxHeight","srcWidth","srcHeight","scale","scaledWidth","scaledHeight","scaledCanvas","BLUR_TILE","DEFAULT_STROKE","DEFAULT_PALETTE","COLOR_NAMES","colorName","DEFAULT_STROKE_WIDTH","DEFAULT_QUALITY","DEFAULT_LABELS","rectFromPoints","a","isDegenerate","shape","drawArrow","from","to","width","angle","head","drawPen","points","drawBlur","ty","tx","drawShape","loadImage","src","img","compositeAnnotations","dataUrl","shapes","editorSeq","AnnotationEditor","promise","tool","icon","label","action","extraClass","titleId","descId","title","desc","stage","tools","colors","skipBtn","doneBtn","actions","LABEL_KEY","s","p","target","out","d","props","k","boxHost","tick","idx","n","layer","record","picks","timer","remaining","seconds","videoUrl","videoEl","textarea","sendBtn","formData","videoFeedbackId","failure","note","track","settings","checkFrame","height","sheet","arrow","arrowEl","uid","isUp","toggleClass","toggleLabel","header","fragment","submitBtn","typeToggle","newType","delivered","heading","DATA_ATTRS","findDataAttribute","stopAt","getTruncatedText","maxLength","getNthChildIndex","index","sibling","generateNthChildPath","path","escapeCSSIdentifier","generateSelector","dataAttr","generateElementInfo","TARGETABLE_SELECTOR","NATIVELY_FOCUSABLE","isElementVisible","view","collectTargetableElements","isExcluded","isVisible","startKeyboardTargeting","moveFocus","seed","seedIndex","focusCurrent","emitHighlight","moveTo","nextIndex","len","next","prev","getCurrent","stop","handleKey","select","cancel","TOUCH_TAP_SLOP","vignette","elementUnder"],"mappings":"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"}