makethisbetter 1.19.0 → 1.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/makethisbetter.cjs +2 -2
- package/dist/makethisbetter.cjs.map +1 -1
- package/dist/makethisbetter.esm.js +7 -5
- package/dist/makethisbetter.esm.js.map +1 -1
- package/dist/makethisbetter.js +2 -2
- package/dist/makethisbetter.js.map +1 -1
- package/dist/screenshot/capture.d.ts.map +1 -1
- package/dist/styles/theme-placeholder.test.d.ts +2 -0
- package/dist/styles/theme-placeholder.test.d.ts.map +1 -0
- package/dist/styles/theme-success.test.d.ts +2 -0
- package/dist/styles/theme-success.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/screenshot/capture.ts +8 -3
- package/src/styles/widget.css +7 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makethisbetter.cjs","sources":["../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/annotate/toolbar.ts","../src/annotate/overlay.ts","../src/annotate/highlighter.ts","../src/annotate/pin.ts","../src/styles.ts","../src/annotate/draw.ts","../src/privacy/sanitize.ts","../src/privacy/fields.ts","../src/privacy/text-context.ts","../src/record/session.ts","../src/privacy/dom.ts","../src/annotate/session.ts","../src/context/dom-utils.ts","../src/popup/keyboard-inset.ts","../src/popup/sheet.ts","../src/annotate/draw-note-bar.ts","../src/popup/replay-click-guard.ts","../src/popup/comment.ts","../src/api/client.ts","../src/popup/clarify.ts","../src/popup/success.ts","../node_modules/html-to-image/es/util.js","../node_modules/html-to-image/es/clone-pseudos.js","../node_modules/html-to-image/es/mimes.js","../node_modules/html-to-image/es/dataurl.js","../node_modules/html-to-image/es/clone-node.js","../node_modules/html-to-image/es/embed-resources.js","../node_modules/html-to-image/es/embed-images.js","../node_modules/html-to-image/es/apply-style.js","../node_modules/html-to-image/es/embed-webfonts.js","../node_modules/html-to-image/es/index.js","../src/screenshot/crop.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/context/collector.ts","../src/widget/payload.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/control-bar.ts","../src/widget/recording-manager.ts","../src/widget/controller.ts","../src/index.ts"],"sourcesContent":["import cssText from '../styles/widget.css?inline'\nimport type { MakeThisBetterConfig } from '../types'\n\nexport class ShadowContainer {\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\n constructor(theme: MakeThisBetterConfig['theme'] = 'auto') {\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('mtb-widget-host')\n this.ownsHost = !provided\n\n this.host = provided ?? document.createElement('div')\n this.host.id = 'mtb-widget-host'\n this.host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;overflow:visible;z-index:2147483646;'\n this.host.setAttribute('data-mtb-theme', theme)\n if (this.ownsHost) document.body.appendChild(this.host)\n\n this.root = this.host.shadowRoot ?? this.host.attachShadow({ mode: 'open' })\n this.root.replaceChildren()\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 destroy(): void {\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 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('click', onClick)\n shadow.append(this.el)\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('click', this.onClick)\n this.el.remove()\n }\n}\n","import type { ShadowContainer } from './shadow'\nimport type { I18nMessages } from '../i18n'\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\">⚠️</div>\n <span class=\"mtb-frustration-text\">${messages.frustration.prompt}</span>\n <div class=\"mtb-frustration-actions\">\n <button class=\"mtb-frustration-tell\">${messages.frustration.action}</button>\n <button class=\"mtb-frustration-dismiss\">${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","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { isTouchPointer } from '../widget/pointer-kind'\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 private dismissTouchHint = (): void => {\n this.hintBarEl.classList.add('mtb-hint-bar--dismissed')\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\">${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 }\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=\"${m.toolbar.markup}\" title=\"${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\">${m.toolbar.markup}</span>\n </button>\n <button class=\"mtb-toolbar-mode-btn mtb-toolbar-mode-record\" aria-label=\"${m.toolbar.record}\" title=\"${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\">${m.toolbar.record}</span>\n </button>\n </div>\n <span class=\"mtb-toolbar-sep\"></span>\n <button class=\"mtb-exit-btn\" aria-label=\"${m.toolbar.exit}\" title=\"${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\">${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 document.removeEventListener('pointerdown', this.dismissTouchHint, true)\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'\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\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 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: BRAND_COLOR,\n width: '3.5',\n linecap: 'round',\n linejoin: 'round',\n filter: `drop-shadow(0 1px 2px ${brandAlpha(0.35)})`,\n} as const\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { Annotation } from '../types'\nimport { DRAW_STROKE } from '../styles'\n\ntype Point = [number, number]\n\ninterface Stroke {\n path: SVGPathElement\n d: string\n points: Point[]\n}\n\nexport class DrawLayer {\n private svg: SVGSVGElement\n private currentPath: SVGPathElement | null = null\n private currentPoints: Point[] = []\n private isDrawing = false\n private committed: Stroke[] = []\n private redoStack: { d: string; points: Point[] }[] = []\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.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({ path: this.currentPath, d: buildPathD(this.currentPoints), points: this.currentPoints })\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 })\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 this.committed.push({ path, d: entry.d, points: entry.points })\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 if (this.committed.length === 0) return null\n const points = this.committed.flatMap(s => s.points)\n const [cx, cy] = center(points)\n return {\n type: 'draw',\n x: cx,\n y: cy,\n drawPath: this.committed.map(s => s.d).join(' '),\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 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","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","import type { ShadowContainer } from '../widget/shadow'\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// 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// 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\nfunction loadRrweb(): Promise<RRWebRecordFn> {\n if (rrwebLoaded) return rrwebLoaded\n rrwebLoaded = loadViaImport().catch(() => loadViaCdn())\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 { BLOCK_CLASS, MASK_TEXT_CLASS } from '../record/session'\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, getElementName, getCssSelector } from './highlighter'\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 = getElementUnder(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 = getElementUnder(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 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 }\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 const underEl = getElementUnder(annotation.x, annotation.y)\n if (!underEl) return annotation\n\n const rect = underEl.getBoundingClientRect()\n const target = annotationTarget(underEl)\n return {\n ...annotation,\n targetSelector: target.selector,\n targetText: target.text,\n targetName: target.name,\n targetRect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height, bottom: rect.bottom },\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// Skips the widget host via elementsFromPoint instead of toggling its\n// display — hiding the host on every mousemove flickered the toolbar.\nfunction getElementUnder(x: number, y: number): Element | null {\n return document.elementsFromPoint(x, y).find(el => el.id !== 'mtb-widget-host') ?? null\n}\n","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\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\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 } 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}\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\n constructor(shadow: ShadowContainer, opts: DrawNoteBarOptions) {\n this.onSubmit = opts.onSubmit\n this.submitLabel = opts.messages.draw.submit\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 this.input.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) 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 <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 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","/**\n * Swallows the compatibility click that a touch browser replays from the tap\n * which opened a panel.\n *\n * On iOS the sequence is pointerdown / pointerup — the widget places a pin and\n * opens the sheet — and only then does the browser dispatch a `click` for that\n * same finger press. By that point the sheet is on screen under the touch\n * point, so the click lands inside it. Observed on iOS 18.7 hitting the button\n * row: a few pixels either way and the gesture that opened the sheet would\n * have cancelled or submitted it. Cancelling pointerdown does not prevent it —\n * WebKit dispatches the click regardless.\n *\n * The replay is told apart by what preceded it, not by when it arrives. A real\n * press on the panel always fires `pointerdown` on the panel first; the replay\n * cannot, because its pointerdown went to the overlay underneath. An earlier\n * version simply ignored everything for 400ms, which also ate a fast, genuine\n * second tap — a window is a guess about human speed, and this is not.\n */\nexport function guardReplayClick(el: HTMLElement): () => void {\n let sawPointerDown = false\n\n const onPointerDown = (): void => {\n sawPointerDown = true\n }\n\n const onClick = (e: Event): void => {\n // detail 0 means the click did not come from a pointer at all — a button\n // activated by Enter or by assistive tech. Those never have a pointerdown\n // and must go through.\n const fromPointer = (e as MouseEvent).detail > 0\n if (fromPointer && !sawPointerDown) {\n e.stopPropagation()\n e.preventDefault()\n }\n sawPointerDown = false\n }\n\n el.addEventListener('pointerdown', onPointerDown, true)\n el.addEventListener('click', onClick, true)\n\n return () => {\n el.removeEventListener('pointerdown', onPointerDown, true)\n el.removeEventListener('click', onClick, true)\n }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport type { TargetRect } from '../types'\nimport { escapeHtml } from '../context/dom-utils'\nimport { SheetLayout } from './sheet'\nimport { guardReplayClick } from './replay-click-guard'\nimport { isTouchPointer } from '../widget/pointer-kind'\n\ninterface PopupOptions {\n targetName: string\n x: number\n y: number\n targetRect?: TargetRect\n onSubmit: (description: string) => void\n onClose: () => void\n onMyFeedback?: () => void\n messages: I18nMessages\n position?: 'left' | 'right'\n}\n\nexport class CommentPopup {\n private el: HTMLDivElement\n private sheet: SheetLayout\n private releaseGuard: (() => void) | null = null\n private textarea!: HTMLTextAreaElement\n private submitBtn!: HTMLButtonElement\n private onSubmit: (description: string) => void\n private onClose: () => void\n private submitLabel: string\n private messages: I18nMessages\n private selectedQuickOption: string | null = null\n\n constructor(shadow: ShadowContainer, opts: PopupOptions) {\n this.onSubmit = opts.onSubmit\n this.onClose = opts.onClose\n this.submitLabel = opts.messages.popup.submit\n this.messages = opts.messages\n\n this.el = shadow.el<HTMLDivElement>('div', 'mtb-popup')\n this.el.innerHTML = this.buildHTML(opts)\n shadow.append(this.el)\n this.sheet = new SheetLayout(this.el)\n // The tap that opened this panel is replayed as a click once the panel is\n // already under the finger — on iOS it landed on the button row.\n if (isTouchPointer()) this.releaseGuard = guardReplayClick(this.el)\n\n this.textarea = this.el.querySelector('.mtb-textarea')!\n this.submitBtn = this.el.querySelector('.mtb-submit-btn')!\n\n this.positionPopup(opts.x, opts.y, opts.targetRect, opts.position ?? 'right')\n this.bindEvents(opts.messages)\n if (opts.onMyFeedback) {\n this.el.querySelector('.mtb-my-feedback')!.addEventListener('click', opts.onMyFeedback)\n }\n // Deliberately not focused on a phone. Focusing opens the keyboard, and\n // iOS repositions position:fixed elements as the keyboard animates — the\n // panel visibly shuffles, through no style change of ours, which is why\n // nothing showed up in the geometry logs. Waiting for the reporter to tap\n // the field puts that movement where they asked for it. Nothing is lost:\n // iOS routinely ignores programmatic focus for raising the keyboard.\n //\n // preventScroll stays for the desktop: the sheet is position:fixed, so the\n // browser cannot bring the field into view by moving the panel and scrolls\n // the document instead.\n if (!isTouchPointer()) this.textarea.focus({ preventScroll: true })\n }\n\n private buildHTML(opts: PopupOptions): string {\n const m = opts.messages\n const quickOpts = m.popup.quickOptions\n .map(\n o => `\n <button class=\"mtb-quick-option\" data-quick=\"${escapeHtml(o.label)}\">\n <span class=\"mtb-quick-option-emoji\">${o.emoji}</span>\n <span>${escapeHtml(o.label)}</span>\n </button>`,\n )\n .join('')\n\n return `\n <div class=\"mtb-popup-header\">\n <div class=\"mtb-popup-icon\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#059669\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/>\n </svg>\n </div>\n <span class=\"mtb-popup-title\">${m.popup.about} \"${escapeHtml(opts.targetName)}\"</span>\n <button class=\"mtb-popup-close\" aria-label=\"Close\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"mtb-popup-body\">\n <div class=\"mtb-quick-options\">\n ${quickOpts}\n </div>\n <div class=\"mtb-textarea-wrap\">\n <textarea class=\"mtb-textarea\" placeholder=\"${escapeHtml(m.popup.placeholder)}\"></textarea>\n </div>\n <div class=\"mtb-popup-actions\">\n <button class=\"mtb-cancel-btn\" type=\"button\">${escapeHtml(m.popup.cancel)}</button>\n <button class=\"mtb-submit-btn\" type=\"button\" disabled>${escapeHtml(m.popup.submit)}</button>\n </div>\n ${opts.onMyFeedback ? `<button class=\"mtb-my-feedback\" type=\"button\">${escapeHtml(m.popup.my_feedback)}</button>` : ''}\n </div>\n `\n }\n\n\n\n private positionPopup(x: number, y: number, targetRect: TargetRect | undefined, tabPosition: 'left' | 'right'): void {\n if (this.sheet.apply()) return\n\n const vw = window.innerWidth\n const vh = window.innerHeight\n const margin = 12\n const gap = 8\n const tabWidth = 32\n const popupW = Math.min(320, vw - 2 * margin - tabWidth)\n const popupH = this.el.offsetHeight || 260\n const minLeft = margin + (tabPosition === 'left' ? tabWidth : 0)\n const maxRight = vw - margin - (tabPosition === 'right' ? tabWidth : 0)\n\n let left: number\n let top: number\n\n // Anchoring to the element only helps while the element is small enough to\n // point at. A click on empty space lands on whatever section or body spans\n // the page, and its rect says nothing about where the user clicked: the top\n // edge is off-screen, so the popup flips, and the left edge is 0, so it\n // lands in the bottom-left corner however far right the click was.\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\n if (top + popupH > vh - margin) {\n top = targetRect.top - popupH - gap\n }\n if (top < margin) {\n top = Math.min(targetRect.bottom + gap, vh - popupH - margin)\n }\n } else {\n left = x + 12\n top = y + 12\n }\n\n if (left + popupW > maxRight) {\n left = maxRight - popupW\n }\n if (left < minLeft) left = minLeft\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 private bindEvents(messages: I18nMessages): void {\n this.el.querySelector('.mtb-popup-close')!.addEventListener('click', () => this.onClose())\n this.el.querySelector('.mtb-cancel-btn')!.addEventListener('click', () => this.onClose())\n\n this.el.querySelectorAll('.mtb-quick-option').forEach(btn => {\n btn.addEventListener('click', () => {\n const label = (btn as HTMLElement).dataset['quick'] ?? ''\n\n if (this.textarea.value.trim().length === 0) {\n this.textarea.value = label\n this.updateSubmitState()\n this.submit()\n return\n }\n\n this.selectedQuickOption = label\n this.el.querySelectorAll('.mtb-quick-option').forEach(option => {\n option.classList.toggle('selected', option === btn)\n })\n })\n })\n\n this.textarea.addEventListener('input', () => this.updateSubmitState())\n\n this.textarea.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) return\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n this.submit()\n }\n })\n\n this.submitBtn.addEventListener('click', () => this.submit())\n }\n\n private updateSubmitState(): void {\n const hasText = this.textarea.value.trim().length > 0\n this.submitBtn.classList.toggle('ready', hasText)\n this.submitBtn.disabled = !hasText\n }\n\n private submit(): void {\n const text = this.textarea.value.trim()\n if (!text) return\n const description = this.selectedQuickOption ? `[${this.selectedQuickOption}] ${text}` : text\n this.onSubmit(description)\n }\n\n releaseElement(): HTMLDivElement {\n this.releaseGuard?.()\n this.releaseGuard = null\n // Ownership of the element passes to the caller, which means destroy() will\n // never run for this instance. Stop observing here or the listener outlives\n // the popup and keeps writing `bottom` on an element it no longer manages.\n this.sheet.release()\n return this.el\n }\n\n destroy(): void {\n this.releaseGuard?.()\n this.releaseGuard = null\n this.sheet.release()\n this.el.remove()\n }\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 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\nconst DEFAULT_API_URL = 'https://makethisbetter.dev/api/v1'\nconst CAPTURE_POLICY = 'sensitive-data-v1'\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(projectKey: string, apiUrl?: string, retryDelayMs = 1000, userToken?: string, userTokenFn?: () => Promise<string>) {\n this.projectKey = projectKey\n this.apiUrl = (apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, '')\n this.retryDelayMs = retryDelayMs\n this.userToken = userToken\n this.userTokenFn = userTokenFn\n }\n\n async resolveUserToken(): Promise<string | undefined> {\n if (this.userTokenFn) return this.userTokenFn()\n return this.userToken\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 async startClarification(submissionSessionId: string, token: string, message?: string): Promise<ClarifyResponse> {\n const body: Record<string, string | boolean> = {}\n if (message) body.message = filterSensitiveText(message)\n return this.requestClarification(submissionSessionId, token, body)\n }\n\n async retryClarification(submissionSessionId: string, token: string): Promise<ClarifyResponse> {\n return this.requestClarification(submissionSessionId, token, { retry: true })\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 async getClarification(submissionSessionId: string, token: string): Promise<ClarifyResponse> {\n const headers = await this.submissionHeaders(token)\n const res = await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/clarification`, {\n method: 'GET',\n headers,\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. Throws if the server doesn't stream\n // (e.g. old backend returns JSON) so the caller can fall back to polling.\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 res = await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/feedback`, {\n method: 'POST',\n headers,\n })\n const response = await res.json() as { feedback: FeedbackResponse }\n return response.feedback\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 if (retries > 0) {\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 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","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport type { ApiClient, ClarifyStreamResult } from '../api/client'\nimport { isTerminalConflict } from '../api/client'\nimport type { ClarifyMessage, ClarifyResponse, SubmissionSessionResponse } from '../types'\nimport { escapeHtml } from '../context/dom-utils'\nimport { SheetLayout } from './sheet'\nimport { isTouchPointer } from '../widget/pointer-kind'\n\nconst POLL_INTERVAL_MS = 2000\n// Give up polling after this long with no assistant reply so the card never spins\n// forever (e.g. a backend job that silently died). This is the queued path — the\n// answer is being generated by a job, not streamed — so it needs room for queue wait\n// plus generation. The 12s deadline on the streaming request is a different budget:\n// there, silence means the stream itself is stuck.\nconst POLL_TIMEOUT_MS = 45000\n// How long a clarification stream may stay silent before it becomes retryable.\n// Measured between tokens, not from the start, so a long answer is not cut off.\nconst CLARIFY_SILENCE_TIMEOUT_MS = 12000\n\ninterface ClarifyCardOptions {\n // Either the session is already known, or it is still being created — the\n // card mounts immediately either way and shows its thinking bubble while\n // pendingSession settles.\n submissionSessionId?: string\n submissionToken?: string\n pendingSession?: Promise<SubmissionSessionResponse | null>\n onRetrySubmission?: () => Promise<SubmissionSessionResponse | null>\n apiClient: ApiClient\n messages: I18nMessages\n onFinalize: () => Promise<void> | void\n onCancel?: () => void\n element?: HTMLDivElement\n position?: 'left' | 'right'\n x?: number\n y?: number\n}\n\nconst CARD_WIDTH = 346\nconst CARD_HEIGHT = 270\n\nexport class ClarifyCard {\n private el: HTMLDivElement\n private sheet: SheetLayout\n private messagesEl!: HTMLDivElement\n private inputEl!: HTMLTextAreaElement\n private sendBtn!: HTMLButtonElement\n private pollTimer: ReturnType<typeof setInterval> | null = null\n private pollDeadline = 0\n private streamingBubble: HTMLDivElement | null = null\n private submissionSessionId: string\n private submissionToken: string\n private apiClient: ApiClient\n private onFinalize: () => Promise<void> | void\n private onCancel?: () => void\n private i18n: I18nMessages\n private lastMessages: ClarifyMessage[] = []\n private done = false\n private sending = false\n private finalSubmissionStarted = false\n private finalizing = false\n private destroyed = false\n // The in-flight clarification stream, so closing the card can hang up on it\n // rather than leaving the connection reading into a card that is gone.\n private streamAbort: AbortController | null = null\n private unrecoverable = false\n private submissionFailed = false\n\n private pendingSession?: Promise<SubmissionSessionResponse | null>\n private onRetrySubmission?: () => Promise<SubmissionSessionResponse | null>\n\n constructor(shadow: ShadowContainer, opts: ClarifyCardOptions) {\n this.submissionSessionId = opts.submissionSessionId ?? ''\n this.submissionToken = opts.submissionToken ?? ''\n this.apiClient = opts.apiClient\n this.onFinalize = opts.onFinalize\n this.onCancel = opts.onCancel\n this.i18n = opts.messages\n this.pendingSession = opts.pendingSession\n this.onRetrySubmission = opts.onRetrySubmission\n\n const existingElement = opts.element\n this.el = existingElement ?? shadow.el<HTMLDivElement>('div', 'mtb-clarify')\n this.el.className = existingElement ? 'mtb-clarify mtb-clarify-continuation' : 'mtb-clarify'\n this.el.innerHTML = this.buildHTML(opts.messages)\n this.sheet = new SheetLayout(this.el)\n if (!existingElement) {\n shadow.append(this.el)\n this.positionCard(opts.x, opts.y, opts.position ?? 'right')\n } else {\n // A continuation inherits the comment popup's element, and with it the\n // sheet layout that popup was using — but the class just changed from\n // .mtb-popup to .mtb-clarify, so the sheet rules stop matching and the\n // cleared inline offsets leave a fixed element with nowhere to sit.\n // Re-applying binds the new class to the same geometry and re-attaches\n // the keyboard tracking the released popup gave up.\n this.sheet.apply()\n }\n\n this.messagesEl = this.el.querySelector('.mtb-clarify-messages')!\n this.bindEvents()\n // After submitting a comment with Enter, keep the next Enter on the\n // explicit skip-and-send action. This makes the two-step shortcut\n // deterministic instead of depending on the browser's focus fallback.\n if (existingElement && !isTouchPointer()) {\n this.el.querySelector<HTMLButtonElement>('.mtb-clarify-skip')!.focus({ preventScroll: true })\n }\n this.startConversation()\n }\n\n // Anchors the card near the annotation point, clamped to the viewport so it\n // never overflows a screen edge (mirrors CommentPopup.positionPopup).\n private positionCard(x: number | undefined, y: number | undefined, tabPosition: 'left' | 'right'): void {\n if (this.sheet.apply()) return\n\n const vw = window.innerWidth\n const vh = window.innerHeight\n const margin = 12\n const tabWidth = 32\n const cardW = Math.min(CARD_WIDTH, vw - 2 * margin - tabWidth)\n const cardH = this.el.offsetHeight || CARD_HEIGHT\n const minLeft = margin + (tabPosition === 'left' ? tabWidth : 0)\n const maxRight = vw - margin - (tabPosition === 'right' ? tabWidth : 0)\n\n if (x === undefined || y === undefined) {\n const centeredLeft = Math.max(minLeft, (vw - cardW) / 2)\n this.el.style.left = `${Math.min(centeredLeft, maxRight - cardW)}px`\n this.el.style.top = `${Math.max(margin, (vh - cardH) / 2)}px`\n this.el.style.maxHeight = `${vh - 2 * margin}px`\n return\n }\n\n let left = x + 12\n let top = y + 12\n if (left + cardW > maxRight) left = x - cardW - 12\n if (left + cardW > maxRight) left = maxRight - cardW\n if (left < minLeft) left = minLeft\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 private buildHTML(m: I18nMessages): string {\n return `\n <div class=\"mtb-clarify-header\">\n <span class=\"mtb-clarify-icon\">\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 3l1.6 5.4L19 10l-5.4 1.6L12 17l-1.6-5.4L5 10l5.4-1.6L12 3z\"/>\n </svg>\n </span>\n <div class=\"mtb-clarify-titles\">\n <div class=\"mtb-clarify-title\">${escapeHtml(m.clarify.title)}</div>\n <div class=\"mtb-clarify-subtitle\">${escapeHtml(m.clarify.subtitle)}</div>\n </div>\n <button class=\"mtb-clarify-close\" type=\"button\" title=\"${escapeHtml(m.clarify.skip)}\" aria-label=\"${escapeHtml(m.clarify.skip)}\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"mtb-clarify-messages\"></div>\n <div class=\"mtb-clarify-footer\">${this.buildConversationControls(m)}</div>\n `\n }\n\n private buildConversationControls(m: I18nMessages): string {\n return `\n <textarea class=\"mtb-clarify-input\" rows=\"1\" placeholder=\"${escapeHtml(m.clarify.placeholder)}\"></textarea>\n <div class=\"mtb-clarify-actions\">\n <button class=\"mtb-clarify-skip\" type=\"button\">${escapeHtml(m.clarify.skip)}</button>\n <button class=\"mtb-clarify-send\" type=\"button\" aria-label=\"${escapeHtml(m.clarify.send)}\" disabled>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n <span class=\"mtb-clarify-send-label\">${escapeHtml(m.clarify.send)}</span>\n <span class=\"mtb-clarify-send-spinner\" aria-hidden=\"true\"></span>\n </button>\n </div>\n `\n }\n\n private bindEvents(): void {\n this.el.querySelector('.mtb-clarify-close')!.addEventListener('click', () => this.skipAndFinalize())\n this.bindConversationControls()\n }\n\n private bindConversationControls(): void {\n this.inputEl = this.el.querySelector('.mtb-clarify-input')!\n this.sendBtn = this.el.querySelector('.mtb-clarify-send')!\n this.el.querySelector('.mtb-clarify-skip')!.addEventListener('click', () => this.skipAndFinalize())\n\n this.inputEl.addEventListener('input', () => {\n this.sendBtn.disabled = !this.inputEl.value.trim()\n })\n\n this.inputEl.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) return\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n this.sendMessage()\n }\n })\n\n this.sendBtn.addEventListener('click', () => this.sendMessage())\n }\n\n private skipAndFinalize(): void {\n // With no session on the server there is nothing to finalize: the failure\n // footer already offers Retry, so the header close just dismisses.\n if (this.submissionFailed) {\n this.onCancel?.()\n return\n }\n this.stopPolling()\n void this.finalize()\n }\n\n private async startConversation(): Promise<void> {\n this.showThinking()\n if (this.pendingSession) {\n // No session id yet, so an eager reply could not be delivered anywhere —\n // keep the input closed until the assistant's question arrives.\n this.inputEl.disabled = true\n await this.adoptSession(this.pendingSession)\n return\n }\n await this.runInitialTurn()\n }\n\n // Waits out the session upload, then drives the first clarification turn.\n private async adoptSession(pending: Promise<SubmissionSessionResponse | null>): Promise<void> {\n let session: SubmissionSessionResponse | null\n try {\n session = await pending\n } catch {\n session = null\n }\n if (this.destroyed) return\n if (!session) {\n this.showSubmissionFailure()\n return\n }\n this.submissionSessionId = session.id\n this.submissionToken = session.token\n this.inputEl.disabled = false\n if (!session.ai_clarify_available) {\n this.applyResult({ messages: [], done: true })\n return\n }\n await this.runInitialTurn()\n }\n\n // The feedback itself never reached the server, so there is no session to\n // converse with and nothing to finalize — only a full re-submission helps.\n private showSubmissionFailure(): void {\n this.submissionFailed = true\n this.stopPolling()\n this.removeThinking()\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.error.submit)}</div>\n <button class=\"mtb-clarify-send-feedback mtb-clarify-retry\" type=\"button\">${escapeHtml(this.i18n.clarify.retry)}</button>\n `\n footer.querySelector('.mtb-clarify-retry')!.addEventListener('click', () => {\n if (!this.destroyed) void this.retrySubmission()\n })\n }\n\n private async retrySubmission(): Promise<void> {\n if (!this.onRetrySubmission || this.sending) return\n this.submissionFailed = false\n this.showConversationControls()\n this.inputEl.disabled = true\n this.showThinking()\n await this.adoptSession(this.onRetrySubmission())\n }\n\n private async sendMessage(): Promise<void> {\n const text = this.inputEl.value.trim()\n if (!text || this.sending || !this.submissionSessionId) return\n\n this.sending = true\n this.inputEl.disabled = true\n this.sendBtn.disabled = true\n this.sendBtn.setAttribute('aria-busy', 'true')\n this.stopPolling()\n this.finalSubmissionStarted = true\n\n this.appendMessage({ role: 'user', content: text })\n this.inputEl.value = ''\n this.clearError()\n\n try {\n await this.runAnswerTurn(text)\n } catch {\n if (this.destroyed) return\n this.finalSubmissionStarted = false\n this.sending = false\n this.renderConversation(this.lastMessages, false)\n this.inputEl.value = text\n this.showError()\n this.sendBtn.removeAttribute('aria-busy')\n this.inputEl.disabled = false\n this.sendBtn.disabled = !this.inputEl.value.trim()\n this.inputEl.focus({ preventScroll: true })\n }\n }\n\n // Streams the assistant reply; falls back to POST + polling if the backend\n // doesn't support SSE (older server) or the stream errors mid-flight.\n //\n // A stream the server accepts but never terminates would leave the card\n // showing its thinking state forever. The deadline measures silence rather\n // than total time: a stream still delivering tokens is making progress and\n // is left alone, however long the answer turns out to be.\n private async runInitialTurn(): Promise<void> {\n const abort = new AbortController()\n this.streamAbort = abort\n let silenceTimer = setTimeout(() => abort.abort(), CLARIFY_SILENCE_TIMEOUT_MS)\n try {\n const result = await this.apiClient.streamClarification(\n this.submissionSessionId,\n this.submissionToken,\n (text) => {\n clearTimeout(silenceTimer)\n silenceTimer = setTimeout(() => abort.abort(), CLARIFY_SILENCE_TIMEOUT_MS)\n this.appendStreamDelta(text)\n },\n abort.signal,\n )\n if (this.destroyed) return\n this.endStreamingBubble()\n if (!this.applyResult(result)) this.startPolling()\n } catch {\n if (this.destroyed) return\n this.endStreamingBubble()\n await this.runInitialTurnViaPolling()\n } finally {\n clearTimeout(silenceTimer)\n if (this.streamAbort === abort) this.streamAbort = null\n }\n }\n\n private async runInitialTurnViaPolling(): Promise<void> {\n try {\n const res = await this.apiClient.startClarification(\n this.submissionSessionId,\n this.submissionToken,\n )\n if (this.destroyed) return\n if (!this.applyResult(res)) this.startPolling()\n } catch {\n if (this.destroyed) return\n this.handleFailure()\n }\n }\n\n private async runAnswerTurn(message: string): Promise<void> {\n const res = await this.apiClient.startClarification(\n this.submissionSessionId,\n this.submissionToken,\n message,\n )\n if (this.destroyed) return\n if (!this.applyFinalSubmissionResult(res)) this.startPolling()\n }\n\n private showError(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n const error = document.createElement('div')\n error.className = 'mtb-clarify-error'\n error.textContent = this.i18n.error.submit\n footer.prepend(error)\n }\n\n private clearError(): void {\n this.el.querySelector('.mtb-clarify-error')?.remove()\n }\n\n private appendStreamDelta(text: string): void {\n if (!this.streamingBubble) {\n this.removeThinking()\n this.streamingBubble = document.createElement('div')\n this.streamingBubble.className = 'mtb-clarify-bubble mtb-clarify-ai'\n this.messagesEl.appendChild(this.streamingBubble)\n }\n this.streamingBubble.textContent = (this.streamingBubble.textContent ?? '') + text\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private endStreamingBubble(): void {\n this.streamingBubble = null\n }\n\n private startPolling(): void {\n this.stopPolling()\n this.pollDeadline = Date.now() + POLL_TIMEOUT_MS\n this.pollTimer = setInterval(() => this.poll(), POLL_INTERVAL_MS)\n }\n\n private stopPolling(): void {\n if (this.pollTimer !== null) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n }\n\n private async poll(): Promise<void> {\n if (Date.now() > this.pollDeadline) {\n this.stopPolling()\n this.handleFailure()\n return\n }\n try {\n const res = await this.apiClient.getClarification(\n this.submissionSessionId,\n this.submissionToken,\n )\n if (this.destroyed) return\n const settled = this.finalSubmissionStarted\n ? this.applyFinalSubmissionResult(res)\n : this.applyResult(res)\n if (settled) this.stopPolling()\n } catch {\n if (this.destroyed) return\n this.stopPolling()\n this.handleFailure()\n }\n }\n\n private handleDone(autoFinalize: boolean): void {\n if (this.done) return\n this.done = true\n this.stopPolling()\n this.removeThinking()\n if (autoFinalize) {\n void this.finalize()\n return\n }\n this.showFinalizeButton()\n }\n\n private handleFailure(): void {\n if (this.done) return\n this.done = true\n this.sending = false\n this.finalSubmissionStarted = false\n this.stopPolling()\n this.removeThinking()\n this.showClarificationFailureActions()\n }\n\n private showClarificationFailureActions(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.clarify.error)}</div>\n <div class=\"mtb-clarify-actions\">\n <button class=\"mtb-clarify-fallback\" type=\"button\">${escapeHtml(this.i18n.clarify.send_feedback)}</button>\n <button class=\"mtb-clarify-retry\" type=\"button\">${escapeHtml(this.i18n.clarify.retry)}</button>\n </div>\n `\n footer.querySelector('.mtb-clarify-fallback')!.addEventListener('click', () => this.skipAndFinalize())\n footer.querySelector('.mtb-clarify-retry')!.addEventListener('click', () => {\n if (!this.destroyed) void this.retryClarification()\n })\n }\n\n private async retryClarification(): Promise<void> {\n if (this.sending || this.destroyed) return\n\n this.sending = true\n this.done = false\n this.showConversationControls()\n this.inputEl.disabled = true\n this.sendBtn.disabled = true\n this.renderConversation(this.lastMessages, false)\n\n try {\n const snapshot = await this.apiClient.getClarification(this.submissionSessionId, this.submissionToken)\n if (this.destroyed) return\n let result = snapshot\n // Each server status needs a different move. `processing` means a job is\n // already running for this session, so kicking off another turn would\n // duplicate it — fall through to polling instead. `awaiting_response` means\n // the assistant already asked its question and the server is waiting on the\n // reporter; applyResult hands the card back to them rather than polling for\n // a state only a human reply can change.\n if (snapshot.status === 'failed') {\n result = await this.apiClient.retryClarification(this.submissionSessionId, this.submissionToken)\n } else if (snapshot.status === 'idle') {\n result = await this.apiClient.startClarification(this.submissionSessionId, this.submissionToken)\n }\n if (this.destroyed) return\n if (!this.applyResult(result)) {\n this.showConversationControls()\n this.startPolling()\n }\n } catch {\n if (this.destroyed) return\n this.handleFailure()\n } finally {\n this.sending = false\n }\n }\n\n private showConversationControls(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = this.buildConversationControls(this.i18n)\n this.bindConversationControls()\n }\n\n // Returns true when the exchange has settled — the caller uses that to decide\n // whether it still needs to poll.\n private applyFinalSubmissionResult(result: ClarifyResponse): boolean {\n if (result.status === 'failed') {\n this.handleFailure()\n return true\n }\n if (result.done) {\n this.handleDone(true)\n return true\n }\n return false\n }\n\n private applyResult(result: ClarifyStreamResult | ClarifyResponse): boolean {\n this.renderConversation(result.messages, result.done)\n const status = 'status' in result ? result.status : undefined\n const failed = 'status' in result ? result.status === 'failed' : result.failed === true\n if (failed) {\n this.handleFailure()\n return true\n }\n if (result.done) {\n // A completed AI exchange already has the reporter's answer and the\n // assistant's response, so submit it without asking for a second click.\n // Empty completed results mean AI clarification was unavailable, where\n // the reporter still needs an explicit way to send their feedback.\n this.handleDone(result.messages.some(message => message.role === 'assistant'))\n return true\n }\n if (status === 'awaiting_response') {\n // The ball is in the reporter's court: polling here would spin until the\n // deadline and then show a failure card for a perfectly healthy session.\n this.stopPolling()\n this.removeThinking()\n this.enableInput()\n return true\n }\n return false\n }\n\n private enableInput(): void {\n if (!this.el.contains(this.inputEl)) this.showConversationControls()\n this.inputEl.disabled = false\n this.sendBtn.disabled = !this.inputEl.value.trim()\n }\n\n private showFinalizeButton(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <button class=\"mtb-clarify-send-feedback\" type=\"button\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n <span>${escapeHtml(this.i18n.clarify.send_feedback)}</span>\n </button>\n `\n footer.querySelector('.mtb-clarify-send-feedback')!.addEventListener('click', () => {\n if (!this.destroyed) void this.finalize()\n })\n }\n\n private async finalize(): Promise<void> {\n if (this.finalizing || this.destroyed || this.unrecoverable) return\n\n this.finalizing = true\n this.el.querySelectorAll<HTMLButtonElement>('button').forEach(button => { button.disabled = true })\n try {\n await this.onFinalize()\n } catch (error) {\n if (this.destroyed) return\n this.finalizing = false\n this.clearError()\n // A 409 means the server will never accept this session again (it expired\n // past its retention window, or was already finalized/abandoned). Offering\n // Retry there just loops the reporter through the same generic error, so say\n // what happened and what to do instead.\n if (isTerminalConflict(error)) {\n this.showUnrecoverable()\n return\n }\n this.showFinalizeRetry()\n }\n }\n\n private showUnrecoverable(): void {\n this.unrecoverable = true\n this.stopPolling()\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `<div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.clarify.expired)}</div>`\n }\n\n // Renders the reason and the retry together: replacing the footer wholesale is\n // what used to discard the error message showError() had just prepended, so the\n // reporter saw a bare Retry button with no explanation.\n private showFinalizeRetry(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.error.submit)}</div>\n <button class=\"mtb-clarify-send-feedback mtb-clarify-retry\" type=\"button\">${escapeHtml(this.i18n.clarify.retry)}</button>\n `\n footer.querySelector('.mtb-clarify-retry')!.addEventListener('click', () => {\n if (!this.destroyed) void this.finalize()\n })\n }\n\n private renderConversation(messages: ClarifyMessage[], done: boolean): void {\n this.lastMessages = messages\n this.renderMessages(messages)\n const last = messages[messages.length - 1]\n if (!done && (!last || last.role === 'user')) this.showThinking()\n }\n\n private renderMessages(messages: ClarifyMessage[]): void {\n this.messagesEl.innerHTML = ''\n for (const msg of messages) {\n this.appendMessage(msg)\n }\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private appendMessage(msg: ClarifyMessage): void {\n const bubble = document.createElement('div')\n bubble.className = msg.role === 'user' ? 'mtb-clarify-bubble mtb-clarify-user' : 'mtb-clarify-bubble mtb-clarify-ai'\n bubble.textContent = msg.content\n this.messagesEl.appendChild(bubble)\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private showThinking(): void {\n if (this.messagesEl.querySelector('.mtb-clarify-thinking')) return\n const dots = document.createElement('div')\n dots.className = 'mtb-clarify-bubble mtb-clarify-ai mtb-clarify-thinking'\n dots.innerHTML = '<span class=\"mtb-dot\"></span><span class=\"mtb-dot\"></span><span class=\"mtb-dot\"></span>'\n this.messagesEl.appendChild(dots)\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private removeThinking(): void {\n this.messagesEl.querySelector('.mtb-clarify-thinking')?.remove()\n }\n\n destroy(): void {\n this.destroyed = true\n this.streamAbort?.abort()\n this.streamAbort = null\n this.stopPolling()\n this.sheet.release()\n this.el.remove()\n }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { BRAND_COLOR } from '../styles'\n\nconst SUCCESS_AUTO_CLOSE_MS = 5000\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport interface SuccessCardOptions {\n skipFollowup?: boolean\n emailCapture?: {\n onSubmit: (email: string) => Promise<boolean>\n }\n}\n\nexport class SuccessCard {\n private el: HTMLDivElement\n private messages: I18nMessages\n\n constructor(shadow: ShadowContainer, messages: I18nMessages, onClose: () => void, onViewFeedback?: () => void, options?: SuccessCardOptions) {\n this.messages = messages\n this.el = shadow.el<HTMLDivElement>('div', 'mtb-success')\n this.el.style.right = '20px'\n this.el.style.bottom = '20px'\n\n const title = options?.skipFollowup ? messages.success.title_no_ai : messages.success.title\n const msg = options?.skipFollowup ? messages.success.message_no_ai : messages.success.message\n\n const viewLink = onViewFeedback\n ? `<button class=\"mtb-view-feedback-link\">${messages.success.view_feedback} <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14M12 5l7 7-7 7\"/></svg></button>`\n : ''\n\n const emailCapture = options?.emailCapture\n ? `\n <div class=\"mtb-email-capture\">\n <div class=\"mtb-email-prompt\">${messages.success.email_prompt}</div>\n <div class=\"mtb-email-row\">\n <input class=\"mtb-email-input\" type=\"email\" placeholder=\"${messages.success.email_placeholder}\">\n <button class=\"mtb-email-submit\">${messages.success.email_submit}</button>\n </div>\n <div class=\"mtb-email-error\" hidden>${messages.success.email_error}</div>\n </div>`\n : ''\n\n this.el.innerHTML = `\n <div class=\"mtb-success-icon\">\n <svg width=\"23\" height=\"23\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"${BRAND_COLOR}\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"/>\n </svg>\n </div>\n <div class=\"mtb-success-title\">${title}</div>\n <div class=\"mtb-success-msg\">${msg}</div>\n ${emailCapture}\n ${viewLink}\n <button class=\"mtb-close-link\">${messages.success.close}</button>\n `\n shadow.append(this.el)\n this.el.querySelector('.mtb-close-link')!.addEventListener('click', onClose)\n\n if (onViewFeedback) {\n this.el.querySelector('.mtb-view-feedback-link')!.addEventListener('click', onViewFeedback)\n }\n\n if (options?.emailCapture) {\n this.bindEmailCapture(options.emailCapture.onSubmit)\n } else {\n setTimeout(() => onClose(), SUCCESS_AUTO_CLOSE_MS)\n }\n }\n\n private bindEmailCapture(onSubmit: (email: string) => Promise<boolean>): void {\n const input = this.el.querySelector<HTMLInputElement>('.mtb-email-input')!\n const submit = this.el.querySelector<HTMLButtonElement>('.mtb-email-submit')!\n const error = this.el.querySelector<HTMLDivElement>('.mtb-email-error')!\n\n const handle = async () => {\n const email = input.value.trim()\n if (!EMAIL_PATTERN.test(email)) {\n input.classList.add('mtb-email-invalid')\n return\n }\n input.classList.remove('mtb-email-invalid')\n error.hidden = true\n submit.disabled = true\n\n const saved = await onSubmit(email)\n if (saved) {\n this.el.querySelector('.mtb-email-capture')!.innerHTML =\n `<div class=\"mtb-email-saved\">${this.messages.success.email_saved}</div>`\n } else {\n error.hidden = false\n submit.disabled = false\n }\n }\n\n submit.addEventListener('click', handle)\n input.addEventListener('keydown', (event) => {\n if (event.key === 'Enter') handle()\n })\n }\n\n destroy(): void {\n this.el.remove()\n }\n}\n","export function resolveUrl(url, baseUrl) {\n // url is absolute already\n if (url.match(/^[a-z]+:\\/\\//i)) {\n return url;\n }\n // url is absolute already, without protocol\n if (url.match(/^\\/\\//)) {\n return window.location.protocol + url;\n }\n // dataURI, mailto:, tel:, etc.\n if (url.match(/^[a-z]+:/i)) {\n return url;\n }\n const doc = document.implementation.createHTMLDocument();\n const base = doc.createElement('base');\n const a = doc.createElement('a');\n doc.head.appendChild(base);\n doc.body.appendChild(a);\n if (baseUrl) {\n base.href = baseUrl;\n }\n a.href = url;\n return a.href;\n}\nexport const uuid = (() => {\n // generate uuid for className of pseudo elements.\n // We should not use GUIDs, otherwise pseudo elements sometimes cannot be captured.\n let counter = 0;\n // ref: http://stackoverflow.com/a/6248722/2519373\n const random = () => \n // eslint-disable-next-line no-bitwise\n `0000${((Math.random() * 36 ** 4) << 0).toString(36)}`.slice(-4);\n return () => {\n counter += 1;\n return `u${random()}${counter}`;\n };\n})();\nexport function delay(ms) {\n return (args) => new Promise((resolve) => {\n setTimeout(() => resolve(args), ms);\n });\n}\nexport function toArray(arrayLike) {\n const arr = [];\n for (let i = 0, l = arrayLike.length; i < l; i++) {\n arr.push(arrayLike[i]);\n }\n return arr;\n}\nlet styleProps = null;\nexport function getStyleProperties(options = {}) {\n if (styleProps) {\n return styleProps;\n }\n if (options.includeStyleProperties) {\n styleProps = options.includeStyleProperties;\n return styleProps;\n }\n styleProps = toArray(window.getComputedStyle(document.documentElement));\n return styleProps;\n}\nfunction px(node, styleProperty) {\n const win = node.ownerDocument.defaultView || window;\n const val = win.getComputedStyle(node).getPropertyValue(styleProperty);\n return val ? parseFloat(val.replace('px', '')) : 0;\n}\nfunction getNodeWidth(node) {\n const leftBorder = px(node, 'border-left-width');\n const rightBorder = px(node, 'border-right-width');\n return node.clientWidth + leftBorder + rightBorder;\n}\nfunction getNodeHeight(node) {\n const topBorder = px(node, 'border-top-width');\n const bottomBorder = px(node, 'border-bottom-width');\n return node.clientHeight + topBorder + bottomBorder;\n}\nexport function getImageSize(targetNode, options = {}) {\n const width = options.width || getNodeWidth(targetNode);\n const height = options.height || getNodeHeight(targetNode);\n return { width, height };\n}\nexport function getPixelRatio() {\n let ratio;\n let FINAL_PROCESS;\n try {\n FINAL_PROCESS = process;\n }\n catch (e) {\n // pass\n }\n const val = FINAL_PROCESS && FINAL_PROCESS.env\n ? FINAL_PROCESS.env.devicePixelRatio\n : null;\n if (val) {\n ratio = parseInt(val, 10);\n if (Number.isNaN(ratio)) {\n ratio = 1;\n }\n }\n return ratio || window.devicePixelRatio || 1;\n}\n// @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size\nconst canvasDimensionLimit = 16384;\nexport function checkCanvasDimensions(canvas) {\n if (canvas.width > canvasDimensionLimit ||\n canvas.height > canvasDimensionLimit) {\n if (canvas.width > canvasDimensionLimit &&\n canvas.height > canvasDimensionLimit) {\n if (canvas.width > canvas.height) {\n canvas.height *= canvasDimensionLimit / canvas.width;\n canvas.width = canvasDimensionLimit;\n }\n else {\n canvas.width *= canvasDimensionLimit / canvas.height;\n canvas.height = canvasDimensionLimit;\n }\n }\n else if (canvas.width > canvasDimensionLimit) {\n canvas.height *= canvasDimensionLimit / canvas.width;\n canvas.width = canvasDimensionLimit;\n }\n else {\n canvas.width *= canvasDimensionLimit / canvas.height;\n canvas.height = canvasDimensionLimit;\n }\n }\n}\nexport function canvasToBlob(canvas, options = {}) {\n if (canvas.toBlob) {\n return new Promise((resolve) => {\n canvas.toBlob(resolve, options.type ? options.type : 'image/png', options.quality ? options.quality : 1);\n });\n }\n return new Promise((resolve) => {\n const binaryString = window.atob(canvas\n .toDataURL(options.type ? options.type : undefined, options.quality ? options.quality : undefined)\n .split(',')[1]);\n const len = binaryString.length;\n const binaryArray = new Uint8Array(len);\n for (let i = 0; i < len; i += 1) {\n binaryArray[i] = binaryString.charCodeAt(i);\n }\n resolve(new Blob([binaryArray], {\n type: options.type ? options.type : 'image/png',\n }));\n });\n}\nexport function createImage(url) {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n img.decode().then(() => {\n requestAnimationFrame(() => resolve(img));\n });\n };\n img.onerror = reject;\n img.crossOrigin = 'anonymous';\n img.decoding = 'async';\n img.src = url;\n });\n}\nexport async function svgToDataURL(svg) {\n return Promise.resolve()\n .then(() => new XMLSerializer().serializeToString(svg))\n .then(encodeURIComponent)\n .then((html) => `data:image/svg+xml;charset=utf-8,${html}`);\n}\nexport async function nodeToDataURL(node, width, height) {\n const xmlns = 'http://www.w3.org/2000/svg';\n const svg = document.createElementNS(xmlns, 'svg');\n const foreignObject = document.createElementNS(xmlns, 'foreignObject');\n svg.setAttribute('width', `${width}`);\n svg.setAttribute('height', `${height}`);\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n foreignObject.setAttribute('width', '100%');\n foreignObject.setAttribute('height', '100%');\n foreignObject.setAttribute('x', '0');\n foreignObject.setAttribute('y', '0');\n foreignObject.setAttribute('externalResourcesRequired', 'true');\n svg.appendChild(foreignObject);\n foreignObject.appendChild(node);\n return svgToDataURL(svg);\n}\nexport const isInstanceOfElement = (node, instance) => {\n if (node instanceof instance)\n return true;\n const nodePrototype = Object.getPrototypeOf(node);\n if (nodePrototype === null)\n return false;\n return (nodePrototype.constructor.name === instance.name ||\n isInstanceOfElement(nodePrototype, instance));\n};\n//# sourceMappingURL=util.js.map","import { uuid, getStyleProperties } from './util';\nfunction formatCSSText(style) {\n const content = style.getPropertyValue('content');\n return `${style.cssText} content: '${content.replace(/'|\"/g, '')}';`;\n}\nfunction formatCSSProperties(style, options) {\n return getStyleProperties(options)\n .map((name) => {\n const value = style.getPropertyValue(name);\n const priority = style.getPropertyPriority(name);\n return `${name}: ${value}${priority ? ' !important' : ''};`;\n })\n .join(' ');\n}\nfunction getPseudoElementStyle(className, pseudo, style, options) {\n const selector = `.${className}:${pseudo}`;\n const cssText = style.cssText\n ? formatCSSText(style)\n : formatCSSProperties(style, options);\n return document.createTextNode(`${selector}{${cssText}}`);\n}\nfunction clonePseudoElement(nativeNode, clonedNode, pseudo, options) {\n const style = window.getComputedStyle(nativeNode, pseudo);\n const content = style.getPropertyValue('content');\n if (content === '' || content === 'none') {\n return;\n }\n const className = uuid();\n try {\n clonedNode.className = `${clonedNode.className} ${className}`;\n }\n catch (err) {\n return;\n }\n const styleElement = document.createElement('style');\n styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));\n clonedNode.appendChild(styleElement);\n}\nexport function clonePseudoElements(nativeNode, clonedNode, options) {\n clonePseudoElement(nativeNode, clonedNode, ':before', options);\n clonePseudoElement(nativeNode, clonedNode, ':after', options);\n}\n//# sourceMappingURL=clone-pseudos.js.map","const WOFF = 'application/font-woff';\nconst JPEG = 'image/jpeg';\nconst mimes = {\n woff: WOFF,\n woff2: WOFF,\n ttf: 'application/font-truetype',\n eot: 'application/vnd.ms-fontobject',\n png: 'image/png',\n jpg: JPEG,\n jpeg: JPEG,\n gif: 'image/gif',\n tiff: 'image/tiff',\n svg: 'image/svg+xml',\n webp: 'image/webp',\n};\nfunction getExtension(url) {\n const match = /\\.([^./]*?)$/g.exec(url);\n return match ? match[1] : '';\n}\nexport function getMimeType(url) {\n const extension = getExtension(url).toLowerCase();\n return mimes[extension] || '';\n}\n//# sourceMappingURL=mimes.js.map","function getContentFromDataUrl(dataURL) {\n return dataURL.split(/,/)[1];\n}\nexport function isDataUrl(url) {\n return url.search(/^(data:)/) !== -1;\n}\nexport function makeDataUrl(content, mimeType) {\n return `data:${mimeType};base64,${content}`;\n}\nexport async function fetchAsDataURL(url, init, process) {\n const res = await fetch(url, init);\n if (res.status === 404) {\n throw new Error(`Resource \"${res.url}\" not found`);\n }\n const blob = await res.blob();\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = reject;\n reader.onloadend = () => {\n try {\n resolve(process({ res, result: reader.result }));\n }\n catch (error) {\n reject(error);\n }\n };\n reader.readAsDataURL(blob);\n });\n}\nconst cache = {};\nfunction getCacheKey(url, contentType, includeQueryParams) {\n let key = url.replace(/\\?.*/, '');\n if (includeQueryParams) {\n key = url;\n }\n // font resource\n if (/ttf|otf|eot|woff2?/i.test(key)) {\n key = key.replace(/.*\\//, '');\n }\n return contentType ? `[${contentType}]${key}` : key;\n}\nexport async function resourceToDataURL(resourceUrl, contentType, options) {\n const cacheKey = getCacheKey(resourceUrl, contentType, options.includeQueryParams);\n if (cache[cacheKey] != null) {\n return cache[cacheKey];\n }\n // ref: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache\n if (options.cacheBust) {\n // eslint-disable-next-line no-param-reassign\n resourceUrl += (/\\?/.test(resourceUrl) ? '&' : '?') + new Date().getTime();\n }\n let dataURL;\n try {\n const content = await fetchAsDataURL(resourceUrl, options.fetchRequestInit, ({ res, result }) => {\n if (!contentType) {\n // eslint-disable-next-line no-param-reassign\n contentType = res.headers.get('Content-Type') || '';\n }\n return getContentFromDataUrl(result);\n });\n dataURL = makeDataUrl(content, contentType);\n }\n catch (error) {\n dataURL = options.imagePlaceholder || '';\n let msg = `Failed to fetch resource: ${resourceUrl}`;\n if (error) {\n msg = typeof error === 'string' ? error : error.message;\n }\n if (msg) {\n console.warn(msg);\n }\n }\n cache[cacheKey] = dataURL;\n return dataURL;\n}\n//# sourceMappingURL=dataurl.js.map","import { clonePseudoElements } from './clone-pseudos';\nimport { createImage, toArray, isInstanceOfElement, getStyleProperties, } from './util';\nimport { getMimeType } from './mimes';\nimport { resourceToDataURL } from './dataurl';\nasync function cloneCanvasElement(canvas) {\n const dataURL = canvas.toDataURL();\n if (dataURL === 'data:,') {\n return canvas.cloneNode(false);\n }\n return createImage(dataURL);\n}\nasync function cloneVideoElement(video, options) {\n if (video.currentSrc) {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n canvas.width = video.clientWidth;\n canvas.height = video.clientHeight;\n ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n const dataURL = canvas.toDataURL();\n return createImage(dataURL);\n }\n const poster = video.poster;\n const contentType = getMimeType(poster);\n const dataURL = await resourceToDataURL(poster, contentType, options);\n return createImage(dataURL);\n}\nasync function cloneIFrameElement(iframe, options) {\n var _a;\n try {\n if ((_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.body) {\n return (await cloneNode(iframe.contentDocument.body, options, true));\n }\n }\n catch (_b) {\n // Failed to clone iframe\n }\n return iframe.cloneNode(false);\n}\nasync function cloneSingleNode(node, options) {\n if (isInstanceOfElement(node, HTMLCanvasElement)) {\n return cloneCanvasElement(node);\n }\n if (isInstanceOfElement(node, HTMLVideoElement)) {\n return cloneVideoElement(node, options);\n }\n if (isInstanceOfElement(node, HTMLIFrameElement)) {\n return cloneIFrameElement(node, options);\n }\n return node.cloneNode(isSVGElement(node));\n}\nconst isSlotElement = (node) => node.tagName != null && node.tagName.toUpperCase() === 'SLOT';\nconst isSVGElement = (node) => node.tagName != null && node.tagName.toUpperCase() === 'SVG';\nasync function cloneChildren(nativeNode, clonedNode, options) {\n var _a, _b;\n if (isSVGElement(clonedNode)) {\n return clonedNode;\n }\n let children = [];\n if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {\n children = toArray(nativeNode.assignedNodes());\n }\n else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) &&\n ((_a = nativeNode.contentDocument) === null || _a === void 0 ? void 0 : _a.body)) {\n children = toArray(nativeNode.contentDocument.body.childNodes);\n }\n else {\n children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);\n }\n if (children.length === 0 ||\n isInstanceOfElement(nativeNode, HTMLVideoElement)) {\n return clonedNode;\n }\n await children.reduce((deferred, child) => deferred\n .then(() => cloneNode(child, options))\n .then((clonedChild) => {\n if (clonedChild) {\n clonedNode.appendChild(clonedChild);\n }\n }), Promise.resolve());\n return clonedNode;\n}\nfunction cloneCSSStyle(nativeNode, clonedNode, options) {\n const targetStyle = clonedNode.style;\n if (!targetStyle) {\n return;\n }\n const sourceStyle = window.getComputedStyle(nativeNode);\n if (sourceStyle.cssText) {\n targetStyle.cssText = sourceStyle.cssText;\n targetStyle.transformOrigin = sourceStyle.transformOrigin;\n }\n else {\n getStyleProperties(options).forEach((name) => {\n let value = sourceStyle.getPropertyValue(name);\n if (name === 'font-size' && value.endsWith('px')) {\n const reducedFont = Math.floor(parseFloat(value.substring(0, value.length - 2))) - 0.1;\n value = `${reducedFont}px`;\n }\n if (isInstanceOfElement(nativeNode, HTMLIFrameElement) &&\n name === 'display' &&\n value === 'inline') {\n value = 'block';\n }\n if (name === 'd' && clonedNode.getAttribute('d')) {\n value = `path(${clonedNode.getAttribute('d')})`;\n }\n targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));\n });\n }\n}\nfunction cloneInputValue(nativeNode, clonedNode) {\n if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {\n clonedNode.innerHTML = nativeNode.value;\n }\n if (isInstanceOfElement(nativeNode, HTMLInputElement)) {\n clonedNode.setAttribute('value', nativeNode.value);\n }\n}\nfunction cloneSelectValue(nativeNode, clonedNode) {\n if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {\n const clonedSelect = clonedNode;\n const selectedOption = Array.from(clonedSelect.children).find((child) => nativeNode.value === child.getAttribute('value'));\n if (selectedOption) {\n selectedOption.setAttribute('selected', '');\n }\n }\n}\nfunction decorate(nativeNode, clonedNode, options) {\n if (isInstanceOfElement(clonedNode, Element)) {\n cloneCSSStyle(nativeNode, clonedNode, options);\n clonePseudoElements(nativeNode, clonedNode, options);\n cloneInputValue(nativeNode, clonedNode);\n cloneSelectValue(nativeNode, clonedNode);\n }\n return clonedNode;\n}\nasync function ensureSVGSymbols(clone, options) {\n const uses = clone.querySelectorAll ? clone.querySelectorAll('use') : [];\n if (uses.length === 0) {\n return clone;\n }\n const processedDefs = {};\n for (let i = 0; i < uses.length; i++) {\n const use = uses[i];\n const id = use.getAttribute('xlink:href');\n if (id) {\n const exist = clone.querySelector(id);\n const definition = document.querySelector(id);\n if (!exist && definition && !processedDefs[id]) {\n // eslint-disable-next-line no-await-in-loop\n processedDefs[id] = (await cloneNode(definition, options, true));\n }\n }\n }\n const nodes = Object.values(processedDefs);\n if (nodes.length) {\n const ns = 'http://www.w3.org/1999/xhtml';\n const svg = document.createElementNS(ns, 'svg');\n svg.setAttribute('xmlns', ns);\n svg.style.position = 'absolute';\n svg.style.width = '0';\n svg.style.height = '0';\n svg.style.overflow = 'hidden';\n svg.style.display = 'none';\n const defs = document.createElementNS(ns, 'defs');\n svg.appendChild(defs);\n for (let i = 0; i < nodes.length; i++) {\n defs.appendChild(nodes[i]);\n }\n clone.appendChild(svg);\n }\n return clone;\n}\nexport async function cloneNode(node, options, isRoot) {\n if (!isRoot && options.filter && !options.filter(node)) {\n return null;\n }\n return Promise.resolve(node)\n .then((clonedNode) => cloneSingleNode(clonedNode, options))\n .then((clonedNode) => cloneChildren(node, clonedNode, options))\n .then((clonedNode) => decorate(node, clonedNode, options))\n .then((clonedNode) => ensureSVGSymbols(clonedNode, options));\n}\n//# sourceMappingURL=clone-node.js.map","import { resolveUrl } from './util';\nimport { getMimeType } from './mimes';\nimport { isDataUrl, makeDataUrl, resourceToDataURL } from './dataurl';\nconst URL_REGEX = /url\\((['\"]?)([^'\"]+?)\\1\\)/g;\nconst URL_WITH_FORMAT_REGEX = /url\\([^)]+\\)\\s*format\\(([\"']?)([^\"']+)\\1\\)/g;\nconst FONT_SRC_REGEX = /src:\\s*(?:url\\([^)]+\\)\\s*format\\([^)]+\\)[,;]\\s*)+/g;\nfunction toRegex(url) {\n // eslint-disable-next-line no-useless-escape\n const escaped = url.replace(/([.*+?^${}()|\\[\\]\\/\\\\])/g, '\\\\$1');\n return new RegExp(`(url\\\\(['\"]?)(${escaped})(['\"]?\\\\))`, 'g');\n}\nexport function parseURLs(cssText) {\n const urls = [];\n cssText.replace(URL_REGEX, (raw, quotation, url) => {\n urls.push(url);\n return raw;\n });\n return urls.filter((url) => !isDataUrl(url));\n}\nexport async function embed(cssText, resourceURL, baseURL, options, getContentFromUrl) {\n try {\n const resolvedURL = baseURL ? resolveUrl(resourceURL, baseURL) : resourceURL;\n const contentType = getMimeType(resourceURL);\n let dataURL;\n if (getContentFromUrl) {\n const content = await getContentFromUrl(resolvedURL);\n dataURL = makeDataUrl(content, contentType);\n }\n else {\n dataURL = await resourceToDataURL(resolvedURL, contentType, options);\n }\n return cssText.replace(toRegex(resourceURL), `$1${dataURL}$3`);\n }\n catch (error) {\n // pass\n }\n return cssText;\n}\nfunction filterPreferredFontFormat(str, { preferredFontFormat }) {\n return !preferredFontFormat\n ? str\n : str.replace(FONT_SRC_REGEX, (match) => {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match) || [];\n if (!format) {\n return '';\n }\n if (format === preferredFontFormat) {\n return `src: ${src};`;\n }\n }\n });\n}\nexport function shouldEmbed(url) {\n return url.search(URL_REGEX) !== -1;\n}\nexport async function embedResources(cssText, baseUrl, options) {\n if (!shouldEmbed(cssText)) {\n return cssText;\n }\n const filteredCSSText = filterPreferredFontFormat(cssText, options);\n const urls = parseURLs(filteredCSSText);\n return urls.reduce((deferred, url) => deferred.then((css) => embed(css, url, baseUrl, options)), Promise.resolve(filteredCSSText));\n}\n//# sourceMappingURL=embed-resources.js.map","import { embedResources } from './embed-resources';\nimport { toArray, isInstanceOfElement } from './util';\nimport { isDataUrl, resourceToDataURL } from './dataurl';\nimport { getMimeType } from './mimes';\nasync function embedProp(propName, node, options) {\n var _a;\n const propValue = (_a = node.style) === null || _a === void 0 ? void 0 : _a.getPropertyValue(propName);\n if (propValue) {\n const cssString = await embedResources(propValue, null, options);\n node.style.setProperty(propName, cssString, node.style.getPropertyPriority(propName));\n return true;\n }\n return false;\n}\nasync function embedBackground(clonedNode, options) {\n ;\n (await embedProp('background', clonedNode, options)) ||\n (await embedProp('background-image', clonedNode, options));\n (await embedProp('mask', clonedNode, options)) ||\n (await embedProp('-webkit-mask', clonedNode, options)) ||\n (await embedProp('mask-image', clonedNode, options)) ||\n (await embedProp('-webkit-mask-image', clonedNode, options));\n}\nasync function embedImageNode(clonedNode, options) {\n const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement);\n if (!(isImageElement && !isDataUrl(clonedNode.src)) &&\n !(isInstanceOfElement(clonedNode, SVGImageElement) &&\n !isDataUrl(clonedNode.href.baseVal))) {\n return;\n }\n const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal;\n const dataURL = await resourceToDataURL(url, getMimeType(url), options);\n await new Promise((resolve, reject) => {\n clonedNode.onload = resolve;\n clonedNode.onerror = options.onImageErrorHandler\n ? (...attributes) => {\n try {\n resolve(options.onImageErrorHandler(...attributes));\n }\n catch (error) {\n reject(error);\n }\n }\n : reject;\n const image = clonedNode;\n if (image.decode) {\n image.decode = resolve;\n }\n if (image.loading === 'lazy') {\n image.loading = 'eager';\n }\n if (isImageElement) {\n clonedNode.srcset = '';\n clonedNode.src = dataURL;\n }\n else {\n clonedNode.href.baseVal = dataURL;\n }\n });\n}\nasync function embedChildren(clonedNode, options) {\n const children = toArray(clonedNode.childNodes);\n const deferreds = children.map((child) => embedImages(child, options));\n await Promise.all(deferreds).then(() => clonedNode);\n}\nexport async function embedImages(clonedNode, options) {\n if (isInstanceOfElement(clonedNode, Element)) {\n await embedBackground(clonedNode, options);\n await embedImageNode(clonedNode, options);\n await embedChildren(clonedNode, options);\n }\n}\n//# sourceMappingURL=embed-images.js.map","export function applyStyle(node, options) {\n const { style } = node;\n if (options.backgroundColor) {\n style.backgroundColor = options.backgroundColor;\n }\n if (options.width) {\n style.width = `${options.width}px`;\n }\n if (options.height) {\n style.height = `${options.height}px`;\n }\n const manual = options.style;\n if (manual != null) {\n Object.keys(manual).forEach((key) => {\n style[key] = manual[key];\n });\n }\n return node;\n}\n//# sourceMappingURL=apply-style.js.map","import { toArray } from './util';\nimport { fetchAsDataURL } from './dataurl';\nimport { shouldEmbed, embedResources } from './embed-resources';\nconst cssFetchCache = {};\nasync function fetchCSS(url) {\n let cache = cssFetchCache[url];\n if (cache != null) {\n return cache;\n }\n const res = await fetch(url);\n const cssText = await res.text();\n cache = { url, cssText };\n cssFetchCache[url] = cache;\n return cache;\n}\nasync function embedFonts(data, options) {\n let cssText = data.cssText;\n const regexUrl = /url\\([\"']?([^\"')]+)[\"']?\\)/g;\n const fontLocs = cssText.match(/url\\([^)]+\\)/g) || [];\n const loadFonts = fontLocs.map(async (loc) => {\n let url = loc.replace(regexUrl, '$1');\n if (!url.startsWith('https://')) {\n url = new URL(url, data.url).href;\n }\n return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {\n cssText = cssText.replace(loc, `url(${result})`);\n return [loc, result];\n });\n });\n return Promise.all(loadFonts).then(() => cssText);\n}\nfunction parseCSS(source) {\n if (source == null) {\n return [];\n }\n const result = [];\n const commentsRegex = /(\\/\\*[\\s\\S]*?\\*\\/)/gi;\n // strip out comments\n let cssText = source.replace(commentsRegex, '');\n // eslint-disable-next-line prefer-regex-literals\n const keyframesRegex = new RegExp('((@.*?keyframes [\\\\s\\\\S]*?){([\\\\s\\\\S]*?}\\\\s*?)})', 'gi');\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const matches = keyframesRegex.exec(cssText);\n if (matches === null) {\n break;\n }\n result.push(matches[0]);\n }\n cssText = cssText.replace(keyframesRegex, '');\n const importRegex = /@import[\\s\\S]*?url\\([^)]*\\)[\\s\\S]*?;/gi;\n // to match css & media queries together\n const combinedCSSRegex = '((\\\\s*?(?:\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\/)?\\\\s*?@media[\\\\s\\\\S]' +\n '*?){([\\\\s\\\\S]*?)}\\\\s*?})|(([\\\\s\\\\S]*?){([\\\\s\\\\S]*?)})';\n // unified regex\n const unifiedRegex = new RegExp(combinedCSSRegex, 'gi');\n // eslint-disable-next-line no-constant-condition\n while (true) {\n let matches = importRegex.exec(cssText);\n if (matches === null) {\n matches = unifiedRegex.exec(cssText);\n if (matches === null) {\n break;\n }\n else {\n importRegex.lastIndex = unifiedRegex.lastIndex;\n }\n }\n else {\n unifiedRegex.lastIndex = importRegex.lastIndex;\n }\n result.push(matches[0]);\n }\n return result;\n}\nasync function getCSSRules(styleSheets, options) {\n const ret = [];\n const deferreds = [];\n // First loop inlines imports\n styleSheets.forEach((sheet) => {\n if ('cssRules' in sheet) {\n try {\n toArray(sheet.cssRules || []).forEach((item, index) => {\n if (item.type === CSSRule.IMPORT_RULE) {\n let importIndex = index + 1;\n const url = item.href;\n const deferred = fetchCSS(url)\n .then((metadata) => embedFonts(metadata, options))\n .then((cssText) => parseCSS(cssText).forEach((rule) => {\n try {\n sheet.insertRule(rule, rule.startsWith('@import')\n ? (importIndex += 1)\n : sheet.cssRules.length);\n }\n catch (error) {\n console.error('Error inserting rule from remote css', {\n rule,\n error,\n });\n }\n }))\n .catch((e) => {\n console.error('Error loading remote css', e.toString());\n });\n deferreds.push(deferred);\n }\n });\n }\n catch (e) {\n const inline = styleSheets.find((a) => a.href == null) || document.styleSheets[0];\n if (sheet.href != null) {\n deferreds.push(fetchCSS(sheet.href)\n .then((metadata) => embedFonts(metadata, options))\n .then((cssText) => parseCSS(cssText).forEach((rule) => {\n inline.insertRule(rule, inline.cssRules.length);\n }))\n .catch((err) => {\n console.error('Error loading remote stylesheet', err);\n }));\n }\n console.error('Error inlining remote css file', e);\n }\n }\n });\n return Promise.all(deferreds).then(() => {\n // Second loop parses rules\n styleSheets.forEach((sheet) => {\n if ('cssRules' in sheet) {\n try {\n toArray(sheet.cssRules || []).forEach((item) => {\n ret.push(item);\n });\n }\n catch (e) {\n console.error(`Error while reading CSS rules from ${sheet.href}`, e);\n }\n }\n });\n return ret;\n });\n}\nfunction getWebFontRules(cssRules) {\n return cssRules\n .filter((rule) => rule.type === CSSRule.FONT_FACE_RULE)\n .filter((rule) => shouldEmbed(rule.style.getPropertyValue('src')));\n}\nasync function parseWebFontRules(node, options) {\n if (node.ownerDocument == null) {\n throw new Error('Provided element is not within a Document');\n }\n const styleSheets = toArray(node.ownerDocument.styleSheets);\n const cssRules = await getCSSRules(styleSheets, options);\n return getWebFontRules(cssRules);\n}\nfunction normalizeFontFamily(font) {\n return font.trim().replace(/[\"']/g, '');\n}\nfunction getUsedFonts(node) {\n const fonts = new Set();\n function traverse(node) {\n const fontFamily = node.style.fontFamily || getComputedStyle(node).fontFamily;\n fontFamily.split(',').forEach((font) => {\n fonts.add(normalizeFontFamily(font));\n });\n Array.from(node.children).forEach((child) => {\n if (child instanceof HTMLElement) {\n traverse(child);\n }\n });\n }\n traverse(node);\n return fonts;\n}\nexport async function getWebFontCSS(node, options) {\n const rules = await parseWebFontRules(node, options);\n const usedFonts = getUsedFonts(node);\n const cssTexts = await Promise.all(rules\n .filter((rule) => usedFonts.has(normalizeFontFamily(rule.style.fontFamily)))\n .map((rule) => {\n const baseUrl = rule.parentStyleSheet\n ? rule.parentStyleSheet.href\n : null;\n return embedResources(rule.cssText, baseUrl, options);\n }));\n return cssTexts.join('\\n');\n}\nexport async function embedWebFonts(clonedNode, options) {\n const cssText = options.fontEmbedCSS != null\n ? options.fontEmbedCSS\n : options.skipFonts\n ? null\n : await getWebFontCSS(clonedNode, options);\n if (cssText) {\n const styleNode = document.createElement('style');\n const sytleContent = document.createTextNode(cssText);\n styleNode.appendChild(sytleContent);\n if (clonedNode.firstChild) {\n clonedNode.insertBefore(styleNode, clonedNode.firstChild);\n }\n else {\n clonedNode.appendChild(styleNode);\n }\n }\n}\n//# sourceMappingURL=embed-webfonts.js.map","import { cloneNode } from './clone-node';\nimport { embedImages } from './embed-images';\nimport { applyStyle } from './apply-style';\nimport { embedWebFonts, getWebFontCSS } from './embed-webfonts';\nimport { getImageSize, getPixelRatio, createImage, canvasToBlob, nodeToDataURL, checkCanvasDimensions, } from './util';\nexport async function toSvg(node, options = {}) {\n const { width, height } = getImageSize(node, options);\n const clonedNode = (await cloneNode(node, options, true));\n await embedWebFonts(clonedNode, options);\n await embedImages(clonedNode, options);\n applyStyle(clonedNode, options);\n const datauri = await nodeToDataURL(clonedNode, width, height);\n return datauri;\n}\nexport async function toCanvas(node, options = {}) {\n const { width, height } = getImageSize(node, options);\n const svg = await toSvg(node, options);\n const img = await createImage(svg);\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n const ratio = options.pixelRatio || getPixelRatio();\n const canvasWidth = options.canvasWidth || width;\n const canvasHeight = options.canvasHeight || height;\n canvas.width = canvasWidth * ratio;\n canvas.height = canvasHeight * ratio;\n if (!options.skipAutoScale) {\n checkCanvasDimensions(canvas);\n }\n canvas.style.width = `${canvasWidth}`;\n canvas.style.height = `${canvasHeight}`;\n if (options.backgroundColor) {\n context.fillStyle = options.backgroundColor;\n context.fillRect(0, 0, canvas.width, canvas.height);\n }\n context.drawImage(img, 0, 0, canvas.width, canvas.height);\n return canvas;\n}\nexport async function toPixelData(node, options = {}) {\n const { width, height } = getImageSize(node, options);\n const canvas = await toCanvas(node, options);\n const ctx = canvas.getContext('2d');\n return ctx.getImageData(0, 0, width, height).data;\n}\nexport async function toPng(node, options = {}) {\n const canvas = await toCanvas(node, options);\n return canvas.toDataURL();\n}\nexport async function toJpeg(node, options = {}) {\n const canvas = await toCanvas(node, options);\n return canvas.toDataURL('image/jpeg', options.quality || 1);\n}\nexport async function toBlob(node, options = {}) {\n const canvas = await toCanvas(node, options);\n const blob = await canvasToBlob(canvas);\n return blob;\n}\nexport async function getFontEmbedCSS(node, options = {}) {\n return getWebFontCSS(node, options);\n}\n//# sourceMappingURL=index.js.map","import type { Annotation } from '../types'\nimport { parsePathPolylines } from './capture'\n\nexport interface CropRect {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nconst MIN_CROP_WIDTH = 400\nconst MIN_CROP_HEIGHT = 300\nconst PADDING_PX = 150\nconst PADDING_RATIO = 0.3\nconst SKIP_CROP_RATIO = 0.85\n\nexport function getAnnotationBounds(annotations: Annotation[]): Bounds | null {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n\n for (const ann of annotations) {\n if (ann.type === 'pin') {\n minX = Math.min(minX, ann.x)\n minY = Math.min(minY, ann.y)\n maxX = Math.max(maxX, ann.x)\n maxY = Math.max(maxY, ann.y)\n if (ann.targetRect) {\n const r = ann.targetRect\n minX = Math.min(minX, r.left)\n minY = Math.min(minY, r.top)\n maxX = Math.max(maxX, r.left + r.width)\n maxY = Math.max(maxY, r.bottom)\n }\n } else if (ann.type === 'draw' && ann.drawPath) {\n for (const polyline of parsePathPolylines(ann.drawPath)) {\n for (const [x, y] of polyline) {\n minX = Math.min(minX, x)\n minY = Math.min(minY, y)\n maxX = Math.max(maxX, x)\n maxY = Math.max(maxY, y)\n }\n }\n }\n }\n\n if (!isFinite(minX) || !isFinite(minY)) return null\n return { minX, minY, maxX, maxY }\n}\n\nexport function computeCropRegion(\n annotations: Annotation[],\n viewportWidth: number,\n viewportHeight: number,\n): CropRect | null {\n const bounds = getAnnotationBounds(annotations)\n if (!bounds) return null\n\n const padX = Math.max(PADDING_PX, viewportWidth * PADDING_RATIO)\n const padY = Math.max(PADDING_PX, viewportHeight * PADDING_RATIO)\n\n let x = bounds.minX - padX\n let y = bounds.minY - padY\n let w = (bounds.maxX - bounds.minX) + padX * 2\n let h = (bounds.maxY - bounds.minY) + padY * 2\n\n if (w < MIN_CROP_WIDTH) {\n x -= (MIN_CROP_WIDTH - w) / 2\n w = MIN_CROP_WIDTH\n }\n if (h < MIN_CROP_HEIGHT) {\n y -= (MIN_CROP_HEIGHT - h) / 2\n h = MIN_CROP_HEIGHT\n }\n\n w = Math.min(w, viewportWidth)\n h = Math.min(h, viewportHeight)\n x = Math.min(Math.max(0, x), viewportWidth - w)\n y = Math.min(Math.max(0, y), viewportHeight - h)\n\n if (w >= viewportWidth * SKIP_CROP_RATIO && h >= viewportHeight * SKIP_CROP_RATIO) {\n return null\n }\n\n return {\n x: Math.round(x),\n y: Math.round(y),\n width: Math.round(w),\n height: Math.round(h),\n }\n}\n\nexport function cropCanvas(source: HTMLCanvasElement, rect: CropRect): HTMLCanvasElement {\n const cropped = document.createElement('canvas')\n cropped.width = rect.width\n cropped.height = rect.height\n const ctx = cropped.getContext('2d')\n if (ctx) {\n ctx.drawImage(source, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height)\n }\n return cropped\n}\n\nexport async function cropDataUrl(dataUrl: string, rect: CropRect): Promise<string> {\n const image = await loadImageForCrop(dataUrl)\n if (!image) return dataUrl\n const canvas = document.createElement('canvas')\n canvas.width = rect.width\n canvas.height = rect.height\n const ctx = canvas.getContext('2d')\n if (!ctx) return dataUrl\n ctx.drawImage(image, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height)\n return canvas.toDataURL('image/jpeg', 0.85)\n}\n\nfunction loadImageForCrop(dataUrl: string): Promise<HTMLImageElement | null> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = () => resolve(null)\n img.src = dataUrl\n })\n}\n","import { toJpeg } from 'html-to-image'\nimport type { Annotation } from '../types'\nimport { BLOCK_CLASS, MASK_TEXT_CLASS } from '../record/session'\nimport { filterInputValue, isSensitiveField } from '../privacy/fields'\nimport { filterSensitiveText } from '../privacy/sanitize'\nimport { createSensitiveTextContext, hasSensitiveTextContext } from '../privacy/text-context'\nimport { BRAND_COLOR, FONT_STACK, brandAlpha } from '../styles'\nimport { computeCropRegion, cropCanvas, getAnnotationBounds } from './crop'\nimport type { CropRect } from './crop'\n\ninterface CaptureMetrics {\n left: number\n top: number\n width: number\n height: number\n}\n\ninterface RedactionRect {\n left: number\n top: number\n width: number\n height: number\n}\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. Pages tall enough to exceed the\n// browser's canvas ceiling go below 1:1 — see capturePixelRatio.\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 async function captureScreenshot(annotations: Annotation[] = []): Promise<Blob | null> {\n try {\n const captureMetrics = getCaptureMetrics(document.body)\n const screenshotAnnotations = annotations.map(translateAnnotationForScreenshot)\n const redactions = collectPrivacyRedactions(document.body)\n const dataUrl = await toJpeg(document.body, {\n quality: 0.85,\n pixelRatio: capturePixelRatio(captureMetrics),\n backgroundColor: '#ffffff',\n skipFonts: true,\n skipAutoScale: false,\n filter: (node) => {\n if (node instanceof HTMLElement && node.id === 'mtb-widget-host') return false\n return true\n },\n })\n return await bakeScreenshot(dataUrl, screenshotAnnotations, captureMetrics, redactions)\n } catch {\n return null\n }\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.\nfunction translateAnnotationForScreenshot(annotation: Annotation): Annotation {\n const offset = nestedScrollOffset(findAnnotationTarget(annotation))\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 && translateDrawPath(annotation.drawPath, offset),\n }\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\nfunction nestedScrollOffset(target: Element | null): { x: number; y: number } {\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\nfunction 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\nfunction translateDrawPath(drawPath: string, offset: { x: number; y: number }): string {\n return drawPath.replace(/([ML])\\s*(-?(?:\\d+\\.?\\d*|\\.\\d+))\\s+(-?(?:\\d+\\.?\\d*|\\.\\d+))/g, (_match, command: string, x: string, y: string) => {\n return `${command} ${Number(x) + offset.x} ${Number(y) + offset.y}`\n })\n}\n\nasync function bakeScreenshot(\n dataUrl: string,\n annotations: Annotation[],\n captureMetrics: CaptureMetrics,\n redactions: RedactionRect[],\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 scaleX = canvas.width / captureMetrics.width\n const scaleY = canvas.height / captureMetrics.height\n\n ctx.fillStyle = '#e5e7eb'\n for (const rect of redactions) {\n ctx.fillRect(\n (rect.left - captureMetrics.left) * scaleX,\n (rect.top - captureMetrics.top) * scaleY,\n rect.width * scaleX,\n rect.height * scaleY,\n )\n }\n\n let pinIndex = 0\n for (const annotation of annotations) {\n if (annotation.type === 'draw' && annotation.drawPath) {\n drawStroke(ctx, annotation.drawPath, captureMetrics, scaleX, scaleY)\n } else if (annotation.type === 'pin') {\n drawPin(\n ctx,\n (annotation.x - captureMetrics.left) * scaleX,\n (annotation.y - captureMetrics.top) * scaleY,\n ++pinIndex,\n )\n }\n }\n\n if (!annotationsFitCanvas(annotations, captureMetrics, scaleX, scaleY, canvas)) {\n return canvasToJpegBlob(canvas)\n }\n\n const cropRect = computeCropRegion(annotations, window.innerWidth, window.innerHeight)\n if (cropRect) {\n const scaled = fitCropToCanvas(cropRect, captureMetrics, scaleX, scaleY, canvas)\n if (!scaled) return canvasToJpegBlob(canvas)\n return canvasToJpegBlob(cropCanvas(canvas, scaled))\n }\n\n return canvasToJpegBlob(canvas)\n}\n\nfunction annotationsFitCanvas(\n annotations: Annotation[],\n captureMetrics: CaptureMetrics,\n scaleX: number,\n scaleY: number,\n canvas: HTMLCanvasElement,\n): boolean {\n const bounds = getAnnotationBounds(annotations)\n if (!bounds) return true\n\n const left = (bounds.minX - captureMetrics.left) * scaleX\n const top = (bounds.minY - captureMetrics.top) * scaleY\n const right = (bounds.maxX - captureMetrics.left) * scaleX\n const bottom = (bounds.maxY - captureMetrics.top) * scaleY\n\n return left >= 0 && top >= 0 && right <= canvas.width && bottom <= canvas.height\n}\n\nfunction fitCropToCanvas(\n cropRect: CropRect,\n captureMetrics: CaptureMetrics,\n scaleX: number,\n scaleY: number,\n canvas: HTMLCanvasElement,\n): CropRect | null {\n const width = Math.min(canvas.width, Math.round(cropRect.width * scaleX))\n const height = Math.min(canvas.height, Math.round(cropRect.height * scaleY))\n if (width <= 0 || height <= 0) return null\n\n const requestedLeft = Math.round((cropRect.x - captureMetrics.left) * scaleX)\n const requestedTop = Math.round((cropRect.y - captureMetrics.top) * scaleY)\n\n return {\n x: Math.min(Math.max(0, requestedLeft), canvas.width - width),\n y: Math.min(Math.max(0, requestedTop), canvas.height - height),\n width,\n height,\n }\n}\n\nfunction collectPrivacyRedactions(root: HTMLElement): RedactionRect[] {\n const redactions: RedactionRect[] = []\n for (const element of elementsIncludingRoot(root, `.${BLOCK_CLASS}`)) {\n addRedaction(redactions, element.getBoundingClientRect())\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(), 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 const range = document.createRange()\n range.selectNodeContents(node)\n for (const rect of Array.from(range.getClientRects())) {\n addRedaction(redactions, rect, REDACTION_PADDING_PX)\n }\n }\n node = walker.nextNode()\n }\n\n return redactions\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('#mtb-widget-host') !== null || element.closest(`.${BLOCK_CLASS}`) !== null\n}\n\nfunction addRedaction(redactions: RedactionRect[], rect: DOMRect, padding = 0): 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 - padding,\n top: rect.top - 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 captureMetrics: CaptureMetrics,\n scaleX: number,\n scaleY: number,\n): void {\n ctx.save()\n ctx.strokeStyle = BRAND_COLOR\n ctx.lineWidth = 3.5\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n ctx.shadowColor = brandAlpha(0.35)\n ctx.shadowOffsetY = 1\n ctx.shadowBlur = 2\n for (const points of parsePathPolylines(drawPath)) {\n if (points.length === 0) continue\n ctx.beginPath()\n ctx.moveTo(\n (points[0][0] - captureMetrics.left) * scaleX,\n (points[0][1] - captureMetrics.top) * scaleY,\n )\n for (let i = 1; i < points.length; i++) {\n ctx.lineTo(\n (points[i][0] - captureMetrics.left) * scaleX,\n (points[i][1] - captureMetrics.top) * scaleY,\n )\n }\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 — on a long marketing page or an infinite feed that\n// is every iPhone report, and the caller's catch turns it into a silent\n// screenshot-less submission. Shrinking the whole page keeps a usable picture\n// where clipping would drop whatever the reporter was pointing at; bakeScreenshot\n// derives its scale from the returned bitmap, so annotations follow along.\nconst MAX_CAPTURE_PIXELS = 16_000_000\nconst REDACTION_PADDING_PX = 2\n\nfunction capturePixelRatio(metrics: CaptureMetrics): 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 getCaptureMetrics(node: HTMLElement): CaptureMetrics {\n const rect = node.getBoundingClientRect()\n const style = window.getComputedStyle(node)\n const borderWidth = parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth)\n const borderHeight = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth)\n\n return {\n left: rect.left,\n top: rect.top,\n width: node.clientWidth + (Number.isFinite(borderWidth) ? borderWidth : 0) || window.innerWidth,\n height: node.clientHeight + (Number.isFinite(borderHeight) ? borderHeight : 0) || window.innerHeight,\n }\n}\n\nfunction drawPin(ctx: CanvasRenderingContext2D, x: number, y: number, index: number): void {\n ctx.save()\n ctx.shadowColor = brandAlpha(0.45)\n ctx.shadowOffsetY = 2\n ctx.shadowBlur = 8\n ctx.fillStyle = BRAND_COLOR\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.fillStyle = '#ffffff'\n ctx.font = `700 10px ${FONT_STACK}`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n ctx.fillText(String(index), x, y)\n ctx.restore()\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\nfunction 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\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\ntype NavListener = () => 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\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(): void {\n // Copy first: a listener may unsubscribe (or subscribe) while being notified.\n for (const listener of [...listeners]) {\n try { listener() } catch { /* never propagate into the host page */ }\n }\n}\n\nfunction install(): void {\n if (installedPushState) return\n\n popstateHandler = () => notify()\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 pushOrig.call(history, data, unused, url)\n notify()\n }\n installedReplaceState = function (data: unknown, unused: string, url?: string | URL | null) {\n replaceOrig.call(history, data, unused, url)\n notify()\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}\n","import { errorSource, type ErrorSourceEvent } from './error-source'\nimport { summarizeError } from './error-summary'\nimport { getCssSelector, getElementName } from '../annotate/highlighter'\nimport { 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 { 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 = 3\nconst RAGE_CLICK_WINDOW_MS = 1000\nconst RAPID_NAV_THRESHOLD = 3\nconst RAPID_NAV_WINDOW_MS = 5000\nconst FORM_CHECK_DELAY_MS = 500\nconst DEAD_CLICK_DOM_DELAY_MS = 500\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\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 errorCount = 0\n private deadClickCount = 0\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 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 errorPageUrls = new Set<string>()\n private domObserver: MutationObserver | null = null\n private pendingDomChecks = 0\n private lastMutationAt = -1\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((_e) => {\n this.errorCount++\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\n this.stopNavigationTracking()\n this.stopFormTracking()\n this.errorPageUrls.clear()\n\n this.disconnectDomObserver()\n }\n\n private startNavigationTracking(): void {\n this.unsubscribeNavigation = onHistoryNavigation(() => this.handleNavigation())\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 }\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 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 }\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\n const now = Date.now()\n this.clickHistory.push({ target, 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 === target)\n if (sameTargetClicks.length >= RAGE_CLICK_THRESHOLD) {\n this.clickHistory = []\n this.emitHighPriority({\n signal: 'rage_click',\n target: describeElement(target),\n timestamp: now,\n })\n return\n }\n\n if (!this.isInteractive(target)) {\n this.deadClickCount++\n if (this.deadClickCount >= 3 && this.errorCount > 0) {\n this.deadClickCount = 0\n this.errorCount = 0\n this.emitIfReady({\n signal: 'dead_click',\n target: describeElement(target),\n timestamp: now,\n })\n }\n }\n\n this.checkDeadClickDom(target, now)\n }\n\n private handleNavigation(): void {\n const now = Date.now()\n this.noteNavigationStarted()\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 this.persistState()\n setTimeout(() => {\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 setTimeout(() => {\n if (!this.active) return\n if (form.querySelector('[aria-invalid=\"true\"], .is-invalid')) {\n this.emitIfReady({\n signal: 'form_failure',\n target: describeElement(form),\n timestamp: Date.now(),\n })\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 rememberSubmitAttempt(form: HTMLFormElement): void {\n this.recentSubmitForms.add(form)\n setTimeout(() => {\n this.recentSubmitForms.delete(form)\n }, FORM_CHECK_DELAY_MS)\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.emitIfReady({\n signal: 'form_failure',\n target: describeElement(form),\n timestamp: Date.now(),\n })\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 setTimeout(() => {\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.body, {\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 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 isInteractive(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 if ((el as HTMLElement).contentEditable === 'true') return true\n\n const style = window.getComputedStyle(el)\n if (style.cursor === 'pointer') return true\n\n const parent = el.closest('a, button, [role=\"button\"], [role=\"link\"], label')\n if (parent) return true\n\n return false\n }\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 { 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 function buildPayload(\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): FeedbackPayload {\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 reporter_external_id: user ? undefined : anonId,\n reporter_email: user ? undefined : anonEmail,\n }\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 cancel: 'Cancel',\n my_feedback: 'My feedback',\n quickOptions: [\n { emoji: '🐛', label: \"Something's broken\" },\n { emoji: '💡', label: 'I have a suggestion' },\n { emoji: '🤔', label: \"I'm confused\" },\n ],\n },\n success: {\n title: 'Sent — thanks!',\n message: 'Your note and captured technical context are on their way to the team. You’ll hear back when it’s resolved.',\n title_no_ai: \"Thanks! We've received your feedback.\",\n message_no_ai: \"The team will take it from here. You'll hear back when it's resolved.\",\n view_feedback: 'View my feedback',\n close: 'Close',\n email_prompt: \"Want updates? Leave your email and we'll let you know when it's resolved.\",\n email_placeholder: 'you@example.com',\n email_submit: 'Notify me',\n email_saved: \"You'll get an email when it's resolved.\",\n email_error: \"Couldn't save your email. Please try again.\",\n },\n error: {\n submit: 'Failed to submit. 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: 'Type your answer — or skip the question',\n send: 'Send',\n send_feedback: 'Send feedback',\n skip: 'Skip & send',\n retry: 'Retry',\n error: 'AI clarification failed. Please try again.',\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 cancel: '取消',\n my_feedback: '我的反馈',\n quickOptions: [\n { emoji: '🐛', label: '出问题了' },\n { emoji: '💡', label: '我有一个建议' },\n { emoji: '🤔', label: '我有点困惑' },\n ],\n },\n success: {\n title: '已发送,感谢!',\n message: '您的反馈和采集到的技术上下文正在发送给团队。问题解决后,您会收到通知。',\n title_no_ai: '感谢!我们已收到您的反馈。',\n message_no_ai: '接下来由团队处理。问题解决后,您会收到通知。',\n view_feedback: '查看我的反馈',\n close: '关闭',\n email_prompt: '想了解进展?留下邮箱,问题解决后我们会通知您。',\n email_placeholder: 'you@example.com',\n email_submit: '通知我',\n email_saved: '问题解决后您会收到邮件通知。',\n email_error: '邮箱保存失败,请重试。',\n },\n error: {\n submit: '提交失败,请重试。',\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 send_feedback: '发送反馈',\n skip: '跳过并发送',\n retry: '重试',\n error: 'AI 澄清失败,请重试。',\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 cancel: 'キャンセル',\n my_feedback: '自分のフィードバック',\n quickOptions: [\n { emoji: '🐛', label: '不具合があります' },\n { emoji: '💡', label: '提案があります' },\n { emoji: '🤔', label: 'わかりにくいです' },\n ],\n },\n success: {\n title: '送信しました。ありがとうございます!',\n message: 'ご意見と取得した技術情報をチームに送信しました。解決次第お知らせします。',\n title_no_ai: 'ありがとうございます!フィードバックを受け付けました。',\n message_no_ai: 'チームが対応します。解決次第お知らせします。',\n view_feedback: 'フィードバックを確認',\n close: '閉じる',\n email_prompt: '進捗を知りたい場合は、メールアドレスをご記入ください。解決次第お知らせします。',\n email_placeholder: 'you@example.com',\n email_submit: '通知を受け取る',\n email_saved: '解決次第メールでお知らせします。',\n email_error: 'メールアドレスを保存できませんでした。もう一度お試しください。',\n },\n error: {\n submit: '送信に失敗しました。もう一度お試しください。',\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 send_feedback: 'フィードバックを送信',\n skip: 'スキップして送信',\n retry: '再試行',\n error: 'AIによる確認に失敗しました。もう一度お試しください。',\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 cancel: '취소',\n my_feedback: '내 피드백',\n quickOptions: [\n { emoji: '🐛', label: '문제가 있어요' },\n { emoji: '💡', label: '제안이 있어요' },\n { emoji: '🤔', label: '헷갈려요' },\n ],\n },\n success: {\n title: '전송되었습니다. 감사합니다!',\n message: '피드백과 수집된 기술 정보가 팀에 전달되었습니다. 해결되면 알려드리겠습니다.',\n title_no_ai: '감사합니다! 피드백을 접수했습니다.',\n message_no_ai: '팀에서 처리합니다. 해결되면 알려드리겠습니다.',\n view_feedback: '내 피드백 보기',\n close: '닫기',\n email_prompt: '진행 상황이 궁금하신가요? 이메일을 남겨주시면 해결 시 알려드립니다.',\n email_placeholder: 'you@example.com',\n email_submit: '알림 받기',\n email_saved: '해결되면 이메일로 알려드립니다.',\n email_error: '이메일을 저장하지 못했습니다. 다시 시도해 주세요.',\n },\n error: {\n submit: '제출에 실패했습니다. 다시 시도해 주세요.',\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 send_feedback: '피드백 보내기',\n skip: '건너뛰고 보내기',\n retry: '재시도',\n error: 'AI 확인에 실패했습니다. 다시 시도해 주세요.',\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 cancel: 'Cancelar',\n my_feedback: 'Mis comentarios',\n quickOptions: [\n { emoji: '🐛', label: 'Algo no funciona' },\n { emoji: '💡', label: 'Tengo una sugerencia' },\n { emoji: '🤔', label: 'Estoy confundido' },\n ],\n },\n success: {\n title: 'Enviado, ¡gracias!',\n message: 'Tu nota y el contexto técnico capturado se han enviado al equipo. Te avisaremos cuando se resuelva.',\n title_no_ai: '¡Gracias! Hemos recibido tu comentario.',\n message_no_ai: 'El equipo se encargará a partir de aquí. Te avisaremos cuando se resuelva.',\n view_feedback: 'Ver mis comentarios',\n close: 'Cerrar',\n email_prompt: '¿Quieres novedades? Deja tu correo y te avisaremos cuando se resuelva.',\n email_placeholder: 'tu@ejemplo.com',\n email_submit: 'Avisarme',\n email_saved: 'Recibirás un correo cuando se resuelva.',\n email_error: 'No se pudo guardar tu correo. Inténtalo de nuevo.',\n },\n error: {\n submit: 'Error al enviar. Por favor, 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: 'Escribe tu respuesta — u omite la pregunta',\n send: 'Enviar',\n send_feedback: 'Enviar comentario',\n skip: 'Omitir y enviar',\n retry: 'Reintentar',\n error: 'No se pudo completar la aclaración con IA. Inténtalo de nuevo.',\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 cancel: 'Annuler',\n my_feedback: 'Mes avis',\n quickOptions: [\n { emoji: '🐛', label: 'Quelque chose ne fonctionne pas' },\n { emoji: '💡', label: \"J'ai une suggestion\" },\n { emoji: '🤔', label: 'Je suis perplexe' },\n ],\n },\n success: {\n title: 'Envoyé, merci !',\n message: 'Votre message et le contexte technique collecté ont été transmis à l’équipe. Vous serez informé(e) dès que ce sera résolu.',\n title_no_ai: 'Merci ! Nous avons bien reçu votre avis.',\n message_no_ai: \"L’équipe prend le relais. Vous serez informé(e) dès que ce sera résolu.\",\n view_feedback: 'Voir mes avis',\n close: 'Fermer',\n email_prompt: \"Envie de suivre l'avancement ? Laissez votre e-mail et nous vous préviendrons une fois résolu.\",\n email_placeholder: 'vous@exemple.com',\n email_submit: 'Me prévenir',\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 },\n error: {\n submit: \"Échec de l'envoi. 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: 'Saisissez votre réponse — ou ignorez la question',\n send: 'Envoyer',\n send_feedback: 'Envoyer le retour',\n skip: 'Ignorer et envoyer',\n retry: 'Réessayer',\n error: 'La clarification par IA a échoué. Réessayez.',\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 cancel: 'Abbrechen',\n my_feedback: 'Mein Feedback',\n quickOptions: [\n { emoji: '🐛', label: 'Etwas funktioniert nicht' },\n { emoji: '💡', label: 'Ich habe einen Vorschlag' },\n { emoji: '🤔', label: 'Ich bin verwirrt' },\n ],\n },\n success: {\n title: 'Gesendet, vielen Dank!',\n message: 'Ihre Nachricht und der erfasste technische Kontext wurden an das Team gesendet. Sie werden benachrichtigt, sobald das Problem gelöst ist.',\n title_no_ai: 'Vielen Dank! Wir haben Ihr Feedback erhalten.',\n message_no_ai: 'Das Team übernimmt ab hier. Sie werden benachrichtigt, sobald das Problem gelöst ist.',\n view_feedback: 'Mein Feedback ansehen',\n close: 'Schließen',\n email_prompt: 'Updates gewünscht? Hinterlassen Sie Ihre E-Mail und wir melden uns, sobald es gelöst ist.',\n email_placeholder: 'sie@beispiel.de',\n email_submit: 'Benachrichtigen',\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 },\n error: {\n submit: 'Senden fehlgeschlagen. 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: 'Antwort eingeben — oder Frage überspringen',\n send: 'Senden',\n send_feedback: 'Feedback senden',\n skip: 'Überspringen & senden',\n retry: 'Erneut versuchen',\n error: 'Die KI-Klärung ist fehlgeschlagen. Bitte erneut versuchen.',\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 type { I18nMessages } from '../i18n'\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\">${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 { ShadowContainer } from './shadow'\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 { AnnotationToolbar } from '../annotate/toolbar'\nimport type { ToolbarMode } from '../annotate/toolbar'\nimport { AnnotationSession } from '../annotate/session'\nimport { DrawNoteBar } from '../annotate/draw-note-bar'\nimport { CommentPopup } from '../popup/comment'\nimport { ClarifyCard } from '../popup/clarify'\nimport { SuccessCard } from '../popup/success'\nimport type { SuccessCardOptions } from '../popup/success'\nimport { captureScreenshot } 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 { collectPageContext } from '../context/collector'\nimport type { PageContext } from '../context/collector'\nimport { ApiClient } from '../api/client'\nimport { buildPayload } from './payload'\nimport { cacheBoardUrl, getAnonId, getCachedBoardUrl, getStoredReporterEmail, rememberReporterEmail } from './anon-id'\nimport { getMessages } from '../i18n'\nimport { RecordingManager } from './recording-manager'\nimport type { RecordingResult } from './recording-manager'\nimport type { MakeThisBetterConfig, Annotation, SubmissionSessionResponse, TargetRect } from '../types'\n\ntype Mode = 'idle' | 'annotating' | 'recording' | 'drawing' | 'popup' | 'clarifying'\ntype Via = 'pin' | 'draw' | 'record'\n\n// Which surface collected the note. The clarify card shows progress and errors\n// from here on, so this is only ever asked whether a submission is in hand — it\n// carries no progress contract of its own.\ntype SubmittingUi = CommentPopup | DrawNoteBar\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 {\n private config: MakeThisBetterConfig\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 popup: CommentPopup | null = null\n private clarify: ClarifyCard | null = null\n private success: SuccessCard | 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 private mode: Mode = 'idle'\n private via: Via = 'pin'\n private currentAnnotation: Annotation | null = null\n private submittingUi: SubmittingUi | null = null\n private lastPopupPos: { x: number; y: number } | null = null\n private recordingManager: RecordingManager | null = null\n private activeSubmission: SubmissionSessionResponse | null = null\n private pendingSubmissionRequest: Promise<SubmissionSessionResponse | null> | null = null\n private submissionAttempt = 0\n private touchHintShown = false\n private lastHandoff: { feedbackId?: string; boardUrl?: string; identityToken?: string } = {}\n\n constructor(config: MakeThisBetterConfig) {\n this.config = config\n this.messages = getMessages(config.locale ?? document.documentElement.lang ?? 'en')\n this.apiClient = new ApiClient(config.projectKey, config.apiUrl, undefined, config.userToken, config.userTokenFn)\n this.consoleCollector = new ConsoleErrorCollector()\n this.consoleCollector.start()\n this.breadcrumbCollector = new BreadcrumbCollector()\n this.breadcrumbCollector.start()\n this.buildUI()\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 // Escape only acts while the widget is in an active state; idle/clarifying\n // fall through untouched so the host page's own Escape handling still works.\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 // Some browsers reset isComposing before the keydown reaches this capture\n // handler but retain the legacy IME sentinel in keyCode.\n if (event.isComposing || event.keyCode === 229) return\n\n switch (this.mode) {\n case 'annotating':\n case 'popup':\n event.preventDefault()\n event.stopPropagation()\n this.exitAll()\n break\n case 'drawing':\n event.preventDefault()\n event.stopPropagation()\n this.cancelDraw()\n break\n case 'recording':\n event.preventDefault()\n event.stopPropagation()\n this.handleRecordStop()\n break\n default:\n break\n }\n }\n\n private buildUI(): void {\n this.shadow = new ShadowContainer(this.config.theme ?? 'auto')\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 if (this.tab) return\n this.tab = new FeedbackTab(\n this.shadow,\n this.messages,\n this.config.position ?? 'right',\n () => this.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.mode !== 'idle') return\n this.dismissFrustrationPrompt()\n this.enterAnnotationMode()\n }\n\n close(): void {\n if (this.mode === 'idle') return\n this.exitAll()\n }\n\n showLauncher(): void {\n if (isTouchPointer()) return\n this.mountTab()\n this.tab?.setActive(this.mode !== 'idle')\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 handleTabClick(): void {\n if (this.mode === 'idle') {\n this.dismissFrustrationPrompt()\n this.enterAnnotationMode()\n } else {\n this.exitAll()\n }\n }\n\n private handleFrustration(): void {\n if (this.mode !== 'idle' || this.frustrationPrompt || isFrustrationDismissed()) return\n\n this.frustrationPrompt = new FrustrationPromptCard(this.shadow, this.messages, {\n onTell: () => {\n this.dismissFrustrationPrompt()\n this.enterAnnotationMode()\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 private dismissFrustrationPrompt(): void {\n this.frustrationPrompt?.destroy()\n this.frustrationPrompt = null\n }\n\n private enterAnnotationMode(): void {\n this.mode = 'annotating'\n this.tab?.setActive(true)\n const touch = isTouchPointer()\n\n this.toolbar = new AnnotationToolbar(\n this.shadow,\n this.messages,\n () => this.exitAll(),\n (mode: ToolbarMode) => this.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.session = this.createSession()\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 private createSession(): AnnotationSession {\n return new AnnotationSession(this.shadow, this.messages, {\n onPin: (annotation, targetName) => {\n this.currentAnnotation = annotation\n this.via = 'pin'\n this.showPopup(annotation.x, annotation.y, targetName, annotation.targetRect)\n },\n onDrawStroke: () => this.handleDrawStroke(),\n })\n }\n\n private handleToolbarModeChange(mode: ToolbarMode): void {\n if (this.mode === 'clarifying') {\n this.toolbar?.setMode(this.via === 'record' ? 'record' : 'markup')\n return\n }\n\n if (mode === 'record') {\n if (this.mode === 'recording') return\n\n this.mode = 'recording'\n this.currentAnnotation = null\n this.teardownDraw()\n this.session?.destroy()\n this.session = null\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.recordingManager = new RecordingManager()\n this.recordingManager.start(\n this.shadow,\n this.messages,\n this.handleRecordStop,\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 if (this.mode !== 'recording') return\n this.toolbar?.setMode('markup')\n this.handleToolbarModeChange('markup')\n })\n } else {\n this.recordingManager?.destroy()\n this.recordingManager = null\n this.teardownDraw()\n if (!this.session) this.session = this.createSession()\n this.mode = 'annotating'\n }\n }\n\n private handleRecordStop = (): void => {\n if (!this.recordingManager) return\n this.recordingManager.stop()\n this.via = 'record'\n this.showPopup(window.innerWidth / 2, window.innerHeight / 2, this.messages.record.timer_label)\n }\n\n private 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 if (isTouchPointer()) {\n this.showDrawSheet()\n return\n }\n\n if (this.mode !== 'drawing') {\n this.mode = 'drawing'\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 })\n }\n this.refreshDrawBar()\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 private showDrawSheet(): void {\n if (this.mode === 'drawing') return\n this.mode = 'drawing'\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\n this.popup = new CommentPopup(this.shadow, {\n targetName: this.messages.annotation.drawing,\n x: window.innerWidth / 2,\n y: window.innerHeight / 2,\n messages: this.messages,\n onSubmit: (description) => this.handleDrawSubmit(description),\n onClose: () => this.cancelDraw(),\n position: this.config.position ?? 'right',\n })\n this.submittingUi = this.popup\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 private refreshDrawBar(): void {\n if (!this.session) return\n this.drawBar?.setUndoRedo(this.session.canUndoDraw(), this.session.canRedoDraw())\n }\n\n private cancelDraw(): void {\n this.session?.clearDraw()\n this.teardownDraw()\n // On touch the sheet is the note surface, so cancelling the drawing has to\n // take the sheet and its scrim with it — teardownDraw only knows the bar.\n this.popup?.destroy()\n this.popup = null\n this.dim?.destroy()\n this.dim = null\n this.submittingUi = null\n this.mode = 'annotating'\n }\n\n private teardownDraw(): 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 // Whichever surface collected the note is the one that shows progress and\n // errors: the inline bar on a desktop, the sheet on touch.\n this.submittingUi = this.drawBar ?? this.popup\n void this.handleSubmit(note || this.messages.annotation.drawing)\n }\n\n private showPopup(x: number, y: number, targetName: string, targetRect?: TargetRect): void {\n this.mode = 'popup'\n this.lastPopupPos = { x, y }\n this.teardownDraw()\n this.session?.dismissInteraction()\n this.popup?.destroy()\n this.popup = null\n this.dim?.destroy()\n this.dim = null\n this.dim = new DimOverlay(this.shadow, () => this.exitAll())\n\n this.popup = new CommentPopup(this.shadow, {\n targetName,\n x,\n y,\n targetRect,\n messages: this.messages,\n onSubmit: (description) => this.handleSubmit(description),\n onClose: () => this.exitAll(),\n onMyFeedback: this.buildMyFeedbackHandler(),\n position: this.config.position ?? 'right',\n })\n this.submittingUi = this.popup\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 handleSubmit(description: string): void {\n if (!this.submittingUi) return\n // Captured before teardown: teardownSubmittingUi destroys the recording\n // manager and the annotation session, and the screenshot bakes the\n // annotation from this data rather than from the live DOM.\n const annotation = this.currentAnnotation\n const recording = this.recordingManager?.getRecording() ?? undefined\n\n // The conversation opens the moment the reporter submits. The popup hands\n // its element to the clarify card, which shows its thinking bubble while\n // the screenshot capture and the session upload run behind it.\n const element = this.teardownSubmittingUi(true)\n const run = () => this.startSubmissionRequest(description, annotation, recording)\n this.showClarify(this.lastPopupPos, { element, pendingSession: run(), onRetrySubmission: run })\n }\n\n private startSubmissionRequest(\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 // Let the clarify card paint its thinking state before the screenshot\n // capture blocks the main thread.\n await waitForPaint()\n const [screenshot, pageContext] = await Promise.all([\n captureScreenshot(annotation ? [annotation] : []),\n Promise.resolve(collectPageContext()),\n ])\n\n const payload = buildPayload(\n description,\n pageContext,\n this.consoleCollector.getErrors(),\n annotation,\n this.config.user,\n recording,\n this.config.user ? undefined : getAnonId(),\n this.config.user ? undefined : getStoredReporterEmail(),\n this.breadcrumbCollector.getBreadcrumbs(),\n )\n\n try {\n const submission = await this.apiClient.createSubmissionSession(payload, screenshot)\n if (submissionAttempt !== this.submissionAttempt) {\n await this.apiClient.abandonSubmissionSession(submission.id, submission.token).catch(() => {})\n return null\n }\n this.activeSubmission = submission\n return submission\n } catch {\n return null\n }\n }\n\n private teardownSubmittingUi(preservePopup = false): HTMLDivElement | undefined {\n const popupElement = preservePopup ? this.popup?.releaseElement() : undefined\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.teardownDraw()\n if (!popupElement) this.popup?.destroy()\n this.popup = null\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 this.submittingUi = null\n return popupElement\n }\n\n private showClarify(\n pos: { x: number; y: number } | null,\n opts: {\n element?: HTMLDivElement\n pendingSession: Promise<SubmissionSessionResponse | null>\n onRetrySubmission: () => Promise<SubmissionSessionResponse | null>\n },\n ): void {\n this.mode = 'clarifying'\n this.tab?.setActive(true)\n this.clarify = new ClarifyCard(this.shadow, {\n apiClient: this.apiClient,\n messages: this.messages,\n element: opts.element,\n position: this.config.position ?? 'right',\n x: pos?.x,\n y: pos?.y,\n onFinalize: () => this.finalizeSubmission(),\n onCancel: () => this.exitAll(),\n pendingSession: opts.pendingSession,\n onRetrySubmission: opts.onRetrySubmission,\n })\n }\n\n private async finalizeSubmission(): Promise<void> {\n const submissionAttempt = this.submissionAttempt\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) throw new Error('No active submission session')\n\n const feedback = await this.apiClient.finalizeSubmissionSession(submission.id, submission.token)\n if (submissionAttempt !== this.submissionAttempt || this.activeSubmission !== submission) return\n\n this.activeSubmission = null\n this.pendingSubmissionRequest = null\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 this.clarify?.destroy()\n this.clarify = null\n this.teardownToolbar()\n this.showSuccess({ skipFollowup: submission.ai_clarify_available === false })\n }\n\n private showSuccess(options?: SuccessCardOptions): void {\n this.mode = 'idle'\n this.tab?.setActive(false)\n this.success = new SuccessCard(this.shadow, this.messages, () => {\n this.success?.destroy()\n this.success = null\n }, this.buildViewFeedbackHandler(), { ...options, emailCapture: this.buildEmailCapture() })\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 const token = await this.apiClient.resolveUserToken()\n if (token) window.open(`${boardUrl}?identity=${encodeURIComponent(token)}`, '_blank')\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 private exitAll(): void {\n this.submissionAttempt += 1\n const submission = this.activeSubmission\n this.activeSubmission = null\n this.pendingSubmissionRequest = null\n if (submission) {\n void this.apiClient.abandonSubmissionSession(submission.id, submission.token).catch(() => {})\n }\n this.mode = 'idle'\n this.via = 'pin'\n this.tab?.setActive(false)\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.teardownDraw()\n this.session?.hideHighlight()\n this.session?.destroy()\n this.session = null\n this.dim?.destroy()\n this.dim = null\n this.popup?.destroy()\n this.popup = null\n this.clarify?.destroy()\n this.clarify = null\n this.currentAnnotation = null\n this.submittingUi = 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.clarify?.destroy()\n this.success?.destroy()\n this.tab?.destroy()\n this.recordingManager?.destroy()\n this.shadow.destroy()\n }\n}\n\nfunction waitForPaint(): Promise<void> {\n return new Promise<void>((r) => requestAnimationFrame(() => setTimeout(r, 0)))\n}\n","import { WidgetController } from './widget/controller'\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\nconst MakeThisBetter = {\n init(config: MakeThisBetterConfig): void {\n cancelPendingMount()\n if (instance) {\n instance.destroy()\n instance = null\n }\n lastConfig = config\n if (document.readyState === 'loading') {\n pendingMount = () => {\n pendingMount = null\n instance = new WidgetController(config)\n }\n document.addEventListener('DOMContentLoaded', pendingMount, { once: true })\n } else {\n instance = new WidgetController(config)\n }\n },\n\n destroy(): void {\n cancelPendingMount()\n instance?.destroy()\n instance = null\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('mtb-widget-host')\n if (lastConfig && (!host || !host.shadowRoot)) {\n instance?.destroy()\n host?.remove()\n instance = new WidgetController(lastConfig)\n }\n })\n}\n"],"names":["ShadowContainer","theme","provided","style","cssText","tag","className","el","nodes","n","FeedbackTab","shadow","messages","position","onClick","customText","label","active","FRUSTRATION_DISMISS_MS","FrustrationPromptCard","handlers","SPAWN_CLICK_GRACE_MS","DimOverlay","onDismiss","now","isTouchPointer","PageOffset","px","current","MOBILE_BAR_HEIGHT_PX","AnnotationToolbar","onExit","onModeChange","showTouchHint","e","rect","inside","m","mode","AnnotationOverlay","pe","pointerId","ElementHighlighter","getElementName","below","UNRENDERED","renderedText","limit","out","node","title","text","placeholder","alt","dataMark","getCssSelector","parts","selector","classNames","c","parent","siblings","idx","PinMarker","x","y","pin","BRAND_COLOR","BRAND_RGB","brandAlpha","alpha","FONT_STACK","DRAW_STROKE","DrawLayer","buildPathD","start","max","stroke","entry","path","points","s","cx","cy","center","fx","fy","d","i","xs","p","ys","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","value","filtered","pattern","candidate","passesLuhn","sanitizeUrl","url","filterSensitiveValue","key","isSensitiveKey","item","sensitiveField","isSensitiveFieldAttributes","nestedKey","filteredItem","normalizeIdentifier","attributes","normalizedKey","token","digits","sum","doubleDigit","index","digit","isSensitiveField","element","filterInputValue","MAX_GROUPED_TEXT_LENGTH","createSensitiveTextContext","hasSensitiveTextContext","context","state","sensitiveTextState","cached","child","childState","MAX_DURATION_S","VALUE_ATTRIBUTE_NAMES","MASKED_INPUT_TYPES","type","BLOCK_CLASS","MASK_TEXT_CLASS","RRWEB_CDN","RRWEB_SRI","rrwebLoaded","loadRrweb","loadViaImport","loadViaCdn","filterReplayEvent","event","record","filterReplayDomValues","textContext","isRecord","hasAttributeValue","name","attributeValue","findGlobalRecord","win","resolve","reject","existing","script","RecordSession","onMaxReached","ripple","PRIVACY_SELECTOR","isPrivacyProtected","structuralElementName","role","structuralElementSelector","DRAW_MIN_TRAVEL_PX","annotationTarget","AnnotationSession","underEl","getElementUnder","target","targetName","annotation","WIDGET_HOST_ID","isWidgetElement","host","escapeHtml","SETTLE_MS","KeyboardInset","reading","vp","inset","onChange","SHEET_MAX_WIDTH","isSheetViewport","SheetLayout","headroom","applied","DrawNoteBar","opts","canUndo","canRedo","guardReplayClick","sawPointerDown","onPointerDown","CommentPopup","quickOpts","o","targetRect","tabPosition","vw","vh","margin","gap","tabWidth","popupW","popupH","minLeft","maxRight","left","top","anchorIsInformative","btn","option","hasText","description","ApiError","status","isTerminalConflict","error","DEFAULT_API_URL","CAPTURE_POLICY","ApiClient","projectKey","apiUrl","retryDelayMs","userToken","userTokenFn","contentType","headers","payload","screenshot","submissionToken","generateSubmissionToken","form","json","blob","ext","generateIdempotencyKey","reporterExternalId","feedbackId","email","identityToken","submissionSessionId","message","body","onDelta","signal","res","consumeSse","init","retries","err","delay","ms","reader","decoder","buffer","result","flush","raw","eventLine","l","dataLine","done","sep","bytes","byte","POLL_INTERVAL_MS","POLL_TIMEOUT_MS","CLARIFY_SILENCE_TIMEOUT_MS","CARD_WIDTH","CARD_HEIGHT","ClarifyCard","existingElement","cardW","cardH","centeredLeft","pending","session","footer","abort","silenceTimer","autoFinalize","snapshot","button","last","msg","bubble","dots","SUCCESS_AUTO_CLOSE_MS","EMAIL_PATTERN","SuccessCard","onClose","onViewFeedback","options","viewLink","emailCapture","onSubmit","input","submit","handle","resolveUrl","baseUrl","doc","base","a","uuid","counter","random","toArray","arrayLike","arr","styleProps","getStyleProperties","styleProperty","val","getNodeWidth","leftBorder","rightBorder","getNodeHeight","topBorder","bottomBorder","getImageSize","targetNode","width","height","getPixelRatio","ratio","FINAL_PROCESS","canvasDimensionLimit","checkCanvasDimensions","canvas","createImage","img","svgToDataURL","svg","html","nodeToDataURL","xmlns","foreignObject","isInstanceOfElement","instance","nodePrototype","formatCSSText","content","formatCSSProperties","priority","getPseudoElementStyle","pseudo","clonePseudoElement","nativeNode","clonedNode","styleElement","clonePseudoElements","WOFF","JPEG","mimes","getExtension","match","getMimeType","extension","getContentFromDataUrl","dataURL","isDataUrl","makeDataUrl","mimeType","fetchAsDataURL","process","cache","getCacheKey","includeQueryParams","resourceToDataURL","resourceUrl","cacheKey","cloneCanvasElement","cloneVideoElement","video","ctx","poster","cloneIFrameElement","iframe","_a","cloneNode","cloneSingleNode","isSVGElement","isSlotElement","cloneChildren","_b","children","deferred","clonedChild","cloneCSSStyle","targetStyle","sourceStyle","cloneInputValue","cloneSelectValue","clonedSelect","selectedOption","decorate","ensureSVGSymbols","clone","uses","processedDefs","id","exist","definition","ns","defs","isRoot","URL_REGEX","URL_WITH_FORMAT_REGEX","FONT_SRC_REGEX","toRegex","escaped","parseURLs","urls","quotation","embed","resourceURL","baseURL","getContentFromUrl","resolvedURL","filterPreferredFontFormat","str","preferredFontFormat","src","format","shouldEmbed","embedResources","filteredCSSText","css","embedProp","propName","propValue","cssString","embedBackground","embedImageNode","isImageElement","image","embedChildren","deferreds","embedImages","applyStyle","manual","cssFetchCache","fetchCSS","embedFonts","data","regexUrl","loadFonts","loc","parseCSS","source","commentsRegex","keyframesRegex","matches","importRegex","combinedCSSRegex","unifiedRegex","getCSSRules","styleSheets","ret","sheet","importIndex","metadata","rule","inline","getWebFontRules","cssRules","parseWebFontRules","normalizeFontFamily","font","getUsedFonts","fonts","traverse","getWebFontCSS","rules","usedFonts","embedWebFonts","styleNode","sytleContent","toSvg","toCanvas","canvasWidth","canvasHeight","toJpeg","MIN_CROP_WIDTH","MIN_CROP_HEIGHT","PADDING_PX","PADDING_RATIO","SKIP_CROP_RATIO","getAnnotationBounds","annotations","minX","minY","maxX","maxY","ann","r","polyline","parsePathPolylines","computeCropRegion","viewportWidth","viewportHeight","bounds","padX","padY","w","h","cropCanvas","cropped","captureScreenshot","captureMetrics","getCaptureMetrics","screenshotAnnotations","translateAnnotationForScreenshot","redactions","collectPrivacyRedactions","dataUrl","capturePixelRatio","bakeScreenshot","offset","nestedScrollOffset","findAnnotationTarget","translateDrawPath","parentElement","root","drawPath","_match","command","loadImage","scaleX","scaleY","pinIndex","drawStroke","drawPin","annotationsFitCanvas","canvasToJpegBlob","cropRect","scaled","fitCropToCanvas","right","bottom","requestedLeft","requestedTop","elementsIncludingRoot","addRedaction","isWidgetOrBlocked","isMasked","hasSensitiveValue","REDACTION_PADDING_PX","walker","hasSensitiveText","range","descendants","padding","MAX_CAPTURE_PIXELS","metrics","total","borderWidth","borderHeight","polylines","tokens","ErrorSource","listener","line","col","errorName","reason","errorSource","summarizeError","validErrorName","inferErrorName","location","errorLocation","pathname","MAX_CONSOLE_ERRORS","ConsoleErrorCollector","listeners","nativePushState","nativeReplaceState","installedPushState","installedReplaceState","popstateHandler","onHistoryNavigation","install","released","uninstall","notify","pushOrig","replaceOrig","unused","MAX_BREADCRUMBS","describeTarget","describeSelector","BreadcrumbCollector","crumb","field","summary","from","to","sanitizedFrom","sanitizedTo","STORAGE_KEY","memoryState","loadFrustrationState","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","DEAD_CLICK_DOM_DELAY_MS","ERROR_PAGE_CHECK_DELAY_MS","INTERACTIVE_TAGS","INTERACTIVE_ROLES","ERROR_PAGE_PATTERNS","_FrustrationDetector","onFrustration","_e","stored","cutoff","describeElement","control","candidates","clickTime","mutated","navigated","cls","lower","FrustrationDetector","collectPageContext","detectBrowser","detectOS","ua","buildPayload","pageContext","consoleErrors","user","recording","anonId","anonEmail","breadcrumbs","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","RecordControlBar","getDuration","onStop","secs","mm","ss","RecordingManager","isLocalHost","hostname","WidgetController","config","touch","note","action","exchange","run","submissionAttempt","request","waitForPaint","submission","preservePopup","popupElement","pos","feedback","saved","boardUrl","lastConfig","pendingMount","cancelPendingMount","MakeThisBetter"],"mappings":"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","x_google_ignoreList":[27,28,29,30,31,32,33,34,35,36]}
|
|
1
|
+
{"version":3,"file":"makethisbetter.cjs","sources":["../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/annotate/toolbar.ts","../src/annotate/overlay.ts","../src/annotate/highlighter.ts","../src/annotate/pin.ts","../src/styles.ts","../src/annotate/draw.ts","../src/privacy/sanitize.ts","../src/privacy/fields.ts","../src/privacy/text-context.ts","../src/record/session.ts","../src/privacy/dom.ts","../src/annotate/session.ts","../src/context/dom-utils.ts","../src/popup/keyboard-inset.ts","../src/popup/sheet.ts","../src/annotate/draw-note-bar.ts","../src/popup/replay-click-guard.ts","../src/popup/comment.ts","../src/api/client.ts","../src/popup/clarify.ts","../src/popup/success.ts","../node_modules/html-to-image/es/util.js","../node_modules/html-to-image/es/clone-pseudos.js","../node_modules/html-to-image/es/mimes.js","../node_modules/html-to-image/es/dataurl.js","../node_modules/html-to-image/es/clone-node.js","../node_modules/html-to-image/es/embed-resources.js","../node_modules/html-to-image/es/embed-images.js","../node_modules/html-to-image/es/apply-style.js","../node_modules/html-to-image/es/embed-webfonts.js","../node_modules/html-to-image/es/index.js","../src/screenshot/crop.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/context/collector.ts","../src/widget/payload.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/control-bar.ts","../src/widget/recording-manager.ts","../src/widget/controller.ts","../src/index.ts"],"sourcesContent":["import cssText from '../styles/widget.css?inline'\nimport type { MakeThisBetterConfig } from '../types'\n\nexport class ShadowContainer {\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\n constructor(theme: MakeThisBetterConfig['theme'] = 'auto') {\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('mtb-widget-host')\n this.ownsHost = !provided\n\n this.host = provided ?? document.createElement('div')\n this.host.id = 'mtb-widget-host'\n this.host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;overflow:visible;z-index:2147483646;'\n this.host.setAttribute('data-mtb-theme', theme)\n if (this.ownsHost) document.body.appendChild(this.host)\n\n this.root = this.host.shadowRoot ?? this.host.attachShadow({ mode: 'open' })\n this.root.replaceChildren()\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 destroy(): void {\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 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('click', onClick)\n shadow.append(this.el)\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('click', this.onClick)\n this.el.remove()\n }\n}\n","import type { ShadowContainer } from './shadow'\nimport type { I18nMessages } from '../i18n'\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\">⚠️</div>\n <span class=\"mtb-frustration-text\">${messages.frustration.prompt}</span>\n <div class=\"mtb-frustration-actions\">\n <button class=\"mtb-frustration-tell\">${messages.frustration.action}</button>\n <button class=\"mtb-frustration-dismiss\">${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","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { isTouchPointer } from '../widget/pointer-kind'\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 private dismissTouchHint = (): void => {\n this.hintBarEl.classList.add('mtb-hint-bar--dismissed')\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\">${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 }\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=\"${m.toolbar.markup}\" title=\"${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\">${m.toolbar.markup}</span>\n </button>\n <button class=\"mtb-toolbar-mode-btn mtb-toolbar-mode-record\" aria-label=\"${m.toolbar.record}\" title=\"${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\">${m.toolbar.record}</span>\n </button>\n </div>\n <span class=\"mtb-toolbar-sep\"></span>\n <button class=\"mtb-exit-btn\" aria-label=\"${m.toolbar.exit}\" title=\"${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\">${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 document.removeEventListener('pointerdown', this.dismissTouchHint, true)\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'\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\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 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: BRAND_COLOR,\n width: '3.5',\n linecap: 'round',\n linejoin: 'round',\n filter: `drop-shadow(0 1px 2px ${brandAlpha(0.35)})`,\n} as const\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { Annotation } from '../types'\nimport { DRAW_STROKE } from '../styles'\n\ntype Point = [number, number]\n\ninterface Stroke {\n path: SVGPathElement\n d: string\n points: Point[]\n}\n\nexport class DrawLayer {\n private svg: SVGSVGElement\n private currentPath: SVGPathElement | null = null\n private currentPoints: Point[] = []\n private isDrawing = false\n private committed: Stroke[] = []\n private redoStack: { d: string; points: Point[] }[] = []\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.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({ path: this.currentPath, d: buildPathD(this.currentPoints), points: this.currentPoints })\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 })\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 this.committed.push({ path, d: entry.d, points: entry.points })\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 if (this.committed.length === 0) return null\n const points = this.committed.flatMap(s => s.points)\n const [cx, cy] = center(points)\n return {\n type: 'draw',\n x: cx,\n y: cy,\n drawPath: this.committed.map(s => s.d).join(' '),\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 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","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","import type { ShadowContainer } from '../widget/shadow'\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// 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// 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\nfunction loadRrweb(): Promise<RRWebRecordFn> {\n if (rrwebLoaded) return rrwebLoaded\n rrwebLoaded = loadViaImport().catch(() => loadViaCdn())\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 { BLOCK_CLASS, MASK_TEXT_CLASS } from '../record/session'\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, getElementName, getCssSelector } from './highlighter'\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 = getElementUnder(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 = getElementUnder(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 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 }\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 const underEl = getElementUnder(annotation.x, annotation.y)\n if (!underEl) return annotation\n\n const rect = underEl.getBoundingClientRect()\n const target = annotationTarget(underEl)\n return {\n ...annotation,\n targetSelector: target.selector,\n targetText: target.text,\n targetName: target.name,\n targetRect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height, bottom: rect.bottom },\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// Skips the widget host via elementsFromPoint instead of toggling its\n// display — hiding the host on every mousemove flickered the toolbar.\nfunction getElementUnder(x: number, y: number): Element | null {\n return document.elementsFromPoint(x, y).find(el => el.id !== 'mtb-widget-host') ?? null\n}\n","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\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\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 } 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}\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\n constructor(shadow: ShadowContainer, opts: DrawNoteBarOptions) {\n this.onSubmit = opts.onSubmit\n this.submitLabel = opts.messages.draw.submit\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 this.input.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) 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 <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 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","/**\n * Swallows the compatibility click that a touch browser replays from the tap\n * which opened a panel.\n *\n * On iOS the sequence is pointerdown / pointerup — the widget places a pin and\n * opens the sheet — and only then does the browser dispatch a `click` for that\n * same finger press. By that point the sheet is on screen under the touch\n * point, so the click lands inside it. Observed on iOS 18.7 hitting the button\n * row: a few pixels either way and the gesture that opened the sheet would\n * have cancelled or submitted it. Cancelling pointerdown does not prevent it —\n * WebKit dispatches the click regardless.\n *\n * The replay is told apart by what preceded it, not by when it arrives. A real\n * press on the panel always fires `pointerdown` on the panel first; the replay\n * cannot, because its pointerdown went to the overlay underneath. An earlier\n * version simply ignored everything for 400ms, which also ate a fast, genuine\n * second tap — a window is a guess about human speed, and this is not.\n */\nexport function guardReplayClick(el: HTMLElement): () => void {\n let sawPointerDown = false\n\n const onPointerDown = (): void => {\n sawPointerDown = true\n }\n\n const onClick = (e: Event): void => {\n // detail 0 means the click did not come from a pointer at all — a button\n // activated by Enter or by assistive tech. Those never have a pointerdown\n // and must go through.\n const fromPointer = (e as MouseEvent).detail > 0\n if (fromPointer && !sawPointerDown) {\n e.stopPropagation()\n e.preventDefault()\n }\n sawPointerDown = false\n }\n\n el.addEventListener('pointerdown', onPointerDown, true)\n el.addEventListener('click', onClick, true)\n\n return () => {\n el.removeEventListener('pointerdown', onPointerDown, true)\n el.removeEventListener('click', onClick, true)\n }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport type { TargetRect } from '../types'\nimport { escapeHtml } from '../context/dom-utils'\nimport { SheetLayout } from './sheet'\nimport { guardReplayClick } from './replay-click-guard'\nimport { isTouchPointer } from '../widget/pointer-kind'\n\ninterface PopupOptions {\n targetName: string\n x: number\n y: number\n targetRect?: TargetRect\n onSubmit: (description: string) => void\n onClose: () => void\n onMyFeedback?: () => void\n messages: I18nMessages\n position?: 'left' | 'right'\n}\n\nexport class CommentPopup {\n private el: HTMLDivElement\n private sheet: SheetLayout\n private releaseGuard: (() => void) | null = null\n private textarea!: HTMLTextAreaElement\n private submitBtn!: HTMLButtonElement\n private onSubmit: (description: string) => void\n private onClose: () => void\n private submitLabel: string\n private messages: I18nMessages\n private selectedQuickOption: string | null = null\n\n constructor(shadow: ShadowContainer, opts: PopupOptions) {\n this.onSubmit = opts.onSubmit\n this.onClose = opts.onClose\n this.submitLabel = opts.messages.popup.submit\n this.messages = opts.messages\n\n this.el = shadow.el<HTMLDivElement>('div', 'mtb-popup')\n this.el.innerHTML = this.buildHTML(opts)\n shadow.append(this.el)\n this.sheet = new SheetLayout(this.el)\n // The tap that opened this panel is replayed as a click once the panel is\n // already under the finger — on iOS it landed on the button row.\n if (isTouchPointer()) this.releaseGuard = guardReplayClick(this.el)\n\n this.textarea = this.el.querySelector('.mtb-textarea')!\n this.submitBtn = this.el.querySelector('.mtb-submit-btn')!\n\n this.positionPopup(opts.x, opts.y, opts.targetRect, opts.position ?? 'right')\n this.bindEvents(opts.messages)\n if (opts.onMyFeedback) {\n this.el.querySelector('.mtb-my-feedback')!.addEventListener('click', opts.onMyFeedback)\n }\n // Deliberately not focused on a phone. Focusing opens the keyboard, and\n // iOS repositions position:fixed elements as the keyboard animates — the\n // panel visibly shuffles, through no style change of ours, which is why\n // nothing showed up in the geometry logs. Waiting for the reporter to tap\n // the field puts that movement where they asked for it. Nothing is lost:\n // iOS routinely ignores programmatic focus for raising the keyboard.\n //\n // preventScroll stays for the desktop: the sheet is position:fixed, so the\n // browser cannot bring the field into view by moving the panel and scrolls\n // the document instead.\n if (!isTouchPointer()) this.textarea.focus({ preventScroll: true })\n }\n\n private buildHTML(opts: PopupOptions): string {\n const m = opts.messages\n const quickOpts = m.popup.quickOptions\n .map(\n o => `\n <button class=\"mtb-quick-option\" data-quick=\"${escapeHtml(o.label)}\">\n <span class=\"mtb-quick-option-emoji\">${o.emoji}</span>\n <span>${escapeHtml(o.label)}</span>\n </button>`,\n )\n .join('')\n\n return `\n <div class=\"mtb-popup-header\">\n <div class=\"mtb-popup-icon\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#059669\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/>\n </svg>\n </div>\n <span class=\"mtb-popup-title\">${m.popup.about} \"${escapeHtml(opts.targetName)}\"</span>\n <button class=\"mtb-popup-close\" aria-label=\"Close\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"mtb-popup-body\">\n <div class=\"mtb-quick-options\">\n ${quickOpts}\n </div>\n <div class=\"mtb-textarea-wrap\">\n <textarea class=\"mtb-textarea\" placeholder=\"${escapeHtml(m.popup.placeholder)}\"></textarea>\n </div>\n <div class=\"mtb-popup-actions\">\n <button class=\"mtb-cancel-btn\" type=\"button\">${escapeHtml(m.popup.cancel)}</button>\n <button class=\"mtb-submit-btn\" type=\"button\" disabled>${escapeHtml(m.popup.submit)}</button>\n </div>\n ${opts.onMyFeedback ? `<button class=\"mtb-my-feedback\" type=\"button\">${escapeHtml(m.popup.my_feedback)}</button>` : ''}\n </div>\n `\n }\n\n\n\n private positionPopup(x: number, y: number, targetRect: TargetRect | undefined, tabPosition: 'left' | 'right'): void {\n if (this.sheet.apply()) return\n\n const vw = window.innerWidth\n const vh = window.innerHeight\n const margin = 12\n const gap = 8\n const tabWidth = 32\n const popupW = Math.min(320, vw - 2 * margin - tabWidth)\n const popupH = this.el.offsetHeight || 260\n const minLeft = margin + (tabPosition === 'left' ? tabWidth : 0)\n const maxRight = vw - margin - (tabPosition === 'right' ? tabWidth : 0)\n\n let left: number\n let top: number\n\n // Anchoring to the element only helps while the element is small enough to\n // point at. A click on empty space lands on whatever section or body spans\n // the page, and its rect says nothing about where the user clicked: the top\n // edge is off-screen, so the popup flips, and the left edge is 0, so it\n // lands in the bottom-left corner however far right the click was.\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\n if (top + popupH > vh - margin) {\n top = targetRect.top - popupH - gap\n }\n if (top < margin) {\n top = Math.min(targetRect.bottom + gap, vh - popupH - margin)\n }\n } else {\n left = x + 12\n top = y + 12\n }\n\n if (left + popupW > maxRight) {\n left = maxRight - popupW\n }\n if (left < minLeft) left = minLeft\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 private bindEvents(messages: I18nMessages): void {\n this.el.querySelector('.mtb-popup-close')!.addEventListener('click', () => this.onClose())\n this.el.querySelector('.mtb-cancel-btn')!.addEventListener('click', () => this.onClose())\n\n this.el.querySelectorAll('.mtb-quick-option').forEach(btn => {\n btn.addEventListener('click', () => {\n const label = (btn as HTMLElement).dataset['quick'] ?? ''\n\n if (this.textarea.value.trim().length === 0) {\n this.textarea.value = label\n this.updateSubmitState()\n this.submit()\n return\n }\n\n this.selectedQuickOption = label\n this.el.querySelectorAll('.mtb-quick-option').forEach(option => {\n option.classList.toggle('selected', option === btn)\n })\n })\n })\n\n this.textarea.addEventListener('input', () => this.updateSubmitState())\n\n this.textarea.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) return\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n this.submit()\n }\n })\n\n this.submitBtn.addEventListener('click', () => this.submit())\n }\n\n private updateSubmitState(): void {\n const hasText = this.textarea.value.trim().length > 0\n this.submitBtn.classList.toggle('ready', hasText)\n this.submitBtn.disabled = !hasText\n }\n\n private submit(): void {\n const text = this.textarea.value.trim()\n if (!text) return\n const description = this.selectedQuickOption ? `[${this.selectedQuickOption}] ${text}` : text\n this.onSubmit(description)\n }\n\n releaseElement(): HTMLDivElement {\n this.releaseGuard?.()\n this.releaseGuard = null\n // Ownership of the element passes to the caller, which means destroy() will\n // never run for this instance. Stop observing here or the listener outlives\n // the popup and keeps writing `bottom` on an element it no longer manages.\n this.sheet.release()\n return this.el\n }\n\n destroy(): void {\n this.releaseGuard?.()\n this.releaseGuard = null\n this.sheet.release()\n this.el.remove()\n }\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 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\nconst DEFAULT_API_URL = 'https://makethisbetter.dev/api/v1'\nconst CAPTURE_POLICY = 'sensitive-data-v1'\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(projectKey: string, apiUrl?: string, retryDelayMs = 1000, userToken?: string, userTokenFn?: () => Promise<string>) {\n this.projectKey = projectKey\n this.apiUrl = (apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, '')\n this.retryDelayMs = retryDelayMs\n this.userToken = userToken\n this.userTokenFn = userTokenFn\n }\n\n async resolveUserToken(): Promise<string | undefined> {\n if (this.userTokenFn) return this.userTokenFn()\n return this.userToken\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 async startClarification(submissionSessionId: string, token: string, message?: string): Promise<ClarifyResponse> {\n const body: Record<string, string | boolean> = {}\n if (message) body.message = filterSensitiveText(message)\n return this.requestClarification(submissionSessionId, token, body)\n }\n\n async retryClarification(submissionSessionId: string, token: string): Promise<ClarifyResponse> {\n return this.requestClarification(submissionSessionId, token, { retry: true })\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 async getClarification(submissionSessionId: string, token: string): Promise<ClarifyResponse> {\n const headers = await this.submissionHeaders(token)\n const res = await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/clarification`, {\n method: 'GET',\n headers,\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. Throws if the server doesn't stream\n // (e.g. old backend returns JSON) so the caller can fall back to polling.\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 res = await this.fetchWithRetry(`${this.apiUrl}/widget/feedback_submission_sessions/${submissionSessionId}/feedback`, {\n method: 'POST',\n headers,\n })\n const response = await res.json() as { feedback: FeedbackResponse }\n return response.feedback\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 if (retries > 0) {\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 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","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport type { ApiClient, ClarifyStreamResult } from '../api/client'\nimport { isTerminalConflict } from '../api/client'\nimport type { ClarifyMessage, ClarifyResponse, SubmissionSessionResponse } from '../types'\nimport { escapeHtml } from '../context/dom-utils'\nimport { SheetLayout } from './sheet'\nimport { isTouchPointer } from '../widget/pointer-kind'\n\nconst POLL_INTERVAL_MS = 2000\n// Give up polling after this long with no assistant reply so the card never spins\n// forever (e.g. a backend job that silently died). This is the queued path — the\n// answer is being generated by a job, not streamed — so it needs room for queue wait\n// plus generation. The 12s deadline on the streaming request is a different budget:\n// there, silence means the stream itself is stuck.\nconst POLL_TIMEOUT_MS = 45000\n// How long a clarification stream may stay silent before it becomes retryable.\n// Measured between tokens, not from the start, so a long answer is not cut off.\nconst CLARIFY_SILENCE_TIMEOUT_MS = 12000\n\ninterface ClarifyCardOptions {\n // Either the session is already known, or it is still being created — the\n // card mounts immediately either way and shows its thinking bubble while\n // pendingSession settles.\n submissionSessionId?: string\n submissionToken?: string\n pendingSession?: Promise<SubmissionSessionResponse | null>\n onRetrySubmission?: () => Promise<SubmissionSessionResponse | null>\n apiClient: ApiClient\n messages: I18nMessages\n onFinalize: () => Promise<void> | void\n onCancel?: () => void\n element?: HTMLDivElement\n position?: 'left' | 'right'\n x?: number\n y?: number\n}\n\nconst CARD_WIDTH = 346\nconst CARD_HEIGHT = 270\n\nexport class ClarifyCard {\n private el: HTMLDivElement\n private sheet: SheetLayout\n private messagesEl!: HTMLDivElement\n private inputEl!: HTMLTextAreaElement\n private sendBtn!: HTMLButtonElement\n private pollTimer: ReturnType<typeof setInterval> | null = null\n private pollDeadline = 0\n private streamingBubble: HTMLDivElement | null = null\n private submissionSessionId: string\n private submissionToken: string\n private apiClient: ApiClient\n private onFinalize: () => Promise<void> | void\n private onCancel?: () => void\n private i18n: I18nMessages\n private lastMessages: ClarifyMessage[] = []\n private done = false\n private sending = false\n private finalSubmissionStarted = false\n private finalizing = false\n private destroyed = false\n // The in-flight clarification stream, so closing the card can hang up on it\n // rather than leaving the connection reading into a card that is gone.\n private streamAbort: AbortController | null = null\n private unrecoverable = false\n private submissionFailed = false\n\n private pendingSession?: Promise<SubmissionSessionResponse | null>\n private onRetrySubmission?: () => Promise<SubmissionSessionResponse | null>\n\n constructor(shadow: ShadowContainer, opts: ClarifyCardOptions) {\n this.submissionSessionId = opts.submissionSessionId ?? ''\n this.submissionToken = opts.submissionToken ?? ''\n this.apiClient = opts.apiClient\n this.onFinalize = opts.onFinalize\n this.onCancel = opts.onCancel\n this.i18n = opts.messages\n this.pendingSession = opts.pendingSession\n this.onRetrySubmission = opts.onRetrySubmission\n\n const existingElement = opts.element\n this.el = existingElement ?? shadow.el<HTMLDivElement>('div', 'mtb-clarify')\n this.el.className = existingElement ? 'mtb-clarify mtb-clarify-continuation' : 'mtb-clarify'\n this.el.innerHTML = this.buildHTML(opts.messages)\n this.sheet = new SheetLayout(this.el)\n if (!existingElement) {\n shadow.append(this.el)\n this.positionCard(opts.x, opts.y, opts.position ?? 'right')\n } else {\n // A continuation inherits the comment popup's element, and with it the\n // sheet layout that popup was using — but the class just changed from\n // .mtb-popup to .mtb-clarify, so the sheet rules stop matching and the\n // cleared inline offsets leave a fixed element with nowhere to sit.\n // Re-applying binds the new class to the same geometry and re-attaches\n // the keyboard tracking the released popup gave up.\n this.sheet.apply()\n }\n\n this.messagesEl = this.el.querySelector('.mtb-clarify-messages')!\n this.bindEvents()\n // After submitting a comment with Enter, keep the next Enter on the\n // explicit skip-and-send action. This makes the two-step shortcut\n // deterministic instead of depending on the browser's focus fallback.\n if (existingElement && !isTouchPointer()) {\n this.el.querySelector<HTMLButtonElement>('.mtb-clarify-skip')!.focus({ preventScroll: true })\n }\n this.startConversation()\n }\n\n // Anchors the card near the annotation point, clamped to the viewport so it\n // never overflows a screen edge (mirrors CommentPopup.positionPopup).\n private positionCard(x: number | undefined, y: number | undefined, tabPosition: 'left' | 'right'): void {\n if (this.sheet.apply()) return\n\n const vw = window.innerWidth\n const vh = window.innerHeight\n const margin = 12\n const tabWidth = 32\n const cardW = Math.min(CARD_WIDTH, vw - 2 * margin - tabWidth)\n const cardH = this.el.offsetHeight || CARD_HEIGHT\n const minLeft = margin + (tabPosition === 'left' ? tabWidth : 0)\n const maxRight = vw - margin - (tabPosition === 'right' ? tabWidth : 0)\n\n if (x === undefined || y === undefined) {\n const centeredLeft = Math.max(minLeft, (vw - cardW) / 2)\n this.el.style.left = `${Math.min(centeredLeft, maxRight - cardW)}px`\n this.el.style.top = `${Math.max(margin, (vh - cardH) / 2)}px`\n this.el.style.maxHeight = `${vh - 2 * margin}px`\n return\n }\n\n let left = x + 12\n let top = y + 12\n if (left + cardW > maxRight) left = x - cardW - 12\n if (left + cardW > maxRight) left = maxRight - cardW\n if (left < minLeft) left = minLeft\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 private buildHTML(m: I18nMessages): string {\n return `\n <div class=\"mtb-clarify-header\">\n <span class=\"mtb-clarify-icon\">\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 3l1.6 5.4L19 10l-5.4 1.6L12 17l-1.6-5.4L5 10l5.4-1.6L12 3z\"/>\n </svg>\n </span>\n <div class=\"mtb-clarify-titles\">\n <div class=\"mtb-clarify-title\">${escapeHtml(m.clarify.title)}</div>\n <div class=\"mtb-clarify-subtitle\">${escapeHtml(m.clarify.subtitle)}</div>\n </div>\n <button class=\"mtb-clarify-close\" type=\"button\" title=\"${escapeHtml(m.clarify.skip)}\" aria-label=\"${escapeHtml(m.clarify.skip)}\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"mtb-clarify-messages\"></div>\n <div class=\"mtb-clarify-footer\">${this.buildConversationControls(m)}</div>\n `\n }\n\n private buildConversationControls(m: I18nMessages): string {\n return `\n <textarea class=\"mtb-clarify-input\" rows=\"1\" placeholder=\"${escapeHtml(m.clarify.placeholder)}\"></textarea>\n <div class=\"mtb-clarify-actions\">\n <button class=\"mtb-clarify-skip\" type=\"button\">${escapeHtml(m.clarify.skip)}</button>\n <button class=\"mtb-clarify-send\" type=\"button\" aria-label=\"${escapeHtml(m.clarify.send)}\" disabled>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n <span class=\"mtb-clarify-send-label\">${escapeHtml(m.clarify.send)}</span>\n <span class=\"mtb-clarify-send-spinner\" aria-hidden=\"true\"></span>\n </button>\n </div>\n `\n }\n\n private bindEvents(): void {\n this.el.querySelector('.mtb-clarify-close')!.addEventListener('click', () => this.skipAndFinalize())\n this.bindConversationControls()\n }\n\n private bindConversationControls(): void {\n this.inputEl = this.el.querySelector('.mtb-clarify-input')!\n this.sendBtn = this.el.querySelector('.mtb-clarify-send')!\n this.el.querySelector('.mtb-clarify-skip')!.addEventListener('click', () => this.skipAndFinalize())\n\n this.inputEl.addEventListener('input', () => {\n this.sendBtn.disabled = !this.inputEl.value.trim()\n })\n\n this.inputEl.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.isComposing || e.keyCode === 229) return\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n this.sendMessage()\n }\n })\n\n this.sendBtn.addEventListener('click', () => this.sendMessage())\n }\n\n private skipAndFinalize(): void {\n // With no session on the server there is nothing to finalize: the failure\n // footer already offers Retry, so the header close just dismisses.\n if (this.submissionFailed) {\n this.onCancel?.()\n return\n }\n this.stopPolling()\n void this.finalize()\n }\n\n private async startConversation(): Promise<void> {\n this.showThinking()\n if (this.pendingSession) {\n // No session id yet, so an eager reply could not be delivered anywhere —\n // keep the input closed until the assistant's question arrives.\n this.inputEl.disabled = true\n await this.adoptSession(this.pendingSession)\n return\n }\n await this.runInitialTurn()\n }\n\n // Waits out the session upload, then drives the first clarification turn.\n private async adoptSession(pending: Promise<SubmissionSessionResponse | null>): Promise<void> {\n let session: SubmissionSessionResponse | null\n try {\n session = await pending\n } catch {\n session = null\n }\n if (this.destroyed) return\n if (!session) {\n this.showSubmissionFailure()\n return\n }\n this.submissionSessionId = session.id\n this.submissionToken = session.token\n this.inputEl.disabled = false\n if (!session.ai_clarify_available) {\n this.applyResult({ messages: [], done: true })\n return\n }\n await this.runInitialTurn()\n }\n\n // The feedback itself never reached the server, so there is no session to\n // converse with and nothing to finalize — only a full re-submission helps.\n private showSubmissionFailure(): void {\n this.submissionFailed = true\n this.stopPolling()\n this.removeThinking()\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.error.submit)}</div>\n <button class=\"mtb-clarify-send-feedback mtb-clarify-retry\" type=\"button\">${escapeHtml(this.i18n.clarify.retry)}</button>\n `\n footer.querySelector('.mtb-clarify-retry')!.addEventListener('click', () => {\n if (!this.destroyed) void this.retrySubmission()\n })\n }\n\n private async retrySubmission(): Promise<void> {\n if (!this.onRetrySubmission || this.sending) return\n this.submissionFailed = false\n this.showConversationControls()\n this.inputEl.disabled = true\n this.showThinking()\n await this.adoptSession(this.onRetrySubmission())\n }\n\n private async sendMessage(): Promise<void> {\n const text = this.inputEl.value.trim()\n if (!text || this.sending || !this.submissionSessionId) return\n\n this.sending = true\n this.inputEl.disabled = true\n this.sendBtn.disabled = true\n this.sendBtn.setAttribute('aria-busy', 'true')\n this.stopPolling()\n this.finalSubmissionStarted = true\n\n this.appendMessage({ role: 'user', content: text })\n this.inputEl.value = ''\n this.clearError()\n\n try {\n await this.runAnswerTurn(text)\n } catch {\n if (this.destroyed) return\n this.finalSubmissionStarted = false\n this.sending = false\n this.renderConversation(this.lastMessages, false)\n this.inputEl.value = text\n this.showError()\n this.sendBtn.removeAttribute('aria-busy')\n this.inputEl.disabled = false\n this.sendBtn.disabled = !this.inputEl.value.trim()\n this.inputEl.focus({ preventScroll: true })\n }\n }\n\n // Streams the assistant reply; falls back to POST + polling if the backend\n // doesn't support SSE (older server) or the stream errors mid-flight.\n //\n // A stream the server accepts but never terminates would leave the card\n // showing its thinking state forever. The deadline measures silence rather\n // than total time: a stream still delivering tokens is making progress and\n // is left alone, however long the answer turns out to be.\n private async runInitialTurn(): Promise<void> {\n const abort = new AbortController()\n this.streamAbort = abort\n let silenceTimer = setTimeout(() => abort.abort(), CLARIFY_SILENCE_TIMEOUT_MS)\n try {\n const result = await this.apiClient.streamClarification(\n this.submissionSessionId,\n this.submissionToken,\n (text) => {\n clearTimeout(silenceTimer)\n silenceTimer = setTimeout(() => abort.abort(), CLARIFY_SILENCE_TIMEOUT_MS)\n this.appendStreamDelta(text)\n },\n abort.signal,\n )\n if (this.destroyed) return\n this.endStreamingBubble()\n if (!this.applyResult(result)) this.startPolling()\n } catch {\n if (this.destroyed) return\n this.endStreamingBubble()\n await this.runInitialTurnViaPolling()\n } finally {\n clearTimeout(silenceTimer)\n if (this.streamAbort === abort) this.streamAbort = null\n }\n }\n\n private async runInitialTurnViaPolling(): Promise<void> {\n try {\n const res = await this.apiClient.startClarification(\n this.submissionSessionId,\n this.submissionToken,\n )\n if (this.destroyed) return\n if (!this.applyResult(res)) this.startPolling()\n } catch {\n if (this.destroyed) return\n this.handleFailure()\n }\n }\n\n private async runAnswerTurn(message: string): Promise<void> {\n const res = await this.apiClient.startClarification(\n this.submissionSessionId,\n this.submissionToken,\n message,\n )\n if (this.destroyed) return\n if (!this.applyFinalSubmissionResult(res)) this.startPolling()\n }\n\n private showError(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n const error = document.createElement('div')\n error.className = 'mtb-clarify-error'\n error.textContent = this.i18n.error.submit\n footer.prepend(error)\n }\n\n private clearError(): void {\n this.el.querySelector('.mtb-clarify-error')?.remove()\n }\n\n private appendStreamDelta(text: string): void {\n if (!this.streamingBubble) {\n this.removeThinking()\n this.streamingBubble = document.createElement('div')\n this.streamingBubble.className = 'mtb-clarify-bubble mtb-clarify-ai'\n this.messagesEl.appendChild(this.streamingBubble)\n }\n this.streamingBubble.textContent = (this.streamingBubble.textContent ?? '') + text\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private endStreamingBubble(): void {\n this.streamingBubble = null\n }\n\n private startPolling(): void {\n this.stopPolling()\n this.pollDeadline = Date.now() + POLL_TIMEOUT_MS\n this.pollTimer = setInterval(() => this.poll(), POLL_INTERVAL_MS)\n }\n\n private stopPolling(): void {\n if (this.pollTimer !== null) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n }\n\n private async poll(): Promise<void> {\n if (Date.now() > this.pollDeadline) {\n this.stopPolling()\n this.handleFailure()\n return\n }\n try {\n const res = await this.apiClient.getClarification(\n this.submissionSessionId,\n this.submissionToken,\n )\n if (this.destroyed) return\n const settled = this.finalSubmissionStarted\n ? this.applyFinalSubmissionResult(res)\n : this.applyResult(res)\n if (settled) this.stopPolling()\n } catch {\n if (this.destroyed) return\n this.stopPolling()\n this.handleFailure()\n }\n }\n\n private handleDone(autoFinalize: boolean): void {\n if (this.done) return\n this.done = true\n this.stopPolling()\n this.removeThinking()\n if (autoFinalize) {\n void this.finalize()\n return\n }\n this.showFinalizeButton()\n }\n\n private handleFailure(): void {\n if (this.done) return\n this.done = true\n this.sending = false\n this.finalSubmissionStarted = false\n this.stopPolling()\n this.removeThinking()\n this.showClarificationFailureActions()\n }\n\n private showClarificationFailureActions(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.clarify.error)}</div>\n <div class=\"mtb-clarify-actions\">\n <button class=\"mtb-clarify-fallback\" type=\"button\">${escapeHtml(this.i18n.clarify.send_feedback)}</button>\n <button class=\"mtb-clarify-retry\" type=\"button\">${escapeHtml(this.i18n.clarify.retry)}</button>\n </div>\n `\n footer.querySelector('.mtb-clarify-fallback')!.addEventListener('click', () => this.skipAndFinalize())\n footer.querySelector('.mtb-clarify-retry')!.addEventListener('click', () => {\n if (!this.destroyed) void this.retryClarification()\n })\n }\n\n private async retryClarification(): Promise<void> {\n if (this.sending || this.destroyed) return\n\n this.sending = true\n this.done = false\n this.showConversationControls()\n this.inputEl.disabled = true\n this.sendBtn.disabled = true\n this.renderConversation(this.lastMessages, false)\n\n try {\n const snapshot = await this.apiClient.getClarification(this.submissionSessionId, this.submissionToken)\n if (this.destroyed) return\n let result = snapshot\n // Each server status needs a different move. `processing` means a job is\n // already running for this session, so kicking off another turn would\n // duplicate it — fall through to polling instead. `awaiting_response` means\n // the assistant already asked its question and the server is waiting on the\n // reporter; applyResult hands the card back to them rather than polling for\n // a state only a human reply can change.\n if (snapshot.status === 'failed') {\n result = await this.apiClient.retryClarification(this.submissionSessionId, this.submissionToken)\n } else if (snapshot.status === 'idle') {\n result = await this.apiClient.startClarification(this.submissionSessionId, this.submissionToken)\n }\n if (this.destroyed) return\n if (!this.applyResult(result)) {\n this.showConversationControls()\n this.startPolling()\n }\n } catch {\n if (this.destroyed) return\n this.handleFailure()\n } finally {\n this.sending = false\n }\n }\n\n private showConversationControls(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = this.buildConversationControls(this.i18n)\n this.bindConversationControls()\n }\n\n // Returns true when the exchange has settled — the caller uses that to decide\n // whether it still needs to poll.\n private applyFinalSubmissionResult(result: ClarifyResponse): boolean {\n if (result.status === 'failed') {\n this.handleFailure()\n return true\n }\n if (result.done) {\n this.handleDone(true)\n return true\n }\n return false\n }\n\n private applyResult(result: ClarifyStreamResult | ClarifyResponse): boolean {\n this.renderConversation(result.messages, result.done)\n const status = 'status' in result ? result.status : undefined\n const failed = 'status' in result ? result.status === 'failed' : result.failed === true\n if (failed) {\n this.handleFailure()\n return true\n }\n if (result.done) {\n // A completed AI exchange already has the reporter's answer and the\n // assistant's response, so submit it without asking for a second click.\n // Empty completed results mean AI clarification was unavailable, where\n // the reporter still needs an explicit way to send their feedback.\n this.handleDone(result.messages.some(message => message.role === 'assistant'))\n return true\n }\n if (status === 'awaiting_response') {\n // The ball is in the reporter's court: polling here would spin until the\n // deadline and then show a failure card for a perfectly healthy session.\n this.stopPolling()\n this.removeThinking()\n this.enableInput()\n return true\n }\n return false\n }\n\n private enableInput(): void {\n if (!this.el.contains(this.inputEl)) this.showConversationControls()\n this.inputEl.disabled = false\n this.sendBtn.disabled = !this.inputEl.value.trim()\n }\n\n private showFinalizeButton(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <button class=\"mtb-clarify-send-feedback\" type=\"button\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n <span>${escapeHtml(this.i18n.clarify.send_feedback)}</span>\n </button>\n `\n footer.querySelector('.mtb-clarify-send-feedback')!.addEventListener('click', () => {\n if (!this.destroyed) void this.finalize()\n })\n }\n\n private async finalize(): Promise<void> {\n if (this.finalizing || this.destroyed || this.unrecoverable) return\n\n this.finalizing = true\n this.el.querySelectorAll<HTMLButtonElement>('button').forEach(button => { button.disabled = true })\n try {\n await this.onFinalize()\n } catch (error) {\n if (this.destroyed) return\n this.finalizing = false\n this.clearError()\n // A 409 means the server will never accept this session again (it expired\n // past its retention window, or was already finalized/abandoned). Offering\n // Retry there just loops the reporter through the same generic error, so say\n // what happened and what to do instead.\n if (isTerminalConflict(error)) {\n this.showUnrecoverable()\n return\n }\n this.showFinalizeRetry()\n }\n }\n\n private showUnrecoverable(): void {\n this.unrecoverable = true\n this.stopPolling()\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `<div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.clarify.expired)}</div>`\n }\n\n // Renders the reason and the retry together: replacing the footer wholesale is\n // what used to discard the error message showError() had just prepended, so the\n // reporter saw a bare Retry button with no explanation.\n private showFinalizeRetry(): void {\n const footer = this.el.querySelector('.mtb-clarify-footer')!\n footer.innerHTML = `\n <div class=\"mtb-clarify-error\">${escapeHtml(this.i18n.error.submit)}</div>\n <button class=\"mtb-clarify-send-feedback mtb-clarify-retry\" type=\"button\">${escapeHtml(this.i18n.clarify.retry)}</button>\n `\n footer.querySelector('.mtb-clarify-retry')!.addEventListener('click', () => {\n if (!this.destroyed) void this.finalize()\n })\n }\n\n private renderConversation(messages: ClarifyMessage[], done: boolean): void {\n this.lastMessages = messages\n this.renderMessages(messages)\n const last = messages[messages.length - 1]\n if (!done && (!last || last.role === 'user')) this.showThinking()\n }\n\n private renderMessages(messages: ClarifyMessage[]): void {\n this.messagesEl.innerHTML = ''\n for (const msg of messages) {\n this.appendMessage(msg)\n }\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private appendMessage(msg: ClarifyMessage): void {\n const bubble = document.createElement('div')\n bubble.className = msg.role === 'user' ? 'mtb-clarify-bubble mtb-clarify-user' : 'mtb-clarify-bubble mtb-clarify-ai'\n bubble.textContent = msg.content\n this.messagesEl.appendChild(bubble)\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private showThinking(): void {\n if (this.messagesEl.querySelector('.mtb-clarify-thinking')) return\n const dots = document.createElement('div')\n dots.className = 'mtb-clarify-bubble mtb-clarify-ai mtb-clarify-thinking'\n dots.innerHTML = '<span class=\"mtb-dot\"></span><span class=\"mtb-dot\"></span><span class=\"mtb-dot\"></span>'\n this.messagesEl.appendChild(dots)\n this.messagesEl.scrollTop = this.messagesEl.scrollHeight\n }\n\n private removeThinking(): void {\n this.messagesEl.querySelector('.mtb-clarify-thinking')?.remove()\n }\n\n destroy(): void {\n this.destroyed = true\n this.streamAbort?.abort()\n this.streamAbort = null\n this.stopPolling()\n this.sheet.release()\n this.el.remove()\n }\n}\n","import type { ShadowContainer } from '../widget/shadow'\nimport type { I18nMessages } from '../i18n'\nimport { BRAND_COLOR } from '../styles'\n\nconst SUCCESS_AUTO_CLOSE_MS = 5000\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport interface SuccessCardOptions {\n skipFollowup?: boolean\n emailCapture?: {\n onSubmit: (email: string) => Promise<boolean>\n }\n}\n\nexport class SuccessCard {\n private el: HTMLDivElement\n private messages: I18nMessages\n\n constructor(shadow: ShadowContainer, messages: I18nMessages, onClose: () => void, onViewFeedback?: () => void, options?: SuccessCardOptions) {\n this.messages = messages\n this.el = shadow.el<HTMLDivElement>('div', 'mtb-success')\n this.el.style.right = '20px'\n this.el.style.bottom = '20px'\n\n const title = options?.skipFollowup ? messages.success.title_no_ai : messages.success.title\n const msg = options?.skipFollowup ? messages.success.message_no_ai : messages.success.message\n\n const viewLink = onViewFeedback\n ? `<button class=\"mtb-view-feedback-link\">${messages.success.view_feedback} <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14M12 5l7 7-7 7\"/></svg></button>`\n : ''\n\n const emailCapture = options?.emailCapture\n ? `\n <div class=\"mtb-email-capture\">\n <div class=\"mtb-email-prompt\">${messages.success.email_prompt}</div>\n <div class=\"mtb-email-row\">\n <input class=\"mtb-email-input\" type=\"email\" placeholder=\"${messages.success.email_placeholder}\">\n <button class=\"mtb-email-submit\">${messages.success.email_submit}</button>\n </div>\n <div class=\"mtb-email-error\" hidden>${messages.success.email_error}</div>\n </div>`\n : ''\n\n this.el.innerHTML = `\n <div class=\"mtb-success-icon\">\n <svg width=\"23\" height=\"23\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"${BRAND_COLOR}\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"/>\n </svg>\n </div>\n <div class=\"mtb-success-title\">${title}</div>\n <div class=\"mtb-success-msg\">${msg}</div>\n ${emailCapture}\n ${viewLink}\n <button class=\"mtb-close-link\">${messages.success.close}</button>\n `\n shadow.append(this.el)\n this.el.querySelector('.mtb-close-link')!.addEventListener('click', onClose)\n\n if (onViewFeedback) {\n this.el.querySelector('.mtb-view-feedback-link')!.addEventListener('click', onViewFeedback)\n }\n\n if (options?.emailCapture) {\n this.bindEmailCapture(options.emailCapture.onSubmit)\n } else {\n setTimeout(() => onClose(), SUCCESS_AUTO_CLOSE_MS)\n }\n }\n\n private bindEmailCapture(onSubmit: (email: string) => Promise<boolean>): void {\n const input = this.el.querySelector<HTMLInputElement>('.mtb-email-input')!\n const submit = this.el.querySelector<HTMLButtonElement>('.mtb-email-submit')!\n const error = this.el.querySelector<HTMLDivElement>('.mtb-email-error')!\n\n const handle = async () => {\n const email = input.value.trim()\n if (!EMAIL_PATTERN.test(email)) {\n input.classList.add('mtb-email-invalid')\n return\n }\n input.classList.remove('mtb-email-invalid')\n error.hidden = true\n submit.disabled = true\n\n const saved = await onSubmit(email)\n if (saved) {\n this.el.querySelector('.mtb-email-capture')!.innerHTML =\n `<div class=\"mtb-email-saved\">${this.messages.success.email_saved}</div>`\n } else {\n error.hidden = false\n submit.disabled = false\n }\n }\n\n submit.addEventListener('click', handle)\n input.addEventListener('keydown', (event) => {\n if (event.key === 'Enter') handle()\n })\n }\n\n destroy(): void {\n this.el.remove()\n }\n}\n","export function resolveUrl(url, baseUrl) {\n // url is absolute already\n if (url.match(/^[a-z]+:\\/\\//i)) {\n return url;\n }\n // url is absolute already, without protocol\n if (url.match(/^\\/\\//)) {\n return window.location.protocol + url;\n }\n // dataURI, mailto:, tel:, etc.\n if (url.match(/^[a-z]+:/i)) {\n return url;\n }\n const doc = document.implementation.createHTMLDocument();\n const base = doc.createElement('base');\n const a = doc.createElement('a');\n doc.head.appendChild(base);\n doc.body.appendChild(a);\n if (baseUrl) {\n base.href = baseUrl;\n }\n a.href = url;\n return a.href;\n}\nexport const uuid = (() => {\n // generate uuid for className of pseudo elements.\n // We should not use GUIDs, otherwise pseudo elements sometimes cannot be captured.\n let counter = 0;\n // ref: http://stackoverflow.com/a/6248722/2519373\n const random = () => \n // eslint-disable-next-line no-bitwise\n `0000${((Math.random() * 36 ** 4) << 0).toString(36)}`.slice(-4);\n return () => {\n counter += 1;\n return `u${random()}${counter}`;\n };\n})();\nexport function delay(ms) {\n return (args) => new Promise((resolve) => {\n setTimeout(() => resolve(args), ms);\n });\n}\nexport function toArray(arrayLike) {\n const arr = [];\n for (let i = 0, l = arrayLike.length; i < l; i++) {\n arr.push(arrayLike[i]);\n }\n return arr;\n}\nlet styleProps = null;\nexport function getStyleProperties(options = {}) {\n if (styleProps) {\n return styleProps;\n }\n if (options.includeStyleProperties) {\n styleProps = options.includeStyleProperties;\n return styleProps;\n }\n styleProps = toArray(window.getComputedStyle(document.documentElement));\n return styleProps;\n}\nfunction px(node, styleProperty) {\n const win = node.ownerDocument.defaultView || window;\n const val = win.getComputedStyle(node).getPropertyValue(styleProperty);\n return val ? parseFloat(val.replace('px', '')) : 0;\n}\nfunction getNodeWidth(node) {\n const leftBorder = px(node, 'border-left-width');\n const rightBorder = px(node, 'border-right-width');\n return node.clientWidth + leftBorder + rightBorder;\n}\nfunction getNodeHeight(node) {\n const topBorder = px(node, 'border-top-width');\n const bottomBorder = px(node, 'border-bottom-width');\n return node.clientHeight + topBorder + bottomBorder;\n}\nexport function getImageSize(targetNode, options = {}) {\n const width = options.width || getNodeWidth(targetNode);\n const height = options.height || getNodeHeight(targetNode);\n return { width, height };\n}\nexport function getPixelRatio() {\n let ratio;\n let FINAL_PROCESS;\n try {\n FINAL_PROCESS = process;\n }\n catch (e) {\n // pass\n }\n const val = FINAL_PROCESS && FINAL_PROCESS.env\n ? FINAL_PROCESS.env.devicePixelRatio\n : null;\n if (val) {\n ratio = parseInt(val, 10);\n if (Number.isNaN(ratio)) {\n ratio = 1;\n }\n }\n return ratio || window.devicePixelRatio || 1;\n}\n// @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size\nconst canvasDimensionLimit = 16384;\nexport function checkCanvasDimensions(canvas) {\n if (canvas.width > canvasDimensionLimit ||\n canvas.height > canvasDimensionLimit) {\n if (canvas.width > canvasDimensionLimit &&\n canvas.height > canvasDimensionLimit) {\n if (canvas.width > canvas.height) {\n canvas.height *= canvasDimensionLimit / canvas.width;\n canvas.width = canvasDimensionLimit;\n }\n else {\n canvas.width *= canvasDimensionLimit / canvas.height;\n canvas.height = canvasDimensionLimit;\n }\n }\n else if (canvas.width > canvasDimensionLimit) {\n canvas.height *= canvasDimensionLimit / canvas.width;\n canvas.width = canvasDimensionLimit;\n }\n else {\n canvas.width *= canvasDimensionLimit / canvas.height;\n canvas.height = canvasDimensionLimit;\n }\n }\n}\nexport function canvasToBlob(canvas, options = {}) {\n if (canvas.toBlob) {\n return new Promise((resolve) => {\n canvas.toBlob(resolve, options.type ? options.type : 'image/png', options.quality ? options.quality : 1);\n });\n }\n return new Promise((resolve) => {\n const binaryString = window.atob(canvas\n .toDataURL(options.type ? options.type : undefined, options.quality ? options.quality : undefined)\n .split(',')[1]);\n const len = binaryString.length;\n const binaryArray = new Uint8Array(len);\n for (let i = 0; i < len; i += 1) {\n binaryArray[i] = binaryString.charCodeAt(i);\n }\n resolve(new Blob([binaryArray], {\n type: options.type ? options.type : 'image/png',\n }));\n });\n}\nexport function createImage(url) {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n img.decode().then(() => {\n requestAnimationFrame(() => resolve(img));\n });\n };\n img.onerror = reject;\n img.crossOrigin = 'anonymous';\n img.decoding = 'async';\n img.src = url;\n });\n}\nexport async function svgToDataURL(svg) {\n return Promise.resolve()\n .then(() => new XMLSerializer().serializeToString(svg))\n .then(encodeURIComponent)\n .then((html) => `data:image/svg+xml;charset=utf-8,${html}`);\n}\nexport async function nodeToDataURL(node, width, height) {\n const xmlns = 'http://www.w3.org/2000/svg';\n const svg = document.createElementNS(xmlns, 'svg');\n const foreignObject = document.createElementNS(xmlns, 'foreignObject');\n svg.setAttribute('width', `${width}`);\n svg.setAttribute('height', `${height}`);\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n foreignObject.setAttribute('width', '100%');\n foreignObject.setAttribute('height', '100%');\n foreignObject.setAttribute('x', '0');\n foreignObject.setAttribute('y', '0');\n foreignObject.setAttribute('externalResourcesRequired', 'true');\n svg.appendChild(foreignObject);\n foreignObject.appendChild(node);\n return svgToDataURL(svg);\n}\nexport const isInstanceOfElement = (node, instance) => {\n if (node instanceof instance)\n return true;\n const nodePrototype = Object.getPrototypeOf(node);\n if (nodePrototype === null)\n return false;\n return (nodePrototype.constructor.name === instance.name ||\n isInstanceOfElement(nodePrototype, instance));\n};\n//# sourceMappingURL=util.js.map","import { uuid, getStyleProperties } from './util';\nfunction formatCSSText(style) {\n const content = style.getPropertyValue('content');\n return `${style.cssText} content: '${content.replace(/'|\"/g, '')}';`;\n}\nfunction formatCSSProperties(style, options) {\n return getStyleProperties(options)\n .map((name) => {\n const value = style.getPropertyValue(name);\n const priority = style.getPropertyPriority(name);\n return `${name}: ${value}${priority ? ' !important' : ''};`;\n })\n .join(' ');\n}\nfunction getPseudoElementStyle(className, pseudo, style, options) {\n const selector = `.${className}:${pseudo}`;\n const cssText = style.cssText\n ? formatCSSText(style)\n : formatCSSProperties(style, options);\n return document.createTextNode(`${selector}{${cssText}}`);\n}\nfunction clonePseudoElement(nativeNode, clonedNode, pseudo, options) {\n const style = window.getComputedStyle(nativeNode, pseudo);\n const content = style.getPropertyValue('content');\n if (content === '' || content === 'none') {\n return;\n }\n const className = uuid();\n try {\n clonedNode.className = `${clonedNode.className} ${className}`;\n }\n catch (err) {\n return;\n }\n const styleElement = document.createElement('style');\n styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));\n clonedNode.appendChild(styleElement);\n}\nexport function clonePseudoElements(nativeNode, clonedNode, options) {\n clonePseudoElement(nativeNode, clonedNode, ':before', options);\n clonePseudoElement(nativeNode, clonedNode, ':after', options);\n}\n//# sourceMappingURL=clone-pseudos.js.map","const WOFF = 'application/font-woff';\nconst JPEG = 'image/jpeg';\nconst mimes = {\n woff: WOFF,\n woff2: WOFF,\n ttf: 'application/font-truetype',\n eot: 'application/vnd.ms-fontobject',\n png: 'image/png',\n jpg: JPEG,\n jpeg: JPEG,\n gif: 'image/gif',\n tiff: 'image/tiff',\n svg: 'image/svg+xml',\n webp: 'image/webp',\n};\nfunction getExtension(url) {\n const match = /\\.([^./]*?)$/g.exec(url);\n return match ? match[1] : '';\n}\nexport function getMimeType(url) {\n const extension = getExtension(url).toLowerCase();\n return mimes[extension] || '';\n}\n//# sourceMappingURL=mimes.js.map","function getContentFromDataUrl(dataURL) {\n return dataURL.split(/,/)[1];\n}\nexport function isDataUrl(url) {\n return url.search(/^(data:)/) !== -1;\n}\nexport function makeDataUrl(content, mimeType) {\n return `data:${mimeType};base64,${content}`;\n}\nexport async function fetchAsDataURL(url, init, process) {\n const res = await fetch(url, init);\n if (res.status === 404) {\n throw new Error(`Resource \"${res.url}\" not found`);\n }\n const blob = await res.blob();\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = reject;\n reader.onloadend = () => {\n try {\n resolve(process({ res, result: reader.result }));\n }\n catch (error) {\n reject(error);\n }\n };\n reader.readAsDataURL(blob);\n });\n}\nconst cache = {};\nfunction getCacheKey(url, contentType, includeQueryParams) {\n let key = url.replace(/\\?.*/, '');\n if (includeQueryParams) {\n key = url;\n }\n // font resource\n if (/ttf|otf|eot|woff2?/i.test(key)) {\n key = key.replace(/.*\\//, '');\n }\n return contentType ? `[${contentType}]${key}` : key;\n}\nexport async function resourceToDataURL(resourceUrl, contentType, options) {\n const cacheKey = getCacheKey(resourceUrl, contentType, options.includeQueryParams);\n if (cache[cacheKey] != null) {\n return cache[cacheKey];\n }\n // ref: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache\n if (options.cacheBust) {\n // eslint-disable-next-line no-param-reassign\n resourceUrl += (/\\?/.test(resourceUrl) ? '&' : '?') + new Date().getTime();\n }\n let dataURL;\n try {\n const content = await fetchAsDataURL(resourceUrl, options.fetchRequestInit, ({ res, result }) => {\n if (!contentType) {\n // eslint-disable-next-line no-param-reassign\n contentType = res.headers.get('Content-Type') || '';\n }\n return getContentFromDataUrl(result);\n });\n dataURL = makeDataUrl(content, contentType);\n }\n catch (error) {\n dataURL = options.imagePlaceholder || '';\n let msg = `Failed to fetch resource: ${resourceUrl}`;\n if (error) {\n msg = typeof error === 'string' ? error : error.message;\n }\n if (msg) {\n console.warn(msg);\n }\n }\n cache[cacheKey] = dataURL;\n return dataURL;\n}\n//# sourceMappingURL=dataurl.js.map","import { clonePseudoElements } from './clone-pseudos';\nimport { createImage, toArray, isInstanceOfElement, getStyleProperties, } from './util';\nimport { getMimeType } from './mimes';\nimport { resourceToDataURL } from './dataurl';\nasync function cloneCanvasElement(canvas) {\n const dataURL = canvas.toDataURL();\n if (dataURL === 'data:,') {\n return canvas.cloneNode(false);\n }\n return createImage(dataURL);\n}\nasync function cloneVideoElement(video, options) {\n if (video.currentSrc) {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n canvas.width = video.clientWidth;\n canvas.height = video.clientHeight;\n ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n const dataURL = canvas.toDataURL();\n return createImage(dataURL);\n }\n const poster = video.poster;\n const contentType = getMimeType(poster);\n const dataURL = await resourceToDataURL(poster, contentType, options);\n return createImage(dataURL);\n}\nasync function cloneIFrameElement(iframe, options) {\n var _a;\n try {\n if ((_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.body) {\n return (await cloneNode(iframe.contentDocument.body, options, true));\n }\n }\n catch (_b) {\n // Failed to clone iframe\n }\n return iframe.cloneNode(false);\n}\nasync function cloneSingleNode(node, options) {\n if (isInstanceOfElement(node, HTMLCanvasElement)) {\n return cloneCanvasElement(node);\n }\n if (isInstanceOfElement(node, HTMLVideoElement)) {\n return cloneVideoElement(node, options);\n }\n if (isInstanceOfElement(node, HTMLIFrameElement)) {\n return cloneIFrameElement(node, options);\n }\n return node.cloneNode(isSVGElement(node));\n}\nconst isSlotElement = (node) => node.tagName != null && node.tagName.toUpperCase() === 'SLOT';\nconst isSVGElement = (node) => node.tagName != null && node.tagName.toUpperCase() === 'SVG';\nasync function cloneChildren(nativeNode, clonedNode, options) {\n var _a, _b;\n if (isSVGElement(clonedNode)) {\n return clonedNode;\n }\n let children = [];\n if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {\n children = toArray(nativeNode.assignedNodes());\n }\n else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) &&\n ((_a = nativeNode.contentDocument) === null || _a === void 0 ? void 0 : _a.body)) {\n children = toArray(nativeNode.contentDocument.body.childNodes);\n }\n else {\n children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);\n }\n if (children.length === 0 ||\n isInstanceOfElement(nativeNode, HTMLVideoElement)) {\n return clonedNode;\n }\n await children.reduce((deferred, child) => deferred\n .then(() => cloneNode(child, options))\n .then((clonedChild) => {\n if (clonedChild) {\n clonedNode.appendChild(clonedChild);\n }\n }), Promise.resolve());\n return clonedNode;\n}\nfunction cloneCSSStyle(nativeNode, clonedNode, options) {\n const targetStyle = clonedNode.style;\n if (!targetStyle) {\n return;\n }\n const sourceStyle = window.getComputedStyle(nativeNode);\n if (sourceStyle.cssText) {\n targetStyle.cssText = sourceStyle.cssText;\n targetStyle.transformOrigin = sourceStyle.transformOrigin;\n }\n else {\n getStyleProperties(options).forEach((name) => {\n let value = sourceStyle.getPropertyValue(name);\n if (name === 'font-size' && value.endsWith('px')) {\n const reducedFont = Math.floor(parseFloat(value.substring(0, value.length - 2))) - 0.1;\n value = `${reducedFont}px`;\n }\n if (isInstanceOfElement(nativeNode, HTMLIFrameElement) &&\n name === 'display' &&\n value === 'inline') {\n value = 'block';\n }\n if (name === 'd' && clonedNode.getAttribute('d')) {\n value = `path(${clonedNode.getAttribute('d')})`;\n }\n targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));\n });\n }\n}\nfunction cloneInputValue(nativeNode, clonedNode) {\n if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {\n clonedNode.innerHTML = nativeNode.value;\n }\n if (isInstanceOfElement(nativeNode, HTMLInputElement)) {\n clonedNode.setAttribute('value', nativeNode.value);\n }\n}\nfunction cloneSelectValue(nativeNode, clonedNode) {\n if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {\n const clonedSelect = clonedNode;\n const selectedOption = Array.from(clonedSelect.children).find((child) => nativeNode.value === child.getAttribute('value'));\n if (selectedOption) {\n selectedOption.setAttribute('selected', '');\n }\n }\n}\nfunction decorate(nativeNode, clonedNode, options) {\n if (isInstanceOfElement(clonedNode, Element)) {\n cloneCSSStyle(nativeNode, clonedNode, options);\n clonePseudoElements(nativeNode, clonedNode, options);\n cloneInputValue(nativeNode, clonedNode);\n cloneSelectValue(nativeNode, clonedNode);\n }\n return clonedNode;\n}\nasync function ensureSVGSymbols(clone, options) {\n const uses = clone.querySelectorAll ? clone.querySelectorAll('use') : [];\n if (uses.length === 0) {\n return clone;\n }\n const processedDefs = {};\n for (let i = 0; i < uses.length; i++) {\n const use = uses[i];\n const id = use.getAttribute('xlink:href');\n if (id) {\n const exist = clone.querySelector(id);\n const definition = document.querySelector(id);\n if (!exist && definition && !processedDefs[id]) {\n // eslint-disable-next-line no-await-in-loop\n processedDefs[id] = (await cloneNode(definition, options, true));\n }\n }\n }\n const nodes = Object.values(processedDefs);\n if (nodes.length) {\n const ns = 'http://www.w3.org/1999/xhtml';\n const svg = document.createElementNS(ns, 'svg');\n svg.setAttribute('xmlns', ns);\n svg.style.position = 'absolute';\n svg.style.width = '0';\n svg.style.height = '0';\n svg.style.overflow = 'hidden';\n svg.style.display = 'none';\n const defs = document.createElementNS(ns, 'defs');\n svg.appendChild(defs);\n for (let i = 0; i < nodes.length; i++) {\n defs.appendChild(nodes[i]);\n }\n clone.appendChild(svg);\n }\n return clone;\n}\nexport async function cloneNode(node, options, isRoot) {\n if (!isRoot && options.filter && !options.filter(node)) {\n return null;\n }\n return Promise.resolve(node)\n .then((clonedNode) => cloneSingleNode(clonedNode, options))\n .then((clonedNode) => cloneChildren(node, clonedNode, options))\n .then((clonedNode) => decorate(node, clonedNode, options))\n .then((clonedNode) => ensureSVGSymbols(clonedNode, options));\n}\n//# sourceMappingURL=clone-node.js.map","import { resolveUrl } from './util';\nimport { getMimeType } from './mimes';\nimport { isDataUrl, makeDataUrl, resourceToDataURL } from './dataurl';\nconst URL_REGEX = /url\\((['\"]?)([^'\"]+?)\\1\\)/g;\nconst URL_WITH_FORMAT_REGEX = /url\\([^)]+\\)\\s*format\\(([\"']?)([^\"']+)\\1\\)/g;\nconst FONT_SRC_REGEX = /src:\\s*(?:url\\([^)]+\\)\\s*format\\([^)]+\\)[,;]\\s*)+/g;\nfunction toRegex(url) {\n // eslint-disable-next-line no-useless-escape\n const escaped = url.replace(/([.*+?^${}()|\\[\\]\\/\\\\])/g, '\\\\$1');\n return new RegExp(`(url\\\\(['\"]?)(${escaped})(['\"]?\\\\))`, 'g');\n}\nexport function parseURLs(cssText) {\n const urls = [];\n cssText.replace(URL_REGEX, (raw, quotation, url) => {\n urls.push(url);\n return raw;\n });\n return urls.filter((url) => !isDataUrl(url));\n}\nexport async function embed(cssText, resourceURL, baseURL, options, getContentFromUrl) {\n try {\n const resolvedURL = baseURL ? resolveUrl(resourceURL, baseURL) : resourceURL;\n const contentType = getMimeType(resourceURL);\n let dataURL;\n if (getContentFromUrl) {\n const content = await getContentFromUrl(resolvedURL);\n dataURL = makeDataUrl(content, contentType);\n }\n else {\n dataURL = await resourceToDataURL(resolvedURL, contentType, options);\n }\n return cssText.replace(toRegex(resourceURL), `$1${dataURL}$3`);\n }\n catch (error) {\n // pass\n }\n return cssText;\n}\nfunction filterPreferredFontFormat(str, { preferredFontFormat }) {\n return !preferredFontFormat\n ? str\n : str.replace(FONT_SRC_REGEX, (match) => {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match) || [];\n if (!format) {\n return '';\n }\n if (format === preferredFontFormat) {\n return `src: ${src};`;\n }\n }\n });\n}\nexport function shouldEmbed(url) {\n return url.search(URL_REGEX) !== -1;\n}\nexport async function embedResources(cssText, baseUrl, options) {\n if (!shouldEmbed(cssText)) {\n return cssText;\n }\n const filteredCSSText = filterPreferredFontFormat(cssText, options);\n const urls = parseURLs(filteredCSSText);\n return urls.reduce((deferred, url) => deferred.then((css) => embed(css, url, baseUrl, options)), Promise.resolve(filteredCSSText));\n}\n//# sourceMappingURL=embed-resources.js.map","import { embedResources } from './embed-resources';\nimport { toArray, isInstanceOfElement } from './util';\nimport { isDataUrl, resourceToDataURL } from './dataurl';\nimport { getMimeType } from './mimes';\nasync function embedProp(propName, node, options) {\n var _a;\n const propValue = (_a = node.style) === null || _a === void 0 ? void 0 : _a.getPropertyValue(propName);\n if (propValue) {\n const cssString = await embedResources(propValue, null, options);\n node.style.setProperty(propName, cssString, node.style.getPropertyPriority(propName));\n return true;\n }\n return false;\n}\nasync function embedBackground(clonedNode, options) {\n ;\n (await embedProp('background', clonedNode, options)) ||\n (await embedProp('background-image', clonedNode, options));\n (await embedProp('mask', clonedNode, options)) ||\n (await embedProp('-webkit-mask', clonedNode, options)) ||\n (await embedProp('mask-image', clonedNode, options)) ||\n (await embedProp('-webkit-mask-image', clonedNode, options));\n}\nasync function embedImageNode(clonedNode, options) {\n const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement);\n if (!(isImageElement && !isDataUrl(clonedNode.src)) &&\n !(isInstanceOfElement(clonedNode, SVGImageElement) &&\n !isDataUrl(clonedNode.href.baseVal))) {\n return;\n }\n const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal;\n const dataURL = await resourceToDataURL(url, getMimeType(url), options);\n await new Promise((resolve, reject) => {\n clonedNode.onload = resolve;\n clonedNode.onerror = options.onImageErrorHandler\n ? (...attributes) => {\n try {\n resolve(options.onImageErrorHandler(...attributes));\n }\n catch (error) {\n reject(error);\n }\n }\n : reject;\n const image = clonedNode;\n if (image.decode) {\n image.decode = resolve;\n }\n if (image.loading === 'lazy') {\n image.loading = 'eager';\n }\n if (isImageElement) {\n clonedNode.srcset = '';\n clonedNode.src = dataURL;\n }\n else {\n clonedNode.href.baseVal = dataURL;\n }\n });\n}\nasync function embedChildren(clonedNode, options) {\n const children = toArray(clonedNode.childNodes);\n const deferreds = children.map((child) => embedImages(child, options));\n await Promise.all(deferreds).then(() => clonedNode);\n}\nexport async function embedImages(clonedNode, options) {\n if (isInstanceOfElement(clonedNode, Element)) {\n await embedBackground(clonedNode, options);\n await embedImageNode(clonedNode, options);\n await embedChildren(clonedNode, options);\n }\n}\n//# sourceMappingURL=embed-images.js.map","export function applyStyle(node, options) {\n const { style } = node;\n if (options.backgroundColor) {\n style.backgroundColor = options.backgroundColor;\n }\n if (options.width) {\n style.width = `${options.width}px`;\n }\n if (options.height) {\n style.height = `${options.height}px`;\n }\n const manual = options.style;\n if (manual != null) {\n Object.keys(manual).forEach((key) => {\n style[key] = manual[key];\n });\n }\n return node;\n}\n//# sourceMappingURL=apply-style.js.map","import { toArray } from './util';\nimport { fetchAsDataURL } from './dataurl';\nimport { shouldEmbed, embedResources } from './embed-resources';\nconst cssFetchCache = {};\nasync function fetchCSS(url) {\n let cache = cssFetchCache[url];\n if (cache != null) {\n return cache;\n }\n const res = await fetch(url);\n const cssText = await res.text();\n cache = { url, cssText };\n cssFetchCache[url] = cache;\n return cache;\n}\nasync function embedFonts(data, options) {\n let cssText = data.cssText;\n const regexUrl = /url\\([\"']?([^\"')]+)[\"']?\\)/g;\n const fontLocs = cssText.match(/url\\([^)]+\\)/g) || [];\n const loadFonts = fontLocs.map(async (loc) => {\n let url = loc.replace(regexUrl, '$1');\n if (!url.startsWith('https://')) {\n url = new URL(url, data.url).href;\n }\n return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {\n cssText = cssText.replace(loc, `url(${result})`);\n return [loc, result];\n });\n });\n return Promise.all(loadFonts).then(() => cssText);\n}\nfunction parseCSS(source) {\n if (source == null) {\n return [];\n }\n const result = [];\n const commentsRegex = /(\\/\\*[\\s\\S]*?\\*\\/)/gi;\n // strip out comments\n let cssText = source.replace(commentsRegex, '');\n // eslint-disable-next-line prefer-regex-literals\n const keyframesRegex = new RegExp('((@.*?keyframes [\\\\s\\\\S]*?){([\\\\s\\\\S]*?}\\\\s*?)})', 'gi');\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const matches = keyframesRegex.exec(cssText);\n if (matches === null) {\n break;\n }\n result.push(matches[0]);\n }\n cssText = cssText.replace(keyframesRegex, '');\n const importRegex = /@import[\\s\\S]*?url\\([^)]*\\)[\\s\\S]*?;/gi;\n // to match css & media queries together\n const combinedCSSRegex = '((\\\\s*?(?:\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\/)?\\\\s*?@media[\\\\s\\\\S]' +\n '*?){([\\\\s\\\\S]*?)}\\\\s*?})|(([\\\\s\\\\S]*?){([\\\\s\\\\S]*?)})';\n // unified regex\n const unifiedRegex = new RegExp(combinedCSSRegex, 'gi');\n // eslint-disable-next-line no-constant-condition\n while (true) {\n let matches = importRegex.exec(cssText);\n if (matches === null) {\n matches = unifiedRegex.exec(cssText);\n if (matches === null) {\n break;\n }\n else {\n importRegex.lastIndex = unifiedRegex.lastIndex;\n }\n }\n else {\n unifiedRegex.lastIndex = importRegex.lastIndex;\n }\n result.push(matches[0]);\n }\n return result;\n}\nasync function getCSSRules(styleSheets, options) {\n const ret = [];\n const deferreds = [];\n // First loop inlines imports\n styleSheets.forEach((sheet) => {\n if ('cssRules' in sheet) {\n try {\n toArray(sheet.cssRules || []).forEach((item, index) => {\n if (item.type === CSSRule.IMPORT_RULE) {\n let importIndex = index + 1;\n const url = item.href;\n const deferred = fetchCSS(url)\n .then((metadata) => embedFonts(metadata, options))\n .then((cssText) => parseCSS(cssText).forEach((rule) => {\n try {\n sheet.insertRule(rule, rule.startsWith('@import')\n ? (importIndex += 1)\n : sheet.cssRules.length);\n }\n catch (error) {\n console.error('Error inserting rule from remote css', {\n rule,\n error,\n });\n }\n }))\n .catch((e) => {\n console.error('Error loading remote css', e.toString());\n });\n deferreds.push(deferred);\n }\n });\n }\n catch (e) {\n const inline = styleSheets.find((a) => a.href == null) || document.styleSheets[0];\n if (sheet.href != null) {\n deferreds.push(fetchCSS(sheet.href)\n .then((metadata) => embedFonts(metadata, options))\n .then((cssText) => parseCSS(cssText).forEach((rule) => {\n inline.insertRule(rule, inline.cssRules.length);\n }))\n .catch((err) => {\n console.error('Error loading remote stylesheet', err);\n }));\n }\n console.error('Error inlining remote css file', e);\n }\n }\n });\n return Promise.all(deferreds).then(() => {\n // Second loop parses rules\n styleSheets.forEach((sheet) => {\n if ('cssRules' in sheet) {\n try {\n toArray(sheet.cssRules || []).forEach((item) => {\n ret.push(item);\n });\n }\n catch (e) {\n console.error(`Error while reading CSS rules from ${sheet.href}`, e);\n }\n }\n });\n return ret;\n });\n}\nfunction getWebFontRules(cssRules) {\n return cssRules\n .filter((rule) => rule.type === CSSRule.FONT_FACE_RULE)\n .filter((rule) => shouldEmbed(rule.style.getPropertyValue('src')));\n}\nasync function parseWebFontRules(node, options) {\n if (node.ownerDocument == null) {\n throw new Error('Provided element is not within a Document');\n }\n const styleSheets = toArray(node.ownerDocument.styleSheets);\n const cssRules = await getCSSRules(styleSheets, options);\n return getWebFontRules(cssRules);\n}\nfunction normalizeFontFamily(font) {\n return font.trim().replace(/[\"']/g, '');\n}\nfunction getUsedFonts(node) {\n const fonts = new Set();\n function traverse(node) {\n const fontFamily = node.style.fontFamily || getComputedStyle(node).fontFamily;\n fontFamily.split(',').forEach((font) => {\n fonts.add(normalizeFontFamily(font));\n });\n Array.from(node.children).forEach((child) => {\n if (child instanceof HTMLElement) {\n traverse(child);\n }\n });\n }\n traverse(node);\n return fonts;\n}\nexport async function getWebFontCSS(node, options) {\n const rules = await parseWebFontRules(node, options);\n const usedFonts = getUsedFonts(node);\n const cssTexts = await Promise.all(rules\n .filter((rule) => usedFonts.has(normalizeFontFamily(rule.style.fontFamily)))\n .map((rule) => {\n const baseUrl = rule.parentStyleSheet\n ? rule.parentStyleSheet.href\n : null;\n return embedResources(rule.cssText, baseUrl, options);\n }));\n return cssTexts.join('\\n');\n}\nexport async function embedWebFonts(clonedNode, options) {\n const cssText = options.fontEmbedCSS != null\n ? options.fontEmbedCSS\n : options.skipFonts\n ? null\n : await getWebFontCSS(clonedNode, options);\n if (cssText) {\n const styleNode = document.createElement('style');\n const sytleContent = document.createTextNode(cssText);\n styleNode.appendChild(sytleContent);\n if (clonedNode.firstChild) {\n clonedNode.insertBefore(styleNode, clonedNode.firstChild);\n }\n else {\n clonedNode.appendChild(styleNode);\n }\n }\n}\n//# sourceMappingURL=embed-webfonts.js.map","import { cloneNode } from './clone-node';\nimport { embedImages } from './embed-images';\nimport { applyStyle } from './apply-style';\nimport { embedWebFonts, getWebFontCSS } from './embed-webfonts';\nimport { getImageSize, getPixelRatio, createImage, canvasToBlob, nodeToDataURL, checkCanvasDimensions, } from './util';\nexport async function toSvg(node, options = {}) {\n const { width, height } = getImageSize(node, options);\n const clonedNode = (await cloneNode(node, options, true));\n await embedWebFonts(clonedNode, options);\n await embedImages(clonedNode, options);\n applyStyle(clonedNode, options);\n const datauri = await nodeToDataURL(clonedNode, width, height);\n return datauri;\n}\nexport async function toCanvas(node, options = {}) {\n const { width, height } = getImageSize(node, options);\n const svg = await toSvg(node, options);\n const img = await createImage(svg);\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n const ratio = options.pixelRatio || getPixelRatio();\n const canvasWidth = options.canvasWidth || width;\n const canvasHeight = options.canvasHeight || height;\n canvas.width = canvasWidth * ratio;\n canvas.height = canvasHeight * ratio;\n if (!options.skipAutoScale) {\n checkCanvasDimensions(canvas);\n }\n canvas.style.width = `${canvasWidth}`;\n canvas.style.height = `${canvasHeight}`;\n if (options.backgroundColor) {\n context.fillStyle = options.backgroundColor;\n context.fillRect(0, 0, canvas.width, canvas.height);\n }\n context.drawImage(img, 0, 0, canvas.width, canvas.height);\n return canvas;\n}\nexport async function toPixelData(node, options = {}) {\n const { width, height } = getImageSize(node, options);\n const canvas = await toCanvas(node, options);\n const ctx = canvas.getContext('2d');\n return ctx.getImageData(0, 0, width, height).data;\n}\nexport async function toPng(node, options = {}) {\n const canvas = await toCanvas(node, options);\n return canvas.toDataURL();\n}\nexport async function toJpeg(node, options = {}) {\n const canvas = await toCanvas(node, options);\n return canvas.toDataURL('image/jpeg', options.quality || 1);\n}\nexport async function toBlob(node, options = {}) {\n const canvas = await toCanvas(node, options);\n const blob = await canvasToBlob(canvas);\n return blob;\n}\nexport async function getFontEmbedCSS(node, options = {}) {\n return getWebFontCSS(node, options);\n}\n//# sourceMappingURL=index.js.map","import type { Annotation } from '../types'\nimport { parsePathPolylines } from './capture'\n\nexport interface CropRect {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nconst MIN_CROP_WIDTH = 400\nconst MIN_CROP_HEIGHT = 300\nconst PADDING_PX = 150\nconst PADDING_RATIO = 0.3\nconst SKIP_CROP_RATIO = 0.85\n\nexport function getAnnotationBounds(annotations: Annotation[]): Bounds | null {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n\n for (const ann of annotations) {\n if (ann.type === 'pin') {\n minX = Math.min(minX, ann.x)\n minY = Math.min(minY, ann.y)\n maxX = Math.max(maxX, ann.x)\n maxY = Math.max(maxY, ann.y)\n if (ann.targetRect) {\n const r = ann.targetRect\n minX = Math.min(minX, r.left)\n minY = Math.min(minY, r.top)\n maxX = Math.max(maxX, r.left + r.width)\n maxY = Math.max(maxY, r.bottom)\n }\n } else if (ann.type === 'draw' && ann.drawPath) {\n for (const polyline of parsePathPolylines(ann.drawPath)) {\n for (const [x, y] of polyline) {\n minX = Math.min(minX, x)\n minY = Math.min(minY, y)\n maxX = Math.max(maxX, x)\n maxY = Math.max(maxY, y)\n }\n }\n }\n }\n\n if (!isFinite(minX) || !isFinite(minY)) return null\n return { minX, minY, maxX, maxY }\n}\n\nexport function computeCropRegion(\n annotations: Annotation[],\n viewportWidth: number,\n viewportHeight: number,\n): CropRect | null {\n const bounds = getAnnotationBounds(annotations)\n if (!bounds) return null\n\n const padX = Math.max(PADDING_PX, viewportWidth * PADDING_RATIO)\n const padY = Math.max(PADDING_PX, viewportHeight * PADDING_RATIO)\n\n let x = bounds.minX - padX\n let y = bounds.minY - padY\n let w = (bounds.maxX - bounds.minX) + padX * 2\n let h = (bounds.maxY - bounds.minY) + padY * 2\n\n if (w < MIN_CROP_WIDTH) {\n x -= (MIN_CROP_WIDTH - w) / 2\n w = MIN_CROP_WIDTH\n }\n if (h < MIN_CROP_HEIGHT) {\n y -= (MIN_CROP_HEIGHT - h) / 2\n h = MIN_CROP_HEIGHT\n }\n\n w = Math.min(w, viewportWidth)\n h = Math.min(h, viewportHeight)\n x = Math.min(Math.max(0, x), viewportWidth - w)\n y = Math.min(Math.max(0, y), viewportHeight - h)\n\n if (w >= viewportWidth * SKIP_CROP_RATIO && h >= viewportHeight * SKIP_CROP_RATIO) {\n return null\n }\n\n return {\n x: Math.round(x),\n y: Math.round(y),\n width: Math.round(w),\n height: Math.round(h),\n }\n}\n\nexport function cropCanvas(source: HTMLCanvasElement, rect: CropRect): HTMLCanvasElement {\n const cropped = document.createElement('canvas')\n cropped.width = rect.width\n cropped.height = rect.height\n const ctx = cropped.getContext('2d')\n if (ctx) {\n ctx.drawImage(source, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height)\n }\n return cropped\n}\n\nexport async function cropDataUrl(dataUrl: string, rect: CropRect): Promise<string> {\n const image = await loadImageForCrop(dataUrl)\n if (!image) return dataUrl\n const canvas = document.createElement('canvas')\n canvas.width = rect.width\n canvas.height = rect.height\n const ctx = canvas.getContext('2d')\n if (!ctx) return dataUrl\n ctx.drawImage(image, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height)\n return canvas.toDataURL('image/jpeg', 0.85)\n}\n\nfunction loadImageForCrop(dataUrl: string): Promise<HTMLImageElement | null> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = () => resolve(null)\n img.src = dataUrl\n })\n}\n","import { toJpeg } from 'html-to-image'\nimport type { Annotation } from '../types'\nimport { BLOCK_CLASS, MASK_TEXT_CLASS } from '../record/session'\nimport { filterInputValue, isSensitiveField } from '../privacy/fields'\nimport { filterSensitiveText } from '../privacy/sanitize'\nimport { createSensitiveTextContext, hasSensitiveTextContext } from '../privacy/text-context'\nimport { BRAND_COLOR, FONT_STACK, brandAlpha } from '../styles'\nimport { computeCropRegion, cropCanvas, getAnnotationBounds } from './crop'\nimport type { CropRect } from './crop'\n\ninterface CaptureMetrics {\n left: number\n top: number\n width: number\n height: number\n}\n\ninterface RedactionRect {\n left: number\n top: number\n width: number\n height: number\n}\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. Pages tall enough to exceed the\n// browser's canvas ceiling go below 1:1 — see capturePixelRatio.\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 async function captureScreenshot(annotations: Annotation[] = []): Promise<Blob | null> {\n try {\n const captureMetrics = getCaptureMetrics(document.body)\n const screenshotAnnotations = annotations.map(translateAnnotationForScreenshot)\n const redactions = collectPrivacyRedactions(document.body)\n const dataUrl = await toJpeg(document.body, {\n quality: 0.85,\n width: captureMetrics.width,\n height: captureMetrics.height,\n pixelRatio: capturePixelRatio(captureMetrics),\n backgroundColor: '#ffffff',\n skipFonts: false,\n skipAutoScale: false,\n filter: (node) => {\n if (node instanceof HTMLElement && node.id === 'mtb-widget-host') return false\n return true\n },\n })\n return await bakeScreenshot(dataUrl, screenshotAnnotations, captureMetrics, redactions)\n } catch {\n return null\n }\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.\nfunction translateAnnotationForScreenshot(annotation: Annotation): Annotation {\n const offset = nestedScrollOffset(findAnnotationTarget(annotation))\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 && translateDrawPath(annotation.drawPath, offset),\n }\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\nfunction nestedScrollOffset(target: Element | null): { x: number; y: number } {\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\nfunction 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\nfunction translateDrawPath(drawPath: string, offset: { x: number; y: number }): string {\n return drawPath.replace(/([ML])\\s*(-?(?:\\d+\\.?\\d*|\\.\\d+))\\s+(-?(?:\\d+\\.?\\d*|\\.\\d+))/g, (_match, command: string, x: string, y: string) => {\n return `${command} ${Number(x) + offset.x} ${Number(y) + offset.y}`\n })\n}\n\nasync function bakeScreenshot(\n dataUrl: string,\n annotations: Annotation[],\n captureMetrics: CaptureMetrics,\n redactions: RedactionRect[],\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 scaleX = canvas.width / captureMetrics.width\n const scaleY = canvas.height / captureMetrics.height\n\n ctx.fillStyle = '#e5e7eb'\n for (const rect of redactions) {\n ctx.fillRect(\n (rect.left - captureMetrics.left) * scaleX,\n (rect.top - captureMetrics.top) * scaleY,\n rect.width * scaleX,\n rect.height * scaleY,\n )\n }\n\n let pinIndex = 0\n for (const annotation of annotations) {\n if (annotation.type === 'draw' && annotation.drawPath) {\n drawStroke(ctx, annotation.drawPath, captureMetrics, scaleX, scaleY)\n } else if (annotation.type === 'pin') {\n drawPin(\n ctx,\n (annotation.x - captureMetrics.left) * scaleX,\n (annotation.y - captureMetrics.top) * scaleY,\n ++pinIndex,\n )\n }\n }\n\n if (!annotationsFitCanvas(annotations, captureMetrics, scaleX, scaleY, canvas)) {\n return canvasToJpegBlob(canvas)\n }\n\n const cropRect = computeCropRegion(annotations, window.innerWidth, window.innerHeight)\n if (cropRect) {\n const scaled = fitCropToCanvas(cropRect, captureMetrics, scaleX, scaleY, canvas)\n if (!scaled) return canvasToJpegBlob(canvas)\n return canvasToJpegBlob(cropCanvas(canvas, scaled))\n }\n\n return canvasToJpegBlob(canvas)\n}\n\nfunction annotationsFitCanvas(\n annotations: Annotation[],\n captureMetrics: CaptureMetrics,\n scaleX: number,\n scaleY: number,\n canvas: HTMLCanvasElement,\n): boolean {\n const bounds = getAnnotationBounds(annotations)\n if (!bounds) return true\n\n const left = (bounds.minX - captureMetrics.left) * scaleX\n const top = (bounds.minY - captureMetrics.top) * scaleY\n const right = (bounds.maxX - captureMetrics.left) * scaleX\n const bottom = (bounds.maxY - captureMetrics.top) * scaleY\n\n return left >= 0 && top >= 0 && right <= canvas.width && bottom <= canvas.height\n}\n\nfunction fitCropToCanvas(\n cropRect: CropRect,\n captureMetrics: CaptureMetrics,\n scaleX: number,\n scaleY: number,\n canvas: HTMLCanvasElement,\n): CropRect | null {\n const width = Math.min(canvas.width, Math.round(cropRect.width * scaleX))\n const height = Math.min(canvas.height, Math.round(cropRect.height * scaleY))\n if (width <= 0 || height <= 0) return null\n\n const requestedLeft = Math.round((cropRect.x - captureMetrics.left) * scaleX)\n const requestedTop = Math.round((cropRect.y - captureMetrics.top) * scaleY)\n\n return {\n x: Math.min(Math.max(0, requestedLeft), canvas.width - width),\n y: Math.min(Math.max(0, requestedTop), canvas.height - height),\n width,\n height,\n }\n}\n\nfunction collectPrivacyRedactions(root: HTMLElement): RedactionRect[] {\n const redactions: RedactionRect[] = []\n for (const element of elementsIncludingRoot(root, `.${BLOCK_CLASS}`)) {\n addRedaction(redactions, element.getBoundingClientRect())\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(), 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 const range = document.createRange()\n range.selectNodeContents(node)\n for (const rect of Array.from(range.getClientRects())) {\n addRedaction(redactions, rect, REDACTION_PADDING_PX)\n }\n }\n node = walker.nextNode()\n }\n\n return redactions\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('#mtb-widget-host') !== null || element.closest(`.${BLOCK_CLASS}`) !== null\n}\n\nfunction addRedaction(redactions: RedactionRect[], rect: DOMRect, padding = 0): 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 - padding,\n top: rect.top - 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 captureMetrics: CaptureMetrics,\n scaleX: number,\n scaleY: number,\n): void {\n ctx.save()\n ctx.strokeStyle = BRAND_COLOR\n ctx.lineWidth = 3.5\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n ctx.shadowColor = brandAlpha(0.35)\n ctx.shadowOffsetY = 1\n ctx.shadowBlur = 2\n for (const points of parsePathPolylines(drawPath)) {\n if (points.length === 0) continue\n ctx.beginPath()\n ctx.moveTo(\n (points[0][0] - captureMetrics.left) * scaleX,\n (points[0][1] - captureMetrics.top) * scaleY,\n )\n for (let i = 1; i < points.length; i++) {\n ctx.lineTo(\n (points[i][0] - captureMetrics.left) * scaleX,\n (points[i][1] - captureMetrics.top) * scaleY,\n )\n }\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 — on a long marketing page or an infinite feed that\n// is every iPhone report, and the caller's catch turns it into a silent\n// screenshot-less submission. Shrinking the whole page keeps a usable picture\n// where clipping would drop whatever the reporter was pointing at; bakeScreenshot\n// derives its scale from the returned bitmap, so annotations follow along.\nconst MAX_CAPTURE_PIXELS = 16_000_000\nconst REDACTION_PADDING_PX = 2\n\nfunction capturePixelRatio(metrics: CaptureMetrics): 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 getCaptureMetrics(node: HTMLElement): CaptureMetrics {\n const rect = node.getBoundingClientRect()\n const style = window.getComputedStyle(node)\n const borderWidth = parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth)\n const borderHeight = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth)\n const root = document.documentElement\n const width = Math.max(node.clientWidth, node.scrollWidth, root.clientWidth, root.scrollWidth)\n const height = Math.max(node.clientHeight, node.scrollHeight, root.clientHeight, root.scrollHeight)\n\n return {\n left: rect.left,\n top: rect.top,\n width: width + (Number.isFinite(borderWidth) ? borderWidth : 0) || window.innerWidth,\n height: height + (Number.isFinite(borderHeight) ? borderHeight : 0) || window.innerHeight,\n }\n}\n\nfunction drawPin(ctx: CanvasRenderingContext2D, x: number, y: number, index: number): void {\n ctx.save()\n ctx.shadowColor = brandAlpha(0.45)\n ctx.shadowOffsetY = 2\n ctx.shadowBlur = 8\n ctx.fillStyle = BRAND_COLOR\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.fillStyle = '#ffffff'\n ctx.font = `700 10px ${FONT_STACK}`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n ctx.fillText(String(index), x, y)\n ctx.restore()\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\nfunction 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\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\ntype NavListener = () => 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\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(): void {\n // Copy first: a listener may unsubscribe (or subscribe) while being notified.\n for (const listener of [...listeners]) {\n try { listener() } catch { /* never propagate into the host page */ }\n }\n}\n\nfunction install(): void {\n if (installedPushState) return\n\n popstateHandler = () => notify()\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 pushOrig.call(history, data, unused, url)\n notify()\n }\n installedReplaceState = function (data: unknown, unused: string, url?: string | URL | null) {\n replaceOrig.call(history, data, unused, url)\n notify()\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}\n","import { errorSource, type ErrorSourceEvent } from './error-source'\nimport { summarizeError } from './error-summary'\nimport { getCssSelector, getElementName } from '../annotate/highlighter'\nimport { 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 { 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 = 3\nconst RAGE_CLICK_WINDOW_MS = 1000\nconst RAPID_NAV_THRESHOLD = 3\nconst RAPID_NAV_WINDOW_MS = 5000\nconst FORM_CHECK_DELAY_MS = 500\nconst DEAD_CLICK_DOM_DELAY_MS = 500\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\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 errorCount = 0\n private deadClickCount = 0\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 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 errorPageUrls = new Set<string>()\n private domObserver: MutationObserver | null = null\n private pendingDomChecks = 0\n private lastMutationAt = -1\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((_e) => {\n this.errorCount++\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\n this.stopNavigationTracking()\n this.stopFormTracking()\n this.errorPageUrls.clear()\n\n this.disconnectDomObserver()\n }\n\n private startNavigationTracking(): void {\n this.unsubscribeNavigation = onHistoryNavigation(() => this.handleNavigation())\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 }\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 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 }\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\n const now = Date.now()\n this.clickHistory.push({ target, 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 === target)\n if (sameTargetClicks.length >= RAGE_CLICK_THRESHOLD) {\n this.clickHistory = []\n this.emitHighPriority({\n signal: 'rage_click',\n target: describeElement(target),\n timestamp: now,\n })\n return\n }\n\n if (!this.isInteractive(target)) {\n this.deadClickCount++\n if (this.deadClickCount >= 3 && this.errorCount > 0) {\n this.deadClickCount = 0\n this.errorCount = 0\n this.emitIfReady({\n signal: 'dead_click',\n target: describeElement(target),\n timestamp: now,\n })\n }\n }\n\n this.checkDeadClickDom(target, now)\n }\n\n private handleNavigation(): void {\n const now = Date.now()\n this.noteNavigationStarted()\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 this.persistState()\n setTimeout(() => {\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 setTimeout(() => {\n if (!this.active) return\n if (form.querySelector('[aria-invalid=\"true\"], .is-invalid')) {\n this.emitIfReady({\n signal: 'form_failure',\n target: describeElement(form),\n timestamp: Date.now(),\n })\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 rememberSubmitAttempt(form: HTMLFormElement): void {\n this.recentSubmitForms.add(form)\n setTimeout(() => {\n this.recentSubmitForms.delete(form)\n }, FORM_CHECK_DELAY_MS)\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.emitIfReady({\n signal: 'form_failure',\n target: describeElement(form),\n timestamp: Date.now(),\n })\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 setTimeout(() => {\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.body, {\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 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 isInteractive(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 if ((el as HTMLElement).contentEditable === 'true') return true\n\n const style = window.getComputedStyle(el)\n if (style.cursor === 'pointer') return true\n\n const parent = el.closest('a, button, [role=\"button\"], [role=\"link\"], label')\n if (parent) return true\n\n return false\n }\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 { 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 function buildPayload(\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): FeedbackPayload {\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 reporter_external_id: user ? undefined : anonId,\n reporter_email: user ? undefined : anonEmail,\n }\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 cancel: 'Cancel',\n my_feedback: 'My feedback',\n quickOptions: [\n { emoji: '🐛', label: \"Something's broken\" },\n { emoji: '💡', label: 'I have a suggestion' },\n { emoji: '🤔', label: \"I'm confused\" },\n ],\n },\n success: {\n title: 'Sent — thanks!',\n message: 'Your note and captured technical context are on their way to the team. You’ll hear back when it’s resolved.',\n title_no_ai: \"Thanks! We've received your feedback.\",\n message_no_ai: \"The team will take it from here. You'll hear back when it's resolved.\",\n view_feedback: 'View my feedback',\n close: 'Close',\n email_prompt: \"Want updates? Leave your email and we'll let you know when it's resolved.\",\n email_placeholder: 'you@example.com',\n email_submit: 'Notify me',\n email_saved: \"You'll get an email when it's resolved.\",\n email_error: \"Couldn't save your email. Please try again.\",\n },\n error: {\n submit: 'Failed to submit. 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: 'Type your answer — or skip the question',\n send: 'Send',\n send_feedback: 'Send feedback',\n skip: 'Skip & send',\n retry: 'Retry',\n error: 'AI clarification failed. Please try again.',\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 cancel: '取消',\n my_feedback: '我的反馈',\n quickOptions: [\n { emoji: '🐛', label: '出问题了' },\n { emoji: '💡', label: '我有一个建议' },\n { emoji: '🤔', label: '我有点困惑' },\n ],\n },\n success: {\n title: '已发送,感谢!',\n message: '您的反馈和采集到的技术上下文正在发送给团队。问题解决后,您会收到通知。',\n title_no_ai: '感谢!我们已收到您的反馈。',\n message_no_ai: '接下来由团队处理。问题解决后,您会收到通知。',\n view_feedback: '查看我的反馈',\n close: '关闭',\n email_prompt: '想了解进展?留下邮箱,问题解决后我们会通知您。',\n email_placeholder: 'you@example.com',\n email_submit: '通知我',\n email_saved: '问题解决后您会收到邮件通知。',\n email_error: '邮箱保存失败,请重试。',\n },\n error: {\n submit: '提交失败,请重试。',\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 send_feedback: '发送反馈',\n skip: '跳过并发送',\n retry: '重试',\n error: 'AI 澄清失败,请重试。',\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 cancel: 'キャンセル',\n my_feedback: '自分のフィードバック',\n quickOptions: [\n { emoji: '🐛', label: '不具合があります' },\n { emoji: '💡', label: '提案があります' },\n { emoji: '🤔', label: 'わかりにくいです' },\n ],\n },\n success: {\n title: '送信しました。ありがとうございます!',\n message: 'ご意見と取得した技術情報をチームに送信しました。解決次第お知らせします。',\n title_no_ai: 'ありがとうございます!フィードバックを受け付けました。',\n message_no_ai: 'チームが対応します。解決次第お知らせします。',\n view_feedback: 'フィードバックを確認',\n close: '閉じる',\n email_prompt: '進捗を知りたい場合は、メールアドレスをご記入ください。解決次第お知らせします。',\n email_placeholder: 'you@example.com',\n email_submit: '通知を受け取る',\n email_saved: '解決次第メールでお知らせします。',\n email_error: 'メールアドレスを保存できませんでした。もう一度お試しください。',\n },\n error: {\n submit: '送信に失敗しました。もう一度お試しください。',\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 send_feedback: 'フィードバックを送信',\n skip: 'スキップして送信',\n retry: '再試行',\n error: 'AIによる確認に失敗しました。もう一度お試しください。',\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 cancel: '취소',\n my_feedback: '내 피드백',\n quickOptions: [\n { emoji: '🐛', label: '문제가 있어요' },\n { emoji: '💡', label: '제안이 있어요' },\n { emoji: '🤔', label: '헷갈려요' },\n ],\n },\n success: {\n title: '전송되었습니다. 감사합니다!',\n message: '피드백과 수집된 기술 정보가 팀에 전달되었습니다. 해결되면 알려드리겠습니다.',\n title_no_ai: '감사합니다! 피드백을 접수했습니다.',\n message_no_ai: '팀에서 처리합니다. 해결되면 알려드리겠습니다.',\n view_feedback: '내 피드백 보기',\n close: '닫기',\n email_prompt: '진행 상황이 궁금하신가요? 이메일을 남겨주시면 해결 시 알려드립니다.',\n email_placeholder: 'you@example.com',\n email_submit: '알림 받기',\n email_saved: '해결되면 이메일로 알려드립니다.',\n email_error: '이메일을 저장하지 못했습니다. 다시 시도해 주세요.',\n },\n error: {\n submit: '제출에 실패했습니다. 다시 시도해 주세요.',\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 send_feedback: '피드백 보내기',\n skip: '건너뛰고 보내기',\n retry: '재시도',\n error: 'AI 확인에 실패했습니다. 다시 시도해 주세요.',\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 cancel: 'Cancelar',\n my_feedback: 'Mis comentarios',\n quickOptions: [\n { emoji: '🐛', label: 'Algo no funciona' },\n { emoji: '💡', label: 'Tengo una sugerencia' },\n { emoji: '🤔', label: 'Estoy confundido' },\n ],\n },\n success: {\n title: 'Enviado, ¡gracias!',\n message: 'Tu nota y el contexto técnico capturado se han enviado al equipo. Te avisaremos cuando se resuelva.',\n title_no_ai: '¡Gracias! Hemos recibido tu comentario.',\n message_no_ai: 'El equipo se encargará a partir de aquí. Te avisaremos cuando se resuelva.',\n view_feedback: 'Ver mis comentarios',\n close: 'Cerrar',\n email_prompt: '¿Quieres novedades? Deja tu correo y te avisaremos cuando se resuelva.',\n email_placeholder: 'tu@ejemplo.com',\n email_submit: 'Avisarme',\n email_saved: 'Recibirás un correo cuando se resuelva.',\n email_error: 'No se pudo guardar tu correo. Inténtalo de nuevo.',\n },\n error: {\n submit: 'Error al enviar. Por favor, 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: 'Escribe tu respuesta — u omite la pregunta',\n send: 'Enviar',\n send_feedback: 'Enviar comentario',\n skip: 'Omitir y enviar',\n retry: 'Reintentar',\n error: 'No se pudo completar la aclaración con IA. Inténtalo de nuevo.',\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 cancel: 'Annuler',\n my_feedback: 'Mes avis',\n quickOptions: [\n { emoji: '🐛', label: 'Quelque chose ne fonctionne pas' },\n { emoji: '💡', label: \"J'ai une suggestion\" },\n { emoji: '🤔', label: 'Je suis perplexe' },\n ],\n },\n success: {\n title: 'Envoyé, merci !',\n message: 'Votre message et le contexte technique collecté ont été transmis à l’équipe. Vous serez informé(e) dès que ce sera résolu.',\n title_no_ai: 'Merci ! Nous avons bien reçu votre avis.',\n message_no_ai: \"L’équipe prend le relais. Vous serez informé(e) dès que ce sera résolu.\",\n view_feedback: 'Voir mes avis',\n close: 'Fermer',\n email_prompt: \"Envie de suivre l'avancement ? Laissez votre e-mail et nous vous préviendrons une fois résolu.\",\n email_placeholder: 'vous@exemple.com',\n email_submit: 'Me prévenir',\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 },\n error: {\n submit: \"Échec de l'envoi. 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: 'Saisissez votre réponse — ou ignorez la question',\n send: 'Envoyer',\n send_feedback: 'Envoyer le retour',\n skip: 'Ignorer et envoyer',\n retry: 'Réessayer',\n error: 'La clarification par IA a échoué. Réessayez.',\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 cancel: 'Abbrechen',\n my_feedback: 'Mein Feedback',\n quickOptions: [\n { emoji: '🐛', label: 'Etwas funktioniert nicht' },\n { emoji: '💡', label: 'Ich habe einen Vorschlag' },\n { emoji: '🤔', label: 'Ich bin verwirrt' },\n ],\n },\n success: {\n title: 'Gesendet, vielen Dank!',\n message: 'Ihre Nachricht und der erfasste technische Kontext wurden an das Team gesendet. Sie werden benachrichtigt, sobald das Problem gelöst ist.',\n title_no_ai: 'Vielen Dank! Wir haben Ihr Feedback erhalten.',\n message_no_ai: 'Das Team übernimmt ab hier. Sie werden benachrichtigt, sobald das Problem gelöst ist.',\n view_feedback: 'Mein Feedback ansehen',\n close: 'Schließen',\n email_prompt: 'Updates gewünscht? Hinterlassen Sie Ihre E-Mail und wir melden uns, sobald es gelöst ist.',\n email_placeholder: 'sie@beispiel.de',\n email_submit: 'Benachrichtigen',\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 },\n error: {\n submit: 'Senden fehlgeschlagen. 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: 'Antwort eingeben — oder Frage überspringen',\n send: 'Senden',\n send_feedback: 'Feedback senden',\n skip: 'Überspringen & senden',\n retry: 'Erneut versuchen',\n error: 'Die KI-Klärung ist fehlgeschlagen. Bitte erneut versuchen.',\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 type { I18nMessages } from '../i18n'\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\">${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 { ShadowContainer } from './shadow'\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 { AnnotationToolbar } from '../annotate/toolbar'\nimport type { ToolbarMode } from '../annotate/toolbar'\nimport { AnnotationSession } from '../annotate/session'\nimport { DrawNoteBar } from '../annotate/draw-note-bar'\nimport { CommentPopup } from '../popup/comment'\nimport { ClarifyCard } from '../popup/clarify'\nimport { SuccessCard } from '../popup/success'\nimport type { SuccessCardOptions } from '../popup/success'\nimport { captureScreenshot } 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 { collectPageContext } from '../context/collector'\nimport type { PageContext } from '../context/collector'\nimport { ApiClient } from '../api/client'\nimport { buildPayload } from './payload'\nimport { cacheBoardUrl, getAnonId, getCachedBoardUrl, getStoredReporterEmail, rememberReporterEmail } from './anon-id'\nimport { getMessages } from '../i18n'\nimport { RecordingManager } from './recording-manager'\nimport type { RecordingResult } from './recording-manager'\nimport type { MakeThisBetterConfig, Annotation, SubmissionSessionResponse, TargetRect } from '../types'\n\ntype Mode = 'idle' | 'annotating' | 'recording' | 'drawing' | 'popup' | 'clarifying'\ntype Via = 'pin' | 'draw' | 'record'\n\n// Which surface collected the note. The clarify card shows progress and errors\n// from here on, so this is only ever asked whether a submission is in hand — it\n// carries no progress contract of its own.\ntype SubmittingUi = CommentPopup | DrawNoteBar\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 {\n private config: MakeThisBetterConfig\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 popup: CommentPopup | null = null\n private clarify: ClarifyCard | null = null\n private success: SuccessCard | 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 private mode: Mode = 'idle'\n private via: Via = 'pin'\n private currentAnnotation: Annotation | null = null\n private submittingUi: SubmittingUi | null = null\n private lastPopupPos: { x: number; y: number } | null = null\n private recordingManager: RecordingManager | null = null\n private activeSubmission: SubmissionSessionResponse | null = null\n private pendingSubmissionRequest: Promise<SubmissionSessionResponse | null> | null = null\n private submissionAttempt = 0\n private touchHintShown = false\n private lastHandoff: { feedbackId?: string; boardUrl?: string; identityToken?: string } = {}\n\n constructor(config: MakeThisBetterConfig) {\n this.config = config\n this.messages = getMessages(config.locale ?? document.documentElement.lang ?? 'en')\n this.apiClient = new ApiClient(config.projectKey, config.apiUrl, undefined, config.userToken, config.userTokenFn)\n this.consoleCollector = new ConsoleErrorCollector()\n this.consoleCollector.start()\n this.breadcrumbCollector = new BreadcrumbCollector()\n this.breadcrumbCollector.start()\n this.buildUI()\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 // Escape only acts while the widget is in an active state; idle/clarifying\n // fall through untouched so the host page's own Escape handling still works.\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 // Some browsers reset isComposing before the keydown reaches this capture\n // handler but retain the legacy IME sentinel in keyCode.\n if (event.isComposing || event.keyCode === 229) return\n\n switch (this.mode) {\n case 'annotating':\n case 'popup':\n event.preventDefault()\n event.stopPropagation()\n this.exitAll()\n break\n case 'drawing':\n event.preventDefault()\n event.stopPropagation()\n this.cancelDraw()\n break\n case 'recording':\n event.preventDefault()\n event.stopPropagation()\n this.handleRecordStop()\n break\n default:\n break\n }\n }\n\n private buildUI(): void {\n this.shadow = new ShadowContainer(this.config.theme ?? 'auto')\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 if (this.tab) return\n this.tab = new FeedbackTab(\n this.shadow,\n this.messages,\n this.config.position ?? 'right',\n () => this.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.mode !== 'idle') return\n this.dismissFrustrationPrompt()\n this.enterAnnotationMode()\n }\n\n close(): void {\n if (this.mode === 'idle') return\n this.exitAll()\n }\n\n showLauncher(): void {\n if (isTouchPointer()) return\n this.mountTab()\n this.tab?.setActive(this.mode !== 'idle')\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 handleTabClick(): void {\n if (this.mode === 'idle') {\n this.dismissFrustrationPrompt()\n this.enterAnnotationMode()\n } else {\n this.exitAll()\n }\n }\n\n private handleFrustration(): void {\n if (this.mode !== 'idle' || this.frustrationPrompt || isFrustrationDismissed()) return\n\n this.frustrationPrompt = new FrustrationPromptCard(this.shadow, this.messages, {\n onTell: () => {\n this.dismissFrustrationPrompt()\n this.enterAnnotationMode()\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 private dismissFrustrationPrompt(): void {\n this.frustrationPrompt?.destroy()\n this.frustrationPrompt = null\n }\n\n private enterAnnotationMode(): void {\n this.mode = 'annotating'\n this.tab?.setActive(true)\n const touch = isTouchPointer()\n\n this.toolbar = new AnnotationToolbar(\n this.shadow,\n this.messages,\n () => this.exitAll(),\n (mode: ToolbarMode) => this.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.session = this.createSession()\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 private createSession(): AnnotationSession {\n return new AnnotationSession(this.shadow, this.messages, {\n onPin: (annotation, targetName) => {\n this.currentAnnotation = annotation\n this.via = 'pin'\n this.showPopup(annotation.x, annotation.y, targetName, annotation.targetRect)\n },\n onDrawStroke: () => this.handleDrawStroke(),\n })\n }\n\n private handleToolbarModeChange(mode: ToolbarMode): void {\n if (this.mode === 'clarifying') {\n this.toolbar?.setMode(this.via === 'record' ? 'record' : 'markup')\n return\n }\n\n if (mode === 'record') {\n if (this.mode === 'recording') return\n\n this.mode = 'recording'\n this.currentAnnotation = null\n this.teardownDraw()\n this.session?.destroy()\n this.session = null\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.recordingManager = new RecordingManager()\n this.recordingManager.start(\n this.shadow,\n this.messages,\n this.handleRecordStop,\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 if (this.mode !== 'recording') return\n this.toolbar?.setMode('markup')\n this.handleToolbarModeChange('markup')\n })\n } else {\n this.recordingManager?.destroy()\n this.recordingManager = null\n this.teardownDraw()\n if (!this.session) this.session = this.createSession()\n this.mode = 'annotating'\n }\n }\n\n private handleRecordStop = (): void => {\n if (!this.recordingManager) return\n this.recordingManager.stop()\n this.via = 'record'\n this.showPopup(window.innerWidth / 2, window.innerHeight / 2, this.messages.record.timer_label)\n }\n\n private 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 if (isTouchPointer()) {\n this.showDrawSheet()\n return\n }\n\n if (this.mode !== 'drawing') {\n this.mode = 'drawing'\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 })\n }\n this.refreshDrawBar()\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 private showDrawSheet(): void {\n if (this.mode === 'drawing') return\n this.mode = 'drawing'\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\n this.popup = new CommentPopup(this.shadow, {\n targetName: this.messages.annotation.drawing,\n x: window.innerWidth / 2,\n y: window.innerHeight / 2,\n messages: this.messages,\n onSubmit: (description) => this.handleDrawSubmit(description),\n onClose: () => this.cancelDraw(),\n position: this.config.position ?? 'right',\n })\n this.submittingUi = this.popup\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 private refreshDrawBar(): void {\n if (!this.session) return\n this.drawBar?.setUndoRedo(this.session.canUndoDraw(), this.session.canRedoDraw())\n }\n\n private cancelDraw(): void {\n this.session?.clearDraw()\n this.teardownDraw()\n // On touch the sheet is the note surface, so cancelling the drawing has to\n // take the sheet and its scrim with it — teardownDraw only knows the bar.\n this.popup?.destroy()\n this.popup = null\n this.dim?.destroy()\n this.dim = null\n this.submittingUi = null\n this.mode = 'annotating'\n }\n\n private teardownDraw(): 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 // Whichever surface collected the note is the one that shows progress and\n // errors: the inline bar on a desktop, the sheet on touch.\n this.submittingUi = this.drawBar ?? this.popup\n void this.handleSubmit(note || this.messages.annotation.drawing)\n }\n\n private showPopup(x: number, y: number, targetName: string, targetRect?: TargetRect): void {\n this.mode = 'popup'\n this.lastPopupPos = { x, y }\n this.teardownDraw()\n this.session?.dismissInteraction()\n this.popup?.destroy()\n this.popup = null\n this.dim?.destroy()\n this.dim = null\n this.dim = new DimOverlay(this.shadow, () => this.exitAll())\n\n this.popup = new CommentPopup(this.shadow, {\n targetName,\n x,\n y,\n targetRect,\n messages: this.messages,\n onSubmit: (description) => this.handleSubmit(description),\n onClose: () => this.exitAll(),\n onMyFeedback: this.buildMyFeedbackHandler(),\n position: this.config.position ?? 'right',\n })\n this.submittingUi = this.popup\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 handleSubmit(description: string): void {\n if (!this.submittingUi) return\n // Captured before teardown: teardownSubmittingUi destroys the recording\n // manager and the annotation session, and the screenshot bakes the\n // annotation from this data rather than from the live DOM.\n const annotation = this.currentAnnotation\n const recording = this.recordingManager?.getRecording() ?? undefined\n\n // The conversation opens the moment the reporter submits. The popup hands\n // its element to the clarify card, which shows its thinking bubble while\n // the screenshot capture and the session upload run behind it.\n const element = this.teardownSubmittingUi(true)\n const run = () => this.startSubmissionRequest(description, annotation, recording)\n this.showClarify(this.lastPopupPos, { element, pendingSession: run(), onRetrySubmission: run })\n }\n\n private startSubmissionRequest(\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 // Let the clarify card paint its thinking state before the screenshot\n // capture blocks the main thread.\n await waitForPaint()\n const [screenshot, pageContext] = await Promise.all([\n captureScreenshot(annotation ? [annotation] : []),\n Promise.resolve(collectPageContext()),\n ])\n\n const payload = buildPayload(\n description,\n pageContext,\n this.consoleCollector.getErrors(),\n annotation,\n this.config.user,\n recording,\n this.config.user ? undefined : getAnonId(),\n this.config.user ? undefined : getStoredReporterEmail(),\n this.breadcrumbCollector.getBreadcrumbs(),\n )\n\n try {\n const submission = await this.apiClient.createSubmissionSession(payload, screenshot)\n if (submissionAttempt !== this.submissionAttempt) {\n await this.apiClient.abandonSubmissionSession(submission.id, submission.token).catch(() => {})\n return null\n }\n this.activeSubmission = submission\n return submission\n } catch {\n return null\n }\n }\n\n private teardownSubmittingUi(preservePopup = false): HTMLDivElement | undefined {\n const popupElement = preservePopup ? this.popup?.releaseElement() : undefined\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.teardownDraw()\n if (!popupElement) this.popup?.destroy()\n this.popup = null\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 this.submittingUi = null\n return popupElement\n }\n\n private showClarify(\n pos: { x: number; y: number } | null,\n opts: {\n element?: HTMLDivElement\n pendingSession: Promise<SubmissionSessionResponse | null>\n onRetrySubmission: () => Promise<SubmissionSessionResponse | null>\n },\n ): void {\n this.mode = 'clarifying'\n this.tab?.setActive(true)\n this.clarify = new ClarifyCard(this.shadow, {\n apiClient: this.apiClient,\n messages: this.messages,\n element: opts.element,\n position: this.config.position ?? 'right',\n x: pos?.x,\n y: pos?.y,\n onFinalize: () => this.finalizeSubmission(),\n onCancel: () => this.exitAll(),\n pendingSession: opts.pendingSession,\n onRetrySubmission: opts.onRetrySubmission,\n })\n }\n\n private async finalizeSubmission(): Promise<void> {\n const submissionAttempt = this.submissionAttempt\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) throw new Error('No active submission session')\n\n const feedback = await this.apiClient.finalizeSubmissionSession(submission.id, submission.token)\n if (submissionAttempt !== this.submissionAttempt || this.activeSubmission !== submission) return\n\n this.activeSubmission = null\n this.pendingSubmissionRequest = null\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 this.clarify?.destroy()\n this.clarify = null\n this.teardownToolbar()\n this.showSuccess({ skipFollowup: submission.ai_clarify_available === false })\n }\n\n private showSuccess(options?: SuccessCardOptions): void {\n this.mode = 'idle'\n this.tab?.setActive(false)\n this.success = new SuccessCard(this.shadow, this.messages, () => {\n this.success?.destroy()\n this.success = null\n }, this.buildViewFeedbackHandler(), { ...options, emailCapture: this.buildEmailCapture() })\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 const token = await this.apiClient.resolveUserToken()\n if (token) window.open(`${boardUrl}?identity=${encodeURIComponent(token)}`, '_blank')\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 private exitAll(): void {\n this.submissionAttempt += 1\n const submission = this.activeSubmission\n this.activeSubmission = null\n this.pendingSubmissionRequest = null\n if (submission) {\n void this.apiClient.abandonSubmissionSession(submission.id, submission.token).catch(() => {})\n }\n this.mode = 'idle'\n this.via = 'pin'\n this.tab?.setActive(false)\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.teardownDraw()\n this.session?.hideHighlight()\n this.session?.destroy()\n this.session = null\n this.dim?.destroy()\n this.dim = null\n this.popup?.destroy()\n this.popup = null\n this.clarify?.destroy()\n this.clarify = null\n this.currentAnnotation = null\n this.submittingUi = 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.clarify?.destroy()\n this.success?.destroy()\n this.tab?.destroy()\n this.recordingManager?.destroy()\n this.shadow.destroy()\n }\n}\n\nfunction waitForPaint(): Promise<void> {\n return new Promise<void>((r) => requestAnimationFrame(() => setTimeout(r, 0)))\n}\n","import { WidgetController } from './widget/controller'\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\nconst MakeThisBetter = {\n init(config: MakeThisBetterConfig): void {\n cancelPendingMount()\n if (instance) {\n instance.destroy()\n instance = null\n }\n lastConfig = config\n if (document.readyState === 'loading') {\n pendingMount = () => {\n pendingMount = null\n instance = new WidgetController(config)\n }\n document.addEventListener('DOMContentLoaded', pendingMount, { once: true })\n } else {\n instance = new WidgetController(config)\n }\n },\n\n destroy(): void {\n cancelPendingMount()\n instance?.destroy()\n instance = null\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('mtb-widget-host')\n if (lastConfig && (!host || !host.shadowRoot)) {\n instance?.destroy()\n host?.remove()\n instance = new WidgetController(lastConfig)\n }\n })\n}\n"],"names":["ShadowContainer","theme","provided","style","cssText","tag","className","el","nodes","n","FeedbackTab","shadow","messages","position","onClick","customText","label","active","FRUSTRATION_DISMISS_MS","FrustrationPromptCard","handlers","SPAWN_CLICK_GRACE_MS","DimOverlay","onDismiss","now","isTouchPointer","PageOffset","px","current","MOBILE_BAR_HEIGHT_PX","AnnotationToolbar","onExit","onModeChange","showTouchHint","e","rect","inside","m","mode","AnnotationOverlay","pe","pointerId","ElementHighlighter","getElementName","below","UNRENDERED","renderedText","limit","out","node","title","text","placeholder","alt","dataMark","getCssSelector","parts","selector","classNames","c","parent","siblings","idx","PinMarker","x","y","pin","BRAND_COLOR","BRAND_RGB","brandAlpha","alpha","FONT_STACK","DRAW_STROKE","DrawLayer","buildPathD","start","max","stroke","entry","path","points","s","cx","cy","center","fx","fy","d","i","xs","p","ys","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","value","filtered","pattern","candidate","passesLuhn","sanitizeUrl","url","filterSensitiveValue","key","isSensitiveKey","item","sensitiveField","isSensitiveFieldAttributes","nestedKey","filteredItem","normalizeIdentifier","attributes","normalizedKey","token","digits","sum","doubleDigit","index","digit","isSensitiveField","element","filterInputValue","MAX_GROUPED_TEXT_LENGTH","createSensitiveTextContext","hasSensitiveTextContext","context","state","sensitiveTextState","cached","child","childState","MAX_DURATION_S","VALUE_ATTRIBUTE_NAMES","MASKED_INPUT_TYPES","type","BLOCK_CLASS","MASK_TEXT_CLASS","RRWEB_CDN","RRWEB_SRI","rrwebLoaded","loadRrweb","loadViaImport","loadViaCdn","filterReplayEvent","event","record","filterReplayDomValues","textContext","isRecord","hasAttributeValue","name","attributeValue","findGlobalRecord","win","resolve","reject","existing","script","RecordSession","onMaxReached","ripple","PRIVACY_SELECTOR","isPrivacyProtected","structuralElementName","role","structuralElementSelector","DRAW_MIN_TRAVEL_PX","annotationTarget","AnnotationSession","underEl","getElementUnder","target","targetName","annotation","WIDGET_HOST_ID","isWidgetElement","host","escapeHtml","SETTLE_MS","KeyboardInset","reading","vp","inset","onChange","SHEET_MAX_WIDTH","isSheetViewport","SheetLayout","headroom","applied","DrawNoteBar","opts","canUndo","canRedo","guardReplayClick","sawPointerDown","onPointerDown","CommentPopup","quickOpts","o","targetRect","tabPosition","vw","vh","margin","gap","tabWidth","popupW","popupH","minLeft","maxRight","left","top","anchorIsInformative","btn","option","hasText","description","ApiError","status","isTerminalConflict","error","DEFAULT_API_URL","CAPTURE_POLICY","ApiClient","projectKey","apiUrl","retryDelayMs","userToken","userTokenFn","contentType","headers","payload","screenshot","submissionToken","generateSubmissionToken","form","json","blob","ext","generateIdempotencyKey","reporterExternalId","feedbackId","email","identityToken","submissionSessionId","message","body","onDelta","signal","res","consumeSse","init","retries","err","delay","ms","reader","decoder","buffer","result","flush","raw","eventLine","l","dataLine","done","sep","bytes","byte","POLL_INTERVAL_MS","POLL_TIMEOUT_MS","CLARIFY_SILENCE_TIMEOUT_MS","CARD_WIDTH","CARD_HEIGHT","ClarifyCard","existingElement","cardW","cardH","centeredLeft","pending","session","footer","abort","silenceTimer","autoFinalize","snapshot","button","last","msg","bubble","dots","SUCCESS_AUTO_CLOSE_MS","EMAIL_PATTERN","SuccessCard","onClose","onViewFeedback","options","viewLink","emailCapture","onSubmit","input","submit","handle","resolveUrl","baseUrl","doc","base","a","uuid","counter","random","toArray","arrayLike","arr","styleProps","getStyleProperties","styleProperty","val","getNodeWidth","leftBorder","rightBorder","getNodeHeight","topBorder","bottomBorder","getImageSize","targetNode","width","height","getPixelRatio","ratio","FINAL_PROCESS","canvasDimensionLimit","checkCanvasDimensions","canvas","createImage","img","svgToDataURL","svg","html","nodeToDataURL","xmlns","foreignObject","isInstanceOfElement","instance","nodePrototype","formatCSSText","content","formatCSSProperties","priority","getPseudoElementStyle","pseudo","clonePseudoElement","nativeNode","clonedNode","styleElement","clonePseudoElements","WOFF","JPEG","mimes","getExtension","match","getMimeType","extension","getContentFromDataUrl","dataURL","isDataUrl","makeDataUrl","mimeType","fetchAsDataURL","process","cache","getCacheKey","includeQueryParams","resourceToDataURL","resourceUrl","cacheKey","cloneCanvasElement","cloneVideoElement","video","ctx","poster","cloneIFrameElement","iframe","_a","cloneNode","cloneSingleNode","isSVGElement","isSlotElement","cloneChildren","_b","children","deferred","clonedChild","cloneCSSStyle","targetStyle","sourceStyle","cloneInputValue","cloneSelectValue","clonedSelect","selectedOption","decorate","ensureSVGSymbols","clone","uses","processedDefs","id","exist","definition","ns","defs","isRoot","URL_REGEX","URL_WITH_FORMAT_REGEX","FONT_SRC_REGEX","toRegex","escaped","parseURLs","urls","quotation","embed","resourceURL","baseURL","getContentFromUrl","resolvedURL","filterPreferredFontFormat","str","preferredFontFormat","src","format","shouldEmbed","embedResources","filteredCSSText","css","embedProp","propName","propValue","cssString","embedBackground","embedImageNode","isImageElement","image","embedChildren","deferreds","embedImages","applyStyle","manual","cssFetchCache","fetchCSS","embedFonts","data","regexUrl","loadFonts","loc","parseCSS","source","commentsRegex","keyframesRegex","matches","importRegex","combinedCSSRegex","unifiedRegex","getCSSRules","styleSheets","ret","sheet","importIndex","metadata","rule","inline","getWebFontRules","cssRules","parseWebFontRules","normalizeFontFamily","font","getUsedFonts","fonts","traverse","getWebFontCSS","rules","usedFonts","embedWebFonts","styleNode","sytleContent","toSvg","toCanvas","canvasWidth","canvasHeight","toJpeg","MIN_CROP_WIDTH","MIN_CROP_HEIGHT","PADDING_PX","PADDING_RATIO","SKIP_CROP_RATIO","getAnnotationBounds","annotations","minX","minY","maxX","maxY","ann","r","polyline","parsePathPolylines","computeCropRegion","viewportWidth","viewportHeight","bounds","padX","padY","w","h","cropCanvas","cropped","captureScreenshot","captureMetrics","getCaptureMetrics","screenshotAnnotations","translateAnnotationForScreenshot","redactions","collectPrivacyRedactions","dataUrl","capturePixelRatio","bakeScreenshot","offset","nestedScrollOffset","findAnnotationTarget","translateDrawPath","parentElement","root","drawPath","_match","command","loadImage","scaleX","scaleY","pinIndex","drawStroke","drawPin","annotationsFitCanvas","canvasToJpegBlob","cropRect","scaled","fitCropToCanvas","right","bottom","requestedLeft","requestedTop","elementsIncludingRoot","addRedaction","isWidgetOrBlocked","isMasked","hasSensitiveValue","REDACTION_PADDING_PX","walker","hasSensitiveText","range","descendants","padding","MAX_CAPTURE_PIXELS","metrics","total","borderWidth","borderHeight","polylines","tokens","ErrorSource","listener","line","col","errorName","reason","errorSource","summarizeError","validErrorName","inferErrorName","location","errorLocation","pathname","MAX_CONSOLE_ERRORS","ConsoleErrorCollector","listeners","nativePushState","nativeReplaceState","installedPushState","installedReplaceState","popstateHandler","onHistoryNavigation","install","released","uninstall","notify","pushOrig","replaceOrig","unused","MAX_BREADCRUMBS","describeTarget","describeSelector","BreadcrumbCollector","crumb","field","summary","from","to","sanitizedFrom","sanitizedTo","STORAGE_KEY","memoryState","loadFrustrationState","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","DEAD_CLICK_DOM_DELAY_MS","ERROR_PAGE_CHECK_DELAY_MS","INTERACTIVE_TAGS","INTERACTIVE_ROLES","ERROR_PAGE_PATTERNS","_FrustrationDetector","onFrustration","_e","stored","cutoff","describeElement","control","candidates","clickTime","mutated","navigated","cls","lower","FrustrationDetector","collectPageContext","detectBrowser","detectOS","ua","buildPayload","pageContext","consoleErrors","user","recording","anonId","anonEmail","breadcrumbs","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","RecordControlBar","getDuration","onStop","secs","mm","ss","RecordingManager","isLocalHost","hostname","WidgetController","config","touch","note","action","exchange","run","submissionAttempt","request","waitForPaint","submission","preservePopup","popupElement","pos","feedback","saved","boardUrl","lastConfig","pendingMount","cancelPendingMount","MakeThisBetter"],"mappings":"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","x_google_ignoreList":[27,28,29,30,31,32,33,34,35,36]}
|