{"version":3,"file":"makethisbetter.cjs","sources":["../src/context/dom-utils.ts","../src/widget/shadow.ts","../src/widget/tab.ts","../src/widget/frustration-prompt.ts","../src/widget/dim-overlay.ts","../src/widget/pointer-kind.ts","../src/widget/page-offset.ts","../src/widget/scroll-lock.ts","../src/annotate/toolbar.ts","../src/annotate/overlay.ts","../src/annotate/highlighter.ts","../src/screenshot/geometry.ts","../src/annotate/pin.ts","../src/styles.ts","../src/annotate/draw.ts","../src/privacy/dom.ts","../src/annotate/session.ts","../src/popup/keyboard-inset.ts","../src/popup/sheet.ts","../src/annotate/draw-note-bar.ts","../src/popup/position.ts","../src/popup/focus-trap.ts","../src/popup/chat-popup.ts","../src/popup/receipt.ts","../src/privacy/sanitize.ts","../src/privacy/fields.ts","../src/privacy/text-context.ts","../src/screenshot/image.ts","../src/screenshot/svg-path.ts","../src/screenshot/capture.ts","../src/context/error-source.ts","../src/context/error-summary.ts","../src/context/console.ts","../src/context/history-patch.ts","../src/context/breadcrumbs.ts","../src/context/frustration-state.ts","../src/context/frustration.ts","../src/api/client.ts","../src/widget/anon-id.ts","../src/i18n/en.ts","../src/i18n/zh-CN.ts","../src/i18n/ja.ts","../src/i18n/ko.ts","../src/i18n/es.ts","../src/i18n/fr.ts","../src/i18n/de.ts","../src/i18n/index.ts","../src/record/session.ts","../src/record/control-bar.ts","../src/widget/recording-manager.ts","../src/context/collector.ts","../src/widget/payload.ts","../src/widget/submission-lifecycle.ts","../src/widget/mode-states.ts","../src/widget/brand-colors.ts","../src/widget/controller.ts","../src/index.ts"],"sourcesContent":["export const WIDGET_HOST_ID = 'mtb-widget-host'\n\nexport function isWidgetElement(el: Element): boolean {\n  const host = document.getElementById(WIDGET_HOST_ID)\n  if (!host) return false\n  return host === el || host.contains(el)\n}\n\n// Skips the widget host via elementsFromPoint instead of toggling its\n// display — hiding the host on every mousemove flickered the toolbar.\nexport function elementUnderPoint(x: number, y: number): Element | null {\n  return document.elementsFromPoint?.(x, y).find(el => el.id !== WIDGET_HOST_ID) ?? null\n}\n\nexport function escapeHtml(text: string): string {\n  return text\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#039;')\n}\n\n// A keydown fired mid-IME-composition confirms a candidate, not the widget\n// action bound to the key — acting on it would eat what a reporter typing\n// Chinese or Japanese meant as text. Some browsers reset isComposing before\n// the keydown reaches a capture handler but retain the legacy IME sentinel\n// in keyCode.\nexport function isImeConfirmKeydown(event: KeyboardEvent): boolean {\n  return event.isComposing || event.keyCode === 229\n}\n\n// textContent counts the source of <style> and <script> children as text. A\n// page with an inline stylesheet in its header therefore names that header\n// after its own CSS, and that name travels into the pin, the report, and the\n// triage prompt. Only what a reader would see counts.\nconst UNRENDERED = /^(script|style|noscript|template)$/i\n\nfunction renderedText(el: Element, limit = 80): string {\n  let out = ''\n  for (const node of Array.from(el.childNodes)) {\n    if (out.length >= limit) break\n\n    if (node.nodeType === 3) {\n      out += node.nodeValue ?? ''\n    } else if (node instanceof Element && !UNRENDERED.test(node.tagName)) {\n      out += renderedText(node, limit - out.length)\n    }\n  }\n  return out\n}\n\nexport function getElementName(el: Element): string {\n  if (el instanceof HTMLElement) {\n    const label = el.getAttribute('aria-label')\n    if (label) return label\n\n    const title = el.getAttribute('title')\n    if (title) return title\n\n    const text = renderedText(el).trim().slice(0, 40)\n    if (text) return text\n\n    const placeholder = (el as HTMLInputElement).placeholder\n    if (placeholder) return placeholder\n\n    const alt = (el as HTMLImageElement).alt\n    if (alt) return alt\n\n    const dataMark = el.getAttribute('data-mark')\n    if (dataMark) return dataMark\n  }\n  return el.tagName.toLowerCase()\n}\n\nexport function getCssSelector(el: Element): string {\n  const parts: string[] = []\n  let current: Element | null = el\n  while (current && current !== document.body && parts.length < 5) {\n    let selector = current.tagName.toLowerCase()\n    if (current.id) {\n      selector = `#${current.id}`\n      parts.unshift(selector)\n      break\n    }\n    const classNames = Array.from(current.classList)\n      .filter(c => !c.startsWith('mtb-'))\n      .slice(0, 2)\n      .join('.')\n    if (classNames) selector += `.${classNames}`\n\n    const parent = current.parentElement\n    if (parent) {\n      const siblings = Array.from(parent.children).filter(\n        c => c.tagName === current!.tagName,\n      )\n      if (siblings.length > 1) {\n        const idx = siblings.indexOf(current) + 1\n        selector += `:nth-of-type(${idx})`\n      }\n    }\n\n    parts.unshift(selector)\n    current = current.parentElement\n  }\n  return parts.join(' > ')\n}\n","import cssText from '../styles/widget.css?inline'\nimport { WIDGET_HOST_ID } from '../context/dom-utils'\nimport type { MakeThisBetterConfig } from '../types'\nimport type { WidgetBrandColors } from './brand-colors'\n\nexport class ShadowContainer {\n  // Click-away layers listen at different points in a mouse or touch sequence.\n  // Keep discrete widget interactions private, but leave pointermove composed\n  // because toolbar dragging intentionally tracks movement on document.\n  private static readonly HOST_INTERACTION_EVENTS = [\n    'pointerdown',\n    'pointerup',\n    'pointercancel',\n    'mousedown',\n    'mouseup',\n    'touchstart',\n    'touchend',\n    'touchcancel',\n    'click',\n  ] as const\n\n  // Readable so a component can mark the host with its own presence and have\n  // :host(...) rules react — the only way CSS inside the shadow tree can be\n  // told which components are currently mounted.\n  readonly host: HTMLElement\n  readonly root: ShadowRoot\n  // Only a host we made is ours to take away. One the page supplied has to\n  // outlive us, or a framework counting on it being there — Turbo pairs its\n  // permanent elements by id — would find it gone after the first teardown.\n  private readonly ownsHost: boolean\n  // A borrowed host's inline style and theme attribute belong to the page.\n  // Snapshot the whole attribute values, not just the properties we write:\n  // cssText assignment below replaces everything the customer had inline, so\n  // only the full original string can put it back. null means the attribute\n  // did not exist, and restoring must remove it rather than leave ''.\n  private readonly originalInlineStyle: string | null\n  private readonly originalTheme: string | null\n\n  constructor(\n    theme: MakeThisBetterConfig['theme'] = 'auto',\n    brandColors?: WidgetBrandColors,\n  ) {\n    // A page that renders its own host controls where the widget lives, and\n    // that is the only way to survive a framework which replaces <body>\n    // wholesale on navigation: Turbo carries an element marked\n    // data-turbo-permanent across the swap, shadow root and all, so a\n    // recording in progress is not thrown away by a link click.\n    const provided = document.getElementById(WIDGET_HOST_ID)\n    this.ownsHost = !provided\n\n    this.host = provided ?? document.createElement('div')\n    // Attach before any host mutation: a supplied element that cannot take a\n    // shadow root fails right here, while the page's element is still exactly\n    // as the page left it — there is no partially-styled host to unwind.\n    this.root = this.host.shadowRoot ?? this.host.attachShadow({ mode: 'open' })\n    this.root.replaceChildren()\n    this.originalInlineStyle = this.host.getAttribute('style')\n    this.originalTheme = this.host.getAttribute('data-mtb-theme')\n    this.host.id = WIDGET_HOST_ID\n    this.host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;overflow:visible;z-index:2147483646;'\n    if (brandColors) {\n      const rgb = [1, 3, 5].map((offset) => parseInt(brandColors.primary.slice(offset, offset + 2), 16))\n      const onPrimaryRgb = [1, 3, 5].map((offset) => parseInt(brandColors.onPrimary.slice(offset, offset + 2), 16))\n      this.host.style.setProperty('--mtb-brand-primary', brandColors.primary.toLowerCase())\n      this.host.style.setProperty('--mtb-brand-hover', brandColors.hover.toLowerCase())\n      this.host.style.setProperty('--mtb-brand-active', brandColors.active.toLowerCase())\n      this.host.style.setProperty('--mtb-brand-on-primary', brandColors.onPrimary.toLowerCase())\n      this.host.style.setProperty('--mtb-brand-rgb', rgb.join(', '))\n      this.host.style.setProperty('--mtb-brand-on-primary-rgb', onPrimaryRgb.join(', '))\n      this.host.style.setProperty('--mtb-brand-emphasis', brandColors.primary.toLowerCase())\n      this.host.style.setProperty('--mtb-brand-emphasis-rgb', rgb.join(', '))\n      this.host.style.setProperty('--mtb-brand-gradient-start', brandColors.primary.toLowerCase())\n      this.host.style.setProperty('--mtb-brand-gradient-end', brandColors.hover.toLowerCase())\n    }\n    this.host.setAttribute('data-mtb-theme', theme)\n    if (this.ownsHost) document.body.appendChild(this.host)\n\n    for (const eventType of ShadowContainer.HOST_INTERACTION_EVENTS) {\n      this.root.addEventListener(eventType, this.stopHostInteraction)\n    }\n\n    const style = document.createElement('style')\n    style.textContent = cssText\n    this.root.appendChild(style)\n  }\n\n  el<T extends HTMLElement = HTMLElement>(tag: string, className?: string): T {\n    const el = document.createElement(tag) as T\n    if (className) el.className = className\n    return el\n  }\n\n  append(...nodes: Node[]): void {\n    for (const n of nodes) this.root.appendChild(n)\n  }\n\n  remove(...nodes: Node[]): void {\n    for (const n of nodes) {\n      if (n.parentNode === this.root) this.root.removeChild(n)\n    }\n  }\n\n  private stopHostInteraction = (event: Event): void => {\n    event.stopPropagation()\n  }\n\n  destroy(): void {\n    for (const eventType of ShadowContainer.HOST_INTERACTION_EVENTS) {\n      this.root.removeEventListener(eventType, this.stopHostInteraction)\n    }\n\n    if (!this.ownsHost) {\n      // Borrowed. Empty it and leave it standing for whoever put it there.\n      this.root.replaceChildren()\n      this.host.removeAttribute('data-mtb-toolbar')\n      if (this.originalInlineStyle === null) this.host.removeAttribute('style')\n      else this.host.setAttribute('style', this.originalInlineStyle)\n      if (this.originalTheme === null) this.host.removeAttribute('data-mtb-theme')\n      else this.host.setAttribute('data-mtb-theme', this.originalTheme)\n      return\n    }\n\n    // host.remove() is a safe no-op if the host is already detached — e.g.\n    // when Turbo swapped document.body on navigation. removeChild(host) would\n    // throw NotFoundError there, aborting re-init and dropping the widget.\n    this.host.remove()\n  }\n}\n","import type { ShadowContainer } from './shadow'\nimport type { I18nMessages } from '../i18n'\n\nexport class FeedbackTab {\n  private el: HTMLButtonElement\n  private onClick: () => void\n  private idleLabel: string\n  private activeLabel: string\n  private customText?: string\n  private active = false\n\n  constructor(\n    shadow: ShadowContainer,\n    messages: I18nMessages,\n    position: 'left' | 'right',\n    onClick: () => void,\n    customText?: string,\n  ) {\n    this.onClick = onClick\n    this.customText = customText\n    this.el = shadow.el<HTMLButtonElement>('button', 'mtb-tab')\n    if (position === 'left') this.el.classList.add('left')\n    this.idleLabel = customText ?? messages.tab\n    this.activeLabel = messages.toolbar.exit\n    this.setLabel(this.idleLabel)\n    this.el.addEventListener('mousedown', this.preserveHostFocus)\n    this.el.addEventListener('click', onClick)\n    shadow.append(this.el)\n  }\n\n  private preserveHostFocus = (event: MouseEvent): void => {\n    event.preventDefault()\n  }\n\n  private setLabel(label: string): void {\n    this.el.textContent = label\n    this.el.setAttribute('aria-label', label)\n  }\n\n  setMessages(messages: I18nMessages): void {\n    this.idleLabel = this.customText ?? messages.tab\n    this.activeLabel = messages.toolbar.exit\n    this.setLabel(this.active ? this.activeLabel : this.idleLabel)\n  }\n\n  setActive(active: boolean): void {\n    this.active = active\n    this.el.classList.toggle('active', active)\n    this.setLabel(active ? this.activeLabel : this.idleLabel)\n  }\n\n  destroy(): void {\n    this.el.removeEventListener('mousedown', this.preserveHostFocus)\n    this.el.removeEventListener('click', this.onClick)\n    this.el.remove()\n  }\n}\n","import type { ShadowContainer } from './shadow'\nimport type { I18nMessages } from '../i18n'\nimport { escapeHtml } from '../context/dom-utils'\n\nconst FRUSTRATION_DISMISS_MS = 8000\n\nexport interface FrustrationPromptHandlers {\n  onTell: () => void\n  // The reporter clicked Dismiss — a deliberate refusal.\n  onDismiss: () => void\n  // Nobody touched the card and it faded out on its own. Kept separate from\n  // onDismiss because \"did not notice\" is not \"said no\": treating the two the\n  // same suppressed every later prompt for the session.\n  onAutoHide: () => void\n}\n\nexport class FrustrationPromptCard {\n  private el: HTMLDivElement\n  private autoHideTimer: ReturnType<typeof setTimeout> | null = null\n\n  constructor(\n    shadow: ShadowContainer,\n    messages: I18nMessages,\n    handlers: FrustrationPromptHandlers,\n  ) {\n    this.el = shadow.el<HTMLDivElement>('div', 'mtb-frustration-prompt')\n    this.el.innerHTML = `\n      <div class=\"mtb-frustration-icon\">&#9888;&#65039;</div>\n      <span class=\"mtb-frustration-text\">${escapeHtml(messages.frustration.prompt)}</span>\n      <div class=\"mtb-frustration-actions\">\n        <button class=\"mtb-frustration-tell\">${escapeHtml(messages.frustration.action)}</button>\n        <button class=\"mtb-frustration-dismiss\">${escapeHtml(messages.frustration.dismiss)}</button>\n      </div>\n    `\n\n    this.el.querySelector('.mtb-frustration-tell')!.addEventListener('click', handlers.onTell)\n    this.el.querySelector('.mtb-frustration-dismiss')!.addEventListener('click', handlers.onDismiss)\n\n    shadow.append(this.el)\n    this.autoHideTimer = setTimeout(handlers.onAutoHide, FRUSTRATION_DISMISS_MS)\n  }\n\n  destroy(): void {\n    // Clearing matters on the engagement path too: without it, a card destroyed\n    // by \"Tell us\" still fired its auto-hide 8s later.\n    if (this.autoHideTimer) {\n      clearTimeout(this.autoHideTimer)\n      this.autoHideTimer = null\n    }\n    this.el.remove()\n  }\n}\n","import type { ShadowContainer } from './shadow'\n\n/**\n * A touch browser replays the tap that opened the popup as a compatibility\n * click a moment after pointerup — and by then this scrim exists underneath,\n * so that click dismissed the popup the same gesture had just opened. On a\n * phone it read as the sheet flashing and vanishing, or as tapping doing\n * nothing at all. Cancelling pointerdown does not prevent it: WebKit still\n * dispatches the click (observed on iOS 18.7).\n *\n * So the scrim ignores anything that arrives before it could plausibly be a\n * new gesture. A deliberate dismissal is a separate press, well past this.\n */\nconst SPAWN_CLICK_GRACE_MS = 400\n\n// Full-screen scrim shown behind the comment popup. Clicking it resets the\n// widget, mirroring the design's dim-to-dismiss behaviour.\nexport class DimOverlay {\n  private el: HTMLDivElement\n  private bornAt = Date.now()\n  private onClick: (e: Event) => void\n\n  constructor(shadow: ShadowContainer, onDismiss: () => void, now: () => number = () => Date.now()) {\n    this.bornAt = now()\n    this.onClick = () => {\n      if (now() - this.bornAt < SPAWN_CLICK_GRACE_MS) return\n      onDismiss()\n    }\n    this.el = shadow.el<HTMLDivElement>('div', 'mtb-dim mtb-dim-clickable')\n    this.el.addEventListener('click', this.onClick)\n    shadow.append(this.el)\n  }\n\n  destroy(): void {\n    this.el.removeEventListener('click', this.onClick)\n    this.el.remove()\n  }\n}\n","/**\n * Whether a finger is driving, rather than a mouse.\n *\n * Several behaviours fork on this — freezing the host page, which input\n * surface collects a note — and they have to agree, so the question is asked\n * in exactly one place.\n *\n * No matchMedia means no evidence of a touch device: assume a mouse and leave\n * the desktop path alone, which is the one that already worked.\n */\nexport function isTouchPointer(): boolean {\n  return window.matchMedia?.('(pointer: coarse)').matches ?? false\n}\n","/**\n * Reserves room at the top of the host page for the annotation toolbar, so the\n * bar occupies space of its own instead of sitting on the customer's content.\n *\n * Without this the bar is roughly 100px of dead zone on a phone — 44px plus\n * the notch inset — covering exactly the strip most sites put their header in,\n * and taps there do nothing at all with no explanation. Measured on a device:\n * everything below responded, the whole top band did not.\n *\n * Known limit, and it is not small: `position: fixed` and `sticky` elements do\n * not move with padding. A site whose header is fixed keeps it where it is,\n * still underneath the bar. This buys back normal-flow content, which is most\n * of the page, and nothing else.\n *\n * The host page is not ours, so the previous inline value is put back verbatim\n * — writing '' would delete a padding the page had set for its own reasons.\n */\nexport class PageOffset {\n  private applied = false\n  private previousPadding = ''\n  private previousTransition = ''\n\n  reserve(px: number): void {\n    if (this.applied || px <= 0) return\n    this.applied = true\n\n    const style = document.body.style\n    this.previousPadding = style.paddingTop\n    this.previousTransition = style.transition\n\n    const current = parseFloat(getComputedStyle(document.body).paddingTop) || 0\n    // Suppress any transition the page has on padding: the bar appears at once,\n    // and a site animating its own layout in response looks like a glitch.\n    style.transition = 'none'\n    style.paddingTop = `${current + px}px`\n  }\n\n  release(): void {\n    if (!this.applied) return\n    this.applied = false\n\n    const style = document.body.style\n    style.paddingTop = this.previousPadding\n    style.transition = this.previousTransition\n  }\n\n  isApplied(): boolean {\n    return this.applied\n  }\n}\n","/**\n * Holds the host page still while strokes or a note card are on screen.\n *\n * Annotations are positioned in viewport coordinates; scrolling moves the page\n * out from under them. The lock engages when drawing starts or a note card\n * opens, and releases when the surface tears down or the flow ends (exitAll).\n *\n * Nothing on the host page is mutated. Cancelling the gestures that scroll\n * leaves the page's own styles untouched, so there is nothing to restore and\n * nothing to strand if teardown is ever missed.\n */\nexport interface ScrollLock {\n  /** Let the page scroll again. Safe to call more than once. */\n  release(): void\n}\n\nconst POINTER_GESTURES = ['wheel', 'touchmove'] as const\n\nconst SCROLL_KEYS = new Set([\n  'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',\n  'PageUp', 'PageDown', 'Home', 'End', ' ',\n])\n\nfunction scrollsInternally(event: Event): boolean {\n  for (const node of event.composedPath()) {\n    if (!(node instanceof Element)) continue\n    const style = getComputedStyle(node)\n    const scrollable = /auto|scroll/.test(`${style.overflowY} ${style.overflowX}`)\n    if (!scrollable) continue\n    if (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth) return true\n  }\n  return false\n}\n\nfunction consumesScrollKey(el: Element, key: string): boolean {\n  const tag = el.tagName\n  if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true\n  if (el instanceof HTMLElement && el.isContentEditable) return true\n  if (key === ' ' && (tag === 'BUTTON' || tag === 'SUMMARY')) return true\n  return false\n}\n\nexport function lockPageScroll(): ScrollLock {\n  const blockGesture = (event: Event): void => {\n    if (scrollsInternally(event)) return\n    if (event.cancelable) event.preventDefault()\n  }\n\n  const blockKey = (event: Event): void => {\n    if (!(event instanceof KeyboardEvent)) return\n    if (!SCROLL_KEYS.has(event.key)) return\n    const origin = event.composedPath()[0]\n    if (origin instanceof Element && consumesScrollKey(origin, event.key)) return\n    if (event.cancelable) event.preventDefault()\n  }\n\n  for (const gesture of POINTER_GESTURES) {\n    window.addEventListener(gesture, blockGesture, { capture: true, passive: false })\n  }\n  window.addEventListener('keydown', blockKey, { capture: true })\n\n  return {\n    release(): void {\n      for (const gesture of POINTER_GESTURES) {\n        window.removeEventListener(gesture, blockGesture, { capture: true })\n      }\n      window.removeEventListener('keydown', blockKey, { capture: true })\n    },\n  }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { isTouchPointer } from '../widget/pointer-kind'\nimport { escapeHtml } from '../context/dom-utils'\n\nexport type ToolbarMode = 'markup' | 'record'\n\n// Mirrors the `height` the max-width: 480px block gives .mtb-toolbar. The\n// safe-area inset stacked on top of it can only be measured, not predicted.\nconst MOBILE_BAR_HEIGHT_PX = 44\n\nexport class AnnotationToolbar {\n  private el: HTMLDivElement\n  // The capsule, which is what gets measured and dimmed. hintTextEl is only the\n  // words inside it — dimming that instead would leave the capsule around them\n  // at full strength, and shrink the region counted as \"under the pointer\".\n  private hintBarEl: HTMLDivElement\n  private hintTextEl!: HTMLSpanElement\n  private host: HTMLElement\n  private markupBtn!: HTMLButtonElement\n  private recordBtn!: HTMLButtonElement\n  private messages: I18nMessages\n  private currentMode: ToolbarMode = 'markup'\n  private readonly touch: boolean\n\n  // The hint sits over the page and carries nothing to click, so it steps back\n  // while the pointer is on top of it and comes forward again the moment the\n  // pointer leaves. Nothing else about the page's movement affects it.\n  //\n  // This is a hover affordance, and touch has no hover — but a tap still emits\n  // a compatibility move wherever the finger landed, which would dim the hint\n  // for no reason. Restricting to a real mouse makes it a no-op on a phone.\n  private handlePointerMove = (e: PointerEvent): void => {\n    if (e.pointerType !== 'mouse') return\n\n    const rect = this.hintBarEl.getBoundingClientRect()\n    const inside =\n      e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom\n\n    this.hintBarEl.classList.toggle('mtb-hint-bar--under-pointer', inside)\n  }\n\n  // Tracks whether the touch-hint dismiss listener is currently attached, so\n  // add and remove stay symmetric: it is only ever added on touch with the\n  // hint showing, and destroy() must not issue a remove for an add that never\n  // happened.\n  private touchHintDismissListening = false\n\n  private dismissTouchHint = (): void => {\n    this.hintBarEl.classList.add('mtb-hint-bar--dismissed')\n    this.stopTouchHintDismissListening()\n  }\n\n  private stopTouchHintDismissListening(): void {\n    if (!this.touchHintDismissListening) return\n    this.touchHintDismissListening = false\n    document.removeEventListener('pointerdown', this.dismissTouchHint, true)\n  }\n\n  constructor(\n    shadow: ShadowContainer,\n    messages: I18nMessages,\n    onExit: () => void,\n    onModeChange?: (mode: ToolbarMode) => void,\n    position: 'left' | 'right' = 'right',\n    showTouchHint = true,\n  ) {\n    this.messages = messages\n    this.touch = isTouchPointer()\n\n    // Two siblings, not one element. A fixed-position child is laid out against\n    // its nearest transformed ancestor rather than the viewport, and the bar is\n    // transformed both at rest and by its entry animation — so a hint nested\n    // inside it could never be centred on the page.\n    this.hintBarEl = shadow.el<HTMLDivElement>('div', 'mtb-hint-bar')\n    // \"Click to comment\" is a lie on a phone, where a tap is deliberately inert\n    // — the hint has to describe the one gesture that does anything.\n    this.hintBarEl.innerHTML = `<span class=\"mtb-toolbar-hint\">${escapeHtml(this.idleHint())}</span>`\n    if (this.touch && !showTouchHint) this.hintBarEl.classList.add('mtb-hint-bar--dismissed')\n\n    this.el = shadow.el<HTMLDivElement>('div', 'mtb-toolbar')\n    if (position === 'left') this.el.classList.add('left')\n    this.el.innerHTML = this.buildHTML(messages)\n    shadow.append(this.hintBarEl, this.el)\n\n    // Lets the CSS retire the tab for exactly as long as this bar is up. Keyed\n    // to the bar's own lifetime rather than to the tab's active state: the tab\n    // is still active while a clarification card is open, and by then the bar\n    // is gone — hiding it there would leave no way out at all.\n    this.host = shadow.host\n    this.host.setAttribute('data-mtb-toolbar', '')\n\n    this.hintTextEl = this.hintBarEl.querySelector('.mtb-toolbar-hint')!\n    this.markupBtn = this.el.querySelector('.mtb-toolbar-mode-markup')!\n    this.recordBtn = this.el.querySelector('.mtb-toolbar-mode-record')!\n\n    this.markupBtn.addEventListener('click', () => {\n      this.setMode('markup')\n      onModeChange?.('markup')\n    })\n    this.recordBtn.addEventListener('click', () => {\n      this.setMode('record')\n      onModeChange?.('record')\n    })\n    this.el.querySelector('.mtb-exit-btn')!.addEventListener('click', onExit)\n\n    document.addEventListener('pointermove', this.handlePointerMove as EventListener)\n    if (this.touch && showTouchHint) {\n      document.addEventListener('pointerdown', this.dismissTouchHint, true)\n      this.touchHintDismissListening = true\n    }\n  }\n\n  private buildHTML(m: I18nMessages): string {\n    return `\n      <div class=\"mtb-toolbar-modes\">\n        <button class=\"mtb-toolbar-mode-btn mtb-toolbar-mode-markup active\" aria-label=\"${escapeHtml(m.toolbar.markup)}\" title=\"${escapeHtml(m.toolbar.markup)}\">\n          <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"7\"/><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"21\"/><line x1=\"3\" y1=\"12\" x2=\"7\" y2=\"12\"/><line x1=\"17\" y1=\"12\" x2=\"21\" y2=\"12\"/></svg><span class=\"mtb-toolbar-label\">${escapeHtml(m.toolbar.markup)}</span>\n        </button>\n        <button class=\"mtb-toolbar-mode-btn mtb-toolbar-mode-record\" aria-label=\"${escapeHtml(m.toolbar.record)}\" title=\"${escapeHtml(m.toolbar.record)}\">\n          <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"6\" width=\"13\" height=\"12\" rx=\"2.5\"/><path d=\"M15 10.5 21.2 7.4a.6.6 0 0 1 .8.5v8.2a.6.6 0 0 1-.8.5L15 13.5z\"/></svg><span class=\"mtb-toolbar-label\">${escapeHtml(m.toolbar.record)}</span>\n        </button>\n      </div>\n      <span class=\"mtb-toolbar-sep\"></span>\n      <button class=\"mtb-exit-btn\" aria-label=\"${escapeHtml(m.toolbar.exit)}\" title=\"${escapeHtml(m.toolbar.exit)}\">\n        <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n          <path d=\"M18 6L6 18M6 6l12 12\"/>\n        </svg><span class=\"mtb-toolbar-label\">${escapeHtml(m.toolbar.exit)}</span>\n      </button>\n    `\n  }\n\n  private idleHint(): string {\n    return this.touch ? this.messages.toolbar.hintTouch : this.messages.toolbar.hint\n  }\n\n  setMode(mode: ToolbarMode): void {\n    this.currentMode = mode\n    if (mode === 'markup') {\n      this.markupBtn.classList.add('active')\n      this.recordBtn.classList.remove('active')\n      this.hintTextEl.textContent = this.idleHint()\n    } else {\n      this.markupBtn.classList.remove('active')\n      this.recordBtn.classList.add('active')\n      this.hintTextEl.textContent = this.messages.toolbar.hintRecord\n    }\n  }\n\n  getMode(): ToolbarMode {\n    return this.currentMode\n  }\n\n  /**\n   * How much vertical room the bar occupies, including any safe-area inset.\n   *\n   * Falls back to the height the stylesheet declares when measurement returns\n   * nothing — which happens before first paint, and in any environment without\n   * layout. Reserving the wrong amount is recoverable; reserving none means\n   * the bar covers the page again, which is the defect this exists to fix.\n   */\n  height(): number {\n    return this.el.getBoundingClientRect().height || MOBILE_BAR_HEIGHT_PX\n  }\n\n  destroy(): void {\n    document.removeEventListener('pointermove', this.handlePointerMove as EventListener)\n    this.stopTouchHintDismissListening()\n    this.host.removeAttribute('data-mtb-toolbar')\n    this.hintBarEl.remove()\n    this.el.remove()\n  }\n}\n","import type { ShadowContainer } from '../widget/shadow'\n\nexport interface OverlayHandlers {\n  onPointerDown: (e: PointerEvent) => void\n  onPointerMove: (e: PointerEvent) => void\n  onPointerUp: (e: PointerEvent) => void\n  onPointerCancel: (e: PointerEvent) => void\n}\n\n/**\n * The annotation surface. Runs on pointer events only — never mouse events.\n *\n * Mouse listeners are deliberately absent. Touch browsers replay a tap as\n * pointerdown/up followed by compatibility mousedown/up/click, so listening to\n * both would run the annotation logic twice for one tap and drop two pins.\n * Pointer events already cover mouse and stylus, so the desktop loses nothing.\n */\nexport class AnnotationOverlay {\n  private overlayEl: HTMLDivElement\n  private dimEl: HTMLDivElement\n  private handlers: OverlayHandlers\n  private onPointerDown: (e: Event) => void\n  private onPointerMove: (e: Event) => void\n  private onPointerUp: (e: Event) => void\n  private onPointerCancel: (e: Event) => void\n\n  constructor(shadow: ShadowContainer, handlers: OverlayHandlers) {\n    this.handlers = handlers\n\n    this.overlayEl = shadow.el<HTMLDivElement>('div', 'mtb-overlay')\n\n    this.onPointerDown = (e) => {\n      const pe = e as PointerEvent\n      if (!pe.isPrimary) return\n      // Capture keeps a stroke alive when the finger crosses the overlay edge.\n      // Without it, a drag that leaves the surface silently stops mid-line.\n      try {\n        this.overlayEl.setPointerCapture(pe.pointerId)\n      } catch {\n        // Thrown when the pointer is already gone. The stroke can still run\n        // uncaptured, so this is not worth aborting the gesture over.\n      }\n      this.handlers.onPointerDown(pe)\n    }\n\n    this.onPointerMove = (e) => {\n      const pe = e as PointerEvent\n      if (!pe.isPrimary) return\n      this.handlers.onPointerMove(pe)\n    }\n\n    this.onPointerUp = (e) => {\n      const pe = e as PointerEvent\n      if (!pe.isPrimary) return\n      this.releaseCapture(pe.pointerId)\n      this.handlers.onPointerUp(pe)\n    }\n\n    // Replaces the old mouseleave path. The browser fires this when it takes\n    // the gesture away from us — a system gesture, or the page going\n    // background — which is exactly when a half-drawn stroke should end.\n    this.onPointerCancel = (e) => {\n      const pe = e as PointerEvent\n      // A palm or second finger can be cancelled on its own. That pointer never\n      // reached the handlers, so acting on its cancel would cut short a stroke\n      // the primary finger is still drawing.\n      if (!pe.isPrimary) return\n      this.releaseCapture(pe.pointerId)\n      this.handlers.onPointerCancel(pe)\n    }\n\n    this.overlayEl.addEventListener('pointerdown', this.onPointerDown)\n    this.overlayEl.addEventListener('pointermove', this.onPointerMove)\n    this.overlayEl.addEventListener('pointerup', this.onPointerUp)\n    this.overlayEl.addEventListener('pointercancel', this.onPointerCancel)\n    shadow.append(this.overlayEl)\n\n    this.dimEl = shadow.el<HTMLDivElement>('div', 'mtb-dim')\n    shadow.append(this.dimEl)\n  }\n\n  private releaseCapture(pointerId: number): void {\n    try {\n      this.overlayEl.releasePointerCapture(pointerId)\n    } catch {\n      // Already released, or the pointer is gone. Nothing to undo.\n    }\n  }\n\n  destroy(): void {\n    this.overlayEl.removeEventListener('pointerdown', this.onPointerDown)\n    this.overlayEl.removeEventListener('pointermove', this.onPointerMove)\n    this.overlayEl.removeEventListener('pointerup', this.onPointerUp)\n    this.overlayEl.removeEventListener('pointercancel', this.onPointerCancel)\n    this.overlayEl.remove()\n    this.dimEl.remove()\n  }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport { getElementName } from '../context/dom-utils'\n\nexport class ElementHighlighter {\n  private highlightEl: HTMLDivElement\n  private tooltipEl: HTMLDivElement\n\n  constructor(shadow: ShadowContainer) {\n    this.highlightEl = shadow.el<HTMLDivElement>('div', 'mtb-highlight')\n    this.tooltipEl = shadow.el<HTMLDivElement>('div', 'mtb-highlight-tooltip')\n    this.hide()\n    shadow.append(this.highlightEl, this.tooltipEl)\n  }\n\n  highlight(el: Element): void {\n    const rect = el.getBoundingClientRect()\n    this.highlightEl.style.top = `${rect.top - 2}px`\n    this.highlightEl.style.left = `${rect.left - 2}px`\n    this.highlightEl.style.width = `${rect.width + 4}px`\n    this.highlightEl.style.height = `${rect.height + 4}px`\n    this.highlightEl.style.display = 'block'\n    this.showTooltip(el, rect)\n  }\n\n  private showTooltip(el: Element, rect: DOMRect): void {\n    this.tooltipEl.textContent = getElementName(el)\n    const below = rect.top < 30\n    this.tooltipEl.classList.toggle('below', below)\n    this.tooltipEl.style.left = `${rect.left + rect.width / 2}px`\n    this.tooltipEl.style.top = below ? `${rect.bottom + 8}px` : `${rect.top - 8}px`\n    this.tooltipEl.style.display = 'block'\n  }\n\n  select(): void {\n    this.highlightEl.classList.add('selected')\n    this.tooltipEl.style.display = 'none'\n  }\n\n  hide(): void {\n    this.highlightEl.style.display = 'none'\n    this.highlightEl.classList.remove('selected')\n    this.tooltipEl.style.display = 'none'\n  }\n\n  destroy(): void {\n    this.highlightEl.remove()\n    this.tooltipEl.remove()\n  }\n}\n","// The capture pipeline works in clone-space: viewport rects corrected for\n// nested-scroll drift, i.e. where html-to-image renders each element in the\n// captured bitmap. Capture metrics and privacy covers are the same kind of\n// rectangle in that one space, so they share this spelling. Crop rectangles\n// (viewport / canvas pixels) stay a separate type on purpose — the space\n// difference is information.\nexport interface Rect {\n  left: number\n  top: number\n  width: number\n  height: number\n}\n\n// Owns the offset+scale math from clone-space onto the rendered canvas, so\n// callers stop hand-writing (v - left) * scaleX at every layer. left/top is\n// the clone origin inside the capture; scale is capture CSS px → canvas px.\nexport interface CaptureTransform {\n  left: number\n  top: number\n  scaleX: number\n  scaleY: number\n}\n\nexport function captureTransform(region: Rect, canvasWidth: number, canvasHeight: number): CaptureTransform {\n  return {\n    left: region.left,\n    top: region.top,\n    scaleX: canvasWidth / region.width,\n    scaleY: canvasHeight / region.height,\n  }\n}\n\nexport function toCanvasX(transform: CaptureTransform, x: number): number {\n  return (x - transform.left) * transform.scaleX\n}\n\nexport function toCanvasY(transform: CaptureTransform, y: number): number {\n  return (y - transform.top) * transform.scaleY\n}\n\nexport function rectToCanvas(transform: CaptureTransform, rect: Rect): Rect {\n  return {\n    left: toCanvasX(transform, rect.left),\n    top: toCanvasY(transform, rect.top),\n    width: rect.width * transform.scaleX,\n    height: rect.height * transform.scaleY,\n  }\n}\n\nexport interface ScrollOffset {\n  x: number\n  y: number\n}\n\n/**\n * The capture region: the visual viewport, and nothing else. html-to-image\n * renders document.body shifted by the page scroll so the on-screen region\n * lands at the canvas origin — which makes this rect both the capture request\n * and the deliverable. There is no crop step: cropping a clone-space bitmap\n * was only ever needed when the capture was the whole document.\n */\nexport function viewportCaptureRect(): Rect {\n  return { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight }\n}\n\n/**\n * How far an anchored annotation must move to land in a capture's frame.\n *\n * The anchor is the annotation's scroll debt at creation (page + nested\n * scrollers); the capture's pageScroll is the page half of that debt at capture\n * time. Adding the anchor lifts the annotation into clone space, subtracting\n * the capture scroll brings it back down into the viewport bitmap.\n */\nexport function anchorScreenshotOffset(\n  anchor: ScrollOffset,\n  pageScroll: ScrollOffset,\n): ScrollOffset {\n  return { x: anchor.x - pageScroll.x, y: anchor.y - pageScroll.y }\n}\n\n// How far a viewport measurement has to move to reach clone space: every\n// scroller between the element and the body has pushed its content out of view\n// by its own scrollLeft/scrollTop, and html-to-image renders it back.\nexport function nestedScrollOffset(target: Element | null): ScrollOffset {\n  let x = 0\n  let y = 0\n  let current = target\n\n  while (current && current !== document.body) {\n    if (current instanceof HTMLElement) {\n      x += current.scrollLeft\n      y += current.scrollTop\n    }\n    current = parentElement(current)\n  }\n\n  return { x, y }\n}\n\nexport function parentElement(element: Element): Element | null {\n  if (element.parentElement) return element.parentElement\n\n  const root = element.getRootNode()\n  return root instanceof ShadowRoot ? root.host : null\n}\n\n/**\n * The full offset from a viewport measurement to clone space, page scroll\n * included — the anchor an annotation records so that later scrolling cannot\n * move it.\n *\n * nestedScrollOffset stops at the body because the capture's own metrics carry\n * the page scroll for anything measured at capture time. An annotation is not\n * measured at capture time, so it has to carry that half itself.\n */\nexport function captureAnchorOffset(target: Element | null): ScrollOffset {\n  const nested = nestedScrollOffset(target)\n  return { x: nested.x + window.scrollX, y: nested.y + window.scrollY }\n}\n","import type { ShadowContainer } from '../widget/shadow'\n\nexport class PinMarker {\n  private pins: HTMLDivElement[] = []\n  private shadow: ShadowContainer\n\n  constructor(shadow: ShadowContainer) {\n    this.shadow = shadow\n  }\n\n  addPin(x: number, y: number): void {\n    const pin = this.shadow.el<HTMLDivElement>('div', 'mtb-pin')\n    pin.style.top = `${y}px`\n    pin.style.left = `${x}px`\n    this.shadow.append(pin)\n    this.pins.push(pin)\n  }\n\n  clearAll(): void {\n    for (const pin of this.pins) pin.remove()\n    this.pins = []\n  }\n\n  destroy(): void {\n    this.clearAll()\n  }\n}\n","export const BRAND_COLOR = '#059669'\n// Same green as BRAND_COLOR, in the form shadows need. Derived once so a brand\n// change cannot leave the shadows on the old colour.\nconst BRAND_RGB = '5,150,105'\n\nexport function brandAlpha(alpha: number): string {\n  return `rgba(${BRAND_RGB},${alpha})`\n}\n\nexport const FONT_STACK = '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif'\n\nexport const DRAW_STROKE = {\n  color: 'var(--mtb-brand-primary)',\n  width: '3.5',\n  linecap: 'round',\n  linejoin: 'round',\n  filter: 'drop-shadow(0 0 1px rgba(255, 255, 255, 0.95)) drop-shadow(0 1px 2px rgba(15, 23, 42, 0.72))',\n} as const\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { Annotation } from '../types'\nimport { DRAW_STROKE } from '../styles'\nimport { elementUnderPoint } from '../context/dom-utils'\nimport { nestedScrollOffset } from '../screenshot/geometry'\nimport type { ScrollOffset } from '../screenshot/geometry'\n\ntype Point = [number, number]\n\ninterface StrokeAnchor {\n  /** Page scroll plus enclosing-container scroll: the offset to clone space. */\n  offset: ScrollOffset\n  /** The page-scroll half on its own, kept so viewportDrift can undo it. */\n  pageScroll: ScrollOffset\n}\n\ninterface Stroke {\n  path: SVGPathElement\n  d: string\n  points: Point[]\n  anchor: StrokeAnchor\n}\n\nexport class DrawLayer {\n  private svg: SVGSVGElement\n  private currentPath: SVGPathElement | null = null\n  private currentPoints: Point[] = []\n  // Sampled when the stroke starts rather than when the drawing is handed over,\n  // so a page or container scroll partway through a multi-stroke drawing moves\n  // neither the strokes already made nor the ones still to come.\n  private currentAnchor: StrokeAnchor = emptyAnchor()\n  private isDrawing = false\n  private committed: Stroke[] = []\n  private redoStack: { d: string; points: Point[]; anchor: StrokeAnchor }[] = []\n\n  constructor(shadow: ShadowContainer) {\n    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n    this.svg.setAttribute('class', 'mtb-draw-svg')\n    shadow.append(this.svg)\n  }\n\n  startDraw(x: number, y: number): void {\n    this.isDrawing = true\n    this.currentPoints = [[x, y]]\n    this.currentAnchor = strokeAnchor(x, y)\n    this.currentPath = this.createPath()\n    this.svg.appendChild(this.currentPath)\n  }\n\n  continueDraw(x: number, y: number): void {\n    if (!this.isDrawing || !this.currentPath) return\n    this.currentPoints.push([x, y])\n    this.currentPath.setAttribute('d', buildPathD(this.currentPoints))\n  }\n\n  /**\n   * How far the pointer has wandered from where it went down, in pixels.\n   *\n   * This is what separates a tap from a stroke. Counting points cannot: a\n   * mouse click reports no movement at all, while a finger reports a burst of\n   * sub-pixel jitter, so any point-based threshold reads every tap on a phone\n   * as a drawing.\n   */\n  getCurrentSpan(): number {\n    const [start] = this.currentPoints\n    if (!start) return 0\n\n    let max = 0\n    for (const [x, y] of this.currentPoints) {\n      max = Math.max(max, Math.hypot(x - start[0], y - start[1]))\n    }\n    return max\n  }\n\n  // Commits the in-progress stroke to the undo stack and clears the redo stack.\n  // Callers decide whether the stroke has enough points before committing.\n  commitStroke(): boolean {\n    if (!this.isDrawing || !this.currentPath) return false\n    this.isDrawing = false\n    this.committed.push({\n      path: this.currentPath,\n      d: buildPathD(this.currentPoints),\n      points: this.currentPoints,\n      anchor: this.currentAnchor,\n    })\n    this.redoStack = []\n    this.currentPath = null\n    this.currentPoints = []\n    return true\n  }\n\n  cancelCurrentDraw(): void {\n    this.currentPath?.remove()\n    this.currentPath = null\n    this.isDrawing = false\n    this.currentPoints = []\n  }\n\n  undo(): boolean {\n    const stroke = this.committed.pop()\n    if (!stroke) return false\n    stroke.path.remove()\n    this.redoStack.push({ d: stroke.d, points: stroke.points, anchor: stroke.anchor })\n    return true\n  }\n\n  redo(): boolean {\n    const entry = this.redoStack.pop()\n    if (!entry) return false\n    const path = this.createPath()\n    path.setAttribute('d', entry.d)\n    this.svg.appendChild(path)\n    // The stroke comes back in the frame it was drawn in, not the frame the\n    // page happens to be scrolled to when redo is pressed.\n    this.committed.push({ path, d: entry.d, points: entry.points, anchor: entry.anchor })\n    return true\n  }\n\n  canUndo(): boolean {\n    return this.committed.length > 0\n  }\n\n  canRedo(): boolean {\n    return this.redoStack.length > 0\n  }\n\n  hasStrokes(): boolean {\n    return this.committed.length > 0\n  }\n\n  getAnnotation(): Annotation | null {\n    const [first] = this.committed\n    if (!first) return null\n\n    // Strokes made either side of a scroll were recorded against different\n    // viewports. Restate them all in the frame the first stroke was drawn in so\n    // the drawing is one shape, and hand that frame's anchor to the caller.\n    const strokes = this.committed.map(stroke => shiftPoints(stroke.points, {\n      x: stroke.anchor.offset.x - first.anchor.offset.x,\n      y: stroke.anchor.offset.y - first.anchor.offset.y,\n    }))\n    const [cx, cy] = center(strokes.flat())\n    return {\n      type: 'draw',\n      x: cx,\n      y: cy,\n      drawPath: strokes.map(buildPathD).join(' '),\n      captureOffsetX: first.anchor.offset.x,\n      captureOffsetY: first.anchor.offset.y,\n    }\n  }\n\n  /**\n   * How far the drawing's frame has drifted from the live viewport, so a caller\n   * can map a point in the drawing back onto the page as it stands now — which\n   * is what hit-testing the drawing's target needs.\n   */\n  viewportDrift(): ScrollOffset {\n    const [first] = this.committed\n    if (!first) return { x: 0, y: 0 }\n    return {\n      x: window.scrollX - first.anchor.pageScroll.x,\n      y: window.scrollY - first.anchor.pageScroll.y,\n    }\n  }\n\n  clearAll(): void {\n    while (this.svg.firstChild) this.svg.removeChild(this.svg.firstChild)\n    this.currentPath = null\n    this.isDrawing = false\n    this.currentPoints = []\n    this.committed = []\n    this.redoStack = []\n  }\n\n  isCurrentlyDrawing(): boolean {\n    return this.isDrawing\n  }\n\n  destroy(): void {\n    this.svg.remove()\n  }\n\n  private createPath(): SVGPathElement {\n    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n    path.setAttribute('fill', 'none')\n    path.setAttribute('stroke', DRAW_STROKE.color)\n    path.setAttribute('stroke-width', DRAW_STROKE.width)\n    path.setAttribute('stroke-linecap', DRAW_STROKE.linecap)\n    path.setAttribute('stroke-linejoin', DRAW_STROKE.linejoin)\n    path.setAttribute('style', `filter:${DRAW_STROKE.filter}`)\n    return path\n  }\n}\n\nfunction buildPathD(points: Point[]): string {\n  if (points.length === 0) return ''\n  const [fx, fy] = points[0]\n  let d = `M ${fx} ${fy}`\n  for (let i = 1; i < points.length; i++) {\n    const [x, y] = points[i]\n    d += ` L ${x} ${y}`\n  }\n  return d\n}\n\nfunction emptyAnchor(): StrokeAnchor {\n  return { offset: { x: 0, y: 0 }, pageScroll: { x: 0, y: 0 } }\n}\n\nfunction strokeAnchor(x: number, y: number): StrokeAnchor {\n  const pageScroll = { x: window.scrollX, y: window.scrollY }\n  const nested = nestedScrollOffset(elementUnderPoint(x, y))\n  return {\n    offset: { x: nested.x + pageScroll.x, y: nested.y + pageScroll.y },\n    pageScroll,\n  }\n}\n\nfunction shiftPoints(points: Point[], offset: ScrollOffset): Point[] {\n  if (offset.x === 0 && offset.y === 0) return points\n  return points.map(([x, y]) => [x + offset.x, y + offset.y])\n}\n\nfunction center(points: Point[]): Point {\n  const xs = points.map(p => p[0])\n  const ys = points.map(p => p[1])\n  const cx = (Math.min(...xs) + Math.max(...xs)) / 2\n  const cy = (Math.min(...ys) + Math.max(...ys)) / 2\n  return [cx, cy]\n}\n","// rrweb's own defaults for opting an element out of a replay. Named here so the\n// README can document them and so the values stay in one place.\nexport const BLOCK_CLASS = 'rr-block'\nexport const MASK_TEXT_CLASS = 'rr-mask'\n\nexport const PRIVACY_SELECTOR = `.${BLOCK_CLASS}, .${MASK_TEXT_CLASS}`\n\nexport function isPrivacyProtected(element: Element): boolean {\n  return element.closest(PRIVACY_SELECTOR) !== null\n}\n\nexport function structuralElementName(element: Element): string {\n  const role = element.getAttribute('role')?.trim().split(/\\s+/)[0]\n  if (role && /^[a-z][a-z0-9-]*$/i.test(role)) return role\n  return element.tagName.toLowerCase()\n}\n\nexport function structuralElementSelector(element: Element): string {\n  return element.tagName.toLowerCase()\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport type { Annotation } from '../types'\nimport { AnnotationOverlay } from './overlay'\nimport { ElementHighlighter } from './highlighter'\nimport { getElementName, getCssSelector, elementUnderPoint } from '../context/dom-utils'\nimport { captureAnchorOffset } from '../screenshot/geometry'\nimport { PinMarker } from './pin'\nimport { DrawLayer } from './draw'\nimport { isTouchPointer } from '../widget/pointer-kind'\nimport { isPrivacyProtected, structuralElementName, structuralElementSelector } from '../privacy/dom'\n\n// How far the pointer must travel before the gesture counts as drawing rather\n// than pointing. Roughly the slop browsers themselves allow between mousedown\n// and click, and comfortably above the jitter a resting finger produces.\nconst DRAW_MIN_TRAVEL_PX = 10\n\ninterface SessionHandlers {\n  onPin: (annotation: Annotation, targetName: string) => void\n  onDrawStroke: () => void\n}\n\nfunction annotationTarget(element: Element): { name: string; text: string; selector: string } {\n  if (isPrivacyProtected(element)) {\n    return {\n      name: structuralElementName(element),\n      text: '',\n      selector: structuralElementSelector(element),\n    }\n  }\n\n  return {\n    name: getElementName(element),\n    text: element.textContent?.trim().slice(0, 200) ?? '',\n    selector: getCssSelector(element),\n  }\n}\n\nexport class AnnotationSession {\n  private overlay: AnnotationOverlay\n  private highlighter: ElementHighlighter\n  private pinMarker: PinMarker\n  private drawLayer: DrawLayer\n  private interactionActive = true\n  private pendingHighlightFrame: number | null = null\n  private lastMoveX = 0\n  private lastMoveY = 0\n  // Read once. A pointer kind does not change mid-session, and re-querying it\n  // per gesture would let a mid-stroke media-query flip strand a stroke.\n  private readonly touch = isTouchPointer()\n\n  constructor(\n    shadow: ShadowContainer,\n    private messages: I18nMessages,\n    private handlers: SessionHandlers,\n  ) {\n    this.highlighter = new ElementHighlighter(shadow)\n    this.pinMarker = new PinMarker(shadow)\n    this.drawLayer = new DrawLayer(shadow)\n    this.overlay = new AnnotationOverlay(shadow, {\n      onPointerDown: (e) => this.handlePointerDown(e),\n      onPointerMove: (e) => this.handlePointerMove(e),\n      onPointerUp: (e) => this.handlePointerUp(e),\n      // Previously mouseleave. The overlay now captures the pointer, so a\n      // stroke no longer ends by wandering off the surface — only by the\n      // browser taking the gesture away, which is what pointercancel means.\n      onPointerCancel: () => {\n        if (this.drawLayer.isCurrentlyDrawing()) this.finishStroke()\n        this.cancelPendingHighlight()\n        this.highlighter.hide()\n      },\n    })\n  }\n\n  private handlePointerDown(e: PointerEvent): void {\n    e.preventDefault()\n    this.drawLayer.startDraw(e.clientX, e.clientY)\n  }\n\n  private handlePointerMove(e: PointerEvent): void {\n    if (this.drawLayer.isCurrentlyDrawing()) {\n      this.cancelPendingHighlight()\n      this.drawLayer.continueDraw(e.clientX, e.clientY)\n      this.highlighter.hide()\n      return\n    }\n\n    this.lastMoveX = e.clientX\n    this.lastMoveY = e.clientY\n    if (this.pendingHighlightFrame !== null) return\n    this.pendingHighlightFrame = requestAnimationFrame(() => {\n      this.pendingHighlightFrame = null\n      this.updateHighlight()\n    })\n  }\n\n  private updateHighlight(): void {\n    if (!this.interactionActive || this.drawLayer.isCurrentlyDrawing() || this.drawSessionActive()) return\n    const underEl = elementUnderPoint(this.lastMoveX, this.lastMoveY)\n    if (underEl) this.highlighter.highlight(underEl)\n    else this.highlighter.hide()\n  }\n\n  private cancelPendingHighlight(): void {\n    if (this.pendingHighlightFrame === null) return\n    cancelAnimationFrame(this.pendingHighlightFrame)\n    this.pendingHighlightFrame = null\n  }\n\n  private handlePointerUp(e: PointerEvent): void {\n    if (!this.drawLayer.isCurrentlyDrawing()) return\n    this.finishStroke(e)\n  }\n\n  // A drag that travels far enough becomes a freehand stroke and opens the\n  // draw note bar; a tap on an element becomes a pin. Stray taps while a\n  // drawing already exists are ignored so they don't hijack the draw flow.\n  private finishStroke(e?: PointerEvent): void {\n    if (this.drawLayer.getCurrentSpan() >= DRAW_MIN_TRAVEL_PX) {\n      this.drawLayer.commitStroke()\n      this.highlighter.hide()\n      this.handlers.onDrawStroke()\n      return\n    }\n\n    this.drawLayer.cancelCurrentDraw()\n    // A finger draws; it does not point. Hitting a 40px control on a phone is\n    // fiddly, and the habit people already have is to scribble over what is\n    // wrong rather than tap it precisely. So on touch a tap means nothing at\n    // all — which also retires a whole family of defects, since every \"the tap\n    // did nothing\" and \"a stray dot appeared\" report was a tap that carried\n    // meaning it should never have carried. The element identity a pin used to\n    // supply is recovered from the stroke instead, in getDrawAnnotation().\n    if (this.touch) return\n    if (this.drawLayer.hasStrokes() || !e) return\n\n    const underEl = elementUnderPoint(e.clientX, e.clientY)\n    const target = underEl ? annotationTarget(underEl) : null\n    const targetName = target?.name ?? this.messages.annotation.element\n    const rect = underEl?.getBoundingClientRect()\n    // Sampled here, alongside the coordinates it belongs to. Read any later and\n    // it would describe wherever the reporter has scrolled to since.\n    const anchor = captureAnchorOffset(underEl)\n    const annotation: Annotation = {\n      type: 'pin',\n      x: e.clientX,\n      y: e.clientY,\n      targetSelector: target?.selector,\n      targetText: target?.text ?? '',\n      targetName,\n      targetRect: rect ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height, bottom: rect.bottom } : undefined,\n      captureOffsetX: anchor.x,\n      captureOffsetY: anchor.y,\n    }\n    this.pinMarker.addPin(e.clientX, e.clientY)\n    this.highlighter.select()\n    this.handlers.onPin(annotation, targetName)\n  }\n\n  undoDraw(): void {\n    this.drawLayer.undo()\n  }\n\n  redoDraw(): void {\n    this.drawLayer.redo()\n  }\n\n  canUndoDraw(): boolean {\n    return this.drawLayer.canUndo()\n  }\n\n  canRedoDraw(): boolean {\n    return this.drawLayer.canRedo()\n  }\n\n  hasDrawStrokes(): boolean {\n    return this.drawLayer.hasStrokes()\n  }\n\n  private drawSessionActive(): boolean {\n    return this.drawLayer.hasStrokes() || this.drawLayer.canRedo()\n  }\n\n  // The stroke carries the element identity a pin used to supply. Without it a\n  // touch report would say only \"the user drew somewhere\", where the board has\n  // always been able to say which control they meant — that identity is what\n  // makes a report actionable, and it should not be the price of not making\n  // people aim. Resolved from the stroke's centre, so a circle drawn around a\n  // button lands inside it. Best effort by design: an underline sits below its\n  // target and will miss, which is why the drawing itself is still the record.\n  getDrawAnnotation(): Annotation | null {\n    const annotation = this.drawLayer.getAnnotation()\n    if (!annotation) return null\n\n    // The drawing's coordinates are in the frame it was drawn in; hit-testing\n    // reads the page as it is now, so the centre has to make that trip first.\n    const drift = this.drawLayer.viewportDrift()\n    const underEl = elementUnderPoint(annotation.x - drift.x, annotation.y - drift.y)\n    if (!underEl) return annotation\n\n    // The rect and its own anchor are read in the same breath, so together they\n    // say where this element sits in the capture; subtracting the drawing's\n    // anchor restates that in the frame the rest of the annotation uses.\n    const rect = underEl.getBoundingClientRect()\n    const anchor = captureAnchorOffset(underEl)\n    const dx = anchor.x - (annotation.captureOffsetX ?? 0)\n    const dy = anchor.y - (annotation.captureOffsetY ?? 0)\n    const target = annotationTarget(underEl)\n    return {\n      ...annotation,\n      targetSelector: target.selector,\n      targetText: target.text,\n      targetName: target.name,\n      targetRect: {\n        top: rect.top + dy,\n        left: rect.left + dx,\n        width: rect.width,\n        height: rect.height,\n        bottom: rect.bottom + dy,\n      },\n    }\n  }\n\n  clearDraw(): void {\n    this.drawLayer.clearAll()\n  }\n\n  dismissInteraction(): void {\n    if (!this.interactionActive) return\n    this.interactionActive = false\n    this.cancelPendingHighlight()\n    this.overlay.destroy()\n  }\n\n  hideHighlight(): void {\n    this.highlighter.hide()\n  }\n\n  destroy(): void {\n    this.dismissInteraction()\n    this.highlighter.destroy()\n    this.pinMarker.destroy()\n    this.drawLayer.destroy()\n  }\n}\n","/**\n * How much of the window the software keyboard is covering.\n *\n * A `position: fixed` sheet gets no help from the browser here — the built-in\n * \"scroll the focused field into view\" only moves elements that are in the\n * document flow, so on iOS the keyboard simply covers the sheet.\n *\n * The two platforms hide the keyboard in different places, and this one\n * formula covers both. iOS keeps the layout viewport at full height and\n * shrinks (and shifts) the visual viewport, so the difference is the keyboard.\n * Android resizes the layout viewport itself, so `innerHeight` shrinks too and\n * the difference falls out to zero — which is the right answer there, because\n * the bottom of the window is already above the keyboard.\n *\n * `offsetTop` matters as much as `height`: iOS shifts the visual viewport up\n * as well as shrinking it, and ignoring the shift leaves the sheet floating\n * exactly that far off, with a strip of page showing beneath it.\n */\n\n/**\n * The keyboard slides in, and iOS reports the viewport continuously while it\n * moves. Measured on an iOS 18.7 device over 130ms: 660 -> 323 -> 95 -> 660.\n * Acting on each of those walked the sheet up to 337px, then 565px — off the\n * top of the screen — and back down. Waiting for the numbers to stop moving\n * turns that into one placement.\n */\nconst SETTLE_MS = 140\n\nexport class KeyboardInset {\n  private onChange: ((inset: number) => void) | null = null\n  private timer: ReturnType<typeof setTimeout> | null = null\n  private lastReading: number | null = null\n\n  private handler = (): void => {\n    if (this.timer !== null) clearTimeout(this.timer)\n    this.timer = setTimeout(() => {\n      this.timer = null\n      const reading = this.current()\n      // Take it only once the viewport says the same thing twice. Mid-slide\n      // iOS reports values that are not measurements — -122 and 95 on a 660px\n      // window were both seen — and a single quiet moment is not proof the\n      // animation has finished, only that events paused. Two agreeing reads\n      // are, and cost one more settle interval.\n      if (reading === this.lastReading) {\n        this.lastReading = null\n        this.onChange?.(reading)\n        return\n      }\n      this.lastReading = reading\n      this.handler()\n    }, SETTLE_MS)\n  }\n\n  current(): number {\n    const vp = window.visualViewport\n    if (!vp) return 0\n\n    // Observed on iOS 18.7 mid-keyboard-animation: height reported as -122.\n    // A viewport has no negative height — that is not a small measurement, it\n    // is not a measurement. Deriving an inset from it gave 782px on a 660px\n    // window, and only the settle window happened to discard it that time.\n    if (vp.height <= 0) return 0\n\n    const inset = Math.round(window.innerHeight - (vp.height + vp.offsetTop))\n    // The keyboard cannot hide more than the window contains.\n    return Math.min(Math.max(0, inset), window.innerHeight)\n  }\n\n  observe(onChange: (inset: number) => void): void {\n    const vp = window.visualViewport\n    if (!vp) return\n    this.onChange = onChange\n    vp.addEventListener('resize', this.handler)\n    // Scroll fires for the shift-up on iOS, which resize alone does not report.\n    vp.addEventListener('scroll', this.handler)\n  }\n\n  stop(): void {\n    const vp = window.visualViewport\n    this.onChange = null\n    this.lastReading = null\n    if (this.timer !== null) {\n      clearTimeout(this.timer)\n      this.timer = null\n    }\n    if (!vp) return\n    vp.removeEventListener('resize', this.handler)\n    vp.removeEventListener('scroll', this.handler)\n  }\n}\n","import { KeyboardInset } from './keyboard-inset'\n\n/**\n * Matches the `max-width: 480px` block in widget.css, where every panel that\n * holds a text field stops being a card anchored near the annotation and\n * becomes a bottom sheet.\n */\nexport const SHEET_MAX_WIDTH = 480\n\nexport function isSheetViewport(): boolean {\n  return window.innerWidth <= SHEET_MAX_WIDTH\n}\n\n/**\n * Hands a panel's geometry to the stylesheet on narrow screens, and keeps it\n * clear of the software keyboard.\n *\n * Two jobs that have to live together. The panels each position themselves\n * with inline `left`/`top`, which would beat the media query and drag the sheet\n * back into a floating card — so the inline values have to be cleared, not just\n * overridden. And a `position: fixed` sheet gets no help from the browser's\n * scroll-into-view, which only moves elements in the document flow, so the\n * keyboard simply covers it unless something lifts it.\n *\n * Shared by the comment popup, the clarify card, and the draw note bar: all\n * three carry a text field, and all three are the last thing between a reporter\n * and a submitted report.\n */\nexport class SheetLayout {\n  private inset = new KeyboardInset()\n\n  constructor(private el: HTMLElement) {}\n\n  /** Returns true when this took over layout, i.e. the caller should not position. */\n  apply(): boolean {\n    if (!isSheetViewport()) return false\n\n    this.el.style.left = ''\n    this.el.style.top = ''\n    this.el.style.maxHeight = ''\n    this.lift(this.inset.current())\n    this.inset.observe((px) => this.lift(px))\n    return true\n  }\n\n  release(): void {\n    this.inset.stop()\n  }\n\n  /**\n   * Lifting is clamped so the panel's own top edge stays on screen.\n   *\n   * The rule is geometric rather than a guess about how tall a keyboard gets:\n   * an earlier version refused any inset above 60% of the window, a number\n   * calibrated from a single reading on a single device. What actually matters\n   * is that the reporter can still see and reach the panel, and that is a\n   * question about this panel's height, which is known right here.\n   */\n  private lift(inset: number): void {\n    const headroom = Math.max(0, window.innerHeight - this.el.offsetHeight)\n    const applied = Math.min(inset, headroom)\n    this.el.style.bottom = applied > 0 ? `${applied}px` : ''\n  }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { escapeHtml, isImeConfirmKeydown } from '../context/dom-utils'\nimport { SheetLayout } from '../popup/sheet'\n\ninterface DrawNoteBarOptions {\n  messages: I18nMessages\n  onUndo: () => void\n  onRedo: () => void\n  onCancel: () => void\n  onSubmit: (note: string) => void\n  // Fired on every keystroke; the owner uses it to defer heavy capture work\n  // until the reporter pauses.\n  onActivity?: () => void\n}\n\nexport class DrawNoteBar {\n  private el: HTMLDivElement\n  private sheet: SheetLayout\n  private input!: HTMLInputElement\n  private undoBtn!: HTMLButtonElement\n  private redoBtn!: HTMLButtonElement\n  private submitBtn!: HTMLButtonElement\n  private onSubmit: (note: string) => void\n  private submitLabel: string\n  private messages: I18nMessages\n\n  constructor(shadow: ShadowContainer, opts: DrawNoteBarOptions) {\n    this.onSubmit = opts.onSubmit\n    this.submitLabel = opts.messages.draw.submit\n    this.messages = opts.messages\n\n    this.el = shadow.el<HTMLDivElement>('div', 'mtb-draw-bar')\n    this.el.innerHTML = this.buildHTML(opts.messages)\n    shadow.append(this.el)\n    // The bar carries a text field and sits flush to the bottom on a phone,\n    // which is exactly where the keyboard lands.\n    this.sheet = new SheetLayout(this.el)\n    this.sheet.apply()\n\n    this.input = this.el.querySelector('.mtb-draw-input')!\n    this.undoBtn = this.el.querySelector('.mtb-draw-undo')!\n    this.redoBtn = this.el.querySelector('.mtb-draw-redo')!\n    this.submitBtn = this.el.querySelector('.mtb-draw-submit')!\n\n    this.undoBtn.addEventListener('click', opts.onUndo)\n    this.redoBtn.addEventListener('click', opts.onRedo)\n    this.el.querySelector('.mtb-draw-cancel')!.addEventListener('click', opts.onCancel)\n    this.submitBtn.addEventListener('click', () => this.submit())\n    if (opts.onActivity) this.input.addEventListener('input', opts.onActivity)\n    this.input.addEventListener('keydown', (e: KeyboardEvent) => {\n      if (isImeConfirmKeydown(e)) return\n      if (e.key === 'Enter' && !e.shiftKey) {\n        e.preventDefault()\n        this.submit()\n      }\n    })\n\n    this.input.focus({ preventScroll: true })\n  }\n\n  private buildHTML(m: I18nMessages): string {\n    return `\n      <input class=\"mtb-draw-input\" type=\"text\" placeholder=\"${escapeHtml(m.draw.note_placeholder)}\" />\n      <button class=\"mtb-draw-icon-btn mtb-draw-undo\" type=\"button\" title=\"${escapeHtml(m.draw.undo)}\" aria-label=\"${escapeHtml(m.draw.undo)}\" disabled>\n        <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 14L4 9l5-5\"/><path d=\"M4 9h11a5 5 0 0 1 0 10h-1\"/></svg>\n      </button>\n      <button class=\"mtb-draw-icon-btn mtb-draw-redo\" type=\"button\" title=\"${escapeHtml(m.draw.redo)}\" aria-label=\"${escapeHtml(m.draw.redo)}\" disabled>\n        <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 14l5-5-5-5\"/><path d=\"M20 9H9a5 5 0 0 0 0 10h1\"/></svg>\n      </button>\n      <label class=\"mtb-draw-shot\" title=\"${escapeHtml(m.popup.screenshot_locked_hint)}\">\n        <input type=\"checkbox\" checked disabled><span>${escapeHtml(m.popup.screenshot_short)}</span>\n      </label>\n      <span class=\"mtb-draw-sep\"></span>\n      <button class=\"mtb-draw-cancel\" type=\"button\">${escapeHtml(m.draw.cancel)}</button>\n      <button class=\"mtb-draw-submit\" type=\"button\">${escapeHtml(m.draw.submit)}</button>\n    `\n  }\n\n  setLoading(loading: boolean): void {\n    if (loading) {\n      this.submitBtn.disabled = true\n      this.submitBtn.classList.add('mtb-btn-loading')\n      this.submitBtn.textContent = this.messages.popup.sending\n      this.input.disabled = true\n    } else {\n      this.submitBtn.disabled = false\n      this.submitBtn.classList.remove('mtb-btn-loading')\n      this.submitBtn.textContent = this.submitLabel\n      this.input.disabled = false\n    }\n  }\n\n  setUndoRedo(canUndo: boolean, canRedo: boolean): void {\n    this.undoBtn.disabled = !canUndo\n    this.redoBtn.disabled = !canRedo\n  }\n\n  private submit(): void {\n    this.onSubmit(this.input.value.trim())\n  }\n\n  destroy(): void {\n    this.sheet.release()\n    this.el.remove()\n  }\n}\n\n","// Shared floating-panel geometry for the chat card and its kin. Panels anchor\n// a fixed panel inside the viewport while keeping clear of the launcher tab;\n// the margin and tab reserve used to be duplicated in each class and had to\n// stay hand-synced, so they are named once here.\n\n/** Gap kept between a floating panel and every viewport edge. */\nexport const VIEWPORT_MARGIN = 12\n/** Horizontal space reserved for the launcher tab on its side of the screen. */\nexport const TAB_WIDTH = 32\n\n/**\n * The horizontal band a floating panel may occupy: the viewport minus the edge\n * margin, minus the launcher tab on whichever side it sits.\n */\nexport interface ViewportFrame {\n  vw: number\n  vh: number\n  minLeft: number\n  maxRight: number\n}\n\nexport function measureViewportFrame(tabPosition: 'left' | 'right'): ViewportFrame {\n  const vw = window.innerWidth\n  const vh = window.innerHeight\n  return {\n    vw,\n    vh,\n    minLeft: VIEWPORT_MARGIN + (tabPosition === 'left' ? TAB_WIDTH : 0),\n    maxRight: vw - VIEWPORT_MARGIN - (tabPosition === 'right' ? TAB_WIDTH : 0),\n  }\n}\n\n/** Widest a panel can be without crossing the edge margin or the launcher tab. */\nexport function panelWidth(preferred: number, frame: ViewportFrame): number {\n  return Math.min(preferred, frame.vw - 2 * VIEWPORT_MARGIN - TAB_WIDTH)\n}\n","/**\n * Keeps keyboard focus inside an open card, and remembers who opened it.\n *\n * The cards float above a dimmed host page, but the dim is only visual: Tab\n * happily walks out of the card and into controls the reporter can no longer\n * see or reach with a pointer. Cycling within the card is what the dialog role\n * promises assistive tech, and restoring the opener afterwards puts a keyboard\n * user back where they were instead of at the top of the document.\n *\n * The Tab handler lives on the card element, not on document or window: it can\n * only ever see keys typed while focus is inside the card, and removing the\n * card's own listener is a teardown the host page cannot be stranded by.\n */\nexport interface FocusTrap {\n  /**\n   * Detach the Tab handler without moving focus — for when the element is\n   * handed to a successor (the clarify continuation) that installs its own\n   * trap and must not have focus yanked out from under it.\n   */\n  release(): void\n  /** Put focus back on whatever had it when the trap was installed. */\n  restoreFocus(): void\n}\n\nconst TABBABLE_SELECTOR = [\n  'button:not([disabled])',\n  'input:not([disabled])',\n  'textarea:not([disabled])',\n  'select:not([disabled])',\n  'a[href]',\n  '[tabindex]:not([tabindex=\"-1\"])',\n].join(', ')\n\nexport function trapFocus(card: HTMLElement): FocusTrap {\n  // Captured before any card control gets autofocused, so the trap remembers\n  // the host page's element, not the card's own field.\n  const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null\n\n  const handleKeydown = (event: KeyboardEvent): void => {\n    if (event.key !== 'Tab') return\n    const tabbables = Array.from(card.querySelectorAll<HTMLElement>(TABBABLE_SELECTOR))\n      .filter(el => !el.hidden)\n    if (tabbables.length === 0) return\n\n    const first = tabbables[0]\n    const last = tabbables[tabbables.length - 1]\n    // Inside a shadow tree the document only reports the host element; the\n    // shadow root knows which control actually holds focus. Fall back to the\n    // document for environments that leave ShadowRoot.activeElement null.\n    const root = card.getRootNode() as Document | ShadowRoot\n    const active = root.activeElement ?? document.activeElement\n\n    if (event.shiftKey) {\n      if (active === first || !card.contains(active)) {\n        event.preventDefault()\n        last.focus({ preventScroll: true })\n      }\n    } else if (active === last || !card.contains(active)) {\n      event.preventDefault()\n      first.focus({ preventScroll: true })\n    }\n  }\n\n  card.addEventListener('keydown', handleKeydown)\n\n  return {\n    release(): void {\n      card.removeEventListener('keydown', handleKeydown)\n    },\n    restoreFocus(): void {\n      // A detached opener would silently drop focus on <body>, so only reach\n      // for one that still exists. preventScroll for the same reason every\n      // panel uses it: the widget must never scroll the host page.\n      if (opener?.isConnected) opener.focus({ preventScroll: true })\n    },\n  }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport type { ClarifyStreamResult } from '../api/client'\nimport type { SubmissionSessionResponse, TargetRect } from '../types'\nimport { escapeHtml, isImeConfirmKeydown } from '../context/dom-utils'\nimport { SheetLayout } from './sheet'\nimport { measureViewportFrame, panelWidth, VIEWPORT_MARGIN } from './position'\nimport { trapFocus, type FocusTrap } from './focus-trap'\n\ntype ChatState = 'idle' | 'sending' | 'clarifying' | 'answering' | 'done'\n\ninterface ChatPopupOptions {\n  targetName: string\n  x: number\n  y: number\n  targetRect?: TargetRect\n  messages: I18nMessages\n  position?: 'left' | 'right'\n  screenshotEnabled?: boolean\n  onScreenshotToggle?: (enabled: boolean) => void\n  onSubmit: (description: string) => Promise<SubmissionSessionResponse | null>\n  onClarify: (sessionId: string, token: string, onDelta: (text: string) => void, signal?: AbortSignal) => Promise<ClarifyStreamResult>\n  onAnswer: (sessionId: string, token: string, message: string) => Promise<unknown>\n  onFinalize: () => Promise<void> | void\n  onCancel: () => void\n  /** A question is on screen: the card now holds an uploaded submission. */\n  onConversationOpen?: () => void\n  onActivity?: () => void\n  recordingSeconds?: number\n  // Resolved when the success view renders, not at mount: both depend on the\n  // handoff the finalize returns, which does not exist while the reporter is\n  // still typing.\n  resolveBoardLink?: () => (() => void) | undefined\n  resolveEmailCapture?: () => { onSubmit: (email: string) => Promise<boolean> } | undefined\n}\n\nconst CARD_WIDTH = 320\nconst AUTO_CLOSE_MS = 3000\n\nconst BRAND_URL = 'https://makethisbetter.dev'\n\nconst BRAND_ICON = '<svg viewBox=\"0 0 512 512\" fill=\"none\"><defs><linearGradient id=\"mtb-bg\" x1=\".3\" y1=\"0\" x2=\".7\" y2=\"1\"><stop offset=\"0%\" stop-color=\"var(--mtb-chat-logo-start, #10b981)\"/><stop offset=\"100%\" stop-color=\"var(--mtb-chat-logo-end, #059669)\"/></linearGradient></defs><rect width=\"512\" height=\"512\" rx=\"96\" fill=\"url(#mtb-bg)\"/><g transform=\"translate(89,89) scale(13.9)\" stroke=\"var(--mtb-chat-logo-fg, #fff)\" stroke-width=\"2.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 2v6h-6\"/><path d=\"M21 13a9 9 0 1 1-3-7.7L21 8\"/><circle cx=\"12\" cy=\"12\" r=\"2.2\" fill=\"var(--mtb-chat-logo-fg, #fff)\" stroke=\"none\"/></g></svg>'\nconst CLOSE_ICON = '<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><path d=\"M8.9 8l3.3-3.3-.9-.9L8 7.1 4.7 3.8l-.9.9L7.1 8l-3.3 3.3.9.9L8 8.9l3.3 3.3.9-.9z\"/></svg>'\nconst CAMERA_ICON = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><path d=\"M6.2 2.6h3.6l.9 1.4H14a1 1 0 011 1v7.4a1 1 0 01-1 1H2a1 1 0 01-1-1V5a1 1 0 011-1h3.3l.9-1.4z\"/><circle cx=\"8\" cy=\"9\" r=\"2.5\" class=\"mtb-chat-icon-lens\"/></svg>'\nconst RECORD_ICON = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><circle cx=\"8\" cy=\"8\" r=\"6.6\"/><polygon points=\"6.4,5.2 11,8 6.4,10.8\" class=\"mtb-chat-icon-lens\"/></svg>'\nconst CHECK_ICON = '<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M8.1 14.6L3.8 10.3l1.5-1.5 2.8 2.8 6.6-6.6 1.5 1.5z\"/></svg>'\n\nexport class ChatPopup {\n  private el: HTMLDivElement\n  private sheet: SheetLayout\n  private focusTrap: FocusTrap\n  private messagesEl!: HTMLDivElement\n  private composerEl!: HTMLDivElement\n  private actionsEl!: HTMLDivElement\n  private inputEl!: HTMLTextAreaElement\n  private sendBtn!: HTMLButtonElement\n  private quickRepliesEl!: HTMLDivElement\n  private state: ChatState = 'idle'\n  private i18n: I18nMessages\n  private opts: ChatPopupOptions\n  private autoCloseTimer: ReturnType<typeof setTimeout> | null = null\n  private session: SubmissionSessionResponse | null = null\n  private clarifyAbort: AbortController | null = null\n  private destroyed = false\n\n  constructor(shadow: ShadowContainer, opts: ChatPopupOptions) {\n    this.opts = opts\n    this.i18n = opts.messages\n\n    this.el = shadow.el<HTMLDivElement>('div', 'mtb-chat')\n    this.el.setAttribute('role', 'dialog')\n    this.el.setAttribute('aria-modal', 'true')\n    this.el.setAttribute('aria-label', opts.targetName)\n    this.el.innerHTML = this.buildHTML()\n    shadow.append(this.el)\n    this.focusTrap = trapFocus(this.el)\n    this.sheet = new SheetLayout(this.el)\n    if (!this.sheet.apply()) {\n      this.positionCard(opts.x, opts.y, opts.targetRect, opts.position ?? 'right')\n    }\n\n    this.messagesEl = this.el.querySelector('.mtb-chat-messages')!\n    this.composerEl = this.el.querySelector('.mtb-chat-composer')!\n    this.actionsEl = this.el.querySelector('.mtb-chat-actions')!\n    this.inputEl = this.el.querySelector('.mtb-chat-input')!\n    this.sendBtn = this.el.querySelector('.mtb-chat-send')!\n    this.quickRepliesEl = this.el.querySelector('.mtb-chat-quick-replies')!\n\n    this.bindEvents()\n    this.handleEscape = this.handleEscape.bind(this)\n    window.addEventListener('keydown', this.handleEscape)\n    this.inputEl.focus({ preventScroll: true })\n  }\n\n  private handleEscape(e: KeyboardEvent): void {\n    if (e.key !== 'Escape') return\n    e.preventDefault()\n    e.stopPropagation()\n    if (this.state === 'sending') return\n    if (this.state === 'done') {\n      this.destroy()\n      return\n    }\n    this.opts.onCancel()\n  }\n\n  private buildHTML(): string {\n    const m = this.i18n\n    const opts = this.opts\n    const quickOpts = m.popup.quickOptions\n      .map(o => `<button class=\"mtb-chat-qr\" type=\"button\" data-quick=\"${escapeHtml(o.label)}\" data-value=\"${escapeHtml(o.value ?? o.label)}\">${escapeHtml(o.emoji)} ${escapeHtml(o.label)}</button>`)\n      .join('')\n\n    const capture = opts.recordingSeconds != null\n      ? `<span class=\"mtb-chat-attachment\" title=\"${escapeHtml(m.popup.recording_locked_hint)}\">${RECORD_ICON}<span class=\"mtb-chat-capture-label\">${Math.round(opts.recordingSeconds)}s</span></span>`\n      : opts.onScreenshotToggle\n        ? `<button class=\"mtb-chat-screenshot\" type=\"button\" title=\"${escapeHtml(m.popup.screenshot_hint)}\" aria-pressed=\"${opts.screenshotEnabled !== false}\">${CAMERA_ICON}<span class=\"mtb-chat-capture-label\">${escapeHtml(m.popup.screenshot_short)}</span></button>`\n        : ''\n\n    return `\n      <div class=\"mtb-chat-header\">\n        <div class=\"mtb-chat-handle\"></div>\n        <div class=\"mtb-chat-header-row\">\n          <a class=\"mtb-chat-brand\" href=\"${BRAND_URL}\" target=\"_blank\" rel=\"noopener\">\n            <span class=\"mtb-chat-logo\">${BRAND_ICON}</span>\n            <span class=\"mtb-chat-brand-name\">Make This Better</span>\n          </a>\n          <span class=\"mtb-chat-header-gap\"></span>\n          <button class=\"mtb-chat-close\" type=\"button\" aria-label=\"${escapeHtml(m.popup.close)}\">${CLOSE_ICON}</button>\n        </div>\n      </div>\n      <div class=\"mtb-chat-messages\"></div>\n      <div class=\"mtb-chat-quick-replies\">${quickOpts}</div>\n      <div class=\"mtb-chat-composer\">\n        <textarea class=\"mtb-chat-input\" rows=\"1\" placeholder=\"${escapeHtml(m.popup.placeholder)}\"></textarea>\n        <div class=\"mtb-chat-actions\">\n          ${capture}\n          <span class=\"mtb-chat-actions-gap\"></span>\n          <button class=\"mtb-chat-send\" type=\"button\" disabled>${escapeHtml(m.clarify.send)}</button>\n        </div>\n      </div>\n    `\n  }\n\n  private bindEvents(): void {\n    this.el.querySelector('.mtb-chat-close')!.addEventListener('click', () => {\n      this.opts.onCancel()\n    })\n\n    this.quickRepliesEl.querySelectorAll('.mtb-chat-qr').forEach(btn => {\n      btn.addEventListener('click', () => {\n        const value = (btn as HTMLElement).dataset['value'] ?? (btn as HTMLElement).dataset['quick'] ?? ''\n        this.inputEl.value = value\n        this.sendMessage(value)\n      })\n    })\n\n    this.inputEl.addEventListener('input', () => {\n      this.sendBtn.disabled = !this.inputEl.value.trim()\n      this.opts.onActivity?.()\n    })\n\n    this.inputEl.addEventListener('keydown', (e: KeyboardEvent) => {\n      if (isImeConfirmKeydown(e)) return\n      if (e.key === 'Enter' && !e.shiftKey) {\n        e.preventDefault()\n        const text = this.inputEl.value.trim()\n        if (text) {\n          this.sendMessage(text)\n        } else if (this.state === 'clarifying') {\n          this.skipAndSend()\n        }\n      }\n    })\n\n    this.sendBtn.addEventListener('click', () => {\n      const text = this.inputEl.value.trim()\n      if (text) this.sendMessage(text)\n    })\n\n    const screenshotBtn = this.el.querySelector('.mtb-chat-screenshot')\n    if (screenshotBtn && this.opts.onScreenshotToggle) {\n      screenshotBtn.addEventListener('click', () => {\n        const pressed = screenshotBtn.getAttribute('aria-pressed') === 'true'\n        screenshotBtn.setAttribute('aria-pressed', String(!pressed))\n        screenshotBtn.classList.toggle('mtb-chat-screenshot-off', pressed)\n        this.opts.onScreenshotToggle!(!pressed)\n      })\n    }\n  }\n\n  private async sendMessage(text: string): Promise<void> {\n    if (this.state === 'sending' || this.state === 'answering') return\n\n    if (this.state === 'clarifying') {\n      this.handleAnswer(text)\n      return\n    }\n\n    this.state = 'sending'\n    this.appendUserBubble(text)\n    this.quickRepliesEl.style.display = 'none'\n    this.hideCapture()\n    this.showThinking()\n    this.setComposerDisabled(true)\n\n    let session: SubmissionSessionResponse | null\n    try {\n      session = await this.opts.onSubmit(text)\n    } catch {\n      session = null\n    }\n\n    if (this.destroyed) return\n\n    if (!session) {\n      this.removeThinking()\n      this.setComposerDisabled(false)\n      this.showRetryableError(this.i18n.error.submit, () => {\n        this.clearError()\n        void this.sendMessage(text)\n      })\n      this.state = 'idle'\n      return\n    }\n\n    this.session = session\n\n    if (!session.ai_clarify_available) {\n      this.removeThinking()\n      void this.showDone()\n      return\n    }\n\n    let result: ClarifyStreamResult | null = null\n    let streaming = false\n    this.clarifyAbort = new AbortController()\n    try {\n      result = await this.opts.onClarify(session.id, session.token, (delta) => {\n        if (this.destroyed) return\n        if (!streaming) {\n          this.removeThinking()\n          this.startStreamingBubble()\n          streaming = true\n        }\n        this.appendStreamDelta(delta)\n      }, this.clarifyAbort.signal)\n    } catch {\n      if (this.destroyed) return\n      if (!streaming) this.removeThinking()\n      else this.endStreamingBubble()\n      this.showDone()\n      return\n    }\n\n    if (this.destroyed) return\n\n    if (!streaming) this.removeThinking()\n    else this.endStreamingBubble()\n\n    if (result.done || !result.messages.length) {\n      void this.showDone()\n      return\n    }\n\n    const aiMessage = result.messages.find(msg => msg.role === 'assistant')\n    if (!streaming && aiMessage) {\n      this.appendAiBubble(aiMessage.content)\n    }\n    this.renderSuggestions(result.suggestions)\n    this.state = 'clarifying'\n    this.setComposerDisabled(false)\n    this.inputEl.value = ''\n    this.inputEl.placeholder = this.i18n.clarify.placeholder\n    this.sendBtn.textContent = this.i18n.clarify.submit ?? this.i18n.clarify.send\n    this.sendBtn.disabled = true\n    this.showSkip()\n    this.inputEl.focus({ preventScroll: true })\n    this.opts.onConversationOpen?.()\n  }\n\n  private handleAnswer(text: string): void {\n    const session = this.session\n    if (!session) return\n    this.state = 'done'\n    this.appendUserBubble(text)\n    this.clearSuggestions()\n    this.hideSkip()\n    this.setComposerDisabled(true)\n    this.renderSuccess()\n    // The answer has to reach the server before finalize runs: finalizing\n    // deactivates the session, after which the server rejects the answer as\n    // terminal. A failed answer still finalizes — losing the follow-up answer\n    // must never lose the feedback itself.\n    Promise.resolve(this.opts.onAnswer(session.id, session.token, text))\n      .catch(() => {})\n      .then(() => {\n        if (!this.destroyed) this.finalizeInBackground()\n      })\n  }\n\n  /**\n   * The receipt goes up the moment the reporter is done rather than waiting on\n   * the finalize round trip; the hand-off it returns is patched into the card\n   * when it lands. A failed finalize replaces this receipt with an in-card\n   * retry state so the reporter stays in the conversation they used.\n   */\n  private showDone(): void {\n    this.state = 'done'\n    this.hideSkip()\n    this.setComposerDisabled(true)\n    this.renderSuccess()\n    this.finalizeInBackground()\n  }\n\n  /**\n   * The success view takes over everything under the header — the transcript,\n   * the quick replies and the composer are all gone, so the card reads as a\n   * receipt rather than a conversation the reporter could keep typing into.\n   */\n  private renderSuccess(): void {\n    const m = this.i18n.success\n    this.messagesEl.remove()\n    this.quickRepliesEl.remove()\n    this.composerEl.remove()\n\n    const view = this.el.querySelector<HTMLDivElement>('.mtb-chat-success') ?? document.createElement('div')\n    view.className = 'mtb-chat-success mtb-chat-success-simple'\n    view.replaceChildren()\n\n    const icon = document.createElement('div')\n    icon.className = 'mtb-chat-success-icon'\n    icon.innerHTML = CHECK_ICON\n    view.appendChild(icon)\n\n    const title = document.createElement('div')\n    title.className = 'mtb-chat-success-title'\n    title.textContent = this.session?.ai_clarify_available === false ? m.title_no_ai : m.title\n    view.appendChild(title)\n\n    const link = document.createElement('a')\n    link.className = 'mtb-chat-success-link mtb-chat-success-link-pending'\n    link.href = '#'\n    link.textContent = m.track_feedback\n    link.addEventListener('click', (e: MouseEvent) => e.preventDefault())\n    view.appendChild(link)\n\n    if (!view.parentElement) this.el.appendChild(view)\n    this.attachHandoff()\n  }\n\n  /** Keep a failed background finalize in the conversation the reporter used. */\n  showFinalizeFailure(onRetry?: () => void): void {\n    this.clearAutoCloseTimer()\n\n    let view = this.el.querySelector<HTMLDivElement>('.mtb-chat-success')\n    if (!view) {\n      this.messagesEl.remove()\n      this.quickRepliesEl.remove()\n      this.composerEl.remove()\n      view = document.createElement('div')\n      this.el.appendChild(view)\n    }\n\n    view.className = 'mtb-chat-success mtb-chat-finalize-failure'\n    view.innerHTML = `\n      <div class=\"mtb-chat-success-icon mtb-chat-failure-icon\">!</div>\n      <div class=\"mtb-chat-success-title\">${escapeHtml(this.i18n.error.finalize_title)}</div>\n      <div class=\"mtb-chat-finalize-message\">${escapeHtml(this.i18n.error.finalize_message)}</div>\n      ${onRetry ? `<button class=\"mtb-chat-finalize-retry\" type=\"button\">${escapeHtml(this.i18n.clarify.retry)}</button>` : ''}\n      <button class=\"mtb-chat-finalize-close\" type=\"button\">${escapeHtml(this.i18n.success.close)}</button>\n    `\n\n    if (onRetry) {\n      view.querySelector<HTMLButtonElement>('.mtb-chat-finalize-retry')!.addEventListener('click', () => {\n        const retry = view.querySelector<HTMLButtonElement>('.mtb-chat-finalize-retry')!\n        retry.disabled = true\n        retry.innerHTML = '<span class=\"mtb-chat-finalize-spinner\" aria-label=\"Retrying\"></span>'\n        onRetry()\n      })\n    }\n    view.querySelector<HTMLButtonElement>('.mtb-chat-finalize-close')!.addEventListener('click', () => this.opts.onCancel())\n  }\n\n  /** Restore the receipt after a retry reaches the final endpoint. */\n  showFinalizeSuccess(): void {\n    this.state = 'done'\n    this.finalized = true\n    this.renderSuccess()\n  }\n\n  /**\n   * The board link and the email form are both built from the hand-off that\n   * finalize returns, but the receipt renders the moment the reporter is done\n   * rather than waiting on that round trip — so this runs twice: once against\n   * whatever is already known, and again when finalize lands.\n   */\n  private attachHandoff(): void {\n    const view = this.el.querySelector<HTMLDivElement>('.mtb-chat-success')\n    if (!view) return\n    const m = this.i18n.success\n\n    const openBoard = this.opts.resolveBoardLink?.()\n    const pendingLink = view.querySelector<HTMLAnchorElement>('.mtb-chat-success-link-pending')\n    if (openBoard && pendingLink) {\n      pendingLink.classList.remove('mtb-chat-success-link-pending')\n      pendingLink.addEventListener('click', (e: MouseEvent) => {\n        e.preventDefault()\n        openBoard()\n      })\n    }\n\n    const emailCapture = this.opts.resolveEmailCapture?.()\n    if (emailCapture && !view.querySelector('.mtb-chat-email-card')) {\n      view.classList.remove('mtb-chat-success-simple')\n      view.appendChild(this.buildEmailCard(emailCapture))\n    }\n\n    if (this.finalized && !this.autoCloseTimer) this.armAutoCloseTimer()\n  }\n\n  private finalized = false\n\n  private finalizeInBackground(): void {\n    if (this.destroyed) return\n    Promise.resolve(this.opts.onFinalize())\n      .then(() => {\n        if (!this.destroyed) this.attachHandoff()\n      })\n      .catch(() => {})\n  }\n\n  private buildEmailCard(capture: { onSubmit: (email: string) => Promise<boolean> }): HTMLDivElement {\n    const m = this.i18n.success\n    const card = document.createElement('div')\n    card.className = 'mtb-chat-email-card'\n\n    const row = document.createElement('div')\n    row.className = 'mtb-chat-email-row'\n\n    const field = document.createElement('div')\n    field.className = 'mtb-chat-email-field'\n    const input = document.createElement('input')\n    input.className = 'mtb-chat-email-input'\n    input.type = 'email'\n    input.placeholder = m.email_cta\n    field.appendChild(input)\n\n    const btn = document.createElement('button')\n    btn.className = 'mtb-chat-email-submit'\n    btn.type = 'button'\n    btn.textContent = m.notify\n    btn.disabled = true\n\n    const submitEmail = async () => {\n      const email = input.value.trim()\n      if (!email) return\n      this.clearAutoCloseTimer()\n      btn.disabled = true\n      input.disabled = true\n      const saved = await capture.onSubmit(email)\n      if (this.destroyed) return\n      if (saved) {\n        const note = document.createElement('div')\n        note.className = 'mtb-chat-email-note'\n        note.textContent = m.email_saved\n        card.replaceChildren(note)\n        this.armAutoCloseTimer()\n        return\n      }\n      input.disabled = false\n      btn.disabled = false\n      const error = card.querySelector('.mtb-chat-email-error') ?? document.createElement('div')\n      error.className = 'mtb-chat-email-error'\n      error.textContent = m.email_error\n      card.appendChild(error)\n    }\n\n    input.addEventListener('focus', () => this.clearAutoCloseTimer())\n    input.addEventListener('input', () => {\n      this.clearAutoCloseTimer()\n      btn.disabled = !input.value.trim()\n    })\n    input.addEventListener('keydown', (e: KeyboardEvent) => {\n      if (e.key === 'Enter' && input.value.trim()) void submitEmail()\n    })\n    btn.addEventListener('click', () => void submitEmail())\n\n    row.appendChild(field)\n    row.appendChild(btn)\n    card.appendChild(row)\n    return card\n  }\n\n  private appendUserBubble(text: string): void {\n    const bubble = document.createElement('div')\n    bubble.className = 'mtb-chat-bubble mtb-chat-bubble-user'\n    bubble.textContent = text\n    this.messagesEl.appendChild(bubble)\n    this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n    this.inputEl.value = ''\n    this.sendBtn.disabled = true\n  }\n\n  private streamingBubbleEl: HTMLDivElement | null = null\n\n  private startStreamingBubble(): void {\n    const row = document.createElement('div')\n    row.className = 'mtb-chat-ai-row'\n    row.innerHTML = `<div class=\"mtb-chat-avatar\">${BRAND_ICON}</div><div class=\"mtb-chat-bubble mtb-chat-bubble-ai\"></div>`\n    this.messagesEl.appendChild(row)\n    this.streamingBubbleEl = row.querySelector('.mtb-chat-bubble-ai')!\n  }\n\n  private appendStreamDelta(text: string): void {\n    if (!this.streamingBubbleEl) return\n    this.streamingBubbleEl.textContent = (this.streamingBubbleEl.textContent ?? '') + text\n    this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n  }\n\n  private endStreamingBubble(): void {\n    this.streamingBubbleEl = null\n  }\n\n  private appendAiBubble(text: string): void {\n    const row = document.createElement('div')\n    row.className = 'mtb-chat-ai-row'\n    row.innerHTML = `<div class=\"mtb-chat-avatar\">${BRAND_ICON}</div><div class=\"mtb-chat-bubble mtb-chat-bubble-ai\"></div>`\n    row.querySelector('.mtb-chat-bubble-ai')!.textContent = text\n    this.messagesEl.appendChild(row)\n    this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n  }\n\n  private renderSuggestions(suggestions: string[] | undefined): void {\n    this.clearSuggestions()\n    if (!suggestions?.length) return\n\n    const row = document.createElement('div')\n    row.className = 'mtb-chat-suggestions'\n    for (const suggestion of suggestions) {\n      const pill = document.createElement('button')\n      pill.className = 'mtb-chat-sg'\n      pill.type = 'button'\n      pill.textContent = suggestion\n      pill.addEventListener('click', () => {\n        this.inputEl.value = suggestion\n        this.sendBtn.disabled = false\n        this.inputEl.focus({ preventScroll: true })\n      })\n      row.appendChild(pill)\n    }\n    this.messagesEl.appendChild(row)\n    this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n  }\n\n  private clearSuggestions(): void {\n    this.messagesEl.querySelector('.mtb-chat-suggestions')?.remove()\n  }\n\n  private showThinking(): void {\n    if (this.messagesEl.querySelector('.mtb-chat-thinking')) return\n    const row = document.createElement('div')\n    row.className = 'mtb-chat-ai-row mtb-chat-thinking'\n    row.innerHTML = `<div class=\"mtb-chat-avatar\">${BRAND_ICON}</div><div class=\"mtb-chat-dots\"><span></span><span></span><span></span></div>`\n    this.messagesEl.appendChild(row)\n    this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n  }\n\n  private removeThinking(): void {\n    this.messagesEl.querySelector('.mtb-chat-thinking')?.remove()\n  }\n\n  private showRetryableError(text: string, onRetry: () => void): void {\n    this.clearError()\n    const row = document.createElement('div')\n    row.className = 'mtb-chat-error-row'\n    const msg = document.createElement('span')\n    msg.className = 'mtb-chat-error-text'\n    msg.textContent = text\n    const retry = document.createElement('button')\n    retry.className = 'mtb-chat-error-retry'\n    retry.type = 'button'\n    retry.textContent = this.i18n.clarify.retry\n    retry.addEventListener('click', onRetry)\n    row.appendChild(msg)\n    row.appendChild(retry)\n    this.messagesEl.appendChild(row)\n    this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n  }\n\n  private clearError(): void {\n    this.messagesEl.querySelectorAll('.mtb-chat-error-row').forEach(el => el.remove())\n  }\n\n  private showSkip(): void {\n    if (this.actionsEl.querySelector('.mtb-chat-skip')) return\n    const skip = document.createElement('button')\n    skip.className = 'mtb-chat-skip'\n    skip.type = 'button'\n    skip.title = this.i18n.clarify.skip\n    skip.textContent = this.i18n.clarify.skip_short\n    skip.addEventListener('click', () => this.skipAndSend())\n    this.actionsEl.insertBefore(skip, this.sendBtn)\n  }\n\n  private skipAndSend(): void {\n    this.hideSkip()\n    this.state = 'done'\n    this.setComposerDisabled(true)\n    this.renderSuccess()\n    this.finalizeInBackground()\n  }\n\n  private hideSkip(): void {\n    this.actionsEl?.querySelector('.mtb-chat-skip')?.remove()\n  }\n\n  private hideCapture(): void {\n    this.actionsEl.querySelector('.mtb-chat-screenshot')?.remove()\n    this.actionsEl.querySelector('.mtb-chat-attachment')?.remove()\n  }\n\n  private setComposerDisabled(disabled: boolean): void {\n    this.inputEl.disabled = disabled\n    this.sendBtn.disabled = disabled\n    this.composerEl.classList.toggle('mtb-chat-composer-disabled', disabled)\n  }\n\n  private positionCard(x: number, y: number, targetRect: TargetRect | undefined, tabPosition: 'left' | 'right'): void {\n    const frame = measureViewportFrame(tabPosition)\n    const { vw, vh, minLeft, maxRight } = frame\n    const margin = VIEWPORT_MARGIN\n    const gap = 8\n    const cardW = panelWidth(CARD_WIDTH, frame)\n    const measured = this.el.offsetHeight || 280\n    const cardH = measured + 80\n\n    let left: number\n    let top: number\n\n    const anchorIsInformative =\n      targetRect && targetRect.width < vw * 0.6 && targetRect.height < vh * 0.6\n\n    if (targetRect && anchorIsInformative) {\n      left = targetRect.left\n      top = targetRect.bottom + gap\n      if (top + cardH > vh - margin) top = targetRect.top - cardH - gap\n      if (top < margin) top = Math.min(targetRect.bottom + gap, vh - cardH - margin)\n    } else {\n      left = x + 12\n      top = y + 12\n    }\n\n    if (left + cardW > maxRight) left = maxRight - cardW\n    if (left < minLeft) left = minLeft\n    if (top < margin) top = margin\n\n    if (top + cardH > vh - margin) top = vh - cardH - margin\n    if (top < margin) top = margin\n\n    this.el.style.left = `${left}px`\n    this.el.style.top = `${top}px`\n    this.el.style.maxHeight = `${vh - top - margin}px`\n  }\n\n  submit(text: string): void {\n    void this.sendMessage(text)\n  }\n\n  /** Which submission this card holds — null while the reporter is still typing. */\n  sessionId(): string | null {\n    return this.session?.id ?? null\n  }\n\n  destroy(): void {\n    if (this.destroyed) return\n    this.destroyed = true\n    this.clarifyAbort?.abort()\n    this.clearAutoCloseTimer()\n    window.removeEventListener('keydown', this.handleEscape)\n    this.sheet.release()\n    this.focusTrap.release()\n    this.el.remove()\n    this.focusTrap.restoreFocus()\n  }\n\n  private armAutoCloseTimer(): void {\n    this.clearAutoCloseTimer()\n    this.autoCloseTimer = setTimeout(() => this.destroy(), AUTO_CLOSE_MS)\n  }\n\n  private clearAutoCloseTimer(): void {\n    if (!this.autoCloseTimer) return\n    clearTimeout(this.autoCloseTimer)\n    this.autoCloseTimer = null\n  }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { trapFocus, type FocusTrap } from './focus-trap'\n\nconst AUTO_CLOSE_MS = 5000\n\nconst CHECK_ICON = '<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M8.1 14.6L3.8 10.3l1.5-1.5 2.8 2.8 6.6-6.6 1.5 1.5z\"/></svg>'\n\nexport interface ReceiptCardOptions {\n  skipFollowup?: boolean\n  onViewFeedback?: () => void\n}\n\n/**\n * The standalone receipt for a submission whose chat card is already gone —\n * today that is one path: a background finalize failed, its failure card was\n * dismissed by the retry, and the retry then succeeded. Renders with the chat\n * card's own receipt classes so the two ways of saying \"sent\" stay one design.\n */\nexport class ReceiptCard {\n  private el: HTMLDivElement\n  private focusTrap: FocusTrap\n  private autoCloseTimer: ReturnType<typeof setTimeout>\n\n  constructor(shadow: ShadowContainer, messages: I18nMessages, onClose: () => void, options?: ReceiptCardOptions) {\n    const m = messages.success\n    const title = options?.skipFollowup ? m.title_no_ai : m.title\n\n    this.el = shadow.el<HTMLDivElement>('div', 'mtb-chat mtb-chat-receipt')\n    this.el.setAttribute('role', 'dialog')\n    this.el.setAttribute('aria-modal', 'true')\n    this.el.setAttribute('aria-label', title)\n\n    const view = document.createElement('div')\n    view.className = 'mtb-chat-success mtb-chat-success-simple'\n\n    const icon = document.createElement('div')\n    icon.className = 'mtb-chat-success-icon'\n    icon.innerHTML = CHECK_ICON\n    view.appendChild(icon)\n\n    const titleEl = document.createElement('div')\n    titleEl.className = 'mtb-chat-success-title'\n    titleEl.textContent = title\n    view.appendChild(titleEl)\n\n    if (options?.onViewFeedback) {\n      const link = document.createElement('a')\n      link.className = 'mtb-chat-success-link'\n      link.href = '#'\n      link.textContent = m.track_feedback\n      link.addEventListener('click', (e: MouseEvent) => {\n        e.preventDefault()\n        options.onViewFeedback!()\n      })\n      view.appendChild(link)\n    }\n\n    this.el.appendChild(view)\n    shadow.append(this.el)\n    this.focusTrap = trapFocus(this.el)\n    this.autoCloseTimer = setTimeout(onClose, AUTO_CLOSE_MS)\n  }\n\n  destroy(): void {\n    clearTimeout(this.autoCloseTimer)\n    this.focusTrap.release()\n    this.el.remove()\n    this.focusTrap.restoreFocus()\n  }\n}\n","export const FILTERED_VALUE = '[Filtered]'\n\nconst PRIVATE_KEY_PATTERN = /-----BEGIN ((?:[A-Z0-9]+ )*PRIVATE KEY)-----[\\s\\S]*?-----END \\1-----/g\nconst JWT_PATTERN = /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\b/g\nconst API_KEY_PATTERNS = [\n  /\\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\\b/g,\n  /\\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{20,}\\b/g,\n  /\\bgh[pousr]_[A-Za-z0-9]{30,}\\b/g,\n  /\\bAKIA[0-9A-Z]{16}\\b/g,\n  /\\bxox[baprs]-[A-Za-z0-9-]{20,}\\b/g,\n]\nconst CARD_CANDIDATE_PATTERN = /\\b(?:\\d[ -]?){12,18}\\d\\b/g\nconst HTTP_URL_PATTERN = /\\bhttps?:\\/\\/[^\\s<>\"']+/gi\nconst FIELD_SEMANTIC_KEYS = new Set(['type', 'autocomplete', 'name', 'id', 'aria_label', 'placeholder'])\nconst VALUE_ATTRIBUTE_KEYS = new Set(['value', 'content'])\nconst SENSITIVE_INPUT_TYPES = new Set(['password', 'hidden', 'file'])\nconst SENSITIVE_AUTOCOMPLETE = new Set([\n  'current_password',\n  'new_password',\n  'one_time_code',\n  'cc_number',\n  'cc_csc',\n  'cc_exp',\n  'cc_exp_month',\n  'cc_exp_year',\n])\nconst SENSITIVE_KEY_PATTERN = /(?:^|_)(?:password|passwd|passcode|card_(?:number|no)(?:_[0-9]+)?|credit_card|cc_(?:number|num|csc|cvv|cvc|exp|expiry)|cvv|cvc|csc|security_code|one_time_(?:code|password)|otp(?:_code)?|verification_code|auth(?:entication)?_token|access_token|refresh_token|id_token|token|api_key|client_secret|secret|authorization|cookie|set_cookie|credential|private_key|hidden_value|file_name|filename)(?:$|_(?:value|field|input|confirmation|confirm|header|data|pem)$)/\n\nexport function filterSensitiveText(value: string): string {\n  let filtered = value\n    .replace(PRIVATE_KEY_PATTERN, FILTERED_VALUE)\n    .replace(JWT_PATTERN, FILTERED_VALUE)\n\n  for (const pattern of API_KEY_PATTERNS) {\n    filtered = filtered.replace(pattern, FILTERED_VALUE)\n  }\n\n  filtered = filtered.replace(CARD_CANDIDATE_PATTERN, candidate => (\n    passesLuhn(candidate) ? FILTERED_VALUE : candidate\n  ))\n\n  return filtered.replace(HTTP_URL_PATTERN, candidate => sanitizeUrl(candidate) || FILTERED_VALUE)\n}\n\nexport function sanitizeUrl(value: string): string {\n  try {\n    const url = new URL(value)\n    return `${url.origin}${url.pathname}`\n  } catch {\n    return ''\n  }\n}\n\nexport function filterSensitiveValue<T>(value: T, key?: string): T {\n  if (key && isSensitiveKey(key) && value != null) return FILTERED_VALUE as T\n  if (typeof value === 'string') return filterSensitiveText(value) as T\n  if (Array.isArray(value)) return value.map(item => filterSensitiveValue(item)) as T\n  if (value && typeof value === 'object') {\n    const sensitiveField = isSensitiveFieldAttributes(value as Record<string, unknown>)\n    const filtered = Object.fromEntries(\n      Object.entries(value).map(([nestedKey, item]) => {\n        const filteredItem = sensitiveField && VALUE_ATTRIBUTE_KEYS.has(normalizeIdentifier(nestedKey)) && item != null\n          ? FILTERED_VALUE\n          : filterSensitiveValue(item, nestedKey)\n        return [filterSensitiveText(nestedKey), filteredItem]\n      }),\n    )\n    return filtered as T\n  }\n  return value\n}\n\nexport function isSensitiveKey(value: string): boolean {\n  return SENSITIVE_KEY_PATTERN.test(normalizeIdentifier(value))\n}\n\nexport function isSensitiveFieldAttributes(attributes: Record<string, unknown>): boolean {\n  return Object.entries(attributes).some(([key, value]) => {\n    if (typeof value !== 'string') return false\n\n    const normalizedKey = normalizeIdentifier(key)\n    if (!FIELD_SEMANTIC_KEYS.has(normalizedKey)) return false\n    if (normalizedKey === 'type') return SENSITIVE_INPUT_TYPES.has(normalizeIdentifier(value))\n    if (normalizedKey === 'autocomplete') {\n      return value.split(/\\s+/).some(token => SENSITIVE_AUTOCOMPLETE.has(normalizeIdentifier(token)))\n    }\n    return isSensitiveKey(value)\n  })\n}\n\nfunction normalizeIdentifier(value: string): string {\n  return value\n    .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '_')\n    .replace(/^_+|_+$/g, '')\n}\n\nfunction passesLuhn(candidate: string): boolean {\n  const digits = candidate.replace(/\\D/g, '')\n  if (digits.length < 13 || digits.length > 19) return false\n\n  let sum = 0\n  let doubleDigit = false\n  for (let index = digits.length - 1; index >= 0; index -= 1) {\n    let digit = Number(digits[index])\n    if (doubleDigit) {\n      digit *= 2\n      if (digit > 9) digit -= 9\n    }\n    sum += digit\n    doubleDigit = !doubleDigit\n  }\n  return sum % 10 === 0\n}\n","import { FILTERED_VALUE, filterSensitiveText, isSensitiveFieldAttributes } from './sanitize'\n\nexport function isSensitiveField(element: Element): boolean {\n  return isSensitiveFieldAttributes({\n    type: element.getAttribute('type'),\n    autocomplete: element.getAttribute('autocomplete'),\n    name: element.getAttribute('name'),\n    id: element.id,\n    'aria-label': element.getAttribute('aria-label'),\n    placeholder: element.getAttribute('placeholder'),\n  })\n}\n\nexport function filterInputValue(value: string, element: Element): string {\n  if (!value) return value\n  if (isSensitiveField(element)) return FILTERED_VALUE\n  return filterSensitiveText(value)\n}\n","import { filterSensitiveText } from './sanitize'\n\nconst MAX_GROUPED_TEXT_LENGTH = 512\ntype SensitiveTextState = 'none' | 'sensitive' | 'contains-sensitive-child' | 'too-large'\nexport type SensitiveTextContext = WeakMap<Element, SensitiveTextState>\n\nexport function createSensitiveTextContext(): SensitiveTextContext {\n  return new WeakMap()\n}\n\nexport function hasSensitiveTextContext(node: Node, context: SensitiveTextContext): boolean {\n  let element = node.parentElement\n  while (element && element !== document.body && element !== document.documentElement) {\n    const state = sensitiveTextState(element, context)\n    if (state === 'sensitive') return true\n    if (state === 'contains-sensitive-child' || state === 'too-large') return false\n    element = element.parentElement\n  }\n  return false\n}\n\nfunction sensitiveTextState(element: Element, context: SensitiveTextContext): SensitiveTextState {\n  const cached = context.get(element)\n  if (cached) return cached\n\n  const text = element.textContent ?? ''\n  let state: SensitiveTextState = 'none'\n  if (text.length > MAX_GROUPED_TEXT_LENGTH) {\n    state = 'too-large'\n  } else if (filterSensitiveText(text) !== text) {\n    const childContainsSensitiveText = Array.from(element.children).some((child) => {\n      const childState = sensitiveTextState(child, context)\n      return childState === 'sensitive' || childState === 'contains-sensitive-child'\n    })\n    state = childContainsSensitiveText ? 'contains-sensitive-child' : 'sensitive'\n  }\n  context.set(element, state)\n  return state\n}\n","export function loadImage(dataUrl: string): Promise<HTMLImageElement | null> {\n  return new Promise(resolve => {\n    const image = new Image()\n    image.onload = () => resolve(image)\n    image.onerror = () => resolve(null)\n    image.src = dataUrl\n  })\n}\n","// The draw layer only ever emits M/L polyline commands (see buildPathD in\n// annotate/draw.ts); anything else in the string is ignored.\nexport function parsePathPolylines(drawPath: string): Array<Array<[number, number]>> {\n  const polylines: Array<Array<[number, number]>> = []\n  let current: Array<[number, number]> | null = null\n  const tokens = drawPath.match(/[ML]\\s*[-\\d.]+\\s+[-\\d.]+/g) ?? []\n  for (const token of tokens) {\n    const [x, y] = token.slice(1).trim().split(/\\s+/).map(Number)\n    if (Number.isNaN(x) || Number.isNaN(y)) continue\n    if (token[0] === 'M') {\n      current = [[x, y]]\n      polylines.push(current)\n    } else if (current) {\n      current.push([x, y])\n    }\n  }\n  return polylines\n}\n\n// Rebuilt from the parsed polylines so this module stays the single reader of\n// the path grammar. Tokens the parser ignores are dropped, which is safe\n// because every consumer reads the translated path through that same parser.\nexport function translatePathD(drawPath: string, dx: number, dy: number): string {\n  return parsePathPolylines(drawPath)\n    .map(points =>\n      points.map(([x, y], index) => `${index === 0 ? 'M' : 'L'} ${x + dx} ${y + dy}`).join(' '),\n    )\n    .join(' ')\n}\n","import type { Annotation, WidgetBrandColors } from '../types'\nimport { BLOCK_CLASS, MASK_TEXT_CLASS } from '../privacy/dom'\nimport { WIDGET_HOST_ID } from '../context/dom-utils'\nimport { filterInputValue, isSensitiveField } from '../privacy/fields'\nimport { filterSensitiveText } from '../privacy/sanitize'\nimport { createSensitiveTextContext, hasSensitiveTextContext } from '../privacy/text-context'\nimport { BRAND_COLOR } from '../styles'\nimport { anchorScreenshotOffset, captureTransform, nestedScrollOffset, parentElement, rectToCanvas, toCanvasX, toCanvasY, viewportCaptureRect } from './geometry'\nimport type { CaptureTransform, Rect, ScrollOffset } from './geometry'\nimport { loadImage } from './image'\nimport { parsePathPolylines, translatePathD } from './svg-path'\n\n// Capture at CSS pixels (pixelRatio 1) as JPEG to keep the upload small: on\n// retina screens the default devicePixelRatio would quadruple the pixel count,\n// and the model downscales past ~1568px anyway. The capture covers only the\n// viewport; capturePixelRatio stays as a guard for a viewport that would ever\n// exceed the browser's canvas ceiling.\n//\n// Annotations are baked onto the captured bitmap with canvas 2D afterwards —\n// never mounted into the live page — so the capture leaves the page visually\n// untouched no matter how long the DOM snapshot takes.\nexport interface BaseScreenshot {\n  dataUrl: string\n  metrics: Rect\n  redactions: Rect[]\n  /**\n   * Where the page was scrolled when the bitmap was measured. The bake happens\n   * later — the reporter is still typing — so it cannot read this itself, and\n   * annotations that carry their own anchor need it to come back to this\n   * capture's frame.\n   */\n  pageScroll: ScrollOffset\n}\n\n// html-to-image is the largest dependency in the bundle, and most sessions\n// never open the widget, let alone capture a screenshot. Loading it lazily\n// keeps it out of the first-injection payload for ESM/CJS consumers (the IIFE\n// build inlines the import back into its single file). warmFontEmbedCss is the\n// first caller on every capture path, so the popup-open warm-up that prefetches\n// the font CSS also prefetches this module and submit latency does not regress.\ntype HtmlToImageModule = typeof import('html-to-image')\n\nlet htmlToImageLoaded: Promise<HtmlToImageModule> | null = null\n\nfunction loadHtmlToImage(): Promise<HtmlToImageModule> {\n  if (htmlToImageLoaded) return htmlToImageLoaded\n  // A rejection must not stay cached: one flaky-network failure would otherwise\n  // make every later capture fail instantly until the page reloads. Same\n  // reset-on-failure as record/session.ts loadRrweb.\n  htmlToImageLoaded = import('html-to-image').catch((err) => {\n    htmlToImageLoaded = null\n    throw err\n  })\n  return htmlToImageLoaded\n}\n\n// Embedding webfonts is the expensive half of a capture: html-to-image walks\n// every stylesheet and fetches each referenced font file — a hundred-odd\n// requests on a Google-Fonts page — on every capture. The font set does not\n// change while the page lives, so compute the embed CSS once and reuse it.\nlet fontEmbedCss: string | undefined\nlet fontEmbedCssRequest: Promise<string | undefined> | null = null\n\nexport function warmFontEmbedCss(): Promise<string | undefined> {\n  if (fontEmbedCss !== undefined) return Promise.resolve(fontEmbedCss)\n  // On failure fall back to undefined so toJpeg embeds fonts itself, and allow\n  // a later capture to try warming again.\n  fontEmbedCssRequest ??= loadHtmlToImage()\n    .then(({ getFontEmbedCSS }) => getFontEmbedCSS(document.body))\n    .then((css) => {\n      fontEmbedCss = css\n      return css\n    })\n    .catch(() => {\n      fontEmbedCssRequest = null\n      return undefined\n    })\n  return fontEmbedCssRequest\n}\n\n// The rasterized page without annotations. Annotations are baked afterwards\n// from data, so this half can run early — during a pause in the reporter's\n// typing — and the submit path only pays for the cheap bake.\n//\n// Everything below this margin past the viewport is culled from the clone and\n// never rasterized. A full-page capture rasterized a tall page — sometimes 10x\n// the pixels and DOM nodes — only for the bake to crop it back to roughly a\n// viewport, and that wasted work is what froze typing on heavy pages.\n//\n// Only content BELOW the viewport can go: removing in-flow content above it\n// collapses the clone's layout upward and breaks the scroll alignment the\n// transform relies on (verified: culling above + translate rendered an\n// entirely blank capture).\nconst CULL_MARGIN_PX = 200\n// The browser cannot read cross-origin images whose server omits CORS headers.\n// Replace only that resource in html-to-image's clone so one inaccessible image\n// cannot discard the rest of the privacy-filtered screenshot.\nconst TRANSPARENT_IMAGE_PLACEHOLDER = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'\n\nexport type ScreenshotResourceUrlResolver = (\n  resourceUrl: string,\n  contentType?: string,\n) => string | undefined | Promise<string | undefined>\n\nexport async function captureBaseScreenshot(\n  resourceUrlResolver?: ScreenshotResourceUrlResolver,\n  signal?: AbortSignal,\n): Promise<BaseScreenshot | null> {\n  try {\n    if (signal?.aborted) return null\n    // The module load rides alongside the warm-up (which itself needs the\n    // module); a load failure rejects here and fails the capture closed to\n    // null, same as any other capture error.\n    const [{ toJpeg }, fontEmbedCSS] = await Promise.all([loadHtmlToImage(), warmFontEmbedCss()])\n    if (signal?.aborted) return null\n    // Measure only after the font warm-up settles: a cold warm-up is long\n    // enough (a hundred-odd requests) for late-arriving fonts to reflow the\n    // page, and a cover measured before that shift lands on the wrong pixels\n    // with no fail-closed signal.\n    const bodyRect = document.body.getBoundingClientRect()\n    const metrics = viewportCaptureRect()\n    const pageScroll = { x: window.scrollX, y: window.scrollY }\n    const redactions = collectPrivacyRedactions(document.body)\n    const dataUrl = await toJpeg(document.body, {\n      quality: 0.85,\n      imagePlaceholder: TRANSPARENT_IMAGE_PLACEHOLDER,\n      resourceUrlResolver,\n      fetchRequestInit: signal ? { signal } : undefined,\n      width: metrics.width,\n      height: metrics.height,\n      pixelRatio: capturePixelRatio(metrics),\n      backgroundColor: '#ffffff',\n      skipFonts: false,\n      skipAutoScale: false,\n      fontEmbedCSS,\n      style: {\n        // The clone lays out from the document top; shifting it by the body's\n        // current viewport offset puts the scrolled-to region at the canvas\n        // origin, so live viewport measurements map onto the bitmap 1:1.\n        transform: `translate(${bodyRect.left}px, ${bodyRect.top}px)`,\n        transformOrigin: 'top left',\n      },\n      filter: (node) => {\n        if (node instanceof HTMLElement && node.id === WIDGET_HOST_ID) return false\n        if (!(node instanceof Element)) return true\n        const rect = node.getBoundingClientRect()\n        // Zero-sized nodes carry no pixels but often carry styles or heads of\n        // rendered subtrees (style/script/positioned wrappers) — keep them.\n        if (rect.width === 0 && rect.height === 0) return true\n        // The clone walk is asynchronous, so the page can scroll while it\n        // runs — and a live rect.top is measured against wherever the page is\n        // by then. Re-expressing it against the scroll this capture was framed\n        // at keeps the cull decision stable however late the walk reads it.\n        const topInCapture = rect.top + window.scrollY - pageScroll.y\n        return topInCapture <= metrics.height + CULL_MARGIN_PX\n      },\n    })\n    if (signal?.aborted) return null\n    return { dataUrl, metrics, redactions, pageScroll }\n  } catch {\n    return null\n  }\n}\n\nexport async function bakeAnnotatedScreenshot(\n  base: BaseScreenshot,\n  annotations: Annotation[] = [],\n  brandColors?: WidgetBrandColors,\n): Promise<Blob | null> {\n  try {\n    const screenshotAnnotations = annotations.map(\n      annotation => translateAnnotationForScreenshot(annotation, base.pageScroll),\n    )\n    return await bakeScreenshot(base.dataUrl, screenshotAnnotations, base.metrics, base.redactions, brandColors)\n  } catch {\n    return null\n  }\n}\n\nexport async function captureScreenshot(\n  annotations: Annotation[] = [],\n  brandColors?: WidgetBrandColors,\n): Promise<Blob | null> {\n  const base = await captureBaseScreenshot()\n  if (!base) return null\n  return bakeAnnotatedScreenshot(base, annotations, brandColors)\n}\n\n// html-to-image clones an element's children at their unscrolled positions.\n// Annotations, however, are recorded in viewport coordinates. Translate the\n// screenshot-only copy so it remains attached to its target in that clone;\n// the original annotation is still sent to the API unchanged.\n//\n// The capture is one viewport — the one on screen at capture time, which the\n// prime-at-annotation flow makes the viewport the first annotation was made in.\n// An annotation whose anchor puts it outside that rect simply is not in the\n// shot: its strokes land off-canvas and the canvas clips them silently. That is\n// the honest picture (the marked content was not captured), and the wire\n// annotation data still carries the mark for the board.\nfunction translateAnnotationForScreenshot(annotation: Annotation, pageScroll: ScrollOffset): Annotation {\n  const offset = screenshotOffset(annotation, pageScroll)\n  if (offset.x === 0 && offset.y === 0) return annotation\n\n  return {\n    ...annotation,\n    x: annotation.x + offset.x,\n    y: annotation.y + offset.y,\n    targetRect: annotation.targetRect && {\n      ...annotation.targetRect,\n      top: annotation.targetRect.top + offset.y,\n      left: annotation.targetRect.left + offset.x,\n      bottom: annotation.targetRect.bottom + offset.y,\n    },\n    drawPath: annotation.drawPath && translatePathD(annotation.drawPath, offset.x, offset.y),\n  }\n}\n\n/**\n * How far to move an annotation to land where the capture rendered its target.\n *\n * An annotation that recorded its own anchor already knows where it belongs in\n * clone space, so all that is left is coming back to the frame this capture was\n * measured in — no live DOM read, and therefore nothing that a scroll between\n * the annotation and the capture can spoil.\n *\n * Without that anchor (an older client, or replayed data) the only thing left to\n * do is measure the target now and hope nothing moved, which is what every\n * annotation used to do.\n */\nfunction screenshotOffset(annotation: Annotation, pageScroll: ScrollOffset): ScrollOffset {\n  if (annotation.captureOffsetX !== undefined && annotation.captureOffsetY !== undefined) {\n    return anchorScreenshotOffset(\n      { x: annotation.captureOffsetX, y: annotation.captureOffsetY },\n      pageScroll,\n    )\n  }\n\n  return nestedScrollOffset(findAnnotationTarget(annotation))\n}\n\nfunction findAnnotationTarget(annotation: Annotation): Element | null {\n  if (annotation.targetSelector) {\n    try {\n      const target = document.querySelector(annotation.targetSelector)\n      if (target) return target\n    } catch {\n      // Selectors are captured from the page, so a later DOM change can make\n      // one invalid. Fall back to the element beneath the annotation instead.\n    }\n  }\n\n  return document.elementsFromPoint?.(annotation.x, annotation.y)[0] ?? null\n}\n\nasync function bakeScreenshot(\n  dataUrl: string,\n  annotations: Annotation[],\n  captureMetrics: Rect,\n  redactions: Rect[],\n  brandColors?: WidgetBrandColors,\n): Promise<Blob | null> {\n  const canvas = document.createElement('canvas')\n  const ctx = canvas.getContext('2d')\n  if (!ctx) return null\n\n  const image = await loadImage(dataUrl)\n  if (!image) return null\n\n  canvas.width = image.naturalWidth || window.innerWidth\n  canvas.height = image.naturalHeight || window.innerHeight\n  ctx.drawImage(image, 0, 0)\n  const transform = captureTransform(captureMetrics, canvas.width, canvas.height)\n\n  ctx.fillStyle = '#e5e7eb'\n  for (const rect of redactions) {\n    const cover = rectToCanvas(transform, rect)\n    ctx.fillRect(cover.left, cover.top, cover.width, cover.height)\n  }\n\n  for (const annotation of annotations) {\n    if (annotation.type === 'draw' && annotation.drawPath) {\n      drawStroke(ctx, annotation.drawPath, transform, brandColors?.primary)\n    } else if (annotation.type === 'pin') {\n      drawPin(\n        ctx,\n        toCanvasX(transform, annotation.x),\n        toCanvasY(transform, annotation.y),\n        brandColors,\n      )\n    }\n  }\n\n  // No crop step: the capture rect is the viewport, which is exactly the\n  // deliverable. Cropping this bitmap could only ever remove captured pixels —\n  // and cropping content above the visible band was the bug class that shifted\n  // the remaining content up and blanked the shot.\n  return canvasToJpegBlob(canvas)\n}\n\nfunction collectPrivacyRedactions(root: HTMLElement): Rect[] {\n  const redactions: Rect[] = []\n  for (const element of elementsIncludingRoot(root, `.${BLOCK_CLASS}`)) {\n    addRedaction(redactions, element.getBoundingClientRect(), cloneScrollOffset(element))\n  }\n\n  for (const element of root.querySelectorAll('input, textarea, select')) {\n    if (isWidgetOrBlocked(element)) continue\n    const value = element instanceof HTMLInputElement\n      || element instanceof HTMLTextAreaElement\n      || element instanceof HTMLSelectElement\n      ? element.value\n      : ''\n    const isMasked = element.closest(`.${MASK_TEXT_CLASS}`) !== null\n    const hasSensitiveValue = isSensitiveField(element) || filterInputValue(value, element) !== value\n    if (!isMasked && !hasSensitiveValue) continue\n    addRedaction(redactions, element.getBoundingClientRect(), cloneScrollOffset(element), REDACTION_PADDING_PX)\n  }\n\n  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT)\n  const textContext = createSensitiveTextContext()\n  let node = walker.nextNode()\n  while (node) {\n    const parent = node.parentElement\n    const text = node.textContent ?? ''\n    const isMasked = parent?.closest(`.${MASK_TEXT_CLASS}`) !== null\n    const hasSensitiveText = filterSensitiveText(text) !== text || hasSensitiveTextContext(node, textContext)\n    if (parent && !isWidgetOrBlocked(parent) && (isMasked || hasSensitiveText)) {\n      // Text is the parent's content, so the parent's own scrollTop moves it\n      // too — start the offset walk at the parent itself, not its ancestors.\n      const offset = nestedScrollOffset(parent)\n      const range = document.createRange()\n      range.selectNodeContents(node)\n      for (const rect of Array.from(range.getClientRects())) {\n        addRedaction(redactions, rect, offset, REDACTION_PADDING_PX)\n      }\n    }\n    node = walker.nextNode()\n  }\n\n  return redactions\n}\n\n// html-to-image clones a scroller's children back at their unscrolled\n// positions, so a cover measured from the live viewport rect of an element\n// inside a scrolled container would land away from the secret in the export\n// and leave it visible — the same drift the annotation path compensates in\n// translateAnnotationForScreenshot. A border box moves with ancestor scroll\n// only (an element's own scrollTop shifts its content, not its box), so the\n// walk starts at the parent; page scroll stays out because nestedScrollOffset\n// stops at body, whose offset captureMetrics already carries.\nfunction cloneScrollOffset(element: Element): { x: number; y: number } {\n  return nestedScrollOffset(parentElement(element))\n}\n\nfunction elementsIncludingRoot(root: HTMLElement, selector: string): Element[] {\n  const descendants = Array.from(root.querySelectorAll(selector))\n  return root.matches(selector) ? [root, ...descendants] : descendants\n}\n\nfunction isWidgetOrBlocked(element: Element): boolean {\n  return element.closest(`#${WIDGET_HOST_ID}`) !== null || element.closest(`.${BLOCK_CLASS}`) !== null\n}\n\nfunction addRedaction(\n  redactions: Rect[],\n  rect: DOMRect,\n  offset: { x: number; y: number },\n  padding = 0,\n): void {\n  if (![rect.left, rect.top, rect.width, rect.height].every(Number.isFinite)) {\n    throw new Error('Invalid privacy redaction geometry')\n  }\n  if (rect.width <= 0 || rect.height <= 0) return\n  redactions.push({\n    left: rect.left + offset.x - padding,\n    top: rect.top + offset.y - padding,\n    width: rect.width + padding * 2,\n    height: rect.height + padding * 2,\n  })\n}\n\nfunction drawStroke(\n  ctx: CanvasRenderingContext2D,\n  drawPath: string,\n  transform: CaptureTransform,\n  color = BRAND_COLOR,\n): void {\n  ctx.save()\n  ctx.lineCap = 'round'\n  ctx.lineJoin = 'round'\n  for (const points of parsePathPolylines(drawPath)) {\n    if (points.length === 0) continue\n    ctx.beginPath()\n    ctx.moveTo(\n      toCanvasX(transform, points[0][0]),\n      toCanvasY(transform, points[0][1]),\n    )\n    for (let i = 1; i < points.length; i++) {\n      ctx.lineTo(\n        toCanvasX(transform, points[i][0]),\n        toCanvasY(transform, points[i][1]),\n      )\n    }\n    ctx.shadowColor = 'transparent'\n    ctx.strokeStyle = 'rgba(15, 23, 42, 0.72)'\n    ctx.lineWidth = 7\n    ctx.stroke()\n    ctx.strokeStyle = '#ffffff'\n    ctx.lineWidth = 5.5\n    ctx.stroke()\n    ctx.strokeStyle = color\n    ctx.lineWidth = 3.5\n    ctx.stroke()\n  }\n  ctx.restore()\n}\n\n// Safari refuses to back a canvas larger than roughly 16.7M pixels and hands\n// back a blank one instead, and the caller's catch turns that into a silent\n// screenshot-less submission. A viewport should never get near the ceiling,\n// but the guard costs nothing and keeps an absurd embedder viewport rendered\n// smaller instead of not at all; bakeScreenshot derives its scale from the\n// returned bitmap, so annotations follow along.\nconst MAX_CAPTURE_PIXELS = 16_000_000\nconst REDACTION_PADDING_PX = 2\n\nfunction capturePixelRatio(metrics: Rect): number {\n  const total = metrics.width * metrics.height\n  if (!Number.isFinite(total) || total <= MAX_CAPTURE_PIXELS) return 1\n  return Math.sqrt(MAX_CAPTURE_PIXELS / total)\n}\n\nfunction drawPin(\n  ctx: CanvasRenderingContext2D,\n  x: number,\n  y: number,\n  brandColors?: WidgetBrandColors,\n): void {\n  const primary = brandColors?.primary ?? BRAND_COLOR\n  ctx.save()\n  ctx.shadowColor = 'rgba(15, 23, 42, 0.68)'\n  ctx.shadowOffsetY = 2\n  ctx.shadowBlur = 8\n  ctx.fillStyle = primary\n  ctx.beginPath()\n  ctx.arc(x, y, 11, 0, Math.PI * 2)\n  ctx.fill()\n  ctx.shadowColor = 'transparent'\n  ctx.lineWidth = 2\n  ctx.strokeStyle = '#ffffff'\n  ctx.stroke()\n  ctx.restore()\n}\n\nfunction canvasToJpegBlob(canvas: HTMLCanvasElement): Promise<Blob | null> {\n  return new Promise(resolve => {\n    try {\n      canvas.toBlob(blob => resolve(blob), 'image/jpeg', 0.85)\n    } catch {\n      resolve(null)\n    }\n  })\n}\n","export interface ErrorSourceEvent {\n  name?: string\n  message: string\n  source?: string\n  line?: number\n  col?: number\n  error?: unknown\n}\n\ntype ErrorListener = (event: ErrorSourceEvent) => void\n\nclass ErrorSource {\n  private listeners = new Set<ErrorListener>()\n  private originalOnError: OnErrorEventHandler | null = null\n  private installedOnError: OnErrorEventHandler | null = null\n  private rejectionHandler: ((e: PromiseRejectionEvent) => void) | null = null\n  private hooked = false\n\n  subscribe(listener: ErrorListener): () => void {\n    this.listeners.add(listener)\n    this.hook()\n    return () => {\n      this.listeners.delete(listener)\n      if (this.listeners.size === 0) this.unhook()\n    }\n  }\n\n  private hook(): void {\n    if (this.hooked) return\n    this.hooked = true\n\n    this.originalOnError = window.onerror\n    this.installedOnError = (msg, source, line, col, error) => {\n      this.emit({ name: errorName(error), message: String(msg), source, line, col, error })\n      if (this.originalOnError) {\n        return this.originalOnError.call(window, msg, source, line, col, error)\n      }\n      return false\n    }\n    window.onerror = this.installedOnError\n\n    this.rejectionHandler = (e: PromiseRejectionEvent) => {\n      const reason = e.reason instanceof Error ? e.reason.message : String(e.reason)\n      const name = e.reason instanceof Error ? e.reason.name : 'UnhandledRejection'\n      this.emit({ name, message: `Unhandled rejection: ${reason}` })\n    }\n    window.addEventListener('unhandledrejection', this.rejectionHandler)\n  }\n\n  private unhook(): void {\n    if (!this.hooked) return\n    this.hooked = false\n    // Only restore if nothing replaced our handler meanwhile — restoring\n    // the stale snapshot would clobber a handler the page installed later.\n    if (window.onerror === this.installedOnError) {\n      window.onerror = this.originalOnError\n    }\n    this.installedOnError = null\n    this.originalOnError = null\n    if (this.rejectionHandler) {\n      window.removeEventListener('unhandledrejection', this.rejectionHandler)\n      this.rejectionHandler = null\n    }\n  }\n\n  private emit(event: ErrorSourceEvent): void {\n    for (const listener of this.listeners) listener(event)\n  }\n}\n\nexport const errorSource = new ErrorSource()\n\nfunction errorName(error: unknown): string | undefined {\n  if (!error || typeof error !== 'object' || !('name' in error)) return undefined\n  const name = String(error.name)\n  return name || undefined\n}\n","import type { ErrorSourceEvent } from './error-source'\n\nexport function summarizeError(event: ErrorSourceEvent): string {\n  const name = validErrorName(event.name) || inferErrorName(event.message)\n  const location = errorLocation(event)\n  return location ? `${name} (${location})` : name\n}\n\nfunction validErrorName(name: string | undefined): string {\n  return name && /^[A-Za-z][A-Za-z0-9]*(?:Error|Exception|Rejection)$/.test(name) ? name : ''\n}\n\nfunction inferErrorName(message: string): string {\n  return message.match(/^([A-Za-z][A-Za-z0-9]*(?:Error|Exception))\\b/)?.[1] ?? 'Error'\n}\n\nfunction errorLocation(event: ErrorSourceEvent): string {\n  if (!event.source) return ''\n\n  try {\n    const pathname = new URL(event.source, window.location.href).pathname\n    if (event.line == null) return pathname\n    return `${pathname}:${event.line}:${event.col ?? 0}`\n  } catch {\n    return ''\n  }\n}\n","import { errorSource, type ErrorSourceEvent } from './error-source'\nimport { summarizeError } from './error-summary'\n\nconst MAX_CONSOLE_ERRORS = 20\n\nexport class ConsoleErrorCollector {\n  private errors: string[] = []\n  private unsubscribe: (() => void) | null = null\n\n  start(): void {\n    if (this.unsubscribe) return\n    this.unsubscribe = errorSource.subscribe((e) => this.record(e))\n  }\n\n  private record(e: ErrorSourceEvent): void {\n    this.addError(summarizeError(e))\n  }\n\n  private addError(msg: string): void {\n    if (this.errors.length >= MAX_CONSOLE_ERRORS) return\n    if (!this.errors.includes(msg)) {\n      this.errors.push(msg)\n    }\n  }\n\n  getErrors(): string[] {\n    return [...this.errors]\n  }\n\n  stop(): void {\n    this.unsubscribe?.()\n    this.unsubscribe = null\n  }\n}\n","// Single owner of the history.pushState / replaceState patch.\n//\n// Two collectors (breadcrumbs and frustration) both need to know when an SPA\n// navigates. When each wrapped history itself, teardown could only restore the\n// native function if it happened to be the outermost wrapper — so tearing them\n// down in install order (FIFO teardown of a LIFO stack) left one dead wrapper\n// behind per cycle, and Turbo Drive reinstalls on every visit. Refcounting one\n// wrapper here makes the install order irrelevant: the patch goes on when the\n// first listener subscribes and comes off when the last one unsubscribes.\n\nexport interface HistoryNavigationEvent {\n  type: 'push' | 'replace' | 'pop'\n  fromUrl: string\n  toUrl: string\n}\n\ntype NavListener = (event: HistoryNavigationEvent) => void\n\nconst listeners = new Set<NavListener>()\n\nlet nativePushState: History['pushState'] | null = null\nlet nativeReplaceState: History['replaceState'] | null = null\nlet installedPushState: History['pushState'] | null = null\nlet installedReplaceState: History['replaceState'] | null = null\nlet popstateHandler: (() => void) | null = null\nlet lastUrl: string | null = null\n\n// Subscribes to pushState, replaceState and popstate. Returns an unsubscribe\n// function; calling it twice is harmless.\nexport function onHistoryNavigation(listener: NavListener): () => void {\n  listeners.add(listener)\n  install()\n\n  let released = false\n  return () => {\n    if (released) return\n    released = true\n    listeners.delete(listener)\n    if (listeners.size === 0) uninstall()\n  }\n}\n\nfunction notify(type: HistoryNavigationEvent['type'], fromUrl: string): void {\n  const event = { type, fromUrl, toUrl: location.href }\n  lastUrl = event.toUrl\n  // Copy first: a listener may unsubscribe (or subscribe) while being notified.\n  for (const listener of [...listeners]) {\n    try { listener(event) } catch { /* never propagate into the host page */ }\n  }\n}\n\nfunction install(): void {\n  if (installedPushState) return\n\n  lastUrl = location.href\n  popstateHandler = () => notify('pop', lastUrl ?? location.href)\n  window.addEventListener('popstate', popstateHandler)\n\n  // Snapshot the raw functions and invoke them with .call — binding would hand\n  // back a different function object on uninstall, so the host page would never\n  // get its own pushState identity back and each cycle would add a bind layer.\n  nativePushState = history.pushState\n  nativeReplaceState = history.replaceState\n  const pushOrig = nativePushState\n  const replaceOrig = nativeReplaceState\n\n  installedPushState = function (data: unknown, unused: string, url?: string | URL | null) {\n    const fromUrl = location.href\n    pushOrig.call(history, data, unused, url)\n    notify('push', fromUrl)\n  }\n  installedReplaceState = function (data: unknown, unused: string, url?: string | URL | null) {\n    const fromUrl = location.href\n    replaceOrig.call(history, data, unused, url)\n    notify('replace', fromUrl)\n  }\n  history.pushState = installedPushState\n  history.replaceState = installedReplaceState\n}\n\nfunction uninstall(): void {\n  if (popstateHandler) {\n    window.removeEventListener('popstate', popstateHandler)\n    popstateHandler = null\n  }\n  // Only restore if nothing (e.g. an SPA router) re-wrapped the functions\n  // after us — restoring the stale snapshot would discard their wrapper.\n  if (nativePushState && history.pushState === installedPushState) {\n    history.pushState = nativePushState\n  }\n  if (nativeReplaceState && history.replaceState === installedReplaceState) {\n    history.replaceState = nativeReplaceState\n  }\n  nativePushState = null\n  nativeReplaceState = null\n  installedPushState = null\n  installedReplaceState = null\n  lastUrl = null\n}\n","import { errorSource, type ErrorSourceEvent } from './error-source'\nimport { summarizeError } from './error-summary'\nimport { getCssSelector, getElementName, isWidgetElement } from './dom-utils'\nimport { onHistoryNavigation } from './history-patch'\nimport { isPrivacyProtected, structuralElementName, structuralElementSelector } from '../privacy/dom'\nimport { sanitizeUrl } from '../privacy/sanitize'\nimport type { Breadcrumb } from '../types'\n\nconst MAX_BREADCRUMBS = 20\n// A click breadcrumb names the element by its on-screen text, which on a page\n// showing an order total or a patient name is that value verbatim. The replay\n// recorder already honours rr-block/rr-mask for exactly this; a customer who\n// marked a region has marked it for the whole SDK, not for replays only. Inside\n// one, fall back to what the element is rather than what it says.\nfunction describeTarget(el: Element): string {\n  if (isPrivacyProtected(el)) return structuralElementName(el)\n  return getElementName(el)\n}\n\nfunction describeSelector(el: Element): string {\n  return isPrivacyProtected(el) ? structuralElementSelector(el) : getCssSelector(el)\n}\n\nexport class BreadcrumbCollector {\n  private breadcrumbs: Breadcrumb[] = []\n  private active = false\n  private unsubscribeErrors: (() => void) | null = null\n  private boundClickHandler: (e: MouseEvent) => void\n  private boundChangeHandler: (e: Event) => void\n  private unsubscribeNavigation: (() => void) | null = null\n  private lastUrl = ''\n\n  constructor() {\n    this.boundClickHandler = (e: MouseEvent) => this.handleClick(e)\n    this.boundChangeHandler = (e: Event) => this.handleChange(e)\n  }\n\n  start(): void {\n    if (this.active) return\n    this.active = true\n    this.lastUrl = location.href\n    document.addEventListener('click', this.boundClickHandler, { capture: true, passive: true })\n    document.addEventListener('change', this.boundChangeHandler, { capture: true, passive: true })\n    this.unsubscribeErrors = errorSource.subscribe((e) => this.recordError(e))\n    this.unsubscribeNavigation = onHistoryNavigation(() => this.handleNavigation())\n  }\n\n  stop(): void {\n    if (!this.active) return\n    this.active = false\n    document.removeEventListener('click', this.boundClickHandler, true)\n    document.removeEventListener('change', this.boundChangeHandler, true)\n    this.unsubscribeErrors?.()\n    this.unsubscribeErrors = null\n    this.unsubscribeNavigation?.()\n    this.unsubscribeNavigation = null\n  }\n\n  getBreadcrumbs(): Breadcrumb[] {\n    return [...this.breadcrumbs]\n  }\n\n  private add(crumb: Breadcrumb): void {\n    this.breadcrumbs.push(crumb)\n    if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\n      this.breadcrumbs.shift()\n    }\n  }\n\n  private handleClick(e: MouseEvent): void {\n    const target = e.target as Element | null\n    if (!target) return\n    if (isWidgetElement(target)) return\n\n    try {\n      this.add({\n        type: 'ui',\n        category: 'ui.click',\n        timestamp: Date.now(),\n        message: describeTarget(target),\n        data: { selector: describeSelector(target) },\n      })\n    } catch { /* never propagate into host page */ }\n  }\n\n  private handleChange(e: Event): void {\n    const target = e.target as Element | null\n    if (!target) return\n    if (isWidgetElement(target)) return\n\n    try {\n      const input = target as HTMLInputElement\n      const field = isPrivacyProtected(target)\n        ? structuralElementName(target)\n        : input.name || input.placeholder || target.getAttribute('aria-label') || target.tagName\n      this.add({\n        type: 'ui',\n        category: 'ui.input',\n        timestamp: Date.now(),\n        message: field,\n        data: { field },\n      })\n    } catch { /* never propagate into host page */ }\n  }\n\n  private recordError(e: ErrorSourceEvent): void {\n    const summary = summarizeError(e)\n    this.add({\n      type: 'console',\n      category: 'console.error',\n      timestamp: Date.now(),\n      message: summary,\n      data: { message: summary },\n    })\n  }\n\n  private handleNavigation(): void {\n    if (!this.active) return\n    const from = this.lastUrl\n    const to = location.href\n    if (from === to) return\n    this.lastUrl = to\n    const sanitizedFrom = sanitizeUrl(from)\n    const sanitizedTo = sanitizeUrl(to)\n    this.add({\n      type: 'navigation',\n      category: 'navigation',\n      timestamp: Date.now(),\n      message: `${sanitizedFrom} → ${sanitizedTo}`,\n      data: { from: sanitizedFrom, to: sanitizedTo },\n    })\n  }\n\n}\n","// Frustration state that must outlive a single page visit.\n//\n// The detector and the widget controller are both rebuilt from scratch on every\n// Turbo Drive navigation (see src/index.ts), which used to reset all of this.\n// The consequences were: `rapid_navigation` could never fire (it needs 3\n// timestamps within 5s but each visit contributed at most one), the 60s cooldown\n// was bypassed by navigating, an already-reported error page re-prompted on\n// revisit, and a reporter's explicit \"Dismiss\" did not survive one visit.\n//\n// sessionStorage is the right scope — this is per-tab, per-visit-series state,\n// not a long-lived identity like mtb_anon_id. Writes are best-effort: Safari\n// private mode and storage-partitioned iframes throw, so an in-memory copy backs\n// every read (it covers the current page at least).\n\nconst STORAGE_KEY = 'mtb_frustration_state'\n// Keeps a long browsing session from growing the stored payload without bound.\nconst MAX_ERROR_PAGE_URLS = 20\n\nexport interface FrustrationState {\n  navTimestamps: number[]\n  cooldownUntil: number\n  errorPageUrls: string[]\n  dismissedByUser: boolean\n}\n\nlet memoryState: FrustrationState | null = null\n\n// Every return path goes through normalize(), which rebuilds the arrays. Callers\n// own their copy and mutate it in place (the detector pushes onto navTimestamps),\n// so handing out a shared array would let one caller corrupt the stored state.\nexport function loadFrustrationState(): FrustrationState {\n  try {\n    const raw = window.sessionStorage.getItem(STORAGE_KEY)\n    if (!raw) return normalize(memoryState)\n    return normalize(JSON.parse(raw))\n  } catch {\n    return normalize(memoryState)\n  }\n}\n\nexport function saveFrustrationState(state: FrustrationState): void {\n  const normalized = normalize(state)\n  memoryState = normalized\n  try {\n    window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(normalized))\n  } catch {\n    // sessionStorage unavailable — the in-memory copy covers this page\n  }\n}\n\nexport function isFrustrationDismissed(): boolean {\n  return loadFrustrationState().dismissedByUser\n}\n\n// Records a real refusal (the reporter clicked Dismiss), which suppresses\n// proactive prompts for the rest of the tab session. An auto-hide must never\n// call this — the 60s cooldown is what rate-limits an ignored prompt.\nexport function markFrustrationDismissed(): void {\n  saveFrustrationState({ ...loadFrustrationState(), dismissedByUser: true })\n}\n\nexport function clearFrustrationState(): void {\n  memoryState = null\n  try {\n    window.sessionStorage.removeItem(STORAGE_KEY)\n  } catch {\n    // nothing to clear\n  }\n}\n\nfunction normalize(value: unknown): FrustrationState {\n  const raw = (value ?? {}) as Partial<Record<keyof FrustrationState, unknown>>\n  return {\n    navTimestamps: Array.isArray(raw.navTimestamps)\n      ? raw.navTimestamps.filter((t): t is number => typeof t === 'number')\n      : [],\n    cooldownUntil: typeof raw.cooldownUntil === 'number' ? raw.cooldownUntil : 0,\n    errorPageUrls: Array.isArray(raw.errorPageUrls)\n      ? raw.errorPageUrls.filter((u): u is string => typeof u === 'string').slice(-MAX_ERROR_PAGE_URLS)\n      : [],\n    dismissedByUser: raw.dismissedByUser === true,\n  }\n}\n","import { errorSource } from './error-source'\nimport { isWidgetElement } from './dom-utils'\nimport { onHistoryNavigation } from './history-patch'\nimport type { HistoryNavigationEvent } from './history-patch'\nimport { loadFrustrationState, saveFrustrationState } from './frustration-state'\n\nexport type FrustrationSignal =\n  | 'rage_click'\n  | 'dead_click'\n  | 'rapid_navigation'\n  | 'form_failure'\n  | 'error_page'\n  | 'dead_click_dom'\n\nexport interface FrustrationEvent {\n  signal: FrustrationSignal\n  target?: string\n  detail?: string\n  timestamp: number\n}\n\ninterface ClickRecord {\n  target: EventTarget | null\n  time: number\n}\n\nconst RAGE_CLICK_THRESHOLD = 4\nconst RAGE_CLICK_WINDOW_MS = 1500\nconst RAPID_NAV_THRESHOLD = 3\nconst RAPID_NAV_WINDOW_MS = 5000\nconst FORM_CHECK_DELAY_MS = 500\nconst FORM_FAILURE_THRESHOLD = 2\nconst FORM_FAILURE_WINDOW_MS = 30_000\nconst INTERACTION_ERROR_WINDOW_MS = 2000\nconst DEAD_CLICK_DOM_DELAY_MS = 1000\nconst ERROR_PAGE_CHECK_DELAY_MS = 300\n\nconst INTERACTIVE_TAGS = new Set([\n  'A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'LABEL', 'SUMMARY', 'DETAILS',\n])\n\nconst INTERACTIVE_ROLES = new Set([\n  'button', 'link', 'checkbox', 'radio', 'tab', 'menuitem', 'option',\n  'switch', 'textbox', 'combobox', 'listbox', 'slider', 'spinbutton',\n])\n\nconst COMMAND_TAGS = new Set(['A', 'BUTTON', 'SUMMARY'])\nconst COMMAND_ROLES = new Set(['button', 'link', 'tab', 'menuitem'])\n\n// Matched as whole words, never as substrings. Substring matching read the\n// '500' in \"Top 500 SaaS Tools\" as an outage and put the high-priority prompt\n// in front of every visitor on load.\n//\n// A bare status code stays ambiguous even with word boundaries — it is a\n// listicle count, a company name, a price — so it only counts when the title is\n// nothing but the code, or an explicit error word stands next to it. The\n// phrases are error-shaped on their own and carry themselves.\nconst ERROR_PAGE_PATTERNS = [\n  /\\bnot found\\b/,\n  /\\bserver error\\b/,\n  /^\\s*(?:http\\s*)?[45]\\d{2}\\s*$/,\n  /\\b(?:error|http|status)\\s*[:-]?\\s*[45]\\d{2}\\b/,\n  /\\b[45]\\d{2}\\s*[:-]?\\s*error\\b/,\n]\n\nexport class FrustrationDetector {\n  private clickHistory: ClickRecord[] = []\n  private onFrustration: (event: FrustrationEvent) => void\n  private boundClickHandler: (e: MouseEvent) => void\n  private unsubscribeErrors: (() => void) | null = null\n  private active = false\n  private lastInteraction: { target: Element; time: number } | null = null\n  private cooldownUntil = 0\n  private static readonly COOLDOWN_MS = 60_000\n\n  private navTimestamps: number[] = []\n  private unsubscribeNavigation: (() => void) | null = null\n  private boundPageLeaveHandler: (() => void) | null = null\n  private boundTurboNavigationHandler: (() => void) | null = null\n  private lastNavigationAt = -1\n  private boundSubmitHandler: ((e: Event) => void) | null = null\n  private boundInvalidHandler: ((e: Event) => void) | null = null\n  private recentSubmitForms = new WeakSet<HTMLFormElement>()\n  private recentFailedForms = new WeakSet<HTMLFormElement>()\n  private formFailureTimestamps = new WeakMap<HTMLFormElement, number[]>()\n  private errorPageUrls = new Set<string>()\n  private domObserver: MutationObserver | null = null\n  private pendingDomChecks = 0\n  private lastMutationAt = -1\n\n  // Every deferred check outlives the interaction that scheduled it, so stop()\n  // has to cancel the ones still waiting. Two reasons: a dead-click timer holds\n  // the clicked host element for a second past teardown, and a start() inside\n  // that window would let the previous session's click emit against the new one.\n  private pendingTimers = new Set<ReturnType<typeof setTimeout>>()\n\n  constructor(onFrustration: (event: FrustrationEvent) => void) {\n    this.onFrustration = onFrustration\n    this.boundClickHandler = (e: MouseEvent) => this.handleClick(e)\n  }\n\n  start(): void {\n    if (this.active) return\n    this.active = true\n    this.restoreState()\n    document.addEventListener('click', this.boundClickHandler, true)\n    this.unsubscribeErrors = errorSource.subscribe(() => {\n      const now = Date.now()\n      if (!this.lastInteraction || now - this.lastInteraction.time > INTERACTION_ERROR_WINDOW_MS) return\n\n      const interaction = this.lastInteraction\n      this.lastInteraction = null\n      this.emitIfReady({\n        signal: 'dead_click',\n        target: describeElement(interaction.target),\n        detail: 'Uncaught error after interaction',\n        timestamp: now,\n      })\n    })\n\n    this.startNavigationTracking()\n    this.startFormTracking()\n    this.checkErrorPage()\n  }\n\n  stop(): void {\n    if (!this.active) return\n    this.active = false\n    document.removeEventListener('click', this.boundClickHandler, true)\n    this.unsubscribeErrors?.()\n    this.unsubscribeErrors = null\n    this.clickHistory = []\n    this.lastInteraction = null\n\n    this.stopNavigationTracking()\n    this.stopFormTracking()\n    this.errorPageUrls.clear()\n\n    this.clearPendingTimers()\n    // The cancelled dead-click timers are the only thing that would have\n    // decremented this, so the count has to come back to zero by hand or the\n    // observer of a restarted detector never reaches its disconnect condition.\n    this.pendingDomChecks = 0\n    this.disconnectDomObserver()\n  }\n\n  private schedule(callback: () => void, delayMs: number): void {\n    const timer = setTimeout(() => {\n      this.pendingTimers.delete(timer)\n      callback()\n    }, delayMs)\n    this.pendingTimers.add(timer)\n  }\n\n  private clearPendingTimers(): void {\n    for (const timer of this.pendingTimers) clearTimeout(timer)\n    this.pendingTimers.clear()\n  }\n\n  private startNavigationTracking(): void {\n    this.unsubscribeNavigation = onHistoryNavigation((event) => this.handleNavigation(event))\n\n    // A click that starts a full-page navigation (checkout, OAuth, external\n    // link, download, window.location assignment) leaves the DOM untouched\n    // while the browser tears the page down, which is indistinguishable from a\n    // dead click by mutation alone. Recording when the page starts leaving lets\n    // checkDeadClickDom acquit those clicks. Both events are needed: browsers\n    // skip beforeunload when nothing registered an unload prompt, and Safari\n    // reaches bfcache via pagehide only.\n    this.boundPageLeaveHandler = () => this.noteNavigationStarted()\n    window.addEventListener('beforeunload', this.boundPageLeaveHandler)\n    window.addEventListener('pagehide', this.boundPageLeaveHandler)\n    this.boundTurboNavigationHandler = () => this.noteNavigationStarted()\n    document.addEventListener('turbo:before-visit', this.boundTurboNavigationHandler)\n    document.addEventListener('turbo:submit-start', this.boundTurboNavigationHandler)\n    document.addEventListener('turbo:before-fetch-request', this.boundTurboNavigationHandler)\n  }\n\n  private stopNavigationTracking(): void {\n    this.unsubscribeNavigation?.()\n    this.unsubscribeNavigation = null\n    this.navTimestamps = []\n\n    if (this.boundPageLeaveHandler) {\n      window.removeEventListener('beforeunload', this.boundPageLeaveHandler)\n      window.removeEventListener('pagehide', this.boundPageLeaveHandler)\n      this.boundPageLeaveHandler = null\n    }\n    if (this.boundTurboNavigationHandler) {\n      document.removeEventListener('turbo:before-visit', this.boundTurboNavigationHandler)\n      document.removeEventListener('turbo:submit-start', this.boundTurboNavigationHandler)\n      document.removeEventListener('turbo:before-fetch-request', this.boundTurboNavigationHandler)\n      this.boundTurboNavigationHandler = null\n    }\n    this.lastNavigationAt = -1\n  }\n\n  private noteNavigationStarted(): void {\n    this.lastNavigationAt = Date.now()\n  }\n\n  // Rehydrates the state a Turbo Drive visit would otherwise reset. Without this,\n  // a fresh detector on every page starts with an empty nav history (so\n  // `rapid_navigation` can never reach its 3-in-5s threshold), no cooldown, and no\n  // memory of which error pages it already reported.\n  private restoreState(): void {\n    const stored = loadFrustrationState()\n    this.navTimestamps = stored.navTimestamps\n    this.cooldownUntil = stored.cooldownUntil\n    this.errorPageUrls = new Set(stored.errorPageUrls)\n  }\n\n  private persistState(): void {\n    const stored = loadFrustrationState()\n    saveFrustrationState({\n      ...stored,\n      navTimestamps: this.navTimestamps,\n      cooldownUntil: this.cooldownUntil,\n      errorPageUrls: [...this.errorPageUrls],\n    })\n  }\n\n  private startFormTracking(): void {\n    this.boundSubmitHandler = (e: Event) => this.handleFormSubmit(e)\n    this.boundInvalidHandler = (e: Event) => this.handleFormInvalid(e)\n    document.addEventListener('submit', this.boundSubmitHandler, true)\n    document.addEventListener('invalid', this.boundInvalidHandler, true)\n  }\n\n  private stopFormTracking(): void {\n    if (this.boundSubmitHandler) {\n      document.removeEventListener('submit', this.boundSubmitHandler, true)\n      this.boundSubmitHandler = null\n    }\n    if (this.boundInvalidHandler) {\n      document.removeEventListener('invalid', this.boundInvalidHandler, true)\n      this.boundInvalidHandler = null\n    }\n    // Their expiry timers were just cancelled, so membership would otherwise be\n    // permanent and a form left mid-submit here would be judged failed by a\n    // later detector on the strength of an attempt from the previous one.\n    this.recentSubmitForms = new WeakSet()\n    this.recentFailedForms = new WeakSet()\n  }\n\n  private handleClick(e: MouseEvent): void {\n    const target = e.target as Element | null\n    if (!target) return\n\n    if (isWidgetElement(target)) return\n    this.trackFormSubmitIntent(target)\n    this.trackNativeNavigationIntent(e, target)\n    const interactionTarget = this.interactionTarget(target)\n\n    const now = Date.now()\n    this.lastInteraction = { target: interactionTarget, time: now }\n    this.clickHistory.push({ target: interactionTarget, time: now })\n\n    const cutoff = now - RAGE_CLICK_WINDOW_MS\n    this.clickHistory = this.clickHistory.filter(r => r.time > cutoff)\n\n    const sameTargetClicks = this.clickHistory.filter(r => r.target === interactionTarget)\n    if (sameTargetClicks.length >= RAGE_CLICK_THRESHOLD) {\n      this.clickHistory = []\n      this.emitHighPriority({\n        signal: 'rage_click',\n        target: describeElement(interactionTarget),\n        timestamp: now,\n      })\n      return\n    }\n\n    this.checkDeadClickDom(interactionTarget, now)\n  }\n\n  private handleNavigation(event: HistoryNavigationEvent): void {\n    const now = Date.now()\n    this.noteNavigationStarted()\n    const meaningfulNavigation = event.type !== 'replace' && event.fromUrl !== event.toUrl\n    // Successful forward navigation is ordinary browsing, even when several\n    // pages are opened quickly. Repeated browser back/forward movement is the\n    // narrower signal that the visitor may be struggling to find their way.\n    if (meaningfulNavigation && event.type === 'pop') {\n      this.navTimestamps.push(now)\n      const cutoff = now - RAPID_NAV_WINDOW_MS\n      this.navTimestamps = this.navTimestamps.filter(t => t > cutoff)\n      if (this.navTimestamps.length >= RAPID_NAV_THRESHOLD) {\n        this.navTimestamps = []\n        this.emitIfReady({\n          signal: 'rapid_navigation',\n          timestamp: now,\n        })\n      }\n    }\n    this.persistState()\n    this.schedule(() => {\n      if (this.active) this.checkErrorPage()\n    }, ERROR_PAGE_CHECK_DELAY_MS)\n  }\n\n  private handleFormSubmit(e: Event): void {\n    const form = e.target\n    if (!(form instanceof HTMLFormElement)) return\n    if (isWidgetElement(form)) return\n\n    this.rememberSubmitAttempt(form)\n\n    this.schedule(() => {\n      if (!this.active) return\n      if (form.querySelector('[aria-invalid=\"true\"], .is-invalid')) {\n        this.recordFormFailure(form)\n      }\n    }, FORM_CHECK_DELAY_MS)\n  }\n\n  private trackFormSubmitIntent(target: Element): void {\n    const control = target.closest('button, input')\n    if (!(control instanceof HTMLButtonElement || control instanceof HTMLInputElement)) return\n    if (control.type !== 'submit' && control.type !== 'image') return\n    if (!control.form || isWidgetElement(control.form)) return\n\n    this.rememberSubmitAttempt(control.form)\n  }\n\n  private trackNativeNavigationIntent(event: MouseEvent, target: Element): void {\n    if (!target.closest('a[href]')) return\n\n    queueMicrotask(() => {\n      if (this.active && !event.defaultPrevented) this.noteNavigationStarted()\n    })\n  }\n\n  private rememberSubmitAttempt(form: HTMLFormElement): void {\n    this.recentSubmitForms.add(form)\n    this.schedule(() => {\n      this.recentSubmitForms.delete(form)\n    }, FORM_CHECK_DELAY_MS)\n  }\n\n  private recordFormFailure(form: HTMLFormElement): void {\n    if (this.recentFailedForms.has(form)) return\n    this.recentFailedForms.add(form)\n    this.schedule(() => this.recentFailedForms.delete(form), FORM_CHECK_DELAY_MS)\n\n    const now = Date.now()\n    const cutoff = now - FORM_FAILURE_WINDOW_MS\n    const failures = (this.formFailureTimestamps.get(form) ?? []).filter(timestamp => timestamp > cutoff)\n    failures.push(now)\n    this.formFailureTimestamps.set(form, failures)\n    if (failures.length < FORM_FAILURE_THRESHOLD) return\n\n    this.formFailureTimestamps.set(form, [])\n    this.emitIfReady({\n      signal: 'form_failure',\n      target: describeElement(form),\n      timestamp: now,\n    })\n  }\n\n  private handleFormInvalid(e: Event): void {\n    const el = e.target as Element | null\n    if (!el) return\n    if (isWidgetElement(el)) return\n    const form = el.closest('form')\n    if (!form || !this.recentSubmitForms.has(form)) return\n    this.recordFormFailure(form)\n  }\n\n  private checkErrorPage(): void {\n    const url = location.href\n    if (this.errorPageUrls.has(url)) return\n\n    const title = document.title.toLowerCase()\n    if (ERROR_PAGE_PATTERNS.some(p => p.test(title))) {\n      this.errorPageUrls.add(url)\n      this.emitHighPriority({\n        signal: 'error_page',\n        detail: document.title,\n        timestamp: Date.now(),\n      })\n      return\n    }\n\n    const candidates = document.querySelectorAll(\n      'h1, h2, [class*=\"error\"], [class*=\"not-found\"], [id*=\"error\"], [id*=\"not-found\"]'\n    )\n    for (const el of candidates) {\n      const text = el.textContent ?? ''\n      if (text.length < 200 && ERROR_PAGE_PATTERNS.some(p => p.test(text.toLowerCase()))) {\n        this.errorPageUrls.add(url)\n        this.emitHighPriority({\n          signal: 'error_page',\n          detail: text.trim().slice(0, 60),\n          timestamp: Date.now(),\n        })\n        return\n      }\n    }\n  }\n\n  // One shared observer for all in-flight dead-click checks, connected only\n  // while at least one check is pending — a full-page observer per click was\n  // too heavy on mutation-busy pages.\n  private checkDeadClickDom(target: Element, clickTime: number): void {\n    if (!this.looksInteractive(target)) return\n\n    this.pendingDomChecks++\n    this.ensureDomObserver()\n\n    this.schedule(() => {\n      this.pendingDomChecks--\n      const mutated = this.lastMutationAt >= clickTime\n      // A navigation that began at or after the click means the click worked;\n      // the page just left before it could mutate anything.\n      const navigated = this.lastNavigationAt >= clickTime\n      if (this.pendingDomChecks === 0) this.disconnectDomObserver()\n\n      if (!mutated && !navigated && this.active) {\n        this.emitIfReady({\n          signal: 'dead_click_dom',\n          target: describeElement(target),\n          timestamp: clickTime,\n        })\n      }\n    }, DEAD_CLICK_DOM_DELAY_MS)\n  }\n\n  private ensureDomObserver(): void {\n    if (this.domObserver) return\n    this.domObserver = new MutationObserver(() => {\n      this.lastMutationAt = Date.now()\n    })\n    this.domObserver.observe(document.documentElement, {\n      childList: true,\n      subtree: true,\n      attributes: true,\n      characterData: true,\n    })\n  }\n\n  private disconnectDomObserver(): void {\n    this.domObserver?.disconnect()\n    this.domObserver = null\n  }\n\n  private looksInteractive(el: Element): boolean {\n    if (COMMAND_TAGS.has(el.tagName) || el.hasAttribute('onclick')) return true\n\n    const role = el.getAttribute('role')\n    if (role && COMMAND_ROLES.has(role)) return true\n\n    const style = window.getComputedStyle(el)\n    if (style.cursor === 'pointer') return true\n\n    if (el.classList) {\n      for (const cls of el.classList) {\n        const lower = cls.toLowerCase()\n        if (lower.includes('btn') || lower.includes('button')) return true\n      }\n    }\n\n    return false\n  }\n\n  private interactionTarget(el: Element): Element {\n    let current: Element | null = el\n    while (current && current !== document.body) {\n      if (this.isIntrinsicInteractive(current)) return current\n      current = current.parentElement\n    }\n\n    let candidate = el\n    current = el.parentElement\n    while (current && current !== document.body && this.looksInteractive(current)) {\n      candidate = current\n      current = current.parentElement\n    }\n    return candidate\n  }\n\n  private isIntrinsicInteractive(el: Element): boolean {\n    if (INTERACTIVE_TAGS.has(el.tagName)) return true\n\n    const role = el.getAttribute('role')\n    if (role && INTERACTIVE_ROLES.has(role)) return true\n\n    if (el.hasAttribute('onclick') || el.hasAttribute('tabindex')) return true\n    return (el as HTMLElement).contentEditable === 'true'\n  }\n\n  private emitIfReady(event: FrustrationEvent): void {\n    const now = Date.now()\n    if (now < this.cooldownUntil) return\n    this.cooldownUntil = now + FrustrationDetector.COOLDOWN_MS\n    this.persistState()\n    this.onFrustration(event)\n  }\n\n  private emitHighPriority(event: FrustrationEvent): void {\n    this.cooldownUntil = Date.now() + FrustrationDetector.COOLDOWN_MS\n    this.persistState()\n    this.onFrustration(event)\n  }\n}\n\nfunction describeElement(el: Element): string {\n  const tag = el.tagName.toLowerCase()\n  const id = el.id ? `#${el.id}` : ''\n  const cls = el.className && typeof el.className === 'string'\n    ? '.' + el.className.trim().split(/\\s+/).slice(0, 2).join('.')\n    : ''\n  const text = el.textContent?.trim().slice(0, 30) ?? ''\n  return `<${tag}${id}${cls}>${text ? ` \"${text}\"` : ''}`\n}\n","import type {\n  FeedbackPayload,\n  FeedbackResponse,\n  SubmissionSessionResponse,\n  ClarifyResponse,\n  ClarifyMessage,\n} from '../types'\nimport { filterSensitiveText } from '../privacy/sanitize'\n\nexport interface ClarifyStreamResult {\n  messages: ClarifyMessage[]\n  suggestions?: string[]\n  done: boolean\n  failed?: boolean\n}\n\n// Carries the HTTP status so callers can tell a retryable failure from a terminal\n// one (e.g. 409 = the submission session is no longer accepting a finalize, which\n// retrying can never fix). The message stays `HTTP <status>` for compatibility.\nexport class ApiError extends Error {\n  readonly status: number\n\n  constructor(status: number) {\n    super(`HTTP ${status}`)\n    this.name = 'ApiError'\n    this.status = status\n  }\n}\n\nexport function isTerminalConflict(error: unknown): boolean {\n  return error instanceof ApiError && error.status === 409\n}\n\n// A finalize the reporter can no longer watch must still end. Without a ceiling\n// a request that never settles leaves the widget mid-submission forever, with\n// the launcher inert and nothing on screen to explain why.\nexport const FINALIZE_TIMEOUT_MS = 25_000\n\nconst DEFAULT_API_URL = 'https://makethisbetter.dev/api/v1'\nconst CAPTURE_POLICY = 'sensitive-data-v1'\n\nexport interface ApiClientOptions {\n  projectKey: string\n  apiUrl?: string\n  retryDelayMs?: number\n  userToken?: string\n  userTokenFn?: () => Promise<string>\n}\n\nexport class ApiClient {\n  private apiUrl: string\n  private projectKey: string\n  private retryDelayMs: number\n  private userToken?: string\n  private userTokenFn?: () => Promise<string>\n\n  constructor(options: ApiClientOptions) {\n    this.projectKey = options.projectKey\n    this.apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, '')\n    this.retryDelayMs = options.retryDelayMs ?? 1000\n    this.userToken = options.userToken\n    this.userTokenFn = options.userTokenFn\n  }\n\n  async resolveUserToken(): Promise<string | undefined> {\n    if (this.userTokenFn) return this.userTokenFn()\n    return this.userToken\n  }\n\n  async resolveScreenshotResourceUrl(\n    submissionSessionId: string,\n    token: string,\n    resourceUrl: string,\n    contentType?: string,\n    signal?: AbortSignal,\n  ): Promise<string | undefined> {\n    if (contentType && !contentType.startsWith('image/')) return undefined\n\n    try {\n      const source = new URL(resourceUrl, window.location.href)\n      if (!['http:', 'https:'].includes(source.protocol)) return undefined\n      if (source.origin === window.location.origin) return undefined\n\n      const headers = await this.submissionHeaders(token, 'application/json')\n      const res = await this.fetchWithRetry(\n        `${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/image_proxy_url`,\n        {\n          method: 'POST',\n          headers,\n          body: JSON.stringify({ image_proxy_url: { source: source.href } }),\n          signal,\n        },\n      )\n      const body = await res.json() as { url?: unknown }\n      return typeof body.url === 'string' ? body.url : undefined\n    } catch {\n      return undefined\n    }\n  }\n\n  private async authHeaders(contentType?: string): Promise<Record<string, string>> {\n    const headers: Record<string, string> = { 'X-Project-Key': this.projectKey }\n    const token = await this.resolveUserToken()\n    if (token) headers['X-User-Token'] = token\n    if (contentType) headers['Content-Type'] = contentType\n    return headers\n  }\n\n  private async submissionHeaders(token: string, contentType?: string): Promise<Record<string, string>> {\n    const headers = await this.authHeaders(contentType)\n    headers['X-Submission-Token'] = token\n    return headers\n  }\n\n  async createSubmissionSession(payload: FeedbackPayload, screenshot: Blob | null): Promise<SubmissionSessionResponse> {\n    const submissionToken = generateSubmissionToken()\n    const form = new FormData()\n    form.append('feedback[description]', payload.description)\n    form.append('feedback[page_url]', payload.page_url)\n    form.append('feedback[user_agent]', payload.user_agent)\n    form.append('feedback[browser]', payload.browser)\n    form.append('feedback[os]', payload.os)\n    form.append('feedback[screen_width]', String(payload.screen_width))\n    form.append('feedback[screen_height]', String(payload.screen_height))\n    form.append('feedback[console_errors]', JSON.stringify(payload.console_errors))\n    form.append('feedback[annotations]', JSON.stringify(payload.annotations))\n\n    if (payload.target_element) {\n      form.append('feedback[target_element]', JSON.stringify(payload.target_element))\n    }\n    if (payload.user_id) form.append('feedback[user_id]', payload.user_id)\n    if (payload.reporter_external_id) form.append('feedback[reporter_external_id]', payload.reporter_external_id)\n    if (payload.reporter_email) form.append('feedback[reporter_email]', payload.reporter_email)\n    if (payload.user_email) form.append('feedback[user_email]', payload.user_email)\n    if (payload.user_name) form.append('feedback[user_name]', payload.user_name)\n\n    if (payload.breadcrumbs && payload.breadcrumbs.length > 0) {\n      form.append('feedback[breadcrumbs]', JSON.stringify(payload.breadcrumbs))\n    }\n\n    if (payload.recording_events && payload.recording_events.length > 0) {\n      const json = JSON.stringify(payload.recording_events)\n      const blob = new Blob([json], { type: 'application/json' })\n      form.append('feedback[recording]', blob, 'interaction-replay.json')\n      form.append('feedback[recording_duration]', String(payload.recording_duration ?? 0))\n    }\n\n    if (screenshot) {\n      const ext = screenshot.type === 'image/jpeg' ? 'jpg' : 'png'\n      form.append('feedback[screenshot]', screenshot, `screenshot.${ext}`)\n    }\n\n    const headers = await this.authHeaders()\n    headers['X-Submission-Token'] = submissionToken\n    headers['X-MTB-Capture-Policy'] = CAPTURE_POLICY\n    // One key per submission, reused across retries: if the first POST\n    // reached the server but the response was lost, the retry must not\n    // create a second feedback.\n    headers['X-Idempotency-Key'] = generateIdempotencyKey()\n    const res = await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions`, {\n      method: 'POST',\n      headers,\n      body: form,\n    })\n    const body = await res.json() as {\n      submission_session: Omit<SubmissionSessionResponse, 'token'> & { token?: string }\n    }\n    return { ...body.submission_session, token: submissionToken }\n  }\n\n  async createIdentityToken(reporterExternalId: string): Promise<{ identity_token: string; board_url: string } | null> {\n    const headers = await this.authHeaders('application/json')\n    try {\n      const res = await this.fetchWithRetry(`${this.apiUrl}/widget/identity_tokens`, {\n        method: 'POST',\n        headers,\n        body: JSON.stringify({ reporter_external_id: reporterExternalId }),\n      })\n      return await res.json()\n    } catch {\n      return null\n    }\n  }\n\n  async updateReporter(feedbackId: string, email: string, identityToken: string): Promise<boolean> {\n    const headers = await this.authHeaders('application/json')\n    headers['X-Identity-Token'] = identityToken\n    try {\n      await this.fetchWithRetry(`${this.apiUrl}/widget/feedbacks/${feedbackId}/reporter`, {\n        method: 'PATCH',\n        headers,\n        body: JSON.stringify({ reporter: { email } }),\n      })\n      return true\n    } catch {\n      return false\n    }\n  }\n\n  // Records the reporter's answer to the AI follow-up question. Must run\n  // before finalize: finalizing deactivates the session and the server then\n  // rejects the answer as terminal.\n  async answerClarification(submissionSessionId: string, token: string, message: string): Promise<ClarifyResponse> {\n    return this.requestClarification(submissionSessionId, token, { message: filterSensitiveText(message) })\n  }\n\n  private async requestClarification(\n    submissionSessionId: string,\n    token: string,\n    body: Record<string, string | boolean>,\n  ): Promise<ClarifyResponse> {\n    const headers = await this.submissionHeaders(token, 'application/json')\n    const res = await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/clarification`, {\n      method: 'POST',\n      headers,\n      body: JSON.stringify(body),\n    })\n    const response = await res.json() as { clarification: ClarifyResponse }\n    return response.clarification\n  }\n\n  // Streams a clarification turn over SSE. Calls onDelta for each token and\n  // resolves with the committed result.\n  async streamClarification(\n    submissionSessionId: string,\n    token: string,\n    onDelta: (text: string) => void,\n    signal?: AbortSignal,\n  ): Promise<ClarifyStreamResult> {\n    const headers = await this.submissionHeaders(token, 'application/json')\n    headers['Accept'] = 'text/event-stream'\n\n    const res = await fetch(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/clarification`, {\n      method: 'POST',\n      headers,\n      body: JSON.stringify({}),\n      signal,\n    })\n\n    const contentType = res.headers.get('Content-Type') ?? ''\n    if (!res.ok || !res.body || !contentType.includes('text/event-stream')) {\n      throw new Error('clarify stream unavailable')\n    }\n\n    return consumeSse(res.body, onDelta)\n  }\n\n  // No X-Idempotency-Key here, unlike createSubmissionSession: the session id in\n  // the path already is one. The server finalizes under a row lock and returns\n  // the feedback it already created if the session is finalized, so the retry\n  // fetchWithRetry makes after a 5xx cannot produce a second Feedback.\n  async finalizeSubmissionSession(submissionSessionId: string, token: string): Promise<FeedbackResponse> {\n    const headers = await this.submissionHeaders(token)\n    const controller = new AbortController()\n    // Covers both attempts, so the caller waits the ceiling once rather than\n    // once per retry.\n    const timer = setTimeout(() => controller.abort(), FINALIZE_TIMEOUT_MS)\n    try {\n      const res = await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/feedback`, {\n        method: 'POST',\n        headers,\n        signal: controller.signal,\n      })\n      const response = await res.json() as { feedback: FeedbackResponse }\n      return response.feedback\n    } finally {\n      clearTimeout(timer)\n    }\n  }\n\n  async uploadScreenshot(\n    submissionSessionId: string,\n    token: string,\n    screenshot: Blob,\n    signal?: AbortSignal,\n  ): Promise<void> {\n    const headers = await this.submissionHeaders(token)\n    delete (headers as Record<string, string>)['Content-Type']\n    const body = new FormData()\n    body.append('screenshot', screenshot, 'screenshot.jpg')\n    await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/screenshot`, {\n      method: 'PUT',\n      headers,\n      body,\n      signal,\n    })\n  }\n\n  async abandonSubmissionSession(submissionSessionId: string, token: string): Promise<void> {\n    const headers = await this.submissionHeaders(token)\n    await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}`, {\n      method: 'DELETE',\n      headers,\n    })\n  }\n\n  private async fetchWithRetry(url: string, init: RequestInit, retries = 1): Promise<Response> {\n    let res: Response\n    try {\n      res = await fetch(url, init)\n    } catch (err) {\n      // An aborted request has spent its deadline; retrying only makes the\n      // caller wait out the retry delay for a second guaranteed abort.\n      if (retries > 0 && !init.signal?.aborted) {\n        await delay(this.retryDelayMs)\n        return this.fetchWithRetry(url, init, retries - 1)\n      }\n      throw err\n    }\n\n    if (res.ok) return res\n    if (res.status >= 500 && retries > 0) {\n      await delay(this.retryDelayMs)\n      return this.fetchWithRetry(url, init, retries - 1)\n    }\n    throw new ApiError(res.status)\n  }\n}\n\nfunction delay(ms: number): Promise<void> {\n  if (ms <= 0) return Promise.resolve()\n  return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nasync function consumeSse(\n  body: ReadableStream<Uint8Array>,\n  onDelta: (text: string) => void,\n): Promise<ClarifyStreamResult> {\n  const reader = body.getReader()\n  const decoder = new TextDecoder()\n  let buffer = ''\n  let result: ClarifyStreamResult | null = null\n\n  const flush = (raw: string): void => {\n    // Each SSE record is \"event: <name>\\n data: <json>\". Parse event + data.\n    const eventLine = raw.split('\\n').find(l => l.startsWith('event:'))\n    const dataLine = raw.split('\\n').find(l => l.startsWith('data:'))\n    if (!dataLine) return\n    const event = eventLine?.slice(6).trim()\n    const payload = JSON.parse(dataLine.slice(5).trim())\n    if (event === 'delta' && typeof payload.text === 'string') {\n      onDelta(payload.text)\n    } else if (event === 'done') {\n      result = {\n        messages: Array.isArray(payload.messages) ? payload.messages : [],\n        suggestions: Array.isArray(payload.suggestions)\n          ? payload.suggestions.filter((s: unknown): s is string => typeof s === 'string')\n          : [],\n        done: payload.done !== false,\n        failed: payload.error === true,\n      }\n    }\n  }\n\n  for (;;) {\n    const { done, value } = await reader.read()\n    if (done) break\n    buffer += decoder.decode(value, { stream: true })\n    let sep: number\n    while ((sep = buffer.indexOf('\\n\\n')) !== -1) {\n      const record = buffer.slice(0, sep)\n      buffer = buffer.slice(sep + 2)\n      try { flush(record) } catch { /* skip malformed record */ }\n    }\n  }\n\n  buffer += decoder.decode()\n  if (buffer.trim()) {\n    try { flush(buffer) } catch { /* handled by the missing done check below */ }\n  }\n  if (!result) throw new Error('clarify stream ended before done')\n\n  return result\n}\n\nfunction generateIdempotencyKey(): string {\n  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n    return crypto.randomUUID()\n  }\n  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`\n}\n\nfunction generateSubmissionToken(): string {\n  if (typeof crypto === 'undefined' || typeof crypto.getRandomValues !== 'function') {\n    throw new Error('Secure random generation is unavailable')\n  }\n\n  const bytes = crypto.getRandomValues(new Uint8Array(32))\n  return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('')\n}\n","const STORAGE_KEY = 'mtb_anon_id'\nconst EMAIL_KEY = 'mtb_reporter_email'\nconst BOARD_URL_KEY = 'mtb_board_url'\n\nlet memoryId: string | null = null\nlet memoryEmail: string | null = null\nlet memoryBoardUrl: string | null = null\n\nexport function getAnonId(): string {\n  try {\n    const stored = window.localStorage.getItem(STORAGE_KEY)\n    if (stored?.startsWith('anon_')) return stored\n    const id = generateAnonId()\n    window.localStorage.setItem(STORAGE_KEY, id)\n    return id\n  } catch {\n    memoryId ??= generateAnonId()\n    return memoryId\n  }\n}\n\nexport function getStoredReporterEmail(): string | undefined {\n  try {\n    return window.localStorage.getItem(EMAIL_KEY) ?? undefined\n  } catch {\n    return memoryEmail ?? undefined\n  }\n}\n\nexport function rememberReporterEmail(email: string): void {\n  memoryEmail = email\n  try {\n    window.localStorage.setItem(EMAIL_KEY, email)\n  } catch {\n    // localStorage unavailable — the in-memory copy covers this session\n  }\n}\n\nexport function getCachedBoardUrl(): string | undefined {\n  try {\n    return window.localStorage.getItem(BOARD_URL_KEY) ?? undefined\n  } catch {\n    return memoryBoardUrl ?? undefined\n  }\n}\n\nexport function cacheBoardUrl(url: string): void {\n  memoryBoardUrl = url\n  try {\n    window.localStorage.setItem(BOARD_URL_KEY, url)\n  } catch {\n    // localStorage unavailable — the in-memory copy covers this session\n  }\n}\n\nfunction generateAnonId(): string {\n  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n    return `anon_${crypto.randomUUID()}`\n  }\n  const random = `${Date.now().toString(16)}-${Math.random().toString(16).slice(2, 10)}`\n  return `anon_${random}`\n}\n","export default {\n  tab: 'Feedback',\n  toolbar: {\n    markup: 'Mark up',\n    record: 'Record',\n    hint: 'Click to comment, drag to draw',\n    hintTouch: \"Drag to mark what's wrong\",\n    hintRecord: 'Recording your screen',\n    exit: 'Exit',\n  },\n  popup: {\n    about: 'About',\n    placeholder: 'Describe the issue or suggestion...',\n    submit: 'Submit',\n    sending: 'Sending...',\n    cancel: 'Cancel',\n    close: 'Close',\n    my_feedback: 'My feedback',\n    screenshot_label: 'Attach screenshot',\n    screenshot_hint: 'Passwords and other sensitive fields are covered automatically.',\n    screenshot_short: 'Screenshot',\n    screenshot_locked_hint: 'Your drawing is baked onto the screenshot, so it is always attached. Sensitive fields are covered automatically.',\n    recording_label: 'Recording',\n    recording_locked_hint: 'A replay of what the page showed and how it was used — always included with a recording. No camera or microphone. Sensitive fields are covered automatically.',\n    quickOptions: [\n      { emoji: '🐛', label: 'Broken', value: \"Something's broken\" },\n      { emoji: '💡', label: 'Suggestion', value: 'I have a suggestion' },\n      { emoji: '🤔', label: 'Confused', value: \"I'm confused\" },\n    ],\n  },\n  success: {\n    title: 'Thanks, your feedback is in.',\n    title_no_ai: \"Thanks! We've received your feedback.\",\n    close: 'Close',\n    email_saved: \"You'll get an email when it's resolved.\",\n    email_error: \"Couldn't save your email. Please try again.\",\n    track_feedback: 'Track your feedback',\n    email_cta: 'Email me when it\\'s fixed',\n    notify: 'Notify',\n  },\n  error: {\n    submit: 'Failed to submit. Please try again.',\n    finalize_title: \"We couldn't send this yet.\",\n    finalize_message: 'Your feedback is still saved. Please try again.',\n  },\n  pet: {\n    bubble: 'Need help?',\n  },\n  frustration: {\n    prompt: 'Something not working? Tell us.',\n    action: 'Annotate',\n    dismiss: 'Dismiss',\n  },\n  annotation: {\n    drawing: 'Drawing',\n    element: 'element',\n  },\n  draw: {\n    note_placeholder: 'Add a note to your drawing',\n    undo: 'Undo',\n    redo: 'Redo',\n    cancel: 'Cancel',\n    submit: 'Submit',\n  },\n  record: {\n    stop: 'Stop',\n    timer_label: 'Recording',\n    max_reached: 'Maximum recording time reached',\n    replay_label: 'screen-recording.json',\n    play: 'Play recording',\n    close: 'Close',\n    preview_unavailable: 'Preview unavailable',\n  },\n  clarify: {\n    title: 'Make This Better AI',\n    subtitle: 'Sharpening your feedback',\n    thinking: 'Thinking...',\n    placeholder: 'Add details, then submit',\n    send: 'Send',\n    submit: 'Submit',\n    send_feedback: 'Send feedback',\n    skip: 'Skip & submit',\n    skip_short: 'Skip',\n    cancel: \"Cancel — don't send feedback\",\n    retry: 'Retry',\n    error: \"Couldn't load the question. Send your feedback as is.\",\n    expired: 'This feedback session expired before it could be sent. Please close the widget and submit again.',\n  },\n}\n","export default {\n  tab: '反馈',\n  toolbar: {\n    markup: '标注',\n    record: '录屏',\n    hint: '点击标注，拖动绘制',\n    hintTouch: '用手指圈出问题',\n    hintRecord: '正在录制屏幕',\n    exit: '退出',\n  },\n  popup: {\n    about: '关于',\n    placeholder: '描述问题或建议...',\n    submit: '提交',\n    sending: '发送中...',\n    cancel: '取消',\n    close: '关闭',\n    my_feedback: '我的反馈',\n    screenshot_label: '附带页面截图',\n    screenshot_hint: '密码等敏感字段会自动打码。',\n    screenshot_short: '截图',\n    screenshot_locked_hint: '圈画会画在截图上，所以必定附带。敏感字段会自动打码。',\n    recording_label: '录制',\n    recording_locked_hint: '页面画面与操作过程的回放，录制内容必定随反馈提交。不含摄像头和麦克风，敏感字段会自动打码。',\n    quickOptions: [\n      { emoji: '🐛', label: '出错', value: '出问题了' },\n      { emoji: '💡', label: '建议', value: '我有一个建议' },\n      { emoji: '🤔', label: '疑问', value: '我有点疑问' },\n    ],\n  },\n  success: {\n    title: '收到了，谢谢你的反馈。',\n    title_no_ai: '感谢！我们已收到您的反馈。',\n    close: '关闭',\n    email_saved: '问题解决后您会收到邮件通知。',\n    email_error: '邮箱保存失败，请重试。',\n    track_feedback: '查看处理进度',\n    email_cta: '解决后邮件通知我',\n    notify: '通知我',\n  },\n  error: {\n    submit: '提交失败，请重试。',\n    finalize_title: '暂时无法发送。',\n    finalize_message: '你的反馈仍已保存，请重试。',\n  },\n  pet: {\n    bubble: '需要帮助吗？',\n  },\n  frustration: {\n    prompt: '遇到问题了？告诉我们。',\n    action: '标注',\n    dismiss: '关闭',\n  },\n  annotation: {\n    drawing: '绘制',\n    element: '元素',\n  },\n  draw: {\n    note_placeholder: '给你的绘制添加说明',\n    undo: '撤销',\n    redo: '重做',\n    cancel: '取消',\n    submit: '提交',\n  },\n  record: {\n    stop: '停止',\n    timer_label: '录制中',\n    max_reached: '已达到最长录制时间',\n    replay_label: 'screen-recording.json',\n    play: '播放录屏',\n    close: '关闭',\n    preview_unavailable: '暂无法预览',\n  },\n  clarify: {\n    title: 'Make This Better AI',\n    subtitle: '正在完善你的反馈',\n    thinking: '思考中...',\n    placeholder: '补充说明后提交',\n    send: '发送',\n    submit: '提交',\n    send_feedback: '发送反馈',\n    skip: '跳过并提交',\n    skip_short: '跳过',\n    cancel: '取消并不发送',\n    retry: '重试',\n    error: '追问加载失败，可以直接发送反馈。',\n    expired: '这条反馈在发送前已过期。请关闭组件后重新提交。',\n  },\n}\n","export default {\n  tab: 'フィードバック',\n  toolbar: {\n    markup: 'マークアップ',\n    record: '録画',\n    hint: 'クリックでコメント、ドラッグで描画',\n    hintTouch: 'ドラッグして問題箇所を囲んでください',\n    hintRecord: '画面を録画中',\n    exit: '終了',\n  },\n  popup: {\n    about: '概要',\n    placeholder: '問題や提案を入力してください...',\n    submit: '送信',\n    sending: '送信中...',\n    cancel: 'キャンセル',\n    close: '閉じる',\n    my_feedback: '自分のフィードバック',\n    screenshot_label: 'スクリーンショットを添付',\n    screenshot_hint: 'パスワードなどの機密項目は自動的に隠されます。',\n    screenshot_short: 'スクリーンショット',\n    screenshot_locked_hint: '描き込みはスクリーンショット上に焼き込まれるため、必ず添付されます。機密項目は自動的に隠されます。',\n    recording_label: '記録',\n    recording_locked_hint: 'ページの表示と操作の再現記録で、必ず添付されます。カメラやマイクは含まれず、機密項目は自動的に隠されます。',\n    quickOptions: [\n      { emoji: '🐛', label: '不具合', value: '不具合があります' },\n      { emoji: '💡', label: '提案', value: '提案があります' },\n      { emoji: '🤔', label: '不明点', value: 'わかりにくいです' },\n    ],\n  },\n  success: {\n    title: 'フィードバックを受け取りました。ありがとうございます。',\n    title_no_ai: 'ありがとうございます！フィードバックを受け付けました。',\n    close: '閉じる',\n    email_saved: '解決次第メールでお知らせします。',\n    email_error: 'メールアドレスを保存できませんでした。もう一度お試しください。',\n    track_feedback: '対応状況を確認',\n    email_cta: '解決したらメールで知らせる',\n    notify: '通知',\n  },\n  error: {\n    submit: '送信に失敗しました。もう一度お試しください。',\n    finalize_title: 'まだ送信できませんでした。',\n    finalize_message: 'フィードバックは保存されています。もう一度お試しください。',\n  },\n  pet: {\n    bubble: 'お困りですか？',\n  },\n  frustration: {\n    prompt: 'うまくいかないことがありますか？お知らせください。',\n    action: '注釈',\n    dismiss: '閉じる',\n  },\n  annotation: {\n    drawing: '描画',\n    element: '要素',\n  },\n  draw: {\n    note_placeholder: '描画にメモを追加',\n    undo: '元に戻す',\n    redo: 'やり直す',\n    cancel: 'キャンセル',\n    submit: '送信',\n  },\n  record: {\n    stop: '停止',\n    timer_label: '録画中',\n    max_reached: '最大記録時間に達しました',\n    replay_label: 'screen-recording.json',\n    play: '録画を再生',\n    close: '閉じる',\n    preview_unavailable: 'プレビューは利用できません',\n  },\n  clarify: {\n    title: 'Make This Better AI',\n    subtitle: 'フィードバックを整えています',\n    thinking: '考え中...',\n    placeholder: '詳細を追加して送信',\n    send: '送信',\n    submit: '送信する',\n    send_feedback: 'フィードバックを送信',\n    skip: 'スキップして送信',\n    skip_short: 'スキップ',\n    cancel: 'キャンセル — 送信しない',\n    retry: '再試行',\n    error: '質問を読み込めませんでした。そのままフィードバックを送信できます。',\n    expired: 'このフィードバックは送信される前に有効期限が切れました。ウィジェットを閉じて、もう一度送信してください。',\n  },\n}\n","export default {\n  tab: '피드백',\n  toolbar: {\n    markup: '마크업',\n    record: '녹화',\n    hint: '클릭하여 댓글, 드래그하여 그리기',\n    hintTouch: '드래그해서 문제가 있는 곳을 표시하세요',\n    hintRecord: '화면 녹화 중',\n    exit: '나가기',\n  },\n  popup: {\n    about: '정보',\n    placeholder: '문제 또는 제안 사항을 설명해 주세요...',\n    submit: '제출',\n    sending: '전송 중...',\n    cancel: '취소',\n    close: '닫기',\n    my_feedback: '내 피드백',\n    screenshot_label: '스크린샷 첨부',\n    screenshot_hint: '비밀번호 등 민감한 입력란은 자동으로 가려집니다.',\n    screenshot_short: '스크린샷',\n    screenshot_locked_hint: '그린 표시는 스크린샷 위에 합쳐지므로 항상 첨부됩니다. 민감한 입력란은 자동으로 가려집니다.',\n    recording_label: '기록',\n    recording_locked_hint: '페이지 화면과 조작 과정의 재현 기록으로, 항상 첨부됩니다. 카메라와 마이크는 포함되지 않으며 민감한 입력란은 자동으로 가려집니다.',\n    quickOptions: [\n      { emoji: '🐛', label: '오류', value: '문제가 있어요' },\n      { emoji: '💡', label: '제안', value: '제안이 있어요' },\n      { emoji: '🤔', label: '혼란', value: '헷갈려요' },\n    ],\n  },\n  success: {\n    title: '피드백이 접수되었습니다. 감사합니다.',\n    title_no_ai: '감사합니다! 피드백을 접수했습니다.',\n    close: '닫기',\n    email_saved: '해결되면 이메일로 알려드립니다.',\n    email_error: '이메일을 저장하지 못했습니다. 다시 시도해 주세요.',\n    track_feedback: '처리 상황 보기',\n    email_cta: '해결되면 이메일로 알림',\n    notify: '알림',\n  },\n  error: {\n    submit: '제출에 실패했습니다. 다시 시도해 주세요.',\n    finalize_title: '아직 전송하지 못했습니다.',\n    finalize_message: '피드백은 저장되어 있습니다. 다시 시도해 주세요.',\n  },\n  pet: {\n    bubble: '도움이 필요하신가요?',\n  },\n  frustration: {\n    prompt: '문제가 있으신가요? 알려 주세요.',\n    action: '주석',\n    dismiss: '닫기',\n  },\n  annotation: {\n    drawing: '그리기',\n    element: '요소',\n  },\n  draw: {\n    note_placeholder: '그림에 메모 추가',\n    undo: '실행 취소',\n    redo: '다시 실행',\n    cancel: '취소',\n    submit: '보내기',\n  },\n  record: {\n    stop: '중지',\n    timer_label: '녹화 중',\n    max_reached: '최대 녹화 시간에 도달했습니다',\n    replay_label: 'screen-recording.json',\n    play: '녹화 재생',\n    close: '닫기',\n    preview_unavailable: '미리보기를 사용할 수 없습니다',\n  },\n  clarify: {\n    title: 'Make This Better AI',\n    subtitle: '피드백을 다듬는 중',\n    thinking: '생각 중...',\n    placeholder: '자세히 적은 뒤 제출',\n    send: '보내기',\n    submit: '제출',\n    send_feedback: '피드백 보내기',\n    skip: '건너뛰고 보내기',\n    skip_short: '건너뛰기',\n    cancel: '취소 — 보내지 않기',\n    retry: '재시도',\n    error: '질문을 불러오지 못했습니다. 피드백을 그대로 보내세요.',\n    expired: '이 피드백 세션은 전송되기 전에 만료되었습니다. 위젯을 닫고 다시 보내 주세요.',\n  },\n}\n","export default {\n  tab: 'Comentarios',\n  toolbar: {\n    markup: 'Anotar',\n    record: 'Grabar',\n    hint: 'Clic para comentar, arrastrar para dibujar',\n    hintTouch: 'Arrastra para marcar el problema',\n    hintRecord: 'Grabando pantalla',\n    exit: 'Salir',\n  },\n  popup: {\n    about: 'Acerca de',\n    placeholder: 'Describe el problema o sugerencia...',\n    submit: 'Enviar',\n    sending: 'Enviando...',\n    cancel: 'Cancelar',\n    close: 'Cerrar',\n    my_feedback: 'Mis comentarios',\n    screenshot_label: 'Adjuntar captura de pantalla',\n    screenshot_hint: 'Las contraseñas y otros campos sensibles se ocultan automáticamente.',\n    screenshot_short: 'Captura',\n    screenshot_locked_hint: 'Tu dibujo se integra en la captura, por eso siempre se adjunta. Los campos sensibles se ocultan automáticamente.',\n    recording_label: 'Grabación',\n    recording_locked_hint: 'Una reproducción de lo que mostraba la página y cómo se usó; siempre se adjunta. Sin cámara ni micrófono. Los campos sensibles se ocultan automáticamente.',\n    quickOptions: [\n      { emoji: '🐛', label: 'Error', value: 'Algo no funciona' },\n      { emoji: '💡', label: 'Sugerencia', value: 'Tengo una sugerencia' },\n      { emoji: '🤔', label: 'Confuso', value: 'Estoy confundido' },\n    ],\n  },\n  success: {\n    title: 'Gracias, hemos recibido tu comentario.',\n    title_no_ai: '¡Gracias! Hemos recibido tu comentario.',\n    close: 'Cerrar',\n    email_saved: 'Recibirás un correo cuando se resuelva.',\n    email_error: 'No se pudo guardar tu correo. Inténtalo de nuevo.',\n    track_feedback: 'Seguir mi comentario',\n    email_cta: 'Avísame por correo cuando se arregle',\n    notify: 'Avisarme',\n  },\n  error: {\n    submit: 'Error al enviar. Por favor, inténtalo de nuevo.',\n    finalize_title: 'Aún no pudimos enviarlo.',\n    finalize_message: 'Tu comentario sigue guardado. Inténtalo de nuevo.',\n  },\n  pet: {\n    bubble: '¿Necesitas ayuda?',\n  },\n  frustration: {\n    prompt: '¿Algo no funciona? Cuéntanos.',\n    action: 'Anotar',\n    dismiss: 'Cerrar',\n  },\n  annotation: {\n    drawing: 'Dibujo',\n    element: 'elemento',\n  },\n  draw: {\n    note_placeholder: 'Añade una nota a tu dibujo',\n    undo: 'Deshacer',\n    redo: 'Rehacer',\n    cancel: 'Cancelar',\n    submit: 'Enviar',\n  },\n  record: {\n    stop: 'Detener',\n    timer_label: 'Grabando',\n    max_reached: 'Tiempo máximo de grabación alcanzado',\n    replay_label: 'screen-recording.json',\n    play: 'Reproducir grabación',\n    close: 'Cerrar',\n    preview_unavailable: 'Vista previa no disponible',\n  },\n  clarify: {\n    title: 'Make This Better AI',\n    subtitle: 'Afinando tus comentarios',\n    thinking: 'Pensando...',\n    placeholder: 'Añade detalles y envía',\n    send: 'Enviar',\n    submit: 'Enviar',\n    send_feedback: 'Enviar comentario',\n    skip: 'Omitir y enviar',\n    skip_short: 'Omitir',\n    cancel: 'Cancelar — no enviar',\n    retry: 'Reintentar',\n    error: 'No se pudo cargar la pregunta. Envía tus comentarios tal cual.',\n    expired: 'Esta sesión de comentarios caducó antes de poder enviarse. Cierra el widget y envíalo de nuevo.',\n  },\n}\n","export default {\n  tab: 'Avis',\n  toolbar: {\n    markup: 'Annoter',\n    record: 'Enregistrer',\n    hint: 'Cliquer pour commenter, glisser pour dessiner',\n    hintTouch: 'Faites glisser pour marquer le problème',\n    hintRecord: \"Enregistrement de l'écran\",\n    exit: 'Quitter',\n  },\n  popup: {\n    about: 'À propos',\n    placeholder: 'Décrivez le problème ou votre suggestion...',\n    submit: 'Envoyer',\n    sending: 'Envoi en cours...',\n    cancel: 'Annuler',\n    close: 'Fermer',\n    my_feedback: 'Mes avis',\n    screenshot_label: 'Joindre une capture d\\'écran',\n    screenshot_hint: 'Les mots de passe et autres champs sensibles sont masqués automatiquement.',\n    screenshot_short: 'Capture',\n    screenshot_locked_hint: \"Votre dessin est intégré à la capture, elle est donc toujours jointe. Les champs sensibles sont masqués automatiquement.\",\n    recording_label: 'Enregistrement',\n    recording_locked_hint: \"Une relecture de ce que la page affichait et de son utilisation — toujours jointe. Ni caméra ni micro. Les champs sensibles sont masqués automatiquement.\",\n    quickOptions: [\n      { emoji: '🐛', label: 'Bug', value: 'Quelque chose ne fonctionne pas' },\n      { emoji: '💡', label: 'Suggestion', value: \"J'ai une suggestion\" },\n      { emoji: '🤔', label: 'Confus', value: 'Je suis perplexe' },\n    ],\n  },\n  success: {\n    title: 'Merci, votre retour est bien arrivé.',\n    title_no_ai: 'Merci ! Nous avons bien reçu votre avis.',\n    close: 'Fermer',\n    email_saved: 'Vous recevrez un e-mail une fois résolu.',\n    email_error: \"Impossible d'enregistrer votre e-mail. Veuillez réessayer.\",\n    track_feedback: 'Suivre mon retour',\n    email_cta: 'M’avertir par e-mail une fois corrigé',\n    notify: 'Avertir',\n  },\n  error: {\n    submit: \"Échec de l'envoi. Veuillez réessayer.\",\n    finalize_title: \"Nous n'avons pas encore pu l'envoyer.\",\n    finalize_message: 'Votre retour est toujours enregistré. Veuillez réessayer.',\n  },\n  pet: {\n    bubble: \"Besoin d'aide ?\",\n  },\n  frustration: {\n    prompt: 'Un problème ? Dites-le-nous.',\n    action: 'Annoter',\n    dismiss: 'Fermer',\n  },\n  annotation: {\n    drawing: 'Dessin',\n    element: 'élément',\n  },\n  draw: {\n    note_placeholder: 'Ajoutez une note à votre dessin',\n    undo: 'Annuler',\n    redo: 'Rétablir',\n    cancel: 'Annuler',\n    submit: 'Envoyer',\n  },\n  record: {\n    stop: 'Arrêter',\n    timer_label: 'Enregistrement',\n    max_reached: \"Durée maximale d'enregistrement atteinte\",\n    replay_label: 'screen-recording.json',\n    play: \"Lire l'enregistrement\",\n    close: 'Fermer',\n    preview_unavailable: 'Aperçu indisponible',\n  },\n  clarify: {\n    title: 'Make This Better AI',\n    subtitle: 'Affinement de votre retour',\n    thinking: 'Réflexion...',\n    placeholder: 'Ajoutez des détails, puis envoyez',\n    send: 'Envoyer',\n    submit: 'Soumettre',\n    send_feedback: 'Envoyer le retour',\n    skip: 'Ignorer et envoyer',\n    skip_short: 'Ignorer',\n    cancel: 'Annuler — ne pas envoyer',\n    retry: 'Réessayer',\n    error: 'Impossible de charger la question. Envoyez votre avis tel quel.',\n    expired: 'Cette session de retour a expiré avant d\\'avoir pu être envoyée. Fermez le widget et renvoyez-la.',\n  },\n}\n","export default {\n  tab: 'Feedback',\n  toolbar: {\n    markup: 'Markieren',\n    record: 'Aufnehmen',\n    hint: 'Klicken zum Kommentieren, ziehen zum Zeichnen',\n    hintTouch: 'Ziehen, um das Problem zu markieren',\n    hintRecord: 'Bildschirm wird aufgenommen',\n    exit: 'Beenden',\n  },\n  popup: {\n    about: 'Info',\n    placeholder: 'Beschreiben Sie das Problem oder Ihren Vorschlag...',\n    submit: 'Absenden',\n    sending: 'Wird gesendet...',\n    cancel: 'Abbrechen',\n    close: 'Schließen',\n    my_feedback: 'Mein Feedback',\n    screenshot_label: 'Screenshot anhängen',\n    screenshot_hint: 'Passwörter und andere sensible Felder werden automatisch abgedeckt.',\n    screenshot_short: 'Screenshot',\n    screenshot_locked_hint: 'Deine Zeichnung wird in den Screenshot eingebrannt, daher wird er immer angehängt. Sensible Felder werden automatisch abgedeckt.',\n    recording_label: 'Aufzeichnung',\n    recording_locked_hint: 'Eine Wiedergabe dessen, was die Seite zeigte und wie sie bedient wurde — immer angehängt. Keine Kamera, kein Mikrofon. Sensible Felder werden automatisch abgedeckt.',\n    quickOptions: [\n      { emoji: '🐛', label: 'Fehler', value: 'Etwas funktioniert nicht' },\n      { emoji: '💡', label: 'Vorschlag', value: 'Ich habe einen Vorschlag' },\n      { emoji: '🤔', label: 'Unklar', value: 'Ich bin verwirrt' },\n    ],\n  },\n  success: {\n    title: 'Danke, Ihr Feedback ist angekommen.',\n    title_no_ai: 'Vielen Dank! Wir haben Ihr Feedback erhalten.',\n    close: 'Schließen',\n    email_saved: 'Sie erhalten eine E-Mail, sobald es gelöst ist.',\n    email_error: 'E-Mail konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.',\n    track_feedback: 'Feedback verfolgen',\n    email_cta: 'E-Mail, sobald es behoben ist',\n    notify: 'Melden',\n  },\n  error: {\n    submit: 'Senden fehlgeschlagen. Bitte versuchen Sie es erneut.',\n    finalize_title: 'Wir konnten dies noch nicht senden.',\n    finalize_message: 'Ihr Feedback ist weiterhin gespeichert. Bitte versuchen Sie es erneut.',\n  },\n  pet: {\n    bubble: 'Brauchen Sie Hilfe?',\n  },\n  frustration: {\n    prompt: 'Etwas funktioniert nicht? Sagen Sie es uns.',\n    action: 'Kommentieren',\n    dismiss: 'Schließen',\n  },\n  annotation: {\n    drawing: 'Zeichnung',\n    element: 'Element',\n  },\n  draw: {\n    note_placeholder: 'Fügen Sie Ihrer Zeichnung eine Notiz hinzu',\n    undo: 'Rückgängig',\n    redo: 'Wiederholen',\n    cancel: 'Abbrechen',\n    submit: 'Absenden',\n  },\n  record: {\n    stop: 'Stopp',\n    timer_label: 'Aufnahme',\n    max_reached: 'Maximale Aufnahmedauer erreicht',\n    replay_label: 'screen-recording.json',\n    play: 'Aufnahme abspielen',\n    close: 'Schließen',\n    preview_unavailable: 'Vorschau nicht verfügbar',\n  },\n  clarify: {\n    title: 'Make This Better AI',\n    subtitle: 'Wir schärfen dein Feedback',\n    thinking: 'Einen Moment...',\n    placeholder: 'Details ergänzen, dann absenden',\n    send: 'Senden',\n    submit: 'Absenden',\n    send_feedback: 'Feedback senden',\n    skip: 'Überspringen & senden',\n    skip_short: 'Überspringen',\n    cancel: 'Abbrechen — nicht senden',\n    retry: 'Erneut versuchen',\n    error: 'Die Frage konnte nicht geladen werden. Senden Sie Ihr Feedback einfach so.',\n    expired: 'Diese Feedback-Sitzung ist abgelaufen, bevor sie gesendet werden konnte. Bitte schließe das Widget und sende erneut.',\n  },\n}\n","import en from './en'\nimport zhCN from './zh-CN'\nimport ja from './ja'\nimport ko from './ko'\nimport es from './es'\nimport fr from './fr'\nimport de from './de'\n\nexport type I18nMessages = typeof en\n\nconst locales: Record<string, I18nMessages> = {\n  en,\n  'zh-CN': zhCN,\n  ja,\n  ko,\n  es,\n  fr,\n  de,\n}\n\nexport function getMessages(locale: string): I18nMessages {\n  if (locales[locale]) return locales[locale]\n  const lang = locale.split('-')[0]\n  if (locales[lang]) return locales[lang]\n  return locales['en']\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport { BLOCK_CLASS, MASK_TEXT_CLASS } from '../privacy/dom'\nimport { filterInputValue } from '../privacy/fields'\nimport { FILTERED_VALUE, filterSensitiveValue } from '../privacy/sanitize'\nimport {\n  createSensitiveTextContext,\n  hasSensitiveTextContext,\n  type SensitiveTextContext,\n} from '../privacy/text-context'\n\ntype RRWebEvent = Record<string, unknown>\ntype StopFn = () => void\n\ninterface RRWebRecordOptions {\n  emit: (event: RRWebEvent) => void\n  maskAllInputs: boolean\n  maskInputOptions: Record<string, boolean>\n  maskInputFn: (text: string, element: HTMLElement) => string\n  maskTextFn: () => string\n  blockClass: string\n  maskTextClass: string\n}\n\ninterface RRWebRecordFn {\n  (options: RRWebRecordOptions): StopFn\n  addCustomEvent: (tag: string, payload: Record<string, unknown>) => void\n  mirror: { getNode: (id: number) => Node | null }\n}\n\nexport interface RecordingResult {\n  events: RRWebEvent[]\n  duration: number\n}\n\nconst MAX_DURATION_S = 60\nconst VALUE_ATTRIBUTE_NAMES = ['value', 'content'] as const\nconst MASKED_INPUT_TYPES = Object.fromEntries([\n  'button',\n  'checkbox',\n  'color',\n  'date',\n  'datetime-local',\n  'email',\n  'file',\n  'hidden',\n  'image',\n  'month',\n  'number',\n  'password',\n  'radio',\n  'range',\n  'reset',\n  'search',\n  'select',\n  'submit',\n  'tel',\n  'text',\n  'textarea',\n  'time',\n  'url',\n  'week',\n].map(type => [type, true]))\n// The marker classes live with the rest of the privacy policy in privacy/dom;\n// re-exported here because this module has been their documented import point\n// since 1.x.\nexport { BLOCK_CLASS, MASK_TEXT_CLASS }\n// Pinned version + SRI: this script executes on customers' pages, so a\n// floating tag or unverified content would be a supply-chain hole. Bumping\n// the version requires recomputing the hash from the exact CDN file:\n//   curl -sL <url> | openssl dgst -sha384 -binary | openssl base64 -A\n// Must stay on @rrweb/record's static /umd/ build: the `rrweb` package ships\n// no UMD in 2.x (its dist files are ESM and throw \"Unexpected token 'export'\"\n// in a classic script tag), and jsDelivr's auto-minified files are unsafe for\n// SRI because their hash changes when jsDelivr upgrades its minifier.\nconst RRWEB_CDN = 'https://cdn.jsdelivr.net/npm/@rrweb/record@2.1.0/umd/record.min.js'\nconst RRWEB_SRI = 'sha384-MrD66HBNSykaP2N95+6hQCFlF5oH2tvL3TD/zyvHNkP/sAFWZx98DX9MEDy8MdVT'\n\nlet rrwebLoaded: Promise<RRWebRecordFn> | null = null\n\n// The IIFE/CDN bundle runs as a classic script, where the bare\n// import('@rrweb/record') can never resolve — without this flag every CDN\n// user's first recording would pay a thrown TypeError (console noise on the\n// customer's page) plus a wasted round before the CDN fallback. The typeof\n// guard keeps the untransformed source (vitest) on the ESM path.\nfunction isIifeBuild(): boolean {\n  return typeof __MTB_IIFE__ !== 'undefined' && __MTB_IIFE__\n}\n\nfunction loadRrweb(): Promise<RRWebRecordFn> {\n  if (rrwebLoaded) return rrwebLoaded\n  const load = isIifeBuild() ? loadViaCdn() : loadViaImport().catch(() => loadViaCdn())\n  // A rejection must not stay cached: one flaky-network failure would otherwise\n  // make every later Record tap fail instantly until the page reloads. Same\n  // reset-on-failure as record/replay.ts.\n  rrwebLoaded = load.catch((err) => {\n    rrwebLoaded = null\n    throw err\n  })\n  return rrwebLoaded\n}\n\nfunction filterReplayEvent(event: RRWebEvent, record: RRWebRecordFn): RRWebEvent {\n  const filtered = filterSensitiveValue(event)\n  filterReplayDomValues(filtered, record, createSensitiveTextContext())\n  return filtered\n}\n\nfunction filterReplayDomValues(\n  value: unknown,\n  record: RRWebRecordFn,\n  textContext: SensitiveTextContext,\n): void {\n  if (Array.isArray(value)) {\n    value.forEach(item => filterReplayDomValues(item, record, textContext))\n    return\n  }\n  if (!isRecord(value)) return\n\n  const attributes = isRecord(value.attributes) ? value.attributes : null\n  const hasAttributeValue = attributes && VALUE_ATTRIBUTE_NAMES.some(name => typeof attributes[name] === 'string')\n  const hasNodeValue = typeof value.text === 'string'\n    || typeof value.textContent === 'string'\n    || typeof value.value === 'string'\n    || hasAttributeValue\n  const node = hasNodeValue && typeof value.id === 'number' ? record.mirror.getNode(value.id) : null\n  if (node instanceof Element) {\n    if (typeof value.text === 'string') value.text = filterInputValue(value.text, node)\n    if (attributes) {\n      for (const name of VALUE_ATTRIBUTE_NAMES) {\n        const attributeValue = attributes[name]\n        if (typeof attributeValue === 'string') attributes[name] = filterInputValue(attributeValue, node)\n      }\n    }\n  } else if (node?.nodeType === Node.TEXT_NODE && hasSensitiveTextContext(node, textContext)) {\n    if (typeof value.textContent === 'string') value.textContent = FILTERED_VALUE\n    if (typeof value.value === 'string') value.value = FILTERED_VALUE\n  }\n\n  Object.values(value).forEach(item => filterReplayDomValues(item, record, textContext))\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n}\n\nasync function loadViaImport(): Promise<RRWebRecordFn> {\n  const mod = await import('@rrweb/record')\n  return mod.record as unknown as RRWebRecordFn\n}\n\ninterface RRWebGlobals {\n  rrwebRecord?: { record: RRWebRecordFn }\n  rrweb?: { record: RRWebRecordFn }\n}\n\nfunction findGlobalRecord(): RRWebRecordFn | undefined {\n  const win = globalThis as unknown as Window & RRWebGlobals\n  return win.rrwebRecord?.record ?? win.rrweb?.record\n}\n\nfunction loadViaCdn(): Promise<RRWebRecordFn> {\n  return new Promise((resolve, reject) => {\n    const existing = findGlobalRecord()\n    if (existing) { resolve(existing); return }\n    const script = document.createElement('script')\n    script.src = RRWEB_CDN\n    script.integrity = RRWEB_SRI\n    script.crossOrigin = 'anonymous'\n    script.onload = () => {\n      const record = findGlobalRecord()\n      if (record) resolve(record)\n      else reject(new Error('rrweb record global not found after script load'))\n    }\n    script.onerror = () => reject(new Error('Failed to load rrweb from CDN'))\n    document.head.appendChild(script)\n  })\n}\n\nexport class RecordSession {\n  private events: RRWebEvent[] = []\n  private stopFn: StopFn | null = null\n  private startedAt = 0\n  private timer: ReturnType<typeof setTimeout> | null = null\n  private clickHandler: ((e: MouseEvent) => void) | null = null\n  private shadow: ShadowContainer\n  private onMaxReached?: () => void\n  private stopped = false\n  private destroyed = false\n\n  constructor(shadow: ShadowContainer, onMaxReached?: () => void) {\n    this.shadow = shadow\n    this.onMaxReached = onMaxReached\n  }\n\n  async start(): Promise<void> {\n    const record = await loadRrweb()\n\n    // A cold dynamic import takes hundreds of milliseconds, and the reporter can\n    // cancel the replay inside that window. Without this guard the resumed start()\n    // would attach a recorder, a document click listener and a 60s timer that no\n    // live object holds a handle to — rrweb would then serialize every mutation of\n    // the host page for the rest of the session, after an explicit cancel.\n    if (this.destroyed) return\n\n    this.startedAt = Date.now()\n    this.stopped = false\n\n    this.stopFn = record({\n      emit: (event: RRWebEvent) => {\n        if (this.stopped) return\n        try {\n          this.events.push(filterReplayEvent(event, record))\n        } catch {\n          // A malformed host event must not bypass filtering or stop recording.\n        }\n      },\n      // Explicit options include hidden/file, which rrweb's maskAllInputs=true\n      // preset omits. Every value is routed through the fixed policy, where\n      // ordinary values are returned unchanged.\n      maskAllInputs: false,\n      maskInputOptions: MASKED_INPUT_TYPES,\n      maskInputFn: (text, element) => (\n        element.closest(`.${MASK_TEXT_CLASS}`) ? FILTERED_VALUE : filterInputValue(text, element)\n      ),\n      maskTextFn: () => FILTERED_VALUE,\n      blockClass: BLOCK_CLASS,\n      maskTextClass: MASK_TEXT_CLASS,\n    })\n\n    this.clickHandler = (e: MouseEvent) => {\n      this.showClickRipple(e.clientX, e.clientY)\n      record.addCustomEvent?.('mtb-click', {\n        x: e.clientX,\n        y: e.clientY,\n        target: e.target instanceof HTMLElement ? e.target.tagName : '',\n      })\n    }\n    document.addEventListener('click', this.clickHandler, true)\n\n    this.timer = setTimeout(() => {\n      this.onMaxReached?.()\n    }, MAX_DURATION_S * 1000)\n  }\n\n  stop(): RecordingResult {\n    this.stopped = true\n    this.stopFn?.()\n    this.stopFn = null\n    this.cleanupTimer()\n    this.cleanupClickHandler()\n\n    return {\n      events: this.events,\n      duration: this.getDuration(),\n    }\n  }\n\n  getDuration(): number {\n    if (this.startedAt === 0) return 0\n    return Math.max(1, Math.round((Date.now() - this.startedAt) / 1000))\n  }\n\n  destroy(): void {\n    this.destroyed = true\n    this.stopped = true\n    this.stopFn?.()\n    this.stopFn = null\n    this.cleanupTimer()\n    this.cleanupClickHandler()\n    this.events = []\n    this.startedAt = 0\n  }\n\n  private cleanupTimer(): void {\n    if (this.timer) {\n      clearTimeout(this.timer)\n      this.timer = null\n    }\n  }\n\n  private cleanupClickHandler(): void {\n    if (this.clickHandler) {\n      document.removeEventListener('click', this.clickHandler, true)\n      this.clickHandler = null\n    }\n  }\n\n  private showClickRipple(x: number, y: number): void {\n    const ripple = this.shadow.el<HTMLDivElement>('div', 'mtb-click-ripple')\n    ripple.style.left = `${x}px`\n    ripple.style.top = `${y}px`\n    this.shadow.append(ripple)\n    ripple.addEventListener('animationend', () => ripple.remove())\n  }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { escapeHtml } from '../context/dom-utils'\n\nexport class RecordControlBar {\n  private el: HTMLDivElement\n  private timerEl!: HTMLSpanElement\n  private intervalId: ReturnType<typeof setInterval> | null = null\n  private getDuration: () => number\n\n  constructor(\n    shadow: ShadowContainer,\n    messages: I18nMessages,\n    getDuration: () => number,\n    onStop: () => void,\n  ) {\n    this.getDuration = getDuration\n\n    this.el = shadow.el<HTMLDivElement>('div', 'mtb-record-bar')\n    this.el.innerHTML = `\n      <span class=\"mtb-record-dot\"></span>\n      <span class=\"mtb-record-timer\">00:00</span>\n      <button class=\"mtb-record-stop\" type=\"button\">${escapeHtml(messages.record.stop)}</button>\n    `\n    shadow.append(this.el)\n\n    this.timerEl = this.el.querySelector('.mtb-record-timer')!\n    this.el.querySelector('.mtb-record-stop')!.addEventListener('click', onStop)\n\n    this.intervalId = setInterval(() => this.updateTimer(), 1000)\n  }\n\n  private updateTimer(): void {\n    const secs = this.getDuration()\n    const mm = String(Math.floor(secs / 60)).padStart(2, '0')\n    const ss = String(secs % 60).padStart(2, '0')\n    this.timerEl.textContent = `${mm}:${ss}`\n  }\n\n  destroy(): void {\n    if (this.intervalId) {\n      clearInterval(this.intervalId)\n      this.intervalId = null\n    }\n    this.el.remove()\n  }\n}\n","import { RecordSession } from '../record/session'\nimport type { RecordingResult } from '../record/session'\nimport { RecordControlBar } from '../record/control-bar'\nimport type { ShadowContainer } from './shadow'\nimport type { I18nMessages } from '../i18n'\n\nexport type { RecordingResult }\n\nexport class RecordingManager {\n  private session: RecordSession | null = null\n  private bar: RecordControlBar | null = null\n  private result: RecordingResult | null = null\n\n  async start(\n    shadow: ShadowContainer,\n    messages: I18nMessages,\n    onStop: () => void,\n  ): Promise<void> {\n    this.session = new RecordSession(shadow, onStop)\n    this.bar = new RecordControlBar(\n      shadow,\n      messages,\n      () => this.session?.getDuration() ?? 0,\n      onStop,\n    )\n    await this.session.start()\n  }\n\n  stop(): RecordingResult | null {\n    if (!this.session) return null\n    this.result = this.session.stop()\n    this.teardownUI()\n    return this.result\n  }\n\n  getRecording(): RecordingResult | null {\n    return this.result\n  }\n\n  destroy(): void {\n    this.teardownUI()\n    this.result = null\n  }\n\n  private teardownUI(): void {\n    this.session?.destroy()\n    this.session = null\n    this.bar?.destroy()\n    this.bar = null\n  }\n}\n","import { sanitizeUrl } from '../privacy/sanitize'\n\nexport interface PageContext {\n  page_url: string\n  user_agent: string\n  browser: string\n  os: string\n  screen_width: number\n  screen_height: number\n}\n\nexport function collectPageContext(): PageContext {\n  return {\n    page_url: sanitizeUrl(window.location.href),\n    user_agent: navigator.userAgent,\n    browser: detectBrowser(),\n    os: detectOS(),\n    screen_width: window.screen.width,\n    screen_height: window.screen.height,\n  }\n}\n\nfunction detectBrowser(): string {\n  const ua = navigator.userAgent\n  if (/Edg\\//.test(ua)) return `Edge ${ua.match(/Edg\\/(\\S+)/)?.[1] ?? ''}`\n  if (/OPR\\//.test(ua)) return `Opera ${ua.match(/OPR\\/(\\S+)/)?.[1] ?? ''}`\n  if (/Chrome\\//.test(ua)) return `Chrome ${ua.match(/Chrome\\/(\\S+)/)?.[1] ?? ''}`\n  if (/Safari\\//.test(ua) && /Version\\//.test(ua)) return `Safari ${ua.match(/Version\\/(\\S+)/)?.[1] ?? ''}`\n  if (/Firefox\\//.test(ua)) return `Firefox ${ua.match(/Firefox\\/(\\S+)/)?.[1] ?? ''}`\n  return 'Unknown'\n}\n\nfunction detectOS(): string {\n  const ua = navigator.userAgent\n  if (/Windows NT 10/.test(ua)) return 'Windows 10/11'\n  if (/Windows NT/.test(ua)) return 'Windows'\n  if (/Mac OS X 10_15|macOS 1[56789]/.test(ua)) return 'macOS'\n  if (/Mac OS X/.test(ua)) return 'macOS'\n  if (/Android/.test(ua)) return `Android ${ua.match(/Android (\\d+)/)?.[1] ?? ''}`\n  if (/iPhone|iPad|iPod/.test(ua)) return 'iOS'\n  if (/Linux/.test(ua)) return 'Linux'\n  return 'Unknown'\n}\n","import type { PageContext } from '../context/collector'\nimport { filterSensitiveValue, sanitizeUrl } from '../privacy/sanitize'\nimport type { Annotation, Breadcrumb, FeedbackPayload, MakeThisBetterConfig } from '../types'\nimport type { RecordingResult } from '../record/session'\n\nexport interface BuildPayloadOptions {\n  description: string\n  pageContext: PageContext\n  consoleErrors: string[]\n  annotation: Annotation | null\n  user: MakeThisBetterConfig['user']\n  recording?: RecordingResult\n  anonId?: string\n  anonEmail?: string\n  breadcrumbs?: Breadcrumb[]\n}\n\nexport function buildPayload(options: BuildPayloadOptions): FeedbackPayload {\n  const { description, pageContext, consoleErrors, annotation, user, recording, anonId, anonEmail, breadcrumbs } = options\n\n  const capturedContext = filterSensitiveValue({\n    description,\n    ...pageContext,\n    page_url: sanitizeUrl(pageContext.page_url),\n    console_errors: consoleErrors,\n    annotations: annotation ? [annotation] : [],\n    target_element: annotation?.targetSelector\n      ? {\n          selector: annotation.targetSelector,\n          text: annotation.targetText ?? '',\n          name: annotation.targetName ?? '',\n        }\n      : undefined,\n    recording_events: recording?.events,\n    recording_duration: recording?.duration,\n    breadcrumbs,\n  })\n\n  return {\n    ...capturedContext,\n    user_id: user?.id,\n    user_email: user?.email,\n    user_name: user?.name,\n    // An identified user carries no anonymous identity: the host has vouched\n    // for who the reporter is, so attaching the stored anon id/email would tie\n    // that account to a stranger's earlier anonymous submissions.\n    reporter_external_id: user ? undefined : anonId,\n    reporter_email: user ? undefined : anonEmail,\n  }\n}\n","import { bakeAnnotatedScreenshot, captureBaseScreenshot, warmFontEmbedCss } from '../screenshot/capture'\nimport type { BaseScreenshot } from '../screenshot/capture'\nimport { collectPageContext } from '../context/collector'\nimport { buildPayload } from './payload'\nimport { getAnonId, getStoredReporterEmail } from './anon-id'\nimport type { ApiClient } from '../api/client'\nimport type { ConsoleErrorCollector } from '../context/console'\nimport type { BreadcrumbCollector } from '../context/breadcrumbs'\nimport type { ShadowContainer } from './shadow'\nimport type { RecordingResult } from './recording-manager'\nimport type { WidgetBrandColors } from './brand-colors'\nimport type { getMessages } from '../i18n'\nimport type { MakeThisBetterConfig, Annotation, SubmissionSessionResponse, FeedbackResponse } from '../types'\n\nconst MIN_FINALIZE_LOADING_MS = 300\nexport const IDLE_CAPTURE_MS = 900\nexport const SCREENSHOT_WAIT_MS = 5_000\n\n/**\n * Mode/UI transitions the lifecycle needs from its owner. The controller owns\n * mode and the launcher tab; the lifecycle only reports when a submission\n * outcome forces a transition, so \"which UI is on screen\" stays decided in one\n * place.\n */\nexport interface SubmissionLifecycleHost {\n  /** True once the conversation has transitioned to its final receipt state. */\n  isBackground(): boolean\n  /** Replace the current conversation receipt with its retryable failure state. */\n  onFinalizeFailure(onRetry?: () => void): void\n  /** Return to the background-finalizing state ahead of a retry. */\n  onReenterBackground(): void\n  /** A finalize completed: hand off the feedback and show success. */\n  onFinalized(feedback: FeedbackResponse, submission: SubmissionSessionResponse): void\n}\n\n/**\n * Everything the submission pipeline reads from the widget. Getters where the\n * value can change after construction (locale swaps messages; shadow only\n * exists once startup succeeded).\n */\nexport interface SubmissionLifecycleDeps {\n  apiClient: ApiClient\n  consoleCollector: ConsoleErrorCollector\n  breadcrumbCollector: BreadcrumbCollector\n  brandColors: WidgetBrandColors | undefined\n  shadow: () => ShadowContainer\n  messages: () => ReturnType<typeof getMessages>\n  user: () => MakeThisBetterConfig['user']\n}\n\n/**\n * The submission concurrency state machine: attempt versioning, stale-request\n * abandonment, the foreground/background finalize dual path, and the failure\n * card's retry loop. Extracted so the controller's mode/UI orchestration and\n * this bookkeeping cannot silently interleave.\n */\nexport class SubmissionLifecycle {\n  private activeSubmission: SubmissionSessionResponse | null = null\n  private pendingSubmissionRequest: Promise<SubmissionSessionResponse | null> | null = null\n  private submissionAttempt = 0\n  private pendingScreenshotTask: Promise<void> | null = null\n  private screenshotAbortController: AbortController | null = null\n  private pendingBaseCapture: Promise<BaseScreenshot | null> | null = null\n  private idleCaptureTimer: ReturnType<typeof setTimeout> | null = null\n  private screenshotSession: Promise<SubmissionSessionResponse | null> | null = null\n  private screenshotSessionReady: Promise<void> | null = null\n  private resolveScreenshotSessionReady: (() => void) | null = null\n  private screenshotEnabled = true\n\n  constructor(\n    private deps: SubmissionLifecycleDeps,\n    private host: SubmissionLifecycleHost,\n  ) {}\n\n  /**\n   * What the note surface must render. Reading it back rather than assuming\n   * the default is what keeps the checkbox from claiming a screenshot is\n   * attached while this object has already been told otherwise — the popup is\n   * remounted mid-session (record mode returns through it), so \"freshly opened\"\n   * does not imply \"freshly consented\".\n   */\n  isScreenshotEnabled(): boolean {\n    return this.screenshotEnabled\n  }\n\n  /**\n   * Back to the default for a fresh report. Unlike setScreenshotEnabled(true)\n   * this starts no capture: the widget is idle at both call sites, and\n   * rasterizing a page nobody is annotating would be work done for a report\n   * that may never be written.\n   */\n  resetScreenshotConsent(): void {\n    this.screenshotEnabled = true\n  }\n\n  /**\n   * Consent, as reported by the note surface's checkbox.\n   *\n   * Turning it off prevents capture at submit. Turning it back on warms the\n   * lazy screenshot dependency while the reporter is still composing.\n   */\n  setScreenshotEnabled(enabled: boolean): void {\n    this.screenshotEnabled = enabled\n    if (enabled) {\n      this.warmScreenshotDeps()\n      return\n    }\n    this.clearIdleCaptureTimer()\n    this.screenshotAbortController?.abort()\n    this.screenshotAbortController = null\n    this.pendingBaseCapture = null\n  }\n\n  // Load the screenshot dependency and font CSS ahead of submit. Bitmap capture\n  // starts alongside Session creation; only cross-origin fallback waits for it.\n  warmScreenshotDeps(): void {\n    if (!this.screenshotEnabled) return\n    void warmFontEmbedCss()\n  }\n\n  // Preserve the viewport where the first annotation was made without\n  // rasterizing while the reporter is actively typing. Cross-origin resource\n  // resolution can wait for Session credentials without changing that frame.\n  beginBaseCapture(): void {\n    if (!this.screenshotEnabled || this.pendingBaseCapture) return\n    this.warmScreenshotDeps()\n    this.clearIdleCaptureTimer()\n    this.idleCaptureTimer = setTimeout(() => {\n      this.idleCaptureTimer = null\n      const controller = new AbortController()\n      this.screenshotAbortController?.abort()\n      this.screenshotAbortController = controller\n      const sessionReady = this.ensureScreenshotSessionReady()\n      this.pendingBaseCapture = captureBaseScreenshot(async (resourceUrl, contentType) => {\n        await Promise.race([sessionReady, waitForAbort(controller.signal)])\n        if (controller.signal.aborted) return undefined\n        const submission = await this.screenshotSession\n        if (!submission || controller.signal.aborted) return undefined\n        return this.deps.apiClient.resolveScreenshotResourceUrl(\n          submission.id,\n          submission.token,\n          resourceUrl,\n          contentType,\n          controller.signal,\n        )\n      }, controller.signal)\n    }, IDLE_CAPTURE_MS)\n  }\n\n  private clearIdleCaptureTimer(): void {\n    if (this.idleCaptureTimer === null) return\n    clearTimeout(this.idleCaptureTimer)\n    this.idleCaptureTimer = null\n  }\n\n  private ensureScreenshotSessionReady(): Promise<void> {\n    this.screenshotSessionReady ??= new Promise<void>((resolve) => {\n      this.resolveScreenshotSessionReady = resolve\n    })\n    return this.screenshotSessionReady\n  }\n\n  start(\n    description: string,\n    annotation: Annotation | null,\n    recording: RecordingResult | undefined,\n  ): Promise<SubmissionSessionResponse | null> {\n    const submissionAttempt = ++this.submissionAttempt\n    const request = this.createSubmission(description, annotation, recording, submissionAttempt)\n    this.pendingSubmissionRequest = request\n    return request\n  }\n\n  private async createSubmission(\n    description: string,\n    annotation: Annotation | null,\n    recording: RecordingResult | undefined,\n    submissionAttempt: number,\n  ): Promise<SubmissionSessionResponse | null> {\n    await waitForPaint()\n    if (submissionAttempt !== this.submissionAttempt) return null\n    const pageContext = collectPageContext()\n\n    const payload = buildPayload({\n      description,\n      pageContext,\n      consoleErrors: this.deps.consoleCollector.getErrors(),\n      annotation,\n      user: this.deps.user(),\n      recording,\n      anonId: getAnonId(),\n      anonEmail: getStoredReporterEmail(),\n      breadcrumbs: this.deps.breadcrumbCollector.getBreadcrumbs(),\n    })\n\n    // Create the Session and capture concurrently. Directly readable resources\n    // can finish without the Session; only proxy fallback and upload await it.\n    let submissionRequest: Promise<SubmissionSessionResponse>\n    try {\n      submissionRequest = this.deps.apiClient.createSubmissionSession(payload, null)\n    } catch {\n      return null\n    }\n\n    this.clearIdleCaptureTimer()\n    this.screenshotSession = submissionRequest.catch(() => null)\n    this.resolveScreenshotSessionReady?.()\n\n    let screenshotController: AbortController | null = null\n    if (this.screenshotEnabled) {\n      const controller = this.screenshotAbortController ?? new AbortController()\n      screenshotController = controller\n      this.screenshotAbortController = controller\n      const baseCapture = this.pendingBaseCapture\n      const screenshotTask = (async () => {\n        // Wait for the session response before starting the heavy capture.\n        // Both this task and the main path await the same promise; the main\n        // path's await was registered first, so its microtask runs first —\n        // returning the submission and letting onClarify fire before toJpeg\n        // blocks the main thread.\n        const submission = await this.screenshotSession\n        if (!submission || controller.signal.aborted || submissionAttempt !== this.submissionAttempt) return\n        try {\n          const resolver = async (resourceUrl: string, contentType?: string) => {\n            if (controller.signal.aborted || submissionAttempt !== this.submissionAttempt) return undefined\n            return this.deps.apiClient.resolveScreenshotResourceUrl(\n              submission.id,\n              submission.token,\n              resourceUrl,\n              contentType,\n              controller.signal,\n            )\n          }\n          const base = baseCapture\n            ? await baseCapture\n            : await captureBaseScreenshot(resolver, controller.signal)\n          if (controller.signal.aborted || submissionAttempt !== this.submissionAttempt || !base) return\n          const screenshot = await bakeAnnotatedScreenshot(base, annotation ? [annotation] : [], this.deps.brandColors)\n          if (controller.signal.aborted || submissionAttempt !== this.submissionAttempt || !screenshot) return\n          await this.deps.apiClient.uploadScreenshot(submission.id, submission.token, screenshot, controller.signal)\n        } catch { /* screenshot is best-effort */ }\n      })()\n      const trackedTask = screenshotTask.finally(() => {\n        if (this.pendingScreenshotTask === trackedTask) this.pendingScreenshotTask = null\n        if (this.screenshotAbortController === controller) this.screenshotAbortController = null\n        if (this.pendingBaseCapture === baseCapture) this.pendingBaseCapture = null\n        this.screenshotSession = null\n        this.screenshotSessionReady = null\n        this.resolveScreenshotSessionReady = null\n      })\n      this.pendingScreenshotTask = trackedTask\n    }\n\n    let submission: SubmissionSessionResponse\n    try {\n      submission = await submissionRequest\n      if (submissionAttempt !== this.submissionAttempt) {\n        screenshotController?.abort()\n        await this.deps.apiClient.abandonSubmissionSession(submission.id, submission.token).catch(() => {})\n        return null\n      }\n      this.activeSubmission = submission\n    } catch {\n      screenshotController?.abort()\n      return null\n    }\n\n    return submission\n  }\n\n  async finalize(): Promise<void> {\n    const submissionAttempt = this.submissionAttempt\n    // Consent is per submission and this one is decided — its screenshot was\n    // captured (or declined) at session creation. Resetting here, when the\n    // receipt renders, rather than when the finalize round trip lands, means a\n    // reporter who starts their next report immediately gets a fresh default\n    // instead of inheriting a decline through the in-flight window.\n    this.resetScreenshotConsent()\n    // The conversation has become a receipt on the skip path. A finalization\n    // failure replaces that receipt with an in-card retry state.\n    const background = this.host.isBackground()\n    // Finalize can be asked for (skip button) while the session upload is still\n    // in flight — wait for it rather than failing a healthy submission.\n    const submission = this.activeSubmission ?? await (this.pendingSubmissionRequest ?? Promise.resolve(null))\n    if (submissionAttempt !== this.submissionAttempt) return\n    if (!submission) {\n      if (background) return this.showFinalizeFailure({ canRetry: false })\n      throw new Error('No active submission session')\n    }\n\n    await this.waitForScreenshot()\n    if (submissionAttempt !== this.submissionAttempt) return\n\n    // The loading floor protects the clarify card's busy state; on the\n    // background path the card is gone, so waiting would only delay the result.\n    const minimumLoading = background\n      ? Promise.resolve()\n      : new Promise<void>((resolve) => setTimeout(resolve, MIN_FINALIZE_LOADING_MS))\n    let feedback: FeedbackResponse\n    try {\n      feedback = await this.deps.apiClient.finalizeSubmissionSession(submission.id, submission.token)\n    } catch (error) {\n      if (background) return this.showFinalizeFailure({ canRetry: true })\n      throw error\n    }\n    await Promise.all([minimumLoading, waitForPaint()])\n    if (submissionAttempt !== this.submissionAttempt || this.activeSubmission !== submission) return\n\n    this.activeSubmission = null\n    this.pendingSubmissionRequest = null\n    this.host.onFinalized(feedback, submission)\n  }\n\n  private async waitForScreenshot(): Promise<void> {\n    const task = this.pendingScreenshotTask\n    if (!task) return\n\n    let timer: ReturnType<typeof setTimeout> | undefined\n    const completed = await Promise.race([\n      task.then(() => true),\n      new Promise<false>((resolve) => {\n        timer = setTimeout(() => resolve(false), SCREENSHOT_WAIT_MS)\n      }),\n    ])\n    if (timer !== undefined) clearTimeout(timer)\n    if (!completed) {\n      this.screenshotAbortController?.abort()\n      if (this.pendingScreenshotTask === task) this.pendingScreenshotTask = null\n    }\n  }\n\n  private showFinalizeFailure(options: {canRetry: boolean}): void {\n    this.host.onFinalizeFailure(options.canRetry ? () => this.retryBackgroundFinalization() : undefined)\n  }\n\n  private retryBackgroundFinalization(): void {\n    // Re-enter the background state so another failure comes back to this card\n    // rather than throwing with nothing on screen.\n    this.host.onReenterBackground()\n    void this.finalize().catch(() => this.showFinalizeFailure({canRetry: true}))\n  }\n\n  /**\n   * Invalidate every in-flight attempt and abandon what already reached the\n   * server. Both exit and destroy route through here.\n   */\n  abandon(): void {\n    this.submissionAttempt += 1\n    this.clearIdleCaptureTimer()\n    this.screenshotAbortController?.abort()\n    this.screenshotAbortController = null\n    this.pendingScreenshotTask = null\n    this.pendingBaseCapture = null\n    this.screenshotSession = null\n    this.resolveScreenshotSessionReady?.()\n    this.screenshotSessionReady = null\n    this.resolveScreenshotSessionReady = null\n    // Consent is per submission, not per visitor. A reporter who declined once\n    // on a page with something private on it is not declining for every report\n    // they ever file, and a team silently receiving no screenshots forever\n    // would have no way to notice why.\n    //\n    // This covers the exit path only. A successful submit never routes through\n    // exitAll, so the controller resets there as well.\n    this.resetScreenshotConsent()\n    const submission = this.activeSubmission\n    this.activeSubmission = null\n    this.pendingSubmissionRequest = null\n    if (submission) {\n      void this.deps.apiClient.abandonSubmissionSession(submission.id, submission.token).catch(() => {})\n    }\n  }\n}\n\nfunction waitForPaint(): Promise<void> {\n  return new Promise<void>((r) => requestAnimationFrame(() => setTimeout(r, 0)))\n}\n\nfunction waitForAbort(signal: AbortSignal): Promise<void> {\n  if (signal.aborted) return Promise.resolve()\n  return new Promise<void>((resolve) => signal.addEventListener('abort', () => resolve(), { once: true }))\n}\n","import { isTouchPointer } from './pointer-kind'\nimport type { ToolbarMode } from '../annotate/toolbar'\nimport type { TargetRect } from '../types'\n\nexport type ModeKind = 'idle' | 'annotating' | 'recording' | 'drawing' | 'popup' | 'clarifying' | 'submitting'\n\nexport interface NotePopupOptions {\n  x: number\n  y: number\n  targetName: string\n  targetRect?: TargetRect\n}\n\n/**\n * The controller-side verbs a mode may perform. Each verb owns one concrete\n * mount, unmount, or hand-off; the states own which verbs run for which input\n * in which mode. Kept as an interface (implemented by WidgetController) so the\n * coupling between the machine and its resources is written down in one place.\n */\nexport interface ModeHost {\n  transitionTo(next: ModeState): void\n  exitAll(): void\n  dismissFrustrationPrompt(): void\n  /** True once the annotation toolbar is mounted — the flow chrome exists. */\n  hasAnnotationChrome(): boolean\n  /** First entry into markup: font warmup, toolbar, drawing surface, mobile strip. */\n  mountAnnotationChrome(): void\n  ensureSession(): void\n  destroySession(): void\n  clearCurrentAnnotation(): void\n  teardownDrawBar(): void\n  destroyRecordingManager(): void\n  startRecorder(): void\n  stopRecordingIntoNote(): void\n  cancelDraw(): void\n  mountDrawBar(): void\n  mountDrawSheet(): void\n  refreshDrawBar(): void\n  mountNotePopup(opts: NotePopupOptions): void\n  /** Abandon the note popup without ending the flow: surface, scrim and scroll lock come down, annotating resumes. */\n  dismissNotePopup(): void\n  /** Dismiss the success card from the previous submission. */\n  dismissSuccess(): void\n  revertToolbarDuringClarify(): void\n  releaseClarifyUi(): void\n}\n\n/**\n * One object per widget mode. The controller holds a single `state` reference\n * and delegates every mode-dependent input here; transitions run through\n * `transitionTo(next)`, which calls the outgoing state's exit() before the\n * incoming state's enter().\n *\n * enter() mounts what the mode itself needs. exit() is deliberately thin: the\n * toolbar and the annotation session span the whole flow (annotating through\n * clarifying), and several out-edges hand a resource to the next mode instead\n * of releasing it — the note popup's element becomes the clarify card, a\n * stopped recording becomes the submit payload. Those edges release or hand\n * off through their own named paths (cancelDraw, the submit teardown, exitAll),\n * and exitAll remains the universal release every abandon route ends in.\n */\nexport interface ModeState {\n  readonly kind: ModeKind\n  /** Whether the docked launcher renders lit while this mode is active. */\n  readonly launcherLit: boolean\n  /** Whether a frustration signal may surface its prompt in this mode. */\n  readonly acceptsFrustrationPrompt: boolean\n  enter(): void\n  exit(): void\n  /** Public API open(). Only idle acts; while open it is an idempotent no-op. */\n  handleOpen(): void\n  handleClose(): void\n  handleEscape(event: KeyboardEvent): void\n  handleTabClick(): void\n  handleToolbarModeChange(mode: ToolbarMode): void\n  /** A stroke landed on the annotation overlay. */\n  handleDrawStroke(): void\n}\n\nfunction consume(event: KeyboardEvent): void {\n  event.preventDefault()\n  event.stopPropagation()\n}\n\nabstract class BaseModeState implements ModeState {\n  abstract readonly kind: ModeKind\n  readonly launcherLit: boolean = true\n  readonly acceptsFrustrationPrompt: boolean = false\n\n  constructor(protected host: ModeHost) {}\n\n  enter(): void {}\n  exit(): void {}\n  handleOpen(): void {}\n  handleClose(): void {\n    this.host.exitAll()\n  }\n  handleEscape(_event: KeyboardEvent): void {}\n  handleTabClick(): void {\n    this.host.exitAll()\n  }\n  handleToolbarModeChange(_mode: ToolbarMode): void {}\n  handleDrawStroke(): void {}\n}\n\nexport class IdleState extends BaseModeState {\n  readonly kind = 'idle'\n  override readonly launcherLit = false\n  override readonly acceptsFrustrationPrompt = true\n\n  override handleOpen(): void {\n    this.host.dismissFrustrationPrompt()\n    this.host.dismissSuccess()\n    this.host.transitionTo(new AnnotatingState(this.host))\n  }\n\n  override handleClose(): void {\n    // Already closed; nothing to exit.\n  }\n\n  // Escape falls through untouched (not consumed) so the host page's own\n  // Escape handling still works while the widget shows nothing.\n\n  override handleTabClick(): void {\n    this.handleOpen()\n  }\n}\n\nexport class AnnotatingState extends BaseModeState {\n  readonly kind = 'annotating'\n\n  override enter(): void {\n    if (!this.host.hasAnnotationChrome()) {\n      this.host.mountAnnotationChrome()\n      return\n    }\n    // Resuming markup from recording, a cancelled drawing, or the note popup:\n    // the toolbar survived, only the mode-specific surfaces change hands.\n    this.host.destroyRecordingManager()\n    this.host.teardownDrawBar()\n    this.host.ensureSession()\n  }\n\n  // exit: the toolbar and session deliberately outlive this state — they span\n  // the whole flow and are released by the flow-ending paths, not per mode.\n\n  override handleEscape(event: KeyboardEvent): void {\n    consume(event)\n    this.host.exitAll()\n  }\n\n  override handleToolbarModeChange(mode: ToolbarMode): void {\n    if (mode === 'record') {\n      this.host.transitionTo(new RecordingState(this.host))\n    } else {\n      // Re-entering markup is idempotent: enter() finds the chrome and session\n      // already in place and changes nothing.\n      this.host.transitionTo(new AnnotatingState(this.host))\n    }\n  }\n\n  override handleDrawStroke(): void {\n    // The draw note bar packs a field, undo/redo, cancel and submit into one\n    // pill at the bottom of the screen — which on a phone is exactly where the\n    // keyboard lands, and none of it is thumb-sized. Touch collects the note in\n    // the same bottom sheet the pin flow already uses.\n    this.host.transitionTo(new DrawingState(this.host, isTouchPointer() ? 'sheet' : 'bar'))\n  }\n}\n\nexport class RecordingState extends BaseModeState {\n  readonly kind = 'recording'\n\n  override enter(): void {\n    // Recording exists to capture the user reproducing something, which the\n    // pin and draw surfaces would sit on top of — they come down first.\n    this.host.clearCurrentAnnotation()\n    this.host.teardownDrawBar()\n    this.host.destroySession()\n    this.host.startRecorder()\n  }\n\n  override handleEscape(event: KeyboardEvent): void {\n    // Escape here is the Stop button, not a cancel: the recording is kept and\n    // the note popup opens, exactly as if the reporter had pressed Stop. The\n    // recording itself is carried into the popup for the submit payload, so\n    // this edge hands the manager off instead of destroying it.\n    consume(event)\n    this.host.stopRecordingIntoNote()\n  }\n\n  override handleToolbarModeChange(mode: ToolbarMode): void {\n    // A second press of Record must not restart the session already running.\n    if (mode === 'record') return\n    this.host.transitionTo(new AnnotatingState(this.host))\n  }\n}\n\nexport class DrawingState extends BaseModeState {\n  readonly kind = 'drawing'\n\n  // Which note surface collects the drawing's note: the inline bar on a\n  // desktop, the bottom sheet on touch.\n  constructor(host: ModeHost, private surface: 'bar' | 'sheet') {\n    super(host)\n  }\n\n  override enter(): void {\n    if (this.surface === 'sheet') {\n      this.host.mountDrawSheet()\n    } else {\n      this.host.mountDrawBar()\n      this.host.refreshDrawBar()\n    }\n  }\n\n  override handleEscape(event: KeyboardEvent): void {\n    // One Escape costs the stroke, not the session: back to annotating.\n    consume(event)\n    this.host.cancelDraw()\n  }\n\n  override handleToolbarModeChange(mode: ToolbarMode): void {\n    if (mode === 'record') {\n      this.host.transitionTo(new RecordingState(this.host))\n    } else {\n      this.host.transitionTo(new AnnotatingState(this.host))\n    }\n  }\n\n  override handleDrawStroke(): void {\n    // Additional strokes while the note surface is already up only need the\n    // undo/redo affordances refreshed; the sheet has none.\n    if (this.surface === 'bar') this.host.refreshDrawBar()\n  }\n}\n\nexport class PopupState extends BaseModeState {\n  readonly kind = 'popup'\n\n  constructor(host: ModeHost, private opts: NotePopupOptions) {\n    super(host)\n  }\n\n  override enter(): void {\n    this.host.mountNotePopup(this.opts)\n  }\n\n  override handleEscape(event: KeyboardEvent): void {\n    consume(event)\n    this.host.exitAll()\n  }\n\n  override handleToolbarModeChange(mode: ToolbarMode): void {\n    if (mode === 'record') {\n      // The popup and its scrim deliberately stay up: stopping the recording\n      // opens a fresh note popup whose mount replaces both, so exactly one\n      // scrim exists throughout instead of an orphaned pair.\n      this.host.transitionTo(new RecordingState(this.host))\n    } else {\n      // Markup has no successor surface to replace the popup, so it must come\n      // down here. Left standing it is a zombie: a scrim and scroll lock the\n      // reporter cannot see past, and a checkbox whose change listener keeps\n      // writing screenshot consent for a note that was just abandoned.\n      this.host.dismissNotePopup()\n      this.host.transitionTo(new AnnotatingState(this.host))\n    }\n  }\n}\n\n/**\n * A question is open inside the chat card. The card is already mounted and\n * stays put, so entering mounts nothing — what changes is that the submission\n * is now on the server, which is why Escape stops being a cancel here.\n */\nexport class ChatClarifyingState extends BaseModeState {\n  readonly kind = 'clarifying'\n\n  // handleEscape stays a no-op here because the chat card owns its own Escape\n  // and routes it to the same cancel its X does — routing it twice would\n  // cancel an already-cancelled submission.\n\n  override handleToolbarModeChange(_mode: ToolbarMode): void {\n    // No new surface may open under the conversation; snap the toolbar's\n    // visual back to the mode the submission came from.\n    this.host.revertToolbarDuringClarify()\n  }\n}\n\nexport class SubmittingState extends BaseModeState {\n  readonly kind = 'submitting'\n  override readonly launcherLit = false\n\n  override enter(): void {\n    // Both entries converge here: skipping the clarification drops its chrome,\n    // and re-entering from a failure-card retry finds it already gone — every\n    // release below is idempotent.\n    this.host.releaseClarifyUi()\n  }\n\n  override handleTabClick(): void {\n    // Deliberate no-op: the launcher must not start a second feedback on top\n    // of the one still finalizing in the background.\n  }\n\n  // Escape falls through unconsumed: nothing is on screen to dismiss, and the\n  // background finalize must not be cancellable by a stray keypress.\n}\n","import type { MakeThisBetterConfig, WidgetBrandColors } from '../types'\n\nexport type { WidgetBrandColors } from '../types'\n\nconst HEX_COLOR = /^#[0-9a-f]{6}$/i\n\nexport function resolveBrandColors(\n  colors: MakeThisBetterConfig['brandColors'],\n): WidgetBrandColors | undefined {\n  if (colors === undefined) return undefined\n\n  if (typeof colors !== 'object' || colors === null) return rejectBrandColors()\n\n  const values = [colors.primary, colors.hover, colors.active, colors.onPrimary]\n  if (!values.every((color) => typeof color === 'string' && HEX_COLOR.test(color))) {\n    return rejectBrandColors()\n  }\n\n  return {\n    primary: colors.primary.toLowerCase(),\n    hover: colors.hover.toLowerCase(),\n    active: colors.active.toLowerCase(),\n    onPrimary: colors.onPrimary.toLowerCase(),\n  }\n}\n\nfunction rejectBrandColors(): undefined {\n  console.warn(\n    '[MakeThisBetter] brandColors requires primary, hover, active, and onPrimary as six-digit hex colors.',\n  )\n  return undefined\n}\n","import { ShadowContainer } from './shadow'\nimport { isImeConfirmKeydown } from '../context/dom-utils'\nimport { FeedbackTab } from './tab'\nimport { FrustrationPromptCard } from './frustration-prompt'\nimport { DimOverlay } from './dim-overlay'\nimport { isTouchPointer } from './pointer-kind'\nimport { PageOffset } from './page-offset'\nimport { lockPageScroll } from './scroll-lock'\nimport type { ScrollLock } from './scroll-lock'\nimport { AnnotationToolbar } from '../annotate/toolbar'\nimport { AnnotationSession } from '../annotate/session'\nimport { DrawNoteBar } from '../annotate/draw-note-bar'\nimport { ChatPopup } from '../popup/chat-popup'\nimport { ReceiptCard } from '../popup/receipt'\nimport { warmFontEmbedCss } from '../screenshot/capture'\nimport { ConsoleErrorCollector } from '../context/console'\nimport { BreadcrumbCollector } from '../context/breadcrumbs'\nimport { FrustrationDetector } from '../context/frustration'\nimport { isFrustrationDismissed, markFrustrationDismissed } from '../context/frustration-state'\nimport { ApiClient } from '../api/client'\nimport { cacheBoardUrl, getAnonId, getCachedBoardUrl, getStoredReporterEmail, rememberReporterEmail } from './anon-id'\nimport { getMessages } from '../i18n'\nimport { RecordingManager } from './recording-manager'\nimport { SubmissionLifecycle } from './submission-lifecycle'\nimport { AnnotatingState, ChatClarifyingState, IdleState, PopupState, SubmittingState } from './mode-states'\nimport type { ModeHost, ModeState, NotePopupOptions } from './mode-states'\nimport type { MakeThisBetterConfig, Annotation, SubmissionSessionResponse, FeedbackResponse } from '../types'\nimport { resolveBrandColors } from './brand-colors'\nimport type { WidgetBrandColors } from './brand-colors'\n\ntype Via = 'pin' | 'draw' | 'record'\n\n// A browser SDK has no build-time environment, so \"is a developer watching?\"\n// has to be inferred. Loopback and the .local/.test dev TLDs are where an\n// integrator runs; anything else is somebody's live site.\nfunction isLocalHost(): boolean {\n  const { hostname } = window.location\n  return (\n    hostname === 'localhost' ||\n    hostname === '127.0.0.1' ||\n    hostname === '[::1]' ||\n    hostname.endsWith('.localhost') ||\n    hostname.endsWith('.local') ||\n    hostname.endsWith('.test')\n  )\n}\n\nexport class WidgetController implements ModeHost {\n  private config: MakeThisBetterConfig\n  private brandColors: WidgetBrandColors | undefined\n  private shadow!: ShadowContainer\n  private tab: FeedbackTab | null = null\n  private toolbar: AnnotationToolbar | null = null\n  private session: AnnotationSession | null = null\n  private drawBar: DrawNoteBar | null = null\n  private dim: DimOverlay | null = null\n  private pageOffset = new PageOffset()\n  private chatPopup: ChatPopup | null = null\n  private scrollLock: ScrollLock | null = null\n  private success: ReceiptCard | null = null\n  private frustrationPrompt: FrustrationPromptCard | null = null\n  private apiClient: ApiClient\n  private consoleCollector: ConsoleErrorCollector\n  private breadcrumbCollector: BreadcrumbCollector\n  private frustrationDetector: FrustrationDetector | null = null\n  private messages: ReturnType<typeof getMessages>\n  // The mode machine. One state object per mode owns what mounts on entry and\n  // how each input behaves there; the controller only delegates and keeps the\n  // component references the states operate on (through the ModeHost verbs).\n  private state: ModeState = new IdleState(this)\n  private via: Via = 'pin'\n  private currentAnnotation: Annotation | null = null\n  private lastPopupPos: { x: number; y: number } | null = null\n  private recordingManager: RecordingManager | null = null\n  private lifecycle: SubmissionLifecycle\n  private touchHintShown = false\n  private lastHandoff: { feedbackId?: string; boardUrl?: string; identityToken?: string } = {}\n\n  constructor(config: MakeThisBetterConfig) {\n    this.config = config\n    // A missing key still renders a fully working widget whose every submit\n    // 401s — the reporter sees a generic failure and the integrator sees\n    // nothing at all. Say it at init, where the integrator is looking.\n    if (!config.projectKey) {\n      console.warn('[MakeThisBetter] projectKey is missing. Feedback submission will be rejected.')\n    }\n    this.brandColors = resolveBrandColors(config.brandColors)\n    this.messages = getMessages(config.locale ?? document.documentElement.lang ?? 'en')\n    this.apiClient = new ApiClient({\n      projectKey: config.projectKey,\n      apiUrl: config.apiUrl,\n      userToken: config.userToken,\n      userTokenFn: config.userTokenFn,\n    })\n    this.consoleCollector = new ConsoleErrorCollector()\n    this.consoleCollector.start()\n    this.breadcrumbCollector = new BreadcrumbCollector()\n    this.breadcrumbCollector.start()\n    this.lifecycle = new SubmissionLifecycle(\n      {\n        apiClient: this.apiClient,\n        consoleCollector: this.consoleCollector,\n        breadcrumbCollector: this.breadcrumbCollector,\n        brandColors: this.brandColors,\n        shadow: () => this.shadow,\n        messages: () => this.messages,\n        user: () => this.config.user,\n      },\n      {\n        isBackground: () => this.state.kind === 'submitting',\n        onFinalizeFailure: (onRetry) => this.chatPopup?.showFinalizeFailure(onRetry),\n        onReenterBackground: () => this.transitionTo(new SubmittingState(this)),\n        onFinalized: (feedback, submission) => this.handleFinalized(feedback, submission),\n      },\n    )\n    try {\n      this.buildUI()\n    } catch (error) {\n      // A page-supplied host can be an element that refuses attachShadow. The\n      // collectors above have already patched window.onerror and history, and\n      // letting this throw into the customer's script would leave those\n      // patches with no owner to release them — so roll back and go dark\n      // instead of crashing code that is not ours.\n      this.consoleCollector.stop()\n      this.breadcrumbCollector.stop()\n      console.warn('[MakeThisBetter] Widget failed to start:', error)\n      return\n    }\n\n    if (config.frustrationDetection !== false) {\n      this.frustrationDetector = new FrustrationDetector(() => this.handleFrustration())\n      this.frustrationDetector.start()\n    }\n\n    document.addEventListener('keydown', this.handleKeydown, true)\n  }\n\n  /**\n   * The single mutation point of the mode machine: the outgoing state exits,\n   * the incoming state enters, and the launcher reflects the new mode.\n   */\n  transitionTo(next: ModeState): void {\n    const previous = this.state\n    this.state = next\n    previous.exit()\n    next.enter()\n    this.tab?.setActive(next.launcherLit)\n  }\n\n  private handleKeydown = (event: KeyboardEvent): void => {\n    if (event.key !== 'Escape') return\n    // Escape during IME composition cancels the candidate list, not the widget.\n    // Without this a reporter typing Chinese or Japanese loses the popup and\n    // everything they had written by backing out of one wrong candidate.\n    if (isImeConfirmKeydown(event)) return\n\n    this.state.handleEscape(event)\n  }\n\n  private buildUI(): void {\n    this.shadow = new ShadowContainer(this.config.theme ?? 'auto', this.brandColors)\n\n    if (this.config.entryMode === 'api') {\n      // Nothing is reachable until the host wires up open(), and silence would\n      // look identical to a broken install. But 'api' is a supported setting,\n      // so a warning on every production page load would be a third-party SDK\n      // shouting at customers who did nothing wrong — say it only where an\n      // integrator is watching.\n      if (isLocalHost()) {\n        console.warn(\n          '[MakeThisBetter] entryMode: \"api\" renders no launcher. ' +\n          'Call MakeThisBetter.open() from your own UI.',\n        )\n      }\n      return\n    }\n\n    // A phone gets no launcher of ours. The edge tab is 36x40 of someone\n    // else's screen, and it is genuinely hard to find — its own author missed\n    // it on a test page — while a narrow layout has no spare margin to dock\n    // anything to. Where a feedback entry belongs on a phone is the host's\n    // call, not a third-party script's, so touch renders nothing and waits for\n    // open() from the host's own UI.\n    if (isTouchPointer()) {\n      if (isLocalHost()) {\n        console.warn(\n          '[MakeThisBetter] No launcher is rendered on touch devices. ' +\n          'Call MakeThisBetter.open() from your own mobile UI.',\n        )\n      }\n      return\n    }\n\n    this.mountTab()\n  }\n\n  private mountTab(): void {\n    // No shadow means startup was rolled back; there is nowhere to mount.\n    if (this.tab || !this.shadow) return\n    this.tab = new FeedbackTab(\n      this.shadow,\n      this.messages,\n      this.config.position ?? 'right',\n      () => this.state.handleTabClick(),\n      this.config.tabText,\n    )\n  }\n\n  /** Opens annotation mode. Idempotent: a second call while open does nothing. */\n  open(): void {\n    if (!this.shadow) return\n    this.state.handleOpen()\n  }\n\n  close(): void {\n    this.state.handleClose()\n  }\n\n  showLauncher(): void {\n    if (isTouchPointer()) return\n    this.mountTab()\n    this.tab?.setActive(this.state.launcherLit)\n  }\n\n  hideLauncher(): void {\n    this.tab?.destroy()\n    this.tab = null\n  }\n\n  // Takes effect for anything opened afterwards. Components already on screen keep\n  // the language they were opened in rather than being re-rendered underneath a\n  // reporter who may be mid-sentence.\n  setLocale(locale: string): void {\n    this.config.locale = locale\n    this.messages = getMessages(locale)\n    this.tab?.setMessages(this.messages)\n  }\n\n  private handleFrustration(): void {\n    if (!this.state.acceptsFrustrationPrompt || this.frustrationPrompt || isFrustrationDismissed()) return\n\n    this.frustrationPrompt = new FrustrationPromptCard(this.shadow, this.messages, {\n      onTell: () => {\n        this.dismissFrustrationPrompt()\n        this.state.handleOpen()\n      },\n      onDismiss: () => {\n        markFrustrationDismissed()\n        this.dismissFrustrationPrompt()\n      },\n      // An unnoticed card is not a refusal, so it must not suppress later\n      // prompts. The detector's own 60s cooldown does the rate limiting.\n      onAutoHide: () => this.dismissFrustrationPrompt(),\n    })\n  }\n\n  dismissSuccess(): void {\n    this.success?.destroy()\n    this.success = null\n    // The chat card ends as its own receipt, so it is the previous\n    // submission's success card on that path.\n    this.chatPopup?.destroy()\n    this.chatPopup = null\n  }\n\n  dismissFrustrationPrompt(): void {\n    this.frustrationPrompt?.destroy()\n    this.frustrationPrompt = null\n  }\n\n  hasAnnotationChrome(): boolean {\n    return this.toolbar !== null\n  }\n\n  mountAnnotationChrome(): void {\n    // Network-only warmup: annotating usually ends in a capture, and the font\n    // embed CSS is the slow, cacheable half of one.\n    void warmFontEmbedCss()\n    const touch = isTouchPointer()\n\n    this.toolbar = new AnnotationToolbar(\n      this.shadow,\n      this.messages,\n      () => this.exitAll(),\n      (mode) => this.state.handleToolbarModeChange(mode),\n      // The bar stands at whichever edge the tab does. Leaving it pinned right\n      // while a left-configured tab sat opposite would split the widget across\n      // both sides of the page.\n      this.config.position ?? 'right',\n      !touch || !this.touchHintShown,\n    )\n    if (touch) this.touchHintShown = true\n    this.ensureSession()\n    // The bar is a full-bleed strip at the top on a phone. Left overlapping,\n    // it covers roughly 100px of the page — usually the site's own header —\n    // and taps there do nothing, silently. Give it room of its own instead.\n    if (touch && this.toolbar) this.pageOffset.reserve(this.toolbar.height())\n  }\n\n  ensureSession(): void {\n    if (this.session) return\n    this.session = new AnnotationSession(this.shadow, this.messages, {\n      onPin: (annotation, targetName) => {\n        this.currentAnnotation = annotation\n        this.via = 'pin'\n        this.lifecycle.beginBaseCapture()\n        this.transitionTo(new PopupState(this, {\n          x: annotation.x,\n          y: annotation.y,\n          targetName,\n          targetRect: annotation.targetRect,\n        }))\n      },\n      onDrawStroke: () => {\n        this.lifecycle.beginBaseCapture()\n        this.state.handleDrawStroke()\n      },\n    })\n  }\n\n  destroySession(): void {\n    this.session?.destroy()\n    this.session = null\n  }\n\n  clearCurrentAnnotation(): void {\n    this.currentAnnotation = null\n  }\n\n  startRecorder(): void {\n    this.recordingManager = new RecordingManager()\n    this.recordingManager.start(\n      this.shadow,\n      this.messages,\n      this.stopRecordingIntoNote,\n    ).catch((error) => {\n      // rrweb failed to load (network / SRI mismatch) — fall back to markup\n      console.warn('[MakeThisBetter] Interaction replay unavailable, falling back to markup:', error)\n      // Staleness guard: the reporter may have left recording while the load\n      // was still failing, and a stale fallback must not disturb wherever\n      // they are now.\n      if (this.state.kind !== 'recording') return\n      this.toolbar?.setMode('markup')\n      this.state.handleToolbarModeChange('markup')\n    })\n  }\n\n  destroyRecordingManager(): void {\n    this.recordingManager?.destroy()\n    this.recordingManager = null\n  }\n\n  stopRecordingIntoNote = (): void => {\n    if (!this.recordingManager) return\n    this.recordingManager.stop()\n    this.via = 'record'\n    this.transitionTo(new PopupState(this, {\n      x: window.innerWidth / 2,\n      y: window.innerHeight / 2,\n      targetName: this.messages.record.timer_label,\n    }))\n  }\n\n  mountDrawBar(): void {\n    this.engageScrollLock()\n    this.drawBar = new DrawNoteBar(this.shadow, {\n      messages: this.messages,\n      onUndo: () => this.handleDrawUndoRedo('undo'),\n      onRedo: () => this.handleDrawUndoRedo('redo'),\n      onCancel: () => this.cancelDraw(),\n      onSubmit: (note) => this.handleDrawSubmit(note),\n      onActivity: () => this.lifecycle.beginBaseCapture(),\n    })\n    // Strokes are baked onto the screenshot, so this surface offers no opt-out\n    // and must undo one made earlier in the session.\n    this.lifecycle.setScreenshotEnabled(true)\n    this.lifecycle.warmScreenshotDeps()\n  }\n\n  /**\n   * The note sheet for a finished drawing on touch.\n   *\n   * Cancel here means \"throw the drawing away and keep annotating\", not \"close\n   * the widget\" — the reporter is mid-task and a stray stroke should cost one\n   * tap, not the whole session.\n   */\n  mountDrawSheet(): void {\n    // No dismissInteraction and no scrim here, matching the desktop draw bar:\n    // the overlay stays live so more strokes are still possible, and the scrim\n    // sits below it anyway so it could never receive the tap.\n    this.engageScrollLock()\n    // Cancel means \"throw the drawing away\" only while the drawing is still the\n    // reporter's; once the note is sent the card is a receipt and closing it\n    // ends the session instead.\n    let submitted = false\n    this.chatPopup = new ChatPopup(this.shadow, {\n      targetName: this.messages.annotation.drawing,\n      x: window.innerWidth / 2,\n      y: window.innerHeight / 2,\n      messages: this.messages,\n      position: this.config.position ?? 'right',\n      onSubmit: async (description: string) => {\n        const annotation = this.session?.getDrawAnnotation()\n        if (!annotation) {\n          this.cancelDraw()\n          return null\n        }\n        submitted = true\n        this.currentAnnotation = annotation\n        this.via = 'draw'\n        this.session?.dismissInteraction()\n        this.teardownDrawBar()\n        const recording = this.recordingManager?.getRecording() ?? undefined\n        const session = await this.lifecycle.start(\n          description || this.messages.annotation.drawing,\n          annotation,\n          recording,\n        )\n        if (session?.board_url) cacheBoardUrl(session.board_url)\n        return session\n      },\n      onClarify: async (sessionId: string, token: string, onDelta: (text: string) => void, signal?: AbortSignal) => {\n        return this.apiClient.streamClarification(sessionId, token, onDelta, signal)\n      },\n      onAnswer: (sessionId: string, token: string, message: string) =>\n        this.apiClient.answerClarification(sessionId, token, message),\n      onFinalize: () => {\n        this.transitionTo(new SubmittingState(this))\n        return this.lifecycle.finalize()\n      },\n      onCancel: () => (submitted ? this.exitAll() : this.cancelDraw()),\n      onConversationOpen: () => this.transitionTo(new ChatClarifyingState(this)),\n      onActivity: () => this.lifecycle.beginBaseCapture(),\n      resolveBoardLink: () => this.buildViewFeedbackHandler() ?? this.buildMyFeedbackHandler(),\n      resolveEmailCapture: () => this.buildEmailCapture(),\n    })\n    // Same rule as the desktop draw bar: no opt-out on a drawing.\n    this.lifecycle.setScreenshotEnabled(true)\n    this.lifecycle.warmScreenshotDeps()\n  }\n\n  private handleDrawUndoRedo(action: 'undo' | 'redo'): void {\n    if (action === 'undo') this.session?.undoDraw()\n    else this.session?.redoDraw()\n    this.refreshDrawBar()\n  }\n\n  refreshDrawBar(): void {\n    if (!this.session) return\n    this.drawBar?.setUndoRedo(this.session.canUndoDraw(), this.session.canRedoDraw())\n  }\n\n  cancelDraw(): void {\n    this.session?.clearDraw()\n    this.releaseScrollLock()\n    this.teardownDrawBar()\n    // On touch the sheet is the note surface, so cancelling the drawing has to\n    // take the sheet and its scrim with it — teardownDrawBar only knows the bar.\n    this.chatPopup?.destroy()\n    this.chatPopup = null\n    this.dim?.destroy()\n    this.dim = null\n    this.transitionTo(new AnnotatingState(this))\n  }\n\n  teardownDrawBar(): void {\n    this.drawBar?.destroy()\n    this.drawBar = null\n  }\n\n  private handleDrawSubmit(note: string): void {\n    const annotation = this.session?.getDrawAnnotation()\n    if (!annotation) {\n      this.cancelDraw()\n      return\n    }\n    this.currentAnnotation = annotation\n    this.via = 'draw'\n    this.session?.dismissInteraction()\n    const description = note || this.messages.annotation.drawing\n    const recording = this.recordingManager?.getRecording() ?? undefined\n\n    this.teardownDrawBar()\n    this.releaseScrollLock()\n    this.dim?.destroy()\n    this.dim = null\n\n    this.mountChatAfterSubmit(description, annotation, recording)\n  }\n\n  private mountChatAfterSubmit(description: string, annotation: Annotation | null, recording: import('./recording-manager').RecordingResult | undefined): void {\n    this.dim = new DimOverlay(this.shadow, () => this.exitAll())\n    this.engageScrollLock()\n\n    this.chatPopup = new ChatPopup(this.shadow, {\n      targetName: annotation?.targetName ?? this.messages.annotation.drawing,\n      x: this.lastPopupPos?.x ?? window.innerWidth / 2,\n      y: this.lastPopupPos?.y ?? window.innerHeight / 2,\n      messages: this.messages,\n      position: this.config.position ?? 'right',\n      onSubmit: async () => {\n        const session = await this.lifecycle.start(description, annotation, recording)\n        if (session?.board_url) cacheBoardUrl(session.board_url)\n        return session\n      },\n      onClarify: async (sessionId: string, token: string, onDelta: (text: string) => void, signal?: AbortSignal) => {\n        return this.apiClient.streamClarification(sessionId, token, onDelta, signal)\n      },\n      onAnswer: (sessionId: string, token: string, message: string) =>\n        this.apiClient.answerClarification(sessionId, token, message),\n      onFinalize: () => {\n        this.transitionTo(new SubmittingState(this))\n        return this.lifecycle.finalize()\n      },\n      onCancel: () => this.exitAll(),\n      onConversationOpen: () => this.transitionTo(new ChatClarifyingState(this)),\n      resolveBoardLink: () => this.buildViewFeedbackHandler() ?? this.buildMyFeedbackHandler(),\n      resolveEmailCapture: () => this.buildEmailCapture(),\n    })\n    this.chatPopup.submit(description)\n  }\n\n  mountNotePopup({ x, y, targetName, targetRect }: NotePopupOptions): void {\n    this.lastPopupPos = { x, y }\n    this.teardownDrawBar()\n    this.session?.dismissInteraction()\n    this.chatPopup?.destroy()\n    this.chatPopup = null\n    this.dim?.destroy()\n    this.dim = null\n    this.dim = new DimOverlay(this.shadow, () => this.exitAll())\n    const recording = this.recordingManager?.getRecording()\n    if (recording) this.lifecycle.setScreenshotEnabled(true)\n\n    const annotation = this.currentAnnotation\n    const recordingResult = this.recordingManager?.getRecording() ?? undefined\n\n    this.chatPopup = new ChatPopup(this.shadow, {\n      targetName,\n      x,\n      y,\n      targetRect,\n      messages: this.messages,\n      position: this.config.position ?? 'right',\n      onSubmit: async (description: string) => {\n        this.currentAnnotation = annotation ?? this.currentAnnotation\n        const session = await this.lifecycle.start(description, this.currentAnnotation, recordingResult)\n        if (session?.board_url) cacheBoardUrl(session.board_url)\n        return session\n      },\n      onClarify: async (sessionId: string, token: string, onDelta: (text: string) => void, signal?: AbortSignal) => {\n        return this.apiClient.streamClarification(sessionId, token, onDelta, signal)\n      },\n      onAnswer: (sessionId: string, token: string, message: string) =>\n        this.apiClient.answerClarification(sessionId, token, message),\n      // The chat card turns into a receipt at this point: the pin, the scrim\n      // and the scroll lock have nothing left to hold. A finalization failure\n      // remains in this card as a retryable receipt state.\n      onFinalize: () => {\n        this.teardownSubmittingUi()\n        this.transitionTo(new SubmittingState(this))\n        return this.lifecycle.finalize()\n      },\n      onCancel: () => this.exitAll(),\n      onConversationOpen: () => this.transitionTo(new ChatClarifyingState(this)),\n      onActivity: () => {\n        if (annotation) this.lifecycle.beginBaseCapture()\n        else this.lifecycle.warmScreenshotDeps()\n      },\n      resolveBoardLink: () => this.buildViewFeedbackHandler() ?? this.buildMyFeedbackHandler(),\n      resolveEmailCapture: () => this.buildEmailCapture(),\n      ...(recording\n        ? { recordingSeconds: recording.duration }\n        : {\n            onScreenshotToggle: (enabled: boolean) => this.lifecycle.setScreenshotEnabled(enabled),\n            screenshotEnabled: this.lifecycle.isScreenshotEnabled(),\n          }),\n    })\n    this.engageScrollLock()\n    this.lifecycle.warmScreenshotDeps()\n  }\n\n  /**\n   * The Markup-button escape from a note popup. The popup's mount destroyed\n   * the session's overlay (dismissInteraction), so the session goes with it —\n   * AnnotatingState.enter() rebuilds a fresh one and the reporter can point\n   * and draw again, rather than facing a dead overlay under a leftover scrim.\n   */\n  dismissNotePopup(): void {\n    this.chatPopup?.destroy()\n    this.chatPopup = null\n    this.dim?.destroy()\n    this.dim = null\n    this.releaseScrollLock()\n    this.clearCurrentAnnotation()\n    this.destroySession()\n  }\n\n  private engageScrollLock(): void {\n    // Re-entrant: record mode returns here and mounts a fresh popup over the\n    // existing one, and a second lock would leave a listener behind.\n    if (!this.scrollLock) this.scrollLock = lockPageScroll()\n  }\n\n  private releaseScrollLock(): void {\n    this.scrollLock?.release()\n    this.scrollLock = null\n  }\n\n  private buildMyFeedbackHandler(): (() => void) | undefined {\n    if (this.config.user) return undefined\n    if (!getCachedBoardUrl()) return undefined\n\n    return async () => {\n      const exchange = await this.apiClient.createIdentityToken(getAnonId())\n      if (!exchange?.identity_token || !exchange.board_url) return\n      cacheBoardUrl(exchange.board_url)\n      window.open(`${exchange.board_url}?identity=${encodeURIComponent(exchange.identity_token)}`, '_blank')\n    }\n  }\n\n  private teardownSubmittingUi(): void {\n    // The highlight exists to hold the target visible under a pin. This is\n    // where the session — and with it the pin — goes away, so the highlight\n    // has to go with it rather than outliving what it pointed at.\n    this.teardownDrawBar()\n    // The pin goes away with the session below, so nothing anchored is left for\n    // a scroll to strand — the conversation that follows scrolls freely.\n    this.releaseScrollLock()\n    this.dim?.destroy()\n    this.dim = null\n    this.session?.hideHighlight()\n    this.session?.destroy()\n    this.session = null\n    this.recordingManager?.destroy()\n    this.recordingManager = null\n  }\n\n  revertToolbarDuringClarify(): void {\n    this.toolbar?.setMode(this.via === 'record' ? 'record' : 'markup')\n  }\n\n  releaseClarifyUi(): void {\n    this.teardownToolbar()\n    this.currentAnnotation = null\n    // The card is not destroyed here: the skip path's card dismisses itself\n    // after handing off, and every other entry into submitting finds it gone.\n  }\n\n  private handleFinalized(feedback: FeedbackResponse, submission: SubmissionSessionResponse): void {\n    this.lastHandoff = {\n      feedbackId: feedback.id,\n      boardUrl: feedback.board_url,\n      identityToken: feedback.identity_token,\n    }\n    if (feedback.board_url) cacheBoardUrl(feedback.board_url)\n\n    // A finalize that lands after the reporter already started their next\n    // report must not touch the screen: any toolbar, consent state or chat\n    // card up now belongs to the new report, not to this submission. The\n    // hand-off above is still recorded — it is about the finalized feedback,\n    // not about what is on screen.\n    if (this.chatPopup && this.chatPopup.sessionId() !== submission.id) return\n    if (!this.chatPopup && this.state.kind !== 'submitting' && this.state.kind !== 'clarifying') return\n\n    if (this.chatPopup) {\n      this.chatPopup.showFinalizeSuccess()\n      this.teardownSubmittingUi()\n      this.teardownToolbar()\n      this.lifecycle.resetScreenshotConsent()\n      this.transitionTo(new IdleState(this))\n      return\n    }\n\n    this.teardownToolbar()\n    this.lifecycle.resetScreenshotConsent()\n    this.showSuccess({ skipFollowup: submission.ai_clarify_available === false })\n  }\n\n  private showSuccess(options?: { skipFollowup?: boolean }): void {\n    this.transitionTo(new IdleState(this))\n    this.success = new ReceiptCard(this.shadow, this.messages, () => {\n      this.success?.destroy()\n      this.success = null\n    }, { ...options, onViewFeedback: this.buildViewFeedbackHandler() })\n  }\n\n  private buildEmailCapture(): { onSubmit: (email: string) => Promise<boolean> } | undefined {\n    if (this.config.user) return undefined\n    const { feedbackId, identityToken } = this.lastHandoff\n    if (!feedbackId || !identityToken) return undefined\n    if (getStoredReporterEmail()) return undefined\n\n    return {\n      onSubmit: async (email: string) => {\n        const saved = await this.apiClient.updateReporter(feedbackId, email, identityToken)\n        if (saved) rememberReporterEmail(email)\n        return saved\n      },\n    }\n  }\n\n  private buildViewFeedbackHandler(): (() => void) | undefined {\n    const { boardUrl, identityToken } = this.lastHandoff\n    if (!boardUrl) return undefined\n\n    if (identityToken) {\n      return () => {\n        window.open(`${boardUrl}?identity=${encodeURIComponent(identityToken)}`, '_blank')\n      }\n    }\n\n    if (this.config.userToken || this.config.userTokenFn) {\n      return async () => {\n        // resolveUserToken runs the host's own userTokenFn. A rejection there\n        // is the customer's backend failing, not ours — swallowing it beats\n        // handing their page an unhandled rejection from a click handler they\n        // never wrote.\n        try {\n          const token = await this.apiClient.resolveUserToken()\n          if (token) window.open(`${boardUrl}?identity=${encodeURIComponent(token)}`, '_blank')\n        } catch {\n          // The board link just does nothing; the feedback itself already sent.\n        }\n      }\n    }\n\n    return undefined\n  }\n\n  /**\n   * The reserved strip belongs to the toolbar, so it goes back the moment the\n   * toolbar does. Both teardown paths route through here: exit, and the\n   * successful submit — which never passes through exitAll and used to leave\n   * the host page padded for the rest of its life.\n   */\n  private teardownToolbar(): void {\n    this.pageOffset.release()\n    this.toolbar?.destroy()\n    this.toolbar = null\n  }\n\n  exitAll(): void {\n    this.lifecycle.abandon()\n    this.via = 'pin'\n    this.transitionTo(new IdleState(this))\n    // Ahead of anything that can throw: a half-torn-down widget is survivable,\n    // a host page left permanently padded is not.\n    this.teardownToolbar()\n    this.teardownDrawBar()\n    this.releaseScrollLock()\n    this.session?.hideHighlight()\n    this.session?.destroy()\n    this.session = null\n    this.dim?.destroy()\n    this.dim = null\n    this.chatPopup?.destroy()\n    this.chatPopup = null\n    this.currentAnnotation = null\n    this.recordingManager?.destroy()\n    this.recordingManager = null\n  }\n\n  destroy(): void {\n    document.removeEventListener('keydown', this.handleKeydown, true)\n    this.exitAll()\n    this.consoleCollector.stop()\n    this.breadcrumbCollector.stop()\n    this.frustrationDetector?.stop()\n    this.dismissFrustrationPrompt()\n    this.success?.destroy()\n    this.tab?.destroy()\n    this.recordingManager?.destroy()\n    // Undefined when startup rolled back before the shadow container existed.\n    this.shadow?.destroy()\n  }\n}\n","import { WidgetController } from './widget/controller'\nimport { WIDGET_HOST_ID } from './context/dom-utils'\nimport type { MakeThisBetterConfig } from './types'\n\nlet instance: WidgetController | null = null\nlet lastConfig: MakeThisBetterConfig | null = null\n// A mount deferred until DOMContentLoaded. Held so a second init() — or a\n// destroy() — can call it off: otherwise each init before the document is ready\n// queues its own listener, and every controller but the last mounts with no\n// live reference, leaking its document listeners and detectors for good.\nlet pendingMount: (() => void) | null = null\n\nfunction cancelPendingMount(): void {\n  if (!pendingMount) return\n  document.removeEventListener('DOMContentLoaded', pendingMount)\n  pendingMount = null\n}\n\nfunction replaceInstance(config?: MakeThisBetterConfig): void {\n  cancelPendingMount()\n  instance?.destroy()\n  instance = config ? new WidgetController(config) : null\n}\n\nconst MakeThisBetter = {\n  init(config: MakeThisBetterConfig): void {\n    // SSR frameworks run the same component code on the server, where there is\n    // no document at all — a widget cannot exist there, and throwing a\n    // ReferenceError into the host's render pass is the one thing a\n    // third-party SDK must never do.\n    if (typeof document === 'undefined') return\n    replaceInstance()\n    lastConfig = config\n    if (document.readyState === 'loading') {\n      pendingMount = () => {\n        pendingMount = null\n        replaceInstance(config)\n      }\n      document.addEventListener('DOMContentLoaded', pendingMount, { once: true })\n    } else {\n      replaceInstance(config)\n    }\n  },\n\n  destroy(): void {\n    replaceInstance()\n    lastConfig = null\n  },\n\n  setLocale(locale: string): void {\n    if (lastConfig) lastConfig.locale = locale\n    instance?.setLocale(locale)\n  },\n\n  /**\n   * Opens annotation mode from the host's own UI. Pair with\n   * `entryMode: 'api'` to replace the docked tab entirely, which is the only\n   * good answer on a phone where a permanent floating control is in the way.\n   */\n  open(): void {\n    instance?.open()\n  },\n\n  close(): void {\n    instance?.close()\n  },\n\n  /** Shows the docked tab on non-touch devices. Mobile entry UI belongs to the host. */\n  showLauncher(): void {\n    instance?.showLauncher()\n  },\n\n  hideLauncher(): void {\n    instance?.hideLauncher()\n  },\n}\n\nexport { MakeThisBetter }\nexport type { MakeThisBetterConfig }\n\nif (typeof window !== 'undefined') {\n  (window as Window & { MakeThisBetter?: typeof MakeThisBetter }).MakeThisBetter = MakeThisBetter\n\n  document.addEventListener('turbo:load', () => {\n    const host = document.getElementById(WIDGET_HOST_ID)\n    if (lastConfig && (!host || !host.shadowRoot)) {\n      host?.remove()\n      replaceInstance(lastConfig)\n    }\n  })\n}\n"],"names":["WIDGET_HOST_ID","isWidgetElement","el","host","elementUnderPoint","x","y","escapeHtml","text","isImeConfirmKeydown","event","UNRENDERED","renderedText","limit","out","node","getElementName","label","title","placeholder","alt","dataMark","getCssSelector","parts","current","selector","classNames","c","parent","siblings","idx","_ShadowContainer","theme","brandColors","provided","rgb","offset","onPrimaryRgb","eventType","style","cssText","tag","className","nodes","n","ShadowContainer","FeedbackTab","shadow","messages","position","onClick","customText","active","FRUSTRATION_DISMISS_MS","FrustrationPromptCard","handlers","SPAWN_CLICK_GRACE_MS","DimOverlay","onDismiss","now","isTouchPointer","PageOffset","px","POINTER_GESTURES","SCROLL_KEYS","scrollsInternally","consumesScrollKey","key","lockPageScroll","blockGesture","blockKey","origin","gesture","MOBILE_BAR_HEIGHT_PX","AnnotationToolbar","onExit","onModeChange","showTouchHint","e","rect","inside","m","mode","AnnotationOverlay","pe","pointerId","ElementHighlighter","below","captureTransform","region","canvasWidth","canvasHeight","toCanvasX","transform","toCanvasY","rectToCanvas","viewportCaptureRect","anchorScreenshotOffset","anchor","pageScroll","nestedScrollOffset","target","parentElement","element","root","captureAnchorOffset","nested","PinMarker","pin","BRAND_COLOR","DRAW_STROKE","DrawLayer","emptyAnchor","strokeAnchor","buildPathD","start","max","stroke","entry","path","first","strokes","shiftPoints","cx","cy","center","points","fx","fy","d","i","xs","p","ys","BLOCK_CLASS","MASK_TEXT_CLASS","PRIVACY_SELECTOR","isPrivacyProtected","structuralElementName","role","structuralElementSelector","DRAW_MIN_TRAVEL_PX","annotationTarget","AnnotationSession","underEl","targetName","annotation","drift","dx","dy","SETTLE_MS","KeyboardInset","reading","vp","inset","onChange","SHEET_MAX_WIDTH","isSheetViewport","SheetLayout","headroom","applied","DrawNoteBar","opts","loading","canUndo","canRedo","VIEWPORT_MARGIN","TAB_WIDTH","measureViewportFrame","tabPosition","vw","vh","panelWidth","preferred","frame","TABBABLE_SELECTOR","trapFocus","card","opener","handleKeydown","tabbables","last","CARD_WIDTH","AUTO_CLOSE_MS","BRAND_URL","BRAND_ICON","CLOSE_ICON","CAMERA_ICON","RECORD_ICON","CHECK_ICON","ChatPopup","quickOpts","o","capture","btn","value","screenshotBtn","pressed","session","result","streaming","delta","aiMessage","msg","view","icon","link","onRetry","retry","openBoard","pendingLink","emailCapture","row","field","input","submitEmail","email","saved","note","error","bubble","suggestions","suggestion","pill","skip","disabled","targetRect","minLeft","maxRight","margin","gap","cardW","cardH","left","top","anchorIsInformative","ReceiptCard","onClose","options","titleEl","FILTERED_VALUE","PRIVATE_KEY_PATTERN","JWT_PATTERN","API_KEY_PATTERNS","CARD_CANDIDATE_PATTERN","HTTP_URL_PATTERN","FIELD_SEMANTIC_KEYS","VALUE_ATTRIBUTE_KEYS","SENSITIVE_INPUT_TYPES","SENSITIVE_AUTOCOMPLETE","SENSITIVE_KEY_PATTERN","filterSensitiveText","filtered","pattern","candidate","passesLuhn","sanitizeUrl","url","filterSensitiveValue","isSensitiveKey","item","sensitiveField","isSensitiveFieldAttributes","nestedKey","filteredItem","normalizeIdentifier","attributes","normalizedKey","token","digits","sum","doubleDigit","index","digit","isSensitiveField","filterInputValue","MAX_GROUPED_TEXT_LENGTH","createSensitiveTextContext","hasSensitiveTextContext","context","state","sensitiveTextState","cached","child","childState","loadImage","dataUrl","resolve","image","parsePathPolylines","drawPath","polylines","tokens","translatePathD","htmlToImageLoaded","loadHtmlToImage","err","fontEmbedCss","fontEmbedCssRequest","warmFontEmbedCss","getFontEmbedCSS","css","CULL_MARGIN_PX","TRANSPARENT_IMAGE_PLACEHOLDER","captureBaseScreenshot","resourceUrlResolver","signal","toJpeg","fontEmbedCSS","bodyRect","metrics","redactions","collectPrivacyRedactions","capturePixelRatio","bakeAnnotatedScreenshot","base","annotations","screenshotAnnotations","translateAnnotationForScreenshot","bakeScreenshot","screenshotOffset","findAnnotationTarget","captureMetrics","canvas","ctx","cover","drawStroke","drawPin","canvasToJpegBlob","elementsIncludingRoot","addRedaction","cloneScrollOffset","isWidgetOrBlocked","isMasked","hasSensitiveValue","REDACTION_PADDING_PX","walker","textContext","hasSensitiveText","range","descendants","padding","color","MAX_CAPTURE_PIXELS","total","primary","blob","ErrorSource","listener","source","line","col","errorName","reason","name","errorSource","summarizeError","validErrorName","inferErrorName","location","errorLocation","message","pathname","MAX_CONSOLE_ERRORS","ConsoleErrorCollector","listeners","nativePushState","nativeReplaceState","installedPushState","installedReplaceState","popstateHandler","lastUrl","onHistoryNavigation","install","released","uninstall","notify","type","fromUrl","pushOrig","replaceOrig","data","unused","MAX_BREADCRUMBS","describeTarget","describeSelector","BreadcrumbCollector","crumb","summary","from","to","sanitizedFrom","sanitizedTo","STORAGE_KEY","memoryState","loadFrustrationState","raw","normalize","saveFrustrationState","normalized","isFrustrationDismissed","markFrustrationDismissed","t","u","RAGE_CLICK_THRESHOLD","RAGE_CLICK_WINDOW_MS","RAPID_NAV_THRESHOLD","RAPID_NAV_WINDOW_MS","FORM_CHECK_DELAY_MS","FORM_FAILURE_THRESHOLD","FORM_FAILURE_WINDOW_MS","INTERACTION_ERROR_WINDOW_MS","DEAD_CLICK_DOM_DELAY_MS","ERROR_PAGE_CHECK_DELAY_MS","INTERACTIVE_TAGS","INTERACTIVE_ROLES","COMMAND_TAGS","COMMAND_ROLES","ERROR_PAGE_PATTERNS","_FrustrationDetector","onFrustration","interaction","describeElement","callback","delayMs","timer","stored","interactionTarget","cutoff","r","form","control","failures","timestamp","candidates","clickTime","mutated","navigated","cls","lower","FrustrationDetector","id","ApiError","status","FINALIZE_TIMEOUT_MS","DEFAULT_API_URL","CAPTURE_POLICY","ApiClient","submissionSessionId","resourceUrl","contentType","headers","body","payload","screenshot","submissionToken","generateSubmissionToken","json","ext","generateIdempotencyKey","reporterExternalId","feedbackId","identityToken","onDelta","res","consumeSse","controller","init","retries","delay","ms","reader","decoder","buffer","flush","eventLine","l","dataLine","s","done","sep","record","bytes","byte","EMAIL_KEY","BOARD_URL_KEY","memoryId","memoryEmail","memoryBoardUrl","getAnonId","generateAnonId","getStoredReporterEmail","rememberReporterEmail","getCachedBoardUrl","cacheBoardUrl","en","zhCN","ja","ko","es","fr","de","locales","getMessages","locale","lang","MAX_DURATION_S","VALUE_ATTRIBUTE_NAMES","MASKED_INPUT_TYPES","RRWEB_CDN","RRWEB_SRI","rrwebLoaded","loadRrweb","loadViaImport","loadViaCdn","filterReplayEvent","filterReplayDomValues","isRecord","hasAttributeValue","attributeValue","findGlobalRecord","win","reject","existing","script","RecordSession","onMaxReached","ripple","RecordControlBar","getDuration","onStop","secs","mm","ss","RecordingManager","collectPageContext","detectBrowser","detectOS","ua","buildPayload","description","pageContext","consoleErrors","user","recording","anonId","anonEmail","breadcrumbs","MIN_FINALIZE_LOADING_MS","IDLE_CAPTURE_MS","SCREENSHOT_WAIT_MS","SubmissionLifecycle","deps","enabled","sessionReady","waitForAbort","submission","submissionAttempt","request","waitForPaint","submissionRequest","screenshotController","baseCapture","trackedTask","background","minimumLoading","feedback","task","completed","consume","BaseModeState","_event","_mode","IdleState","AnnotatingState","RecordingState","DrawingState","surface","PopupState","ChatClarifyingState","SubmittingState","HEX_COLOR","resolveBrandColors","colors","rejectBrandColors","isLocalHost","hostname","WidgetController","config","next","previous","touch","submitted","sessionId","action","recordingResult","exchange","boardUrl","instance","lastConfig","pendingMount","cancelPendingMount","replaceInstance","MakeThisBetter"],"mappings":"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"}