@mhosaic/feedback 0.12.1 → 0.12.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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/api/client.ts","../src/capture/urlSanitizer.ts","../src/capture/device.ts","../src/capture/console.ts","../src/capture/network.ts","../src/capture/errors.ts","../src/capture/performance.ts","../src/capture/ringBuffer.ts","../src/capture/index.ts","../src/widget/i18n.ts","../src/widget/mount.tsx","../src/widget/BoardView.tsx","../src/widget/ReportDetailView.tsx","../src/widget/CommentBubble.tsx","../src/widget/screenshot-utils.ts","../src/widget/ChangelogList.tsx","../src/widget/ReportRow.tsx","../src/widget/Fab.tsx","../src/widget/Form.tsx","../src/widget/Annotator.tsx","../src/widget/MineList.tsx","../src/widget/KpiStrip.tsx","../src/widget/Modal.tsx","../src/widget/styles.ts","../src/core.ts"],"sourcesContent":["import type {\n BoardFilters,\n ReportPayload,\n SubmittedReport,\n WidgetBoardKpis,\n WidgetBoardRow,\n WidgetChangelogRow,\n WidgetCommentRow,\n WidgetReportDetail,\n WidgetReportRow,\n} from '../types'\n\n/** Page envelope DRF returns for paginated list endpoints. */\nexport interface BoardListPage {\n count: number\n next: string | null\n previous: string | null\n results: WidgetBoardRow[]\n}\n\nexport interface ApiClientOptions {\n apiKey: string\n endpoint: string\n fetch?: typeof fetch\n beforeSend?: (payload: ReportPayload) => ReportPayload | false | Promise<ReportPayload | false>\n}\n\nexport interface ApiClient {\n submitReport(payload: ReportPayload): Promise<SubmittedReport>\n /** GET /v1/reports/widget/mine/ — caller's own reports on this project. */\n listMine(externalId: string): Promise<WidgetReportRow[]>\n /** GET /v1/reports/widget/changelog/ — caller's resolved reports for the\n * \"This week\" tab; client groups by ISO week of `resolved_at`. */\n listChangelog(externalId: string): Promise<WidgetChangelogRow[]>\n /** GET /v1/reports/widget/<id>/ — single report + thread. */\n getReport(reportId: string, externalId: string): Promise<WidgetReportDetail>\n /** POST /v1/reports/widget/<id>/comments/ — submitter follow-up. */\n addComment(\n reportId: string,\n externalId: string,\n body: string,\n clientNonce?: string,\n ): Promise<WidgetCommentRow>\n /** PATCH /v1/reports/widget/<id>/ — close-as-resolved by the submitter. */\n closeAsResolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail>\n /** GET /v1/reports/widget/board/ — paginated, filtered list backing the\n * v0.12 Board tab. Scope follows the project's\n * `share_reports_with_widget` flag (submitter-only when off, project-\n * wide when on); `filters.mine = true` narrows even when on. */\n listBoard(externalId: string, filters?: BoardFilters): Promise<BoardListPage>\n /** GET /v1/reports/widget/board/kpis/ — total + per-status counts +\n * resolution rate. Cheap; safe to poll alongside the list. */\n fetchBoardKpis(externalId: string, filters?: BoardFilters): Promise<WidgetBoardKpis>\n}\n\nconst SCALAR_FIELDS: Array<keyof ReportPayload> = [\n 'description', 'feedback_type', 'severity', 'env', 'page_url', 'user_agent', 'capture_method',\n]\n\nexport function createApiClient(options: ApiClientOptions): ApiClient {\n // No silent fallback to a hardcoded host — a misconfigured deploy used\n // to leak reports to https://core.mhosaic.com (which doesn't even\n // resolve). Failing fast at construction makes the misconfig obvious.\n const endpoint = (options.endpoint ?? '').replace(/\\/+$/, '')\n if (!endpoint) {\n throw new Error(\n '[mhosaic-feedback] `endpoint` is required (e.g. \"https://feedback.example.com\").',\n )\n }\n const fetcher = options.fetch ?? globalThis.fetch\n\n async function submitReport(input: ReportPayload): Promise<SubmittedReport> {\n let payload: ReportPayload | false = input\n if (options.beforeSend) payload = await options.beforeSend(input)\n if (payload === false) throw new Error('Submission cancelled by beforeSend')\n\n const form = new FormData()\n for (const field of SCALAR_FIELDS) {\n form.append(field, String(payload[field]))\n }\n form.append('technical_context', JSON.stringify(payload.technical_context))\n if (payload.screenshot) form.append('screenshot', payload.screenshot, 'screenshot.png')\n // Only emit `synthetic` when truthy — a `false` value would still trigger\n // the BooleanField parser on the backend, which is fine, but omitting it\n // keeps the request shape byte-identical for existing curated submissions.\n if (payload.synthetic) form.append('synthetic', 'true')\n // v0.7: nest the identity payload so DRF's WidgetUserIdentitySerializer\n // sees a real object. FormData can't carry nested objects directly —\n // use a JSON-encoded string under the `user` key; DRF parses it via\n // the JSONField semantics on the WidgetUser fields.\n if (payload.user?.id) {\n form.append('user', JSON.stringify(payload.user))\n }\n // v0.7.3: widget_version is build-time-stamped by tsup. Lets the\n // backend show \"currently running v0.7.2\" per project on the\n // operator Companies page (per-customer version observability).\n if (payload.widget_version) {\n form.append('widget_version', payload.widget_version)\n }\n\n // The backend honors `synthetic=true` only when this header is set,\n // so a hand-crafted curl with the public key can't smuggle a curated\n // report into the auto-error bucket (where the default operator view\n // hides it). The bundled debugger always sets payload.synthetic; the\n // user-facing widget never does.\n const headers: Record<string, string> = {\n Authorization: `Bearer ${options.apiKey}`,\n }\n if (payload.synthetic) {\n headers['X-Mhosaic-Capture-Source'] = 'error-tracking'\n }\n const response = await fetcher(`${endpoint}/api/feedback/v1/reports/`, {\n method: 'POST',\n headers,\n body: form,\n })\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`Feedback submit failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<SubmittedReport>\n }\n\n function widgetHeaders(externalId: string) {\n return {\n Authorization: `Bearer ${options.apiKey}`,\n 'X-Mhosaic-User': externalId,\n }\n }\n\n async function listMine(externalId: string): Promise<WidgetReportRow[]> {\n const response = await fetcher(`${endpoint}/api/feedback/v1/reports/widget/mine/`, {\n method: 'GET',\n headers: widgetHeaders(externalId),\n })\n if (response.status === 404) return [] // submitter has no thread yet\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`listMine failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportRow[]>\n }\n\n async function listChangelog(externalId: string): Promise<WidgetChangelogRow[]> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/changelog/`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (response.status === 404) return []\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`listChangelog failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetChangelogRow[]>\n }\n\n async function getReport(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`getReport failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n async function addComment(\n reportId: string,\n externalId: string,\n body: string,\n clientNonce?: string,\n ): Promise<WidgetCommentRow> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/comments/`,\n {\n method: 'POST',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n body,\n ...(clientNonce !== undefined && { client_nonce: clientNonce }),\n }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`addComment failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetCommentRow>\n }\n\n async function closeAsResolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'PATCH',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ status: 'closed' }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`closeAsResolved failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n function boardQueryString(filters?: BoardFilters): string {\n if (!filters) return ''\n const params = new URLSearchParams()\n // Multi-value fields use append() so repeated keys stack the way DRF's\n // `request.query_params.getlist()` reads them on the server.\n filters.status?.forEach((s) => params.append('status', s))\n filters.type?.forEach((t) => params.append('type', t))\n filters.severity?.forEach((s) => params.append('severity', s))\n if (filters.q) params.set('q', filters.q)\n if (filters.mine) params.set('mine', '1')\n if (filters.page && filters.page > 1) params.set('page', String(filters.page))\n const qs = params.toString()\n return qs ? `?${qs}` : ''\n }\n\n async function listBoard(\n externalId: string,\n filters?: BoardFilters,\n ): Promise<BoardListPage> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/board/${boardQueryString(filters)}`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (response.status === 404) {\n // Caller has no WidgetUser row yet — render an empty Board rather\n // than a hard error so the first-time experience is a clean slate.\n return { count: 0, next: null, previous: null, results: [] }\n }\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`listBoard failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<BoardListPage>\n }\n\n async function fetchBoardKpis(\n externalId: string,\n filters?: BoardFilters,\n ): Promise<WidgetBoardKpis> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/board/kpis/${boardQueryString(filters)}`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (response.status === 404) {\n return { total: 0, by_status: {}, resolution_rate: 0, scope: 'mine' }\n }\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`fetchBoardKpis failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetBoardKpis>\n }\n\n return {\n submitReport,\n listMine,\n listChangelog,\n getReport,\n addComment,\n closeAsResolved,\n listBoard,\n fetchBoardKpis,\n }\n}\n","/**\n * URL sanitizer for captured network request URLs.\n *\n * Two layers of defense:\n *\n * 1) Param NAME match — anything whose name suggests a credential is\n * redacted unconditionally (token, key, password, secret, auth, etc.).\n *\n * 2) Param VALUE shape match — even when the name is innocent (\"id\",\n * \"redirect_uri\", \"context\"), if the VALUE looks like a JWT, a\n * Bearer token, an mhosaic-feedback API key, or a long opaque hex/b64\n * string we redact it. This catches the realistic case where a backend\n * routes credentials through generic-named params or where a careless\n * developer puts a token in a URL fragment for \"convenience.\"\n *\n * The path portion is preserved as-is — its purpose is operator-side\n * \"which page was this report filed from\", and stripping it would\n * destroy that signal. Hosts that route secrets through path segments\n * (uncommon) should mark those routes for the widget to skip via the\n * sanitizeUrl hook in createFeedback().\n */\n\nconst SENSITIVE_NAME = /token|key|password|secret|auth|session|sig|credential|bearer|cookie/i\n\nconst SENSITIVE_VALUE_PATTERNS: RegExp[] = [\n // JWT: three base64url segments separated by dots, header begins with eyJ.\n /^eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/,\n // Mhosaic feedback keys (the project's own format).\n /^(?:sk|pk)_proj_[A-Za-z0-9_-]{16,}$/,\n // GitHub PATs (ghp/gho/ghu/ghs/ghr prefixes, 36+ chars).\n /^gh[pousr]_[A-Za-z0-9]{30,}$/,\n // Stripe live/test secret keys.\n /^(?:sk|pk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{16,}$/,\n // AWS access key id.\n /^AKIA[0-9A-Z]{12,}$/,\n // Slack bot tokens.\n /^xox[abprso]-[A-Za-z0-9-]{12,}$/,\n // Generic high-entropy: hex 40+ chars (SHA-1, SHA-256) or long alnum 32+.\n /^[a-f0-9]{40,}$/i,\n // Long opaque base64-ish string (common for OAuth codes & session ids).\n /^[A-Za-z0-9_-]{40,}$/,\n]\n\nfunction looksLikeCredential(value: string): boolean {\n return SENSITIVE_VALUE_PATTERNS.some((re) => re.test(value))\n}\n\nexport function sanitizeUrl(url: string): string {\n try {\n // Accept absolute URLs or root-relative paths; reject everything else\n const isAbsolute = /^https?:\\/\\//i.test(url)\n const isRootRelative = url.startsWith('/')\n if (!isAbsolute && !isRootRelative) return url\n\n const base = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n const u = new URL(url, base)\n const clean = new URLSearchParams()\n u.searchParams.forEach((value, name) => {\n if (SENSITIVE_NAME.test(name) || looksLikeCredential(value)) {\n clean.set(name, '[redacted]')\n } else {\n clean.set(name, value)\n }\n })\n u.search = clean.toString()\n // Hash fragments occasionally carry credentials (OAuth implicit flow):\n // never log them.\n u.hash = u.hash ? '#[redacted]' : ''\n return u.toString()\n } catch {\n return url\n }\n}\n","import type { DeviceContext } from '../types'\nimport { scrubCredentials } from './scrub'\nimport { sanitizeUrl } from './urlSanitizer'\n\nexport function collectDevice(): DeviceContext {\n const nav = navigator as Navigator & { connection?: { effectiveType?: string }; deviceMemory?: number }\n const connection = nav.connection?.effectiveType\n const deviceMemory = nav.deviceMemory\n // Auth flows commonly hand off via query-string tokens (`?token=…`,\n // `?code=…`); when the user clicks through to the host page, both the\n // raw referrer AND the host's own pathname can carry those values.\n // Run both through the same sanitizer used for fetch/XHR; collapse the\n // result back to a path-only string so we don't accidentally upload the\n // operator-side host's origin as if it were the captured page's URL.\n const rawReferrer = document.referrer || undefined\n const referrer = rawReferrer !== undefined ? sanitizeUrl(rawReferrer) : undefined\n const sanitizedHere = sanitizeUrl(window.location.pathname + window.location.search)\n let pathname: string\n try {\n const parsed = new URL(sanitizedHere, window.location.origin)\n pathname = parsed.pathname + parsed.search\n } catch {\n pathname = window.location.pathname\n }\n return {\n viewport: { w: window.innerWidth, h: window.innerHeight, dpr: window.devicePixelRatio || 1 },\n screen: { w: window.screen.width, h: window.screen.height },\n platform: nav.platform,\n language: nav.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n timezoneOffset: new Date().getTimezoneOffset(),\n ...(connection !== undefined && { connection }),\n online: nav.onLine,\n ...(deviceMemory !== undefined && { deviceMemory }),\n hardwareConcurrency: nav.hardwareConcurrency,\n ...(referrer !== undefined && { referrer }),\n // Hosts commonly stuff credentials / PII into page titles\n // (\"Inbox (3) — bob@x.com\", \"Reset password — token=abc\"). Cap to a\n // sane length and run through the same scrubber the console/error\n // paths use.\n title: scrubCredentials((document.title || '')).slice(0, 200),\n pathname,\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ConsoleEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\ntype ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug'\n\nfunction safeStringify(arg: unknown): string {\n if (arg == null) return String(arg)\n if (typeof arg === 'string') return arg\n if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg)\n if (arg instanceof Error) return `${arg.name}: ${arg.message}`\n if (arg instanceof Element) return `<${arg.tagName.toLowerCase()}>`\n try {\n return JSON.stringify(arg, (_k, v) => (typeof v === 'bigint' ? v.toString() : v))\n } catch {\n try { return String(arg) } catch { return '[unserializable]' }\n }\n}\n\nexport function installConsolePatch(buffer: RingBuffer<ConsoleEntry>): () => void {\n const levels: ConsoleLevel[] = ['log', 'info', 'warn', 'error', 'debug']\n const originals: Partial<Record<ConsoleLevel, (...args: unknown[]) => void>> = {}\n for (const level of levels) {\n const original = console[level] as ((...args: unknown[]) => void) | undefined\n if (typeof original !== 'function') continue\n originals[level] = original\n console[level] = function patched(...args: unknown[]) {\n try {\n const rawMessage = args.map(safeStringify).join(' ')\n const message = scrubCredentials(rawMessage).slice(0, 2000)\n const entry: ConsoleEntry = { level, message, ts: Date.now() }\n if (level === 'error') {\n const stack = new Error().stack\n if (stack) entry.stack = stack.split('\\n').slice(2, 8).join('\\n')\n }\n buffer.push(entry)\n } catch { /* never break console */ }\n original.apply(console, args)\n }\n }\n return () => {\n for (const [level, fn] of Object.entries(originals)) {\n (console as unknown as Record<string, (...args: unknown[]) => void>)[level] = fn\n }\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { NetworkEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\nexport function installFetchPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n if (typeof window === 'undefined' || typeof window.fetch !== 'function') return () => {}\n const original = window.fetch.bind(window)\n window.fetch = async function patched(input: RequestInfo | URL, init?: RequestInit) {\n const start = performance.now()\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n const method = (init?.method || (input instanceof Request ? input.method : 'GET')).toUpperCase()\n try {\n const response = await original(input, init)\n buffer.push({ url: sanitize(url), method, status: response.status, durationMs: Math.round(performance.now() - start), ts: Date.now() })\n return response\n } catch (err) {\n // Error messages from fetch (CORS rejection, abort, etc.) sometimes\n // echo the requested URL back including its full query string. Scrub\n // for the same patterns the console path does.\n const rawErr = err instanceof Error ? err.message : String(err)\n buffer.push({\n url: sanitize(url), method, status: 0,\n durationMs: Math.round(performance.now() - start),\n ts: Date.now(),\n error: scrubCredentials(rawErr),\n })\n throw err\n }\n }\n return () => { window.fetch = original }\n}\n\nexport function installXhrPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n if (typeof window === 'undefined' || typeof window.XMLHttpRequest !== 'function') return () => {}\n const Original = window.XMLHttpRequest\n const originalOpen = Original.prototype.open\n const originalSend = Original.prototype.send\n\n Original.prototype.open = function patchedOpen(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, method: string, url: string | URL) {\n this.__mfb = { method: method.toUpperCase(), url: typeof url === 'string' ? url : url.toString(), start: performance.now() }\n return originalOpen.apply(this, arguments as unknown as Parameters<typeof originalOpen>)\n }\n\n Original.prototype.send = function patchedSend(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, body?: Document | XMLHttpRequestBodyInit | null) {\n this.addEventListener('loadend', () => {\n try {\n const ctx = this.__mfb\n if (!ctx) return\n buffer.push({\n url: sanitize(ctx.url),\n method: ctx.method,\n status: this.status,\n durationMs: Math.round(performance.now() - ctx.start),\n ts: Date.now(),\n })\n } catch { /* noop */ }\n })\n return originalSend.call(this, body ?? null)\n }\n\n return () => {\n Original.prototype.open = originalOpen\n Original.prototype.send = originalSend\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ErrorEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\n// Errors that bubble up to `window.error` or `unhandledrejection` often\n// carry credentials in plain text — e.g. an SDK throws `new Error(\"Bearer\n// eyJ... rejected (401)\")`, or a promise rejects with `{apiKey: \"sk_live_…\"}`.\n// Both ship to the central backend unless we scrub here.\n\nexport function installErrorHandlers(buffer: RingBuffer<ErrorEntry>): () => void {\n if (typeof window === 'undefined') return () => {}\n const onError = (e: ErrorEvent) => {\n const rawStack = e.error instanceof Error ? e.error.stack : undefined\n const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n buffer.push({\n message: scrubCredentials(e.message || 'Unknown error'),\n ...(stack !== undefined && { stack }),\n ts: Date.now(),\n source: 'window.error',\n })\n }\n const onRejection = (e: PromiseRejectionEvent) => {\n const reason = e.reason\n const rawMessage =\n reason instanceof Error ? reason.message :\n typeof reason === 'string' ? reason :\n (() => { try { return JSON.stringify(reason) } catch { return String(reason) } })()\n const rawStack = reason instanceof Error ? reason.stack : undefined\n const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n buffer.push({\n message: scrubCredentials(rawMessage),\n ...(stack !== undefined && { stack }),\n ts: Date.now(),\n source: 'unhandledrejection',\n })\n }\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onRejection)\n return () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onRejection)\n }\n}\n","import { sanitizeUrl } from './urlSanitizer'\n\nexport interface PerformanceSnapshot {\n navigation?: { type: string; duration: number }\n longTasks: { duration: number; startTime: number }[]\n slowResources: { name: string; duration: number; initiatorType: string }[]\n}\n\nexport function createPerformanceCollector(slowResourceMs = 1000) {\n const longTasks: PerformanceSnapshot['longTasks'] = []\n const slowResources: PerformanceSnapshot['slowResources'] = []\n let observer: PerformanceObserver | null = null\n\n if (typeof PerformanceObserver !== 'undefined') {\n try {\n observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.entryType === 'longtask') {\n longTasks.push({ duration: entry.duration, startTime: entry.startTime })\n while (longTasks.length > 20) longTasks.shift()\n } else if (entry.entryType === 'resource') {\n const e = entry as PerformanceResourceTiming\n if (e.duration > slowResourceMs) {\n // sanitizeUrl strips credential-shaped query params + the hash\n // — the resource URL is otherwise raw input from any third-party\n // request the host page makes (analytics pixels, OAuth\n // callbacks with tokens in the query, etc.).\n slowResources.push({ name: sanitizeUrl(e.name), duration: e.duration, initiatorType: e.initiatorType })\n while (slowResources.length > 20) slowResources.shift()\n }\n }\n }\n })\n observer.observe({ entryTypes: ['longtask', 'resource'] })\n } catch { /* unsupported */ }\n }\n\n return {\n snapshot(): PerformanceSnapshot {\n const nav = typeof performance !== 'undefined' ? performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined : undefined\n const navigation = nav ? { type: nav.type, duration: nav.duration } : undefined\n return {\n ...(navigation !== undefined && { navigation }),\n longTasks: longTasks.slice(),\n slowResources: slowResources.slice(),\n }\n },\n dispose() { observer?.disconnect() },\n }\n}\n","export class RingBuffer<T> {\n private items: T[] = []\n constructor(private readonly max: number) {}\n\n push(item: T): void {\n this.items.push(item)\n while (this.items.length > this.max) this.items.shift()\n }\n\n snapshot(): T[] {\n return this.items.slice()\n }\n\n clear(): void {\n this.items.length = 0\n }\n}\n","import { sanitizeUrl } from './urlSanitizer'\nimport { collectDevice } from './device'\nimport { installConsolePatch } from './console'\nimport { installFetchPatch, installXhrPatch } from './network'\nimport { installErrorHandlers } from './errors'\nimport { createPerformanceCollector } from './performance'\nimport { RingBuffer } from './ringBuffer'\nimport type { CapturedContext, ConsoleEntry, ErrorEntry, NetworkEntry } from '../types'\n\nexport interface CaptureOptions {\n sanitizeUrl?: (url: string) => string\n maxConsole?: number\n maxNetwork?: number\n maxErrors?: number\n}\n\nexport interface CaptureHandle {\n snapshot(): CapturedContext\n clear(): void\n dispose(): void\n}\n\nexport function installCapture(options: CaptureOptions = {}): CaptureHandle {\n const { maxConsole = 50, maxNetwork = 50, maxErrors = 20 } = options\n const sanitize = options.sanitizeUrl ?? sanitizeUrl\n\n const consoleBuf = new RingBuffer<ConsoleEntry>(maxConsole)\n const networkBuf = new RingBuffer<NetworkEntry>(maxNetwork)\n const errorBuf = new RingBuffer<ErrorEntry>(maxErrors)\n\n const uninstallConsole = installConsolePatch(consoleBuf)\n const uninstallFetch = installFetchPatch(networkBuf, sanitize)\n const uninstallXhr = installXhrPatch(networkBuf, sanitize)\n const uninstallErrors = installErrorHandlers(errorBuf)\n const perf = createPerformanceCollector()\n\n return {\n snapshot(): CapturedContext {\n return {\n consoleLogs: consoleBuf.snapshot(),\n networkRequests: networkBuf.snapshot(),\n errors: errorBuf.snapshot(),\n device: collectDevice(),\n capturedAt: Date.now(),\n }\n },\n clear() {\n consoleBuf.clear(); networkBuf.clear(); errorBuf.clear()\n },\n dispose() {\n uninstallConsole(); uninstallFetch(); uninstallXhr(); uninstallErrors()\n perf.dispose()\n },\n }\n}\n","export const DEFAULT_STRINGS = {\n 'fab.label': 'Send feedback',\n 'form.title': 'Send feedback',\n 'form.description.label': 'What happened?',\n 'form.description.placeholder': 'Describe the issue or idea in one or two sentences.',\n 'form.type.label': 'Type',\n 'form.severity.label': 'Severity',\n 'form.submit': 'Send',\n 'form.cancel': 'Cancel',\n 'form.close': 'Close',\n 'form.submitting': 'Sending…',\n 'form.success': 'Thanks — your feedback was sent.',\n 'form.error': 'Could not send. Please try again.',\n 'form.description.required': 'Please describe the issue before sending.',\n 'form.screenshot.label': 'Screenshot',\n 'form.screenshot.cta_click': 'Click',\n 'form.screenshot.cta_rest': 'drop, or paste an image',\n 'form.screenshot.formats': 'PNG, JPEG or WebP — up to 10 MB',\n 'form.screenshot.remove': 'Remove screenshot',\n 'form.screenshot.annotate': 'Annotate',\n 'form.screenshot.error_type': 'Unsupported file type. Use PNG, JPEG or WebP.',\n 'form.screenshot.error_size': 'File too large (max {max} MB).',\n 'form.screenshot.or': 'or',\n 'form.screenshot.capture_page': 'Capture this page',\n 'form.screenshot.capture_page_hint': 'Pick a tab or window to share',\n 'form.screenshot.capture_error': 'Couldn’t capture the page. Try uploading a screenshot instead.',\n 'form.context.label': 'Page',\n 'type.bug': 'Bug',\n 'type.feature': 'Feature request',\n 'type.question': 'Question',\n 'type.praise': 'Praise',\n 'type.typo': 'Typo',\n 'severity.blocker': 'Blocker',\n 'severity.high': 'High',\n 'severity.medium': 'Medium',\n 'severity.low': 'Low',\n 'annotator.title': 'Annotate screenshot',\n 'annotator.tool.rectangle': 'Rectangle',\n 'annotator.tool.arrow': 'Arrow',\n 'annotator.tool.freehand': 'Freehand',\n 'annotator.tool.text': 'Text',\n 'annotator.tool.highlight': 'Highlight',\n 'annotator.tool.blur': 'Blur (hide sensitive data)',\n 'annotator.text_prompt': 'Enter text:',\n 'annotator.color_picker': 'Custom color',\n 'annotator.undo': 'Undo',\n 'annotator.redo': 'Redo',\n 'annotator.clear': 'Clear all',\n 'annotator.count_suffix': 'annotations',\n 'annotator.loading': 'Loading…',\n 'annotator.apply': 'Apply',\n 'annotator.applying': 'Applying…',\n 'tab.send': 'Send',\n 'tab.mine': 'My reports',\n 'tab.changelog': 'This week',\n 'tab.board': 'Board',\n 'board.kpi.total': 'Total',\n 'board.kpi.new': 'New',\n 'board.kpi.in_progress': 'In progress',\n 'board.kpi.awaiting_validation': 'Awaiting validation',\n 'board.kpi.closed': 'Closed',\n 'board.kpi.rejected': 'Rejected',\n 'board.kpi.resolution_rate': 'Resolution rate',\n 'board.filter.status': 'Status',\n 'board.filter.type': 'Type',\n 'board.filter.severity': 'Severity',\n 'board.filter.search.placeholder': 'Search…',\n 'board.filter.mine': 'Mine only',\n 'board.filter.clear': 'Clear filters',\n 'board.list.empty.title': 'Nothing here yet',\n 'board.list.empty.description': 'When reports land, they’ll show up here.',\n 'board.list.loading': 'Loading…',\n 'board.list.error': 'Couldn’t load reports. Try again.',\n 'board.list.you': 'you',\n 'board.list.count': '{n} of {total}',\n 'board.detail.empty': 'Pick a report on the left to see its full thread.',\n 'board.detail.by': 'By',\n 'board.detail.confirm_resolution': 'Confirm resolution',\n 'board.detail.status_history': 'History',\n 'board.scope.project': 'Project reports',\n 'board.scope.mine': 'Your reports',\n 'board.back': 'Back',\n 'changelog.empty.title': 'Nothing resolved yet',\n 'changelog.empty.body': 'Once a report you sent is fixed it will appear here, grouped by week.',\n 'changelog.week_of': 'Week of {date}',\n 'changelog.resolved_one': '{count} resolved',\n 'changelog.resolved_many': '{count} resolved',\n 'mine.empty.title': 'No reports yet',\n 'mine.empty.body': 'Once you send feedback you can follow the thread here.',\n 'mine.refresh': 'Refresh',\n 'mine.loading': 'Loading…',\n 'mine.error': 'Could not load your reports.',\n 'mine.replies_one': '1 reply',\n 'mine.replies_many': '{count} replies',\n 'mine.filter.empty': 'No reports match this filter.',\n 'kpi.new': 'New',\n 'kpi.in_progress': 'In progress',\n 'kpi.awaiting_validation': 'Awaiting you',\n 'kpi.resolution_rate': 'Resolution rate',\n 'detail.back': 'Back',\n 'detail.thread': 'Conversation',\n 'detail.no_replies': 'No replies yet — we’ll let you know when an operator responds.',\n 'detail.compose_placeholder': 'Add a follow-up reply…',\n 'detail.compose_send': 'Reply',\n 'detail.compose_sending': 'Sending…',\n 'detail.close_cta': 'Mark as resolved',\n 'detail.close_busy': 'Marking…',\n 'detail.history': 'Status history',\n 'detail.context.submitted_at': 'Submitted',\n 'detail.context.page': 'Page',\n 'detail.context.capture.manual': 'Manual capture',\n 'detail.context.capture.html2canvas': 'Auto capture',\n 'detail.context.capture.display_media': 'Screen share',\n 'detail.context.capture.none': 'No screenshot',\n 'detail.author.staff': 'Operator',\n 'detail.author.mcp': 'Mhosaic Team',\n 'detail.author.system': 'System',\n 'detail.tech.title': 'What we received',\n 'detail.tech.errors_one': 'error',\n 'detail.tech.errors_many': 'errors',\n 'detail.tech.device': 'Device',\n 'detail.tech.device.viewport': 'Viewport',\n 'detail.tech.device.platform': 'Platform',\n 'detail.tech.device.language': 'Language',\n 'detail.tech.device.timezone': 'Timezone',\n 'detail.tech.device.connection': 'Connection',\n 'detail.tech.errors': 'Runtime errors',\n 'detail.tech.console': 'Console (last 20)',\n 'detail.tech.network': 'Network (last 15)',\n 'status.new': 'New',\n 'status.in_progress': 'In progress',\n 'status.awaiting_validation': 'Awaiting your validation',\n 'status.closed': 'Closed',\n 'status.rejected': 'Rejected',\n 'status.duplicate': 'Duplicate',\n 'status.wontfix': 'Won’t fix',\n}\n\nexport type StringKey = keyof typeof DEFAULT_STRINGS\n\nconst FRENCH_STRINGS: Record<StringKey, string> = {\n 'fab.label': 'Envoyer un commentaire',\n 'form.title': 'Envoyer un commentaire',\n 'form.description.label': 'Qu’est-il arrivé ?',\n 'form.description.placeholder': 'Décrivez le problème ou l’idée en une ou deux phrases.',\n 'form.type.label': 'Type',\n 'form.severity.label': 'Sévérité',\n 'form.submit': 'Envoyer',\n 'form.cancel': 'Annuler',\n 'form.close': 'Fermer',\n 'form.submitting': 'Envoi…',\n 'form.success': 'Merci — votre commentaire a été envoyé.',\n 'form.error': 'Échec d’envoi. Veuillez réessayer.',\n 'form.description.required': 'Décrivez le problème avant d’envoyer.',\n 'form.screenshot.label': 'Capture d’écran',\n 'form.screenshot.cta_click': 'Cliquez',\n 'form.screenshot.cta_rest': 'déposez ou collez une image',\n 'form.screenshot.formats': 'PNG, JPEG ou WebP — jusqu’à 10 Mo',\n 'form.screenshot.remove': 'Retirer la capture',\n 'form.screenshot.annotate': 'Annoter',\n 'form.screenshot.error_type': 'Format non supporté. Utilisez PNG, JPEG ou WebP.',\n 'form.screenshot.error_size': 'Fichier trop volumineux (max {max} Mo).',\n 'form.screenshot.or': 'ou',\n 'form.screenshot.capture_page': 'Capturer la page',\n 'form.screenshot.capture_page_hint': 'Choisissez un onglet ou une fenêtre à partager',\n 'form.screenshot.capture_error': 'Impossible de capturer la page. Téléversez une capture à la place.',\n 'form.context.label': 'Page',\n 'type.bug': 'Bogue',\n 'type.feature': 'Suggestion',\n 'type.question': 'Question',\n 'type.praise': 'Compliment',\n 'type.typo': 'Coquille',\n 'severity.blocker': 'Bloquant',\n 'severity.high': 'Élevée',\n 'severity.medium': 'Moyenne',\n 'severity.low': 'Faible',\n 'annotator.title': 'Annoter la capture',\n 'annotator.tool.rectangle': 'Rectangle',\n 'annotator.tool.arrow': 'Flèche',\n 'annotator.tool.freehand': 'Dessin libre',\n 'annotator.tool.text': 'Texte',\n 'annotator.tool.highlight': 'Surligner',\n 'annotator.tool.blur': 'Flouter (données sensibles)',\n 'annotator.text_prompt': 'Entrez le texte :',\n 'annotator.color_picker': 'Couleur personnalisée',\n 'annotator.undo': 'Annuler',\n 'annotator.redo': 'Refaire',\n 'annotator.clear': 'Tout effacer',\n 'annotator.count_suffix': 'annotations',\n 'annotator.loading': 'Chargement…',\n 'annotator.apply': 'Appliquer',\n 'annotator.applying': 'Application…',\n 'tab.send': 'Envoyer',\n 'tab.mine': 'Mes rapports',\n 'tab.changelog': 'Cette semaine',\n 'tab.board': 'Tableau',\n 'board.kpi.total': 'Total',\n 'board.kpi.new': 'Nouveau',\n 'board.kpi.in_progress': 'En cours',\n 'board.kpi.awaiting_validation': 'À valider',\n 'board.kpi.closed': 'Fermé',\n 'board.kpi.rejected': 'Refusé',\n 'board.kpi.resolution_rate': 'Taux de résolution',\n 'board.filter.status': 'Statut',\n 'board.filter.type': 'Type',\n 'board.filter.severity': 'Sévérité',\n 'board.filter.search.placeholder': 'Rechercher…',\n 'board.filter.mine': 'Les miens',\n 'board.filter.clear': 'Effacer les filtres',\n 'board.list.empty.title': 'Rien à voir ici',\n 'board.list.empty.description': 'Les rapports apparaîtront ici dès qu’ils arrivent.',\n 'board.list.loading': 'Chargement…',\n 'board.list.error': 'Impossible de charger les rapports. Réessayez.',\n 'board.list.you': 'vous',\n 'board.list.count': '{n} sur {total}',\n 'board.detail.empty': 'Sélectionnez un rapport à gauche pour voir le fil complet.',\n 'board.detail.by': 'Par',\n 'board.detail.confirm_resolution': 'Confirmer la résolution',\n 'board.detail.status_history': 'Historique',\n 'board.scope.project': 'Rapports du projet',\n 'board.scope.mine': 'Vos rapports',\n 'board.back': 'Retour',\n 'changelog.empty.title': 'Rien de résolu pour l’instant',\n 'changelog.empty.body': 'Quand un rapport que vous avez envoyé est corrigé, il apparaîtra ici, regroupé par semaine.',\n 'changelog.week_of': 'Semaine du {date}',\n 'changelog.resolved_one': '{count} résolu',\n 'changelog.resolved_many': '{count} résolus',\n 'mine.empty.title': 'Aucun rapport',\n 'mine.empty.body': 'Après votre premier envoi vous pourrez suivre la conversation ici.',\n 'mine.refresh': 'Actualiser',\n 'mine.loading': 'Chargement…',\n 'mine.error': 'Impossible de charger vos rapports.',\n 'mine.replies_one': '1 réponse',\n 'mine.replies_many': '{count} réponses',\n 'mine.filter.empty': 'Aucun rapport ne correspond à ce filtre.',\n 'kpi.new': 'Nouveau',\n 'kpi.in_progress': 'En cours',\n 'kpi.awaiting_validation': 'À valider',\n 'kpi.resolution_rate': 'Taux de résolution',\n 'detail.back': 'Retour',\n 'detail.thread': 'Conversation',\n 'detail.no_replies': 'Pas encore de réponse — vous serez notifié dès qu’un opérateur répondra.',\n 'detail.compose_placeholder': 'Ajouter une réponse…',\n 'detail.compose_send': 'Répondre',\n 'detail.compose_sending': 'Envoi…',\n 'detail.close_cta': 'Marquer comme résolu',\n 'detail.close_busy': 'Validation…',\n 'detail.history': 'Historique du statut',\n 'detail.context.submitted_at': 'Envoyé',\n 'detail.context.page': 'Page',\n 'detail.context.capture.manual': 'Capture manuelle',\n 'detail.context.capture.html2canvas': 'Capture automatique',\n 'detail.context.capture.display_media': 'Partage d’écran',\n 'detail.context.capture.none': 'Sans capture',\n 'detail.author.staff': 'Opérateur',\n 'detail.author.mcp': 'Équipe Mhosaic',\n 'detail.author.system': 'Système',\n 'detail.tech.title': 'Ce que nous avons reçu',\n 'detail.tech.errors_one': 'erreur',\n 'detail.tech.errors_many': 'erreurs',\n 'detail.tech.device': 'Appareil',\n 'detail.tech.device.viewport': 'Fenêtre',\n 'detail.tech.device.platform': 'Plateforme',\n 'detail.tech.device.language': 'Langue',\n 'detail.tech.device.timezone': 'Fuseau horaire',\n 'detail.tech.device.connection': 'Connexion',\n 'detail.tech.errors': 'Erreurs d’exécution',\n 'detail.tech.console': 'Console (20 derniers)',\n 'detail.tech.network': 'Réseau (15 derniers)',\n 'status.new': 'Nouveau',\n 'status.in_progress': 'En cours',\n 'status.awaiting_validation': 'En attente de validation',\n 'status.closed': 'Fermé',\n 'status.rejected': 'Rejeté',\n 'status.duplicate': 'Doublon',\n 'status.wontfix': 'Non corrigé',\n}\n\nconst LOCALE_PACKS: Record<string, Record<StringKey, string>> = {\n fr: FRENCH_STRINGS,\n}\n\ninterface ResolveOptions {\n locale?: string\n}\n\nfunction packFor(locale: string | undefined): Record<StringKey, string> | null {\n if (!locale) return null\n // Match the language subtag only (fr-CA, fr-FR → fr).\n const tag = locale.toLowerCase().split(/[-_]/)[0]!\n return LOCALE_PACKS[tag] ?? null\n}\n\nexport function resolveStrings(\n overrides: Record<string, string>,\n options: ResolveOptions = {},\n): Record<StringKey, string> {\n const localePack = packFor(options.locale) ?? {}\n return {\n ...DEFAULT_STRINGS,\n ...localePack,\n ...overrides,\n } as Record<StringKey, string>\n}\n","import { h, render } from 'preact'\nimport { useCallback, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport { BoardView } from './BoardView'\nimport { ChangelogList } from './ChangelogList'\nimport { Fab } from './Fab'\nimport { Form, type FormValues } from './Form'\nimport { MineList } from './MineList'\nimport { Modal } from './Modal'\nimport { ReportDetailView } from './ReportDetailView'\nimport { WIDGET_STYLES } from './styles'\nimport type { StringKey } from './i18n'\n\nexport interface MountOptions {\n host: HTMLElement\n strings: Record<StringKey, string>\n showFAB: boolean\n onSubmit: (values: FormValues) => Promise<void>\n /** v0.7: ApiClient for the conversation-loop reader UI. Optional so\n * the synthetic / auto-error path keeps working with no host changes. */\n api?: ApiClient\n /** v0.7: callback that returns the host's identified user id, or\n * undefined when no `identify()` has been called yet. The \"My\n * reports\" tab + FAB visibility both depend on this. */\n getExternalId?: () => string | undefined\n}\n\nexport interface MountHandle {\n open(): void\n close(): void\n dispose(): void\n /** Force a re-render — e.g. after the host calls `identify()` so\n * the FAB becomes visible without a page reload. */\n notifyIdentityChanged(): void\n}\n\ntype Status = 'idle' | 'submitting' | 'error' | 'success'\ntype Tab = 'send' | 'mine' | 'changelog' | 'board'\ninterface State {\n open: boolean\n status: Status\n error?: string\n tab: Tab\n /** When set, MineList is replaced by the detail view for that report id. */\n selectedReportId?: string\n}\n\nexport function mountWidget(options: MountOptions): MountHandle {\n const shadow = options.host.attachShadow({ mode: 'open' })\n const style = document.createElement('style')\n style.textContent = WIDGET_STYLES\n shadow.appendChild(style)\n const mountPoint = document.createElement('div')\n shadow.appendChild(mountPoint)\n\n let currentState: State = { open: false, status: 'idle', tab: 'send' }\n\n function rerender(state: State) {\n currentState = state\n render(h(Root, { state }), mountPoint)\n }\n\n /** Strip selectedReportId rather than reassigning `undefined` — under\n * exactOptionalPropertyTypes the explicit `undefined` is not assignable\n * back into the optional slot. */\n function clearSelected(s: State): State {\n const { selectedReportId: _drop, ...rest } = s\n void _drop\n return rest\n }\n\n function Root({ state }: { state: State }) {\n const handleSubmit = useCallback(async (values: FormValues) => {\n rerender({ ...currentState, status: 'submitting' })\n try {\n await options.onSubmit(values)\n rerender({ ...currentState, status: 'success' })\n setTimeout(\n () =>\n rerender({\n ...currentState,\n open: false,\n status: 'idle',\n // After a successful submit, jump the user to \"My reports\" so\n // they immediately see their just-filed report in the list\n // (and the conversation that's about to start).\n tab: options.api ? 'mine' : 'send',\n }),\n 1200,\n )\n } catch (err) {\n rerender({\n ...currentState,\n status: 'error',\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }, [])\n\n const externalId = options.getExternalId?.()\n // FAB-visibility gate: when the host opts into identity (passes a\n // getExternalId callback), require a resolved user before the FAB\n // appears — that's the \"only allowed users see the widget\" semantics\n // Victor signed off on. Legacy hosts that never wired identity stay\n // on the old \"showFAB → visible\" path.\n const fabVisible =\n options.showFAB &&\n (options.getExternalId === undefined ? true : Boolean(externalId))\n const showMineTab = Boolean(options.api && externalId)\n\n return (\n <>\n {fabVisible && (\n <Fab\n label={options.strings['fab.label']}\n onClick={() => rerender({ ...currentState, open: true })}\n />\n )}\n {state.open && (\n <Modal\n onDismiss={() =>\n rerender(clearSelected({ ...currentState, open: false, status: 'idle' }))\n }\n closeLabel={options.strings['form.close']}\n expanded={state.tab === 'board'}\n >\n {showMineTab && (\n <div class=\"tab-strip\" role=\"tablist\">\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'send'}\n class={`tab-button ${state.tab === 'send' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'send' }))\n }\n >\n {options.strings['tab.send']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'mine'}\n class={`tab-button ${state.tab === 'mine' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'mine' }))\n }\n >\n {options.strings['tab.mine']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'changelog'}\n class={`tab-button ${state.tab === 'changelog' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'changelog' }))\n }\n >\n {options.strings['tab.changelog']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'board'}\n class={`tab-button tab-button--board ${state.tab === 'board' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'board' }))\n }\n >\n {options.strings['tab.board']}\n </button>\n </div>\n )}\n {state.tab === 'send' && (\n <Form\n strings={options.strings}\n onSubmit={handleSubmit}\n onCancel={() =>\n rerender({ ...currentState, open: false, status: 'idle' })\n }\n status={state.status}\n {...(state.error !== undefined && { errorMessage: state.error })}\n />\n )}\n {state.tab === 'mine' && options.api && externalId && !state.selectedReportId && (\n <MineList\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n onSelect={(row) =>\n rerender({ ...currentState, selectedReportId: row.id })\n }\n />\n )}\n {(state.tab === 'mine' || state.tab === 'changelog') &&\n options.api &&\n externalId &&\n state.selectedReportId && (\n <ReportDetailView\n api={options.api}\n externalId={externalId}\n reportId={state.selectedReportId}\n strings={options.strings}\n onBack={() =>\n rerender(clearSelected({ ...currentState }))\n }\n />\n )}\n {state.tab === 'changelog' &&\n options.api &&\n externalId &&\n !state.selectedReportId && (\n <ChangelogList\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n onSelect={(row) =>\n rerender({ ...currentState, selectedReportId: row.id })\n }\n />\n )}\n {state.tab === 'board' && options.api && externalId && (\n // BoardView owns its own master/detail navigation — no\n // selectedReportId routing through the modal-level state.\n <BoardView\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n />\n )}\n </Modal>\n )}\n </>\n )\n }\n\n rerender(currentState)\n\n return {\n open() {\n rerender({ ...currentState, open: true })\n },\n close() {\n rerender({ ...currentState, open: false, status: 'idle' })\n },\n dispose() {\n render(null, mountPoint)\n options.host.innerHTML = ''\n },\n notifyIdentityChanged() {\n rerender({ ...currentState })\n },\n }\n}\n","/**\n * BoardView — the \"Voir tout\" surface (v0.12).\n *\n * Renders the project's reports in a master/detail layout with a KPI\n * strip on top and a filter row below it. Scope follows the project's\n * `share_reports_with_widget` flag server-side; the client just renders\n * what the endpoint returns and uses `is_mine` to permission-gate the\n * detail-pane status buttons.\n *\n * Architecture notes:\n * - The list polls every 30s while open (same cadence as MineList).\n * Filters trigger an immediate refetch.\n * - The KPI strip refetches alongside the list so the counts always\n * match what the user sees.\n * - Detail pane is the existing ReportDetailView used by My-reports +\n * This-week. Permission-gating happens via the `canModerate` prop\n * (false for project-scoped rows that aren't `is_mine`).\n * - The whole tab assumes the Modal it's rendered into has been\n * `expanded` — see Modal.tsx + .modal.is-expanded in styles.ts.\n */\n\nimport { h, type JSX } from 'preact'\nimport { useEffect, useMemo, useState } from 'preact/hooks'\n\nimport type { ApiClient, BoardListPage } from '../api/client'\nimport type {\n BoardFilters,\n FeedbackSeverity,\n FeedbackType,\n ReportStatus,\n WidgetBoardKpis,\n WidgetBoardRow,\n} from '../types'\nimport type { StringKey } from './i18n'\nimport { ReportDetailView } from './ReportDetailView'\n\nconst POLL_MS = 30_000\n\nconst STATUSES: ReportStatus[] = [\n 'new',\n 'in_progress',\n 'awaiting_validation',\n 'closed',\n 'rejected',\n]\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\ninterface BoardViewProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n}\n\nexport function BoardView({ api, externalId, strings }: BoardViewProps) {\n const [filters, setFilters] = useState<BoardFilters>({})\n const [page, setPage] = useState<BoardListPage | null>(null)\n const [kpis, setKpis] = useState<WidgetBoardKpis | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [selectedId, setSelectedId] = useState<string | null>(null)\n // Detail-pane mount tick — increments when the user picks a row so the\n // panel can re-mount and re-fetch without us caching old data.\n const [detailKey, setDetailKey] = useState(0)\n\n const activeFilterCount = useMemo(() => {\n return (\n (filters.status?.length ?? 0) +\n (filters.type?.length ?? 0) +\n (filters.severity?.length ?? 0) +\n (filters.q ? 1 : 0) +\n (filters.mine ? 1 : 0)\n )\n }, [filters])\n\n // Single source of truth for list + KPIs — they refetch together so the\n // counts never drift from the visible rows.\n useEffect(() => {\n let cancelled = false\n let timer: ReturnType<typeof setTimeout> | null = null\n const tick = async () => {\n try {\n const [list, k] = await Promise.all([\n api.listBoard(externalId, filters),\n api.fetchBoardKpis(externalId, filters),\n ])\n if (cancelled) return\n setPage(list)\n setKpis(k)\n setError(null)\n } catch (e) {\n if (cancelled) return\n setError(e instanceof Error ? e.message : String(e))\n } finally {\n if (!cancelled) setLoading(false)\n if (!cancelled) timer = setTimeout(tick, POLL_MS)\n }\n }\n setLoading(true)\n void tick()\n return () => {\n cancelled = true\n if (timer) clearTimeout(timer)\n }\n }, [api, externalId, filtersHash(filters)])\n\n const selectedRow = useMemo(() => {\n if (!selectedId || !page) return null\n return page.results.find((r) => r.id === selectedId) ?? null\n }, [selectedId, page])\n\n const onPickRow = (row: WidgetBoardRow) => {\n setSelectedId(row.id)\n setDetailKey((k) => k + 1)\n }\n\n return (\n <div class=\"board-view\">\n <BoardHeader strings={strings} kpis={kpis} />\n <BoardFilters\n filters={filters}\n onChange={setFilters}\n activeCount={activeFilterCount}\n strings={strings}\n />\n <div class=\"board-body\">\n <div class=\"board-list-wrap\" aria-busy={loading && !page}>\n {error && <div class=\"board-error\">{strings['board.list.error']}</div>}\n {!error && loading && !page && (\n <BoardListSkeleton />\n )}\n {!error && page && page.results.length === 0 && !loading && (\n <BoardEmpty strings={strings} />\n )}\n {!error && page && page.results.length > 0 && (\n <BoardList\n rows={page.results}\n selectedId={selectedId}\n onPick={onPickRow}\n strings={strings}\n total={page.count}\n />\n )}\n </div>\n <div class={`board-detail-wrap ${selectedRow ? 'has-selection' : ''}`}>\n {selectedRow ? (\n <ReportDetailView\n key={detailKey}\n api={api}\n externalId={externalId}\n reportId={selectedRow.id}\n strings={strings}\n onBack={() => setSelectedId(null)}\n canModerate={selectedRow.is_mine}\n variant=\"board\"\n />\n ) : (\n <div class=\"board-detail-empty\">\n <DetailEmptyIllustration />\n <p>{strings['board.detail.empty']}</p>\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n// ---------- subcomponents -------------------------------------------------\n\nfunction BoardHeader({\n strings,\n kpis,\n}: {\n strings: Record<StringKey, string>\n kpis: WidgetBoardKpis | null\n}) {\n const scopeLabel =\n kpis?.scope === 'project'\n ? strings['board.scope.project']\n : strings['board.scope.mine']\n return (\n <header class=\"board-header\">\n <div class=\"board-header-title\">\n <span class=\"board-header-emoji\" aria-hidden=\"true\">📋</span>\n <div>\n <h2 class=\"board-header-h\">{strings['tab.board']}</h2>\n <p class=\"board-header-sub\">\n {kpis ? `${kpis.total} · ${scopeLabel}` : scopeLabel}\n </p>\n </div>\n </div>\n <BoardKpiStrip kpis={kpis} strings={strings} />\n </header>\n )\n}\n\nfunction BoardKpiStrip({\n kpis,\n strings,\n}: {\n kpis: WidgetBoardKpis | null\n strings: Record<StringKey, string>\n}) {\n // Always render 4 cards so the strip's height is stable while the\n // first fetch is in flight — eliminates the layout shift the PNR\n // page has on its first paint.\n const cells: { key: string; label: string; value: string; tone: string }[] = [\n {\n key: 'new',\n label: strings['board.kpi.new'],\n value: String(kpis?.by_status?.new ?? 0),\n tone: 'tone-new',\n },\n {\n key: 'in_progress',\n label: strings['board.kpi.in_progress'],\n value: String(kpis?.by_status?.in_progress ?? 0),\n tone: 'tone-progress',\n },\n {\n key: 'awaiting_validation',\n label: strings['board.kpi.awaiting_validation'],\n value: String(kpis?.by_status?.awaiting_validation ?? 0),\n tone: 'tone-validation',\n },\n {\n key: 'rate',\n label: strings['board.kpi.resolution_rate'],\n value: kpis ? `${Math.round((kpis.resolution_rate || 0) * 100)}%` : '—',\n tone: 'tone-rate',\n },\n ]\n return (\n <div class={`board-kpi-strip ${kpis ? 'is-ready' : 'is-loading'}`} aria-live=\"polite\">\n {cells.map((c) => (\n <div key={c.key} class={`board-kpi-card ${c.tone}`}>\n <div class=\"board-kpi-value\">{c.value}</div>\n <div class=\"board-kpi-label\">{c.label}</div>\n </div>\n ))}\n </div>\n )\n}\n\nfunction BoardFilters({\n filters,\n onChange,\n activeCount,\n strings,\n}: {\n filters: BoardFilters\n onChange: (f: BoardFilters) => void\n activeCount: number\n strings: Record<StringKey, string>\n}) {\n // Under `exactOptionalPropertyTypes: true` we can't set keys to\n // `undefined` to clear them — the optional slot doesn't accept the\n // explicit undefined. Drop the key via destructuring instead.\n const setStatus = (value: ReportStatus | '') => {\n if (value === '') {\n const { status: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, status: [value] })\n }\n }\n const setType = (value: FeedbackType | '') => {\n if (value === '') {\n const { type: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, type: [value] })\n }\n }\n const setSeverity = (value: FeedbackSeverity | '') => {\n if (value === '') {\n const { severity: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, severity: [value] })\n }\n }\n const setSearch = (q: string) => onChange({ ...filters, q })\n const toggleMine = () => {\n if (filters.mine) {\n const { mine: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, mine: true })\n }\n }\n const clear = () => onChange({})\n return (\n <div class=\"board-filters\">\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.status']}\n value={filters.status?.[0] ?? ''}\n onChange={(e) =>\n setStatus((e.target as HTMLSelectElement).value as ReportStatus | '')\n }\n >\n <option value=\"\">{strings['board.filter.status']}</option>\n {STATUSES.map((s) => (\n <option key={s} value={s}>\n {strings[`board.kpi.${s}` as StringKey] ?? s}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.type']}\n value={filters.type?.[0] ?? ''}\n onChange={(e) =>\n setType((e.target as HTMLSelectElement).value as FeedbackType | '')\n }\n >\n <option value=\"\">{strings['board.filter.type']}</option>\n {TYPES.map((t) => (\n <option key={t} value={t}>\n {strings[`type.${t}` as StringKey] ?? t}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.severity']}\n value={filters.severity?.[0] ?? ''}\n onChange={(e) =>\n setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity | '')\n }\n >\n <option value=\"\">{strings['board.filter.severity']}</option>\n {SEVERITIES.map((s) => (\n <option key={s} value={s}>\n {strings[`severity.${s}` as StringKey] ?? s}\n </option>\n ))}\n </select>\n <input\n type=\"search\"\n class=\"board-filter-search\"\n placeholder={strings['board.filter.search.placeholder']}\n value={filters.q ?? ''}\n onInput={(e) => setSearch((e.target as HTMLInputElement).value)}\n />\n <label class=\"board-filter-toggle\">\n <input\n type=\"checkbox\"\n checked={Boolean(filters.mine)}\n onChange={toggleMine}\n />\n <span>{strings['board.filter.mine']}</span>\n </label>\n {activeCount > 0 && (\n <button type=\"button\" class=\"board-filter-clear\" onClick={clear}>\n <CloseIcon />\n {strings['board.filter.clear']}\n </button>\n )}\n </div>\n )\n}\n\nfunction BoardList({\n rows,\n selectedId,\n onPick,\n strings,\n total,\n}: {\n rows: WidgetBoardRow[]\n selectedId: string | null\n onPick: (row: WidgetBoardRow) => void\n strings: Record<StringKey, string>\n total: number\n}) {\n return (\n <>\n <div class=\"board-list-count\">\n {strings['board.list.count']\n .replace('{n}', String(rows.length))\n .replace('{total}', String(total))}\n </div>\n <ul class=\"board-list\" role=\"list\">\n {rows.map((row) => (\n <BoardRowCard\n key={row.id}\n row={row}\n selected={row.id === selectedId}\n onPick={onPick}\n strings={strings}\n />\n ))}\n </ul>\n </>\n )\n}\n\nfunction BoardRowCard({\n row,\n selected,\n onPick,\n strings,\n}: {\n row: WidgetBoardRow\n selected: boolean\n onPick: (row: WidgetBoardRow) => void\n strings: Record<StringKey, string>\n}) {\n const author = row.is_mine\n ? strings['board.list.you']\n : row.author_label || '—'\n return (\n <li>\n <button\n type=\"button\"\n class={`board-row ${selected ? 'is-selected' : ''}`}\n onClick={() => onPick(row)}\n aria-pressed={selected}\n >\n <div class=\"board-row-badges\">\n <span class={`badge status-${row.status}`}>\n {strings[`status.${row.status}` as StringKey] ?? row.status}\n </span>\n <span class={`badge severity-${row.severity}`}>\n {strings[`severity.${row.severity}` as StringKey] ?? row.severity}\n </span>\n </div>\n <div class=\"board-row-description\">{row.description}</div>\n <div class=\"board-row-meta\">\n <span class=\"board-row-author\">{author}</span>\n {row.comment_count > 0 && (\n <span class=\"board-row-comments\">\n <CommentIcon /> {row.comment_count}\n </span>\n )}\n <span class=\"board-row-time\">{formatRelative(row.created_at)}</span>\n </div>\n </button>\n </li>\n )\n}\n\nfunction BoardEmpty({ strings }: { strings: Record<StringKey, string> }) {\n return (\n <div class=\"board-empty\">\n <EmptyIllustration />\n <h3>{strings['board.list.empty.title']}</h3>\n <p>{strings['board.list.empty.description']}</p>\n </div>\n )\n}\n\nfunction BoardListSkeleton() {\n return (\n <ul class=\"board-list board-list-skeleton\" aria-hidden=\"true\">\n {Array.from({ length: 5 }).map((_, i) => (\n <li key={i}>\n <div class=\"board-row skeleton-row\">\n <div class=\"skeleton-line skeleton-badges\" />\n <div class=\"skeleton-line skeleton-text\" />\n <div class=\"skeleton-line skeleton-text short\" />\n </div>\n </li>\n ))}\n </ul>\n )\n}\n\n// ---------- icons ---------------------------------------------------------\n\nfunction CommentIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n )\n}\n\nfunction CloseIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n )\n}\n\nfunction EmptyIllustration(): JSX.Element {\n return (\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"32\" cy=\"32\" r=\"28\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" />\n <path d=\"M22 32h20M32 22v20\" stroke=\"currentColor\" stroke-opacity=\"0.35\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n )\n}\n\nfunction DetailEmptyIllustration(): JSX.Element {\n return (\n <svg width=\"80\" height=\"80\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n <rect x=\"10\" y=\"12\" width=\"44\" height=\"36\" rx=\"4\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" />\n <line x1=\"18\" y1=\"22\" x2=\"46\" y2=\"22\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <line x1=\"18\" y1=\"30\" x2=\"38\" y2=\"30\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <line x1=\"18\" y1=\"38\" x2=\"32\" y2=\"38\" stroke=\"currentColor\" stroke-opacity=\"0.14\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n )\n}\n\n// ---------- helpers -------------------------------------------------------\n\nfunction filtersHash(f: BoardFilters): string {\n // Stable serialization for the useEffect dep array — avoids re-running\n // when the BoardView re-renders with a new object identity but the\n // same filter values.\n return JSON.stringify({\n s: f.status?.slice().sort(),\n t: f.type?.slice().sort(),\n sv: f.severity?.slice().sort(),\n q: f.q ?? '',\n m: Boolean(f.mine),\n })\n}\n\nfunction formatRelative(iso: string): string {\n // Tiny \"5m / 3h / 2d / 5w\" formatter — same shape PNR uses. Doesn't\n // depend on date-fns; keeps the bundle lean.\n const then = new Date(iso).getTime()\n if (Number.isNaN(then)) return ''\n const delta = Math.max(0, Date.now() - then)\n const m = Math.floor(delta / 60_000)\n if (m < 1) return 'just now'\n if (m < 60) return `${m}m`\n const h = Math.floor(m / 60)\n if (h < 24) return `${h}h`\n const d = Math.floor(h / 24)\n if (d < 7) return `${d}d`\n const w = Math.floor(d / 7)\n return `${w}w`\n}\n","/**\n * ReportDetailView — selected report's full thread.\n *\n * Renders: status pill + description + screenshot + comment thread +\n * compose box + (when status=awaiting_validation) \"Mark as resolved\"\n * button. Polls every 30 s while open so the user sees an operator's\n * reply without manually refreshing.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type {\n CapturedContext,\n ConsoleEntry,\n ErrorEntry,\n NetworkEntry,\n WidgetCommentRow,\n WidgetReportDetail,\n WidgetStatusChangeRow,\n} from '../types'\nimport { CommentBubble } from './CommentBubble'\nimport type { StringKey } from './i18n'\nimport { truncateUrl } from './screenshot-utils'\n\ninterface ReportDetailViewProps {\n api: ApiClient\n externalId: string\n reportId: string\n strings: Record<StringKey, string>\n onBack: () => void\n /** When false, the self-service \"Confirm resolution\" button is hidden\n * even if the report is in awaiting_validation. The Board passes false\n * for project-scoped rows that don't belong to the current user. */\n canModerate?: boolean\n /** Layout flavor. \"modal\" (default) keeps the original My-reports look —\n * scrollable card centered in the modal. \"board\" tightens the padding\n * + drops the top \"← back\" because the Board renders the back action\n * in its own header. */\n variant?: 'modal' | 'board'\n}\n\nconst POLL_MS = 30_000\n\nexport function ReportDetailView({\n api,\n externalId,\n reportId,\n strings,\n onBack,\n canModerate = true,\n variant = 'modal',\n}: ReportDetailViewProps) {\n const [detail, setDetail] = useState<WidgetReportDetail | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [composeBody, setComposeBody] = useState('')\n const [sending, setSending] = useState(false)\n const [closing, setClosing] = useState(false)\n const mountedRef = useRef(true)\n\n const fetchDetail = async () => {\n try {\n const next = await api.getReport(reportId, externalId)\n if (!mountedRef.current) return\n setDetail(next)\n setError(null)\n } catch (err) {\n if (!mountedRef.current) return\n setError(err instanceof Error ? err.message : 'load_failed')\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n void fetchDetail()\n const timer = setInterval(() => {\n void fetchDetail()\n }, POLL_MS)\n return () => {\n mountedRef.current = false\n clearInterval(timer)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [reportId, externalId])\n\n const handleSend = async () => {\n if (!composeBody.trim() || sending) return\n setSending(true)\n try {\n const nonce = `${reportId}:${Date.now()}`\n const created = await api.addComment(reportId, externalId, composeBody.trim(), nonce)\n if (!mountedRef.current) return\n setComposeBody('')\n // Optimistically append + then refetch so server-rendered timestamps\n // and any race with operator replies stay coherent.\n setDetail((prev) =>\n prev ? { ...prev, comments: appendComment(prev.comments, created) } : prev,\n )\n void fetchDetail()\n } catch (err) {\n if (!mountedRef.current) return\n setError(err instanceof Error ? err.message : 'comment_failed')\n } finally {\n if (mountedRef.current) setSending(false)\n }\n }\n\n const handleClose = async () => {\n if (closing) return\n setClosing(true)\n try {\n const next = await api.closeAsResolved(reportId, externalId)\n if (!mountedRef.current) return\n setDetail(next)\n } catch (err) {\n if (!mountedRef.current) return\n setError(err instanceof Error ? err.message : 'close_failed')\n } finally {\n if (mountedRef.current) setClosing(false)\n }\n }\n\n if (!detail && !error) {\n return <div class=\"mine-loading\">{strings['mine.loading']}</div>\n }\n\n if (!detail) {\n return (\n <div class=\"error\" role=\"alert\">\n {error}\n </div>\n )\n }\n\n // \"Confirm resolution\" button is the only self-service status transition\n // the widget allows (awaiting_validation → closed). Anyone moderating\n // — i.e. on their own report — can press it. Board-tab readers viewing\n // someone else's project-scoped row pass canModerate=false and the\n // button stays hidden so they can't trip the (server-enforced) refusal.\n const showCloseCta = canModerate && detail.status === 'awaiting_validation'\n\n return (\n <div class={`report-detail variant-${variant}`}>\n {variant === 'modal' && (\n <div class=\"report-detail-header\">\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.back']}\n </button>\n <span class={`pill pill-status pill-status--${detail.status}`}>\n {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n </span>\n </div>\n )}\n {variant === 'board' && (\n <div class=\"report-detail-header report-detail-header--board\">\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-back\"\n onClick={onBack}\n aria-label={strings['board.back']}\n >\n ← {strings['board.back']}\n </button>\n <span class={`pill pill-status pill-status--${detail.status}`}>\n {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n </span>\n </div>\n )}\n\n <div class=\"report-detail-body\">\n <ContextBlock detail={detail} strings={strings} />\n <p class=\"report-detail-description\">{detail.description}</p>\n {detail.screenshot_url && (\n <a\n class=\"report-detail-screenshot\"\n href={detail.screenshot_url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <img src={detail.screenshot_url} alt=\"\" loading=\"lazy\" />\n </a>\n )}\n\n <h3 class=\"report-detail-section\">{strings['detail.thread']}</h3>\n {detail.comments.length === 0 ? (\n <p class=\"report-detail-empty\">{strings['detail.no_replies']}</p>\n ) : (\n <ul class=\"report-comments\">\n {detail.comments.map((c) => (\n <li>\n <CommentBubble comment={c} strings={strings} />\n </li>\n ))}\n </ul>\n )}\n\n {detail.status_history && detail.status_history.length > 0 && (\n <StatusHistorySection rows={detail.status_history} strings={strings} />\n )}\n\n {detail.technical_context && (\n <TechnicalContextDrawer ctx={detail.technical_context} strings={strings} />\n )}\n\n <div class=\"report-compose\">\n <textarea\n value={composeBody}\n placeholder={strings['detail.compose_placeholder']}\n onInput={(e) => setComposeBody((e.target as HTMLTextAreaElement).value)}\n disabled={sending}\n />\n <div class=\"report-compose-actions\">\n {showCloseCta && (\n <button\n type=\"button\"\n class=\"btn\"\n onClick={handleClose}\n disabled={closing}\n >\n {closing ? strings['detail.close_busy'] : strings['detail.close_cta']}\n </button>\n )}\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSend}\n disabled={!composeBody.trim() || sending}\n >\n {sending ? strings['detail.compose_sending'] : strings['detail.compose_send']}\n </button>\n </div>\n </div>\n\n {error && <div class=\"error\">{error}</div>}\n </div>\n </div>\n )\n}\n\nfunction appendComment(\n current: WidgetCommentRow[],\n next: WidgetCommentRow,\n): WidgetCommentRow[] {\n if (current.some((c) => c.id === next.id)) return current\n return [...current, next]\n}\n\ninterface ContextBlockProps {\n detail: WidgetReportDetail\n strings: Record<StringKey, string>\n}\n\n/**\n * The original-context block that sits above the description on the\n * detail page. Surfaces what the user originally reported (type +\n * severity at submit time, page they were on, when they sent it,\n * how the screenshot was captured) so they can recall the report\n * without having to re-derive it from the description.\n */\nfunction ContextBlock({ detail, strings }: ContextBlockProps) {\n const captureKey: StringKey = `detail.context.capture.${detail.capture_method}` as StringKey\n const captureLabel = strings[captureKey] ?? detail.capture_method\n return (\n <div class=\"report-detail-context\">\n <div class=\"report-detail-context-pills\">\n <span class=\"pill pill-type\">{strings[`type.${detail.feedback_type}` as StringKey]}</span>\n <span class={`pill pill-severity pill-severity--${detail.severity}`}>\n {strings[`severity.${detail.severity}` as StringKey]}\n </span>\n <span class=\"pill pill-capture\">{captureLabel}</span>\n </div>\n <div class=\"report-detail-context-line\">\n <span class=\"report-detail-context-label\">{strings['detail.context.submitted_at']}</span>\n <span>{formatSubmittedAt(detail.created_at)}</span>\n </div>\n {detail.page_url && (\n <div class=\"report-detail-context-line\" title={detail.page_url}>\n <span class=\"report-detail-context-label\">{strings['detail.context.page']}</span>\n <a\n class=\"report-detail-context-url\"\n href={detail.page_url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {truncateUrl(detail.page_url, 64)}\n </a>\n </div>\n )}\n </div>\n )\n}\n\nfunction formatSubmittedAt(iso: string): string {\n try {\n return new Date(iso).toLocaleString(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n } catch {\n return iso\n }\n}\n\ninterface TechnicalContextDrawerProps {\n ctx: CapturedContext\n strings: Record<StringKey, string>\n}\n\nconst TECH_CONSOLE_LIMIT = 20\nconst TECH_NETWORK_LIMIT = 15\n\n/**\n * The \"transparency drawer\" — collapsible panel showing the user the\n * exact diagnostic data their browser submitted with the report (console\n * tail, network tail, JS errors, device summary). Closed by default so\n * the regular conversation flow stays uncluttered; one click reveals\n * everything. Borrowed from thePnr's customer-facing `TechnicalContextBlock`.\n */\nfunction TechnicalContextDrawer({ ctx, strings }: TechnicalContextDrawerProps) {\n const errors = ctx.errors ?? []\n const consoleLogs = (ctx.consoleLogs ?? []).slice(-TECH_CONSOLE_LIMIT)\n const network = (ctx.networkRequests ?? []).slice(-TECH_NETWORK_LIMIT)\n const device = ctx.device\n const hasAny =\n !!device || errors.length > 0 || consoleLogs.length > 0 || network.length > 0\n if (!hasAny) return null\n const errorCount = errors.length\n const summary =\n errorCount > 0\n ? `${strings['detail.tech.title']} · ${errorCount} ${\n errorCount === 1 ? strings['detail.tech.errors_one'] : strings['detail.tech.errors_many']\n }`\n : strings['detail.tech.title']\n return (\n <details class=\"report-detail-tech\">\n <summary>{summary}</summary>\n <div class=\"tech-body\">\n {device && <DeviceSection device={device} strings={strings} />}\n {errors.length > 0 && <ErrorsSection errors={errors} strings={strings} />}\n {consoleLogs.length > 0 && <ConsoleSection logs={consoleLogs} strings={strings} />}\n {network.length > 0 && <NetworkSection rows={network} strings={strings} />}\n </div>\n </details>\n )\n}\n\nfunction DeviceSection({\n device,\n strings,\n}: {\n device: CapturedContext['device']\n strings: Record<StringKey, string>\n}) {\n const parts: Array<{ label: string; value: string }> = []\n if (device?.viewport) {\n const dpr = device.viewport.dpr ? ` @${device.viewport.dpr}x` : ''\n parts.push({\n label: strings['detail.tech.device.viewport'],\n value: `${device.viewport.w}×${device.viewport.h}${dpr}`,\n })\n }\n if (device?.platform) parts.push({ label: strings['detail.tech.device.platform'], value: device.platform })\n if (device?.language) parts.push({ label: strings['detail.tech.device.language'], value: device.language })\n if (device?.timezone) parts.push({ label: strings['detail.tech.device.timezone'], value: device.timezone })\n if (device?.connection) parts.push({ label: strings['detail.tech.device.connection'], value: device.connection })\n if (parts.length === 0) return null\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.device']}</h4>\n <dl class=\"tech-device\">\n {parts.map((p) => (\n <>\n <dt>{p.label}</dt>\n <dd>{p.value}</dd>\n </>\n ))}\n </dl>\n </div>\n )\n}\n\nfunction ErrorsSection({\n errors,\n strings,\n}: {\n errors: ErrorEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.errors']}</h4>\n <ul class=\"tech-errors\">\n {errors.map((e) => (\n <li>\n <div class=\"tech-errors-msg\">{e.message}</div>\n {e.stack && <pre class=\"tech-errors-stack\">{truncateStack(e.stack)}</pre>}\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction ConsoleSection({\n logs,\n strings,\n}: {\n logs: ConsoleEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.console']}</h4>\n <ul class=\"tech-console\">\n {logs.map((l) => (\n <li class={`tech-console-row tech-console-row--${l.level}`}>\n <span class=\"tech-console-level\">{l.level}</span>\n <span class=\"tech-console-msg\">{l.message}</span>\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction NetworkSection({\n rows,\n strings,\n}: {\n rows: NetworkEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.network']}</h4>\n <ul class=\"tech-network\">\n {rows.map((n) => {\n const failed = n.status >= 400 || n.status === 0\n return (\n <li class={`tech-network-row${failed ? ' tech-network-row--fail' : ''}`}>\n <span class=\"tech-network-status\">{n.status || '—'}</span>\n <span class=\"tech-network-method\">{n.method}</span>\n <span class=\"tech-network-url\" title={n.url}>\n {truncateUrl(n.url, 56)}\n </span>\n <span class=\"tech-network-time\">{Math.round(n.durationMs)}ms</span>\n </li>\n )\n })}\n </ul>\n </div>\n )\n}\n\nfunction truncateStack(stack: string): string {\n // Clip to first 12 frames so the drawer doesn't blow out vertically on\n // a deep React stack — the user can always click through to a full view\n // if we add one later.\n const lines = stack.split('\\n')\n if (lines.length <= 12) return stack\n return lines.slice(0, 12).join('\\n') + '\\n …'\n}\n\ninterface StatusHistorySectionProps {\n rows: WidgetStatusChangeRow[]\n strings: Record<StringKey, string>\n}\n\n/**\n * The full audit trail of who changed the report's status, when, and via\n * which surface (operator UI, MCP agent, or system). Read-only — purely\n * a transparency / \"you're being heard by real humans\" gesture borrowed\n * from thePnr's customer-facing detail view.\n */\nfunction StatusHistorySection({ rows, strings }: StatusHistorySectionProps) {\n return (\n <div class=\"report-detail-history\">\n <h3 class=\"report-detail-section\">{strings['detail.history']}</h3>\n <ol class=\"status-history\">\n {rows.map((r) => {\n const from = strings[`status.${r.from_status}` as StringKey] ?? r.from_status\n const to = strings[`status.${r.to_status}` as StringKey] ?? r.to_status\n const sourceKey: StringKey =\n r.changed_by_source === 'mcp'\n ? 'detail.author.mcp'\n : r.changed_by_source === 'system'\n ? 'detail.author.system'\n : 'detail.author.staff'\n return (\n <li class=\"status-history-row\">\n <span class=\"status-history-time\">{formatSubmittedAt(r.created_at)}</span>\n <span class=\"status-history-transition\">\n <span class={`pill pill-status pill-status--${r.from_status}`}>{from}</span>\n <span class=\"status-history-arrow\" aria-hidden=\"true\">→</span>\n <span class={`pill pill-status pill-status--${r.to_status}`}>{to}</span>\n </span>\n <span class={`status-history-source status-history-source--${r.changed_by_source}`}>\n {strings[sourceKey]}\n </span>\n </li>\n )\n })}\n </ol>\n </div>\n )\n}\n","/**\n * CommentBubble — single comment rendered inside the report detail.\n *\n * USER comments float right (the user's own follow-ups). MCP / STAFF /\n * SYSTEM float left and pick up an author label so you can tell at a\n * glance who wrote what. The \"Mhosaic Team\" label for MCP comments\n * matches PNR's pattern; hosts can hide it via a project flag (future).\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetCommentRow } from '../types'\nimport type { StringKey } from './i18n'\n\ninterface CommentBubbleProps {\n comment: WidgetCommentRow\n strings: Record<StringKey, string>\n}\n\nexport function CommentBubble({ comment, strings }: CommentBubbleProps) {\n // The submitter's own follow-ups float right; everything else (operator,\n // agent, system) floats left with an attribution label. Both submitter\n // and operator comments arrive as `author_source = USER` from the\n // backend, so we rely on the server-computed `is_mine` flag to tell\n // them apart instead of guessing from the source enum.\n const isMine = comment.is_mine\n const isAgent = !isMine && comment.author_source === 'mcp'\n const isSystem = !isMine && comment.author_source === 'system'\n const variant: 'mcp' | 'system' | 'staff' = isAgent\n ? 'mcp'\n : isSystem\n ? 'system'\n : 'staff'\n const labelKey: StringKey =\n variant === 'mcp'\n ? 'detail.author.mcp'\n : variant === 'system'\n ? 'detail.author.system'\n : 'detail.author.staff'\n const label = comment.author_label || strings[labelKey]\n return (\n <div class={`comment-bubble ${isMine ? 'is-mine' : 'is-other'}`}>\n {!isMine && label && (\n <div class={`comment-author comment-author--${variant}`}>{label}</div>\n )}\n <div class=\"comment-body\">{comment.body}</div>\n <div class=\"comment-time\">{formatTime(comment.created_at)}</div>\n </div>\n )\n}\n\nfunction formatTime(iso: string): string {\n try {\n return new Date(iso).toLocaleString(undefined, {\n dateStyle: 'short',\n timeStyle: 'short',\n })\n } catch {\n return iso\n }\n}\n","/**\n * Helpers for the manual-screenshot-upload UX (v0.6.0).\n *\n * Kept tiny and dependency-free so they can be unit-tested without DOM/jsdom\n * setup. The interesting work — drag/drop, paste, file picker — lives in\n * Form.tsx.\n */\n\nexport const ALLOWED_IMAGE_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n] as const\n\nexport const MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024 // 10 MB\n\nexport type ScreenshotValidationError =\n | { kind: 'type' }\n | { kind: 'size'; maxMb: number }\n\nexport function validateScreenshotFile(file: File): ScreenshotValidationError | null {\n if (\n !ALLOWED_IMAGE_TYPES.includes(\n file.type as (typeof ALLOWED_IMAGE_TYPES)[number],\n )\n ) {\n return { kind: 'type' }\n }\n if (file.size > MAX_SCREENSHOT_BYTES) {\n return { kind: 'size', maxMb: MAX_SCREENSHOT_BYTES / (1024 * 1024) }\n }\n return null\n}\n\n/**\n * Truncate a URL while keeping the start and end visible. Used in the form's\n * \"Vous étiez ici\" footer so the user can verify the URL we'll send without\n * the long path overflowing the modal.\n *\n * \"https://app.example.com/very/long/deep/path?a=1&b=2\" (53 chars, max 30)\n * → \"https://app.examp…h?a=1&b=2\"\n */\nexport function truncateUrl(url: string, maxLength = 80): string {\n if (url.length <= maxLength) return url\n const keepStart = Math.floor((maxLength - 1) / 2)\n const keepEnd = maxLength - 1 - keepStart\n return `${url.slice(0, keepStart)}…${url.slice(url.length - keepEnd)}`\n}\n\n/**\n * Capture a single frame from the browser's native screen-share picker\n * (`navigator.mediaDevices.getDisplayMedia`). Returns the captured PNG blob,\n * or `null` if the user dismissed the picker or the API isn't available.\n *\n * v0.12 replacement for the previous implicit html2canvas-on-submit\n * fallback. The user must now explicitly opt in to a page capture either\n * by uploading a file or by clicking \"Capture this page\" → granting screen\n * share. No DOM is captured silently anymore.\n */\nexport async function captureWithDisplayMedia(): Promise<Blob | null> {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getDisplayMedia) {\n return null\n }\n let stream: MediaStream | null = null\n try {\n stream = await navigator.mediaDevices.getDisplayMedia({\n video: { displaySurface: 'browser' } as MediaTrackConstraints,\n audio: false,\n // Hint that audio isn't needed; not all browsers honor this but it\n // avoids the audio-share toggle being defaulted on in some.\n })\n } catch {\n // User dismissed the picker (NotAllowedError) or browser refused.\n // Treat both as \"no capture taken\" — caller surfaces a soft error.\n return null\n }\n try {\n const track = stream.getVideoTracks()[0]\n if (!track) return null\n // Read one frame. `ImageCapture` is supported in Chrome / Edge / Opera;\n // Safari falls back to the canvas path below.\n const ImageCaptureCtor = (window as unknown as {\n ImageCapture?: new (track: MediaStreamTrack) => {\n grabFrame(): Promise<ImageBitmap>\n }\n }).ImageCapture\n let bitmap: ImageBitmap | null = null\n if (ImageCaptureCtor) {\n try {\n const ic = new ImageCaptureCtor(track)\n bitmap = await ic.grabFrame()\n } catch {\n bitmap = null\n }\n }\n if (!bitmap) {\n // Fallback: pipe the stream into a hidden <video>, draw one frame\n // to a canvas, read it out. Works in Safari + every other browser.\n bitmap = await grabFrameViaVideo(stream)\n }\n if (!bitmap) return null\n const canvas = document.createElement('canvas')\n canvas.width = bitmap.width\n canvas.height = bitmap.height\n const ctx = canvas.getContext('2d')\n if (!ctx) return null\n ctx.drawImage(bitmap, 0, 0)\n return await new Promise<Blob | null>((resolve) =>\n canvas.toBlob(resolve, 'image/png'),\n )\n } finally {\n stream.getTracks().forEach((t) => t.stop())\n }\n}\n\nasync function grabFrameViaVideo(stream: MediaStream): Promise<ImageBitmap | null> {\n const video = document.createElement('video')\n video.muted = true\n video.playsInline = true\n video.srcObject = stream\n try {\n await video.play()\n // Wait one paint frame so the video element has dimensions.\n await new Promise<void>((r) => requestAnimationFrame(() => r()))\n if (!video.videoWidth || !video.videoHeight) return null\n return await createImageBitmap(video)\n } catch {\n return null\n }\n}\n","/**\n * ChangelogList — the \"This week\" tab.\n *\n * Renders the user's resolved reports grouped by ISO week of resolution\n * date, most recent week first. Trust-building surface borrowed from\n * thePnr's `FeedbackChangelogView` (\"see what's been fixed\"). Polls every\n * 30 s while open, same cadence as MineList.\n *\n * Scope is the submitter's own resolved reports only — multi-tenant\n * privacy default. A per-project \"show all-project changelog to\n * customers\" setting can expand the scope in a later release.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetChangelogRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { ReportRow } from './ReportRow'\n\ninterface ChangelogListProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n onSelect: (report: WidgetChangelogRow) => void\n}\n\nconst POLL_MS = 30_000\n\ninterface WeekGroup {\n /** ISO date (YYYY-MM-DD) of the Monday anchoring this week. */\n weekKey: string\n /** Localized \"Week of {Mon DD, YYYY}\" label, computed once per group. */\n label: string\n rows: WidgetChangelogRow[]\n}\n\n/**\n * Week-of-resolution key for a row. Anchored on Monday in UTC so the\n * grouping is consistent across timezones — the user-visible label is\n * localized at render time.\n */\nexport function isoWeekKey(iso: string): string {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return ''\n // JS Sunday=0; we want Monday=0 to anchor weeks the way thePnr does.\n const day = (d.getUTCDay() + 6) % 7\n const monday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - day))\n return monday.toISOString().slice(0, 10)\n}\n\nexport function groupRowsByWeek(rows: WidgetChangelogRow[]): WeekGroup[] {\n const buckets = new Map<string, WidgetChangelogRow[]>()\n for (const row of rows) {\n const key = isoWeekKey(row.resolved_at)\n if (!key) continue\n const existing = buckets.get(key)\n if (existing) existing.push(row)\n else buckets.set(key, [row])\n }\n // Sort buckets by week descending (most recent week first); within each\n // bucket the server already returned -resolved_at order.\n return Array.from(buckets.entries())\n .sort(([a], [b]) => (a < b ? 1 : -1))\n .map(([weekKey, weekRows]) => ({\n weekKey,\n label: formatWeekLabel(weekKey),\n rows: weekRows,\n }))\n}\n\nfunction formatWeekLabel(weekKey: string): string {\n // Render just the date portion (\"May 5, 2026\"); the i18n template\n // wraps it as \"Week of {date}\".\n try {\n return new Date(weekKey).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n } catch {\n return weekKey\n }\n}\n\nexport function ChangelogList({ api, externalId, strings, onSelect }: ChangelogListProps) {\n const [rows, setRows] = useState<WidgetChangelogRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [refreshing, setRefreshing] = useState(false)\n const mountedRef = useRef(true)\n\n const fetchRows = async () => {\n setRefreshing(true)\n setError(null)\n try {\n const next = await api.listChangelog(externalId)\n if (!mountedRef.current) return\n setRows(next)\n } catch (err) {\n if (!mountedRef.current) return\n setError(err instanceof Error ? err.message : strings['mine.error'])\n } finally {\n if (mountedRef.current) setRefreshing(false)\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n void fetchRows()\n const timer = setInterval(() => {\n void fetchRows()\n }, POLL_MS)\n return () => {\n mountedRef.current = false\n clearInterval(timer)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [externalId])\n\n const groups = useMemo(() => (rows ? groupRowsByWeek(rows) : []), [rows])\n const isLoading = rows === null && !error\n const isEmpty = rows !== null && rows.length === 0\n\n return (\n <div class=\"mine-list\">\n <div class=\"mine-list-header\">\n <h2>{strings['tab.changelog']}</h2>\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() => {\n void fetchRows()\n }}\n disabled={refreshing}\n >\n {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n </button>\n </div>\n {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n {error && <div class=\"error\">{error}</div>}\n {isEmpty && (\n <div class=\"mine-empty\">\n <strong>{strings['changelog.empty.title']}</strong>\n <p>{strings['changelog.empty.body']}</p>\n </div>\n )}\n {groups.length > 0 && (\n <div class=\"changelog-groups\">\n {groups.map((g) => (\n <section class=\"changelog-group\" key={g.weekKey}>\n <header class=\"changelog-group-header\">\n <span class=\"changelog-group-marker\" aria-hidden=\"true\">●</span>\n <span class=\"changelog-group-label\">\n {strings['changelog.week_of'].replace('{date}', g.label)}\n </span>\n <span class=\"changelog-group-rule\" aria-hidden=\"true\" />\n <span class=\"changelog-group-count\">\n {strings[\n g.rows.length === 1 ? 'changelog.resolved_one' : 'changelog.resolved_many'\n ].replace('{count}', String(g.rows.length))}\n </span>\n </header>\n <ul class=\"mine-rows\">\n {g.rows.map((row) => (\n <li>\n <ReportRow row={row} strings={strings} onClick={() => onSelect(row)} />\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/**\n * ReportRow — single list item in MineList.\n *\n * Shows status / type / severity badges, a one-line description preview,\n * a relative timestamp, and a \"N replies\" hint pulled from the\n * comment_count annotation on the backend. Whole row is the click\n * target so keyboard nav (Tab + Enter) works.\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\ninterface ReportRowProps {\n row: WidgetReportRow\n strings: Record<StringKey, string>\n onClick: () => void\n}\n\nfunction statusClassName(status: string): string {\n return `pill pill-status pill-status--${status}`\n}\n\nfunction severityClassName(severity: string): string {\n return `pill pill-severity pill-severity--${severity}`\n}\n\nfunction typeClassName(): string {\n return 'pill pill-type'\n}\n\nfunction formatRelative(iso: string): string {\n const then = Date.parse(iso)\n if (!Number.isFinite(then)) return ''\n const seconds = Math.max(1, Math.round((Date.now() - then) / 1000))\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes}m`\n const hours = Math.round(minutes / 60)\n if (hours < 48) return `${hours}h`\n const days = Math.round(hours / 24)\n return `${days}d`\n}\n\nfunction repliesLabel(count: number, strings: Record<StringKey, string>): string {\n if (count === 1) return strings['mine.replies_one']\n return strings['mine.replies_many'].replace('{count}', String(count))\n}\n\nexport function ReportRow({ row, strings, onClick }: ReportRowProps) {\n const preview =\n row.description.length > 120\n ? row.description.slice(0, 117) + '…'\n : row.description\n return (\n <button type=\"button\" class=\"mine-row\" onClick={onClick}>\n <div class=\"mine-row-pills\">\n <span class={statusClassName(row.status)}>{strings[`status.${row.status}` as StringKey] ?? row.status}</span>\n <span class={typeClassName()}>{strings[`type.${row.feedback_type}` as StringKey]}</span>\n <span class={severityClassName(row.severity)}>{strings[`severity.${row.severity}` as StringKey]}</span>\n </div>\n <div class=\"mine-row-preview\">{preview}</div>\n <div class=\"mine-row-meta\">\n <span>{formatRelative(row.updated_at || row.created_at)}</span>\n {row.comment_count > 0 && <span>· {repliesLabel(row.comment_count, strings)}</span>}\n </div>\n </button>\n )\n}\n","import { h } from 'preact'\n\ninterface FabProps {\n label: string\n onClick: () => void\n}\n\n/**\n * Custom chat-bubble glyph at 24px. Emoji rendered inconsistently across\n * platforms (Apple's bubble is blue, Windows' is monochrome, Android's\n * green) and couldn't inherit color — replaced with a single-color SVG\n * that picks up `currentColor` from the FAB's accent-contrast variable.\n */\nfunction ChatBubbleIcon() {\n return (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"\n fill=\"currentColor\"\n />\n </svg>\n )\n}\n\nexport function Fab({ label, onClick }: FabProps) {\n return (\n <button type=\"button\" class=\"fab\" aria-label={label} title={label} onClick={onClick}>\n <ChatBubbleIcon />\n </button>\n )\n}\n","import { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { FeedbackSeverity, FeedbackType } from '../types'\nimport { Annotator } from './Annotator'\nimport type { StringKey } from './i18n'\nimport {\n captureWithDisplayMedia,\n truncateUrl,\n validateScreenshotFile,\n} from './screenshot-utils'\n\nexport type FormStatus = 'idle' | 'submitting' | 'error' | 'success'\n\nexport interface FormValues {\n description: string\n feedback_type: FeedbackType\n severity: FeedbackSeverity\n /** Optional user-attached screenshot — via upload, paste, drag-drop,\n * annotator, or the \"Capture this page\" button (getDisplayMedia). v0.12:\n * no implicit html2canvas-on-submit anymore. */\n screenshot?: Blob\n /** How the screenshot got into the form. \"manual\" for upload/paste/drag/\n * annotator. \"display_media\" for the native screen-share path. Undefined\n * when no screenshot is attached. */\n capture_method?: 'manual' | 'display_media'\n}\n\ninterface FormProps {\n strings: Record<StringKey, string>\n onSubmit: (values: FormValues) => void\n onCancel: () => void\n status: FormStatus\n errorMessage?: string\n}\n\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\nexport function Form({ strings, onSubmit, onCancel, status, errorMessage }: FormProps) {\n const [description, setDescription] = useState('')\n const [feedbackType, setFeedbackType] = useState<FeedbackType>('bug')\n const [severity, setSeverity] = useState<FeedbackSeverity>('medium')\n const [localError, setLocalError] = useState('')\n const [screenshotBlob, setScreenshotBlob] = useState<Blob | null>(null)\n const [screenshotPreview, setScreenshotPreview] = useState<string | null>(null)\n const [screenshotMethod, setScreenshotMethod] = useState<'manual' | 'display_media'>('manual')\n const [isDragOver, setIsDragOver] = useState(false)\n const [annotatorOpen, setAnnotatorOpen] = useState(false)\n const [capturing, setCapturing] = useState(false)\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n const dropZoneRef = useRef<HTMLDivElement | null>(null)\n\n const submitting = status === 'submitting'\n const submitLabel = submitting ? strings['form.submitting'] : strings['form.submit']\n const pageUrl = typeof window !== 'undefined' ? window.location.href : ''\n\n // Revoke any object URL when the preview changes or the form unmounts.\n useEffect(() => {\n return () => {\n if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n }\n }, [screenshotPreview])\n\n const acceptFile = (file: File | Blob, method: 'manual' | 'display_media' = 'manual') => {\n setLocalError('')\n if (file instanceof File) {\n const err = validateScreenshotFile(file)\n if (err) {\n setLocalError(\n err.kind === 'type'\n ? strings['form.screenshot.error_type']\n : strings['form.screenshot.error_size'].replace('{max}', String(err.maxMb)),\n )\n return\n }\n }\n if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n setScreenshotBlob(file)\n setScreenshotPreview(URL.createObjectURL(file))\n setScreenshotMethod(method)\n }\n\n const clearScreenshot = () => {\n if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n setScreenshotPreview(null)\n setScreenshotBlob(null)\n setScreenshotMethod('manual')\n setLocalError('')\n if (fileInputRef.current) fileInputRef.current.value = ''\n }\n\n const handleCapturePage = async () => {\n if (capturing) return\n setLocalError('')\n setCapturing(true)\n try {\n const blob = await captureWithDisplayMedia()\n if (!blob) {\n // User dismissed the picker — stay quiet. Browser refusal / no\n // support → soft error so the user knows the upload path is still\n // available.\n if (\n typeof navigator === 'undefined' ||\n !navigator.mediaDevices?.getDisplayMedia\n ) {\n setLocalError(strings['form.screenshot.capture_error'])\n }\n return\n }\n // Validate the captured blob the same way we validate uploads, just\n // skipping the type check (the blob is server-minted PNG so it's\n // safe by construction).\n if (blob.size > 10 * 1024 * 1024) {\n setLocalError(\n strings['form.screenshot.error_size'].replace('{max}', '10'),\n )\n return\n }\n acceptFile(blob, 'display_media')\n } catch {\n setLocalError(strings['form.screenshot.capture_error'])\n } finally {\n setCapturing(false)\n }\n }\n\n const handleFileInputChange = (e: Event) => {\n const file = (e.target as HTMLInputElement).files?.[0]\n if (file) acceptFile(file)\n }\n\n const handleDragOver = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(true)\n }\n const handleDragLeave = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (e.currentTarget === e.target) setIsDragOver(false)\n }\n const handleDrop = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n const file = e.dataTransfer?.files?.[0]\n if (file) acceptFile(file)\n }\n\n // Clipboard paste, scoped to the dropzone — so we don't hijack paste from\n // textareas elsewhere in the page or in our own description field.\n useEffect(() => {\n const zone = dropZoneRef.current\n if (!zone) return\n const onPaste = (e: ClipboardEvent) => {\n const items = e.clipboardData?.items\n if (!items) return\n for (const item of Array.from(items)) {\n if (item.kind === 'file' && item.type.startsWith('image/')) {\n const file = item.getAsFile()\n if (file) {\n e.preventDefault()\n acceptFile(file)\n return\n }\n }\n }\n }\n zone.addEventListener('paste', onPaste)\n return () => zone.removeEventListener('paste', onPaste)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [screenshotPreview])\n\n const handleAnnotated = (annotated: Blob) => {\n if (screenshotPreview) URL.revokeObjectURL(screenshotPreview)\n setScreenshotBlob(annotated)\n setScreenshotPreview(URL.createObjectURL(annotated))\n // Annotation preserves whatever method seeded the source — a\n // display_media capture that gets annotated stays display_media.\n setAnnotatorOpen(false)\n }\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n if (!description.trim()) {\n setLocalError(strings['form.description.required'])\n return\n }\n setLocalError('')\n const values: FormValues = {\n description: description.trim(),\n feedback_type: feedbackType,\n severity,\n }\n if (screenshotBlob) {\n values.screenshot = screenshotBlob\n values.capture_method = screenshotMethod\n }\n onSubmit(values)\n }\n\n return (\n <form onSubmit={handleSubmit}>\n <h2>{strings['form.title']}</h2>\n\n <div class=\"field\">\n <label for=\"mfb-desc\">{strings['form.description.label']}</label>\n <textarea\n id=\"mfb-desc\"\n value={description}\n placeholder={strings['form.description.placeholder']}\n onInput={(e) => setDescription((e.target as HTMLTextAreaElement).value)}\n />\n </div>\n\n <div class=\"row\">\n <div class=\"field\">\n <label for=\"mfb-type\">{strings['form.type.label']}</label>\n <select\n id=\"mfb-type\"\n value={feedbackType}\n onChange={(e) => setFeedbackType((e.target as HTMLSelectElement).value as FeedbackType)}\n >\n {TYPES.map((t) => <option value={t}>{strings[`type.${t}` as StringKey]}</option>)}\n </select>\n </div>\n <div class=\"field\">\n <label for=\"mfb-sev\">{strings['form.severity.label']}</label>\n <select\n id=\"mfb-sev\"\n value={severity}\n onChange={(e) => setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity)}\n >\n {SEVERITIES.map((s) => <option value={s}>{strings[`severity.${s}` as StringKey]}</option>)}\n </select>\n </div>\n </div>\n\n <div class=\"field\">\n <label>{strings['form.screenshot.label']}</label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp\"\n class=\"mfb-sr-only\"\n onChange={handleFileInputChange}\n aria-hidden=\"true\"\n tabIndex={-1}\n />\n {screenshotPreview ? (\n <div class=\"screenshot-preview\">\n <img src={screenshotPreview} alt=\"\" />\n <div class=\"screenshot-preview-actions\">\n <button\n type=\"button\"\n class=\"btn btn--primary screenshot-annotate\"\n onClick={() => setAnnotatorOpen(true)}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>\n {strings['form.screenshot.annotate']}\n </button>\n <button type=\"button\" class=\"btn\" onClick={clearScreenshot}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z\"/></svg>\n {strings['form.screenshot.remove']}\n </button>\n </div>\n </div>\n ) : (\n <div\n ref={dropZoneRef}\n class={`screenshot-dropzone ${isDragOver ? 'is-dragover' : ''}`}\n tabIndex={0}\n role=\"button\"\n aria-label={strings['form.screenshot.label']}\n onClick={() => fileInputRef.current?.click()}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n fileInputRef.current?.click()\n }\n }}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n >\n <svg class=\"screenshot-icon\" width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\n <polyline points=\"17 8 12 3 7 8\"/>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>\n </svg>\n <div class=\"screenshot-cta\">\n <strong>{strings['form.screenshot.cta_click']}</strong>\n {', '}\n {strings['form.screenshot.cta_rest']}\n </div>\n <div class=\"screenshot-formats\">{strings['form.screenshot.formats']}</div>\n </div>\n )}\n {!screenshotPreview && (\n <div class=\"screenshot-alt\">\n <span class=\"screenshot-or\">{strings['form.screenshot.or']}</span>\n <button\n type=\"button\"\n class=\"btn btn--ghost screenshot-capture-page\"\n onClick={handleCapturePage}\n disabled={capturing || submitting}\n aria-label={strings['form.screenshot.capture_page']}\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" ry=\"2\" />\n <line x1=\"8\" y1=\"21\" x2=\"16\" y2=\"21\" />\n <line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"21\" />\n </svg>\n {capturing\n ? strings['form.submitting']\n : strings['form.screenshot.capture_page']}\n </button>\n <span class=\"screenshot-capture-hint\">\n {strings['form.screenshot.capture_page_hint']}\n </span>\n </div>\n )}\n </div>\n\n {pageUrl && (\n <div class=\"page-context\" title={pageUrl}>\n <span class=\"page-context-label\">{strings['form.context.label']}</span>\n <span class=\"page-context-url\">{truncateUrl(pageUrl, 90)}</span>\n </div>\n )}\n\n {localError && <div class=\"error\">{localError}</div>}\n {status === 'error' && errorMessage && <div class=\"error\">{errorMessage}</div>}\n {status === 'success' && <div class=\"success\">{strings['form.success']}</div>}\n\n <div class=\"actions\">\n <button type=\"button\" class=\"btn\" onClick={onCancel} disabled={submitting}>{strings['form.cancel']}</button>\n <button type=\"submit\" class=\"btn btn--primary\" disabled={submitting}>{submitLabel}</button>\n </div>\n\n {annotatorOpen && screenshotBlob && (\n <Annotator\n imageBlob={screenshotBlob}\n strings={strings}\n onCancel={() => setAnnotatorOpen(false)}\n onSave={handleAnnotated}\n />\n )}\n </form>\n )\n}\n","/**\n * Annotator — screenshot annotation modal.\n *\n * v0.6.0: rectangle, arrow, freehand, text. Custom canvas, no external lib.\n * v0.10.0: + highlight (semi-transparent fill), blur (pixelate sensitive\n * regions), full undo/redo stack, native color picker alongside swatches.\n *\n * Save rasterizes the original image + every shape onto a fresh canvas\n * and returns a PNG Blob. Blurs are always sampled from the source image\n * pixels (not the canvas), so strokes/text/highlights drawn near a blurred\n * region don't get re-blurred by accident.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'\n\nimport type { StringKey } from './i18n'\n\ntype Tool = 'rectangle' | 'arrow' | 'freehand' | 'text' | 'highlight' | 'blur'\n\ninterface BaseShape {\n color: string\n lineWidth: number\n}\n\ninterface RectShape extends BaseShape {\n kind: 'rectangle'\n x: number\n y: number\n w: number\n h: number\n}\n\ninterface ArrowShape extends BaseShape {\n kind: 'arrow'\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\ninterface FreehandShape extends BaseShape {\n kind: 'freehand'\n points: Array<{ x: number; y: number }>\n}\n\ninterface TextShape extends BaseShape {\n kind: 'text'\n x: number\n y: number\n text: string\n fontSize: number\n}\n\ninterface HighlightShape extends BaseShape {\n kind: 'highlight'\n x: number\n y: number\n w: number\n h: number\n}\n\ninterface BlurShape extends BaseShape {\n kind: 'blur'\n x: number\n y: number\n w: number\n h: number\n /** Tile size in source-image pixels. Scales with canvas width. */\n tile: number\n}\n\ntype Shape =\n | RectShape\n | ArrowShape\n | FreehandShape\n | TextShape\n | HighlightShape\n | BlurShape\n\nconst COLORS: string[] = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#ffffff']\nconst HIGHLIGHT_ALPHA = 0.35\n\n// ---------------------------------------------------------------------------\n// Drawing helpers\n// ---------------------------------------------------------------------------\n\nfunction drawShape(\n ctx: CanvasRenderingContext2D,\n shape: Shape,\n sourceImage: HTMLImageElement | null,\n) {\n ctx.save()\n ctx.strokeStyle = shape.color\n ctx.fillStyle = shape.color\n ctx.lineWidth = shape.lineWidth\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n\n if (shape.kind === 'rectangle') {\n ctx.strokeRect(shape.x, shape.y, shape.w, shape.h)\n } else if (shape.kind === 'arrow') {\n drawArrow(ctx, shape.x1, shape.y1, shape.x2, shape.y2)\n } else if (shape.kind === 'freehand') {\n if (shape.points.length < 2) {\n ctx.restore()\n return\n }\n ctx.beginPath()\n ctx.moveTo(shape.points[0]!.x, shape.points[0]!.y)\n for (let i = 1; i < shape.points.length; i++) {\n ctx.lineTo(shape.points[i]!.x, shape.points[i]!.y)\n }\n ctx.stroke()\n } else if (shape.kind === 'text') {\n ctx.font = `bold ${shape.fontSize}px -apple-system, BlinkMacSystemFont, sans-serif`\n ctx.textBaseline = 'top'\n const metrics = ctx.measureText(shape.text)\n const padding = 4\n const w = metrics.width + padding * 2\n const hh = shape.fontSize + padding * 2\n ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'\n ctx.fillRect(shape.x - padding, shape.y - padding, w, hh)\n ctx.fillStyle = shape.color\n ctx.fillText(shape.text, shape.x, shape.y)\n } else if (shape.kind === 'highlight') {\n // Semi-transparent filled rect. Multiply-ish blend so the underlying\n // pixels show through — typical marker-pen look.\n ctx.globalAlpha = HIGHLIGHT_ALPHA\n ctx.fillRect(\n normalizeStart(shape.x, shape.w),\n normalizeStart(shape.y, shape.h),\n Math.abs(shape.w),\n Math.abs(shape.h),\n )\n } else if (shape.kind === 'blur') {\n drawBlur(ctx, shape, sourceImage)\n }\n ctx.restore()\n}\n\nfunction normalizeStart(start: number, size: number): number {\n return size < 0 ? start + size : start\n}\n\nfunction drawArrow(\n ctx: CanvasRenderingContext2D,\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n) {\n const headLen = 14\n const angle = Math.atan2(y2 - y1, x2 - x1)\n ctx.beginPath()\n ctx.moveTo(x1, y1)\n ctx.lineTo(x2, y2)\n ctx.stroke()\n ctx.beginPath()\n ctx.moveTo(x2, y2)\n ctx.lineTo(\n x2 - headLen * Math.cos(angle - Math.PI / 6),\n y2 - headLen * Math.sin(angle - Math.PI / 6),\n )\n ctx.lineTo(\n x2 - headLen * Math.cos(angle + Math.PI / 6),\n y2 - headLen * Math.sin(angle + Math.PI / 6),\n )\n ctx.closePath()\n ctx.fill()\n}\n\n/**\n * Pixelate a rectangular region by averaging tiles of the source image.\n * Always samples from the original `<img>` so blurs over text/strokes\n * still hide the underlying pixels, not the marks drawn on top.\n */\nfunction drawBlur(\n ctx: CanvasRenderingContext2D,\n shape: BlurShape,\n sourceImage: HTMLImageElement | null,\n) {\n if (!sourceImage) return\n const x = normalizeStart(shape.x, shape.w)\n const y = normalizeStart(shape.y, shape.h)\n const w = Math.abs(shape.w)\n const h = Math.abs(shape.h)\n if (w < 2 || h < 2) return\n const tile = Math.max(4, shape.tile)\n // Drop tiles aligned to the source image — pixelation is at native res\n // so the result is crisp regardless of CSS scale on the display canvas.\n for (let yy = y; yy < y + h; yy += tile) {\n for (let xx = x; xx < x + w; xx += tile) {\n const tw = Math.min(tile, x + w - xx)\n const th = Math.min(tile, y + h - yy)\n ctx.drawImage(\n sourceImage,\n xx,\n yy,\n tw,\n th, // source rect from the image\n xx,\n yy,\n tw,\n th, // dest rect on the canvas (same coords)\n )\n }\n }\n // Sampling a single tile-sized chunk and re-painting it at tile size\n // gives the mosaic. To get the visual look, downscale + upscale via\n // imageSmoothingDisabled.\n ctx.imageSmoothingEnabled = false\n for (let yy = y; yy < y + h; yy += tile) {\n for (let xx = x; xx < x + w; xx += tile) {\n const tw = Math.min(tile, x + w - xx)\n const th = Math.min(tile, y + h - yy)\n // Sample the top-left pixel of this tile from the source and stretch.\n ctx.drawImage(\n sourceImage,\n xx,\n yy,\n 1,\n 1, // single source pixel\n xx,\n yy,\n tw,\n th, // stretched dest tile\n )\n }\n }\n ctx.imageSmoothingEnabled = true\n}\n\n// ---------------------------------------------------------------------------\n// Inline icons\n// ---------------------------------------------------------------------------\n\nconst Icon = {\n rect: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"3\" width=\"12\" height=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" />\n </svg>\n ),\n arrow: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2 8h11M9 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n pencil: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\" />\n </svg>\n ),\n text: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 3h10M8 3v10M5 13h6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n highlight: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <path d=\"M2 14h12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n blur: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"2\" width=\"4\" height=\"4\" fill=\"currentColor\" opacity=\"0.85\" />\n <rect x=\"7\" y=\"2\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.55\" />\n <rect x=\"11\" y=\"2\" width=\"3\" height=\"4\" fill=\"currentColor\" opacity=\"0.4\" />\n <rect x=\"2\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.6\" />\n <rect x=\"6\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n <rect x=\"10\" y=\"7\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.5\" />\n <rect x=\"2\" y=\"11\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.4\" />\n <rect x=\"7\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.65\" />\n <rect x=\"11\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n </svg>\n ),\n undo: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n redo: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n trash: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n close: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 4l8 8M12 4l-8 8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface AnnotatorProps {\n imageBlob: Blob\n strings: Record<StringKey, string>\n onSave: (annotated: Blob) => void\n onCancel: () => void\n}\n\nexport function Annotator({ imageBlob, strings, onSave, onCancel }: AnnotatorProps) {\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const containerRef = useRef<HTMLDivElement | null>(null)\n const imageRef = useRef<HTMLImageElement | null>(null)\n const [tool, setTool] = useState<Tool>('rectangle')\n const [color, setColor] = useState<string>(COLORS[0]!)\n const [shapes, setShapes] = useState<Shape[]>([])\n const [past, setPast] = useState<Shape[][]>([])\n const [future, setFuture] = useState<Shape[][]>([])\n const isDrawingRef = useRef(false)\n const [draftShape, setDraftShape] = useState<Shape | null>(null)\n const [imageLoaded, setImageLoaded] = useState(false)\n const [saving, setSaving] = useState(false)\n const scaleRef = useRef(1)\n\n function addShape(shape: Shape) {\n setShapes((s) => {\n // Use the functional setState so we read the most recent shape list\n // even when this fires from a stale-closure event handler. The past\n // stack captures `s` here (the truly current state).\n setPast((p) => [...p, s])\n return [...s, shape]\n })\n setFuture([])\n }\n\n function clearShapes() {\n setShapes((s) => {\n setPast((p) => [...p, s])\n return []\n })\n setFuture([])\n }\n\n function undo() {\n setPast((p) => {\n if (p.length === 0) return p\n const prev = p[p.length - 1]!\n setShapes((s) => {\n setFuture((f) => [s, ...f])\n return prev\n })\n return p.slice(0, -1)\n })\n }\n\n function redo() {\n setFuture((f) => {\n if (f.length === 0) return f\n const next = f[0]!\n setShapes((s) => {\n setPast((p) => [...p, s])\n return next\n })\n return f.slice(1)\n })\n }\n\n // Esc handler uses useLayoutEffect (not useEffect) so the listener is\n // attached SYNCHRONOUSLY after DOM commit, BEFORE paint. Tests (and\n // fast human users) that press Escape the moment the annotator becomes\n // visible would otherwise hit a brief window where the listener isn't\n // yet attached. The parent Modal's keydown handler bails when it sees\n // .annotator-backdrop in the shadow root, so we don't double-fire.\n useLayoutEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopPropagation()\n onCancel()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onCancel])\n\n // Cmd/Ctrl-Z = undo, Cmd/Ctrl-Shift-Z (or Cmd/Ctrl-Y) = redo. Re-binds\n // on each state mutation to capture fresh shapes/past/future closures.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n const mod = e.metaKey || e.ctrlKey\n if (!mod) return\n const k = e.key.toLowerCase()\n if (k === 'z') {\n e.preventDefault()\n e.stopPropagation()\n if (e.shiftKey) redo()\n else undo()\n } else if (k === 'y') {\n e.preventDefault()\n e.stopPropagation()\n redo()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [past, future, shapes])\n\n useEffect(() => {\n const url = URL.createObjectURL(imageBlob)\n const img = new Image()\n img.onload = () => {\n imageRef.current = img\n setImageLoaded(true)\n }\n img.src = url\n return () => URL.revokeObjectURL(url)\n }, [imageBlob])\n\n useEffect(() => {\n if (\n !imageLoaded ||\n !canvasRef.current ||\n !imageRef.current ||\n !containerRef.current\n ) {\n return\n }\n const img = imageRef.current\n const container = containerRef.current\n const maxW = container.clientWidth - 16\n const maxH = window.innerHeight * 0.65\n const fitScale = Math.min(maxW / img.width, maxH / img.height, 1)\n scaleRef.current = fitScale\n\n const canvas = canvasRef.current\n canvas.width = img.width\n canvas.height = img.height\n canvas.style.width = `${img.width * fitScale}px`\n canvas.style.height = `${img.height * fitScale}px`\n redraw()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [imageLoaded])\n\n useEffect(() => {\n redraw()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shapes, draftShape])\n\n function redraw() {\n const canvas = canvasRef.current\n const img = imageRef.current\n if (!canvas || !img) return\n const ctx = canvas.getContext('2d')\n if (!ctx) return\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n ctx.drawImage(img, 0, 0)\n for (const s of shapes) drawShape(ctx, s, img)\n if (draftShape) drawShape(ctx, draftShape, img)\n }\n\n function getCanvasCoords(e: MouseEvent) {\n const canvas = canvasRef.current!\n const rect = canvas.getBoundingClientRect()\n return {\n x: (e.clientX - rect.left) / scaleRef.current,\n y: (e.clientY - rect.top) / scaleRef.current,\n }\n }\n\n const handleMouseDown = (e: MouseEvent) => {\n if (!imageLoaded) return\n const { x, y } = getCanvasCoords(e)\n const canvasW = canvasRef.current!.width\n const lineWidth = Math.max(3, Math.round(canvasW / 400))\n const blurTile = Math.max(8, Math.round(canvasW / 80))\n\n if (tool === 'text') {\n const text = window.prompt(strings['annotator.text_prompt'])\n if (text && text.trim()) {\n addShape({\n kind: 'text',\n x,\n y,\n text: text.trim(),\n color,\n fontSize: Math.max(16, Math.round(canvasW / 50)),\n lineWidth: 1,\n })\n }\n return\n }\n\n isDrawingRef.current = true\n if (tool === 'rectangle') {\n setDraftShape({ kind: 'rectangle', x, y, w: 0, h: 0, color, lineWidth })\n } else if (tool === 'arrow') {\n setDraftShape({ kind: 'arrow', x1: x, y1: y, x2: x, y2: y, color, lineWidth })\n } else if (tool === 'freehand') {\n setDraftShape({ kind: 'freehand', points: [{ x, y }], color, lineWidth })\n } else if (tool === 'highlight') {\n setDraftShape({\n kind: 'highlight',\n x,\n y,\n w: 0,\n h: 0,\n color: color === '#ffffff' ? '#fde047' : color,\n lineWidth: 1,\n })\n } else if (tool === 'blur') {\n setDraftShape({\n kind: 'blur',\n x,\n y,\n w: 0,\n h: 0,\n color: '#000000',\n lineWidth: 0,\n tile: blurTile,\n })\n }\n }\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!isDrawingRef.current) return\n const { x, y } = getCanvasCoords(e)\n // Functional setState: read the most recent draftShape rather than the\n // closure-captured value. Playwright (and fast humans) can fire\n // mousedown→mousemove faster than Preact commits the re-render, leaving\n // the closure's `draftShape` at the previous frame (often null right\n // after mousedown). audit R6 e2e regression fix.\n setDraftShape((current) => {\n if (!current) return current\n if (\n current.kind === 'rectangle' ||\n current.kind === 'highlight' ||\n current.kind === 'blur'\n ) {\n return { ...current, w: x - current.x, h: y - current.y }\n }\n if (current.kind === 'arrow') {\n return { ...current, x2: x, y2: y }\n }\n if (current.kind === 'freehand') {\n return { ...current, points: [...current.points, { x, y }] }\n }\n return current\n })\n }\n\n const handleMouseUp = () => {\n // Same closure-staleness concern as handleMouseMove: read the\n // committed draft via the functional setter rather than rely on the\n // capture from this render.\n setDraftShape((current) => {\n if (isDrawingRef.current && current) {\n const isTiny =\n ((current.kind === 'rectangle' ||\n current.kind === 'highlight' ||\n current.kind === 'blur') &&\n Math.abs(current.w) < 4 &&\n Math.abs(current.h) < 4) ||\n (current.kind === 'arrow' &&\n Math.hypot(\n current.x2 - current.x1,\n current.y2 - current.y1,\n ) < 4) ||\n (current.kind === 'freehand' && current.points.length < 3)\n if (!isTiny) {\n addShape(current)\n }\n }\n return null\n })\n isDrawingRef.current = false\n }\n\n const handleSave = async () => {\n const canvas = canvasRef.current\n if (!canvas) return\n setSaving(true)\n try {\n const blob = await new Promise<Blob | null>((resolve) =>\n canvas.toBlob(resolve, 'image/png', 0.92),\n )\n if (blob) onSave(blob)\n } finally {\n setSaving(false)\n }\n }\n\n const tools: Array<{ id: Tool; icon: typeof Icon.rect; label: string }> = [\n { id: 'rectangle', icon: Icon.rect, label: strings['annotator.tool.rectangle'] },\n { id: 'arrow', icon: Icon.arrow, label: strings['annotator.tool.arrow'] },\n { id: 'freehand', icon: Icon.pencil, label: strings['annotator.tool.freehand'] },\n { id: 'text', icon: Icon.text, label: strings['annotator.tool.text'] },\n { id: 'highlight', icon: Icon.highlight, label: strings['annotator.tool.highlight'] },\n { id: 'blur', icon: Icon.blur, label: strings['annotator.tool.blur'] },\n ]\n\n return (\n <div\n class=\"annotator-backdrop\"\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) onCancel()\n }}\n >\n <div class=\"annotator\" role=\"dialog\" aria-modal=\"true\" aria-label={strings['annotator.title']}>\n <div class=\"annotator-header\">\n <span>{strings['annotator.title']}</span>\n <button\n type=\"button\"\n class=\"modal-close\"\n aria-label={strings['form.close']}\n onClick={onCancel}\n >\n {Icon.close}\n </button>\n </div>\n\n <div class=\"annotator-toolbar\">\n <div class=\"annotator-tools\">\n {tools.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n onClick={() => setTool(t.id)}\n title={t.label}\n aria-label={t.label}\n aria-pressed={tool === t.id}\n class={`annotator-tool ${tool === t.id ? 'is-active' : ''}`}\n >\n {t.icon}\n </button>\n ))}\n </div>\n\n <span class=\"annotator-sep\" />\n\n <div class=\"annotator-colors\">\n {COLORS.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => setColor(c)}\n aria-label={c}\n aria-pressed={color === c}\n class={`annotator-color ${color === c ? 'is-active' : ''}`}\n style={{ backgroundColor: c }}\n />\n ))}\n <label class=\"annotator-color annotator-color-picker\" title={strings['annotator.color_picker']}>\n <input\n type=\"color\"\n value={color}\n onInput={(e) => setColor((e.target as HTMLInputElement).value)}\n aria-label={strings['annotator.color_picker']}\n />\n </label>\n </div>\n\n <span class=\"annotator-sep\" />\n\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={undo}\n disabled={past.length === 0}\n title={strings['annotator.undo']}\n >\n {Icon.undo}\n <span>{strings['annotator.undo']}</span>\n </button>\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={redo}\n disabled={future.length === 0}\n title={strings['annotator.redo']}\n >\n {Icon.redo}\n <span>{strings['annotator.redo']}</span>\n </button>\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={clearShapes}\n disabled={shapes.length === 0}\n >\n {Icon.trash}\n <span>{strings['annotator.clear']}</span>\n </button>\n\n <span class=\"annotator-spacer\" />\n\n <span class=\"annotator-count\">\n {shapes.length} {strings['annotator.count_suffix']}\n </span>\n </div>\n\n <div ref={containerRef} class=\"annotator-canvas-wrap\">\n {!imageLoaded ? (\n <span class=\"annotator-loading\">{strings['annotator.loading']}</span>\n ) : (\n <canvas\n ref={canvasRef}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n class=\"annotator-canvas\"\n />\n )}\n </div>\n\n <div class=\"annotator-footer\">\n <button type=\"button\" class=\"btn\" onClick={onCancel}>\n {strings['form.cancel']}\n </button>\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSave}\n disabled={saving || !imageLoaded}\n >\n {saving ? strings['annotator.applying'] : strings['annotator.apply']}\n </button>\n </div>\n </div>\n </div>\n )\n}\n","/**\n * MineList — the user's own reports on this project.\n *\n * Polls every 30 s while mounted (Sentry-style staleTime + manual\n * refresh). Empty state when the submitter has no thread yet (404 from\n * the backend is mapped to []). Click a row to open the detail view.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { KpiStrip, type KpiFilter, rowsMatchingFilter } from './KpiStrip'\nimport { ReportRow } from './ReportRow'\n\ninterface MineListProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n onSelect: (report: WidgetReportRow) => void\n}\n\nconst POLL_MS = 30_000\n\nexport function MineList({ api, externalId, strings, onSelect }: MineListProps) {\n const [rows, setRows] = useState<WidgetReportRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [refreshing, setRefreshing] = useState(false)\n const [filter, setFilter] = useState<KpiFilter>('all')\n const mountedRef = useRef(true)\n\n const fetchRows = async () => {\n setRefreshing(true)\n setError(null)\n try {\n const next = await api.listMine(externalId)\n if (!mountedRef.current) return\n setRows(next)\n } catch (err) {\n if (!mountedRef.current) return\n setError(err instanceof Error ? err.message : strings['mine.error'])\n } finally {\n if (mountedRef.current) setRefreshing(false)\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n void fetchRows()\n const timer = setInterval(() => {\n void fetchRows()\n }, POLL_MS)\n return () => {\n mountedRef.current = false\n clearInterval(timer)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [externalId])\n\n const isEmpty = rows !== null && rows.length === 0\n const isLoading = rows === null && !error\n const visibleRows = rows ? rowsMatchingFilter(rows, filter) : null\n const visibleEmpty = !!rows && rows.length > 0 && (visibleRows?.length ?? 0) === 0\n\n return (\n <div class=\"mine-list\">\n <div class=\"mine-list-header\">\n <h2>{strings['tab.mine']}</h2>\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() => {\n void fetchRows()\n }}\n disabled={refreshing}\n >\n {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n </button>\n </div>\n {rows && rows.length > 0 && (\n <KpiStrip rows={rows} filter={filter} onFilter={setFilter} strings={strings} />\n )}\n {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n {error && <div class=\"error\">{error}</div>}\n {isEmpty && (\n <div class=\"mine-empty\">\n <strong>{strings['mine.empty.title']}</strong>\n <p>{strings['mine.empty.body']}</p>\n </div>\n )}\n {visibleEmpty && (\n <div class=\"mine-empty\">\n <p>{strings['mine.filter.empty']}</p>\n </div>\n )}\n {visibleRows && visibleRows.length > 0 && (\n <ul class=\"mine-rows\">\n {visibleRows.map((row) => (\n <li>\n <ReportRow row={row} strings={strings} onClick={() => onSelect(row)} />\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","/**\n * KpiStrip — four-pill summary at the top of the user's \"My reports\" tab.\n *\n * Computes counts client-side from the same `WidgetReportRow[]` that\n * MineList already fetches; no extra endpoint. Each pill is clickable\n * and toggles a status filter on the list below — matches thePnr's\n * customer-facing pattern (`FeedbackKPICards.tsx`).\n */\n\nimport { h } from 'preact'\n\nimport type { ReportStatus, WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\nexport type KpiFilter = 'all' | 'new' | 'in_progress' | 'awaiting_validation' | 'resolved'\n\ninterface KpiStripProps {\n rows: WidgetReportRow[]\n filter: KpiFilter\n onFilter: (next: KpiFilter) => void\n strings: Record<StringKey, string>\n}\n\ninterface Counts {\n new: number\n in_progress: number\n awaiting_validation: number\n resolved: number\n total: number\n}\n\nexport function computeKpiCounts(rows: WidgetReportRow[]): Counts {\n const counts: Counts = {\n new: 0,\n in_progress: 0,\n awaiting_validation: 0,\n resolved: 0,\n total: 0,\n }\n for (const row of rows) {\n counts.total += 1\n switch (row.status) {\n case 'new':\n counts.new += 1\n break\n case 'in_progress':\n counts.in_progress += 1\n break\n case 'awaiting_validation':\n counts.awaiting_validation += 1\n break\n case 'closed':\n case 'rejected':\n case 'duplicate':\n case 'wontfix':\n counts.resolved += 1\n break\n }\n }\n return counts\n}\n\n/**\n * Resolution rate = (awaiting_validation + closed + rejected + duplicate +\n * wontfix) / total. Includes operator-resolved-as-no-action so the\n * percentage reflects \"your reports the team has acted on\" rather than\n * \"your reports that turned into shipped fixes\". Returns null if there\n * are no rows yet — avoids showing \"0%\" before the user has filed\n * anything, which reads as ominous.\n */\nexport function computeResolutionRate(counts: Counts): number | null {\n if (counts.total === 0) return null\n return Math.round(\n ((counts.awaiting_validation + counts.resolved) / counts.total) * 100,\n )\n}\n\nconst FILTER_FOR_STATUS: Record<ReportStatus, KpiFilter> = {\n new: 'new',\n in_progress: 'in_progress',\n awaiting_validation: 'awaiting_validation',\n closed: 'resolved',\n rejected: 'resolved',\n duplicate: 'resolved',\n wontfix: 'resolved',\n}\n\nexport function rowsMatchingFilter(\n rows: WidgetReportRow[],\n filter: KpiFilter,\n): WidgetReportRow[] {\n if (filter === 'all') return rows\n return rows.filter((r) => FILTER_FOR_STATUS[r.status] === filter)\n}\n\nexport function KpiStrip({ rows, filter, onFilter, strings }: KpiStripProps) {\n const counts = computeKpiCounts(rows)\n const resolutionRate = computeResolutionRate(counts)\n const cells: Array<{ key: KpiFilter; label: string; value: string }> = [\n { key: 'new', label: strings['kpi.new'], value: String(counts.new) },\n { key: 'in_progress', label: strings['kpi.in_progress'], value: String(counts.in_progress) },\n {\n key: 'awaiting_validation',\n label: strings['kpi.awaiting_validation'],\n value: String(counts.awaiting_validation),\n },\n {\n key: 'resolved',\n label: strings['kpi.resolution_rate'],\n value: resolutionRate === null ? '—' : `${resolutionRate}%`,\n },\n ]\n return (\n <div class=\"kpi-strip\" role=\"toolbar\">\n {cells.map((c) => {\n const active = filter === c.key\n const toggleTo: KpiFilter = active ? 'all' : c.key\n return (\n <button\n type=\"button\"\n class={`kpi-cell kpi-cell--${c.key}${active ? ' is-active' : ''}`}\n onClick={() => onFilter(toggleTo)}\n aria-pressed={active}\n >\n <span class=\"kpi-value\">{c.value}</span>\n <span class=\"kpi-label\">{c.label}</span>\n </button>\n )\n })}\n </div>\n )\n}\n","import { h, type ComponentChildren } from 'preact'\nimport { useEffect, useRef } from 'preact/hooks'\n\ninterface ModalProps {\n onDismiss: () => void\n children: ComponentChildren\n closeLabel?: string\n /** v0.12: when true, the modal grows toward near-fullscreen on desktop\n * and full-screen on mobile. Drives the \"transport\" feel for the Board\n * tab — opening the tab feels like clicking through to a real page. */\n expanded?: boolean\n}\n\nexport function Modal({ onDismiss, children, closeLabel = 'Close', expanded = false }: ModalProps) {\n const modalRef = useRef<HTMLDivElement>(null)\n const previouslyFocused = useRef<Element | null>(null)\n\n // Esc-to-close + focus management. Without this, users couldn't dismiss\n // with the keyboard (a real-world UX expectation). Listener installed on\n // window so it works regardless of which element inside the modal has focus.\n useEffect(() => {\n previouslyFocused.current = document.activeElement\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape') return\n // Ignore Esc when a topmost dialog (e.g. the Annotator) is mounted in\n // our shadow root — it owns the keystroke. Without this guard the\n // Esc fires both handlers on the same event and dismisses the form\n // behind the annotator.\n const root = modalRef.current?.getRootNode()\n if (\n root instanceof ShadowRoot &&\n root.querySelector('.annotator-backdrop')\n ) {\n return\n }\n e.stopPropagation()\n onDismiss()\n }\n window.addEventListener('keydown', onKey)\n // Focus first focusable element inside the modal so the keyboard user\n // lands inside, not still on the FAB or whatever opened the modal.\n const first = modalRef.current?.querySelector<HTMLElement>(\n 'textarea, input, select, button',\n )\n first?.focus()\n return () => {\n window.removeEventListener('keydown', onKey)\n // Restore focus on dismiss so screen-readers don't lose place.\n const prev = previouslyFocused.current as HTMLElement | null\n if (prev && typeof prev.focus === 'function') prev.focus()\n }\n }, [onDismiss])\n\n return (\n <div\n class={`backdrop ${expanded ? 'is-expanded' : ''}`}\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) onDismiss()\n }}\n >\n <div\n ref={modalRef}\n class={`modal ${expanded ? 'is-expanded' : ''}`}\n role=\"dialog\"\n aria-modal=\"true\"\n >\n <button\n type=\"button\"\n class=\"modal-close\"\n aria-label={closeLabel}\n onClick={onDismiss}\n >\n ×\n </button>\n {children}\n </div>\n </div>\n )\n}\n","export const WIDGET_STYLES = `\n:host {\n --mfb-accent: #3b82f6;\n --mfb-accent-contrast: #ffffff;\n --mfb-bg: #ffffff;\n --mfb-surface: #f9fafb;\n --mfb-surface-2: #f3f4f6;\n --mfb-text: #0a0a0a;\n --mfb-text-muted: #6b7280;\n --mfb-border: #e5e7eb;\n --mfb-border-strong: #d1d5db;\n --mfb-radius: 8px;\n --mfb-radius-lg: 14px;\n --mfb-font: system-ui, -apple-system, sans-serif;\n --mfb-z-index: 2147483640;\n\n /* Spacing scale — 4px base, used everywhere instead of magic numbers. */\n --mfb-space-1: 4px;\n --mfb-space-2: 8px;\n --mfb-space-3: 12px;\n --mfb-space-4: 16px;\n --mfb-space-5: 24px;\n --mfb-space-6: 32px;\n --mfb-space-7: 48px;\n\n /* Type scale — fixed sizes, no fluid scaling. Body 14px sits in the\n \"comfortable on every viewport\" range; headings step up gracefully. */\n --mfb-text-xs: 11px;\n --mfb-text-sm: 13px;\n --mfb-text-base: 14px;\n --mfb-text-md: 15px;\n --mfb-text-lg: 17px;\n --mfb-text-xl: 20px;\n\n /* Elevation — three tiers for layering. */\n --mfb-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08);\n --mfb-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);\n --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.18), 0 8px 20px rgba(0, 0, 0, 0.10);\n\n all: initial;\n font-family: var(--mfb-font);\n color: var(--mfb-text);\n position: fixed;\n z-index: var(--mfb-z-index);\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --mfb-bg: #0f172a;\n --mfb-surface: #1e293b;\n --mfb-surface-2: #253349;\n --mfb-text: #f8fafc;\n --mfb-text-muted: #94a3b8;\n --mfb-border: #334155;\n --mfb-border-strong: #475569;\n --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.55), 0 8px 20px rgba(0, 0, 0, 0.40);\n }\n}\n\n/* FAB — 56px (Material standard), two-layer elevation, scale-on-press,\n custom SVG icon (no emoji — emoji renders inconsistently across OSes\n and can't inherit color). */\n.fab {\n position: fixed;\n bottom: 24px;\n right: 24px;\n width: 56px;\n height: 56px;\n border-radius: 999px;\n background: var(--mfb-accent);\n color: var(--mfb-accent-contrast);\n border: none;\n cursor: pointer;\n /* Two-layer elevation: ambient (soft, large) + key (tighter, near). */\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.12);\n display: grid;\n place-items: center;\n transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 180ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.fab:hover {\n transform: translateY(-2px);\n box-shadow:\n 0 8px 24px rgba(0, 0, 0, 0.12),\n 0 3px 6px rgba(0, 0, 0, 0.16);\n}\n.fab:active {\n transform: translateY(0) scale(0.96);\n box-shadow:\n 0 3px 8px rgba(0, 0, 0, 0.10),\n 0 1px 2px rgba(0, 0, 0, 0.14);\n}\n.fab:focus-visible {\n outline: 2px solid var(--mfb-accent-contrast);\n outline-offset: 3px;\n box-shadow:\n 0 0 0 4px var(--mfb-accent),\n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.12);\n}\n@media (prefers-color-scheme: dark) {\n /* Slightly desaturate so the FAB doesn't glow against dark backgrounds. */\n .fab { box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.32),\n 0 2px 4px rgba(0, 0, 0, 0.40); }\n}\n@media (prefers-reduced-motion: reduce) {\n .fab { transition: none; }\n .fab:hover, .fab:active { transform: none; }\n}\n\n/* Backdrop — fade in with a slight blur for depth. The blur gives the\n modal that \"page that opens\" weight: the underlying page recedes\n visually so the widget feels foregrounded, not pasted on. */\n.backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 23, 42, 0.55);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n display: grid;\n place-items: center;\n animation: mfb-backdrop-in 220ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n@keyframes mfb-backdrop-in {\n from { opacity: 0; backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px); }\n to { opacity: 1; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }\n}\n\n.modal {\n background: var(--mfb-bg);\n border-radius: var(--mfb-radius-lg);\n box-shadow: var(--mfb-shadow-lg);\n /* 720px is the \"page that opens\" sweet spot — wide enough to feel\n like a workspace, narrow enough to not dominate the screen. Was\n 440px before; the bump trades a denser modal for a calmer canvas. */\n width: min(720px, calc(100vw - var(--mfb-space-7)));\n padding: var(--mfb-space-6);\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-5);\n position: relative;\n /* Cap height with breathing room. The form scrolls internally if\n the user attaches a tall screenshot. */\n max-height: min(820px, calc(100vh - var(--mfb-space-7)));\n overflow-y: auto;\n /* Entrance: opacity-only. Avoids any geometric shift during the\n animation so interaction tests that read boundingBox immediately\n after the modal becomes visible get stable coordinates. 220ms\n ease-out is snappy enough that human users barely register it. */\n animation: mfb-modal-in 220ms ease-out;\n /* Transition lives on the BASE rule with a SHORTER duration so the\n \"back\" trip (Board → other tab, where .is-expanded is removed)\n feels snappy. The grow direction overrides this with a slower\n curve in .modal.is-expanded below — the asymmetry is intentional:\n a slow grow reads as a transport, a slow shrink reads as\n hesitation. */\n transition:\n width 200ms cubic-bezier(0.4, 0, 0.2, 1),\n height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n max-height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n padding 200ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes mfb-modal-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Mobile: full-height sheet that slides up from the bottom. The\n slide-up gesture matches platform-native sheets users already know. */\n@media (max-width: 640px) {\n .backdrop { place-items: end center; }\n .modal {\n width: 100vw;\n height: calc(100vh - var(--mfb-space-7));\n max-height: none;\n border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n padding: var(--mfb-space-5) var(--mfb-space-4) var(--mfb-space-4);\n animation: mfb-sheet-up 280ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n}\n\n@keyframes mfb-sheet-up {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .backdrop, .modal { animation: none; }\n}\n\n.modal h2 {\n margin: 0;\n font-size: var(--mfb-text-xl);\n font-weight: 600;\n padding-right: var(--mfb-space-6);\n letter-spacing: -0.015em;\n line-height: 1.2;\n}\n\n.modal form {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-5);\n /* Match the entrance motion the Board/MineList/Changelog containers\n * use so switching tabs (especially Board → Send, where the modal\n * is mid-shrink) doesn't show a content snap behind the transition.\n * 40ms delay lets the modal's shrink finish first. */\n animation: mfb-board-in 220ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .modal form { animation: none; }\n}\n\n.modal-close {\n position: absolute;\n top: var(--mfb-space-3);\n right: var(--mfb-space-3);\n width: 36px;\n height: 36px;\n display: grid;\n place-items: center;\n background: transparent;\n border: none;\n border-radius: var(--mfb-radius);\n color: var(--mfb-text-muted);\n font: inherit;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n.modal-close:hover { background: var(--mfb-surface); color: var(--mfb-text); }\n.modal-close:focus-visible { outline: 2px solid var(--mfb-accent); outline-offset: 2px; }\n\n/* Each field group: label + input stacked with breathing room.\n The 24px modal-level gap separates groups. */\n.field { display: flex; flex-direction: column; gap: var(--mfb-space-2); font-size: var(--mfb-text-sm); }\n\n.field label {\n color: var(--mfb-text-muted);\n font-weight: 500;\n font-size: var(--mfb-text-xs);\n letter-spacing: 0.03em;\n text-transform: uppercase;\n}\n\n.field input, .field select, .field textarea {\n font-family: inherit;\n font-size: var(--mfb-text-base);\n color: inherit;\n padding: 11px 14px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;\n}\n\n.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--mfb-border-strong); }\n.field input:focus, .field select:focus, .field textarea:focus {\n outline: none;\n border-color: var(--mfb-accent);\n background: var(--mfb-bg);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 22%, transparent);\n}\n\n.field select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n padding-right: 28px;\n background-image: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/></svg>\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n}\n\n.field textarea { min-height: 120px; resize: vertical; line-height: 1.5; }\n\n.row { display: flex; gap: var(--mfb-space-3); }\n.row > * { flex: 1; }\n\n/* Footer: subtle separation via border, slightly more vertical space. */\n.actions {\n display: flex;\n gap: var(--mfb-space-2);\n justify-content: flex-end;\n padding-top: var(--mfb-space-4);\n margin-top: var(--mfb-space-1);\n border-top: 1px solid var(--mfb-border);\n}\n\n.btn {\n padding: 10px 18px;\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n color: var(--mfb-text);\n font: inherit;\n font-size: var(--mfb-text-sm);\n font-weight: 500;\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease, transform 80ms ease;\n}\n.btn:hover { background: var(--mfb-surface); border-color: var(--mfb-border-strong); }\n.btn:active { transform: scale(0.98); }\n\n.btn--primary {\n background: var(--mfb-accent);\n color: var(--mfb-accent-contrast);\n border-color: var(--mfb-accent);\n}\n.btn--primary:hover {\n background: color-mix(in srgb, var(--mfb-accent) 88%, black);\n border-color: color-mix(in srgb, var(--mfb-accent) 88%, black);\n}\n\n/* Subdued button — borrows accent text but transparent background. Used\n * for the \"Capture this page\" alt-action that sits below the dropzone,\n * where a full --primary would compete with the form's main submit. */\n.btn--ghost {\n background: transparent;\n color: var(--mfb-accent);\n border-color: color-mix(in srgb, var(--mfb-accent) 40%, transparent);\n}\n.btn--ghost:hover {\n background: color-mix(in srgb, var(--mfb-accent) 8%, transparent);\n border-color: var(--mfb-accent);\n}\n\n.btn[disabled] { opacity: 0.6; cursor: not-allowed; }\n\n.error { color: #dc2626; font-size: 13px; }\n.success { color: #059669; font-size: 13px; }\n\n/* ---- v0.6.0: manual screenshot upload + annotator -------------------- */\n\n.mfb-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n/* Dropzone — bigger, more inviting; the icon + heading + sub-line\n hierarchy makes it feel like a deliberate target, not an afterthought. */\n.screenshot-dropzone {\n border: 1.5px dashed var(--mfb-border-strong);\n border-radius: var(--mfb-radius-lg);\n padding: var(--mfb-space-6) var(--mfb-space-4);\n text-align: center;\n cursor: pointer;\n background: var(--mfb-surface);\n transition: border-color 160ms ease, background 160ms ease, transform 120ms ease;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--mfb-space-2);\n}\n.screenshot-dropzone:hover {\n border-color: var(--mfb-accent);\n background: color-mix(in srgb, var(--mfb-accent) 4%, var(--mfb-surface));\n}\n.screenshot-dropzone.is-dragover {\n border-color: var(--mfb-accent);\n border-style: solid;\n background: color-mix(in srgb, var(--mfb-accent) 10%, var(--mfb-surface));\n transform: scale(1.005);\n}\n.screenshot-dropzone:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: 2px;\n}\n.screenshot-icon {\n color: var(--mfb-text-muted);\n opacity: 0.7;\n margin-bottom: var(--mfb-space-1);\n transition: color 160ms ease, opacity 160ms ease;\n}\n.screenshot-dropzone:hover .screenshot-icon,\n.screenshot-dropzone.is-dragover .screenshot-icon {\n color: var(--mfb-accent);\n opacity: 1;\n}\n.screenshot-cta { font-size: var(--mfb-text-base); color: var(--mfb-text); font-weight: 500; }\n.screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }\n.screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }\n\n/* \"or — Capture this page\" row sits below the dropzone. The thin \"or\"\n * separator borrows the pattern from auth forms so the second option\n * reads as an alternative, not a follow-up step. v0.12. */\n.screenshot-alt {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n.screenshot-or {\n position: relative;\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n padding: 0 4px;\n}\n.screenshot-capture-page {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.screenshot-capture-page:disabled {\n opacity: 0.55;\n cursor: progress;\n}\n.screenshot-capture-hint {\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n flex: 1 1 100%;\n margin-top: 4px;\n}\n\n.screenshot-preview {\n position: relative;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n background: var(--mfb-surface);\n display: flex;\n flex-direction: column;\n}\n.screenshot-preview img {\n display: block;\n width: 100%;\n height: auto;\n max-height: 280px;\n object-fit: contain;\n background: #1a1a1a;\n}\n.screenshot-preview-actions {\n display: flex;\n gap: var(--mfb-space-2);\n padding: var(--mfb-space-2);\n border-top: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n}\n.screenshot-preview-actions .btn {\n flex: 1;\n padding: 8px 12px;\n font-size: var(--mfb-text-sm);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n}\n.screenshot-preview-actions .btn--primary {\n background: var(--mfb-bg);\n color: var(--mfb-accent);\n border-color: var(--mfb-accent);\n}\n.screenshot-preview-actions .btn--primary:hover {\n background: color-mix(in srgb, var(--mfb-accent) 8%, var(--mfb-bg));\n border-color: var(--mfb-accent);\n color: var(--mfb-accent);\n}\n.screenshot-remove {\n /* Kept for legacy markup that uses the old corner-cross button. */\n position: absolute;\n top: 6px;\n right: 6px;\n width: 26px;\n height: 26px;\n display: grid;\n place-items: center;\n background: rgba(255, 255, 255, 0.9);\n border: 1px solid var(--mfb-border);\n border-radius: 999px;\n font-size: 18px;\n line-height: 1;\n cursor: pointer;\n color: #111827;\n}\n.screenshot-remove:hover { background: #fff; }\n.screenshot-annotate {\n /* Kept for legacy; new markup uses .screenshot-preview-actions. */\n border-radius: 0;\n border-width: 0;\n border-top: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n}\n.screenshot-annotate:hover { background: var(--mfb-surface); }\n\n.page-context {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mfb-text-muted);\n}\n.page-context-label {\n text-transform: uppercase;\n font-weight: 600;\n letter-spacing: 0.04em;\n font-size: 10px;\n}\n.page-context-url {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--mfb-text-muted);\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Annotator modal — sits above the feedback modal (z-index +1). */\n\n.annotator-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.78);\n display: grid;\n place-items: center;\n z-index: 1;\n padding: 12px;\n}\n.annotator {\n position: relative;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border-radius: calc(var(--mfb-radius) * 1.5);\n width: min(960px, 96vw);\n max-height: calc(100vh - 24px);\n display: flex;\n flex-direction: column;\n box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);\n}\n.annotator-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n border-bottom: 1px solid var(--mfb-border);\n font-size: 13px;\n font-weight: 600;\n}\n.annotator-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-bottom: 1px solid var(--mfb-border);\n}\n.annotator-tools, .annotator-colors { display: flex; gap: 4px; }\n.annotator-sep {\n display: inline-block;\n width: 1px;\n height: 18px;\n background: var(--mfb-border);\n margin: 0 4px;\n}\n.annotator-spacer { flex: 1; }\n.annotator-tool {\n width: 30px;\n height: 30px;\n display: grid;\n place-items: center;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n cursor: pointer;\n}\n.annotator-tool:hover { background: var(--mfb-surface); }\n.annotator-tool.is-active {\n background: var(--mfb-text);\n color: var(--mfb-bg);\n border-color: var(--mfb-text);\n}\n.annotator-color {\n width: 24px;\n height: 24px;\n border-radius: 999px;\n border: 2px solid var(--mfb-border);\n cursor: pointer;\n padding: 0;\n transition: transform 120ms ease;\n position: relative;\n}\n.annotator-color.is-active {\n transform: scale(1.12);\n border-color: var(--mfb-text);\n}\n/* Color-picker swatch: rainbow gradient fill so users see this isn't\n a preset, plus a tiny native <input type=\"color\"> overlaid invisibly. */\n.annotator-color-picker {\n display: grid;\n place-items: center;\n background: conic-gradient(from 180deg, #ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6, #ec4899, #ef4444);\n overflow: hidden;\n}\n.annotator-color-picker input[type=\"color\"] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border: 0;\n padding: 0;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.annotator-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 30px;\n padding: 0 10px;\n font: inherit;\n font-size: 12px;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n cursor: pointer;\n}\n.annotator-btn:hover { background: var(--mfb-surface); }\n.annotator-btn[disabled] { opacity: 0.5; cursor: not-allowed; }\n.annotator-count { font-size: 11px; color: var(--mfb-text-muted); }\n.annotator-canvas-wrap {\n flex: 1;\n overflow: auto;\n background: #1a1a1a;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n min-height: 200px;\n}\n.annotator-canvas {\n cursor: crosshair;\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);\n background: #fff;\n}\n.annotator-loading {\n color: rgba(255, 255, 255, 0.7);\n font-size: 13px;\n}\n.annotator-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 10px 14px;\n border-top: 1px solid var(--mfb-border);\n}\n\n/* ---- v0.7: tabs + reader UI -------------------------------------- */\n\n.tab-strip {\n display: flex;\n gap: var(--mfb-space-1);\n border-bottom: 1px solid var(--mfb-border);\n margin: 0;\n padding: 0;\n}\n.tab-button {\n appearance: none;\n background: transparent;\n border: 0;\n border-bottom: 2px solid transparent;\n padding: var(--mfb-space-3) var(--mfb-space-4);\n margin-bottom: -1px;\n font: inherit;\n font-size: var(--mfb-text-sm);\n font-weight: 500;\n color: var(--mfb-text-muted);\n cursor: pointer;\n transition: color 120ms ease, border-color 120ms ease;\n}\n.tab-button:hover { color: var(--mfb-text); }\n.tab-button.is-active {\n color: var(--mfb-accent);\n border-bottom-color: var(--mfb-accent);\n}\n.tab-button[aria-selected=\"true\"] { font-weight: 600; }\n\n.mine-list {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-3);\n /* Matches .board-view's entrance so switching tabs feels like a real\n * page transition rather than a flicker. */\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .mine-list { animation: none; }\n}\n.mine-list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.mine-list-header h2 { margin: 0; font-size: var(--mfb-text-lg); font-weight: 600; letter-spacing: -0.01em; }\n.mine-loading { color: var(--mfb-text-muted); font-size: 13px; }\n.mine-empty {\n text-align: center;\n padding: 24px 12px;\n color: var(--mfb-text-muted);\n font-size: 13px;\n}\n.mine-empty strong { display: block; color: var(--mfb-text); margin-bottom: 4px; }\n\n.mine-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 380px;\n overflow-y: auto;\n}\n.mine-row {\n appearance: none;\n text-align: left;\n background: var(--mfb-surface);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 10px 12px;\n font: inherit;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n gap: 4px;\n width: 100%;\n transition:\n border-color 140ms ease,\n background 140ms ease,\n transform 100ms ease,\n box-shadow 140ms ease;\n}\n.mine-row:hover {\n border-color: var(--mfb-border-strong);\n background: var(--mfb-bg);\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.mine-row:active { transform: scale(0.997); }\n.mine-row:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: 2px;\n}\n.mine-row-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.mine-row-preview {\n font-size: 13px;\n color: var(--mfb-text);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.mine-row-meta {\n display: flex;\n gap: 6px;\n font-size: 11px;\n color: var(--mfb-text-muted);\n}\n\n.pill {\n display: inline-block;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n padding: 2px 8px;\n border-radius: 999px;\n border: 1px solid transparent;\n}\n.pill-status { background: #eff6ff; color: #1e40af; border-color: #dbeafe; }\n.pill-status--in_progress { background: #fffbeb; color: #92400e; border-color: #fde68a; }\n.pill-status--awaiting_validation { background: #faf5ff; color: #6b21a8; border-color: #e9d5ff; }\n.pill-status--closed { background: #ecfdf5; color: #065f46; border-color: #a7f3d0; }\n.pill-status--rejected, .pill-status--wontfix, .pill-status--duplicate { background: #f3f4f6; color: #374151; border-color: #e5e7eb; }\n.pill-type { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity--blocker { background: #fef2f2; color: #991b1b; border-color: #fecaca; }\n.pill-severity--high { background: #fff7ed; color: #9a3412; border-color: #fed7aa; }\n.pill-severity--medium { background: #fefce8; color: #854d0e; border-color: #fef08a; }\n.pill-severity--low { background: var(--mfb-surface); color: var(--mfb-text-muted); }\n\n.report-detail { display: flex; flex-direction: column; gap: 12px; }\n.report-detail-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.report-detail-body { display: flex; flex-direction: column; gap: 10px; }\n.report-detail-description {\n font-size: 14px;\n white-space: pre-wrap;\n margin: 0;\n}\n.report-detail-screenshot {\n display: block;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n background: #1a1a1a;\n}\n.report-detail-screenshot img {\n display: block;\n width: 100%;\n max-height: 200px;\n object-fit: contain;\n}\n.report-detail-section {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n margin: 8px 0 4px;\n font-weight: 600;\n}\n\n.report-detail-context {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n background: var(--mfb-surface);\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n}\n.report-detail-context-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.report-detail-context-line {\n display: flex;\n align-items: baseline;\n gap: 8px;\n font-size: 12px;\n color: var(--mfb-text);\n}\n.report-detail-context-label {\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-size: 10px;\n font-weight: 600;\n color: var(--mfb-text-muted);\n min-width: 56px;\n}\n.report-detail-context-url {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: var(--mfb-accent);\n text-decoration: none;\n font-size: 11px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.report-detail-context-url:hover { text-decoration: underline; }\n.pill-capture { background: var(--mfb-bg); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.report-detail-empty {\n font-size: 12px;\n color: var(--mfb-text-muted);\n margin: 0;\n}\n\n.report-comments {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.comment-bubble {\n padding: 8px 10px;\n border-radius: 12px;\n max-width: 88%;\n font-size: 13px;\n line-height: 1.4;\n}\n.comment-bubble.is-mine {\n align-self: flex-end;\n background: var(--mfb-accent);\n color: var(--mfb-accent-contrast);\n}\n.comment-bubble.is-other {\n align-self: flex-start;\n background: var(--mfb-surface);\n border: 1px solid var(--mfb-border);\n color: var(--mfb-text);\n}\n.comment-author {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 600;\n margin-bottom: 2px;\n}\n.comment-author--mcp { color: #6b21a8; }\n.comment-author--staff { color: #1e40af; }\n.comment-author--system { color: var(--mfb-text-muted); }\n.comment-body { white-space: pre-wrap; }\n.comment-time {\n font-size: 10px;\n margin-top: 4px;\n opacity: 0.7;\n}\n\n.report-compose {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 4px;\n}\n.report-compose textarea {\n font: inherit;\n font-size: 13px;\n padding: 8px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n color: inherit;\n min-height: 64px;\n resize: vertical;\n}\n.report-compose-actions {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n}\n\n/* Status history — read-only audit trail visible to the submitter.\n Timeline of who flipped the status and when (operator vs. agent vs.\n system). Bordered, neutral surface so it doesn't compete visually\n with the conversation thread above. */\n.report-detail-history {\n display: flex;\n flex-direction: column;\n gap: 6px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n padding: 8px 10px;\n}\n.report-detail-history .report-detail-section {\n margin: 0;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n.status-history {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.status-history-row {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 8px;\n align-items: center;\n font-size: 11px;\n}\n.status-history-time {\n color: var(--mfb-text-muted);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.status-history-transition {\n display: inline-flex;\n gap: 4px;\n align-items: center;\n flex-wrap: wrap;\n}\n.status-history-arrow {\n color: var(--mfb-text-muted);\n font-size: 11px;\n}\n.status-history-source {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 600;\n white-space: nowrap;\n}\n.status-history-source--mcp { color: #6b21a8; }\n.status-history-source--user { color: #1e40af; }\n.status-history-source--system { color: var(--mfb-text-muted); }\n\n/* Transparency drawer — closed by default. The user can click open to\n verify what their browser sent: device, errors, console tail, network\n tail. Read-only; purely a trust-building gesture. */\n.report-detail-tech {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n}\n.report-detail-tech > summary {\n cursor: pointer;\n padding: 8px 10px;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n user-select: none;\n list-style: none;\n}\n.report-detail-tech > summary::-webkit-details-marker { display: none; }\n.report-detail-tech > summary::before {\n content: '▸';\n display: inline-block;\n margin-right: 6px;\n transition: transform 120ms;\n}\n.report-detail-tech[open] > summary::before { transform: rotate(90deg); }\n.tech-body {\n padding: 0 10px 10px 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.tech-section h4 {\n margin: 0 0 4px 0;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n font-weight: 600;\n}\n.tech-device {\n display: grid;\n grid-template-columns: max-content 1fr;\n column-gap: 8px;\n row-gap: 2px;\n margin: 0;\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n}\n.tech-device dt { color: var(--mfb-text-muted); }\n.tech-device dd { margin: 0; }\n.tech-errors, .tech-console, .tech-network {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n font-size: 11px;\n font-family: ui-monospace, \"SF Mono\", Menlo, monospace;\n max-height: 200px;\n overflow-y: auto;\n}\n.tech-errors li { padding: 4px 0; border-top: 1px solid var(--mfb-border); }\n.tech-errors li:first-child { border-top: 0; }\n.tech-errors-msg { color: #991b1b; font-weight: 600; }\n.tech-errors-stack {\n margin: 4px 0 0 0;\n padding: 4px 6px;\n background: var(--mfb-bg);\n border-radius: 4px;\n font-size: 10px;\n white-space: pre-wrap;\n color: var(--mfb-text-muted);\n max-height: 120px;\n overflow-y: auto;\n}\n.tech-console-row {\n display: grid;\n grid-template-columns: 48px 1fr;\n gap: 6px;\n padding: 1px 0;\n}\n.tech-console-level {\n text-transform: uppercase;\n font-size: 9px;\n font-weight: 600;\n align-self: center;\n color: var(--mfb-text-muted);\n}\n.tech-console-row--warn .tech-console-level { color: #92400e; }\n.tech-console-row--error .tech-console-level { color: #991b1b; }\n.tech-console-row--info .tech-console-level { color: #1e40af; }\n.tech-console-msg { word-break: break-word; }\n.tech-network-row {\n display: grid;\n grid-template-columns: 48px 56px 1fr 56px;\n gap: 6px;\n padding: 1px 0;\n}\n.tech-network-row--fail .tech-network-status { color: #991b1b; font-weight: 700; }\n.tech-network-status { font-variant-numeric: tabular-nums; }\n.tech-network-method { color: var(--mfb-text-muted); }\n.tech-network-url { word-break: break-all; }\n.tech-network-time { text-align: right; color: var(--mfb-text-muted); font-variant-numeric: tabular-nums; }\n\n/* KPI strip — four pills above the My Reports list. Each is clickable\n to filter the rows below. Borrowed from thePnr's FeedbackKPICards. */\n.kpi-strip {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 6px;\n}\n.kpi-cell {\n appearance: none;\n background: var(--mfb-bg);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 10px 10px 10px 14px;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n font-family: inherit;\n color: inherit;\n position: relative;\n overflow: hidden;\n transition:\n background 140ms ease,\n border-color 140ms ease,\n transform 100ms ease,\n box-shadow 140ms ease;\n}\n.kpi-cell::before {\n /* Vertical tone accent — matches the Board KPI cards' visual rhythm so\n * the two surfaces feel like they belong to the same product. v0.12\n * polish pass. */\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 3px;\n height: 100%;\n background: var(--mfb-border);\n transition: background 140ms ease;\n}\n.kpi-cell--new::before { background: #3b82f6; }\n.kpi-cell--in_progress::before { background: #f59e0b; }\n.kpi-cell--awaiting_validation::before { background: #a855f7; }\n.kpi-cell--resolved::before { background: #10b981; }\n.kpi-cell:hover {\n background: var(--mfb-surface);\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.kpi-cell.is-active {\n border-color: var(--mfb-accent);\n background: rgba(59, 130, 246, 0.08);\n}\n.kpi-cell.is-active.kpi-cell--in_progress { border-color: #d97706; background: rgba(251, 191, 36, 0.10); }\n.kpi-cell.is-active.kpi-cell--awaiting_validation { border-color: #9333ea; background: rgba(168, 85, 247, 0.10); }\n.kpi-cell.is-active.kpi-cell--resolved { border-color: #059669; background: rgba(16, 185, 129, 0.10); }\n.kpi-value {\n font-size: 18px;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n}\n.kpi-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n\n/* Changelog (\"This week\") — week-grouped resolved-report timeline.\n Borrowed from thePnr's FeedbackChangelogView; the header per group\n reads \"● Week of {date} ━━━ {n} resolved\". */\n.changelog-groups {\n display: flex;\n flex-direction: column;\n gap: 12px;\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .changelog-groups { animation: none; }\n}\n.changelog-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.changelog-group-header {\n display: grid;\n grid-template-columns: auto auto 1fr auto;\n gap: 6px;\n align-items: center;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n.changelog-group-marker {\n color: var(--mfb-accent);\n font-size: 14px;\n line-height: 1;\n}\n.changelog-group-label { font-weight: 600; }\n.changelog-group-rule {\n height: 1px;\n background: var(--mfb-border);\n align-self: center;\n}\n.changelog-group-count {\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n}\n\n/* ---- v0.12: Board tab + expanded modal (\"transport\" feel) ------------ */\n\n/* When the Board tab is active, the modal grows toward near-fullscreen\n * on desktop and full-screen on mobile. The CSS transition gives the\n * sense of clicking through to a real page rather than switching tabs.\n * Width/height/max-* are animated together; padding too, since the\n * board's denser layout needs less of the modal's default padding. */\n.modal.is-expanded {\n width: min(1280px, calc(100vw - var(--mfb-space-5)));\n max-height: calc(100vh - var(--mfb-space-5));\n height: calc(100vh - var(--mfb-space-5));\n padding: var(--mfb-space-5);\n transition:\n width 320ms cubic-bezier(0.22, 1, 0.36, 1),\n height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n max-height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n padding 320ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n@media (prefers-reduced-motion: reduce) {\n .modal.is-expanded { transition: none; }\n}\n.backdrop.is-expanded { /* hook for any backdrop-level overrides */ }\n\n@media (max-width: 640px) {\n .modal.is-expanded {\n width: 100vw;\n height: calc(100vh - var(--mfb-space-5));\n border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n }\n}\n\n/* Decorative purple accent on the Board tab button so users discover\n * it's a different surface — subtle, not loud. */\n.tab-button--board.is-active {\n color: #6b21a8;\n box-shadow: inset 0 -2px 0 0 #a855f7;\n}\n.tab-button--board:not(.is-active):hover {\n color: #6b21a8;\n}\n\n/* ---- Board layout ---- */\n\n.board-view {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-4);\n flex: 1 1 auto;\n min-height: 0; /* allow inner panes to scroll */\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@keyframes mfb-board-in {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n .board-view { animation: none; }\n}\n\n.board-header {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-4);\n}\n.board-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.board-header-emoji {\n font-size: 26px;\n line-height: 1;\n}\n.board-header-h {\n margin: 0;\n font-size: var(--mfb-text-2xl);\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.board-header-sub {\n margin: 2px 0 0 0;\n color: var(--mfb-text-muted);\n font-size: var(--mfb-text-sm);\n}\n\n/* KPI strip — 4 cards, equal width, subtle tone-tints by bucket. */\n.board-kpi-strip {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 12px;\n}\n.board-kpi-card {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 14px 16px;\n background: var(--mfb-bg);\n display: flex;\n flex-direction: column;\n gap: 4px;\n position: relative;\n overflow: hidden;\n transition: transform 160ms ease, box-shadow 160ms ease;\n}\n.board-kpi-card::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 3px;\n height: 100%;\n background: var(--mfb-border);\n transition: background 160ms ease;\n}\n.board-kpi-card:hover {\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.board-kpi-card.tone-new::after { background: #3b82f6; }\n.board-kpi-card.tone-progress::after { background: #f59e0b; }\n.board-kpi-card.tone-validation::after { background: #a855f7; }\n.board-kpi-card.tone-rate::after { background: #10b981; }\n.board-kpi-value {\n font-size: 28px;\n font-weight: 700;\n line-height: 1;\n letter-spacing: -0.02em;\n color: var(--mfb-text);\n font-variant-numeric: tabular-nums;\n}\n.board-kpi-label {\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 500;\n}\n.board-kpi-strip.is-loading .board-kpi-value {\n opacity: 0.35;\n}\n@media (max-width: 720px) {\n .board-kpi-strip { grid-template-columns: repeat(2, 1fr); }\n}\n\n/* Filter row */\n.board-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.board-filter-select,\n.board-filter-search {\n padding: 6px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n color: var(--mfb-text);\n font: inherit;\n font-size: var(--mfb-text-sm);\n height: 32px;\n transition: border-color 120ms ease, box-shadow 120ms ease;\n}\n.board-filter-select:focus-visible,\n.board-filter-search:focus-visible {\n outline: none;\n border-color: var(--mfb-accent);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n.board-filter-search { min-width: 180px; flex: 1 1 220px; }\n.board-filter-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text);\n cursor: pointer;\n user-select: none;\n}\n.board-filter-toggle input { margin: 0; }\n.board-filter-clear {\n background: transparent;\n border: 0;\n color: var(--mfb-text-muted);\n font: inherit;\n font-size: var(--mfb-text-sm);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n padding: 4px 6px;\n border-radius: var(--mfb-radius);\n transition: background 120ms ease, color 120ms ease;\n}\n.board-filter-clear:hover {\n color: var(--mfb-text);\n background: var(--mfb-surface);\n}\n\n/* Master/detail layout — two-column at >=900px, stacked below. */\n.board-body {\n display: grid;\n grid-template-columns: minmax(280px, 360px) 1fr;\n gap: var(--mfb-space-4);\n flex: 1 1 auto;\n min-height: 0;\n}\n@media (max-width: 900px) {\n .board-body { grid-template-columns: 1fr; }\n .board-detail-wrap:not(.has-selection) { display: none; }\n}\n\n.board-list-wrap {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-height: 0;\n overflow: hidden;\n}\n.board-list-count {\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n padding: 0 4px;\n}\n.board-list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n overflow-y: auto;\n flex: 1 1 auto;\n scrollbar-width: thin;\n scrollbar-gutter: stable;\n}\n\n.board-row {\n display: flex;\n flex-direction: column;\n gap: 6px;\n width: 100%;\n text-align: left;\n padding: 12px 14px;\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n border: 1px solid var(--mfb-border);\n cursor: pointer;\n font: inherit;\n color: inherit;\n /* Stagger reveal on first paint. */\n animation: mfb-row-in 280ms ease-out both;\n transition: border-color 140ms ease, background 140ms ease, transform 80ms ease;\n}\n.board-row:hover {\n border-color: var(--mfb-border-strong);\n background: var(--mfb-surface);\n}\n.board-row:active { transform: scale(0.997); }\n.board-row.is-selected {\n border-color: var(--mfb-accent);\n background: color-mix(in srgb, var(--mfb-accent) 6%, var(--mfb-bg));\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n@keyframes mfb-row-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n .board-row { animation: none; }\n}\n/* Stagger first 6 rows so the list reveals top-down. */\n.board-row:nth-child(1) { animation-delay: 0ms; }\n.board-row:nth-child(2) { animation-delay: 40ms; }\n.board-row:nth-child(3) { animation-delay: 80ms; }\n.board-row:nth-child(4) { animation-delay: 120ms; }\n.board-row:nth-child(5) { animation-delay: 160ms; }\n.board-row:nth-child(6) { animation-delay: 200ms; }\n\n.board-row-badges {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-wrap: wrap;\n}\n.board-row-description {\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.board-row-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n flex-wrap: wrap;\n}\n.board-row-author { font-weight: 500; color: var(--mfb-text); }\n.board-row-comments {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-variant-numeric: tabular-nums;\n}\n.board-row-time { margin-left: auto; font-variant-numeric: tabular-nums; }\n\n/* Status / severity badges shared with the detail panel. The tones\n * borrow PNR's badge palette — calm pastels that don't fight content. */\n.board-row-badges .badge {\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.02em;\n padding: 2px 8px;\n border-radius: 999px;\n background: #f3f4f6;\n color: #374151;\n text-transform: lowercase;\n}\n.badge.status-new { background: #dbeafe; color: #1e40af; }\n.badge.status-in_progress { background: #fef3c7; color: #92400e; }\n.badge.status-awaiting_validation { background: #ede9fe; color: #6b21a8; }\n.badge.status-closed { background: #d1fae5; color: #065f46; }\n.badge.status-rejected { background: #fee2e2; color: #991b1b; }\n.badge.status-duplicate { background: #f3f4f6; color: #4b5563; }\n.badge.status-wontfix { background: #f3f4f6; color: #4b5563; }\n.badge.severity-blocker { background: #fee2e2; color: #991b1b; }\n.badge.severity-high { background: #fee2e2; color: #b91c1c; }\n.badge.severity-medium { background: #fef3c7; color: #92400e; }\n.badge.severity-low { background: #e0f2fe; color: #075985; }\n\n/* Detail pane (within Board). Inherits from .report-detail.variant-board */\n.board-detail-wrap {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n overflow-y: auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n.board-detail-empty {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n color: var(--mfb-text-muted);\n padding: var(--mfb-space-5);\n text-align: center;\n font-size: var(--mfb-text-sm);\n}\n.board-detail-empty svg { opacity: 0.6; }\n\n.report-detail.variant-board { padding: 16px 18px; }\n.report-detail-header--board {\n align-items: center;\n}\n.report-detail-back {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n}\n\n/* Empty + loading + error states */\n.board-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n text-align: center;\n padding: var(--mfb-space-5);\n color: var(--mfb-text-muted);\n flex: 1 1 auto;\n}\n.board-empty h3 {\n margin: 0;\n font-size: var(--mfb-text-base);\n color: var(--mfb-text);\n}\n.board-empty p {\n margin: 0;\n font-size: var(--mfb-text-sm);\n}\n.board-error {\n padding: 12px 14px;\n border-radius: var(--mfb-radius);\n background: #fef2f2;\n color: #991b1b;\n font-size: var(--mfb-text-sm);\n}\n\n/* Skeleton rows pulse subtly so the user knows something's coming. */\n.board-list-skeleton .skeleton-row {\n cursor: default;\n pointer-events: none;\n}\n.skeleton-line {\n height: 12px;\n background: linear-gradient(\n 90deg,\n var(--mfb-surface) 0%,\n color-mix(in srgb, var(--mfb-surface) 60%, var(--mfb-border)) 50%,\n var(--mfb-surface) 100%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: mfb-skeleton 1400ms ease-in-out infinite;\n}\n.skeleton-badges { width: 60%; height: 14px; }\n.skeleton-text { width: 90%; }\n.skeleton-text.short { width: 60%; }\n@keyframes mfb-skeleton {\n from { background-position: 100% 0; }\n to { background-position: -100% 0; }\n}\n@media (prefers-reduced-motion: reduce) {\n .skeleton-line { animation: none; }\n}\n`\n","import { createApiClient } from './api/client'\nimport { installCapture } from './capture'\nimport { resolveStrings } from './widget/i18n'\nimport { mountWidget } from './widget/mount'\nimport type { FeedbackApi, FeedbackConfig, ReportPayload, ReportTransformer, UserIdentity } from './types'\n\nexport interface InternalConfig extends FeedbackConfig {\n fetchImpl?: typeof fetch\n}\n\ninterface WindowWithGlobal extends Window {\n mhosaicFeedback?: FeedbackApi\n}\n\nexport function createFeedback(config: InternalConfig): FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } {\n const env = config.env ?? 'prod'\n const locale =\n config.locale ?? (typeof navigator !== 'undefined' ? navigator.language : undefined)\n const strings = resolveStrings(\n config.translations ?? {},\n locale !== undefined ? { locale } : {},\n )\n const capture = installCapture({\n ...(config.sanitizeUrl !== undefined && { sanitizeUrl: config.sanitizeUrl }),\n })\n const api = createApiClient({\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n ...(config.fetchImpl !== undefined && { fetch: config.fetchImpl }),\n ...(config.beforeSend !== undefined && { beforeSend: config.beforeSend }),\n })\n\n let user: UserIdentity | undefined = config.user\n let metadata: Record<string, unknown> = config.metadata ?? {}\n const transformers: ReportTransformer[] = []\n\n const host = document.createElement('div')\n host.className = 'mhosaic-feedback'\n if (config.attachTo) {\n const attach = typeof config.attachTo === 'string' ? document.querySelector(config.attachTo) : config.attachTo\n attach?.appendChild(host)\n } else {\n document.body.appendChild(host)\n }\n\n async function buildAndSubmit(values: {\n description: string\n feedback_type?: string\n severity?: string\n synthetic?: boolean\n /** Screenshot the user explicitly attached — via dropzone, paste, file\n * picker, the annotator, or the \"Capture page\" button (getDisplayMedia).\n * v0.12: no implicit html2canvas-on-submit fallback anymore. If the\n * user didn't attach anything, the report ships without a screenshot. */\n screenshot?: Blob\n /** Capture-method label for the payload. \"manual\" for dropzone/paste/\n * annotator/file-picker. \"display_media\" for the native screen-share\n * picker. Defaults to \"manual\" when a blob is provided without a\n * capture method (back-compat with existing call sites). */\n capture_method?: 'manual' | 'display_media'\n }) {\n // Auto-error reports never carry a screenshot — the captured ring\n // buffer (errors, console, network) carries the signal, and the DOM\n // is typically in an inconsistent state when a JS error fires anyway.\n const manualScreenshot = values.synthetic ? undefined : values.screenshot\n const technical_context = capture.snapshot()\n // Surface identify()/setMetadata() values on the report. Without this\n // the host-supplied user identity and metadata sit in closure forever\n // and never reach the operator — a stack trace from a logged-in user\n // looks anonymous on the dashboard.\n if (user) technical_context.user = user\n if (metadata && Object.keys(metadata).length > 0) {\n technical_context.metadata = { ...metadata }\n }\n const captureMethod: ReportPayload['capture_method'] = manualScreenshot\n ? (values.capture_method ?? 'manual')\n : 'none'\n const payload: ReportPayload = {\n description: values.description,\n feedback_type: (values.feedback_type ?? 'bug') as ReportPayload['feedback_type'],\n severity: (values.severity ?? 'medium') as ReportPayload['severity'],\n env,\n page_url: window.location.href,\n user_agent: navigator.userAgent,\n capture_method: captureMethod,\n technical_context,\n }\n // Build-time stamp; tsup's `define` substitutes the package.json\n // version into __MFB_VERSION__. Conditionally assigned so\n // exactOptionalPropertyTypes stays happy under vitest (no tsup\n // pass) where the constant is undefined.\n if (typeof __MFB_VERSION__ !== 'undefined' && __MFB_VERSION__) {\n payload.widget_version = __MFB_VERSION__\n }\n if (manualScreenshot) payload.screenshot = manualScreenshot\n if (values.synthetic) payload.synthetic = true\n // v0.7: lift the host-provided identity to the top level so the\n // backend can upsert a WidgetUser FK. technical_context.user still\n // carries it for backward-compat with the operator's tech-context\n // viewer (and so an audit-log entry knows who was identified at\n // submission time).\n if (user?.id !== undefined && user.id !== null && user.id !== '') {\n payload.user = {\n // The host can pass `id` as a string or number; the backend\n // stores it as an opaque string. Coerce here to a stable shape.\n id: String(user.id),\n ...(user.email !== undefined && { email: user.email }),\n ...(user.name !== undefined && { name: user.name }),\n }\n }\n let finalPayload: ReportPayload = payload\n for (const t of transformers) finalPayload = await t(finalPayload)\n try {\n const result = await api.submitReport(finalPayload)\n config.onSubmitSuccess?.(result)\n capture.clear()\n return result\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n config.onError?.(error)\n throw error\n }\n }\n\n const handle = mountWidget({\n host,\n strings,\n showFAB: config.showFAB ?? true,\n onSubmit: async (values) => { await buildAndSubmit(values) },\n api,\n // Keep this a callback (not a snapshot) so the mount picks up identity\n // changes that happen after createFeedback() — `notifyIdentityChanged()`\n // is the trigger for a re-render.\n getExternalId: () => (user?.id !== undefined && user.id !== null && user.id !== '' ? String(user.id) : undefined),\n })\n\n const instance: FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } = {\n show() { handle.open() },\n hide() { handle.close() },\n open(opts) { handle.open(); void opts },\n async submit(partial) {\n return buildAndSubmit({\n description: partial.description,\n ...(partial.feedback_type !== undefined && { feedback_type: partial.feedback_type }),\n ...(partial.severity !== undefined && { severity: partial.severity }),\n ...(partial.synthetic !== undefined && { synthetic: partial.synthetic }),\n ...(partial.screenshot !== undefined && { screenshot: partial.screenshot }),\n })\n },\n identify(u) {\n user = u\n // Tell the mount to re-evaluate FAB visibility / Mine tab gating.\n handle.notifyIdentityChanged()\n },\n setMetadata(kv) { metadata = { ...metadata, ...kv } },\n shutdown() {\n handle.dispose()\n capture.dispose()\n host.remove()\n // Only release the global if it still points at *us* — otherwise a\n // newer instance has taken ownership and we mustn't blow it away.\n const w = window as unknown as WindowWithGlobal\n if (w.mhosaicFeedback === instance) {\n delete w.mhosaicFeedback\n }\n },\n _registerTransformer(fn: ReportTransformer) { transformers.push(fn) },\n }\n\n // Expose the instance globally for ad-hoc callers (DevTools, docs pages,\n // help-widget integrations). The most-recently-created instance wins.\n ;(window as unknown as WindowWithGlobal).mhosaicFeedback = instance\n\n return instance\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["useEffect","useState","jsx","jsxs","Fragment","jsx","jsxs","POLL_MS","useState","useEffect","h","useEffect","useMemo","useRef","useState","jsx","jsxs","formatRelative","jsx","jsxs","POLL_MS","useState","useRef","useEffect","useMemo","jsx","useEffect","useRef","useState","useEffect","useRef","useState","jsx","jsxs","h","jsx","jsxs","TYPES","SEVERITIES","useState","useRef","useEffect","useEffect","useRef","useState","jsx","jsxs","jsx","jsxs","POLL_MS","useState","useRef","useEffect","useEffect","useRef","jsx","jsxs","Fragment","jsx","jsxs"]}