@qaiddev/thumbs-embed 1.0.17 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/a11y.d.ts +84 -0
- package/dist/element-selector.d.ts +96 -0
- package/dist/embed.d.ts +30 -0
- package/dist/qaid.js +543 -215
- package/dist/qaid.js.map +1 -1
- package/dist/qaid.umd.cjs +10 -10
- package/dist/qaid.umd.cjs.map +1 -1
- package/dist/types.d.ts +12 -0
- package/package.json +2 -1
package/dist/qaid.umd.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qaid.umd.cjs","sources":["../src/icons.ts","../src/styles.ts","../src/element-selector.ts","../src/modal-positioning.ts","../src/console-capture.ts","../src/network-capture.ts","../src/video-capture.ts","../src/dom-utils.ts","../src/screenshot.ts","../src/screenshot-dom.ts","../src/embed.ts","../src/index.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 * 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 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 = \"#6366f1\",\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 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 * 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 * 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 * 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 * Video capture utilities\n * Uses getDisplayMedia + MediaRecorder for screen recording\n */\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\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\n let stream: MediaStream | 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 (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\n stream = await navigator.mediaDevices.getDisplayMedia({\n video: {\n frameRate: 15,\n },\n audio: false,\n // @ts-expect-error preferCurrentTab is not in the TS types yet\n preferCurrentTab: true,\n });\n\n // Handle browser \"Stop sharing\" button\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack) {\n videoTrack.addEventListener(\"ended\", () => {\n finishRecording();\n });\n }\n\n recorder = new MediaRecorder(stream, {\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 stream tracks\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 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 * Screenshot capture utility using native Screen Capture API\n */\n\nexport interface ScreenshotOptions {\n /** Quality of WebP compression (0-1). Default: 1.0 */\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 = 1.0, 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 * 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","import type {\n FeedbackConfig,\n ResolvedFeedbackConfig,\n FeedbackData,\n SelectedBounds,\n EmbedState,\n FeedbackPayload,\n FeedbackResponse,\n} from \"./types\";\nimport { THUMBS_UP_ICON, THUMBS_DOWN_ICON, RECORD_ICON } from \"./icons\";\nimport { injectStyles, removeStyles, buildCssVars, applyCssVars, getEmbedStyles } from \"./styles\";\nimport { generateElementInfo } from \"./element-selector\";\nimport { calculateModalAndArrowPosition, calculateTooltipPosition } from \"./modal-positioning\";\nimport { captureConsoleErrors, type ConsoleCapture } from \"./console-capture\";\nimport { captureNetworkErrors, type NetworkCapture } from \"./network-capture\";\nimport {\n createVideoRecorder,\n isVideoRecordingSupported,\n type VideoRecorder,\n} from \"./video-capture\";\nimport {\n getElementAtPointUnderOverlay,\n isEmbedElement,\n getElementBounds,\n isMobileViewport,\n} from \"./dom-utils\";\nimport { captureScreenshot } from \"./screenshot\";\nimport { captureDomScreenshot } from \"./screenshot-dom\";\n\nconst VISITOR_ID_KEY = \"qaid_visitor_id\";\nconst HIDE_FEEDBACK_KEY = \"qaid_hide_feedback\";\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 * 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 = crypto.randomUUID();\n localStorage.setItem(VISITOR_ID_KEY, visitorId);\n }\n return visitorId;\n } catch {\n // localStorage not available, generate a session-only ID\n return crypto.randomUUID();\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 private mousePos = { x: 0, y: 0 };\n private isMobile = false;\n private visitorId: string;\n\n // Console capture\n private consoleCapture: ConsoleCapture | null = null;\n\n // Video recording\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 // 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 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 modalContainer: HTMLDivElement | null = null;\n private backdrop: HTMLDivElement | null = null;\n private dismissBtn: HTMLButtonElement | null = null;\n\n // Per-instance CSS variables\n private cssVars: Record<string, string> = {};\n\n // Bound event handlers\n private boundKeyDown: (e: KeyboardEvent) => void;\n private boundMouseMove: (e: MouseEvent) => void;\n private boundClick: (e: MouseEvent) => 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 colors: {\n positive: config.colors?.positive ?? \"rgb(0, 200, 83)\",\n negative: config.colors?.negative ?? \"rgb(255, 0, 0)\",\n marker: config.colors?.marker ?? \"#6366f1\",\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 },\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 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 hideThumbs: config.hideThumbs ?? false,\n css: config.css ?? \"\",\n captureVideo: config.captureVideo ?? false,\n videoOptions: {\n maxDuration: config.videoOptions?.maxDuration ?? 15,\n },\n recordIcon: config.recordIcon ?? \"\",\n };\n\n // Bind event handlers\n this.boundKeyDown = this.handleKeyDown.bind(this);\n this.boundMouseMove = this.handleMouseMove.bind(this);\n this.boundClick = this.handleClick.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 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\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 // 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 // Thumbs up/down buttons (unless hidden)\n 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.innerHTML = this.config.positiveIcon || THUMBS_UP_ICON;\n upBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"up\", e.currentTarget as HTMLElement, e));\n upBtn.addEventListener(\"mouseenter\", () => this.showTooltip(upBtn));\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.innerHTML = this.config.negativeIcon || THUMBS_DOWN_ICON;\n downBtn.addEventListener(\"click\", (e) => this.handleThumbClick(\"down\", e.currentTarget as HTMLElement, e));\n downBtn.addEventListener(\"mouseenter\", () => this.showTooltip(downBtn));\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 && isVideoRecordingSupported()) {\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.innerHTML = this.config.recordIcon || RECORD_ICON;\n recordBtn.addEventListener(\"click\", () => this.startRecording());\n recordBtn.addEventListener(\"mouseenter\", () => this.showTooltip(recordBtn));\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\", \"Hide Feedback\");\n this.dismissBtn.title = \"Hide Feedback\";\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: \"up\" | \"down\", 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 if (this.config.skipTargeting) {\n this.submitDirectFeedback(type, buttonEl);\n } else {\n this.startTargeting(type, e);\n }\n }\n\n private submitDirectFeedback(type: \"up\" | \"down\", 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 private startTargeting(type: \"up\" | \"down\", e: MouseEvent): void {\n this.state = \"TARGETING\";\n this.feedbackData.feedbackType = type;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n this.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.cssVars[\"--qaid-positive\"]);\n document.body.style.setProperty(\"--qaid-negative\", this.cssVars[\"--qaid-negative\"]);\n\n // Create targeting overlay (in overlay shadow host)\n this.createTargetingOverlay();\n\n // Add event listeners on document — overlay is pointer-events:none so scroll works naturally\n document.addEventListener(\"keydown\", this.boundKeyDown);\n document.addEventListener(\"mousemove\", this.boundMouseMove);\n document.addEventListener(\"click\", this.boundClick, true);\n }\n\n private createTargetingOverlay(): void {\n const root = this.ensureOverlayHost();\n\n this.overlayContainer = document.createElement(\"div\");\n this.overlayContainer.className = `qaid-targeting-overlay qaid-type-${this.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.applyVars(this.overlayContainer);\n root.appendChild(this.overlayContainer);\n }\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (e.key === \"Escape\") {\n if (this.isRecording) {\n this.stopRecording();\n } else if (this.videoPreview) {\n this.cancelRecordingPreview();\n } else if (this.state === \"TARGETING\") {\n this.cancelTargeting();\n } else if (this.state === \"MODAL_OPEN\") {\n this.closeModal();\n }\n }\n }\n\n private handleMouseMove(e: MouseEvent): void {\n this.mousePos.x = e.clientX;\n this.mousePos.y = e.clientY;\n\n // Update crosshairs and scope\n if (this.crosshairH) {\n this.crosshairH.style.top = `${e.clientY}px`;\n }\n if (this.crosshairV) {\n this.crosshairV.style.left = `${e.clientX}px`;\n }\n if (this.scope) {\n this.scope.style.left = `${e.clientX}px`;\n this.scope.style.top = `${e.clientY}px`;\n }\n\n // Find element underneath — hide both shadow hosts\n if (this.captureLayer && this.shadowHost) {\n const hosts = [this.shadowHost, this.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const elementUnder = getElementAtPointUnderOverlay(\n e.clientX,\n e.clientY,\n hosts\n );\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 {\n if (this.highlightBox) {\n this.highlightBox.style.display = \"none\";\n }\n }\n }\n }\n\n private handleClick(e: MouseEvent): void {\n e.preventDefault();\n e.stopPropagation();\n\n // Find element underneath — hide both shadow hosts\n const hosts = [this.shadowHost, this.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const target = getElementAtPointUnderOverlay(\n e.clientX,\n e.clientY,\n hosts\n );\n\n if (!target || isEmbedElement(target)) {\n return;\n }\n\n // Get element bounds\n const bounds = getElementBounds(target, 8);\n this.selectedBounds = {\n ...bounds,\n clickX: e.clientX,\n clickY: e.clientY,\n visible: true,\n };\n\n const { selector, text } = generateElementInfo(target);\n this.feedbackData.elementSelector = selector;\n this.feedbackData.elementText = text;\n\n // Remove targeting overlay and document listeners\n this.removeTargetingOverlay();\n document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\n document.removeEventListener(\"keydown\", this.boundKeyDown);\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.state = \"SELECTED\";\n\n // Show marker and submit feedback\n this.showSelectedMarker();\n this.submitFeedback();\n }\n\n private cancelTargeting(): void {\n this.removeTargetingOverlay();\n document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\n document.removeEventListener(\"keydown\", this.boundKeyDown);\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.state = \"IDLE\";\n this.feedbackData.feedbackType = null;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n this.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.ensureOverlayHost();\n\n this.marker = document.createElement(\"div\");\n this.marker.className = \"qaid-selected-marker\";\n this.marker.style.left = `${this.selectedBounds.x}px`;\n this.marker.style.top = `${this.selectedBounds.y}px`;\n this.marker.style.width = `${this.selectedBounds.width}px`;\n this.marker.style.height = `${this.selectedBounds.height}px`;\n this.marker.style.zIndex = String(this.config.zIndex + 1);\n\n this.applyVars(this.marker);\n root.appendChild(this.marker);\n }\n\n private hideSelectedMarker(): void {\n if (this.marker) {\n this.marker.remove();\n this.marker = null;\n }\n }\n\n private async submitFeedback(): Promise<void> {\n // Capture screenshot if enabled (server will gate by plan)\n let screenshot: string | null = null;\n if (this.config.captureScreenshot) {\n if (this.config.screenshotMethod === \"dom\") {\n screenshot = await captureDomScreenshot(this.config.screenshotOptions);\n } else {\n screenshot = await captureScreenshot(this.config.screenshotOptions);\n }\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 }\n } catch (error) {\n console.error(\"Failed to submit feedback:\", error);\n }\n\n // Always show modal (even if API failed - useful for demos)\n this.state = \"MODAL_OPEN\";\n\n this.showModal();\n\n // Enable pointer events on overlay host for modal\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n }\n\n private showModal(): void {\n const root = this.ensureOverlayHost();\n\n // Create backdrop\n this.backdrop = document.createElement(\"div\");\n this.backdrop.className = \"qaid-backdrop\";\n this.backdrop.style.zIndex = String(this.config.zIndex + 2);\n this.backdrop.style.background = `rgba(0, 0, 0, ${this.config.backdropOpacity})`;\n this.backdrop.addEventListener(\"click\", () => this.closeModal());\n this.applyVars(this.backdrop);\n\n if (this.isMobile) {\n this.showBottomSheet();\n } else {\n this.showPositionedModal();\n }\n\n root.appendChild(this.backdrop);\n document.addEventListener(\"keydown\", this.boundKeyDown);\n }\n\n private showBottomSheet(): void {\n const root = this.ensureOverlayHost();\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"qaid-bottom-sheet\";\n sheet.style.zIndex = String(this.config.zIndex + 3);\n\n sheet.innerHTML = `\n <div class=\"qaid-bottom-sheet-content\">\n <div class=\"qaid-bottom-sheet-handle\"></div>\n ${this.getModalContent()}\n </div>\n `;\n\n this.applyVars(sheet);\n root.appendChild(sheet);\n this.modalContainer = sheet;\n\n this.setupModalInteractions();\n }\n\n private showPositionedModal(): void {\n const root = this.ensureOverlayHost();\n\n const { modal, arrow } = calculateModalAndArrowPosition(\n this.selectedBounds,\n window.innerWidth,\n window.innerHeight,\n {\n width: this.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.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.innerHTML = this.getModalContent();\n\n this.modalContainer.appendChild(arrowEl);\n this.modalContainer.appendChild(box);\n this.applyVars(this.modalContainer);\n root.appendChild(this.modalContainer);\n\n this.setupModalInteractions();\n }\n\n private getModalContent(): string {\n const isUp = this.feedbackData.feedbackType === \"up\";\n const positiveIcon = this.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.config.negativeIcon || THUMBS_DOWN_ICON;\n const toggleClass = this.config.buttonClass\n ? `qaid-type-toggle qaid-type-toggle-custom ${this.config.buttonClass} ${isUp ? \"qaid-btn-up\" : \"qaid-btn-down\"}`\n : `qaid-type-toggle ${isUp ? \"qaid-type-up\" : \"qaid-type-down\"}`;\n return `\n <div class=\"qaid-modal-header\">\n <button type=\"button\" class=\"${toggleClass}\" title=\"Click to switch\">\n ${isUp ? positiveIcon : negativeIcon}\n </button>\n <div class=\"qaid-modal-header-text\">\n <h3 class=\"qaid-modal-title\">${this.config.text.modalTitle}</h3>\n <p class=\"qaid-modal-subtitle\">${this.config.text.modalSubtitle}</p>\n </div>\n </div>\n <textarea class=\"qaid-textarea\" placeholder=\"${this.config.text.placeholder}\"></textarea>\n <div class=\"qaid-btn-row\">\n <button type=\"button\" class=\"qaid-btn-submit\">${this.config.text.skipButton}</button>\n </div>\n `;\n }\n\n private setupModalInteractions(): void {\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() ? this.config.text.submitButton : this.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.feedbackData.feedbackType === \"up\" ? \"down\" : \"up\";\n this.feedbackData.feedbackType = newType;\n\n // Update UI - use different classes depending on custom buttonClass\n if (this.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.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.config.negativeIcon || THUMBS_DOWN_ICON;\n typeToggle.innerHTML = newType === \"up\" ? positiveIcon : negativeIcon;\n\n // Update feedback on server\n if (this.feedbackId) {\n fetch(`${this.config.endpoint}/${this.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 if (this.feedbackId) {\n try {\n await fetch(`${this.config.endpoint}/${this.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n } catch (error) {\n console.error(\"Failed to submit feedback message:\", error);\n }\n // Clear feedbackId so closeModal doesn't send another PATCH\n this.feedbackId = null;\n }\n\n this.closeModal();\n }\n\n private closeModal(): void {\n // If modal is open and we're closing without submitting, still finalize\n if (this.state === \"MODAL_OPEN\" && this.feedbackId) {\n fetch(`${this.config.endpoint}/${this.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 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\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n this.hideSelectedMarker();\n\n // Reset overlay host pointer events\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"none\";\n }\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 // ==================== Video Recording ====================\n\n private async startRecording(): Promise<void> {\n // Don't start if already recording or in targeting/modal flow\n if (this.isRecording || this.state !== \"IDLE\") return;\n\n try {\n // Start network capture\n this.networkCapture = captureNetworkErrors();\n\n // Create video recorder\n this.videoRecorder = createVideoRecorder({\n maxDuration: this.config.videoOptions.maxDuration,\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.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n this.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\n // Disable thumb buttons while recording\n this.setButtonsDisabled(true);\n\n // Show recording indicator\n this.showRecordingIndicator();\n\n // Listen for escape key\n document.addEventListener(\"keydown\", this.boundKeyDown);\n } catch (error) {\n // User denied screen share or error occurred\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.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n\n if (this.recordedBlob && this.recordedBlob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n\n private showRecordingIndicator(): void {\n const root = this.ensureOverlayHost();\n\n this.recordingIndicator = document.createElement(\"div\");\n this.recordingIndicator.className = \"qaid-recording-indicator\";\n this.recordingIndicator.style.zIndex = String(this.config.zIndex + 100);\n\n const dot = document.createElement(\"div\");\n dot.className = \"qaid-recording-dot\";\n\n const timer = document.createElement(\"span\");\n timer.className = \"qaid-recording-time\";\n timer.textContent = this.formatTime(this.config.videoOptions.maxDuration);\n\n const stopBtn = document.createElement(\"button\");\n stopBtn.type = \"button\";\n stopBtn.className = \"qaid-recording-stop\";\n stopBtn.textContent = \"Stop\";\n stopBtn.addEventListener(\"click\", () => this.stopRecording());\n\n this.recordingIndicator.appendChild(dot);\n this.recordingIndicator.appendChild(timer);\n this.recordingIndicator.appendChild(stopBtn);\n\n this.applyVars(this.recordingIndicator);\n\n // Enable pointer events on overlay host for recording indicator\n if (this.overlayShadowHost) {\n this.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.config.videoOptions.maxDuration - elapsed);\n timer.textContent = this.formatTime(remaining);\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.ensureOverlayHost();\n\n const videoUrl = URL.createObjectURL(this.recordedBlob!);\n\n this.videoPreview = document.createElement(\"div\");\n this.videoPreview.className = \"qaid-video-preview\";\n this.videoPreview.style.zIndex = String(this.config.zIndex + 100);\n\n const box = document.createElement(\"div\");\n box.className = \"qaid-video-preview-box\";\n\n const title = document.createElement(\"h3\");\n title.textContent = \"Review your recording\";\n\n const videoEl = document.createElement(\"video\");\n videoEl.src = videoUrl;\n videoEl.controls = true;\n videoEl.autoplay = true;\n videoEl.muted = true;\n\n const textarea = document.createElement(\"textarea\");\n textarea.placeholder = \"Optional: Describe the issue you recorded...\";\n\n const actions = document.createElement(\"div\");\n actions.className = \"qaid-video-preview-actions\";\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = \"qaid-video-btn qaid-video-btn-cancel\";\n cancelBtn.textContent = \"Cancel\";\n cancelBtn.addEventListener(\"click\", () => this.cancelRecordingPreview());\n\n const rerecordBtn = document.createElement(\"button\");\n rerecordBtn.type = \"button\";\n rerecordBtn.className = \"qaid-video-btn qaid-video-btn-rerecord\";\n rerecordBtn.textContent = \"Re-record\";\n rerecordBtn.addEventListener(\"click\", () => {\n this.cancelRecordingPreview();\n this.startRecording();\n });\n\n const sendBtn = document.createElement(\"button\");\n sendBtn.type = \"button\";\n sendBtn.className = \"qaid-video-btn qaid-video-btn-send\";\n sendBtn.textContent = \"Send\";\n sendBtn.addEventListener(\"click\", () => {\n const message = textarea.value.trim() || null;\n this.submitVideoFeedback(message, sendBtn);\n });\n\n actions.appendChild(cancelBtn);\n actions.appendChild(rerecordBtn);\n actions.appendChild(sendBtn);\n\n box.appendChild(title);\n box.appendChild(videoEl);\n box.appendChild(textarea);\n box.appendChild(actions);\n\n this.videoPreview.appendChild(box);\n this.applyVars(this.videoPreview);\n\n // Enable pointer events on overlay host for video preview\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n\n root.appendChild(this.videoPreview);\n\n // Listen for escape key\n document.addEventListener(\"keydown\", this.boundKeyDown);\n }\n\n private cancelRecordingPreview(): void {\n this.removeVideoPreview();\n this.cleanupRecording();\n }\n\n private removeVideoPreview(): void {\n if (this.videoPreview) {\n // Revoke object URLs\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.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.visitorId);\n\n if (this.config.apiKey) {\n formData.append(\"apiKey\", this.config.apiKey);\n }\n if (message) {\n formData.append(\"message\", message);\n }\n if (this.consoleCapture) {\n formData.append(\"consoleErrors\", JSON.stringify(this.consoleCapture.errors));\n }\n if (this.networkCapture) {\n formData.append(\"networkErrors\", JSON.stringify(this.networkCapture.errors));\n }\n\n try {\n const response = await fetch(`${this.config.endpoint}/video`, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n console.error(\"Failed to submit video feedback:\", await response.text());\n }\n } catch (error) {\n console.error(\"Failed to submit video feedback:\", error);\n }\n\n this.isSendingVideo = false;\n this.removeVideoPreview();\n this.cleanupRecording();\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 // Re-enable buttons\n this.setButtonsDisabled(false);\n\n // Reset overlay host pointer events if nothing else needs them\n if (this.overlayShadowHost && this.state === \"IDLE\") {\n this.overlayShadowHost.style.pointerEvents = \"none\";\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 // Clean up video recording\n this.cleanupRecording();\n this.removeVideoPreview();\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 document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\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.overlayContainer = null;\n this.captureLayer = null;\n this.crosshairH = null;\n this.crosshairV = null;\n this.scope = null;\n this.marker = null;\n this.modalContainer = null;\n this.backdrop = null;\n this.dismissBtn = null;\n this.tooltipElement = null;\n\n // Remove light DOM styles (reference counted)\n removeStyles();\n }\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({\n * endpoint: '/api/feedback'\n * });\n * ```\n *\n * Usage via script tag:\n * ```html\n * <script src=\"feedback.umd.cjs\" data-endpoint=\"/api/feedback\"></script>\n * ```\n *\n * Or manually initialize:\n * ```html\n * <script src=\"feedback.umd.cjs\"></script>\n * <script>\n * const embed = new QaidFeedback.QaidFeedback({\n * endpoint: '/api/feedback'\n * });\n * </script>\n * ```\n */\n\nimport { QaidFeedback } from \"./embed\";\n\nexport { QaidFeedback };\nexport type {\n FeedbackConfig,\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\";\n\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 */\nfunction 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 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 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 apiKey = script.getAttribute(\"data-api-key\");\n const captureScreenshot = script.getAttribute(\"data-capture-screenshot\");\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 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\n return {\n endpoint,\n css: cssSelector ? findCssFromSelector(cssSelector) : undefined,\n apiKey: apiKey ?? undefined,\n captureScreenshot: captureScreenshot === \"true\" ? true : 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 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) ? {\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 } : undefined,\n positiveIcon: positiveIcon ?? undefined,\n negativeIcon: negativeIcon ?? 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 ? {\n maxDuration: parseInt(videoMaxDuration, 10),\n } : undefined,\n };\n}\n\n// Auto-initialize from script tag\nif (typeof document !== \"undefined\") {\n const initFromScript = (): void => {\n const script = document.currentScript as HTMLScriptElement | null;\n\n // Try JSON config from separate script tag first, fall back to data-* attributes\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 // Run immediately if DOM is ready, otherwise wait\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", initFromScript);\n } else {\n initFromScript();\n }\n}\n"],"names":["THUMBS_UP_ICON","THUMBS_DOWN_ICON","RECORD_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","DATA_ATTRS","findDataAttribute","element","stopAt","current","attr","getTruncatedText","maxLength","textContent","getNthChildIndex","index","sibling","generateNthChildPath","path","tag","escapeCSSIdentifier","str","generateSelector","dataAttr","generateElementInfo","text","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","MAX_ENTRIES","MAX_BODY_SIZE","truncateBody","body","addEntry","entry","safeReadBody","response","captureNetworkErrors","originalFetch","input","init","url","method","requestBody","responseBody","originalXHROpen","originalXHRSend","rest","xhr","isVideoRecordingSupported","getSupportedMimeType","candidates","type","createVideoRecorder","maxDuration","videoBitsPerSecond","stream","recorder","chunks","tickCallback","stopCallback","tickInterval","startTime","resolveStop","rejectStop","stopped","autoStopTimeout","cleanup","t","finishRecording","mimeType","videoTrack","e","blob","elapsed","resolve","reject","callback","isMobileViewport","breakpoint","getElementAtPointUnderOverlay","x","y","hosts","saved","h","i","isEmbedElement","getElementBounds","padding","rect","captureScreenshot","quality","maxWidth","maxHeight","track","settings","video","checkFrame","srcWidth","srcHeight","scale","width","height","canvas","ctx","SCRIPT_URL","LOAD_TIMEOUT","POLL_INTERVAL","loadPromise","isDomScreenshotSupported","loadHtml2Canvas","script","start","poll","captureDomScreenshot","scaledWidth","scaledHeight","scaledCanvas","VISITOR_ID_KEY","HIDE_FEEDBACK_KEY","getHideKey","apiKey","isHiddenByUser","setHiddenByUser","hidden","getOrCreateVisitorId","visitorId","QaidFeedback","config","_error","newDoc","userContainer","baseStyle","themeStyle","dirClass","offsetX","offsetY","useCustomClass","btnBaseClass","tooltipText","upWrapper","upBtn","downWrapper","downBtn","recordWrapper","recordBtn","anchorRect","tooltipRect","buttonEl","root","vignette","elementUnder","target","selector","screenshot","elementBounds","payload","data","sheet","arrow","arrowEl","box","isUp","positiveIcon","negativeIcon","textarea","submitBtn","message","typeToggle","newType","err","dot","timer","stopBtn","remaining","seconds","m","s","videoUrl","title","videoEl","actions","cancelBtn","rerecordBtn","sendBtn","formData","disabled","btn","findCssFromSelector","parseJsonConfig","configScript","parsed","parseDataAttributes","endpoint","zIndex","container","buttonClass","skipTargeting","incognito","modalTitle","modalSubtitle","placeholder","submitButton","skipButton","screenshotQuality","screenshotMaxWidth","screenshotMaxHeight","captureVideo","hideThumbs","hideDismiss","videoMaxDuration","screenshotMethod","direction","cssSelector","initFromScript","jsonConfig","dataConfig"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"qaid.umd.cjs","sources":["../src/icons.ts","../src/styles.ts","../src/element-selector.ts","../src/a11y.ts","../src/modal-positioning.ts","../src/console-capture.ts","../src/network-capture.ts","../src/video-capture.ts","../src/dom-utils.ts","../src/screenshot.ts","../src/screenshot-dom.ts","../src/embed.ts","../src/index.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 * 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 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 = \"#6366f1\",\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 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 * 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 * 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 const entry = changed.pop();\n if (!entry) continue;\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 * 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 * Video capture utilities\n * Uses getDisplayMedia + MediaRecorder for screen recording\n */\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\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\n let stream: MediaStream | 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 (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\n stream = await navigator.mediaDevices.getDisplayMedia({\n video: {\n frameRate: 15,\n },\n audio: false,\n // @ts-expect-error preferCurrentTab is not in the TS types yet\n preferCurrentTab: true,\n });\n\n // Handle browser \"Stop sharing\" button\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack) {\n videoTrack.addEventListener(\"ended\", () => {\n finishRecording();\n });\n }\n\n recorder = new MediaRecorder(stream, {\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 stream tracks\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 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 * Screenshot capture utility using native Screen Capture API\n */\n\nexport interface ScreenshotOptions {\n /** Quality of WebP compression (0-1). Default: 1.0 */\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 = 1.0, 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 * 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","import type {\n FeedbackConfig,\n ResolvedFeedbackConfig,\n FeedbackData,\n SelectedBounds,\n EmbedState,\n FeedbackPayload,\n FeedbackResponse,\n} from \"./types\";\nimport { THUMBS_UP_ICON, THUMBS_DOWN_ICON, RECORD_ICON } from \"./icons\";\nimport { injectStyles, removeStyles, buildCssVars, applyCssVars, getEmbedStyles } from \"./styles\";\nimport {\n generateElementInfo,\n startKeyboardTargeting,\n type KeyboardTargetingController,\n} from \"./element-selector\";\nimport {\n announce,\n createFocusTrap,\n saveFocus,\n restoreFocus,\n applyDialog,\n setBackgroundInert,\n type FocusTrap,\n} from \"./a11y\";\nimport { calculateModalAndArrowPosition, calculateTooltipPosition } from \"./modal-positioning\";\nimport { captureConsoleErrors, type ConsoleCapture } from \"./console-capture\";\nimport { captureNetworkErrors, type NetworkCapture } from \"./network-capture\";\nimport {\n createVideoRecorder,\n isVideoRecordingSupported,\n type VideoRecorder,\n} from \"./video-capture\";\nimport {\n getElementAtPointUnderOverlay,\n isEmbedElement,\n getElementBounds,\n isMobileViewport,\n} from \"./dom-utils\";\nimport { captureScreenshot } from \"./screenshot\";\nimport { captureDomScreenshot } from \"./screenshot-dom\";\n\nconst VISITOR_ID_KEY = \"qaid_visitor_id\";\nconst HIDE_FEEDBACK_KEY = \"qaid_hide_feedback\";\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 * 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 = crypto.randomUUID();\n localStorage.setItem(VISITOR_ID_KEY, visitorId);\n }\n return visitorId;\n } catch {\n // localStorage not available, generate a session-only ID\n return crypto.randomUUID();\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 private mousePos = { x: 0, y: 0 };\n private isMobile = false;\n private visitorId: string;\n\n // Console capture\n private consoleCapture: ConsoleCapture | null = null;\n\n // Video recording\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 // 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 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 modalContainer: HTMLDivElement | null = null;\n private backdrop: HTMLDivElement | null = null;\n private dismissBtn: HTMLButtonElement | null = null;\n\n // Per-instance CSS variables\n private cssVars: Record<string, string> = {};\n\n // Keyboard element-targeting\n private keyboardController: KeyboardTargetingController | null = null;\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 boundMouseMove: (e: MouseEvent) => void;\n private boundClick: (e: MouseEvent) => 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 colors: {\n positive: config.colors?.positive ?? \"rgb(0, 200, 83)\",\n negative: config.colors?.negative ?? \"rgb(255, 0, 0)\",\n marker: config.colors?.marker ?? \"#6366f1\",\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 buttons\",\n },\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 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 hideThumbs: config.hideThumbs ?? false,\n css: config.css ?? \"\",\n captureVideo: config.captureVideo ?? false,\n videoOptions: {\n maxDuration: config.videoOptions?.maxDuration ?? 15,\n },\n recordIcon: config.recordIcon ?? \"\",\n };\n\n // Bind event handlers\n this.boundKeyDown = this.handleKeyDown.bind(this);\n this.boundMouseMove = this.handleMouseMove.bind(this);\n this.boundClick = this.handleClick.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\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 // Thumbs up/down buttons (unless hidden)\n 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\", () => this.showTooltip(upBtn));\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\", () => this.showTooltip(downBtn));\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 && isVideoRecordingSupported()) {\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\", () => this.startRecording());\n recordBtn.addEventListener(\"mouseenter\", () => this.showTooltip(recordBtn));\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: \"up\" | \"down\", 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 if (this.config.skipTargeting) {\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 if (keyboard) {\n this.startKeyboardTargetingFlow(type);\n } else {\n this.startTargeting(type, e);\n }\n }\n }\n\n private submitDirectFeedback(type: \"up\" | \"down\", 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 private startTargeting(type: \"up\" | \"down\", e: MouseEvent): void {\n this.state = \"TARGETING\";\n this.feedbackData.feedbackType = type;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n this.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.cssVars[\"--qaid-positive\"]);\n document.body.style.setProperty(\"--qaid-negative\", this.cssVars[\"--qaid-negative\"]);\n\n // Create targeting overlay (in overlay shadow host)\n this.createTargetingOverlay();\n\n // Add event listeners on document — overlay is pointer-events:none so scroll works naturally\n document.addEventListener(\"keydown\", this.boundKeyDown);\n document.addEventListener(\"mousemove\", this.boundMouseMove);\n document.addEventListener(\"click\", this.boundClick, true);\n }\n\n /**\n * Keyboard-driven targeting. Mirrors startTargeting minus the mouse\n * plumbing: no `qaid-targeting` body class (keeps the cursor visible for\n * keyboard users), no mouse reticle, and no document mouse/click listeners.\n * The KeyboardTargetingController owns Tab/Arrow/Enter/Space/Escape.\n */\n private startKeyboardTargetingFlow(type: \"up\" | \"down\"): void {\n this.state = \"TARGETING\";\n this.feedbackData.feedbackType = type;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n this.selectedBounds.visible = false;\n\n // Set overlay theming colors (NOT the cursor-hiding qaid-targeting class)\n document.body.style.setProperty(\"--qaid-positive\", this.cssVars[\"--qaid-positive\"]);\n document.body.style.setProperty(\"--qaid-negative\", this.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.announceMsg(`Targeting ${text || el.tagName.toLowerCase()}`);\n },\n onSelect: (el) => this.selectKeyboardTarget(el),\n onCancel: () => this.cancelTargeting(),\n });\n }\n\n private selectKeyboardTarget(el: Element): void {\n const bounds = getElementBounds(el, 8);\n this.selectedBounds = {\n ...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.feedbackData.elementSelector = selector;\n this.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.clearActiveThumb();\n\n this.state = \"SELECTED\";\n this.showSelectedMarker();\n this.submitFeedback();\n }\n\n private createTargetingOverlay(): void {\n const root = this.ensureOverlayHost();\n\n this.overlayContainer = document.createElement(\"div\");\n this.overlayContainer.className = `qaid-targeting-overlay qaid-type-${this.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.applyVars(this.overlayContainer);\n root.appendChild(this.overlayContainer);\n }\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (e.key === \"Escape\") {\n if (this.isRecording) {\n this.stopRecording();\n } else if (this.videoPreview) {\n this.cancelRecordingPreview();\n } else if (this.state === \"TARGETING\") {\n this.cancelTargeting();\n } else if (this.state === \"MODAL_OPEN\") {\n this.closeModal();\n }\n }\n }\n\n private handleMouseMove(e: MouseEvent): void {\n this.mousePos.x = e.clientX;\n this.mousePos.y = e.clientY;\n\n // Update crosshairs and scope\n if (this.crosshairH) {\n this.crosshairH.style.top = `${e.clientY}px`;\n }\n if (this.crosshairV) {\n this.crosshairV.style.left = `${e.clientX}px`;\n }\n if (this.scope) {\n this.scope.style.left = `${e.clientX}px`;\n this.scope.style.top = `${e.clientY}px`;\n }\n\n // Find element underneath — hide both shadow hosts\n if (this.captureLayer && this.shadowHost) {\n const hosts = [this.shadowHost, this.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const elementUnder = getElementAtPointUnderOverlay(\n e.clientX,\n e.clientY,\n hosts\n );\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 {\n if (this.highlightBox) {\n this.highlightBox.style.display = \"none\";\n }\n }\n }\n }\n\n private handleClick(e: MouseEvent): void {\n e.preventDefault();\n e.stopPropagation();\n\n // Find element underneath — hide both shadow hosts\n const hosts = [this.shadowHost, this.overlayShadowHost].filter(Boolean) as HTMLElement[];\n const target = getElementAtPointUnderOverlay(\n e.clientX,\n e.clientY,\n hosts\n );\n\n if (!target || isEmbedElement(target)) {\n return;\n }\n\n // Get element bounds\n const bounds = getElementBounds(target, 8);\n this.selectedBounds = {\n ...bounds,\n clickX: e.clientX,\n clickY: e.clientY,\n visible: true,\n };\n\n const { selector, text } = generateElementInfo(target);\n this.feedbackData.elementSelector = selector;\n this.feedbackData.elementText = text;\n\n // Remove targeting overlay and document listeners\n this.removeTargetingOverlay();\n document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\n document.removeEventListener(\"keydown\", this.boundKeyDown);\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.state = \"SELECTED\";\n this.clearActiveThumb();\n\n // Show marker and submit feedback\n this.showSelectedMarker();\n this.submitFeedback();\n }\n\n private cancelTargeting(): void {\n this.keyboardController?.stop();\n this.keyboardController = null;\n this.removeTargetingOverlay();\n document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\n document.removeEventListener(\"keydown\", this.boundKeyDown);\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.clearActiveThumb();\n\n this.state = \"IDLE\";\n this.feedbackData.feedbackType = null;\n this.feedbackData.elementSelector = null;\n this.feedbackData.elementText = null;\n this.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.ensureOverlayHost();\n\n this.marker = document.createElement(\"div\");\n this.marker.className = \"qaid-selected-marker\";\n this.marker.style.left = `${this.selectedBounds.x}px`;\n this.marker.style.top = `${this.selectedBounds.y}px`;\n this.marker.style.width = `${this.selectedBounds.width}px`;\n this.marker.style.height = `${this.selectedBounds.height}px`;\n this.marker.style.zIndex = String(this.config.zIndex + 1);\n\n this.applyVars(this.marker);\n root.appendChild(this.marker);\n }\n\n private hideSelectedMarker(): void {\n if (this.marker) {\n this.marker.remove();\n this.marker = null;\n }\n }\n\n private async submitFeedback(): Promise<void> {\n // Capture screenshot if enabled (server will gate by plan)\n let screenshot: string | null = null;\n if (this.config.captureScreenshot) {\n if (this.config.screenshotMethod === \"dom\") {\n screenshot = await captureDomScreenshot(this.config.screenshotOptions);\n } else {\n screenshot = await captureScreenshot(this.config.screenshotOptions);\n }\n if (screenshot) this.announceMsg(\"Screenshot captured\");\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 // Always show modal (even if API failed - useful for demos)\n this.state = \"MODAL_OPEN\";\n\n this.showModal();\n\n // Enable pointer events on overlay host for modal\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n }\n\n private showModal(): void {\n const root = this.ensureOverlayHost();\n\n // Create backdrop\n this.backdrop = document.createElement(\"div\");\n this.backdrop.className = \"qaid-backdrop\";\n this.backdrop.style.zIndex = String(this.config.zIndex + 2);\n this.backdrop.style.background = `rgba(0, 0, 0, ${this.config.backdropOpacity})`;\n this.backdrop.addEventListener(\"click\", () => this.closeModal());\n this.applyVars(this.backdrop);\n\n if (this.isMobile) {\n this.showBottomSheet();\n } else {\n this.showPositionedModal();\n }\n\n root.appendChild(this.backdrop);\n document.addEventListener(\"keydown\", this.boundKeyDown);\n }\n\n private showBottomSheet(): void {\n const root = this.ensureOverlayHost();\n\n const sheet = document.createElement(\"div\");\n sheet.className = \"qaid-bottom-sheet\";\n sheet.style.zIndex = String(this.config.zIndex + 3);\n\n sheet.innerHTML = `\n <div class=\"qaid-bottom-sheet-content\">\n <div class=\"qaid-bottom-sheet-handle\"></div>\n ${this.getModalContent()}\n </div>\n `;\n\n this.applyVars(sheet);\n root.appendChild(sheet);\n this.modalContainer = sheet;\n\n this.setupModalInteractions();\n }\n\n private showPositionedModal(): void {\n const root = this.ensureOverlayHost();\n\n const { modal, arrow } = calculateModalAndArrowPosition(\n this.selectedBounds,\n window.innerWidth,\n window.innerHeight,\n {\n width: this.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.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.innerHTML = this.getModalContent();\n\n this.modalContainer.appendChild(arrowEl);\n this.modalContainer.appendChild(box);\n this.applyVars(this.modalContainer);\n root.appendChild(this.modalContainer);\n\n this.setupModalInteractions();\n }\n\n private getModalContent(): string {\n const isUp = this.feedbackData.feedbackType === \"up\";\n const positiveIcon = this.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.config.negativeIcon || THUMBS_DOWN_ICON;\n const toggleClass = this.config.buttonClass\n ? `qaid-type-toggle qaid-type-toggle-custom ${this.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 return `\n <div class=\"qaid-modal-header\">\n <button type=\"button\" class=\"${toggleClass}\" title=\"Click to switch\" aria-pressed=\"${isUp}\" aria-label=\"${toggleLabel}\">\n ${isUp ? positiveIcon : negativeIcon}\n </button>\n <div class=\"qaid-modal-header-text\">\n <h3 class=\"qaid-modal-title\" id=\"qaid-modal-title-${this.uid}\">${this.config.text.modalTitle}</h3>\n <p class=\"qaid-modal-subtitle\" id=\"qaid-modal-subtitle-${this.uid}\">${this.config.text.modalSubtitle}</p>\n </div>\n </div>\n <textarea class=\"qaid-textarea\" aria-label=\"${this.config.text.modalSubtitle}\" placeholder=\"${this.config.text.placeholder}\"></textarea>\n <div class=\"qaid-btn-row\">\n <button type=\"button\" class=\"qaid-btn-submit\">${this.config.text.skipButton}</button>\n </div>\n `;\n }\n\n private setupModalInteractions(): void {\n // Dialog semantics + focus trap + background inert\n this.openDialogA11y(this.modalContainer!, {\n labelledbyId: `qaid-modal-title-${this.uid}`,\n describedbyId: `qaid-modal-subtitle-${this.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() ? this.config.text.submitButton : this.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.feedbackData.feedbackType === \"up\" ? \"down\" : \"up\";\n this.feedbackData.feedbackType = newType;\n\n // Update UI - use different classes depending on custom buttonClass\n if (this.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.config.positiveIcon || THUMBS_UP_ICON;\n const negativeIcon = this.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.announceMsg(toggleLabel);\n\n // Update feedback on server\n if (this.feedbackId) {\n fetch(`${this.config.endpoint}/${this.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 if (this.feedbackId) {\n try {\n await fetch(`${this.config.endpoint}/${this.feedbackId}`, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n } catch (error) {\n console.error(\"Failed to submit feedback message:\", error);\n }\n // Clear feedbackId so closeModal doesn't send another PATCH\n this.feedbackId = null;\n }\n\n this.closeModal();\n }\n\n private closeModal(): void {\n // If modal is open and we're closing without submitting, still finalize\n if (this.state === \"MODAL_OPEN\" && this.feedbackId) {\n fetch(`${this.config.endpoint}/${this.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 // Release focus trap / inert and restore focus before removing the DOM\n this.closeDialogA11y();\n\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\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n this.hideSelectedMarker();\n\n // Reset overlay host pointer events\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"none\";\n }\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 // ==================== Video Recording ====================\n\n private async startRecording(): Promise<void> {\n // Don't start if already recording or in targeting/modal flow\n if (this.isRecording || this.state !== \"IDLE\") return;\n\n try {\n // Start network capture\n this.networkCapture = captureNetworkErrors();\n\n // Create video recorder\n this.videoRecorder = createVideoRecorder({\n maxDuration: this.config.videoOptions.maxDuration,\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.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n this.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.announceMsg(\"Recording started\");\n\n // Disable thumb buttons while recording\n this.setButtonsDisabled(true);\n\n // Show recording indicator\n this.showRecordingIndicator();\n\n // Listen for escape key\n document.addEventListener(\"keydown\", this.boundKeyDown);\n } catch (error) {\n // User denied screen share or error occurred\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.announceMsg(\"Recording stopped\");\n this.removeRecordingIndicator();\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n\n if (this.recordedBlob && this.recordedBlob.size > 0) {\n this.showRecordingPreview();\n } else {\n this.cleanupRecording();\n }\n }\n\n private showRecordingIndicator(): void {\n const root = this.ensureOverlayHost();\n\n this.recordingIndicator = document.createElement(\"div\");\n this.recordingIndicator.className = \"qaid-recording-indicator\";\n this.recordingIndicator.style.zIndex = String(this.config.zIndex + 100);\n\n const dot = document.createElement(\"div\");\n dot.className = \"qaid-recording-dot\";\n\n const timer = document.createElement(\"span\");\n timer.className = \"qaid-recording-time\";\n timer.textContent = this.formatTime(this.config.videoOptions.maxDuration);\n\n const stopBtn = document.createElement(\"button\");\n stopBtn.type = \"button\";\n stopBtn.className = \"qaid-recording-stop\";\n stopBtn.textContent = \"Stop\";\n stopBtn.addEventListener(\"click\", () => this.stopRecording());\n\n this.recordingIndicator.appendChild(dot);\n this.recordingIndicator.appendChild(timer);\n this.recordingIndicator.appendChild(stopBtn);\n\n this.applyVars(this.recordingIndicator);\n\n // Enable pointer events on overlay host for recording indicator\n if (this.overlayShadowHost) {\n this.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.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.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.ensureOverlayHost();\n\n const videoUrl = URL.createObjectURL(this.recordedBlob!);\n\n this.videoPreview = document.createElement(\"div\");\n this.videoPreview.className = \"qaid-video-preview\";\n this.videoPreview.style.zIndex = String(this.config.zIndex + 100);\n\n const box = document.createElement(\"div\");\n box.className = \"qaid-video-preview-box\";\n\n const title = document.createElement(\"h3\");\n title.textContent = \"Review your recording\";\n title.id = `qaid-video-title-${this.uid}`;\n\n const videoEl = document.createElement(\"video\");\n videoEl.src = videoUrl;\n videoEl.controls = true;\n videoEl.autoplay = true;\n videoEl.muted = true;\n\n const textarea = document.createElement(\"textarea\");\n textarea.placeholder = \"Optional: Describe the issue you recorded...\";\n textarea.setAttribute(\"aria-label\", \"Describe the issue you recorded\");\n\n const actions = document.createElement(\"div\");\n actions.className = \"qaid-video-preview-actions\";\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = \"qaid-video-btn qaid-video-btn-cancel\";\n cancelBtn.textContent = \"Cancel\";\n cancelBtn.addEventListener(\"click\", () => this.cancelRecordingPreview());\n\n const rerecordBtn = document.createElement(\"button\");\n rerecordBtn.type = \"button\";\n rerecordBtn.className = \"qaid-video-btn qaid-video-btn-rerecord\";\n rerecordBtn.textContent = \"Re-record\";\n rerecordBtn.addEventListener(\"click\", () => {\n this.cancelRecordingPreview();\n this.startRecording();\n });\n\n const sendBtn = document.createElement(\"button\");\n sendBtn.type = \"button\";\n sendBtn.className = \"qaid-video-btn qaid-video-btn-send\";\n sendBtn.textContent = \"Send\";\n sendBtn.addEventListener(\"click\", () => {\n const message = textarea.value.trim() || null;\n this.submitVideoFeedback(message, sendBtn);\n });\n\n actions.appendChild(cancelBtn);\n actions.appendChild(rerecordBtn);\n actions.appendChild(sendBtn);\n\n box.appendChild(title);\n box.appendChild(videoEl);\n box.appendChild(textarea);\n box.appendChild(actions);\n\n this.videoPreview.appendChild(box);\n this.applyVars(this.videoPreview);\n\n // Enable pointer events on overlay host for video preview\n if (this.overlayShadowHost) {\n this.overlayShadowHost.style.pointerEvents = \"auto\";\n }\n\n root.appendChild(this.videoPreview);\n\n // Dialog semantics + focus trap + background inert\n this.openDialogA11y(box, { labelledbyId: title.id });\n\n // Listen for escape key\n document.addEventListener(\"keydown\", this.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.closeDialogA11y();\n if (this.videoPreview) {\n // Revoke object URLs\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.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.visitorId);\n\n if (this.config.apiKey) {\n formData.append(\"apiKey\", this.config.apiKey);\n }\n if (message) {\n formData.append(\"message\", message);\n }\n if (this.consoleCapture) {\n formData.append(\"consoleErrors\", JSON.stringify(this.consoleCapture.errors));\n }\n if (this.networkCapture) {\n formData.append(\"networkErrors\", JSON.stringify(this.networkCapture.errors));\n }\n\n try {\n const response = await fetch(`${this.config.endpoint}/video`, {\n method: \"POST\",\n body: formData,\n });\n\n if (response.ok) {\n this.announceMsg(\"Recording sent\");\n } else {\n console.error(\"Failed to submit video feedback:\", await response.text());\n this.announceMsg(\"Failed to send recording\", true);\n }\n } catch (error) {\n console.error(\"Failed to submit video feedback:\", error);\n this.announceMsg(\"Failed to send recording\", true);\n }\n\n this.isSendingVideo = false;\n this.removeVideoPreview();\n this.cleanupRecording();\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 // Re-enable buttons\n this.setButtonsDisabled(false);\n\n // Reset overlay host pointer events if nothing else needs them\n if (this.overlayShadowHost && this.state === \"IDLE\") {\n this.overlayShadowHost.style.pointerEvents = \"none\";\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 // Stop keyboard targeting and release any open dialog focus state\n this.keyboardController?.stop();\n this.keyboardController = null;\n this.clearActiveThumb();\n this.closeDialogA11y();\n\n // Clean up video recording\n this.cleanupRecording();\n this.removeVideoPreview();\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 document.removeEventListener(\"mousemove\", this.boundMouseMove);\n document.removeEventListener(\"click\", this.boundClick, true);\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.overlayContainer = null;\n this.captureLayer = null;\n this.crosshairH = null;\n this.crosshairV = null;\n this.scope = null;\n this.marker = null;\n this.modalContainer = null;\n this.backdrop = null;\n this.dismissBtn = null;\n this.tooltipElement = null;\n\n // Remove light DOM styles (reference counted)\n removeStyles();\n }\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({\n * endpoint: '/api/feedback'\n * });\n * ```\n *\n * Usage via script tag:\n * ```html\n * <script src=\"feedback.umd.cjs\" data-endpoint=\"/api/feedback\"></script>\n * ```\n *\n * Or manually initialize:\n * ```html\n * <script src=\"feedback.umd.cjs\"></script>\n * <script>\n * const embed = new QaidFeedback.QaidFeedback({\n * endpoint: '/api/feedback'\n * });\n * </script>\n * ```\n */\n\nimport { QaidFeedback } from \"./embed\";\n\nexport { QaidFeedback };\nexport type {\n FeedbackConfig,\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\";\n\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 */\nfunction 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 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 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 apiKey = script.getAttribute(\"data-api-key\");\n const captureScreenshot = script.getAttribute(\"data-capture-screenshot\");\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 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\n return {\n endpoint,\n css: cssSelector ? findCssFromSelector(cssSelector) : undefined,\n apiKey: apiKey ?? undefined,\n captureScreenshot: captureScreenshot === \"true\" ? true : 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 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) ? {\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 } : undefined,\n positiveIcon: positiveIcon ?? undefined,\n negativeIcon: negativeIcon ?? 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 ? {\n maxDuration: parseInt(videoMaxDuration, 10),\n } : undefined,\n };\n}\n\n// Auto-initialize from script tag\nif (typeof document !== \"undefined\") {\n const initFromScript = (): void => {\n const script = document.currentScript as HTMLScriptElement | null;\n\n // Try JSON config from separate script tag first, fall back to data-* attributes\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 // Run immediately if DOM is ready, otherwise wait\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", initFromScript);\n } else {\n initFromScript();\n }\n}\n"],"names":["THUMBS_UP_ICON","THUMBS_DOWN_ICON","RECORD_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","DATA_ATTRS","findDataAttribute","element","stopAt","current","attr","getTruncatedText","maxLength","textContent","getNthChildIndex","index","sibling","generateNthChildPath","path","tag","escapeCSSIdentifier","str","generateSelector","dataAttr","generateElementInfo","text","TARGETABLE_SELECTOR","NATIVELY_FOCUSABLE","isElementVisible","view","collectTargetableElements","root","selector","isExcluded","isVisible","startKeyboardTargeting","candidates","moveFocus","target","addedTabindex","stopped","seed","seedIndex","focusCurrent","focusable","emitHighlight","moveTo","nextIndex","len","next","prev","getCurrent","stop","handleKey","select","cancel","event","POLITE_ATTR","VISUALLY_HIDDEN_CSS","FOCUSABLE_SELECTOR","ownerDoc","getLiveRegion","assertive","kind","existing","region","announce","message","opts","isDisabled","isHidden","node","getFocusable","container","getDeepActiveElement","doc","active","createFocusTrap","onKeydown","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","MAX_ENTRIES","MAX_BODY_SIZE","truncateBody","addEntry","safeReadBody","response","captureNetworkErrors","originalFetch","input","init","url","method","requestBody","responseBody","originalXHROpen","originalXHRSend","rest","xhr","isVideoRecordingSupported","getSupportedMimeType","type","createVideoRecorder","maxDuration","videoBitsPerSecond","stream","recorder","chunks","tickCallback","stopCallback","tickInterval","startTime","resolveStop","rejectStop","autoStopTimeout","cleanup","t","finishRecording","mimeType","videoTrack","e","blob","elapsed","resolve","reject","callback","isMobileViewport","breakpoint","getElementAtPointUnderOverlay","x","y","hosts","saved","h","i","isEmbedElement","getElementBounds","padding","rect","captureScreenshot","quality","maxWidth","maxHeight","track","settings","video","checkFrame","srcWidth","srcHeight","scale","width","height","canvas","ctx","SCRIPT_URL","LOAD_TIMEOUT","POLL_INTERVAL","loadPromise","isDomScreenshotSupported","loadHtml2Canvas","script","start","poll","captureDomScreenshot","scaledWidth","scaledHeight","scaledCanvas","VISITOR_ID_KEY","HIDE_FEEDBACK_KEY","getHideKey","apiKey","isHiddenByUser","setHiddenByUser","hidden","getOrCreateVisitorId","visitorId","QaidFeedback","config","_error","newDoc","userContainer","baseStyle","themeStyle","dirClass","offsetX","offsetY","ev","useCustomClass","btnBaseClass","tooltipText","upWrapper","upBtn","downWrapper","downBtn","recordWrapper","recordBtn","anchorRect","tooltipRect","buttonEl","keyboard","vignette","elementUnder","screenshot","elementBounds","payload","data","sheet","arrow","arrowEl","box","isUp","positiveIcon","negativeIcon","textarea","submitBtn","typeToggle","newType","toggleLabel","err","dot","timer","stopBtn","remaining","seconds","m","s","videoUrl","title","videoEl","actions","cancelBtn","rerecordBtn","sendBtn","formData","disabled","btn","findCssFromSelector","parseJsonConfig","configScript","parsed","parseDataAttributes","endpoint","zIndex","buttonClass","skipTargeting","incognito","modalTitle","modalSubtitle","placeholder","submitButton","skipButton","screenshotQuality","screenshotMaxWidth","screenshotMaxHeight","captureVideo","hideThumbs","hideDismiss","videoMaxDuration","screenshotMethod","direction","cssSelector","initFromScript","jsonConfig","dataConfig"],"mappings":"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"}
|