@unhingged/vizu-core 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auto.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auto.ts","../src/types.ts","../src/util.ts","../src/storage.ts","../src/cloud.ts","../src/fingerprint.ts","../src/highlighter.ts","../src/popover.ts","../src/pill.ts","../src/sidebar.ts","../src/styles.ts","../src/shortcut.ts","../src/events.ts","../src/index.ts"],"sourcesContent":["import { Vizu, type VizuOptions, type StorageOption } from './index';\n\ndeclare global {\n interface Window {\n __vizu?: Vizu;\n Vizu?: typeof Vizu;\n }\n}\n\n/**\n * Script-tag drop-in. Constructs a Vizu instance from `data-*` attributes on the\n * script tag and exposes it as `window.__vizu`. The instance has NO actions\n * registered by default — integrators call `window.__vizu.addAction({...})` to\n * wire up Copy-as-prompt / JSON export / Clear / send-to-backend / anything else.\n *\n * Vizu is a UI for collecting annotations. Formatting and persisting them is the\n * host application's job: subscribe to events (`comment:added`, etc.) and react.\n */\nfunction findScript(): HTMLScriptElement | null {\n if (typeof document === 'undefined') return null;\n const current = document.currentScript as HTMLScriptElement | null;\n if (current) return current;\n const scripts = Array.from(document.scripts);\n return (\n scripts.find((s) => s.dataset && s.dataset.vizuAuto !== undefined) ||\n scripts.find((s) => s.src && /vizu(\\.min)?\\.js/.test(s.src)) ||\n null\n );\n}\n\nfunction init() {\n if (typeof window === 'undefined') return;\n if (window.__vizu) return;\n const script = findScript();\n const ds = script?.dataset || ({} as DOMStringMap);\n\n const storage: StorageOption | undefined = ds.storage\n ? (['local', 'memory', 'none'] as const).includes(ds.storage as any)\n ? (ds.storage as StorageOption)\n : 'local'\n : 'local';\n\n const user = ds.userName\n ? {\n id: ds.userId,\n name: ds.userName,\n avatarUrl: ds.userAvatar,\n email: ds.userEmail,\n }\n : undefined;\n\n const opts: VizuOptions = {\n shortcut: ds.shortcut || 'mod+shift+e',\n namespace: ds.namespace || 'default',\n pageVersion: ds.version,\n accent: ds.accent,\n startEnabled: ds.startEnabled === 'true' || ds.startEnabled === '' || ds.startEnabled === '1',\n ignoreSelectors: ds.ignore\n ? ds.ignore.split(',').map((s) => s.trim()).filter(Boolean)\n : [],\n storage,\n user,\n };\n // Script-tag mode: prefer `local` storage as the convenient default (programmatic API defaults to `memory`).\n const v = new (Vizu as any)(opts, 'local') as Vizu;\n\n window.__vizu = v;\n window.Vizu = Vizu;\n}\n\ninit();\n\nexport { Vizu } from './index';\n","/**\n * Single rung of the ancestor chain: the element's tag and its\n * `nth-of-type` index inside its parent (1-indexed). Captured root-near\n * → closer-to-target so the matcher can shift-search through wrapper\n * additions.\n */\nexport interface AncestorStep {\n tag: string;\n nthOfType: number;\n}\n\nexport interface ElementFingerprint {\n selector: string;\n parentSelector: string;\n tagName: string;\n textSnippet: string;\n siblingIndex: number;\n attributes: {\n id?: string;\n classList?: string[];\n role?: string;\n ariaLabel?: string;\n dataKey?: string;\n };\n /**\n * 3-step ancestor chain. Added in algorithm v2 (Phase 11.4.2). Order:\n * immediate parent first, then grandparent, then great-grandparent.\n * Legacy fingerprints written before this field lack it; the matcher\n * falls back to the parent+sibling-index+tag heuristic for those.\n */\n ancestorChain?: { steps: AncestorStep[] };\n /**\n * Algorithm version that produced this fingerprint. Absent (or `1`) on\n * legacy captures; new captures stamp `2` (Phase 11.4 hardened matcher\n * — class-signature rung, multi-ancestor chain, Levenshtein text scan).\n * The matcher routes on the presence of {@link ancestorChain} today, so\n * this field is informational; it gives us a stable handle to migrate\n * data when v3 ships without re-introspecting fingerprint shape.\n */\n algorithmVersion?: 1 | 2;\n}\n\nexport interface VizuUser {\n id?: string;\n name: string;\n avatarUrl?: string;\n email?: string;\n /** Free-form host-defined extras (team, role, etc.) preserved on each comment. */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Slim shape for the mention picker. The host can @-mention any of\n * these in a comment body. Returned by `Vizu.searchMentionable()` and\n * cached by the popover dropdown. Name + avatar is all the UI needs;\n * email/role intentionally omitted to keep the cross-origin surface\n * minimal.\n */\nexport interface MentionableUser {\n id: string;\n name: string;\n avatarUrl?: string;\n}\n\n/** Current schema version. Bump + add a migration when the shape changes. */\nexport const SCHEMA_VERSION = 2 as const;\nexport type SchemaVersion = typeof SCHEMA_VERSION;\n\n/**\n * Triage state. Cloud workspaces use this for filtering;\n * the local sidebar treats `resolved` / `wontfix` as \"dim\".\n */\nexport type CommentStatus = 'open' | 'resolved' | 'wontfix';\n\n/**\n * Outcome of resolving a comment's fingerprint against the current DOM.\n *\n * - `exact` : `data-vizu-key` or `id` matched. We trust this 100%.\n * - `likely` : The full CSS selector path matched. Probably the right\n * element but not guaranteed if the page has dynamic\n * duplicates.\n * - `drifted` : Only the structural fallback (parent + sibling-index)\n * or the page-wide text snippet matched. The page has\n * changed since the comment was written. Render with a\n * visible warning so visitors don't act on a stale match.\n * - `orphaned` : Nothing matched. The element is gone. Comment is shown\n * in a separate sidebar section.\n *\n * Returned by `findByFingerprint`; emitted via the `anchor:resolved` event.\n */\nexport type MatchConfidence = 'exact' | 'likely' | 'drifted' | 'orphaned';\n\n/** Result of a fingerprint resolution. `element` is null iff `confidence === 'orphaned'`. */\nexport interface FingerprintMatch {\n element: Element | null;\n confidence: MatchConfidence;\n}\n\n/**\n * Embedded reply in a comment thread. Threading UI ships in v1.1 — the\n * shape is stamped in v2 so writes are already future-correct.\n */\nexport interface Reply {\n id: string;\n text: string;\n createdAt: number;\n author?: VizuUser;\n /** Identity provider's user id (Clerk, etc.) when known. */\n authorId?: string;\n /** Clerk user ids mentioned in this reply. */\n mentions?: string[];\n}\n\n/**\n * File attachment metadata. Storage backend (Vercel Blob, S3, …) is\n * decided by the host / cloud adapter; the comment only carries the\n * resolved URL.\n */\nexport interface Attachment {\n id: string;\n url: string;\n mimeType: string;\n sizeBytes: number;\n uploadedAt: number;\n /** Original filename the user uploaded. */\n filename?: string;\n}\n\nexport interface VizuComment {\n id: string;\n /** Schema version. Set to {@link SCHEMA_VERSION} on every new write. */\n schemaVersion: SchemaVersion;\n /**\n * One or more elements this comment is anchored to. Length 1 = single-anchor\n * (the common case); >1 = \"grouped\" comment that applies to multiple\n * elements together (e.g., \"align these three CTAs to the same baseline\").\n *\n * Always has length ≥ 1. Legacy v1 comments (with a singular `fingerprint`\n * field) are auto-migrated to `[fingerprint]` on load — see `migrateComment`.\n */\n fingerprints: ElementFingerprint[];\n /** @deprecated v1 only — kept on the type so older payloads parse cleanly; migrated at load time. */\n fingerprint?: ElementFingerprint;\n text: string;\n createdAt: number;\n pageVersion?: string;\n /** @deprecated use `pageUrl` — kept for backwards-compat read of v1 payloads. */\n url?: string;\n /** Page URL where the comment was anchored. Cloud reads filter on this. */\n pageUrl?: string;\n /** Captured from `Vizu.setUser` / `options.user` at the moment the comment was created. */\n author?: VizuUser;\n /** Identity provider's user id (Clerk, etc.) when known. Cloud writes set this from the session. */\n authorId?: string;\n /**\n * Element references the user inserted via `#` mentions. Keyed by short refId.\n * The comment text contains tokens like `[#label](vizu-ref:refId)` that resolve\n * to these fingerprints; clicking a rendered reference jumps to the element.\n */\n references?: Record<string, ElementFingerprint>;\n /** Triage state. Defaults to `'open'` on new comments. */\n status: CommentStatus;\n /** Threaded replies. UI ships in v1.1; the array is always present. */\n replies: Reply[];\n /** File attachments. UI ships in v1.2; the array is always present. */\n attachments: Attachment[];\n /** Clerk user ids mentioned in this comment. */\n mentions: string[];\n /**\n * Timestamp (ms) of the last self-healing re-fingerprint pass. Set by\n * the Vizu class when a `drifted` resolution lands on an existing\n * element and the fingerprint is rewritten in place. Reads as null\n * for comments that have never drifted. Surface in the dashboard if\n * you want to flag \"auto-healed\" rows; ignore otherwise.\n */\n fingerprintsRefreshedAt?: number | null;\n}\n\n/* ─── Storage adapter (v2) ─────────────────────────────────────────────── */\n\n/**\n * Storage event emitted by adapters that support push (cloud adapters).\n * Local / memory adapters never emit these.\n */\nexport type StorageEvent =\n | { type: 'added'; comment: VizuComment }\n | { type: 'updated'; comment: VizuComment }\n | { type: 'removed'; id: string }\n | { type: 'cleared' };\n\n/**\n * Optional authorization context an adapter may surface to the host.\n * Cloud adapters set this after the user signs in; the Vizu class\n * forwards it via `vizu.getAuthContext()` for hosts that need to call\n * the same backend with the same session.\n */\nexport interface AuthContext {\n /** Identity provider's user id (e.g. Clerk user id). */\n userId: string;\n /** Short-lived bearer token. Adapter is responsible for refresh. */\n token: string;\n /** ISO timestamp the token expires; informational. */\n expiresAt?: string;\n}\n\n/**\n * Storage contract v2. Per-comment operations replace v1's full-array\n * `save(comments[])`. Cloud-friendly: each mutation is one network call,\n * the adapter can do optimistic-concurrency / conflict resolution\n * without re-shipping the whole list.\n *\n * Hosts that wrote against v1 (`load / save / clear`) are still accepted —\n * see `wrapV1Adapter` in `storage.ts` for the back-compat shim.\n *\n * Reply ops (`addReply` / `removeReply`) are optional: adapters that don't\n * implement them fall back to `updateComment` with a manually-patched\n * `replies` array. Wrapping logic lives in the Vizu class, not here.\n */\nexport interface StorageAdapter {\n /** Load all comments for this namespace. Called once at boot, plus on `setComments`. */\n load(namespace: string): Promise<VizuComment[]>;\n /** Persist a new comment. */\n addComment(namespace: string, comment: VizuComment): Promise<void>;\n /** Patch an existing comment by id. Adapter returns the updated doc or `null` if not found. */\n updateComment(namespace: string, id: string, patch: Partial<VizuComment>): Promise<VizuComment | null>;\n /** Remove a comment by id. */\n removeComment(namespace: string, id: string): Promise<void>;\n /** Replace the entire list (used by hosts that hydrate from their own backend). */\n setAll(namespace: string, comments: VizuComment[]): Promise<void>;\n /** Wipe the namespace. */\n clear(namespace: string): Promise<void>;\n /** Append a reply to a comment. Adapter returns the parent or null if not found. */\n addReply?(namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null>;\n /** Remove a reply by id from a comment. Adapter returns the parent or null if not found. */\n removeReply?(namespace: string, commentId: string, replyId: string): Promise<VizuComment | null>;\n /**\n * Upload a file attachment. Returns the resolved {@link Attachment} with\n * a public URL the host can persist on a comment. Adapters that don't\n * implement this surface report `vizu.canUploadAttachments() === false`\n * and the popover hides the upload UI.\n */\n uploadAttachment?(namespace: string, file: File): Promise<Attachment>;\n /** Optional remote-change subscription. Returns an unsubscribe function. */\n subscribe?(namespace: string, handler: (event: StorageEvent) => void): () => void;\n /** Optional auth context (cloud adapters). */\n getAuthContext?(): AuthContext | null;\n}\n\n/**\n * Legacy v1 adapter shape. Kept exported so hosts can still type their\n * existing adapters; the resolver wraps them automatically.\n */\nexport interface StorageAdapterV1 {\n load(namespace: string): Promise<VizuComment[]>;\n save(namespace: string, comments: VizuComment[]): Promise<void>;\n clear(namespace: string): Promise<void>;\n}\n\nexport type StorageOption = 'local' | 'memory' | 'none' | StorageAdapter | StorageAdapterV1;\n\n/* ─── Action API ───────────────────────────────────────────────────────── */\n\n/** Context handed to every action's onClick. Has the data and the side-effect helpers. */\nexport interface ActionContext {\n comments: VizuComment[];\n pageHtml: string;\n pageUrl?: string;\n pageVersion?: string;\n user?: VizuUser;\n timestamp: number;\n /** Quick copy-to-clipboard helper. */\n copyToClipboard: (text: string) => void;\n /** Briefly show a toast above the pill. */\n toast: (message: string) => void;\n}\n\nexport interface VizuAction {\n id: string;\n label: string;\n /** Tooltip / aria-label */\n title?: string;\n variant?: 'primary' | 'default' | 'ghost';\n /** Placement in the pill. Defaults to 'main'. */\n position?: 'main';\n onClick: (ctx: ActionContext) => void | Promise<void>;\n /** Only render the action when this returns true. */\n visibleWhen?: (ctx: { commentsCount: number }) => boolean;\n}\n\n/* ─── Cloud option ─────────────────────────────────────────────────────── */\n\n/**\n * Cloud connection options. When set, the resolver constructs the built-in\n * CloudStorageAdapter (Phase 3) and uses it instead of `storage`. If both\n * `cloud` and `storage` are provided, `cloud` wins and a `console.warn`\n * is emitted at construction time.\n */\nexport interface VizuCloudOptions {\n /** Workspace slug. Required. Created in the Vizu dashboard. */\n workspace: string;\n /** Override the API base URL. Defaults to `'https://vizu.unhingged.com'`. */\n apiUrl?: string;\n /**\n * Auto-open the popup sign-in window when a write is attempted by an\n * unauthenticated user. Defaults to `true`.\n */\n autoSignIn?: boolean;\n}\n\nexport interface VizuOptions {\n shortcut?: string;\n namespace?: string;\n pageVersion?: string;\n /**\n * Storage strategy. Default behavior:\n * - **Programmatic** (`new Vizu()`): defaults to `'memory'`; host should listen to events to persist.\n * - **Script-tag auto-init**: defaults to `'local'` for zero-config drop-in (override via `data-storage=\"memory\"` or `data-storage=\"none\"`).\n *\n * When `cloud` is set this option is ignored.\n */\n storage?: StorageOption;\n /** Cloud workspace connection. Overrides `storage`. See {@link VizuCloudOptions}. */\n cloud?: VizuCloudOptions;\n ignoreSelectors?: string[];\n accent?: string;\n startEnabled?: boolean;\n user?: VizuUser;\n /** Actions to register on construction. You can also call `vizu.addAction(...)` later. */\n actions?: VizuAction[];\n /**\n * Convenience callbacks. These are aliases for `.on('comment:added', ...)` etc.\n * The event API is preferred for new code.\n */\n onCommentAdded?: (c: VizuComment) => void;\n onCommentRemoved?: (id: string) => void;\n}\n\n/* ─── Event bus ────────────────────────────────────────────────────────── */\n\n/**\n * Typed event map. Every event payload is an object (never bare values) so the\n * shape is forward-compatible.\n */\nexport interface VizuEventMap {\n 'enabled': Record<string, never>;\n 'disabled': Record<string, never>;\n 'mounted': Record<string, never>;\n 'unmounted': Record<string, never>;\n 'comment:added': { comment: VizuComment };\n 'comment:removed': { id: string; comment: VizuComment };\n 'comment:updated': { comment: VizuComment };\n 'comments:cleared': { previous: VizuComment[] };\n 'comments:set': { comments: VizuComment[] };\n 'comments:loaded': { comments: VizuComment[] };\n 'element:selected': { target: Element; fingerprint: ElementFingerprint };\n 'element:deselected': Record<string, never>;\n 'sidebar:opened': Record<string, never>;\n 'sidebar:closed': Record<string, never>;\n 'action:invoked': { id: string };\n 'user:changed': { user: VizuUser | null };\n /**\n * Emitted once per comment when the lib resolves (or fails to resolve)\n * its fingerprint against the current DOM. `confidence: 'orphaned'`\n * means the comment is in the sidebar's orphaned section.\n */\n 'anchor:resolved': {\n comment: VizuComment;\n confidence: MatchConfidence;\n element: Element | null;\n };\n}\n\nexport type VizuEventName = keyof VizuEventMap;\nexport type VizuEventHandler<E extends VizuEventName> = (payload: VizuEventMap[E]) => void;\n","import { SCHEMA_VERSION } from './types';\nimport type { VizuUser, VizuComment, ElementFingerprint, CommentStatus, Attachment } from './types';\n\nexport function uuid(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return crypto.randomUUID();\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nexport function isInside(target: Element, selector: string): boolean {\n return !!target.closest(selector);\n}\n\nexport function escapeHtml(s: string | null | undefined): string {\n if (s == null) return '';\n return String(s).replace(/[&<>\"']/g, (ch) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[ch]!));\n}\n\nexport function formatTime(ts: number): string {\n const d = new Date(ts);\n const now = Date.now();\n const diff = now - ts;\n if (diff < 60_000) return 'just now';\n if (diff < 3_600_000) return Math.floor(diff / 60_000) + 'm ago';\n if (diff < 86_400_000) return Math.floor(diff / 3_600_000) + 'h ago';\n return d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n}\n\nexport function initials(name: string | null | undefined): string {\n if (!name) return '';\n return String(name)\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((p) => p[0]!.toUpperCase())\n .join('');\n}\n\nexport function avatarHtml(user: VizuUser | undefined | null, className = 'vz-comment-author-avatar'): string {\n if (!user) return `<span class=\"${className}\">·</span>`;\n // Defensive: author payloads from older clients / migrated data may\n // omit `name`. Fall back through id → 'User' so we never feed\n // undefined to escapeHtml or initials and crash the whole render.\n const displayName = user.name || user.id || 'User';\n if (user.avatarUrl) {\n return `<span class=\"${className}\"><img src=\"${escapeHtml(user.avatarUrl)}\" alt=\"${escapeHtml(displayName)}\" /></span>`;\n }\n return `<span class=\"${className}\">${escapeHtml(initials(displayName) || '?')}</span>`;\n}\n\n/** Short alphanumeric id for ref tokens. */\nexport function refId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nconst REF_TOKEN_RE = /\\[#([^\\]]+)\\]\\(vizu-ref:([a-zA-Z0-9_-]+)\\)/g;\nconst MENTION_TOKEN_RE = /\\[@([^\\]]+)\\]\\(vizu-mention:([a-zA-Z0-9_-]+)\\)/g;\nconst COMBINED_TOKEN_SPLIT_RE = /(\\[#[^\\]]+\\]\\(vizu-ref:[a-zA-Z0-9_-]+\\)|\\[@[^\\]]+\\]\\(vizu-mention:[a-zA-Z0-9_-]+\\))/g;\n\n/**\n * Render a comment's text as HTML, replacing inline tokens with chips:\n *\n * - `[#label](vizu-ref:refId)` → element-reference chip (delegates clicks\n * to find `[data-vz=\"ref\"]` and looks up `comment.references[refId]`).\n * - `[@display name](vizu-mention:userId)` → mention chip. Static for v1\n * (no click target); shows the display name with an @ prefix.\n */\nexport function renderCommentText(text: string, commentId: string): string {\n const parts = text.split(COMBINED_TOKEN_SPLIT_RE);\n return parts\n .map((part) => {\n const refMatch = part.match(/^\\[#([^\\]]+)\\]\\(vizu-ref:([a-zA-Z0-9_-]+)\\)$/);\n if (refMatch) {\n return `<a class=\"vz-ref\" data-vz=\"ref\" data-comment-id=\"${escapeHtml(commentId)}\" data-ref-id=\"${escapeHtml(refMatch[2])}\" role=\"button\" tabindex=\"0\">${escapeHtml(refMatch[1])}</a>`;\n }\n const mentionMatch = part.match(/^\\[@([^\\]]+)\\]\\(vizu-mention:([a-zA-Z0-9_-]+)\\)$/);\n if (mentionMatch) {\n return `<span class=\"vz-mention\" data-vz=\"mention\" data-mention-id=\"${escapeHtml(mentionMatch[2])}\">@${escapeHtml(mentionMatch[1])}</span>`;\n }\n return escapeHtml(part);\n })\n .join('');\n}\n\n/**\n * Extract Clerk user ids from `[@name](vizu-mention:userId)` tokens in a\n * comment's text. Used at save time to populate `comment.mentions[]` so\n * downstream consumers (the dashboard, the notification path) can find\n * who was mentioned without re-parsing the text.\n */\nexport function extractMentions(text: string): string[] {\n const out: string[] = [];\n for (const m of text.matchAll(MENTION_TOKEN_RE)) {\n if (!out.includes(m[2])) out.push(m[2]);\n }\n return out;\n}\n\nexport { REF_TOKEN_RE, MENTION_TOKEN_RE };\n\n/**\n * Render a comment's attachment list as HTML — image thumbnails for\n * image MIME types, a generic file chip otherwise. Click opens the URL\n * in a new tab. Used in both popover + sidebar so the look matches.\n *\n * Returns the empty string when the comment has no attachments so the\n * caller can interpolate unconditionally.\n */\nexport function renderAttachmentsHtml(attachments: Attachment[] | undefined): string {\n if (!Array.isArray(attachments) || attachments.length === 0) return '';\n const items = attachments\n .map((a) => {\n const isImage = a.mimeType?.startsWith('image/');\n const filename = a.filename ?? a.url.split('/').pop() ?? 'file';\n if (isImage) {\n return `\n <a class=\"vz-attachment-display\" href=\"${escapeHtml(a.url)}\" target=\"_blank\" rel=\"noreferrer\" title=\"${escapeHtml(filename)}\">\n <img src=\"${escapeHtml(a.url)}\" alt=\"${escapeHtml(filename)}\" loading=\"lazy\" />\n </a>\n `;\n }\n return `\n <a class=\"vz-attachment-display vz-attachment-file\" href=\"${escapeHtml(a.url)}\" target=\"_blank\" rel=\"noreferrer\">\n <span class=\"vz-attachment-icon\" aria-hidden=\"true\">📄</span>\n <span class=\"vz-attachment-filename\">${escapeHtml(filename)}</span>\n </a>\n `;\n })\n .join('');\n return `<div class=\"vz-attachment-grid\">${items}</div>`;\n}\n\n/**\n * Forward-migrate a comment from any prior schema version to the current one.\n *\n * Idempotent: passing an already-v{@link SCHEMA_VERSION} comment returns a\n * structurally-equivalent value (extra/legacy fields stripped, missing\n * defaults filled). Safe to call on every read; cost is one shallow clone.\n *\n * Versions:\n * - **v1** (unstamped): had a singular `fingerprint`; no status/replies/attachments/mentions.\n * - **v2** (current): `fingerprints[]`, `schemaVersion`, `status`, `replies`,\n * `attachments`, `mentions`. `pageUrl` replaces the older `url`.\n */\nexport function migrateComment(raw: any): VizuComment {\n if (!raw || typeof raw !== 'object') return raw as VizuComment;\n\n // Start from a shallow clone; we'll mutate a new object so callers never\n // observe partial migration.\n const next: any = { ...raw };\n\n // ─── v1 → v2: singular fingerprint becomes fingerprints[].\n if (!Array.isArray(next.fingerprints) || next.fingerprints.length === 0) {\n if (next.fingerprint) {\n next.fingerprints = [next.fingerprint as ElementFingerprint];\n }\n }\n delete next.fingerprint;\n\n // ─── v1 → v2: `url` (host page URL at write time) becomes `pageUrl`.\n if (next.url && !next.pageUrl) {\n next.pageUrl = next.url;\n }\n\n // ─── Default the v2-required fields that v1 payloads don't carry.\n if (!('status' in next) || !isValidStatus(next.status)) next.status = 'open' as CommentStatus;\n if (!Array.isArray(next.replies)) next.replies = [];\n if (!Array.isArray(next.attachments)) next.attachments = [];\n if (!Array.isArray(next.mentions)) next.mentions = [];\n // ─── Phase 11.4.4.3: self-healing timestamp. Optional; null for comments\n // that haven't been re-fingerprinted. Don't overwrite an existing value.\n if (!('fingerprintsRefreshedAt' in next)) next.fingerprintsRefreshedAt = null;\n\n // ─── Always stamp the current schema version.\n next.schemaVersion = SCHEMA_VERSION;\n\n return next as VizuComment;\n}\n\nfunction isValidStatus(s: unknown): s is CommentStatus {\n return s === 'open' || s === 'resolved' || s === 'wontfix';\n}\n\n/** Forward-migrate an array of comments in one call. Skips falsy entries. */\nexport function migrateComments(raws: unknown[]): VizuComment[] {\n if (!Array.isArray(raws)) return [];\n const out: VizuComment[] = [];\n for (const raw of raws) {\n if (!raw || typeof raw !== 'object') continue;\n out.push(migrateComment(raw));\n }\n return out;\n}\n\n/**\n * Returns true if any comment in the list is older than the current schema\n * (e.g. is missing `schemaVersion`, has the singular `fingerprint`, or is\n * missing one of the v2 default arrays). Used by the load path to decide\n * whether to re-persist after migration.\n */\nexport function needsPersist(raws: unknown[]): boolean {\n if (!Array.isArray(raws)) return false;\n for (const raw of raws) {\n if (!raw || typeof raw !== 'object') continue;\n const c = raw as Record<string, unknown>;\n if (c.schemaVersion !== SCHEMA_VERSION) return true;\n if ('fingerprint' in c) return true;\n if (!Array.isArray(c.replies)) return true;\n if (!Array.isArray(c.attachments)) return true;\n if (!Array.isArray(c.mentions)) return true;\n }\n return false;\n}\n","import type {\n StorageAdapter,\n StorageAdapterV1,\n StorageEvent,\n VizuComment,\n Reply,\n} from './types';\nimport { migrateComments } from './util';\n\nconst key = (ns: string) => `vizu:comments:${ns}`;\n\n/**\n * Persist comments to the host page's localStorage. One JSON document per\n * namespace. Migrations are applied on read.\n *\n * v2 per-comment ops are emulated via read-modify-write of the underlying\n * array — fine for localStorage's <1ms cost. Cloud adapters do real\n * single-document writes.\n */\nexport class LocalStorageAdapter implements StorageAdapter {\n async load(namespace: string): Promise<VizuComment[]> {\n return readArray(namespace);\n }\n\n async addComment(namespace: string, comment: VizuComment): Promise<void> {\n const list = readArray(namespace);\n list.push(comment);\n writeArray(namespace, list);\n }\n\n async updateComment(\n namespace: string,\n id: string,\n patch: Partial<VizuComment>,\n ): Promise<VizuComment | null> {\n const list = readArray(namespace);\n const idx = list.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n const next = { ...list[idx], ...patch, id: list[idx].id } as VizuComment;\n list[idx] = next;\n writeArray(namespace, list);\n return next;\n }\n\n async removeComment(namespace: string, id: string): Promise<void> {\n const list = readArray(namespace);\n writeArray(namespace, list.filter((c) => c.id !== id));\n }\n\n async setAll(namespace: string, comments: VizuComment[]): Promise<void> {\n writeArray(namespace, comments);\n }\n\n async clear(namespace: string): Promise<void> {\n if (typeof localStorage === 'undefined') return;\n localStorage.removeItem(key(namespace));\n }\n\n async addReply(namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null> {\n const list = readArray(namespace);\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = { ...list[idx], replies: [...(list[idx].replies ?? []), reply] } as VizuComment;\n list[idx] = next;\n writeArray(namespace, list);\n return next;\n }\n\n async removeReply(namespace: string, commentId: string, replyId: string): Promise<VizuComment | null> {\n const list = readArray(namespace);\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = {\n ...list[idx],\n replies: (list[idx].replies ?? []).filter((r) => r.id !== replyId),\n } as VizuComment;\n list[idx] = next;\n writeArray(namespace, list);\n return next;\n }\n}\n\nfunction readArray(namespace: string): VizuComment[] {\n if (typeof localStorage === 'undefined') return [];\n const raw = localStorage.getItem(key(namespace));\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? migrateComments(parsed) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeArray(namespace: string, comments: VizuComment[]): void {\n if (typeof localStorage === 'undefined') return;\n localStorage.setItem(key(namespace), JSON.stringify(comments));\n}\n\n/**\n * Stores comments in RAM only — wipes on reload. Useful when the host owns\n * persistence via events (the integrator listens to `comment:added` and\n * forwards to its own backend).\n */\nexport class InMemoryStorageAdapter implements StorageAdapter {\n private store = new Map<string, VizuComment[]>();\n\n async load(namespace: string): Promise<VizuComment[]> {\n return [...(this.store.get(namespace) || [])];\n }\n\n async addComment(namespace: string, comment: VizuComment): Promise<void> {\n const list = this.store.get(namespace) ?? [];\n this.store.set(namespace, [...list, comment]);\n }\n\n async updateComment(\n namespace: string,\n id: string,\n patch: Partial<VizuComment>,\n ): Promise<VizuComment | null> {\n const list = this.store.get(namespace) ?? [];\n const idx = list.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n const next = { ...list[idx], ...patch, id: list[idx].id } as VizuComment;\n const copy = [...list];\n copy[idx] = next;\n this.store.set(namespace, copy);\n return next;\n }\n\n async removeComment(namespace: string, id: string): Promise<void> {\n const list = this.store.get(namespace) ?? [];\n this.store.set(\n namespace,\n list.filter((c) => c.id !== id),\n );\n }\n\n async setAll(namespace: string, comments: VizuComment[]): Promise<void> {\n this.store.set(namespace, [...comments]);\n }\n\n async clear(namespace: string): Promise<void> {\n this.store.delete(namespace);\n }\n\n async addReply(namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null> {\n const list = this.store.get(namespace) ?? [];\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = { ...list[idx], replies: [...(list[idx].replies ?? []), reply] } as VizuComment;\n const copy = [...list];\n copy[idx] = next;\n this.store.set(namespace, copy);\n return next;\n }\n\n async removeReply(namespace: string, commentId: string, replyId: string): Promise<VizuComment | null> {\n const list = this.store.get(namespace) ?? [];\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = {\n ...list[idx],\n replies: (list[idx].replies ?? []).filter((r) => r.id !== replyId),\n } as VizuComment;\n const copy = [...list];\n copy[idx] = next;\n this.store.set(namespace, copy);\n return next;\n }\n}\n\n/**\n * No-op adapter — never persists. The host hydrates via `setComments` and\n * listens to events to drive its own storage.\n */\nexport class NullStorageAdapter implements StorageAdapter {\n async load(): Promise<VizuComment[]> {\n return [];\n }\n async addComment(): Promise<void> {}\n async updateComment(): Promise<VizuComment | null> {\n return null;\n }\n async removeComment(): Promise<void> {}\n async setAll(): Promise<void> {}\n async clear(): Promise<void> {}\n}\n\n/* ─── v1 back-compat shim ──────────────────────────────────────────────── */\n\n/**\n * Detect whether a user-supplied object satisfies the v1 adapter contract\n * (`load / save / clear`) but is missing v2's per-comment ops. We treat\n * any object with a `save` method that lacks `addComment` as v1.\n */\nexport function isV1Adapter(x: unknown): x is StorageAdapterV1 {\n if (!x || typeof x !== 'object') return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.load === 'function' &&\n typeof o.save === 'function' &&\n typeof o.clear === 'function' &&\n typeof o.addComment !== 'function'\n );\n}\n\n/**\n * Wrap a v1 adapter so it satisfies the v2 interface. Per-comment ops are\n * emulated by read-modify-write of the full array — slow but correct.\n * Hosts get a one-time `console.warn` so they know to upgrade.\n *\n * Idempotent: wrapping an already-v2 adapter returns it unchanged.\n */\nexport function wrapV1Adapter(v1: StorageAdapterV1): StorageAdapter {\n if (!isV1Adapter(v1)) return v1 as unknown as StorageAdapter;\n let warned = false;\n const warnOnce = () => {\n if (warned) return;\n warned = true;\n if (typeof console !== 'undefined') {\n console.warn(\n '[vizu] StorageAdapter v1 detected. Per-comment ops are emulated via full-array rewrites — ' +\n 'upgrade your adapter to the v2 interface (addComment / updateComment / removeComment / setAll / load / clear) ' +\n 'when you can.',\n );\n }\n };\n\n return {\n async load(namespace: string) {\n warnOnce();\n return v1.load(namespace);\n },\n async addComment(namespace, comment) {\n warnOnce();\n const list = await v1.load(namespace);\n list.push(comment);\n await v1.save(namespace, list);\n },\n async updateComment(namespace, id, patch) {\n warnOnce();\n const list = await v1.load(namespace);\n const idx = list.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n const next = { ...list[idx], ...patch, id: list[idx].id } as VizuComment;\n list[idx] = next;\n await v1.save(namespace, list);\n return next;\n },\n async removeComment(namespace, id) {\n warnOnce();\n const list = await v1.load(namespace);\n await v1.save(\n namespace,\n list.filter((c) => c.id !== id),\n );\n },\n async setAll(namespace, comments) {\n warnOnce();\n await v1.save(namespace, comments);\n },\n async clear(namespace) {\n await v1.clear(namespace);\n },\n };\n}\n\n// Re-export the event type so adapter authors only need one import.\nexport type { StorageEvent };\n","import type {\n StorageAdapter,\n StorageEvent,\n VizuComment,\n VizuUser,\n AuthContext,\n Reply,\n Attachment,\n MentionableUser,\n} from './types';\nimport { migrateComments, migrateComment, uuid } from './util';\n\n/**\n * CloudStorageAdapter — talks to the Vizu hosted backend.\n *\n * Lifecycle:\n * - Reads are unauthenticated-attempted first (the dashboard's own\n * reads are same-origin Clerk session). If the backend says 401, we\n * open the popup once and retry.\n * - Writes require a host-token. First write opens the popup; the\n * resulting JWT is stored in sessionStorage keyed by workspace slug\n * and reused for the tab's lifetime (or until `exp` lapses).\n *\n * Token storage = sessionStorage (per-tab). Cross-tab is intentional:\n * a stolen token from one tab can't leak to another via storage events.\n * The cost is one popup per tab; cheap for the safety win.\n *\n * The popup is hosted at `${apiUrl}/connect?workspace=&origin=`. It\n * handles the Clerk sign-in handshake and posts the token back via\n * `postMessage` targeted at the host origin (never `*`).\n */\nexport interface CloudStorageOptions {\n /** Workspace slug — required. */\n workspace: string;\n /** API base URL (defaults to https://vizu.unhingged.com). */\n apiUrl?: string;\n /** Open the sign-in popup automatically on the first auth-needed write. Defaults to true. */\n autoSignIn?: boolean;\n /**\n * Fired whenever the cached host-token transitions to authenticated —\n * either freshly from the popup or rehydrated from sessionStorage.\n * Vizu wires this to `setUser()` so comments attributed after sign-in\n * carry the proper display name + avatar.\n */\n onAuthChanged?: (info: { userId: string; user: VizuUser | null }) => void;\n}\n\ninterface StoredToken {\n token: string;\n expiresAt: string; // ISO\n userId: string;\n /** Display info from the popup's postMessage; may be null on legacy popups. */\n user: VizuUser | null;\n}\n\nconst DEFAULT_API_URL = 'https://vizu.unhingged.com';\nconst POPUP_WIDTH = 480;\nconst POPUP_HEIGHT = 640;\n\nexport class CloudStorageAdapter implements StorageAdapter {\n private readonly apiUrl: string;\n private readonly workspace: string;\n private readonly autoSignIn: boolean;\n private onAuthChanged: CloudStorageOptions['onAuthChanged'];\n /** Cached current-tab token. Mirrored to sessionStorage. */\n private cachedToken: StoredToken | null = null;\n /** Single-flight: at most one popup at a time. Subsequent requests await this. */\n private pendingAuth: Promise<StoredToken> | null = null;\n /** Whether we've already fired onAuthChanged for the current token. */\n private lastNotifiedTokenId: string | null = null;\n /**\n * Sticky flag set when /connect tells us this workspace is inaccessible\n * to the signed-in user (no role, origin not allowed, workspace gone).\n * Future resolveToken calls reject immediately instead of reopening the\n * popup — the answer won't change without a Clerk account switch, which\n * a page reload covers. Cleared only by reload (or future explicit API).\n */\n private accessDenied = false;\n /**\n * Last denial reason from /connect's postMessage — useful for hosts that\n * want to render a workspace-level \"no access\" banner. Null until denied.\n */\n private accessDeniedReason: string | null = null;\n\n constructor(opts: CloudStorageOptions) {\n this.workspace = opts.workspace;\n this.apiUrl = (opts.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, '');\n this.autoSignIn = opts.autoSignIn !== false;\n this.onAuthChanged = opts.onAuthChanged;\n // Redirect-flow hand-off: when the page reloaded after /connect's\n // redirect-back (popup-blocked fallback), the token rides in the\n // URL fragment. Hydrate first so it wins over any stale value, then\n // strip the fragment so it doesn't leak into history / bookmarks.\n const fromHash = this.readHashToken();\n if (fromHash) {\n this.cachedToken = fromHash;\n this.writeStoredToken(fromHash);\n } else {\n // Popup-flow + warm-reload path: sessionStorage already holds a\n // valid token from a prior popup hand-off.\n this.cachedToken = this.readStoredToken();\n }\n if (this.cachedToken && !this.isExpired(this.cachedToken)) {\n this.fireAuthChanged(this.cachedToken);\n }\n }\n\n /**\n * Pull a token out of the URL fragment if /connect just redirected us\n * back here. Strips the fragment on success so the token doesn't sit\n * in browser history or get shared if the user copies the URL.\n *\n * The fragment is base64-encoded JSON with compact keys; see\n * apps/landing/app/connect/RedirectComplete.tsx for the producer side.\n */\n private readHashToken(): StoredToken | null {\n if (typeof window === 'undefined') return null;\n const hash = window.location.hash;\n if (!hash || hash.length < 2) return null;\n const params = new URLSearchParams(hash.slice(1));\n const payload = params.get('vizu_auth');\n if (!payload) return null;\n try {\n // atob returns a binary string (one Latin-1 char per byte). The\n // server encoded the JSON as UTF-8 before base64-ing, so naked\n // atob+JSON.parse mojibake's non-ASCII names (á → á, etc).\n // Round-trip through TextDecoder to recover the original UTF-8.\n const bytes = Uint8Array.from(atob(payload), (c) => c.charCodeAt(0));\n const decoded = new TextDecoder('utf-8').decode(bytes);\n const json = JSON.parse(decoded) as {\n t?: string;\n e?: string;\n u?: string;\n w?: string;\n n?: string;\n a?: string;\n };\n if (!json.t || !json.e || !json.u || !json.w) return null;\n if (json.w !== this.workspace) return null;\n const stored: StoredToken = {\n token: json.t,\n expiresAt: json.e,\n userId: json.u,\n user:\n typeof json.n === 'string'\n ? {\n id: json.u,\n name: json.n,\n avatarUrl: typeof json.a === 'string' ? json.a : undefined,\n }\n : null,\n };\n // Strip vizu_auth from the hash; preserve any other fragment.\n params.delete('vizu_auth');\n const remaining = params.toString();\n const newHash = remaining ? `#${remaining}` : '';\n try {\n const newUrl =\n window.location.pathname + window.location.search + newHash;\n window.history.replaceState(null, '', newUrl);\n } catch {\n /* history API may be restricted (sandbox); not fatal */\n }\n return stored;\n } catch {\n return null;\n }\n }\n\n /**\n * Open the sign-in popup right now (or no-op if a valid token is\n * cached). Vizu calls this on enable() so the user signs in BEFORE\n * trying to leave a comment — no surprise modal mid-comment.\n *\n * Throws auth_canceled if the user closes the popup. Caller can\n * decide whether to disable the surface or leave it in a read-only\n * state.\n */\n async preflightAuth(): Promise<void> {\n await this.resolveToken();\n }\n\n /**\n * Whether /connect has already told us the user can't access this\n * workspace. Vizu's write gate checks this before reopening the popup —\n * a denied state means clicking again will yield the same \"No access\"\n * page, so we suppress the retry until a page reload (or explicit API\n * call in the future).\n */\n isAccessDenied(): boolean {\n return this.accessDenied;\n }\n\n /** Last denial reason; null when not denied. */\n accessDenialReason(): string | null {\n return this.accessDeniedReason;\n }\n\n /**\n * Fetch the list of users who can be @-mentioned on this workspace.\n * Used by the popover's mention dropdown — returns owner + joined\n * editors/viewers, sorted owner-first. Silent on no auth (returns [])\n * so popover doesn't open a popup just to render an empty dropdown.\n */\n async searchMentionable(): Promise<MentionableUser[]> {\n if (!this.cachedToken || this.isExpired(this.cachedToken)) return [];\n try {\n const res = await this.fetchAuthed(\n this.apiUrl + '/api/workspaces/' + encodeURIComponent(this.workspace) + '/mentionable',\n { method: 'GET' },\n );\n if (!res.ok) return [];\n const json = (await this.parseJson(res)) as { users?: MentionableUser[] };\n return Array.isArray(json?.users) ? json.users : [];\n } catch {\n return [];\n }\n }\n\n /* ─── StorageAdapter v2 ───────────────────────────────────────────── */\n\n async load(_namespace: string): Promise<VizuComment[]> {\n // Silent on no auth: reading comments at page load must NEVER open\n // a sign-in popup. The user hasn't shown intent yet — Vizu may not\n // even be enabled. If no token is cached, return empty; the host\n // will re-call load() via the onAuthChanged callback once the user\n // explicitly signs in (shortcut or click-to-comment).\n if (!this.cachedToken || this.isExpired(this.cachedToken)) {\n // Cheap second look — another tab may have refreshed sessionStorage.\n const fresh = this.readStoredToken();\n if (!fresh || this.isExpired(fresh)) return [];\n this.cachedToken = fresh;\n }\n // Pagination: pull all pages until nextCursor === null.\n const out: VizuComment[] = [];\n let cursor: string | null = null;\n do {\n const url = new URL(this.apiUrl + '/api/comments');\n url.searchParams.set('workspace', this.workspace);\n url.searchParams.set('limit', '100');\n if (cursor) url.searchParams.set('cursor', cursor);\n const res = await this.fetchAuthed(url.toString(), { method: 'GET' });\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n const page = Array.isArray(json.comments) ? json.comments : [];\n out.push(...migrateComments(page));\n cursor = typeof json.nextCursor === 'string' ? json.nextCursor : null;\n } while (cursor);\n return out;\n }\n\n async addComment(_namespace: string, comment: VizuComment): Promise<void> {\n const res = await this.fetchAuthed(this.apiUrl + '/api/comments', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ workspace: this.workspace, comment }),\n });\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n }\n\n async updateComment(\n _namespace: string,\n id: string,\n patch: Partial<VizuComment>,\n ): Promise<VizuComment | null> {\n const url = new URL(this.apiUrl + '/api/comments/' + encodeURIComponent(id));\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(patch),\n });\n if (res.status === 404) return null;\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n return migrateComment(json.comment);\n }\n\n async removeComment(_namespace: string, id: string): Promise<void> {\n const url = new URL(this.apiUrl + '/api/comments/' + encodeURIComponent(id));\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), { method: 'DELETE' });\n if (res.status === 404) return; // already gone — fine\n if (!res.ok) {\n const json = await this.parseJson(res);\n throw apiErr(res.status, json);\n }\n }\n\n async setAll(namespace: string, comments: VizuComment[]): Promise<void> {\n // No bulk-replace endpoint by design (rare op; expensive in Mongo).\n // Wipe then re-add sequentially.\n await this.clear(namespace);\n for (const c of comments) {\n await this.addComment(namespace, c);\n }\n }\n\n async clear(_namespace: string): Promise<void> {\n const url = new URL(this.apiUrl + '/api/comments');\n url.searchParams.set('workspace', this.workspace);\n url.searchParams.set('all', 'true');\n const res = await this.fetchAuthed(url.toString(), { method: 'DELETE' });\n if (res.status === 404) return;\n if (!res.ok) {\n const json = await this.parseJson(res);\n throw apiErr(res.status, json);\n }\n }\n\n async addReply(_namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null> {\n const url = new URL(this.apiUrl + '/api/comments/' + encodeURIComponent(commentId) + '/replies');\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(reply),\n });\n if (res.status === 404) return null;\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n return json?.comment ? migrateComment(json.comment) : null;\n }\n\n async removeReply(_namespace: string, commentId: string, replyId: string): Promise<VizuComment | null> {\n const url = new URL(\n this.apiUrl +\n '/api/comments/' +\n encodeURIComponent(commentId) +\n '/replies/' +\n encodeURIComponent(replyId),\n );\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), { method: 'DELETE' });\n if (res.status === 404) return null;\n if (!res.ok) {\n const json = await this.parseJson(res);\n throw apiErr(res.status, json);\n }\n // Backend returns 204 on success; caller re-reads if it needs the latest doc.\n return null;\n }\n\n /**\n * Upload a file as multipart/form-data to the host backend, which\n * forwards to Vercel Blob server-side. Stays under the 4.5 MB Vercel\n * function body cap (our own 5 MB attachment cap will be enforced\n * server-side before that's hit).\n *\n * Deliberately doesn't use `@vercel/blob/client`'s direct-upload flow:\n * - keeps `@vizu/core` free of any `@vercel/blob` dependency\n * (script-tag and local-only consumers don't pull blob code)\n * - the multipart path is one round-trip; the direct-upload flow\n * is two (signed URL + upload). For attachments capped at 5 MB,\n * the savings don't justify the dep cost.\n */\n async uploadAttachment(_namespace: string, file: File): Promise<Attachment> {\n const form = new FormData();\n form.append('file', file, file.name);\n\n const url = new URL(this.apiUrl + '/api/uploads');\n url.searchParams.set('workspace', this.workspace);\n\n const res = await this.fetchAuthed(url.toString(), {\n method: 'POST',\n body: form,\n // Don't set content-type; the browser fills in the multipart boundary.\n });\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n if (!json?.url || typeof json.url !== 'string') {\n throw apiErr(500, { error: 'upload_failed', message: 'Backend did not return a URL.' });\n }\n return {\n id: uuid(),\n url: json.url,\n mimeType: file.type || 'application/octet-stream',\n sizeBytes: file.size,\n uploadedAt: Date.now(),\n filename: file.name,\n };\n }\n\n getAuthContext(): AuthContext | null {\n if (!this.cachedToken) return null;\n return {\n userId: this.cachedToken.userId,\n token: this.cachedToken.token,\n expiresAt: this.cachedToken.expiresAt,\n };\n }\n\n /* ─── Auth ────────────────────────────────────────────────────────── */\n\n private isExpired(t: StoredToken | null): boolean {\n if (!t) return true;\n const exp = new Date(t.expiresAt).getTime();\n // Treat tokens within 30s of expiry as expired (slop for in-flight requests).\n return !Number.isFinite(exp) || exp - Date.now() < 30_000;\n }\n\n private readStoredToken(): StoredToken | null {\n if (typeof sessionStorage === 'undefined') return null;\n const raw = sessionStorage.getItem(this.sessionKey());\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as StoredToken;\n if (this.isExpired(parsed)) {\n sessionStorage.removeItem(this.sessionKey());\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n }\n\n private writeStoredToken(t: StoredToken): void {\n this.cachedToken = t;\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.setItem(this.sessionKey(), JSON.stringify(t));\n }\n }\n\n private clearStoredToken(): void {\n this.cachedToken = null;\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.removeItem(this.sessionKey());\n }\n }\n\n private sessionKey(): string {\n // v2: keyspace bumped in 0.1.2 to drop pre-0.1.1 tokens that lack the\n // user payload (the popup didn't postMessage `user` until 0.1.1, so any\n // older sessionStorage entry rehydrates with user: null and the host\n // can't refresh setUser → comments come out as Anonymous for ~15 min).\n // Old keys silently abandoned; they expire on their own.\n return `vizu:cloud-token:v2:${this.workspace}`;\n }\n\n /**\n * Resolve a valid token, opening the popup if needed.\n *\n * Single-flight: simultaneous requests await one popup, not N.\n * Caller-friendly: throws a Error with `.code === 'auth_canceled'`\n * if the user closes the popup; hosts can catch this and present\n * a friendly message instead of a blank failure.\n */\n private async resolveToken(): Promise<StoredToken> {\n if (this.cachedToken && !this.isExpired(this.cachedToken)) return this.cachedToken;\n // Re-check sessionStorage in case another tab refreshed it.\n const fresh = this.readStoredToken();\n if (fresh) {\n this.cachedToken = fresh;\n this.fireAuthChanged(fresh);\n return fresh;\n }\n if (this.accessDenied) {\n throw makeAuthError(\n 'access_denied',\n `No access to workspace \"${this.workspace}\" (${this.accessDeniedReason ?? 'no_access'}).`,\n );\n }\n if (!this.autoSignIn) {\n throw makeAuthError('auth_required', 'Sign-in required; autoSignIn is disabled.');\n }\n if (!this.pendingAuth) {\n this.pendingAuth = this.openSignInPopup()\n .then((t) => {\n this.writeStoredToken(t);\n this.fireAuthChanged(t);\n return t;\n })\n .finally(() => {\n this.pendingAuth = null;\n });\n }\n return this.pendingAuth;\n }\n\n /**\n * Notify the host that the authenticated identity changed. De-duped\n * via the token string so multiple resolveToken() hits with the same\n * cached token don't re-call setUser on every fetch.\n */\n private fireAuthChanged(token: StoredToken): void {\n if (this.lastNotifiedTokenId === token.token) return;\n this.lastNotifiedTokenId = token.token;\n try {\n this.onAuthChanged?.({ userId: token.userId, user: token.user });\n } catch (err) {\n if (typeof console !== 'undefined') {\n console.warn('[vizu/cloud] onAuthChanged callback threw', err);\n }\n }\n }\n\n private openSignInPopup(): Promise<StoredToken> {\n return new Promise<StoredToken>((resolve, reject) => {\n if (typeof window === 'undefined') {\n return reject(makeAuthError('auth_unavailable', 'Sign-in requires a browser environment.'));\n }\n const apiOrigin = new URL(this.apiUrl).origin;\n const hostOrigin = window.location.origin;\n const popupUrl =\n this.apiUrl +\n '/connect?workspace=' +\n encodeURIComponent(this.workspace) +\n '&origin=' +\n encodeURIComponent(hostOrigin);\n\n // Center the popup. Browsers reposition off-screen popups but a\n // sane default is friendlier on multi-monitor setups.\n const left = Math.max(0, Math.round((window.screen.width - POPUP_WIDTH) / 2));\n const top = Math.max(0, Math.round((window.screen.height - POPUP_HEIGHT) / 2));\n const popup = window.open(\n popupUrl,\n 'vizu-connect',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},noopener=no,popup=yes`,\n );\n if (!popup) {\n // Fallback: blocked popup → full-page redirect to /connect.\n // /connect signs the user in, then redirects back to the host\n // page with the token packed into the URL fragment. The post-\n // redirect page load constructs a fresh adapter, which picks\n // the token up via readHashToken() above.\n //\n // This promise stays pending — the page is navigating away.\n // Anything awaiting resolveToken() is about to be torn down\n // along with the rest of the host JS.\n try {\n const returnTo = window.location.href;\n const redirectUrl =\n this.apiUrl +\n '/connect?workspace=' +\n encodeURIComponent(this.workspace) +\n '&origin=' +\n encodeURIComponent(hostOrigin) +\n '&return_to=' +\n encodeURIComponent(returnTo);\n window.location.assign(redirectUrl);\n return;\n } catch (err) {\n return reject(\n makeAuthError(\n 'popup_blocked',\n 'Sign-in popup was blocked and redirect fallback failed: ' +\n (err instanceof Error ? err.message : String(err)),\n ),\n );\n }\n }\n\n let resolved = false;\n const onMessage = (e: MessageEvent) => {\n // Strict origin check — only our /connect popup can satisfy this.\n if (e.origin !== apiOrigin) return;\n const data = e.data as\n | {\n type?: string;\n version?: number;\n token?: string;\n expiresAt?: string;\n userId?: string;\n workspace?: string;\n reason?: string;\n }\n | undefined;\n if (!data) return;\n // Denial: /connect resolved the access check to \"no\" and told us\n // why. Sticky-mark the adapter so we don't retry — same workspace\n // + same Clerk session = same answer.\n if (data.type === 'vizu:auth-denied' && data.workspace === this.workspace) {\n resolved = true;\n cleanup();\n this.accessDenied = true;\n this.accessDeniedReason = typeof data.reason === 'string' ? data.reason : 'no_access';\n reject(\n makeAuthError(\n 'access_denied',\n `No access to workspace \"${this.workspace}\" (${this.accessDeniedReason}).`,\n ),\n );\n return;\n }\n if (data.type !== 'vizu:auth') return;\n if (data.workspace !== this.workspace) return;\n if (typeof data.token !== 'string' || typeof data.expiresAt !== 'string' || typeof data.userId !== 'string') return;\n resolved = true;\n cleanup();\n // Parse the optional `user` payload — sent by the post-v0.1.0\n // /connect popup. Older popups omit it; we attribute as null.\n const userPayload = (data as { user?: { id?: unknown; name?: unknown; avatarUrl?: unknown } }).user;\n const user: VizuUser | null =\n userPayload && typeof userPayload.name === 'string'\n ? {\n id: typeof userPayload.id === 'string' ? userPayload.id : data.userId,\n name: userPayload.name,\n avatarUrl:\n typeof userPayload.avatarUrl === 'string'\n ? userPayload.avatarUrl\n : undefined,\n }\n : null;\n resolve({ token: data.token, expiresAt: data.expiresAt, userId: data.userId, user });\n };\n const onPoll = window.setInterval(() => {\n if (popup.closed) {\n if (!resolved) {\n cleanup();\n reject(makeAuthError('auth_canceled', 'Sign-in popup was closed before completing.'));\n }\n }\n }, 500);\n function cleanup() {\n window.clearInterval(onPoll);\n window.removeEventListener('message', onMessage);\n }\n window.addEventListener('message', onMessage);\n });\n }\n\n /* ─── Fetch wrapper ───────────────────────────────────────────────── */\n\n /**\n * Perform an authenticated fetch with one-shot retry on 401 (re-opens\n * the popup if the token expired during the request). 4xx other than\n * 401 / 404 throw; 5xx throws; 2xx returns.\n */\n private async fetchAuthed(url: string, init: RequestInit, retried = false): Promise<Response> {\n const token = await this.resolveToken();\n const headers = new Headers(init.headers);\n headers.set('authorization', `Bearer ${token.token}`);\n const res = await fetch(url, { ...init, headers, credentials: 'omit' });\n if (res.status === 401 && !retried) {\n this.clearStoredToken();\n return this.fetchAuthed(url, init, true);\n }\n return res;\n }\n\n private async parseJson(res: Response): Promise<any> {\n try {\n const text = await res.text();\n return text ? JSON.parse(text) : null;\n } catch {\n return null;\n }\n }\n}\n\n/* ─── Errors ──────────────────────────────────────────────────────────── */\n\ntype CloudErrorCode =\n | 'auth_required'\n | 'auth_canceled'\n | 'auth_unavailable'\n | 'popup_blocked'\n | 'origin_not_allowed'\n | 'workspace_not_found'\n /** /connect resolved access for the signed-in user → no role on the workspace. Sticky for the session. */\n | 'access_denied'\n | 'http_error';\n\nexport interface CloudError extends Error {\n code: CloudErrorCode;\n status?: number;\n body?: unknown;\n}\n\nfunction makeAuthError(code: CloudErrorCode, message: string): CloudError {\n const err = new Error(message) as CloudError;\n err.code = code;\n return err;\n}\nfunction apiErr(status: number, body: any): CloudError {\n const code: CloudErrorCode =\n body?.error === 'origin_not_allowed'\n ? 'origin_not_allowed'\n : body?.error === 'not_found'\n ? 'workspace_not_found'\n : 'http_error';\n const err = new Error(body?.message ?? `HTTP ${status}`) as CloudError;\n err.code = code;\n err.status = status;\n err.body = body;\n return err;\n}\n\nexport type { StoredToken };\n","import type { AncestorStep, ElementFingerprint, FingerprintMatch, MatchConfidence } from './types';\n\nconst TEXT_SNIPPET_MAX = 80;\n\n/**\n * Depth of the ancestor chain we capture and match against. Four steps\n * gives deep elements one extra disambiguating entry, and lets shallow\n * elements reach all the way to `<html>` for shift-search anchor.\n */\nconst ANCESTOR_DEPTH = 4;\n\n/**\n * Maximum number of wrapper additions we tolerate when matching an\n * ancestor chain. A wrapper insertion shifts the captured chain up by\n * one in the live tree; we shift-search through 0..N offsets to absorb\n * that without losing the match. Set to 3 to cover most real-world\n * refactors (Tailwind component extraction, A/B-test wrappers, design-\n * system migrations typically add 1-2 layers).\n */\nconst ANCESTOR_MAX_SHIFT = 3;\n\n/**\n * Tunable thresholds for the fuzzy matchers below. Tradeoff: lower =\n * more false-positive \"drifted\" matches (already wear a warning chip),\n * higher = more orphaned real matches (worse UX: comment leaves the\n * page). Both failure modes are visible to the user; the lower setting\n * loses less work. The stress harness (Phase 11.4.3) is the source of\n * truth for these values; do not relax them further without numbers.\n */\nconst TEXT_RATIO_THRESHOLD = 0.75;\nconst CLASS_JACCARD_THRESHOLD = 0.6;\n\nexport function buildSelector(el: Element): string {\n const parts: string[] = [];\n let current: Element | null = el;\n let depth = 0;\n while (current && current !== document.documentElement && depth < 8) {\n let part = current.tagName.toLowerCase();\n if (current.id) {\n parts.unshift('#' + CSS.escape(current.id));\n break;\n }\n const cls = Array.from(current.classList)\n .filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-'))\n .slice(0, 2);\n if (cls.length) part += '.' + cls.map((c) => CSS.escape(c)).join('.');\n const parent: Element | null = current.parentElement;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === current!.tagName);\n if (sameTag.length > 1) {\n const idx = sameTag.indexOf(current);\n part += ':nth-of-type(' + (idx + 1) + ')';\n }\n }\n parts.unshift(part);\n current = parent;\n depth++;\n }\n return parts.join(' > ');\n}\n\nexport function fingerprint(el: Element): ElementFingerprint {\n const text = (el as HTMLElement).innerText || el.textContent || '';\n const parent = el.parentElement;\n let siblingIndex = 0;\n if (parent) {\n siblingIndex = Array.from(parent.children).indexOf(el);\n }\n return {\n selector: buildSelector(el),\n parentSelector: parent ? buildSelector(parent) : '',\n tagName: el.tagName,\n textSnippet: text.trim().slice(0, TEXT_SNIPPET_MAX),\n siblingIndex,\n attributes: {\n id: el.id || undefined,\n classList: Array.from(el.classList).filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-')),\n role: el.getAttribute('role') || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n dataKey: el.getAttribute('data-vizu-key') || undefined,\n },\n ancestorChain: captureAncestorChain(el, ANCESTOR_DEPTH),\n algorithmVersion: 2,\n };\n}\n\n/**\n * Walk up from the element, collecting up to `depth` ancestors as\n * `{tag, nthOfType}` pairs. Includes `<html>` so shallow elements\n * (top-level sections, page-level h1s) still capture three chain\n * entries — without HTML, those bottom out at `[MAIN, BODY]` which\n * isn't long enough to score ≥2 after a single wrapper insertion.\n * `<html>` is always nthOfType 1; the value adds no discriminating\n * info on its own, but provides a fixed anchor for shift-search to\n * align against.\n *\n * Returns undefined when the element is detached.\n */\nexport function captureAncestorChain(\n el: Element,\n depth: number,\n): { steps: AncestorStep[] } | undefined {\n const steps: AncestorStep[] = [];\n let current: Element | null = el.parentElement;\n while (current && steps.length < depth) {\n const parent: Element | null = current.parentElement;\n let nthOfType = 1;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === current!.tagName);\n nthOfType = sameTag.indexOf(current) + 1;\n }\n steps.push({ tag: current.tagName, nthOfType });\n current = parent;\n }\n return steps.length > 0 ? { steps } : undefined;\n}\n\n/**\n * Resolve a fingerprint to a live DOM element with a confidence tag.\n *\n * Two-phase ladder:\n *\n * **Strong identifiers (short-circuit).** `data-vizu-key` and `#id`\n * are host-explicit anchors — if the captured key exists in the DOM\n * we trust it 100% and return **exact**. For `data-vizu-key`, the\n * *absence* of the captured key is itself evidence: the host marked\n * that element explicitly and the mark is gone, so we **orphan**\n * without consulting weaker rungs. `id` gets the gentler treatment\n * (fall through on miss) because ids get renamed without the element\n * itself going away.\n *\n * **Fuzzy rungs vote (Phase 11.4.4 / #135).** When no strong\n * identifier resolves, every fuzzy rung (class signature, CSS selector,\n * ancestor chain, text snippet) returns its best candidate. We tally\n * votes per element and require ≥ 2 rungs to agree on the same target\n * before returning a match. 3+ votes → **likely**, 2 votes → **drifted**,\n * ≤ 1 vote → **orphaned**.\n *\n * The vote requirement is what catches `element_deleted`: the actual\n * element is gone, so each fuzzy rung finds a *different* nearby\n * element. Votes scatter, no consensus, orphan. Before this, the\n * matcher returned the first rung's hit and confidently mis-anchored\n * the comment to a similar element nearby — the false-positive\n * surfaced by the Phase 11.4.3 stress harness.\n */\nexport function findByFingerprint(fp: ElementFingerprint, root: ParentNode = document): FingerprintMatch {\n // ─── Strong identifier: data-vizu-key ─────────────────────────────\n if (fp.attributes.dataKey) {\n try {\n const byKey = root.querySelector(`[data-vizu-key=\"${CSS.escape(fp.attributes.dataKey)}\"]`);\n if (byKey) return { element: byKey, confidence: 'exact' };\n } catch {}\n // Strong-identifier honor: the host explicitly anchored this element\n // with a key. The key is gone. Don't fuzzy-guess to a similar\n // element nearby — that's the worst kind of false positive.\n return { element: null, confidence: 'orphaned' };\n }\n\n // ─── Strong identifier: #id ───────────────────────────────────────\n if (fp.attributes.id) {\n try {\n const byId = root.querySelector('#' + CSS.escape(fp.attributes.id));\n if (byId) return { element: byId, confidence: 'exact' };\n } catch {}\n // Don't orphan on missing id — ids get renamed in design-system\n // migrations without the element going away. Fall through.\n }\n\n // ─── Fuzzy rungs cast votes ───────────────────────────────────────\n type Rung = 'class' | 'selector' | 'chain' | 'text';\n const candidates: Array<{ el: Element; rung: Rung }> = [];\n\n const classes = fp.attributes.classList;\n if (classes && classes.length > 0) {\n const m = findByClassSignature(root, fp.tagName, classes);\n if (m?.element) candidates.push({ el: m.element, rung: 'class' });\n }\n\n try {\n const bySelector = root.querySelector(fp.selector);\n if (bySelector) candidates.push({ el: bySelector, rung: 'selector' });\n } catch {}\n\n if (fp.ancestorChain && fp.ancestorChain.steps.length > 0) {\n const m = findByAncestorChain(root, fp.tagName, fp.ancestorChain.steps);\n if (m?.element) candidates.push({ el: m.element, rung: 'chain' });\n } else {\n // Legacy v1 fingerprint: structural fallback (parent + sibling index + tag).\n try {\n const parent = fp.parentSelector\n ? (root.querySelector(fp.parentSelector) as Element | null)\n : (root as Element);\n if (parent) {\n const candidate = parent.children[fp.siblingIndex];\n if (candidate && candidate.tagName === fp.tagName) {\n candidates.push({ el: candidate, rung: 'chain' });\n }\n }\n } catch {}\n }\n\n if (fp.textSnippet) {\n const m = findByTextSnippet(root, fp.tagName, fp.textSnippet);\n if (m?.element) candidates.push({ el: m.element, rung: 'text' });\n }\n\n if (candidates.length === 0) {\n return { element: null, confidence: 'orphaned' };\n }\n\n // Tally votes per element. We dedupe via a Map keyed by element identity\n // (Map handles Element references natively — no need to materialize keys).\n const votes = new Map<Element, Set<Rung>>();\n for (const c of candidates) {\n let s = votes.get(c.el);\n if (!s) {\n s = new Set<Rung>();\n votes.set(c.el, s);\n }\n s.add(c.rung);\n }\n\n let best: { el: Element; rungs: Set<Rung> } | null = null;\n for (const [el, rungs] of votes) {\n if (!best || rungs.size > best.rungs.size) best = { el, rungs };\n }\n\n if (!best) return { element: null, confidence: 'orphaned' };\n if (best.rungs.size >= 3) return { element: best.el, confidence: 'likely' };\n if (best.rungs.size >= 2) return { element: best.el, confidence: 'drifted' };\n // Single vote. Trust it only when the rung is structurally precise:\n // - class signature already filters out ambiguous matches upstream\n // - ancestor chain requires ≥2/3 score\n // Selector-alone and text-alone stay too noisy — they're the rungs\n // that confidently mis-anchor when the captured element is gone.\n if (best.rungs.has('class') || best.rungs.has('chain')) {\n return { element: best.el, confidence: 'drifted' };\n }\n return { element: null, confidence: 'orphaned' };\n}\n\n/**\n * Scan elements of the captured tag, score each by classList Jaccard\n * similarity, and return the best match only when it's UNAMBIGUOUSLY\n * the top candidate. Ambiguous results fall through to the more\n * specific selector / chain / text rungs — a misfire here would assign\n * the comment to the wrong element, which is worse than orphaning.\n *\n * \"Unambiguous\" = best score ≥ {@link CLASS_JACCARD_THRESHOLD} AND\n * gap to the runner-up is ≥ 0.1. Returns \"likely\" or null.\n */\nfunction findByClassSignature(\n root: ParentNode,\n tagName: string,\n needle: string[],\n): FingerprintMatch | null {\n const needleSet = new Set(needle.filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-')));\n if (needleSet.size === 0) return null;\n const all = root.querySelectorAll(tagName.toLowerCase());\n let best: { el: Element; score: number } | null = null;\n let secondScore = 0;\n for (const el of all) {\n const haystack = new Set(\n Array.from(el.classList).filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-')),\n );\n if (haystack.size === 0) continue;\n const score = jaccardSimilarity(needleSet, haystack);\n if (score < CLASS_JACCARD_THRESHOLD) continue;\n if (!best || score > best.score) {\n secondScore = best?.score ?? 0;\n best = { el, score };\n } else if (score > secondScore) {\n secondScore = score;\n }\n }\n if (!best) return null;\n if (best.score - secondScore < 0.1) return null; // ambiguous — fall through\n return { element: best.el, confidence: 'likely' };\n}\n\n/**\n * Match candidates of the captured tag by walking their actual ancestor\n * chain and scoring against the captured chain. Returns \"likely\" when\n * every captured step lines up (perfect chain) at some shift offset,\n * \"drifted\" when at least 2/3 line up at the best offset. Anything less\n * is not returned — fall through to the fuzzy text matcher below.\n */\nfunction findByAncestorChain(\n root: ParentNode,\n tagName: string,\n steps: AncestorStep[],\n): FingerprintMatch | null {\n const all = root.querySelectorAll(tagName.toLowerCase());\n let best: { el: Element; score: number } | null = null;\n for (const el of all) {\n const ancestors = collectAncestorSteps(el, steps.length + ANCESTOR_MAX_SHIFT);\n const score = scoreChainAgainstAncestors(ancestors, steps, ANCESTOR_MAX_SHIFT);\n if (score < 2) continue; // threshold: at least 2 ancestors line up\n if (!best || score > best.score) best = { el, score };\n }\n if (!best) return null;\n return {\n element: best.el,\n confidence: best.score === steps.length ? 'likely' : 'drifted',\n };\n}\n\n/**\n * Walk up from the candidate, collecting `limit` ancestors as\n * `AncestorStep` records. Same shape as {@link captureAncestorChain},\n * extracted so the matcher can collect more than the captured depth\n * (to allow shift-search through wrapper insertions).\n */\nfunction collectAncestorSteps(el: Element, limit: number): AncestorStep[] {\n const steps: AncestorStep[] = [];\n let current: Element | null = el.parentElement;\n while (current && steps.length < limit) {\n const parent: Element | null = current.parentElement;\n let nth = 1;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === current!.tagName);\n nth = sameTag.indexOf(current) + 1;\n }\n steps.push({ tag: current.tagName, nthOfType: nth });\n current = parent;\n }\n return steps;\n}\n\n/**\n * Levenshtein-scored scan of every element matching the captured tag.\n * Both the candidate text and the needle are whitespace-normalized\n * before scoring so trailing commas, line-wraps, and adjacent emoji\n * don't kill the match.\n */\nfunction findByTextSnippet(\n root: ParentNode,\n tagName: string,\n snippet: string,\n): FingerprintMatch | null {\n const needle = normalizeText(snippet);\n if (!needle) return null;\n const all = root.querySelectorAll(tagName.toLowerCase());\n let best: { el: Element; score: number } | null = null;\n for (const el of all) {\n const raw = (el as HTMLElement).innerText || el.textContent || '';\n const candidate = normalizeText(raw.slice(0, TEXT_SNIPPET_MAX * 2));\n if (!candidate) continue;\n const score = levenshteinRatio(needle, candidate);\n if (score < TEXT_RATIO_THRESHOLD) continue;\n if (!best || score > best.score) {\n best = { el, score };\n }\n }\n return best ? { element: best.el, confidence: 'drifted' } : null;\n}\n\n/* ─── Pure helpers (testable without a DOM) ────────────────────────────── */\n\n/**\n * Lowercase, collapse internal whitespace, trim. Stable input to the\n * Levenshtein scorer — without it, \"Try it now\" vs \"Try it now\\n\"\n * would score as different strings even though a human reads them\n * identically.\n */\nexport function normalizeText(input: string): string {\n return input.toLowerCase().replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * Normalized Levenshtein similarity in [0, 1]. 1 means identical, 0\n * means completely different. Computed as\n * `1 - distance / max(len)`. Two empty strings score 1.0.\n *\n * O(n*m) time / O(min(n,m)) space — fine for our 80-char snippets even\n * on pages with thousands of candidates.\n */\nexport function levenshteinRatio(a: string, b: string): number {\n if (a === b) return 1;\n if (a.length === 0 || b.length === 0) return a.length === b.length ? 1 : 0;\n const maxLen = Math.max(a.length, b.length);\n const distance = levenshteinDistance(a, b);\n return 1 - distance / maxLen;\n}\n\nfunction levenshteinDistance(a: string, b: string): number {\n // Make `b` the longer one to keep the row buffer small.\n if (a.length > b.length) {\n const tmp = a;\n a = b;\n b = tmp;\n }\n const prev = new Array<number>(a.length + 1);\n const curr = new Array<number>(a.length + 1);\n for (let i = 0; i <= a.length; i++) prev[i] = i;\n for (let j = 1; j <= b.length; j++) {\n curr[0] = j;\n for (let i = 1; i <= a.length; i++) {\n const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;\n curr[i] = Math.min(\n curr[i - 1] + 1, // insert\n prev[i] + 1, // delete\n prev[i - 1] + cost, // substitute\n );\n }\n for (let i = 0; i <= a.length; i++) prev[i] = curr[i];\n }\n return prev[a.length];\n}\n\n/**\n * Score how many captured ancestor steps line up against an actual\n * ancestor list. Tries every offset in `[0, maxShift]` (to absorb up\n * to `maxShift` wrapper insertions between target and root) and\n * returns the best score across offsets.\n *\n * Pure helper — testable without a DOM. The DOM-aware\n * {@link findByAncestorChain} feeds it the captured chain plus the\n * candidate's actual ancestor list (collected one level deeper than\n * the captured chain so the shift-search has room to slide).\n */\nexport function scoreChainAgainstAncestors(\n actual: AncestorStep[],\n captured: AncestorStep[],\n maxShift: number,\n): number {\n let best = 0;\n for (let offset = 0; offset <= maxShift; offset++) {\n let score = 0;\n for (let i = 0; i < captured.length; i++) {\n const a = actual[i + offset];\n if (!a) break;\n if (a.tag === captured[i].tag && a.nthOfType === captured[i].nthOfType) {\n score++;\n }\n }\n if (score > best) best = score;\n }\n return best;\n}\n\n/**\n * Jaccard similarity = |A ∩ B| / |A ∪ B|. Returns 1 for identical\n * non-empty sets, 0 when disjoint. Two empty sets score 1.0 — the\n * caller checks size beforehand for that case.\n */\nexport function jaccardSimilarity<T>(a: Set<T>, b: Set<T>): number {\n if (a.size === 0 && b.size === 0) return 1;\n let intersection = 0;\n for (const item of a) if (b.has(item)) intersection++;\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\n/** Backwards-compat: callers that only need the element. */\nexport function findElementByFingerprint(fp: ElementFingerprint, root?: ParentNode): Element | null {\n return findByFingerprint(fp, root).element;\n}\n\nexport function fingerprintKey(fp: ElementFingerprint): string {\n return fp.selector + '|' + fp.textSnippet;\n}\n\n/** Short human-readable label for an element, used in `#` reference chips. */\nexport function fingerprintLabel(fp: ElementFingerprint): string {\n const tag = fp.tagName.toLowerCase();\n if (fp.textSnippet) {\n const snippet = fp.textSnippet.length > 24 ? fp.textSnippet.slice(0, 24) + '…' : fp.textSnippet;\n return tag + ': ' + snippet;\n }\n if (fp.attributes.id) return tag + '#' + fp.attributes.id;\n if (fp.attributes.classList && fp.attributes.classList.length) return tag + '.' + fp.attributes.classList[0];\n return tag;\n}\n","import { isInside } from './util';\n\nconst IGNORE_SELECTOR = '[data-vizu-root], [data-vizu-ignore], script, style, link, meta, head';\n\nexport class Highlighter {\n private overlay: HTMLDivElement;\n private root: HTMLElement;\n private current: Element | null = null;\n private extraIgnore: string[];\n private onClick: (el: Element, mouseEvent: MouseEvent) => void;\n private active = false;\n private paused = false;\n\n constructor(\n root: HTMLElement,\n extraIgnore: string[],\n onClick: (el: Element, mouseEvent: MouseEvent) => void,\n ) {\n this.root = root;\n this.extraIgnore = extraIgnore;\n this.onClick = onClick;\n this.overlay = document.createElement('div');\n this.overlay.className = 'vz-highlight';\n this.overlay.style.display = 'none';\n this.root.appendChild(this.overlay);\n }\n\n private setCurrent(el: Element | null): void {\n if (this.current === el) return;\n this.current = el;\n }\n\n start() {\n if (this.active) return;\n this.active = true;\n document.addEventListener('mousemove', this.handleMove, true);\n document.addEventListener('click', this.handleClick, true);\n document.addEventListener('scroll', this.handleScroll, true);\n window.addEventListener('resize', this.handleScroll);\n }\n\n stop() {\n if (!this.active) return;\n this.active = false;\n document.removeEventListener('mousemove', this.handleMove, true);\n document.removeEventListener('click', this.handleClick, true);\n document.removeEventListener('scroll', this.handleScroll, true);\n window.removeEventListener('resize', this.handleScroll);\n this.overlay.style.display = 'none';\n this.setCurrent(null);\n }\n\n private shouldIgnore(el: Element): boolean {\n if (el === document.documentElement || el === document.body) return true;\n if (isInside(el, IGNORE_SELECTOR)) return true;\n for (const sel of this.extraIgnore) {\n try {\n if (isInside(el, sel)) return true;\n } catch {}\n }\n return false;\n }\n\n private handleMove = (e: MouseEvent) => {\n if (this.paused) {\n // Move tracking is suspended (e.g., popover open) — keep overlay hidden\n this.setCurrent(null);\n this.overlay.style.display = 'none';\n return;\n }\n const target = document.elementFromPoint(e.clientX, e.clientY);\n if (!target || target === this.current) return;\n if (this.shouldIgnore(target)) {\n this.setCurrent(null);\n this.overlay.style.display = 'none';\n return;\n }\n this.setCurrent(target);\n this.updateOverlay();\n };\n\n private handleClick = (e: MouseEvent) => {\n const target = document.elementFromPoint(e.clientX, e.clientY);\n if (!target) return;\n if (this.shouldIgnore(target)) return;\n e.preventDefault();\n e.stopPropagation();\n this.onClick(target, e);\n };\n\n setPaused(paused: boolean) {\n this.paused = paused;\n if (paused) {\n this.setCurrent(null);\n this.overlay.style.display = 'none';\n }\n }\n\n isPaused(): boolean { return this.paused; }\n\n private handleScroll = () => {\n if (!this.current) return;\n this.updateOverlay();\n };\n\n private updateOverlay() {\n if (!this.current) return;\n const rect = this.current.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) {\n this.overlay.style.display = 'none';\n return;\n }\n this.overlay.style.display = 'block';\n this.overlay.style.left = rect.left + 'px';\n this.overlay.style.top = rect.top + 'px';\n this.overlay.style.width = rect.width + 'px';\n this.overlay.style.height = rect.height + 'px';\n }\n\n destroy() {\n this.stop();\n this.overlay.remove();\n }\n}\n","import type { VizuComment, VizuUser, ElementFingerprint, Reply, Attachment, MentionableUser } from './types';\nimport { escapeHtml, formatTime, avatarHtml, refId as makeRefId, renderCommentText, extractMentions, renderAttachmentsHtml, initials } from './util';\nimport { fingerprintKey, fingerprintLabel } from './fingerprint';\n\n/**\n * Render the inline replies block for a comment in the popover. The\n * reply input is initially hidden and toggled by `data-vz=\"toggle-reply\"`.\n * Reply ids come from the server; the local UI references them via\n * `data-reply-id` for delete operations.\n */\n/**\n * Tiny CSS attribute-value escaper for the few places we build selectors\n * with user-supplied ids. Mirrors `CSS.escape` semantics but works in\n * older environments too.\n */\nfunction cssEscape(s: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') return CSS.escape(s);\n return s.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\nfunction renderRepliesHtml(c: VizuComment): string {\n const replies = c.replies ?? [];\n const items = replies\n .map(\n (r) => `\n <div class=\"vz-reply\" data-reply-id=\"${escapeHtml(r.id)}\">\n ${r.author ? `<div class=\"vz-comment-author\">${avatarHtml(r.author)} <span class=\"vz-comment-author-name\">${escapeHtml(r.author.name || r.author.id || 'User')}</span></div>` : ''}\n <div class=\"vz-reply-text\">${escapeHtml(r.text)}</div>\n <div class=\"vz-comment-meta\">\n <span>${escapeHtml(formatTime(typeof r.createdAt === 'number' ? r.createdAt : new Date(r.createdAt).getTime()))}</span>\n <button class=\"vz-comment-delete\" data-vz=\"delete-reply\" data-comment-id=\"${escapeHtml(c.id)}\" data-reply-id=\"${escapeHtml(r.id)}\">delete</button>\n </div>\n </div>\n `,\n )\n .join('');\n return `\n <div class=\"vz-replies\" data-comment-id=\"${escapeHtml(c.id)}\" hidden>\n <div class=\"vz-reply-list\">${items}</div>\n <div class=\"vz-reply-form\">\n <textarea class=\"vz-reply-input\" placeholder=\"Reply…\" rows=\"2\" data-comment-id=\"${escapeHtml(c.id)}\"></textarea>\n <button class=\"vz-btn vz-btn-primary vz-btn-reply\" data-vz=\"send-reply\" data-id=\"${escapeHtml(c.id)}\">Send</button>\n </div>\n </div>\n `;\n}\n\nexport interface PopoverCallbacks {\n /**\n * Save a comment with one or more anchored fingerprints and the parsed\n * reference map. Refs are inline tokens in `text` and resolved via `references`.\n */\n onAdd: (\n text: string,\n fingerprints: ElementFingerprint[],\n references?: Record<string, ElementFingerprint>,\n mentions?: string[],\n attachments?: Attachment[],\n ) => void;\n onDelete: (id: string) => void;\n onAddReply: (commentId: string, text: string) => void;\n onDeleteReply: (commentId: string, replyId: string) => void;\n /** Upload a file via the active storage adapter; returns the resolved Attachment. */\n onUploadAttachment: (file: File) => Promise<Attachment>;\n /** True iff the active storage adapter supports uploads (popover hides the UI otherwise). */\n canUploadAttachments: () => boolean;\n onClose: () => void;\n onStartReferencePick: (\n onPicked: (fp: ElementFingerprint) => void,\n onCancel: () => void,\n ) => void;\n onJumpToReference: (commentId: string, refId: string) => void;\n onAnchorsChanged: (fingerprints: ElementFingerprint[]) => void;\n /**\n * Fetch the workspace's mentionable users for the @ picker dropdown.\n * Called every time the user opens the picker — keeps the list fresh\n * if members were added/removed in another tab. Returns [] for hosts\n * that don't support mentions (local/memory storage adapters).\n */\n onMentionSearch?: () => Promise<MentionableUser[]>;\n}\n\nexport class Popover {\n private el: HTMLDivElement;\n private root: HTMLElement;\n private callbacks: PopoverCallbacks;\n private currentUser: VizuUser | null = null;\n\n /** All elements this in-progress comment is anchored to. First one positions the popover. */\n private anchorTargets: Element[] = [];\n private anchorFingerprints: ElementFingerprint[] = [];\n /** Refs the user inserted via # picker during this session, keyed by refId. */\n private pendingRefs: Record<string, ElementFingerprint> = {};\n /** Attachments uploaded during this session, persisted into `comment.attachments` on save. */\n private pendingAttachments: Attachment[] = [];\n /** Open mention picker element, if any. Held so the next openMentionPicker / close calls can tear it down. */\n private mentionPicker: HTMLDivElement | null = null;\n /** Dismiss handlers wired to document for the open picker; cleared in closeMentionPicker. */\n private mentionPickerDismiss: (() => void) | null = null;\n\n constructor(root: HTMLElement, callbacks: PopoverCallbacks) {\n this.root = root;\n this.callbacks = callbacks;\n this.el = document.createElement('div');\n this.el.className = 'vz-popover';\n this.el.style.display = 'none';\n this.el.addEventListener('click', this.handleClick);\n this.root.appendChild(this.el);\n }\n\n setUser(user: VizuUser | null) {\n this.currentUser = user;\n }\n\n /**\n * Open the popover anchored to one OR more elements. The popover positions\n * itself relative to `targets[0]` but commits with every fingerprint in `fingerprints`.\n */\n open(targets: Element[], fingerprints: ElementFingerprint[], existingComments: VizuComment[]) {\n this.anchorTargets = [...targets];\n this.anchorFingerprints = [...fingerprints];\n this.pendingRefs = {};\n this.render(existingComments);\n this.position();\n }\n\n /** Refresh the rendered comment list (called by host when comments change). */\n update(existingComments: VizuComment[]) {\n if (!this.anchorTargets.length) return;\n this.render(existingComments);\n this.position();\n }\n\n /** Add another anchor to the in-progress comment (Shift+Click flow). */\n addAnchor(fp: ElementFingerprint, target: Element | null): boolean {\n const key = fingerprintKey(fp);\n if (this.anchorFingerprints.some((x) => fingerprintKey(x) === key)) return false;\n this.anchorFingerprints.push(fp);\n if (target) this.anchorTargets.push(target);\n this.callbacks.onAnchorsChanged([...this.anchorFingerprints]);\n this.renderAnchorList();\n this.updateHeader();\n return true;\n }\n\n /** Remove an anchor by fingerprint key. Never removes the last anchor. */\n removeAnchor(key: string): boolean {\n if (this.anchorFingerprints.length <= 1) return false;\n const idx = this.anchorFingerprints.findIndex((x) => fingerprintKey(x) === key);\n if (idx < 0) return false;\n this.anchorFingerprints.splice(idx, 1);\n this.anchorTargets.splice(idx, 1);\n this.callbacks.onAnchorsChanged([...this.anchorFingerprints]);\n this.renderAnchorList();\n this.updateHeader();\n return true;\n }\n\n private updateHeader() {\n const header = this.el.querySelector('.vz-popover-header span:first-child');\n if (!header) return;\n header.textContent = this.anchorFingerprints.length > 1\n ? `Comment on ${this.anchorFingerprints.length} elements`\n : 'Comment';\n }\n\n /** Recompute viewport position; called by host on scroll/resize. */\n reposition() {\n if (!this.anchorTargets.length) return;\n this.position();\n }\n\n close() {\n this.closeMentionPicker();\n this.el.style.display = 'none';\n this.anchorTargets = [];\n this.anchorFingerprints = [];\n this.pendingRefs = {};\n }\n\n isOpen(): boolean {\n return this.anchorTargets.length > 0;\n }\n\n getAnchors(): ElementFingerprint[] {\n return [...this.anchorFingerprints];\n }\n\n getAnchorTargets(): Element[] {\n return [...this.anchorTargets];\n }\n\n private render(comments: VizuComment[]) {\n if (!this.anchorTargets.length) return;\n const primary = this.anchorTargets[0];\n const cls = Array.from(primary.classList)\n .filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-'))\n .slice(0, 2);\n const label =\n primary.tagName.toLowerCase() +\n (primary.id ? '#' + primary.id : '') +\n (cls.length ? '.' + cls.join('.') : '');\n const text = ((primary as HTMLElement).innerText || '').trim();\n const textPreview = text.slice(0, 60);\n\n const authoringLine = this.currentUser\n ? `<div class=\"vz-comment-author\">${avatarHtml(this.currentUser)} <span class=\"vz-comment-author-name\">${escapeHtml(this.currentUser.name)}</span> <span>· you</span></div>`\n : '';\n\n this.el.innerHTML = `\n <div class=\"vz-popover-header\">\n <span>${this.anchorFingerprints.length > 1 ? `Comment on ${this.anchorFingerprints.length} elements` : 'Comment'}</span>\n <button class=\"vz-popover-close\" data-vz=\"close\" aria-label=\"Close\">×</button>\n </div>\n <div class=\"vz-target-label\">${escapeHtml(label)}${textPreview ? ' · &ldquo;' + escapeHtml(textPreview) + (text.length > 60 ? '…' : '') + '&rdquo;' : ''}</div>\n <div class=\"vz-anchor-list-wrap\"></div>\n <div class=\"vz-comment-list\">\n ${\n comments.length === 0\n ? '<div class=\"vz-empty\">No comments yet.</div>'\n : comments\n .map(\n (c) => `\n <div class=\"vz-comment\" data-comment-id=\"${escapeHtml(c.id)}\">\n ${c.author ? `<div class=\"vz-comment-author\">${avatarHtml(c.author)} <span class=\"vz-comment-author-name\">${escapeHtml(c.author.name || c.author.id || 'User')}</span></div>` : ''}\n <div>${renderCommentText(c.text, c.id)}</div>\n ${renderAttachmentsHtml(c.attachments)}\n <div class=\"vz-comment-meta\">\n <span>${escapeHtml(formatTime(c.createdAt))}</span>\n <span class=\"vz-comment-actions\">\n <button class=\"vz-comment-reply-toggle\" data-vz=\"toggle-reply\" data-id=\"${escapeHtml(c.id)}\">${(c.replies?.length ?? 0) > 0 ? `${c.replies!.length} ${c.replies!.length === 1 ? 'reply' : 'replies'}` : 'reply'}</button>\n <button class=\"vz-comment-delete\" data-vz=\"delete\" data-id=\"${escapeHtml(c.id)}\">delete</button>\n </span>\n </div>\n ${renderRepliesHtml(c)}\n </div>\n `,\n )\n .join('')\n }\n </div>\n ${authoringLine}\n <div class=\"vz-editor-wrap\">\n <div class=\"vz-textarea\" contenteditable=\"true\" data-placeholder=\"Leave a comment… # to reference. ⌘/Ctrl+Enter to save. Shift+Click another element to anchor to it too.\"></div>\n <div class=\"vz-attachment-drop-hint\" data-vz=\"drop-hint\" hidden>Drop image to attach</div>\n </div>\n <div class=\"vz-attachment-list\" data-vz=\"attachment-list\"></div>\n <div class=\"vz-popover-actions\">\n <button class=\"vz-btn vz-btn-ghost\" data-vz=\"close\">Close</button>\n ${this.callbacks.canUploadAttachments() ? `<button class=\"vz-btn vz-btn-ghost vz-btn-upload\" data-vz=\"upload\" title=\"Attach an image\">+ image</button>` : ''}\n ${this.currentUser ? `<button class=\"vz-btn vz-btn-ghost vz-btn-mention\" data-vz=\"mention\" title=\"Mention a teammate\">@ mention</button>` : ''}\n <button class=\"vz-btn vz-btn-primary\" data-vz=\"save\">Save</button>\n </div>\n <input type=\"file\" class=\"vz-file-input\" data-vz=\"file-input\" accept=\"image/png,image/jpeg,image/webp,image/gif,image/svg+xml\" hidden />\n `;\n this.el.style.display = 'flex';\n this.renderAnchorList();\n this.renderAttachmentList();\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement;\n editor.focus();\n editor.addEventListener('keydown', this.handleEditorKey);\n editor.addEventListener('paste', this.handleEditorPaste);\n\n // Drag-and-drop for image attachments. We attach to the wrap so the\n // hint can cover the editor without intercepting type-events.\n const wrap = this.el.querySelector('.vz-editor-wrap') as HTMLElement | null;\n if (wrap && this.callbacks.canUploadAttachments()) {\n wrap.addEventListener('dragover', this.handleDragOver);\n wrap.addEventListener('dragleave', this.handleDragLeave);\n wrap.addEventListener('drop', this.handleDrop);\n }\n\n const fileInput = this.el.querySelector('[data-vz=\"file-input\"]') as HTMLInputElement | null;\n if (fileInput) {\n fileInput.addEventListener('change', this.handleFileChange);\n }\n }\n\n private renderAnchorList() {\n const wrap = this.el.querySelector('.vz-anchor-list-wrap');\n if (!wrap) return;\n if (this.anchorFingerprints.length <= 1) {\n wrap.innerHTML = '';\n return;\n }\n const chips = this.anchorFingerprints\n .map((fp) => {\n const key = fingerprintKey(fp);\n const label = fingerprintLabel(fp);\n return `<span class=\"vz-anchor-chip\" data-fp-key=\"${escapeHtml(key)}\">${escapeHtml(label)}<button class=\"vz-anchor-chip-remove\" data-vz=\"remove-anchor\" data-fp-key=\"${escapeHtml(key)}\" aria-label=\"Remove anchor\">×</button></span>`;\n })\n .join('');\n wrap.innerHTML = `\n <div class=\"vz-anchor-hint\">Anchored to ${this.anchorFingerprints.length} elements:</div>\n <div class=\"vz-anchor-list\">${chips}</div>\n `;\n }\n\n private handleEditorKey = (e: KeyboardEvent) => {\n const editor = e.currentTarget as HTMLElement;\n if (e.key === '#') {\n e.preventDefault();\n const savedRange = this.saveRange();\n this.callbacks.onStartReferencePick(\n (fp) => this.insertChip(fp, savedRange),\n () => {\n editor.focus();\n this.restoreRange(savedRange);\n },\n );\n return;\n }\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n this.save();\n return;\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n this.callbacks.onClose();\n }\n };\n\n private handleEditorPaste = (e: ClipboardEvent) => {\n // If clipboard carries image data and uploads are enabled, swallow\n // the paste and upload the image instead of inserting any text/binary.\n if (this.callbacks.canUploadAttachments()) {\n const items = e.clipboardData?.items ?? null;\n if (items) {\n const imageFiles: File[] = [];\n for (let i = 0; i < items.length; i++) {\n const it = items[i];\n if (it.kind === 'file' && it.type.startsWith('image/')) {\n const f = it.getAsFile();\n if (f) imageFiles.push(f);\n }\n }\n if (imageFiles.length > 0) {\n e.preventDefault();\n for (const f of imageFiles) void this.uploadAndAppend(f);\n return;\n }\n }\n }\n\n e.preventDefault();\n const text = e.clipboardData?.getData('text/plain') ?? '';\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n const r = sel.getRangeAt(0);\n r.deleteContents();\n r.insertNode(document.createTextNode(text));\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n }\n };\n\n /* ─── Attachment handlers ────────────────────────────────────────── */\n\n private handleDragOver = (e: DragEvent) => {\n if (!e.dataTransfer || !Array.from(e.dataTransfer.types).includes('Files')) return;\n e.preventDefault();\n const hint = this.el.querySelector('[data-vz=\"drop-hint\"]') as HTMLElement | null;\n hint?.removeAttribute('hidden');\n };\n private handleDragLeave = (e: DragEvent) => {\n // Only hide when leaving the wrap entirely (relatedTarget outside the wrap).\n const wrap = e.currentTarget as HTMLElement;\n if (e.relatedTarget instanceof Node && wrap.contains(e.relatedTarget)) return;\n const hint = this.el.querySelector('[data-vz=\"drop-hint\"]') as HTMLElement | null;\n hint?.setAttribute('hidden', '');\n };\n private handleDrop = (e: DragEvent) => {\n e.preventDefault();\n const hint = this.el.querySelector('[data-vz=\"drop-hint\"]') as HTMLElement | null;\n hint?.setAttribute('hidden', '');\n if (!e.dataTransfer) return;\n const files = Array.from(e.dataTransfer.files).filter((f) => f.type.startsWith('image/'));\n for (const f of files) void this.uploadAndAppend(f);\n };\n private handleFileChange = (e: Event) => {\n const input = e.currentTarget as HTMLInputElement;\n const files = input.files ? Array.from(input.files) : [];\n for (const f of files) void this.uploadAndAppend(f);\n input.value = ''; // allow re-selecting the same file\n };\n\n private async uploadAndAppend(file: File): Promise<void> {\n if (!this.callbacks.canUploadAttachments()) return;\n // Show an optimistic placeholder while the upload runs. We keep a\n // tracking entry on `pendingAttachments` so callers can see it.\n const placeholderId = 'upload-' + Math.random().toString(36).slice(2, 9);\n const placeholder: Attachment = {\n id: placeholderId,\n url: '',\n mimeType: file.type || 'application/octet-stream',\n sizeBytes: file.size,\n uploadedAt: Date.now(),\n filename: file.name,\n };\n this.pendingAttachments.push(placeholder);\n this.renderAttachmentList(placeholderId);\n\n try {\n const att = await this.callbacks.onUploadAttachment(file);\n const idx = this.pendingAttachments.findIndex((a) => a.id === placeholderId);\n if (idx >= 0) this.pendingAttachments[idx] = att;\n else this.pendingAttachments.push(att);\n this.renderAttachmentList();\n } catch (err) {\n // Surface the failure inline and drop the placeholder.\n this.pendingAttachments = this.pendingAttachments.filter((a) => a.id !== placeholderId);\n this.renderAttachmentList();\n if (typeof console !== 'undefined') {\n console.warn('[vizu] attachment upload failed', err);\n }\n }\n }\n\n private renderAttachmentList(uploadingId?: string) {\n const list = this.el.querySelector('[data-vz=\"attachment-list\"]') as HTMLElement | null;\n if (!list) return;\n if (this.pendingAttachments.length === 0) {\n list.innerHTML = '';\n return;\n }\n list.innerHTML = this.pendingAttachments\n .map((a) => {\n const isUploading = a.id === uploadingId || a.url === '';\n const preview = isUploading\n ? `<div class=\"vz-attachment-thumb vz-attachment-thumb-loading\">…</div>`\n : a.mimeType.startsWith('image/')\n ? `<img class=\"vz-attachment-thumb\" src=\"${escapeHtml(a.url)}\" alt=\"${escapeHtml(a.filename ?? '')}\" />`\n : `<div class=\"vz-attachment-thumb\">📄</div>`;\n return `\n <div class=\"vz-attachment-item\" data-attachment-id=\"${escapeHtml(a.id)}\">\n ${preview}\n <button class=\"vz-attachment-remove\" data-vz=\"remove-attachment\" data-attachment-id=\"${escapeHtml(a.id)}\" aria-label=\"Remove attachment\">×</button>\n </div>\n `;\n })\n .join('');\n }\n\n private saveRange(): Range | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n return sel.getRangeAt(0).cloneRange();\n }\n\n private restoreRange(r: Range | null) {\n if (!r) return;\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(r);\n }\n\n private insertChip(fp: ElementFingerprint, range: Range | null) {\n const rid = makeRefId();\n this.pendingRefs[rid] = fp;\n const label = fingerprintLabel(fp);\n const chip = document.createElement('span');\n chip.className = 'vz-chip';\n chip.contentEditable = 'false';\n chip.setAttribute('data-ref-id', rid);\n chip.textContent = label;\n\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement;\n editor.focus();\n this.restoreRange(range);\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) {\n editor.appendChild(chip);\n editor.appendChild(document.createTextNode(' '));\n this.moveCursorToEnd(editor);\n return;\n }\n const r = sel.getRangeAt(0);\n r.deleteContents();\n r.insertNode(chip);\n // Trailing space so the cursor doesn't get stuck inside chip's neighbour boundary\n const space = document.createTextNode(' ');\n chip.after(space);\n const newRange = document.createRange();\n newRange.setStartAfter(space);\n newRange.collapse(true);\n sel.removeAllRanges();\n sel.addRange(newRange);\n }\n\n private moveCursorToEnd(el: HTMLElement) {\n const range = document.createRange();\n range.selectNodeContents(el);\n range.collapse(false);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n\n /** Walk the editor and produce the markdown-ish string with ref tokens. */\n private serializeEditor(editor: HTMLElement): string {\n const parts: string[] = [];\n const walk = (node: Node) => {\n if (node.nodeType === Node.TEXT_NODE) {\n parts.push(node.textContent || '');\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n const el = node as HTMLElement;\n if (el.classList.contains('vz-chip')) {\n const mid = el.getAttribute('data-mention-id');\n if (mid) {\n // Mention chip — label includes the leading \"@\", strip for token.\n const raw = el.textContent || '';\n const name = raw.startsWith('@') ? raw.slice(1) : raw;\n parts.push(`[@${name}](vizu-mention:${mid})`);\n return;\n }\n const rid = el.getAttribute('data-ref-id');\n const label = el.textContent || '';\n if (rid) parts.push(`[#${label}](vizu-ref:${rid})`);\n return;\n }\n if (el.tagName === 'BR') {\n parts.push('\\n');\n return;\n }\n if (el.tagName === 'DIV' && parts.length && !parts[parts.length - 1].endsWith('\\n')) {\n // <div> often produced by browsers when user presses Enter inside contenteditable\n parts.push('\\n');\n }\n for (const child of el.childNodes) walk(child);\n };\n for (const child of editor.childNodes) walk(child);\n return parts.join('').trim();\n }\n\n private handleClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n // Anchor chip remove\n const removeBtn = target.closest('[data-vz=\"remove-anchor\"]') as HTMLElement | null;\n if (removeBtn) {\n e.stopPropagation();\n const key = removeBtn.getAttribute('data-fp-key');\n if (key) this.removeAnchor(key);\n return;\n }\n // Inline reference chip click → jump\n const refEl = target.closest('[data-vz=\"ref\"]') as HTMLElement | null;\n if (refEl) {\n const commentId = refEl.getAttribute('data-comment-id');\n const refIdAttr = refEl.getAttribute('data-ref-id');\n if (commentId && refIdAttr) {\n e.stopPropagation();\n this.callbacks.onJumpToReference(commentId, refIdAttr);\n }\n return;\n }\n const action = target.getAttribute('data-vz');\n if (action === 'close') this.callbacks.onClose();\n else if (action === 'save') this.save();\n else if (action === 'mention') {\n e.preventDefault();\n this.openMentionPicker(target as HTMLElement);\n } else if (action === 'upload') {\n e.preventDefault();\n const input = this.el.querySelector('[data-vz=\"file-input\"]') as HTMLInputElement | null;\n input?.click();\n } else if (action === 'remove-attachment') {\n e.preventDefault();\n const id = target.getAttribute('data-attachment-id');\n if (id) {\n this.pendingAttachments = this.pendingAttachments.filter((a) => a.id !== id);\n this.renderAttachmentList();\n }\n } else if (action === 'delete') {\n const id = target.getAttribute('data-id');\n if (id) this.callbacks.onDelete(id);\n } else if (action === 'toggle-reply') {\n e.preventDefault();\n const id = target.getAttribute('data-id');\n if (!id) return;\n const block = this.el.querySelector(`.vz-replies[data-comment-id=\"${cssEscape(id)}\"]`) as HTMLElement | null;\n if (!block) return;\n const isHidden = block.hasAttribute('hidden');\n if (isHidden) {\n block.removeAttribute('hidden');\n const input = block.querySelector('.vz-reply-input') as HTMLTextAreaElement | null;\n input?.focus();\n } else {\n block.setAttribute('hidden', '');\n }\n } else if (action === 'send-reply') {\n e.preventDefault();\n const id = target.getAttribute('data-id');\n if (!id) return;\n const input = this.el.querySelector(\n `.vz-reply-input[data-comment-id=\"${cssEscape(id)}\"]`,\n ) as HTMLTextAreaElement | null;\n const text = input?.value.trim() ?? '';\n if (!text) return;\n this.callbacks.onAddReply(id, text);\n if (input) input.value = '';\n } else if (action === 'delete-reply') {\n e.preventDefault();\n const commentId = target.getAttribute('data-comment-id');\n const replyId = target.getAttribute('data-reply-id');\n if (commentId && replyId) this.callbacks.onDeleteReply(commentId, replyId);\n }\n };\n\n /**\n * Open a dropdown listing the workspace's mentionable users. The user\n * clicks one to insert a mention chip for them; Esc or an outside\n * click closes the dropdown.\n *\n * The dropdown is fetched fresh on every open so member adds/removes\n * in another tab are picked up without a popover reopen. Falls back\n * to silent no-op if `onMentionSearch` isn't wired (non-cloud\n * adapters) or returns an empty list.\n */\n private async openMentionPicker(anchor: HTMLElement) {\n this.closeMentionPicker();\n if (!this.callbacks.onMentionSearch) return;\n\n // Anchor the picker just below the mention button. Render an\n // empty shell immediately so the user sees feedback, then fill it\n // once the fetch resolves. Position relative to the popover's\n // bounding box so we live inside its shadow-cast region.\n const picker = document.createElement('div');\n picker.className = 'vz-mention-picker';\n picker.setAttribute('data-vz-ignore', '');\n picker.innerHTML = `<div class=\"vz-mention-loading\">Loading…</div>`;\n this.el.appendChild(picker);\n this.mentionPicker = picker;\n this.positionMentionPicker(picker, anchor);\n\n let users: MentionableUser[] = [];\n try {\n users = await this.callbacks.onMentionSearch();\n } catch {\n /* swallow — render empty state below */\n }\n if (this.mentionPicker !== picker) return; // closed during fetch\n\n if (users.length === 0) {\n picker.innerHTML = `<div class=\"vz-mention-empty\">No teammates to mention yet.</div>`;\n this.positionMentionPicker(picker, anchor);\n this.wireMentionPickerDismiss();\n return;\n }\n\n const itemsHtml = users\n .map((u) => {\n const avatar = u.avatarUrl\n ? `<img class=\"vz-mention-avatar\" src=\"${escapeHtml(u.avatarUrl)}\" alt=\"\" />`\n : `<span class=\"vz-mention-avatar vz-mention-avatar-initials\">${escapeHtml(initials(u.name) || '?')}</span>`;\n return `<button class=\"vz-mention-item\" type=\"button\" data-mention-id=\"${escapeHtml(u.id)}\" data-mention-name=\"${escapeHtml(u.name)}\">${avatar}<span class=\"vz-mention-item-name\">${escapeHtml(u.name)}</span></button>`;\n })\n .join('');\n picker.innerHTML = itemsHtml;\n this.positionMentionPicker(picker, anchor);\n\n picker.addEventListener('click', (e) => {\n const btn = (e.target as Element | null)?.closest<HTMLElement>('.vz-mention-item');\n if (!btn) return;\n e.preventDefault();\n e.stopPropagation();\n const id = btn.getAttribute('data-mention-id') || '';\n const name = btn.getAttribute('data-mention-name') || '';\n if (id && name) this.insertMentionChip({ id, name });\n this.closeMentionPicker();\n });\n\n this.wireMentionPickerDismiss();\n }\n\n /** Wires Esc + outside-click handlers to close the open picker. */\n private wireMentionPickerDismiss() {\n if (this.mentionPickerDismiss) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n this.closeMentionPicker();\n }\n };\n const onOutside = (e: MouseEvent) => {\n if (!this.mentionPicker) return;\n if (this.mentionPicker.contains(e.target as Node)) return;\n this.closeMentionPicker();\n };\n document.addEventListener('keydown', onKey, true);\n document.addEventListener('mousedown', onOutside, true);\n this.mentionPickerDismiss = () => {\n document.removeEventListener('keydown', onKey, true);\n document.removeEventListener('mousedown', onOutside, true);\n };\n }\n\n private closeMentionPicker() {\n if (this.mentionPickerDismiss) {\n this.mentionPickerDismiss();\n this.mentionPickerDismiss = null;\n }\n if (this.mentionPicker) {\n this.mentionPicker.remove();\n this.mentionPicker = null;\n }\n }\n\n /**\n * Place the picker below the anchor button. Stays within the\n * popover's right edge so we don't overflow the popover frame.\n */\n private positionMentionPicker(picker: HTMLDivElement, anchor: HTMLElement) {\n const popRect = this.el.getBoundingClientRect();\n const btnRect = anchor.getBoundingClientRect();\n const left = btnRect.left - popRect.left;\n const top = btnRect.bottom - popRect.top + 4;\n picker.style.left = `${left}px`;\n picker.style.top = `${top}px`;\n }\n\n /**\n * Insert a mention chip for the given user into the editor at the\n * current caret position. Accepts any user-like shape with a required\n * `id` + `name` — covers both the current VizuUser (for self-mention)\n * and MentionableUser (from the workspace member picker).\n */\n private insertMentionChip(user: { id?: string; name: string } | null) {\n if (!user || !user.id) return;\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement | null;\n if (!editor) return;\n const chip = document.createElement('span');\n chip.className = 'vz-chip vz-chip-mention';\n chip.contentEditable = 'false';\n chip.setAttribute('data-mention-id', user.id);\n chip.textContent = '@' + user.name;\n\n editor.focus();\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) {\n editor.appendChild(chip);\n editor.appendChild(document.createTextNode(' '));\n this.moveCursorToEnd(editor);\n return;\n }\n const r = sel.getRangeAt(0);\n r.deleteContents();\n r.insertNode(chip);\n const space = document.createTextNode(' ');\n chip.after(space);\n const newRange = document.createRange();\n newRange.setStartAfter(space);\n newRange.collapse(true);\n sel.removeAllRanges();\n sel.addRange(newRange);\n }\n\n private save() {\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement | null;\n if (!editor) return;\n const text = this.serializeEditor(editor);\n // Allow attachment-only comments: only block save when there's no text\n // AND no finished attachment uploads.\n const finishedCount = this.pendingAttachments.filter((a) => a.url).length;\n if (!text && finishedCount === 0) return;\n const usedRefs: Record<string, ElementFingerprint> = {};\n const re = /\\[#[^\\]]+\\]\\(vizu-ref:([a-zA-Z0-9_-]+)\\)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n const rid = m[1];\n if (this.pendingRefs[rid]) usedRefs[rid] = this.pendingRefs[rid];\n }\n const mentions = extractMentions(text);\n // Only include attachments that finished uploading (have a real URL).\n const finishedAttachments = this.pendingAttachments.filter((a) => a.url);\n this.callbacks.onAdd(\n text,\n [...this.anchorFingerprints],\n Object.keys(usedRefs).length ? usedRefs : undefined,\n mentions.length ? mentions : undefined,\n finishedAttachments.length ? finishedAttachments : undefined,\n );\n editor.innerHTML = '';\n this.pendingRefs = {};\n this.pendingAttachments = [];\n this.renderAttachmentList();\n }\n\n private position() {\n const primary = this.anchorTargets[0];\n if (!primary) return;\n const rect = primary.getBoundingClientRect();\n const popRect = this.el.getBoundingClientRect();\n const pad = 8;\n let top = rect.bottom + pad;\n let left = rect.left;\n if (top + popRect.height > window.innerHeight - pad) {\n top = rect.top - popRect.height - pad;\n if (top < pad) top = pad;\n }\n if (left + popRect.width > window.innerWidth - pad) {\n left = window.innerWidth - popRect.width - pad;\n }\n if (left < pad) left = pad;\n this.el.style.top = top + 'px';\n this.el.style.left = left + 'px';\n }\n\n destroy() {\n this.closeMentionPicker();\n this.el.removeEventListener('click', this.handleClick);\n this.el.remove();\n }\n}\n","import type { VizuAction, VizuComment, VizuUser } from './types';\nimport { escapeHtml, avatarHtml } from './util';\n\nexport interface PillCallbacks {\n onToggleSidebar: () => void;\n onDisable: () => void;\n onAction: (id: string) => void;\n onToggleMultiSelect: () => void;\n onCommitSelection: () => void;\n onClearSelection: () => void;\n}\n\nexport class Pill {\n private el: HTMLDivElement;\n private root: HTMLElement;\n private callbacks: PillCallbacks;\n private sidebarOpen = false;\n private actions: VizuAction[] = [];\n private commentsCount = 0;\n private user: VizuUser | null = null;\n private multiSelectMode = false;\n private selectionCount = 0;\n\n constructor(root: HTMLElement, callbacks: PillCallbacks) {\n this.root = root;\n this.callbacks = callbacks;\n this.el = document.createElement('div');\n this.el.className = 'vz-pill';\n this.el.style.display = 'none';\n this.el.addEventListener('click', this.handleClick);\n this.root.appendChild(this.el);\n }\n\n setActions(actions: VizuAction[]) {\n this.actions = actions;\n this.render();\n }\n\n setComments(comments: VizuComment[]) {\n this.commentsCount = comments.length;\n this.render();\n }\n\n setUser(user: VizuUser | null) {\n this.user = user;\n this.render();\n }\n\n setSidebarOpen(open: boolean) {\n this.sidebarOpen = open;\n const btn = this.el.querySelector<HTMLElement>('[data-vz=\"sidebar\"]');\n if (btn) btn.classList.toggle('is-active', open);\n }\n\n setMultiSelectState(mode: boolean, selectionCount: number) {\n this.multiSelectMode = mode;\n this.selectionCount = selectionCount;\n this.el.classList.toggle('is-selecting', mode && selectionCount > 0);\n this.render();\n }\n\n show() {\n this.el.style.display = 'flex';\n this.render();\n }\n\n hide() {\n this.el.style.display = 'none';\n }\n\n private render() {\n if (this.el.style.display === 'none') return;\n const visibleActions = this.actions.filter((a) =>\n a.visibleWhen ? a.visibleWhen({ commentsCount: this.commentsCount }) : true,\n );\n const userHtml = this.user\n ? `<div class=\"vz-pill-user\" title=\"${escapeHtml(this.user.name)}\">${avatarHtml(this.user, 'vz-pill-user-avatar')} ${escapeHtml(this.user.name.split(' ')[0]!)}</div><span class=\"vz-pill-divider\"></span>`\n : '';\n const actionsHtml = visibleActions.length\n ? visibleActions\n .map(\n (a) =>\n `<button class=\"vz-pill-btn${a.variant === 'primary' ? ' primary' : ''}\" data-vz=\"action\" data-id=\"${escapeHtml(a.id)}\"${a.title ? ` title=\"${escapeHtml(a.title)}\"` : ''}>${escapeHtml(a.label)}</button>`,\n )\n .join('') + '<span class=\"vz-pill-divider\"></span>'\n : '';\n\n // Multi-select state overrides the \"N comments\" badge area with selection + commit\n let middle = '';\n if (this.multiSelectMode && this.selectionCount > 0) {\n middle = `\n <span class=\"vz-pill-count-btn is-active\" title=\"Selected ${this.selectionCount} element${this.selectionCount === 1 ? '' : 's'}\">${this.selectionCount} selected</span>\n <button class=\"vz-pill-btn primary\" data-vz=\"commit-selection\">Comment on ${this.selectionCount} →</button>\n <button class=\"vz-pill-btn\" data-vz=\"clear-selection\">Cancel</button>\n <span class=\"vz-pill-divider\"></span>\n `;\n } else {\n const count = this.commentsCount;\n middle = `\n <button class=\"vz-pill-count-btn ${this.sidebarOpen ? 'is-active' : ''}\" data-vz=\"sidebar\" title=\"Show all comments\">${count} comment${count === 1 ? '' : 's'}</button>\n <span class=\"vz-pill-divider\"></span>\n ${userHtml}\n ${actionsHtml}\n `;\n }\n\n const toggleTitle = this.multiSelectMode\n ? 'Exit multi-select mode'\n : 'Multi-select: click multiple elements, then comment on the group (or hold Shift while clicking)';\n\n this.el.innerHTML = `\n <span class=\"vz-pill-dot\" aria-hidden=\"true\"></span>\n <span class=\"vz-pill-label\">Vizu</span>\n ${middle}\n <button class=\"vz-pill-toggle ${this.multiSelectMode ? 'is-active' : ''}\" data-vz=\"multi\" title=\"${escapeHtml(toggleTitle)}\" aria-label=\"${escapeHtml(toggleTitle)}\">\n <svg viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\"><rect x=\"1\" y=\"1\" width=\"5\" height=\"5\"/><rect x=\"8\" y=\"1\" width=\"5\" height=\"5\"/><rect x=\"1\" y=\"8\" width=\"5\" height=\"5\"/><rect x=\"8\" y=\"8\" width=\"5\" height=\"5\"/></svg>\n </button>\n <button class=\"vz-pill-icon-btn\" data-vz=\"disable\" title=\"Disable Vizu (toggle with shortcut)\" aria-label=\"Disable\">×</button>\n `;\n }\n\n private handleClick = (e: MouseEvent) => {\n const btn = (e.target as HTMLElement).closest('[data-vz]') as HTMLElement | null;\n if (!btn) return;\n const a = btn.getAttribute('data-vz');\n if (a === 'sidebar') this.callbacks.onToggleSidebar();\n else if (a === 'disable') this.callbacks.onDisable();\n else if (a === 'multi') this.callbacks.onToggleMultiSelect();\n else if (a === 'commit-selection') this.callbacks.onCommitSelection();\n else if (a === 'clear-selection') this.callbacks.onClearSelection();\n else if (a === 'action') {\n const id = btn.getAttribute('data-id');\n if (id) this.callbacks.onAction(id);\n }\n };\n\n toast(message: string) {\n const t = document.createElement('div');\n t.className = 'vz-toast';\n t.textContent = message;\n this.root.appendChild(t);\n setTimeout(() => t.remove(), 1800);\n }\n\n destroy() {\n this.el.removeEventListener('click', this.handleClick);\n this.el.remove();\n }\n}\n","import type { VizuComment, ElementFingerprint, CommentStatus, MatchConfidence } from './types';\nimport { escapeHtml, formatTime, avatarHtml, renderCommentText, renderAttachmentsHtml } from './util';\nimport { fingerprintKey, fingerprintLabel } from './fingerprint';\n\nexport interface SidebarCallbacks {\n onJumpTo: (fp: ElementFingerprint) => void;\n onJumpToReference: (commentId: string, refId: string) => void;\n onDelete: (id: string) => void;\n onUpdateStatus: (id: string, status: CommentStatus) => void;\n onClose: () => void;\n}\n\ninterface Group {\n fp: ElementFingerprint;\n comments: VizuComment[];\n}\n\ntype Filter = 'open' | 'all';\n\nexport class Sidebar {\n private el: HTMLDivElement;\n private root: HTMLElement;\n private callbacks: SidebarCallbacks;\n private open = false;\n private filter: Filter = 'open';\n private lastComments: VizuComment[] = [];\n private lastConfidence = new Map<string, MatchConfidence>();\n\n constructor(root: HTMLElement, callbacks: SidebarCallbacks) {\n this.root = root;\n this.callbacks = callbacks;\n this.el = document.createElement('div');\n this.el.className = 'vz-sidebar';\n this.el.addEventListener('click', this.handleClick);\n this.root.appendChild(this.el);\n }\n\n toggle(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n if (this.open) this.close();\n else this.show(comments, confidence);\n }\n show(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n this.render(comments, confidence);\n requestAnimationFrame(() => this.el.classList.add('is-open'));\n this.open = true;\n }\n update(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n if (this.open) this.render(comments, confidence);\n }\n close() { this.el.classList.remove('is-open'); this.open = false; }\n isOpen(): boolean { return this.open; }\n\n private render(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n this.lastComments = comments;\n if (confidence) this.lastConfidence = new Map(confidence);\n const conf = this.lastConfidence;\n\n // Partition into active (exact + likely + drifted) vs orphaned. Active\n // are grouped by the element they anchor to; orphaned get their own\n // section at the bottom with the fingerprint text snippet as label.\n const orphans = comments.filter((c) => conf.get(c.id) === 'orphaned');\n const anchored = comments.filter((c) => conf.get(c.id) !== 'orphaned');\n\n // Apply status filter to the anchored set only. Orphans always show\n // when 'All' is active (they're a separate problem); hidden under 'Open'.\n const visible = this.filter === 'open'\n ? anchored.filter((c) => (c.status ?? 'open') === 'open')\n : anchored;\n const visibleOrphans = this.filter === 'open' ? [] : orphans;\n\n const counts = countByStatus(comments);\n\n if (visible.length === 0 && visibleOrphans.length === 0) {\n const emptyCopy = this.filter === 'open' && comments.length > 0\n ? `<strong>Nothing open.</strong>${counts.resolved + counts.wontfix} closed comment${counts.resolved + counts.wontfix === 1 ? '' : 's'} hidden. Switch to <em>All</em> to see them.`\n : '<strong>No comments yet</strong>Click any element on the page to leave one.';\n this.el.innerHTML = `\n ${this.headerHtml(comments.length, counts)}\n <div class=\"vz-sidebar-body\">\n <div class=\"vz-sidebar-empty\">${emptyCopy}</div>\n </div>\n `;\n return;\n }\n // Group by PRIMARY anchor (first fingerprint). Other anchors of a multi-anchor\n // comment are shown as chips inside the item.\n const groups = new Map<string, Group>();\n for (const c of visible) {\n const fps = c.fingerprints || [];\n const primary = fps[0];\n if (!primary) continue;\n const key = fingerprintKey(primary);\n const g = groups.get(key);\n if (g) g.comments.push(c);\n else groups.set(key, { fp: primary, comments: [c] });\n }\n\n const groupHtml: string[] = [];\n let gi = 0;\n for (const g of groups.values()) {\n const elLabel = g.fp.tagName.toLowerCase() + (g.fp.attributes.id ? '#' + g.fp.attributes.id : '');\n const textPreview = g.fp.textSnippet.slice(0, 60);\n groupHtml.push(`\n <div class=\"vz-sidebar-group\" data-group=\"${gi}\">\n <div class=\"vz-sidebar-group-head\">\n ${escapeHtml(elLabel)}\n ${textPreview ? `<div class=\"vz-sidebar-group-text\">&ldquo;${escapeHtml(textPreview)}${g.fp.textSnippet.length > 60 ? '…' : ''}&rdquo;</div>` : ''}\n </div>\n ${g.comments\n .map((c) => {\n const status = c.status ?? 'open';\n const others = (c.fingerprints || []).slice(1);\n const otherChips = others.length\n ? `<div class=\"vz-anchor-list\" style=\"margin-top:6px\">${others\n .map(\n (fp, i) => `<span class=\"vz-anchor-chip\" data-vz=\"jump-fp\" data-fp-key=\"${escapeHtml(fingerprintKey(fp))}\" title=\"Jump to this element\">+${i + 2}/${(c.fingerprints || []).length} ${escapeHtml(fingerprintLabel(fp))}</span>`,\n )\n .join('')}</div>`\n : '';\n const cConf = conf.get(c.id);\n const driftedBadge = cConf === 'drifted'\n ? `<span class=\"vz-status-pill vz-status-drifted\" title=\"Anchored via a fallback match — the original element may have moved or changed.\">drifted</span>`\n : '';\n return `\n <div class=\"vz-sidebar-item ${status !== 'open' ? 'vz-sidebar-item-dim' : ''}\" data-comment-id=\"${escapeHtml(c.id)}\" data-vz=\"jump\" data-group=\"${gi}\" role=\"button\" tabindex=\"0\">\n <div class=\"vz-sidebar-item-row\">\n ${c.author ? `<div class=\"vz-sidebar-item-author\">${avatarHtml(c.author)} <span class=\"vz-sidebar-item-author-name\">${escapeHtml(c.author.name)}</span></div>` : ''}\n ${driftedBadge}\n ${status !== 'open' ? `<span class=\"vz-status-pill vz-status-${status}\">${status}</span>` : ''}\n </div>\n <span class=\"vz-sidebar-item-text\">${renderCommentText(c.text, c.id)}</span>\n ${renderAttachmentsHtml(c.attachments)}\n ${otherChips}\n <div class=\"vz-sidebar-item-meta\">\n <span>${escapeHtml(formatTime(c.createdAt))}${(c.fingerprints || []).length > 1 ? ` · grouped (${(c.fingerprints || []).length})` : ''}${(c.replies?.length ?? 0) > 0 ? ` · ${c.replies!.length} ${c.replies!.length === 1 ? 'reply' : 'replies'}` : ''}</span>\n <span class=\"vz-sidebar-item-actions\">\n ${statusActionsHtml(c.id, status)}\n <button class=\"vz-sidebar-item-delete\" data-vz=\"delete\" data-id=\"${escapeHtml(c.id)}\" title=\"Delete comment\">delete</button>\n </span>\n </div>\n </div>\n `;\n })\n .join('')}\n </div>\n `);\n gi++;\n }\n const orphanHtml = visibleOrphans.length > 0 ? renderOrphansHtml(visibleOrphans) : '';\n this.el.innerHTML = `\n ${this.headerHtml(comments.length, counts)}\n <div class=\"vz-sidebar-body\">\n ${groupHtml.join('')}\n ${orphanHtml}\n </div>\n `;\n // Stash group payload + per-fp lookup for anchor-chip jumps\n const items = this.el.querySelectorAll('[data-vz=\"jump\"]');\n const groupArr = [...groups.values()];\n for (const item of items) {\n const groupIndex = Number((item as HTMLElement).dataset.group);\n (item as any).__group = groupArr[groupIndex];\n }\n const chips = this.el.querySelectorAll('[data-vz=\"jump-fp\"]');\n for (const chip of chips) {\n const key = chip.getAttribute('data-fp-key');\n const fp = findFingerprintByKey(visible, key || '');\n (chip as any).__fp = fp;\n }\n }\n\n private headerHtml(total: number, counts: { open: number; resolved: number; wontfix: number }): string {\n return `\n <div class=\"vz-sidebar-header\">\n <div class=\"vz-sidebar-title\">\n Comments\n <span class=\"vz-sidebar-title-count\">${total}</span>\n </div>\n <button class=\"vz-sidebar-close\" data-vz=\"close\" aria-label=\"Close sidebar\">×</button>\n </div>\n <div class=\"vz-sidebar-filters\" role=\"tablist\" aria-label=\"Status filter\">\n <button class=\"vz-sidebar-filter ${this.filter === 'open' ? 'is-active' : ''}\" data-vz=\"filter\" data-filter=\"open\" role=\"tab\" aria-selected=\"${this.filter === 'open'}\">\n Open <span class=\"vz-sidebar-filter-count\">${counts.open}</span>\n </button>\n <button class=\"vz-sidebar-filter ${this.filter === 'all' ? 'is-active' : ''}\" data-vz=\"filter\" data-filter=\"all\" role=\"tab\" aria-selected=\"${this.filter === 'all'}\">\n All <span class=\"vz-sidebar-filter-count\">${total}</span>\n </button>\n </div>\n `;\n }\n\n private handleClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n const refEl = target.closest('[data-vz=\"ref\"]') as HTMLElement | null;\n if (refEl) {\n e.stopPropagation();\n const commentId = refEl.getAttribute('data-comment-id');\n const refIdAttr = refEl.getAttribute('data-ref-id');\n if (commentId && refIdAttr) this.callbacks.onJumpToReference(commentId, refIdAttr);\n return;\n }\n const filterEl = target.closest('[data-vz=\"filter\"]') as HTMLElement | null;\n if (filterEl) {\n e.stopPropagation();\n const f = filterEl.getAttribute('data-filter') as Filter | null;\n if (f && f !== this.filter) {\n this.filter = f;\n this.render(this.lastComments);\n }\n return;\n }\n const statusEl = target.closest('[data-vz=\"status\"]') as HTMLElement | null;\n if (statusEl) {\n e.stopPropagation();\n const id = statusEl.getAttribute('data-id');\n const status = statusEl.getAttribute('data-status') as CommentStatus | null;\n if (id && status) this.callbacks.onUpdateStatus(id, status);\n return;\n }\n const deleteEl = target.closest('[data-vz=\"delete\"]') as HTMLElement | null;\n if (deleteEl) {\n e.stopPropagation();\n const id = deleteEl.getAttribute('data-id');\n if (id) this.callbacks.onDelete(id);\n return;\n }\n if (target.closest('[data-vz=\"close\"]')) {\n this.callbacks.onClose();\n return;\n }\n const fpChip = target.closest('[data-vz=\"jump-fp\"]') as any;\n if (fpChip && fpChip.__fp) {\n e.stopPropagation();\n this.callbacks.onJumpTo(fpChip.__fp);\n return;\n }\n const item = target.closest('[data-vz=\"jump\"]') as any;\n if (item && item.__group) this.callbacks.onJumpTo(item.__group.fp);\n };\n\n destroy() {\n this.el.removeEventListener('click', this.handleClick);\n this.el.remove();\n }\n}\n\n/* ─── helpers ──────────────────────────────────────────────────────────── */\n\nfunction countByStatus(comments: VizuComment[]): { open: number; resolved: number; wontfix: number } {\n const c = { open: 0, resolved: 0, wontfix: 0 };\n for (const x of comments) {\n const s = x.status ?? 'open';\n c[s] = (c[s] ?? 0) + 1;\n }\n return c;\n}\n\nfunction statusActionsHtml(id: string, current: CommentStatus): string {\n const safe = escapeHtml(id);\n if (current === 'open') {\n return `\n <button class=\"vz-sidebar-item-status\" data-vz=\"status\" data-id=\"${safe}\" data-status=\"resolved\" title=\"Mark resolved\">resolve</button>\n <button class=\"vz-sidebar-item-status\" data-vz=\"status\" data-id=\"${safe}\" data-status=\"wontfix\" title=\"Won't fix\">won&apos;t fix</button>\n `;\n }\n // For closed states, the only action is reopening.\n return `<button class=\"vz-sidebar-item-status\" data-vz=\"status\" data-id=\"${safe}\" data-status=\"open\" title=\"Reopen\">reopen</button>`;\n}\n\n/**\n * Render the orphaned-comments section. These are comments whose\n * fingerprints didn't resolve to any DOM element on the current page —\n * either because the element was removed, the page was re-laid out\n * significantly, or the comment was migrated from a different deploy.\n *\n * The label is whatever we know about the original anchor: tagName +\n * the captured textSnippet truncated to 60 chars.\n */\nfunction renderOrphansHtml(orphans: VizuComment[]): string {\n const items = orphans\n .map((c) => {\n const status = c.status ?? 'open';\n const primary = (c.fingerprints || [])[0];\n const label = primary ? primary.tagName.toLowerCase() : 'comment';\n const snippet = primary?.textSnippet?.slice(0, 60) ?? '';\n return `\n <div class=\"vz-sidebar-item vz-sidebar-item-orphan\" data-comment-id=\"${escapeHtml(c.id)}\">\n <div class=\"vz-sidebar-item-row\">\n ${c.author ? `<div class=\"vz-sidebar-item-author\">${avatarHtml(c.author)} <span class=\"vz-sidebar-item-author-name\">${escapeHtml(c.author.name)}</span></div>` : ''}\n <span class=\"vz-status-pill vz-status-orphan\">orphaned</span>\n </div>\n <div class=\"vz-sidebar-orphan-anchor\">\n was on <code>${escapeHtml(label)}</code>${snippet ? ` &ldquo;${escapeHtml(snippet)}${(primary?.textSnippet?.length ?? 0) > 60 ? '…' : ''}&rdquo;` : ''}\n </div>\n <span class=\"vz-sidebar-item-text\">${renderCommentText(c.text, c.id)}</span>\n ${renderAttachmentsHtml(c.attachments)}\n <div class=\"vz-sidebar-item-meta\">\n <span>${escapeHtml(formatTime(c.createdAt))}${status !== 'open' ? ` · ${status}` : ''}</span>\n <span class=\"vz-sidebar-item-actions\">\n <button class=\"vz-sidebar-item-delete\" data-vz=\"delete\" data-id=\"${escapeHtml(c.id)}\" title=\"Delete comment\">delete</button>\n </span>\n </div>\n </div>\n `;\n })\n .join('');\n return `\n <div class=\"vz-sidebar-orphans\">\n <div class=\"vz-sidebar-orphans-head\">\n Orphaned · ${orphans.length}\n <span class=\"vz-sidebar-orphans-sub\">couldn't find the original element on this page</span>\n </div>\n ${items}\n </div>\n `;\n}\n\nfunction findFingerprintByKey(comments: VizuComment[], key: string): ElementFingerprint | null {\n for (const c of comments) {\n for (const fp of c.fingerprints || []) {\n if (fingerprintKey(fp) === key) return fp;\n }\n }\n return null;\n}\n","export const STYLES = `\n[data-vizu-root] {\n position: fixed; top: 0; left: 0; width: 0; height: 0;\n z-index: 2147483647;\n pointer-events: none;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif;\n font-size: 13px; line-height: 1.5; color: #fafafa;\n --vz-accent: #FF6647;\n --vz-bg: #0A0A0A;\n --vz-border: #2A2A2A;\n --vz-bg-hover: #1A1A1A;\n --vz-text-muted: #888;\n --vz-marker-fg: #FFFFFF;\n}\n[data-vizu-root] * { box-sizing: border-box; }\n/* Button reset uses :where() to drop its specificity to 0,0,1 so any single-class\n selector like .vz-marker / .vz-pill-btn / .vz-btn that sets background or\n color still wins, without needing to redeclare those everywhere. */\n:where([data-vizu-root]) button {\n font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0;\n}\n\n/* Hover highlight (transient) */\n.vz-highlight {\n position: fixed;\n border: 2px dashed var(--vz-accent);\n background: color-mix(in srgb, var(--vz-accent) 8%, transparent);\n pointer-events: none;\n transition: all 90ms ease-out;\n border-radius: 2px;\n z-index: 1;\n}\n\n/* Always-on outline for commented elements */\n.vz-comment-outline {\n position: fixed;\n border: 1px dashed color-mix(in srgb, var(--vz-accent) 55%, transparent);\n background: color-mix(in srgb, var(--vz-accent) 4%, transparent);\n pointer-events: none;\n border-radius: 2px;\n z-index: 1;\n}\n.vz-comment-outline.is-selected-pending {\n border: 1.5px dashed var(--vz-accent);\n background: color-mix(in srgb, var(--vz-accent) 10%, transparent);\n}\n/* Drifted: the anchor resolved only via a fuzzy fallback (sibling-index\n or page-wide text snippet). Amber border warns visitors the match\n might be wrong. */\n.vz-comment-outline-drifted {\n border-color: rgb(252, 211, 77);\n background: rgba(245, 158, 11, 0.06);\n}\n.vz-comment-outline.is-pulsing {\n animation: vz-pulse 1500ms ease-out;\n}\n@keyframes vz-pulse {\n 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--vz-accent) 60%, transparent); border-color: var(--vz-accent); }\n 60% { box-shadow: 0 0 0 22px color-mix(in srgb, var(--vz-accent) 0%, transparent); border-color: var(--vz-accent); }\n 100% { box-shadow: 0 0 0 0 transparent; border-color: color-mix(in srgb, var(--vz-accent) 55%, transparent); }\n}\n\n/* Strong \"spotlight\" rendered when the user jumps to an element (sidebar item,\n anchor chip, or # reference click). Visible even on elements that don't have\n a saved comment yet. */\n.vz-jump-spotlight {\n position: fixed;\n border: 2.5px solid var(--vz-accent);\n background: color-mix(in srgb, var(--vz-accent) 16%, transparent);\n pointer-events: none;\n border-radius: 4px;\n z-index: 4;\n animation: vz-spotlight 1700ms cubic-bezier(0.22, 0.61, 0.36, 1) forwards;\n}\n@keyframes vz-spotlight {\n 0% { opacity: 0; box-shadow: 0 0 0 0 transparent; }\n 10% { opacity: 1; box-shadow: 0 0 0 6px color-mix(in srgb, var(--vz-accent) 55%, transparent); }\n 45% { opacity: 1; box-shadow: 0 0 0 28px color-mix(in srgb, var(--vz-accent) 10%, transparent); }\n 80% { opacity: 0.9; box-shadow: 0 0 0 36px color-mix(in srgb, var(--vz-accent) 0%, transparent); }\n 100% { opacity: 0; box-shadow: 0 0 0 36px transparent; background: color-mix(in srgb, var(--vz-accent) 0%, transparent); }\n}\n\n/* Figma-style speech-bubble marker — pinned at top-right of element, tail pointing in */\n.vz-marker {\n position: fixed;\n min-width: 28px; height: 26px;\n padding: 0 9px;\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n font-size: 11px; font-weight: 700;\n letter-spacing: 0.01em;\n display: inline-flex; align-items: center; justify-content: center;\n cursor: pointer;\n pointer-events: auto;\n /* Asymmetric radius = the \"pin\" tail: sharp at bottom-left, rounded elsewhere */\n border-radius: 14px 14px 14px 3px;\n /* Solid Vizu-colored halo via 2px ring + drop shadow — visible on any backdrop */\n box-shadow:\n 0 4px 12px rgba(0,0,0,0.28),\n 0 0 0 2px var(--vz-bg);\n /* Center the marker on the element's top-right corner, half outside the element */\n transform: translate(-50%, -50%);\n transition: transform 120ms cubic-bezier(0.34, 1.56, 0.64, 1);\n z-index: 2;\n}\n.vz-marker.is-group-member { font-size: 10px; padding: 0 7px; }\n.vz-marker.is-sibling-pulsing { animation: vz-marker-pulse 600ms ease-out; }\n\n/* Drifted marker: amber-tinted fill + dashed ring instead of solid halo\n so visitors can see at a glance \"this might not be the right element\". */\n.vz-marker-drifted {\n background: rgb(252, 211, 77);\n color: rgba(0, 0, 0, 0.78);\n box-shadow:\n 0 4px 12px rgba(0,0,0,0.28),\n 0 0 0 2px var(--vz-bg),\n 0 0 0 3px rgb(252, 211, 77);\n}\n@keyframes vz-marker-pulse {\n 0% { box-shadow: 0 4px 12px rgba(0,0,0,0.28), 0 0 0 2px var(--vz-bg), 0 0 0 0 var(--vz-accent); }\n 60% { box-shadow: 0 4px 12px rgba(0,0,0,0.28), 0 0 0 2px var(--vz-bg), 0 0 0 10px color-mix(in srgb, var(--vz-accent) 0%, transparent); }\n 100% { box-shadow: 0 4px 12px rgba(0,0,0,0.28), 0 0 0 2px var(--vz-bg), 0 0 0 0 transparent; }\n}\n.vz-marker:hover {\n transform: translate(-50%, -50%) scale(1.1);\n}\n.vz-marker-avatar {\n width: 100%; height: 100%;\n border-radius: 50% 50% 50% 3px;\n object-fit: cover;\n display: block;\n}\n.vz-marker.has-avatar {\n padding: 0;\n width: 28px; min-width: 28px; height: 28px;\n background: var(--vz-bg);\n border-radius: 50% 50% 50% 3px;\n overflow: hidden;\n}\n.vz-marker.has-avatar .vz-marker-count {\n position: absolute;\n bottom: -4px; right: -4px;\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n min-width: 16px; height: 16px;\n border-radius: 8px;\n font-size: 9px; font-weight: 700;\n display: inline-flex; align-items: center; justify-content: center;\n padding: 0 4px;\n box-shadow: 0 0 0 2px var(--vz-bg);\n}\n.vz-marker-initials {\n font-size: 11px; font-weight: 700;\n color: #fafafa;\n background: var(--vz-bg-hover);\n width: 100%; height: 100%;\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50% 50% 50% 3px;\n}\n\n/* Floating pill */\n.vz-pill {\n position: fixed;\n bottom: 24px; left: 50%; transform: translateX(-50%);\n background: var(--vz-bg);\n border: 1px solid var(--vz-border);\n border-radius: 999px;\n padding: 7px 8px 7px 18px;\n display: flex; align-items: center; gap: 10px;\n pointer-events: auto;\n box-shadow: 0 8px 24px rgba(0,0,0,.4), inset 0 0 0 1px rgba(255,255,255,.04);\n user-select: none;\n max-width: calc(100vw - 32px);\n z-index: 5;\n}\n.vz-pill.is-selecting { border-color: var(--vz-accent); }\n.vz-pill-toggle {\n width: 28px; height: 28px;\n border-radius: 6px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--vz-text-muted);\n transition: background 100ms, color 100ms;\n}\n.vz-pill-toggle:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-pill-toggle.is-active { background: color-mix(in srgb, var(--vz-accent) 22%, var(--vz-bg-hover)); color: var(--vz-accent); }\n.vz-pill-toggle svg { width: 14px; height: 14px; }\n.vz-pill-dot {\n width: 8px; height: 8px; border-radius: 4px;\n background: var(--vz-accent);\n box-shadow: 0 0 8px var(--vz-accent);\n flex-shrink: 0;\n}\n.vz-pill-label { font-size: 12px; font-weight: 600; letter-spacing: 0.02em; }\n.vz-pill-count-btn {\n font-size: 11px; color: var(--vz-text-muted);\n padding: 4px 10px;\n border-radius: 999px;\n transition: background 100ms, color 100ms;\n}\n.vz-pill-count-btn:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-pill-count-btn.is-active { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-pill-btn {\n padding: 7px 14px;\n border-radius: 999px;\n font-size: 12px; font-weight: 500;\n color: #fafafa;\n transition: background 100ms;\n white-space: nowrap;\n}\n.vz-pill-btn:hover { background: var(--vz-bg-hover); }\n.vz-pill-btn.primary {\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n font-weight: 600;\n padding: 7px 16px;\n}\n.vz-pill-btn.primary:hover { background: color-mix(in srgb, var(--vz-accent) 88%, white); }\n.vz-pill-divider { width: 1px; height: 18px; background: var(--vz-border); flex-shrink: 0; }\n.vz-pill-icon-btn {\n width: 28px; height: 28px;\n border-radius: 50%;\n display: inline-flex; align-items: center; justify-content: center;\n color: #fafafa;\n transition: background 100ms;\n}\n.vz-pill-icon-btn:hover { background: var(--vz-bg-hover); }\n.vz-pill-user {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 3px 10px 3px 4px;\n background: var(--vz-bg-hover);\n border-radius: 999px;\n font-size: 11px;\n color: #fafafa;\n}\n.vz-pill-user-avatar {\n width: 22px; height: 22px;\n border-radius: 50%;\n object-fit: cover;\n background: var(--vz-border);\n font-size: 10px; font-weight: 700;\n color: #fafafa;\n display: inline-flex; align-items: center; justify-content: center;\n}\n\n/* Popover */\n.vz-popover {\n position: fixed;\n background: var(--vz-bg);\n border: 1px solid var(--vz-border);\n border-radius: 8px;\n padding: 12px;\n min-width: 280px; max-width: 360px;\n pointer-events: auto;\n box-shadow: 0 12px 32px rgba(0,0,0,.5);\n display: flex; flex-direction: column; gap: 8px;\n z-index: 6;\n}\n.vz-popover-header {\n font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;\n color: var(--vz-text-muted);\n display: flex; justify-content: space-between; align-items: center;\n}\n.vz-popover-close {\n font-size: 18px; line-height: 1;\n color: #666;\n width: 22px; height: 22px;\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: 4px;\n}\n.vz-popover-close:hover { color: #fafafa; background: var(--vz-bg-hover); }\n.vz-target-label {\n font-size: 11px; color: var(--vz-text-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n word-break: break-all;\n padding: 6px 8px;\n background: var(--vz-bg-hover);\n border-radius: 4px;\n}\n.vz-comment-list {\n display: flex; flex-direction: column; gap: 6px;\n max-height: 200px; overflow-y: auto;\n}\n.vz-comment {\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n border-radius: 6px;\n padding: 8px 10px;\n font-size: 13px;\n display: flex; flex-direction: column; gap: 6px;\n}\n.vz-comment-author {\n display: flex; align-items: center; gap: 6px;\n font-size: 11px;\n color: var(--vz-text-muted);\n}\n.vz-comment-author-avatar {\n width: 18px; height: 18px;\n border-radius: 50%;\n background: var(--vz-border);\n display: inline-flex; align-items: center; justify-content: center;\n font-size: 9px; font-weight: 700;\n color: #fafafa;\n overflow: hidden;\n}\n.vz-comment-author-avatar img { width: 100%; height: 100%; object-fit: cover; }\n.vz-comment-author-name { color: #fafafa; font-weight: 500; }\n.vz-comment-meta {\n font-size: 10px; color: #666;\n display: flex; justify-content: space-between;\n}\n.vz-comment-delete { color: #666; font-size: 10px; text-decoration: underline; }\n.vz-comment-delete:hover { color: #ff7676; }\n.vz-empty { font-size: 12px; color: var(--vz-text-muted); padding: 4px 0; }\n.vz-textarea {\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n color: #fafafa;\n border-radius: 6px;\n padding: 8px 10px;\n font: inherit;\n font-size: 13px;\n resize: vertical;\n min-height: 60px;\n max-height: 160px;\n overflow-y: auto;\n outline: none;\n width: 100%;\n line-height: 1.5;\n word-break: break-word;\n white-space: pre-wrap;\n}\n.vz-textarea:focus { border-color: var(--vz-accent); }\n.vz-textarea:empty::before {\n content: attr(data-placeholder);\n color: var(--vz-text-muted);\n pointer-events: none;\n}\n/* Inline chip rendered live inside the contenteditable comment editor */\n.vz-chip {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 1px 8px 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-size: 12px; font-weight: 500;\n cursor: default;\n user-select: none;\n margin: 0 2px;\n vertical-align: baseline;\n white-space: nowrap;\n}\n.vz-chip::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--vz-accent); flex-shrink: 0; }\n\n/* Popover anchor list (multi-anchor comment header) */\n.vz-anchor-list {\n display: flex; flex-wrap: wrap; gap: 4px;\n padding: 6px 0 0;\n}\n.vz-anchor-chip {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 3px 8px 3px 6px;\n border-radius: 999px;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n font-size: 11px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #fafafa;\n cursor: pointer;\n}\n.vz-anchor-chip::before {\n content: '';\n width: 5px; height: 5px;\n border-radius: 50%;\n background: var(--vz-accent);\n}\n.vz-anchor-chip:hover { border-color: var(--vz-accent); }\n.vz-anchor-chip-remove {\n font-size: 12px; color: var(--vz-text-muted);\n margin-left: 2px;\n background: none; border: 0; cursor: pointer;\n padding: 0 2px;\n}\n.vz-anchor-chip-remove:hover { color: #ff7676; }\n.vz-anchor-hint { font-size: 11px; color: var(--vz-text-muted); padding: 4px 0; }\n.vz-popover-actions { display: flex; justify-content: flex-end; gap: 6px; }\n.vz-btn {\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 12px; font-weight: 500;\n transition: background 100ms;\n}\n.vz-btn-ghost { color: var(--vz-text-muted); }\n.vz-btn-ghost:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-btn-primary {\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n font-weight: 600;\n}\n.vz-btn-primary:hover { background: color-mix(in srgb, var(--vz-accent) 88%, white); }\n\n/* Mention picker — floating dropdown anchored under the @ mention button */\n.vz-mention-picker {\n position: absolute;\n z-index: 10;\n min-width: 200px;\n max-width: 280px;\n max-height: 240px;\n overflow-y: auto;\n padding: 4px;\n background: var(--vz-bg-popover, #1c1c1c);\n border: 1px solid var(--vz-border, rgba(255,255,255,0.08));\n border-radius: 8px;\n box-shadow: 0 12px 28px rgba(0,0,0,0.4), 0 2px 6px rgba(0,0,0,0.3);\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n.vz-mention-loading,\n.vz-mention-empty {\n padding: 10px 12px;\n font-size: 12px;\n color: var(--vz-text-muted);\n text-align: center;\n}\n.vz-mention-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 10px;\n border-radius: 6px;\n background: transparent;\n border: none;\n color: #fafafa;\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n transition: background 80ms;\n}\n.vz-mention-item:hover,\n.vz-mention-item:focus-visible {\n background: var(--vz-bg-hover, rgba(255,255,255,0.06));\n outline: none;\n}\n.vz-mention-avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px; height: 22px;\n border-radius: 50%;\n font-size: 10px; font-weight: 600;\n color: var(--vz-marker-fg, #0a0a0a);\n background: var(--vz-accent, #ff8b6e);\n flex-shrink: 0;\n overflow: hidden;\n}\n.vz-mention-avatar img {\n width: 100%; height: 100%; object-fit: cover;\n}\n.vz-mention-item-name {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Sidebar */\n.vz-sidebar {\n position: fixed;\n top: 0; right: 0;\n width: 360px;\n height: 100vh;\n background: var(--vz-bg);\n border-left: 1px solid var(--vz-border);\n pointer-events: auto;\n display: flex; flex-direction: column;\n transform: translateX(100%);\n transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1);\n box-shadow: -16px 0 48px rgba(0,0,0,0.35);\n z-index: 7;\n}\n.vz-sidebar.is-open { transform: translateX(0); }\n.vz-sidebar-header {\n padding: 16px 18px;\n border-bottom: 1px solid var(--vz-border);\n display: flex; align-items: center; gap: 10px;\n flex-shrink: 0;\n}\n.vz-sidebar-title {\n font-size: 13px; font-weight: 600;\n display: flex; align-items: center; gap: 8px;\n}\n.vz-sidebar-title-count {\n font-size: 11px;\n color: var(--vz-text-muted);\n font-weight: 500;\n padding: 2px 8px;\n background: var(--vz-bg-hover);\n border-radius: 999px;\n}\n.vz-sidebar-close {\n margin-left: auto;\n width: 28px; height: 28px;\n border-radius: 6px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--vz-text-muted);\n font-size: 18px; line-height: 1;\n}\n.vz-sidebar-close:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-sidebar-body {\n flex: 1 1 auto;\n overflow-y: auto;\n padding: 12px;\n}\n.vz-sidebar-empty {\n padding: 32px 18px;\n text-align: center;\n color: var(--vz-text-muted);\n font-size: 13px;\n}\n.vz-sidebar-empty strong { color: #fafafa; display: block; margin-bottom: 4px; font-weight: 600; }\n.vz-sidebar-group {\n margin-bottom: 16px;\n}\n.vz-sidebar-group-head {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10px;\n text-transform: lowercase;\n color: var(--vz-text-muted);\n padding: 4px 6px;\n margin-bottom: 4px;\n word-break: break-all;\n}\n.vz-sidebar-group-text {\n font-family: inherit;\n text-transform: none;\n font-size: 11px;\n color: #c4c4c4;\n margin-top: 2px;\n}\n.vz-sidebar-item {\n display: block;\n width: 100%;\n text-align: left;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n border-radius: 8px;\n padding: 10px 12px;\n margin-bottom: 6px;\n font-size: 13px;\n color: #fafafa;\n transition: border-color 100ms, transform 100ms;\n}\n.vz-sidebar-item:hover {\n border-color: color-mix(in srgb, var(--vz-accent) 50%, var(--vz-border));\n transform: translateX(-2px);\n}\n.vz-sidebar-item-author {\n display: flex; align-items: center; gap: 6px;\n font-size: 11px;\n color: var(--vz-text-muted);\n margin-bottom: 4px;\n}\n.vz-sidebar-item-author .vz-comment-author-avatar { width: 16px; height: 16px; font-size: 8px; }\n.vz-sidebar-item-author-name { color: #fafafa; font-weight: 500; }\n.vz-sidebar-item-text {\n display: block;\n line-height: 1.45;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.vz-sidebar-item-meta {\n display: flex; justify-content: space-between; align-items: center;\n margin-top: 6px;\n font-size: 10px;\n color: #666;\n}\n.vz-sidebar-item-delete {\n color: #666;\n background: transparent;\n border: 0;\n padding: 0;\n cursor: pointer;\n text-decoration: underline;\n font-size: 10px;\n font-family: inherit;\n}\n.vz-sidebar-item-delete:hover { color: #ff7676; }\n\n/* Status filter tabs in the sidebar header */\n.vz-sidebar-filters {\n display: flex;\n gap: 4px;\n padding: 6px 16px 12px;\n border-bottom: 1px solid var(--vz-border);\n}\n.vz-sidebar-filter {\n background: transparent;\n border: 1px solid transparent;\n color: var(--vz-text-soft);\n font: inherit;\n font-size: 10.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n padding: 4px 10px;\n border-radius: 999px;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: color 100ms, background 100ms, border-color 100ms;\n}\n.vz-sidebar-filter:hover { color: var(--vz-text); }\n.vz-sidebar-filter.is-active {\n background: color-mix(in srgb, var(--vz-accent) 15%, transparent);\n color: var(--vz-accent);\n border-color: color-mix(in srgb, var(--vz-accent) 30%, transparent);\n}\n.vz-sidebar-filter-count {\n color: var(--vz-text-faint);\n font-size: 9.5px;\n}\n.vz-sidebar-filter.is-active .vz-sidebar-filter-count {\n color: inherit;\n opacity: 0.7;\n}\n\n/* Per-item status pill (resolved / wontfix) shown at top-right of dim items */\n.vz-sidebar-item-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 6px;\n}\n.vz-status-pill {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 9.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n font-weight: 600;\n}\n.vz-status-resolved {\n background: rgba(34, 197, 94, 0.18);\n color: rgb(74, 222, 128);\n}\n.vz-status-wontfix {\n background: rgba(113, 113, 122, 0.22);\n color: rgb(161, 161, 170);\n}\n/* Drifted anchor — fell through to a fuzzy fallback. Render amber. */\n.vz-status-drifted {\n background: rgba(245, 158, 11, 0.18);\n color: rgb(252, 211, 77);\n}\n/* Orphaned — anchor element no longer on the page. Quiet zinc, dim text. */\n.vz-status-orphan {\n background: rgba(113, 113, 122, 0.24);\n color: rgb(212, 212, 216);\n}\n\n/* Dim non-open items in 'All' view */\n.vz-sidebar-item-dim { opacity: 0.55; }\n.vz-sidebar-item-dim:hover { opacity: 0.85; }\n\n/* Orphaned-comments section — separate from the active comment list. */\n.vz-sidebar-orphans {\n margin-top: 18px;\n padding-top: 14px;\n border-top: 1px dashed var(--vz-border);\n}\n.vz-sidebar-orphans-head {\n padding: 0 16px;\n font-family: var(--vz-mono, ui-monospace, monospace);\n font-size: 10.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: rgb(212, 212, 216);\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin-bottom: 10px;\n}\n.vz-sidebar-orphans-sub {\n font-size: 9.5px;\n letter-spacing: 0.06em;\n text-transform: none;\n color: var(--vz-text-faint);\n}\n.vz-sidebar-item-orphan {\n opacity: 0.78;\n}\n.vz-sidebar-item-orphan:hover { opacity: 1; }\n.vz-sidebar-orphan-anchor {\n margin-top: 4px;\n margin-bottom: 6px;\n font-size: 11px;\n color: var(--vz-text-faint);\n line-height: 1.5;\n}\n.vz-sidebar-orphan-anchor code {\n background: var(--vz-bg-hover);\n padding: 1px 5px;\n border-radius: 3px;\n font-size: 10.5px;\n}\n\n/* Per-item action buttons (resolve / reopen / wontfix) */\n.vz-sidebar-item-actions {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n}\n.vz-sidebar-item-status {\n background: transparent;\n border: 0;\n padding: 0;\n cursor: pointer;\n color: var(--vz-text-soft);\n text-decoration: underline;\n font-size: 10px;\n font-family: inherit;\n}\n.vz-sidebar-item-status:hover { color: var(--vz-accent); }\n\n/* # element-reference chip (rendered inside comments) */\n.vz-ref {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 1px 8px 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-size: 12px; font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n transition: background 100ms, transform 100ms;\n vertical-align: baseline;\n margin: 0 1px;\n}\n.vz-ref:hover { background: color-mix(in srgb, var(--vz-accent) 30%, var(--vz-bg-hover)); transform: translateY(-1px); }\n.vz-ref::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--vz-accent); flex-shrink: 0; }\n\n/* Inline @mention chip (rendered inside displayed comments) */\n.vz-mention {\n display: inline-flex; align-items: center;\n padding: 1px 8px 1px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-size: 12px; font-weight: 600;\n vertical-align: baseline;\n margin: 0 1px;\n}\n\n/* Mention chip inside the editor (contenteditable=false token) */\n.vz-chip-mention {\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-weight: 600;\n}\n.vz-chip-mention::before { display: none; }\n\n/* @-mention button in the popover action row */\n.vz-btn-mention { color: var(--vz-accent); }\n\n/* Reply count + reply toggle in a comment's action row */\n.vz-comment-actions { display: inline-flex; align-items: center; gap: 10px; }\n.vz-comment-reply-toggle {\n background: transparent;\n border: 0;\n padding: 0;\n cursor: pointer;\n color: var(--vz-text-soft);\n text-decoration: underline;\n font-size: 10px;\n font-family: inherit;\n}\n.vz-comment-reply-toggle:hover { color: var(--vz-accent); }\n\n/* Inline reply list + form (shown when toggle-reply opens it) */\n.vz-replies {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px dashed var(--vz-border);\n}\n.vz-reply-list { display: flex; flex-direction: column; gap: 8px; }\n.vz-reply {\n padding: 6px 10px;\n border-radius: 8px;\n background: var(--vz-bg-hover);\n}\n.vz-reply-text {\n font-size: 13px;\n color: var(--vz-text);\n line-height: 1.45;\n white-space: pre-wrap;\n}\n.vz-reply-form {\n display: flex;\n gap: 6px;\n margin-top: 8px;\n}\n.vz-reply-input {\n flex: 1;\n resize: vertical;\n min-height: 36px;\n padding: 6px 8px;\n font-family: inherit;\n font-size: 12.5px;\n background: var(--vz-bg);\n color: var(--vz-text);\n border: 1px solid var(--vz-border);\n border-radius: 6px;\n outline: none;\n}\n.vz-reply-input:focus { border-color: var(--vz-accent); }\n.vz-btn-reply { padding: 4px 12px; font-size: 12px; }\n\n/* ─── Attachment upload UI (popover) ─────────────────────────────────── */\n\n.vz-editor-wrap {\n position: relative;\n}\n.vz-attachment-drop-hint {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--vz-accent) 18%, var(--vz-bg));\n border: 2px dashed var(--vz-accent);\n border-radius: 8px;\n color: var(--vz-accent);\n font-size: 13px;\n font-weight: 600;\n pointer-events: none;\n z-index: 1;\n}\n\n/* Pending attachments (uploaded but not yet saved) */\n.vz-attachment-list {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.vz-attachment-list:empty { display: none; }\n.vz-attachment-item {\n position: relative;\n width: 64px;\n height: 64px;\n border-radius: 6px;\n overflow: hidden;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n}\n.vz-attachment-thumb {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n color: var(--vz-text-soft);\n font-size: 11px;\n text-align: center;\n line-height: 64px;\n}\n.vz-attachment-thumb-loading {\n background: var(--vz-bg-hover);\n animation: vz-attachment-pulse 1.4s ease-in-out infinite;\n}\n@keyframes vz-attachment-pulse {\n 0%, 100% { opacity: 0.4; }\n 50% { opacity: 1; }\n}\n.vz-attachment-remove {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: rgba(0, 0, 0, 0.72);\n color: #fafafa;\n border: 0;\n cursor: pointer;\n font-size: 12px;\n line-height: 14px;\n padding: 0;\n}\n.vz-attachment-remove:hover { background: rgba(0, 0, 0, 0.9); }\n\n/* Display-side attachment grid (rendered inside comments) */\n.vz-attachment-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));\n gap: 4px;\n margin-top: 6px;\n}\n.vz-attachment-display {\n display: block;\n border-radius: 4px;\n overflow: hidden;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n text-decoration: none;\n color: var(--vz-text-soft);\n}\n.vz-attachment-display img {\n display: block;\n width: 100%;\n height: 60px;\n object-fit: cover;\n}\n.vz-attachment-display:hover { border-color: var(--vz-accent); }\n.vz-attachment-file {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n font-size: 11px;\n}\n.vz-attachment-icon { font-size: 14px; }\n.vz-attachment-filename {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n/* Banner shown while user is picking an element to reference */\n.vz-picking-banner {\n position: fixed;\n top: 24px; left: 50%; transform: translateX(-50%);\n background: var(--vz-bg);\n border: 1px solid var(--vz-accent);\n border-radius: 999px;\n padding: 8px 8px 8px 18px;\n pointer-events: auto;\n box-shadow: 0 8px 24px rgba(0,0,0,0.5);\n display: flex; align-items: center; gap: 12px;\n font-size: 13px;\n color: #fafafa;\n z-index: 8;\n}\n.vz-picking-banner-hint {\n color: var(--vz-text-muted); font-size: 11px;\n display: inline-flex; align-items: center; gap: 6px;\n}\n.vz-picking-banner-kbd {\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n border-radius: 4px;\n padding: 1px 6px;\n font-family: ui-monospace, monospace;\n font-size: 10px;\n color: #fafafa;\n}\n.vz-picking-banner-cancel {\n padding: 4px 12px;\n font-size: 12px;\n background: var(--vz-bg-hover);\n border-radius: 999px;\n color: var(--vz-text-muted);\n}\n.vz-picking-banner-cancel:hover { color: #fafafa; }\n\n.vz-toast {\n position: fixed;\n bottom: 80px; left: 50%; transform: translateX(-50%);\n background: var(--vz-bg);\n border: 1px solid var(--vz-border);\n border-radius: 6px;\n padding: 10px 14px;\n font-size: 12px;\n color: #fafafa;\n pointer-events: none;\n animation: vz-toast-in 200ms ease-out;\n z-index: 9;\n}\n@keyframes vz-toast-in {\n from { opacity: 0; transform: translateX(-50%) translateY(8px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n`;\n\nexport function injectStyles() {\n if (typeof document === 'undefined') return;\n if (document.getElementById('vizu-styles')) return;\n const style = document.createElement('style');\n style.id = 'vizu-styles';\n style.textContent = STYLES;\n (document.head || document.documentElement).appendChild(style);\n}\n","export interface ParsedShortcut {\n key: string;\n mod: boolean;\n shift: boolean;\n alt: boolean;\n ctrl: boolean;\n}\n\nexport function parseShortcut(spec: string): ParsedShortcut {\n const parts = spec.toLowerCase().split('+').map((s) => s.trim());\n const last = parts.pop() || '';\n return {\n key: last,\n mod: parts.includes('mod') || parts.includes('cmdorctrl'),\n shift: parts.includes('shift'),\n alt: parts.includes('alt') || parts.includes('option'),\n ctrl: parts.includes('ctrl') || parts.includes('control'),\n };\n}\n\nexport function matches(e: KeyboardEvent, p: ParsedShortcut): boolean {\n const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform);\n if (e.key.toLowerCase() !== p.key) return false;\n if (p.mod) {\n const modPressed = isMac ? e.metaKey : e.ctrlKey;\n if (!modPressed) return false;\n } else if (p.ctrl && !e.ctrlKey) {\n return false;\n }\n if (p.shift !== e.shiftKey) return false;\n if (p.alt !== e.altKey) return false;\n return true;\n}\n","import type { VizuEventMap, VizuEventName, VizuEventHandler } from './types';\n\ntype AnyHandler = (payload: unknown) => void;\n\nexport class EventBus {\n private listeners = new Map<string, Set<AnyHandler>>();\n\n on<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(handler as AnyHandler);\n return () => this.off(event, handler);\n }\n\n off<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): void {\n this.listeners.get(event)?.delete(handler as AnyHandler);\n }\n\n emit<E extends VizuEventName>(event: E, payload: VizuEventMap[E]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n // Snapshot to allow handlers to unsubscribe during iteration without skipping siblings\n for (const handler of [...set]) {\n try {\n handler(payload);\n } catch (err) {\n // Listener failures should not break Vizu's own state machine\n // eslint-disable-next-line no-console\n console.error('[vizu] event handler error for', event, err);\n }\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n","import type {\n VizuOptions,\n VizuComment,\n ElementFingerprint,\n StorageAdapter,\n StorageOption,\n VizuUser,\n VizuAction,\n ActionContext,\n VizuEventName,\n VizuEventHandler,\n VizuCloudOptions,\n AuthContext,\n Reply,\n Attachment,\n MatchConfidence,\n MentionableUser,\n} from './types';\nimport { SCHEMA_VERSION } from './types';\nimport {\n LocalStorageAdapter,\n InMemoryStorageAdapter,\n NullStorageAdapter,\n isV1Adapter,\n wrapV1Adapter,\n} from './storage';\nimport { CloudStorageAdapter, type CloudError } from './cloud';\nimport { fingerprint, findByFingerprint, findElementByFingerprint, fingerprintKey, fingerprintLabel } from './fingerprint';\nimport { Highlighter } from './highlighter';\nimport { Popover } from './popover';\nimport { Pill } from './pill';\nimport { Sidebar } from './sidebar';\nimport { injectStyles } from './styles';\nimport { parseShortcut, matches as shortcutMatches } from './shortcut';\nimport { uuid, initials, escapeHtml, migrateComment, migrateComments, needsPersist } from './util';\nimport { EventBus } from './events';\n\nexport type {\n VizuOptions,\n VizuComment,\n ElementFingerprint,\n StorageAdapter,\n StorageOption,\n VizuUser,\n VizuAction,\n ActionContext,\n VizuEventName,\n VizuEventHandler,\n VizuCloudOptions,\n AuthContext,\n Reply,\n Attachment,\n MatchConfidence,\n};\nexport { SCHEMA_VERSION };\nexport { LocalStorageAdapter, InMemoryStorageAdapter, NullStorageAdapter };\nexport { CloudStorageAdapter };\nexport type { CloudError };\nexport { fingerprint, findByFingerprint, findElementByFingerprint, fingerprintKey, fingerprintLabel } from './fingerprint';\n\nconst DEFAULTS = {\n shortcut: 'mod+shift+e',\n namespace: 'default',\n accent: '#FF6647',\n};\n\n/** One rendered marker per element that has any comments anchored to it. */\ninterface MarkerEntry {\n marker: HTMLElement;\n target: Element | null;\n fp: ElementFingerprint;\n /** Comment IDs that touch this element (one entry per anchoring). */\n commentIds: string[];\n}\n/** One always-on outline per unique element that has comments anchored to it. */\ninterface OutlineEntry {\n outline: HTMLElement;\n target: Element | null;\n fp: ElementFingerprint;\n}\n\nfunction resolveStorage(\n storage: StorageOption | undefined,\n cloud: VizuCloudOptions | undefined,\n defaultMode: 'memory' | 'local',\n onAuthChanged?: (info: { userId: string; user: VizuUser | null }) => void,\n): StorageAdapter {\n if (cloud) {\n if (storage && typeof console !== 'undefined') {\n console.warn('[vizu] Both `cloud` and `storage` set; `cloud` wins. Drop `storage` to silence this.');\n }\n return new CloudStorageAdapter({\n workspace: cloud.workspace,\n apiUrl: cloud.apiUrl,\n autoSignIn: cloud.autoSignIn,\n onAuthChanged,\n });\n }\n\n if (!storage) return defaultMode === 'local' ? new LocalStorageAdapter() : new InMemoryStorageAdapter();\n if (storage === 'local') return new LocalStorageAdapter();\n if (storage === 'memory') return new InMemoryStorageAdapter();\n if (storage === 'none') return new NullStorageAdapter();\n // Custom adapter passed in. Detect v1 shape and wrap if needed.\n if (isV1Adapter(storage)) return wrapV1Adapter(storage);\n return storage as StorageAdapter;\n}\n\nexport class Vizu {\n private opts: VizuOptions;\n private parsedShortcut;\n private storage: StorageAdapter;\n private root: HTMLDivElement | null = null;\n private highlighter: Highlighter | null = null;\n private popover: Popover | null = null;\n private pill: Pill | null = null;\n private sidebar: Sidebar | null = null;\n /** Saved-comment markers: keyed by `${commentId}:${anchorIndex}` */\n private markers = new Map<string, MarkerEntry>();\n /** Persistent outlines for commented elements: keyed by fingerprintKey */\n private outlines = new Map<string, OutlineEntry>();\n /** Temporary \"active anchor\" outlines shown while popover is open OR\n * while a multi-select session is pending. Stores the fp so we can\n * reposition without needing to consult popover/pending state. */\n private activeAnchorOutlines = new Map<string, { el: HTMLElement; fp: ElementFingerprint }>();\n /** Transient spotlights rendered on jump (click sidebar item / anchor chip / # ref). */\n private spotlights = new Set<{ el: HTMLElement; target: Element }>();\n private comments: VizuComment[] = [];\n /**\n * Best confidence each comment was resolved at on the most recent\n * marker render. Used by the sidebar to surface orphaned + drifted\n * comments separately, and exposed publicly via {@link getCommentConfidence}.\n */\n private resolvedConfidence = new Map<string, MatchConfidence>();\n /**\n * Comment ids the matcher has already re-fingerprinted in this session.\n * Phase 11.4.4.3 self-healing: when a comment lands as `drifted` and\n * we have a live element to re-fingerprint, we rewrite the stored\n * fingerprint so the next load resolves as exact/likely. Tracked\n * here so the same render loop doesn't re-heal on every redraw.\n * Cleared on `destroy()`; survives `disable()`/`enable()`.\n */\n private selfHealedThisSession = new Set<string>();\n private actions: VizuAction[] = [];\n private user: VizuUser | null = null;\n /**\n * Flipped true once the initial loadComments() at construction has\n * settled (success or failure). The cloud onAuthChanged callback uses\n * this to decide whether a subsequent auth change should trigger a\n * re-load: skipped for the initial fire (the explicit loadComments\n * below handles it), fired for every later token acquisition.\n */\n private hasLoadedComments = false;\n private enabled = false;\n private rafScheduled = false;\n private bus = new EventBus();\n\n /** Picker: next element click is consumed as a reference instead of opening a popover. */\n private picking: { onSelect: (fp: ElementFingerprint) => void; onCancel: () => void } | null = null;\n private pickingBanner: HTMLDivElement | null = null;\n\n /** Multi-select state — pending selection NOT yet attached to a popover. */\n private multiSelectMode = false;\n private pendingFingerprints: ElementFingerprint[] = [];\n private pendingTargets: Element[] = [];\n\n constructor(options: VizuOptions = {}, _defaultStorage: 'memory' | 'local' = 'memory') {\n this.opts = { ...options };\n this.opts.shortcut = options.shortcut ?? DEFAULTS.shortcut;\n this.opts.namespace = options.namespace ?? DEFAULTS.namespace;\n this.opts.accent = options.accent ?? DEFAULTS.accent;\n this.parsedShortcut = parseShortcut(this.opts.shortcut);\n // Initialize this.user BEFORE resolveStorage runs — the\n // CloudStorageAdapter constructor synchronously fires\n // onAuthChanged when it rehydrates a sessionStorage token, which\n // calls this.setUser(). If we assigned this.user afterwards we'd\n // clobber the rehydrated identity with null and the user would\n // appear logged-out despite a perfectly good cached token.\n this.user = options.user ?? null;\n // Pass the auth-changed handler so cloud-mode auth (popup or rehydrated\n // sessionStorage token) refreshes `this.user` automatically — comments\n // saved after sign-in carry the proper display name + avatar.\n //\n // The handler ALSO re-runs loadComments() when auth changes after the\n // initial mount — important because the cloud adapter's load() now\n // returns [] silently when there's no token (to avoid page-load auth\n // ambushes). So when the user signs in via the shortcut later, the\n // formerly-empty comment list gets backfilled here.\n this.storage = resolveStorage(\n options.storage,\n options.cloud,\n _defaultStorage,\n (info) => {\n if (info.user) this.setUser(info.user);\n if (this.hasLoadedComments) {\n void this.loadComments();\n }\n },\n );\n if (options.actions) this.actions = [...options.actions];\n\n if (options.onCommentAdded) this.bus.on('comment:added', (p) => options.onCommentAdded!(p.comment));\n if (options.onCommentRemoved) this.bus.on('comment:removed', (p) => options.onCommentRemoved!(p.id));\n\n if (typeof window !== 'undefined') {\n window.addEventListener('keydown', this.onKeyDown);\n void this.loadComments()\n .catch(() => {\n /* logged in loadComments itself / non-fatal */\n })\n .finally(() => {\n this.hasLoadedComments = true;\n this.subscribeStorage();\n });\n if (options.startEnabled) this.deferred(() => this.enable());\n }\n }\n\n /* ===== Event API ===== */\n on<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): () => void {\n return this.bus.on(event, handler);\n }\n off<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): void {\n this.bus.off(event, handler);\n }\n\n /* ===== User API ===== */\n setUser(user: VizuUser | null): void {\n this.user = user;\n this.popover?.setUser(user);\n this.pill?.setUser(user);\n this.bus.emit('user:changed', { user });\n // Deferred-mount path: if the user pressed the shortcut to enable\n // Vizu while signed-out, we held off on mount() and just kicked\n // preflightAuth. Now that auth completed, finish what they started\n // — mount the pill + highlighter + markers in one go.\n if (this.enabled && !this.root && this.shouldMount()) {\n this.deferred(() => this.mount());\n }\n }\n getUser(): VizuUser | null { return this.user; }\n\n /**\n * Fetch the list of users who can be @-mentioned on the current\n * workspace. Used by the popover's mention dropdown; hosts can also\n * call this directly to build their own picker. Returns [] for\n * non-cloud adapters (no workspace, no member list to fetch from).\n */\n async searchMentionable(): Promise<MentionableUser[]> {\n if (this.storage instanceof CloudStorageAdapter) {\n return this.storage.searchMentionable();\n }\n return [];\n }\n\n /**\n * Whether the full Vizu UI (pill, highlighter, markers, popover) can\n * be mounted right now. Cloud-mode workspaces hold off until the\n * user is signed in; non-cloud usage (local / memory storage) has no\n * auth concept and mounts immediately. Re-checked from `enable()`\n * and from `setUser()` so that the moment auth resolves, the UI\n * appears without the user needing to press the shortcut again.\n */\n private shouldMount(): boolean {\n if (!(this.storage instanceof CloudStorageAdapter)) return true;\n return this.user !== null;\n }\n\n /**\n * Cloud-mode write gate. Every entry point that creates or modifies a\n * comment first checks that we have a known user. Without one, the\n * write would land as Anonymous (or 401 on the backend) — neither is\n * the right UX. So we re-trigger the sign-in popup and refuse the\n * action. The user signs in, this.user gets set via onAuthChanged,\n * then they can retry the click themselves.\n *\n * Returns true when the caller may proceed.\n */\n private requireUserOrAuth(): boolean {\n if (!(this.storage instanceof CloudStorageAdapter)) return true;\n if (this.user) return true;\n // Sticky denial from /connect: same workspace + same Clerk session\n // will get the same \"no access\" answer, so reopening the popup just\n // shows the user the same error. Refuse the click silently — a page\n // reload (or a sign-out to a different Clerk account) clears it.\n if (this.storage.isAccessDenied()) return false;\n void this.storage.preflightAuth().catch(() => {\n /* canceled / blocked — UI stays unauthed; next attempt retries */\n });\n return false;\n }\n\n /* ===== Lifecycle ===== */\n enable() {\n if (this.enabled) return;\n this.enabled = true;\n this.bus.emit('enabled', {});\n // Mount is gated on shouldMount(): cloud-mode workspaces wait for\n // a signed-in user before drawing any DOM (no pill, no highlighter,\n // no markers — comments are private to the workspace, so showing\n // them to an unauthenticated viewer would leak data). Once auth\n // resolves, setUser() above re-checks and fires mount.\n //\n // No preflightAuth here on purpose. Auto-init at page load\n // (`startEnabled: true`, or `enable()` from a host script) must NOT\n // throw a popup or redirect at the user before they've shown intent.\n // Sign-in fires from two interactive paths instead:\n // 1. onKeyDown — when the user presses the shortcut to toggle\n // Vizu on, the handler explicitly kicks preflightAuth.\n // 2. requireUserOrAuth — when the user clicks an element to leave\n // a comment without being signed in, the gate kicks preflight.\n // Both are user-driven; neither fires on page load.\n if (this.shouldMount()) {\n this.deferred(() => this.mount());\n }\n }\n disable() {\n if (!this.enabled) return;\n this.enabled = false;\n this.unmount();\n this.bus.emit('disabled', {});\n }\n toggle() { if (this.enabled) this.disable(); else this.enable(); }\n isEnabled(): boolean { return this.enabled; }\n destroy() {\n this.disable();\n if (typeof window !== 'undefined') window.removeEventListener('keydown', this.onKeyDown);\n this.unsubscribeStorage?.();\n this.unsubscribeStorage = null;\n this.selfHealedThisSession.clear();\n this.bus.clear();\n }\n\n /* ===== Action API ===== */\n addAction(action: VizuAction): void {\n const existing = this.actions.findIndex((a) => a.id === action.id);\n if (existing >= 0) this.actions[existing] = action;\n else this.actions.push(action);\n this.pill?.setActions(this.actions);\n }\n removeAction(id: string): void {\n this.actions = this.actions.filter((a) => a.id !== id);\n this.pill?.setActions(this.actions);\n }\n getActions(): VizuAction[] { return [...this.actions]; }\n invokeAction(id: string): void {\n const a = this.actions.find((x) => x.id === id);\n if (!a) return;\n const ctx = this.actionContext();\n this.bus.emit('action:invoked', { id });\n void a.onClick(ctx);\n }\n\n /* ===== Comment API ===== */\n getComments(): VizuComment[] { return [...this.comments]; }\n\n async setComments(comments: VizuComment[], opts?: { persist?: boolean }): Promise<void> {\n this.comments = migrateComments(comments);\n if (opts?.persist !== false) await this.storage.setAll(this.opts.namespace!, this.comments);\n this.bus.emit('comments:set', { comments: this.getComments() });\n this.refreshUi();\n }\n\n /**\n * Patch a single comment. Used by status changes (resolve / reopen),\n * future threading mutations, etc. Returns the updated comment or\n * `null` if no comment with that id exists.\n */\n async updateComment(id: string, patch: Partial<VizuComment>): Promise<VizuComment | null> {\n const idx = this.comments.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n // Never let callers overwrite identity or schema-version fields.\n const { id: _i, schemaVersion: _s, ...safe } = patch as Partial<VizuComment>;\n const next = { ...this.comments[idx], ...safe } as VizuComment;\n this.comments[idx] = next;\n const persisted = await this.storage.updateComment(this.opts.namespace!, id, safe);\n this.bus.emit('comment:updated', { comment: persisted ?? next });\n this.refreshUi();\n return next;\n }\n\n /** Auth context surfaced by the active storage adapter, if any (cloud adapters set this). */\n getAuthContext(): AuthContext | null {\n return this.storage.getAuthContext?.() ?? null;\n }\n\n /**\n * Re-fingerprint a comment's anchors against the live DOM and persist.\n * Called from the render loop when a comment lands `drifted` with a\n * matched element — rewriting the fingerprint here makes the next\n * load resolve via a cleaner rung (data-vizu-key / id / class) instead\n * of falling back to fuzzy text.\n *\n * Marks the comment as healed for this session so the same render\n * doesn't kick the same write twice. Failures are logged; the next\n * page load gets another chance.\n */\n private async selfHealComment(\n c: VizuComment,\n byElement: Map<string, { target: Element | null }>,\n ): Promise<void> {\n this.selfHealedThisSession.add(c.id);\n const next: ElementFingerprint[] = [];\n let changed = false;\n for (const fp of c.fingerprints || []) {\n const bucket = byElement.get(fingerprintKey(fp));\n if (bucket?.target) {\n const refreshed = fingerprint(bucket.target);\n next.push(refreshed);\n if (!changed && fingerprintKey(refreshed) !== fingerprintKey(fp)) changed = true;\n } else {\n // Anchor didn't resolve — keep the old fingerprint, no element to learn from.\n next.push(fp);\n }\n }\n // Skip the write if nothing actually changed (e.g. all anchors were\n // already optimal and the drift came from a co-anchored comment).\n if (!changed) return;\n const patch: Partial<VizuComment> = {\n fingerprints: next,\n fingerprintsRefreshedAt: Date.now(),\n };\n // Mirror locally so subsequent renders see the new fingerprints\n // without waiting for the round-trip.\n const idx = this.comments.findIndex((x) => x.id === c.id);\n if (idx !== -1) this.comments[idx] = { ...this.comments[idx], ...patch };\n try {\n await this.storage.updateComment(this.opts.namespace!, c.id, patch);\n } catch (err) {\n if (typeof console !== 'undefined') {\n console.warn('[vizu] self-heal write failed for', c.id, err);\n }\n }\n }\n\n /**\n * Resolution confidence for a comment as of the last marker render.\n * `'orphaned'` means the comment's fingerprints didn't resolve to any\n * live DOM element — the sidebar surfaces it in the orphaned section.\n * Returns `undefined` for unknown comment ids.\n */\n getCommentConfidence(commentId: string): MatchConfidence | undefined {\n return this.resolvedConfidence.get(commentId);\n }\n\n /** Returns the full Map of comment id → confidence. Useful for the dashboard. */\n getResolvedConfidence(): Map<string, MatchConfidence> {\n return new Map(this.resolvedConfidence);\n }\n\n /**\n * True iff the active storage adapter implements `uploadAttachment`.\n * The popover hides its upload UI when this returns false (local /\n * memory modes don't support attachments today).\n */\n canUploadAttachments(): boolean {\n return typeof this.storage.uploadAttachment === 'function';\n }\n\n /**\n * Upload an attachment via the active storage adapter. Throws if the\n * adapter doesn't implement uploads. Caller is responsible for adding\n * the returned Attachment to a comment (popover does this automatically).\n */\n async uploadAttachment(file: File): Promise<Attachment> {\n if (!this.storage.uploadAttachment) {\n throw new Error(\n '[vizu] active storage adapter does not support uploadAttachment; only the CloudStorageAdapter does in v1',\n );\n }\n return this.storage.uploadAttachment(this.opts.namespace!, file);\n }\n\n /**\n * Append a reply to a comment. Returns the parent comment or null if\n * the comment id doesn't exist. Routes through the storage adapter's\n * native `addReply` when available, falling back to an updateComment\n * patch for adapters that don't implement reply ops.\n */\n async addReply(commentId: string, text: string, mentions?: string[]): Promise<VizuComment | null> {\n if (!this.requireUserOrAuth()) return null;\n const idx = this.comments.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const reply: Reply = {\n id: uuid(),\n text: text.trim(),\n createdAt: Date.now(),\n author: this.user ?? undefined,\n authorId: this.user?.id,\n mentions: mentions && mentions.length ? mentions : undefined,\n };\n if (!reply.text) return null;\n // Optimistic local update.\n const updated = {\n ...this.comments[idx],\n replies: [...(this.comments[idx].replies ?? []), reply],\n } as VizuComment;\n this.comments[idx] = updated;\n // Persist via native reply op if available, else fall back to a\n // wholesale replies[] patch through updateComment.\n if (this.storage.addReply) {\n await this.storage.addReply(this.opts.namespace!, commentId, reply);\n } else {\n await this.storage.updateComment(this.opts.namespace!, commentId, { replies: updated.replies });\n }\n this.bus.emit('comment:updated', { comment: updated });\n this.refreshUi();\n if (this.popover?.isOpen()) {\n const anchors = this.popover.getAnchors();\n if (anchors[0]) this.popover.update(this.commentsForElement(anchors[0]));\n }\n return updated;\n }\n\n /**\n * Remove a reply from a comment. Returns the parent comment or null\n * if either id is unknown.\n */\n async removeReply(commentId: string, replyId: string): Promise<VizuComment | null> {\n const idx = this.comments.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const filtered = (this.comments[idx].replies ?? []).filter((r) => r.id !== replyId);\n if (filtered.length === (this.comments[idx].replies ?? []).length) return null;\n const updated = { ...this.comments[idx], replies: filtered } as VizuComment;\n this.comments[idx] = updated;\n if (this.storage.removeReply) {\n await this.storage.removeReply(this.opts.namespace!, commentId, replyId);\n } else {\n await this.storage.updateComment(this.opts.namespace!, commentId, { replies: filtered });\n }\n this.bus.emit('comment:updated', { comment: updated });\n this.refreshUi();\n if (this.popover?.isOpen()) {\n const anchors = this.popover.getAnchors();\n if (anchors[0]) this.popover.update(this.commentsForElement(anchors[0]));\n }\n return updated;\n }\n\n async clearAll(): Promise<void> {\n const previous = this.comments;\n this.comments = [];\n await this.storage.clear(this.opts.namespace!);\n this.bus.emit('comments:cleared', { previous });\n this.refreshUi();\n this.popover?.close();\n this.clearActiveAnchorOutlines();\n this.highlighter?.setPaused(false);\n }\n\n /* ===== Multi-select API ===== */\n /** Toggle multi-select mode. When on, plain clicks accumulate into the selection. */\n toggleMultiSelectMode(): void {\n if (!this.requireUserOrAuth()) return;\n this.multiSelectMode = !this.multiSelectMode;\n if (!this.multiSelectMode) this.clearSelection();\n this.pill?.setMultiSelectState(this.multiSelectMode, this.pendingFingerprints.length);\n }\n /** Discard the in-progress selection AND exit multi-select mode. */\n clearSelection(): void {\n this.pendingFingerprints = [];\n this.pendingTargets = [];\n this.multiSelectMode = false;\n this.clearActiveAnchorOutlines();\n this.pill?.setMultiSelectState(false, 0);\n }\n /** Open the popover anchored to the current pending selection so the user can write the comment. */\n commitSelection(): void {\n if (this.pendingFingerprints.length === 0) return;\n const targets = [...this.pendingTargets];\n const fps = [...this.pendingFingerprints];\n this.pendingFingerprints = [];\n this.pendingTargets = [];\n this.multiSelectMode = false;\n this.pill?.setMultiSelectState(false, 0);\n // Find an existing element to anchor the popover position\n const target = targets.find((t) => !!t) ?? findElementByFingerprint(fps[0]);\n if (!target) return;\n this.openPopoverFor(targets.filter(Boolean) as Element[], fps);\n }\n getSelection(): ElementFingerprint[] { return [...this.pendingFingerprints]; }\n\n /* ===== Internal ===== */\n private async loadComments() {\n const raw = await this.storage.load(this.opts.namespace!);\n this.comments = migrateComments(raw);\n // If any comment in storage was pre-v2 shape, re-persist the migrated\n // array so the next read doesn't pay the migration cost again.\n if (needsPersist(raw)) {\n void this.storage.setAll(this.opts.namespace!, this.comments).catch(() => {\n // Non-fatal; next mutation will fix it.\n });\n }\n this.bus.emit('comments:loaded', { comments: this.getComments() });\n if (this.enabled) this.refreshUi();\n }\n\n /**\n * Subscribe to remote-change notifications from the active storage\n * adapter (cloud adapters only). Local adapters never emit. The\n * subscription is torn down on `destroy()`.\n */\n private unsubscribeStorage: (() => void) | null = null;\n private subscribeStorage() {\n if (this.unsubscribeStorage) return;\n if (!this.storage.subscribe) return;\n this.unsubscribeStorage = this.storage.subscribe(this.opts.namespace!, (e) => {\n if (e.type === 'added') {\n const fresh = migrateComment(e.comment);\n if (!this.comments.some((c) => c.id === fresh.id)) {\n this.comments.push(fresh);\n this.bus.emit('comment:added', { comment: fresh });\n this.refreshUi();\n }\n } else if (e.type === 'updated') {\n const idx = this.comments.findIndex((c) => c.id === e.comment.id);\n const fresh = migrateComment(e.comment);\n if (idx >= 0) {\n this.comments[idx] = fresh;\n this.bus.emit('comment:updated', { comment: fresh });\n this.refreshUi();\n }\n } else if (e.type === 'removed') {\n const idx = this.comments.findIndex((c) => c.id === e.id);\n if (idx >= 0) {\n const [removed] = this.comments.splice(idx, 1);\n this.bus.emit('comment:removed', { id: e.id, comment: removed });\n this.refreshUi();\n }\n } else if (e.type === 'cleared') {\n const previous = this.comments;\n this.comments = [];\n this.bus.emit('comments:cleared', { previous });\n this.refreshUi();\n }\n });\n }\n\n private deferred(fn: () => void) {\n if (typeof document === 'undefined') return;\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn, { once: true });\n else fn();\n }\n\n private onKeyDown = (e: KeyboardEvent) => {\n if (this.picking && e.key === 'Escape') {\n e.preventDefault();\n this.cancelPicking();\n return;\n }\n // Esc clears in-progress selection\n if (this.pendingFingerprints.length > 0 && e.key === 'Escape' && !this.popover?.isOpen()) {\n e.preventDefault();\n this.clearSelection();\n return;\n }\n if (shortcutMatches(e, this.parsedShortcut)) {\n e.preventDefault();\n const wasEnabled = this.enabled;\n this.toggle();\n // Cloud-mode: shortcut-triggered enable is an explicit user\n // intent, so kick the sign-in popup (or redirect fallback) right\n // away. Auto-init via startEnabled goes through enable() too but\n // skips this branch — it never sees the shortcut.\n if (!wasEnabled && this.enabled && this.storage instanceof CloudStorageAdapter) {\n void this.storage.preflightAuth().catch((err: { code?: string }) => {\n if (err?.code === 'auth_canceled' || err?.code === 'popup_blocked') return;\n if (typeof console !== 'undefined') {\n console.warn('[vizu] preflight auth failed:', err);\n }\n });\n }\n }\n };\n\n private mount() {\n if (this.root) return;\n injectStyles();\n this.root = document.createElement('div');\n this.root.setAttribute('data-vizu-root', '');\n this.root.style.setProperty('--vz-accent', this.opts.accent!);\n document.body.appendChild(this.root);\n\n this.popover = new Popover(this.root, {\n onAdd: (text, fps, refs, mentions, atts) =>\n void this.addCommentFromPopover(text, fps, refs, mentions, atts),\n onUploadAttachment: (file) => this.uploadAttachment(file),\n canUploadAttachments: () => this.canUploadAttachments(),\n onDelete: (id) => void this.removeComment(id),\n onAddReply: (commentId, text) => void this.addReply(commentId, text),\n onDeleteReply: (commentId, replyId) => void this.removeReply(commentId, replyId),\n onClose: () => this.closePopover(),\n onStartReferencePick: (onPicked, onCancel) => this.startPicking(onPicked, onCancel),\n onJumpToReference: (commentId, refId) => this.jumpToReference(commentId, refId),\n onAnchorsChanged: (fps) => this.syncActiveAnchorOutlines(fps),\n onMentionSearch: () => this.searchMentionable(),\n });\n this.popover.setUser(this.user);\n\n this.sidebar = new Sidebar(this.root, {\n onJumpTo: (fp) => this.jumpToFingerprint(fp),\n onJumpToReference: (commentId, refId) => this.jumpToReference(commentId, refId),\n onDelete: (id) => void this.removeComment(id),\n onUpdateStatus: (id, status) => void this.updateComment(id, { status }),\n onClose: () => this.closeSidebar(),\n });\n\n this.pill = new Pill(this.root, {\n onToggleSidebar: () => this.toggleSidebar(),\n onDisable: () => this.disable(),\n onAction: (id) => this.invokeAction(id),\n onToggleMultiSelect: () => this.toggleMultiSelectMode(),\n onCommitSelection: () => this.commitSelection(),\n onClearSelection: () => this.clearSelection(),\n });\n this.pill.setActions(this.actions);\n this.pill.setUser(this.user);\n this.pill.setComments(this.comments);\n this.pill.setMultiSelectState(this.multiSelectMode, this.pendingFingerprints.length);\n // Unconditional show — mount only runs when shouldMount() is true,\n // which in cloud mode means we have a user. No pill-visibility\n // toggle needed elsewhere.\n this.pill.show();\n\n this.highlighter = new Highlighter(\n this.root,\n this.opts.ignoreSelectors ?? [],\n (el, e) => this.onElementClick(el, e),\n );\n this.highlighter.start();\n\n this.renderAllMarkers();\n window.addEventListener('scroll', this.scheduleReposition, true);\n window.addEventListener('resize', this.scheduleReposition);\n this.bus.emit('mounted', {});\n }\n\n private unmount() {\n window.removeEventListener('scroll', this.scheduleReposition, true);\n window.removeEventListener('resize', this.scheduleReposition);\n this.highlighter?.destroy();\n this.popover?.destroy();\n this.pill?.destroy();\n this.sidebar?.destroy();\n this.root?.remove();\n this.root = null;\n this.highlighter = null;\n this.popover = null;\n this.pill = null;\n this.sidebar = null;\n this.markers.clear();\n this.outlines.clear();\n this.activeAnchorOutlines.clear();\n for (const sp of this.spotlights) sp.el.remove();\n this.spotlights.clear();\n this.pendingFingerprints = [];\n this.pendingTargets = [];\n this.bus.emit('unmounted', {});\n }\n\n private onElementClick(el: Element, e: MouseEvent) {\n // Picker mode steals the next click for a # reference\n if (this.picking) {\n const fp = fingerprint(el);\n const p = this.picking;\n this.picking = null;\n this.hidePickingBanner();\n // Restore the popover's paused state once picking ends\n this.highlighter?.setPaused(this.popover?.isOpen() ?? false);\n p.onSelect(fp);\n return;\n }\n\n // Cloud-mode write gate: signed-out users can't open the popover —\n // we reopen the sign-in popup and swallow the click. Existing\n // comment markers stay visible (read access); the user signs in,\n // then clicks again to comment.\n if (!this.requireUserOrAuth()) return;\n\n const fp = fingerprint(el);\n\n // FLOW A: Popover is already open AND user shift-clicks → add another anchor in-flow\n if (this.popover?.isOpen() && e.shiftKey) {\n this.popover.addAnchor(fp, el);\n return;\n }\n\n // FLOW B: multi-select mode OR shift-click without an open popover → accumulate selection\n if (!this.popover?.isOpen() && (this.multiSelectMode || e.shiftKey)) {\n this.addToSelection(fp, el);\n return;\n }\n\n // Default: open popover anchored to this single element\n this.bus.emit('element:selected', { target: el, fingerprint: fp });\n const elComments = this.commentsForElement(fp);\n this.openPopoverFor([el], [fp], elComments);\n }\n\n private openPopoverFor(targets: Element[], fps: ElementFingerprint[], existingComments?: VizuComment[]) {\n if (!this.popover) return;\n // When opening, find existing comments on the primary fingerprint\n const comments = existingComments ?? this.commentsForElement(fps[0]);\n this.popover.open(targets, fps, comments);\n this.syncActiveAnchorOutlines(fps);\n // Lock hover-highlighting while popover is open — no more move tracking unless picking\n this.highlighter?.setPaused(true);\n }\n\n private closePopover() {\n this.popover?.close();\n this.clearActiveAnchorOutlines();\n this.highlighter?.setPaused(false);\n this.bus.emit('element:deselected', {});\n }\n\n private addToSelection(fp: ElementFingerprint, el: Element) {\n const key = fingerprintKey(fp);\n if (this.pendingFingerprints.some((x) => fingerprintKey(x) === key)) {\n // Toggle off: shift-clicking an already-selected element removes it\n this.pendingFingerprints = this.pendingFingerprints.filter((x) => fingerprintKey(x) !== key);\n this.pendingTargets = this.pendingTargets.filter((t) => t !== el);\n } else {\n this.pendingFingerprints.push(fp);\n this.pendingTargets.push(el);\n }\n this.syncActiveAnchorOutlines(this.pendingFingerprints);\n this.pill?.setMultiSelectState(true, this.pendingFingerprints.length);\n // Implicit mode: any selection makes the pill reflect multi-select\n if (this.pendingFingerprints.length > 0) this.multiSelectMode = true;\n else this.multiSelectMode = false;\n }\n\n private commentsForElement(fp: ElementFingerprint): VizuComment[] {\n const key = fingerprintKey(fp);\n return this.comments.filter((c) =>\n (c.fingerprints || []).some((f) => fingerprintKey(f) === key),\n );\n }\n\n private async addCommentFromPopover(\n text: string,\n fps: ElementFingerprint[],\n references?: Record<string, ElementFingerprint>,\n mentions?: string[],\n attachments?: Attachment[],\n ) {\n if (!this.requireUserOrAuth()) return;\n if (fps.length === 0) return;\n const pageUrl = typeof location !== 'undefined' ? location.href : undefined;\n const c: VizuComment = {\n id: uuid(),\n schemaVersion: SCHEMA_VERSION,\n fingerprints: fps,\n text,\n createdAt: Date.now(),\n pageVersion: this.opts.pageVersion,\n pageUrl,\n author: this.user ?? undefined,\n authorId: this.user?.id,\n references: references && Object.keys(references).length ? references : undefined,\n status: 'open',\n replies: [],\n attachments: attachments ?? [],\n mentions: mentions ?? [],\n };\n this.comments.push(c);\n await this.storage.addComment(this.opts.namespace!, c);\n this.bus.emit('comment:added', { comment: c });\n // Refresh popover so the new comment appears in its list\n this.popover?.update(this.commentsForElement(fps[0]));\n this.refreshUi();\n }\n\n private async removeComment(id: string) {\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return;\n this.comments = this.comments.filter((c) => c.id !== id);\n await this.storage.removeComment(this.opts.namespace!, id);\n this.bus.emit('comment:removed', { id, comment });\n if (this.popover?.isOpen()) {\n const anchors = this.popover.getAnchors();\n if (anchors[0]) this.popover.update(this.commentsForElement(anchors[0]));\n }\n this.refreshUi();\n }\n\n private refreshUi() {\n this.pill?.setComments(this.comments);\n // Marker render populates resolvedConfidence; sidebar reads it.\n this.renderAllMarkers();\n this.sidebar?.update(this.comments, this.resolvedConfidence);\n }\n\n /* ===== Sidebar ===== */\n private toggleSidebar() {\n if (!this.sidebar) return;\n // Re-resolve fingerprints against the current DOM before opening so\n // the orphan / drifted state reflects the latest page (host apps\n // mutate the DOM all the time; confidence captured on the previous\n // render may be stale).\n if (!this.sidebar.isOpen()) this.renderAllMarkers();\n this.sidebar.toggle(this.comments, this.resolvedConfidence);\n const open = this.sidebar.isOpen();\n this.pill?.setSidebarOpen(open);\n this.bus.emit(open ? 'sidebar:opened' : 'sidebar:closed', {});\n }\n private closeSidebar() {\n this.sidebar?.close();\n this.pill?.setSidebarOpen(false);\n this.bus.emit('sidebar:closed', {});\n }\n\n /* ===== Picking ===== */\n startPicking(onSelect: (fp: ElementFingerprint) => void, onCancel: () => void): void {\n if (this.picking) this.picking.onCancel();\n this.picking = { onSelect, onCancel };\n // Resume hover-tracking during picking even if popover is open\n this.highlighter?.setPaused(false);\n this.showPickingBanner();\n }\n cancelPicking(): void {\n if (!this.picking) return;\n const p = this.picking;\n this.picking = null;\n this.hidePickingBanner();\n this.highlighter?.setPaused(this.popover?.isOpen() ?? false);\n p.onCancel();\n }\n private showPickingBanner() {\n if (!this.root) return;\n this.pickingBanner = document.createElement('div');\n this.pickingBanner.className = 'vz-picking-banner';\n this.pickingBanner.innerHTML = `\n <span>Click any element to reference</span>\n <span class=\"vz-picking-banner-hint\">or press <span class=\"vz-picking-banner-kbd\">Esc</span></span>\n <button class=\"vz-picking-banner-cancel\" data-vz=\"cancel-pick\">Cancel</button>\n `;\n this.pickingBanner.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('[data-vz=\"cancel-pick\"]')) this.cancelPicking();\n });\n this.root.appendChild(this.pickingBanner);\n }\n private hidePickingBanner() {\n this.pickingBanner?.remove();\n this.pickingBanner = null;\n }\n private jumpToReference(commentId: string, refId: string) {\n const c = this.comments.find((x) => x.id === commentId);\n const fp = c?.references?.[refId];\n if (fp) this.jumpToFingerprint(fp);\n }\n jumpToFingerprint(fp: ElementFingerprint): void {\n const target = findElementByFingerprint(fp);\n if (!target) {\n this.pill?.toast('Element not found on this page');\n return;\n }\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n\n // Always render a spotlight at the destination — works even for elements\n // that have no saved comments (e.g., elements linked via # reference).\n if (this.root) {\n const spotlight = document.createElement('div');\n spotlight.className = 'vz-jump-spotlight';\n this.root.appendChild(spotlight);\n this.positionOutline(spotlight, target);\n const entry = { el: spotlight, target };\n this.spotlights.add(entry);\n setTimeout(() => {\n spotlight.remove();\n this.spotlights.delete(entry);\n }, 1800);\n }\n\n // Additionally pulse the saved-comment outline if the element has one\n const key = fingerprintKey(fp);\n const outline = this.outlines.get(key)?.outline;\n if (outline) {\n // Wait for scroll to settle so the pulse is visible\n setTimeout(() => {\n outline.classList.remove('is-pulsing');\n void outline.offsetWidth;\n outline.classList.add('is-pulsing');\n setTimeout(() => outline.classList.remove('is-pulsing'), 1500);\n }, 250);\n }\n }\n\n /* ===== Marker / outline rendering ===== */\n private renderAllMarkers() {\n if (!this.root) return;\n\n // Tear down existing\n for (const { marker } of this.markers.values()) marker.remove();\n for (const { outline } of this.outlines.values()) outline.remove();\n this.markers.clear();\n this.outlines.clear();\n\n // Build: per element fpKey → all comment IDs that anchor to it, plus\n // the resolution confidence for that anchor (best wins per element).\n interface PerElement {\n fp: ElementFingerprint;\n target: Element | null;\n confidence: MatchConfidence;\n commentIds: string[];\n }\n const byElement = new Map<string, PerElement>();\n // Per-comment best confidence across all its anchors.\n const perComment = new Map<string, MatchConfidence>();\n const rankConf = (c: MatchConfidence): number =>\n c === 'exact' ? 3 : c === 'likely' ? 2 : c === 'drifted' ? 1 : 0;\n const bestConf = (a: MatchConfidence | undefined, b: MatchConfidence): MatchConfidence =>\n a === undefined ? b : rankConf(b) > rankConf(a) ? b : a;\n\n for (const c of this.comments) {\n for (const fp of c.fingerprints || []) {\n const key = fingerprintKey(fp);\n let bucket = byElement.get(key);\n if (!bucket) {\n const match = findByFingerprint(fp);\n bucket = {\n fp,\n target: match.element,\n confidence: match.confidence,\n commentIds: [],\n };\n byElement.set(key, bucket);\n }\n bucket.commentIds.push(c.id);\n perComment.set(c.id, bestConf(perComment.get(c.id), bucket.confidence));\n }\n }\n\n // Commit per-comment confidence + emit a one-shot event each render.\n this.resolvedConfidence.clear();\n for (const c of this.comments) {\n const conf = perComment.get(c.id) ?? 'orphaned';\n this.resolvedConfidence.set(c.id, conf);\n // Only emit once per comment per render.\n const firstFp = (c.fingerprints || [])[0];\n const elForEvent = firstFp ? byElement.get(fingerprintKey(firstFp))?.target ?? null : null;\n this.bus.emit('anchor:resolved', { comment: c, confidence: conf, element: elForEvent });\n }\n\n // Self-heal: any comment that landed `drifted` with live elements\n // gets its fingerprints rewritten in place. The next load resolves\n // those anchors via the cleaner rungs (data-vizu-key / id / class\n // signature / selector) instead of the fuzzy fallback. Fire-and-forget\n // — failures don't block the render and the next page load retries.\n for (const c of this.comments) {\n if (this.selfHealedThisSession.has(c.id)) continue;\n if (this.resolvedConfidence.get(c.id) !== 'drifted') continue;\n void this.selfHealComment(c, byElement);\n }\n\n for (const [key, info] of byElement) {\n // Skip rendering marker + outline for orphaned anchors — there's no\n // element to position against. Those comments still appear in the\n // sidebar's orphan section.\n if (info.confidence === 'orphaned' || !info.target) continue;\n\n // One outline per element\n const outlineEl = document.createElement('div');\n outlineEl.className = 'vz-comment-outline';\n if (info.confidence === 'drifted') outlineEl.classList.add('vz-comment-outline-drifted');\n this.root.appendChild(outlineEl);\n this.outlines.set(key, { outline: outlineEl, target: info.target, fp: info.fp });\n\n // One marker per element. Label rule:\n // - 1 comment → empty flag (just the Vizu-colored pin shape)\n // - N comments → \"N\" (count)\n // Multi-anchor relationships (one comment touching multiple elements) are\n // surfaced via hover-pulse on siblings + the sidebar grouping.\n const marker = document.createElement('button');\n marker.className = 'vz-marker';\n if (info.confidence === 'drifted') marker.classList.add('vz-marker-drifted');\n marker.setAttribute('data-element-key', key);\n marker.setAttribute('data-confidence', info.confidence);\n const total = info.commentIds.length;\n if (total > 1) {\n marker.textContent = String(total);\n marker.title = total + ' comments';\n } else {\n marker.textContent = '';\n const c = this.comments.find((c) => c.id === info.commentIds[0]);\n marker.title = c?.author ? 'Comment by ' + c.author.name : 'Comment';\n }\n marker.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (info.target) {\n const elComments = this.commentsForElement(info.fp);\n this.openPopoverFor([info.target], [info.fp], elComments);\n }\n });\n marker.addEventListener('mouseenter', () => this.pulseRelatedElements(info.commentIds, key));\n this.root.appendChild(marker);\n\n this.markers.set(key, { marker, target: info.target, fp: info.fp, commentIds: info.commentIds });\n\n if (info.target) {\n this.positionMarker(marker, info.target);\n this.positionOutline(outlineEl, info.target);\n } else {\n marker.style.display = 'none';\n outlineEl.style.display = 'none';\n }\n }\n }\n\n /** Pulse outlines of OTHER elements that share a multi-anchor comment with this one. */\n private pulseRelatedElements(commentIds: string[], selfKey: string) {\n const relatedKeys = new Set<string>();\n for (const cid of commentIds) {\n const c = this.comments.find((c) => c.id === cid);\n if (!c) continue;\n const fps = c.fingerprints || [];\n if (fps.length <= 1) continue; // single-anchor comment has no siblings\n for (const fp of fps) {\n const k = fingerprintKey(fp);\n if (k !== selfKey) relatedKeys.add(k);\n }\n }\n for (const key of relatedKeys) {\n const outline = this.outlines.get(key)?.outline;\n if (!outline) continue;\n outline.classList.remove('is-pulsing');\n void outline.offsetWidth;\n outline.classList.add('is-pulsing');\n setTimeout(() => outline.classList.remove('is-pulsing'), 900);\n }\n }\n\n private positionOutline(outline: HTMLElement, target: Element) {\n const rect = target.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) { outline.style.display = 'none'; return; }\n outline.style.display = '';\n outline.style.left = rect.left + 'px';\n outline.style.top = rect.top + 'px';\n outline.style.width = rect.width + 'px';\n outline.style.height = rect.height + 'px';\n }\n\n private positionMarker(marker: HTMLElement, target: Element) {\n const rect = target.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) { marker.style.display = 'none'; return; }\n marker.style.display = '';\n marker.style.left = rect.right + 'px';\n marker.style.top = rect.top + 'px';\n }\n\n /* ===== Active anchor outlines (transient, while popover open or multi-select pending) ===== */\n private syncActiveAnchorOutlines(fps: ElementFingerprint[]) {\n if (!this.root) return;\n const wanted = new Set(fps.map((fp) => fingerprintKey(fp)));\n // Remove outlines that are no longer wanted\n for (const [key, info] of this.activeAnchorOutlines) {\n if (!wanted.has(key)) {\n info.el.remove();\n this.activeAnchorOutlines.delete(key);\n }\n }\n // Add missing\n for (const fp of fps) {\n const key = fingerprintKey(fp);\n if (this.activeAnchorOutlines.has(key)) continue;\n const outline = document.createElement('div');\n outline.className = 'vz-comment-outline is-selected-pending';\n this.root.appendChild(outline);\n const target = findElementByFingerprint(fp);\n if (target) this.positionOutline(outline, target);\n else outline.style.display = 'none';\n this.activeAnchorOutlines.set(key, { el: outline, fp });\n }\n }\n private clearActiveAnchorOutlines() {\n for (const info of this.activeAnchorOutlines.values()) info.el.remove();\n this.activeAnchorOutlines.clear();\n }\n\n /* ===== Reposition on scroll/resize ===== */\n private scheduleReposition = () => {\n if (this.rafScheduled) return;\n this.rafScheduled = true;\n requestAnimationFrame(() => {\n this.rafScheduled = false;\n // Outlines + markers (one per element each)\n for (const [, info] of this.outlines) {\n if (info.target) this.positionOutline(info.outline, info.target);\n }\n for (const [, entry] of this.markers) {\n if (entry.target) this.positionMarker(entry.marker, entry.target);\n }\n // Active anchor outlines — reposition every one using its stored fp.\n // (Critical for pending multi-select selections to track elements on scroll.)\n for (const [, info] of this.activeAnchorOutlines) {\n const target = findElementByFingerprint(info.fp);\n if (target) this.positionOutline(info.el, target);\n }\n // Jump spotlights (transient)\n for (const sp of this.spotlights) {\n if (sp.target) this.positionOutline(sp.el, sp.target);\n }\n // Popover stays anchored to its primary target\n this.popover?.reposition();\n });\n };\n\n /* ===== Helpers ===== */\n private actionContext(): ActionContext {\n return {\n comments: this.getComments(),\n pageHtml: this.snapshotHtml(),\n pageUrl: typeof location !== 'undefined' ? location.href : undefined,\n pageVersion: this.opts.pageVersion,\n user: this.user ?? undefined,\n timestamp: Date.now(),\n copyToClipboard: (text) => this.copyToClipboard(text),\n toast: (msg) => this.pill?.toast(msg),\n };\n }\n\n snapshotHtml(): string {\n const clone = document.documentElement.cloneNode(true) as HTMLElement;\n for (const node of clone.querySelectorAll('[data-vizu-root], #vizu-styles')) node.remove();\n const attrs = document.documentElement\n .getAttributeNames()\n .map((n) => ` ${n}=\"${(document.documentElement.getAttribute(n) || '').replace(/\"/g, '&quot;')}\"`)\n .join('');\n return '<!DOCTYPE html>\\n<html' + attrs + '>\\n' + clone.innerHTML + '\\n</html>';\n }\n\n private copyToClipboard(text: string) {\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).catch(() => fallbackCopy(text));\n } else fallbackCopy(text);\n }\n}\n\nfunction fallbackCopy(text: string) {\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n try { document.execCommand('copy'); } catch {}\n ta.remove();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACiEO,IAAM,iBAAiB;;;AC9DvB,SAAS,OAAe;AAC7B,MAAI,OAAO,WAAW,eAAe,gBAAgB,QAAQ;AAC3D,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE;AACrE;AAEO,SAAS,SAAS,QAAiB,UAA2B;AACnE,SAAO,CAAC,CAAC,OAAO,QAAQ,QAAQ;AAClC;AAEO,SAAS,WAAW,GAAsC;AAC/D,MAAI,KAAK,KAAM,QAAO;AACtB,SAAO,OAAO,CAAC,EAAE,QAAQ,YAAY,CAAC,QAAQ,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ,GAAE,EAAE,CAAG;AAC7H;AAEO,SAAS,WAAW,IAAoB;AAC7C,QAAM,IAAI,IAAI,KAAK,EAAE;AACrB,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,OAAO,MAAM;AACnB,MAAI,OAAO,IAAQ,QAAO;AAC1B,MAAI,OAAO,KAAW,QAAO,KAAK,MAAM,OAAO,GAAM,IAAI;AACzD,MAAI,OAAO,MAAY,QAAO,KAAK,MAAM,OAAO,IAAS,IAAI;AAC7D,SAAO,EAAE,mBAAmB,IAAI,MAAM,EAAE,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC;AACvG;AAEO,SAAS,SAAS,MAAyC;AAChE,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,OAAO,IAAI,EACf,MAAM,KAAK,EACX,OAAO,OAAO,EACd,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM,EAAE,CAAC,EAAG,YAAY,CAAC,EAC9B,KAAK,EAAE;AACZ;AAEO,SAAS,WAAW,MAAmC,YAAY,4BAAoC;AAC5G,MAAI,CAAC,KAAM,QAAO,gBAAgB,SAAS;AAI3C,QAAM,cAAc,KAAK,QAAQ,KAAK,MAAM;AAC5C,MAAI,KAAK,WAAW;AAClB,WAAO,gBAAgB,SAAS,eAAe,WAAW,KAAK,SAAS,CAAC,UAAU,WAAW,WAAW,CAAC;AAAA,EAC5G;AACA,SAAO,gBAAgB,SAAS,KAAK,WAAW,SAAS,WAAW,KAAK,GAAG,CAAC;AAC/E;AAGO,SAAS,QAAgB;AAC9B,SAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE;AAC/C;AAGA,IAAM,mBAAmB;AACzB,IAAM,0BAA0B;AAUzB,SAAS,kBAAkB,MAAc,WAA2B;AACzE,QAAM,QAAQ,KAAK,MAAM,uBAAuB;AAChD,SAAO,MACJ,IAAI,CAAC,SAAS;AACb,UAAM,WAAW,KAAK,MAAM,8CAA8C;AAC1E,QAAI,UAAU;AACZ,aAAO,oDAAoD,WAAW,SAAS,CAAC,kBAAkB,WAAW,SAAS,CAAC,CAAC,CAAC,gCAAgC,WAAW,SAAS,CAAC,CAAC,CAAC;AAAA,IAClL;AACA,UAAM,eAAe,KAAK,MAAM,kDAAkD;AAClF,QAAI,cAAc;AAChB,aAAO,+DAA+D,WAAW,aAAa,CAAC,CAAC,CAAC,MAAM,WAAW,aAAa,CAAC,CAAC,CAAC;AAAA,IACpI;AACA,WAAO,WAAW,IAAI;AAAA,EACxB,CAAC,EACA,KAAK,EAAE;AACZ;AAQO,SAAS,gBAAgB,MAAwB;AACtD,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,KAAK,SAAS,gBAAgB,GAAG;AAC/C,QAAI,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,EAAG,KAAI,KAAK,EAAE,CAAC,CAAC;AAAA,EACxC;AACA,SAAO;AACT;AAYO,SAAS,sBAAsB,aAA+C;AACnF,MAAI,CAAC,MAAM,QAAQ,WAAW,KAAK,YAAY,WAAW,EAAG,QAAO;AACpE,QAAM,QAAQ,YACX,IAAI,CAAC,MAAM;AACV,UAAM,UAAU,EAAE,UAAU,WAAW,QAAQ;AAC/C,UAAM,WAAW,EAAE,YAAY,EAAE,IAAI,MAAM,GAAG,EAAE,IAAI,KAAK;AACzD,QAAI,SAAS;AACX,aAAO;AAAA,mDACoC,WAAW,EAAE,GAAG,CAAC,6CAA6C,WAAW,QAAQ,CAAC;AAAA,wBAC7G,WAAW,EAAE,GAAG,CAAC,UAAU,WAAW,QAAQ,CAAC;AAAA;AAAA;AAAA,IAGjE;AACA,WAAO;AAAA,oEACuD,WAAW,EAAE,GAAG,CAAC;AAAA;AAAA,iDAEpC,WAAW,QAAQ,CAAC;AAAA;AAAA;AAAA,EAGjE,CAAC,EACA,KAAK,EAAE;AACV,SAAO,mCAAmC,KAAK;AACjD;AAcO,SAAS,eAAe,KAAuB;AACpD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAI5C,QAAM,OAAY,EAAE,GAAG,IAAI;AAG3B,MAAI,CAAC,MAAM,QAAQ,KAAK,YAAY,KAAK,KAAK,aAAa,WAAW,GAAG;AACvE,QAAI,KAAK,aAAa;AACpB,WAAK,eAAe,CAAC,KAAK,WAAiC;AAAA,IAC7D;AAAA,EACF;AACA,SAAO,KAAK;AAGZ,MAAI,KAAK,OAAO,CAAC,KAAK,SAAS;AAC7B,SAAK,UAAU,KAAK;AAAA,EACtB;AAGA,MAAI,EAAE,YAAY,SAAS,CAAC,cAAc,KAAK,MAAM,EAAG,MAAK,SAAS;AACtE,MAAI,CAAC,MAAM,QAAQ,KAAK,OAAO,EAAG,MAAK,UAAU,CAAC;AAClD,MAAI,CAAC,MAAM,QAAQ,KAAK,WAAW,EAAG,MAAK,cAAc,CAAC;AAC1D,MAAI,CAAC,MAAM,QAAQ,KAAK,QAAQ,EAAG,MAAK,WAAW,CAAC;AAGpD,MAAI,EAAE,6BAA6B,MAAO,MAAK,0BAA0B;AAGzE,OAAK,gBAAgB;AAErB,SAAO;AACT;AAEA,SAAS,cAAc,GAAgC;AACrD,SAAO,MAAM,UAAU,MAAM,cAAc,MAAM;AACnD;AAGO,SAAS,gBAAgB,MAAgC;AAC9D,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO,CAAC;AAClC,QAAM,MAAqB,CAAC;AAC5B,aAAW,OAAO,MAAM;AACtB,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AACrC,QAAI,KAAK,eAAe,GAAG,CAAC;AAAA,EAC9B;AACA,SAAO;AACT;AAQO,SAAS,aAAa,MAA0B;AACrD,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO;AACjC,aAAW,OAAO,MAAM;AACtB,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AACrC,UAAM,IAAI;AACV,QAAI,EAAE,kBAAkB,eAAgB,QAAO;AAC/C,QAAI,iBAAiB,EAAG,QAAO;AAC/B,QAAI,CAAC,MAAM,QAAQ,EAAE,OAAO,EAAG,QAAO;AACtC,QAAI,CAAC,MAAM,QAAQ,EAAE,WAAW,EAAG,QAAO;AAC1C,QAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,EAAG,QAAO;AAAA,EACzC;AACA,SAAO;AACT;;;AC5MA,IAAM,MAAM,CAAC,OAAe,iBAAiB,EAAE;AAUxC,IAAM,sBAAN,MAAoD;AAAA,EACzD,MAAM,KAAK,WAA2C;AACpD,WAAO,UAAU,SAAS;AAAA,EAC5B;AAAA,EAEA,MAAM,WAAW,WAAmB,SAAqC;AACvE,UAAM,OAAO,UAAU,SAAS;AAChC,SAAK,KAAK,OAAO;AACjB,eAAW,WAAW,IAAI;AAAA,EAC5B;AAAA,EAEA,MAAM,cACJ,WACA,IACA,OAC6B;AAC7B,UAAM,OAAO,UAAU,SAAS;AAChC,UAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AAC7C,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,GAAG,OAAO,IAAI,KAAK,GAAG,EAAE,GAAG;AACxD,SAAK,GAAG,IAAI;AACZ,eAAW,WAAW,IAAI;AAC1B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAc,WAAmB,IAA2B;AAChE,UAAM,OAAO,UAAU,SAAS;AAChC,eAAW,WAAW,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AAAA,EACvD;AAAA,EAEA,MAAM,OAAO,WAAmB,UAAwC;AACtE,eAAW,WAAW,QAAQ;AAAA,EAChC;AAAA,EAEA,MAAM,MAAM,WAAkC;AAC5C,QAAI,OAAO,iBAAiB,YAAa;AACzC,iBAAa,WAAW,IAAI,SAAS,CAAC;AAAA,EACxC;AAAA,EAEA,MAAM,SAAS,WAAmB,WAAmB,OAA2C;AAC9F,UAAM,OAAO,UAAU,SAAS;AAChC,UAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,SAAS;AACpD,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,SAAS,CAAC,GAAI,KAAK,GAAG,EAAE,WAAW,CAAC,GAAI,KAAK,EAAE;AAC5E,SAAK,GAAG,IAAI;AACZ,eAAW,WAAW,IAAI;AAC1B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,WAAmB,WAAmB,SAA8C;AACpG,UAAM,OAAO,UAAU,SAAS;AAChC,UAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,SAAS;AACpD,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,OAAO;AAAA,MACX,GAAG,KAAK,GAAG;AAAA,MACX,UAAU,KAAK,GAAG,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,OAAO;AAAA,IACnE;AACA,SAAK,GAAG,IAAI;AACZ,eAAW,WAAW,IAAI;AAC1B,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,WAAkC;AACnD,MAAI,OAAO,iBAAiB,YAAa,QAAO,CAAC;AACjD,QAAM,MAAM,aAAa,QAAQ,IAAI,SAAS,CAAC;AAC/C,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,MAAM,QAAQ,MAAM,IAAI,gBAAgB,MAAM,IAAI,CAAC;AAAA,EAC5D,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,WAAW,WAAmB,UAA+B;AACpE,MAAI,OAAO,iBAAiB,YAAa;AACzC,eAAa,QAAQ,IAAI,SAAS,GAAG,KAAK,UAAU,QAAQ,CAAC;AAC/D;AAOO,IAAM,yBAAN,MAAuD;AAAA,EAAvD;AACL,SAAQ,QAAQ,oBAAI,IAA2B;AAAA;AAAA,EAE/C,MAAM,KAAK,WAA2C;AACpD,WAAO,CAAC,GAAI,KAAK,MAAM,IAAI,SAAS,KAAK,CAAC,CAAE;AAAA,EAC9C;AAAA,EAEA,MAAM,WAAW,WAAmB,SAAqC;AACvE,UAAM,OAAO,KAAK,MAAM,IAAI,SAAS,KAAK,CAAC;AAC3C,SAAK,MAAM,IAAI,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAM,cACJ,WACA,IACA,OAC6B;AAC7B,UAAM,OAAO,KAAK,MAAM,IAAI,SAAS,KAAK,CAAC;AAC3C,UAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AAC7C,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,GAAG,OAAO,IAAI,KAAK,GAAG,EAAE,GAAG;AACxD,UAAM,OAAO,CAAC,GAAG,IAAI;AACrB,SAAK,GAAG,IAAI;AACZ,SAAK,MAAM,IAAI,WAAW,IAAI;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAc,WAAmB,IAA2B;AAChE,UAAM,OAAO,KAAK,MAAM,IAAI,SAAS,KAAK,CAAC;AAC3C,SAAK,MAAM;AAAA,MACT;AAAA,MACA,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,WAAmB,UAAwC;AACtE,SAAK,MAAM,IAAI,WAAW,CAAC,GAAG,QAAQ,CAAC;AAAA,EACzC;AAAA,EAEA,MAAM,MAAM,WAAkC;AAC5C,SAAK,MAAM,OAAO,SAAS;AAAA,EAC7B;AAAA,EAEA,MAAM,SAAS,WAAmB,WAAmB,OAA2C;AAC9F,UAAM,OAAO,KAAK,MAAM,IAAI,SAAS,KAAK,CAAC;AAC3C,UAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,SAAS;AACpD,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,SAAS,CAAC,GAAI,KAAK,GAAG,EAAE,WAAW,CAAC,GAAI,KAAK,EAAE;AAC5E,UAAM,OAAO,CAAC,GAAG,IAAI;AACrB,SAAK,GAAG,IAAI;AACZ,SAAK,MAAM,IAAI,WAAW,IAAI;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,WAAmB,WAAmB,SAA8C;AACpG,UAAM,OAAO,KAAK,MAAM,IAAI,SAAS,KAAK,CAAC;AAC3C,UAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,SAAS;AACpD,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,OAAO;AAAA,MACX,GAAG,KAAK,GAAG;AAAA,MACX,UAAU,KAAK,GAAG,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,OAAO;AAAA,IACnE;AACA,UAAM,OAAO,CAAC,GAAG,IAAI;AACrB,SAAK,GAAG,IAAI;AACZ,SAAK,MAAM,IAAI,WAAW,IAAI;AAC9B,WAAO;AAAA,EACT;AACF;AAMO,IAAM,qBAAN,MAAmD;AAAA,EACxD,MAAM,OAA+B;AACnC,WAAO,CAAC;AAAA,EACV;AAAA,EACA,MAAM,aAA4B;AAAA,EAAC;AAAA,EACnC,MAAM,gBAA6C;AACjD,WAAO;AAAA,EACT;AAAA,EACA,MAAM,gBAA+B;AAAA,EAAC;AAAA,EACtC,MAAM,SAAwB;AAAA,EAAC;AAAA,EAC/B,MAAM,QAAuB;AAAA,EAAC;AAChC;AASO,SAAS,YAAY,GAAmC;AAC7D,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,IAAI;AACV,SACE,OAAO,EAAE,SAAS,cAClB,OAAO,EAAE,SAAS,cAClB,OAAO,EAAE,UAAU,cACnB,OAAO,EAAE,eAAe;AAE5B;AASO,SAAS,cAAc,IAAsC;AAClE,MAAI,CAAC,YAAY,EAAE,EAAG,QAAO;AAC7B,MAAI,SAAS;AACb,QAAM,WAAW,MAAM;AACrB,QAAI,OAAQ;AACZ,aAAS;AACT,QAAI,OAAO,YAAY,aAAa;AAClC,cAAQ;AAAA,QACN;AAAA,MAGF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,KAAK,WAAmB;AAC5B,eAAS;AACT,aAAO,GAAG,KAAK,SAAS;AAAA,IAC1B;AAAA,IACA,MAAM,WAAW,WAAW,SAAS;AACnC,eAAS;AACT,YAAM,OAAO,MAAM,GAAG,KAAK,SAAS;AACpC,WAAK,KAAK,OAAO;AACjB,YAAM,GAAG,KAAK,WAAW,IAAI;AAAA,IAC/B;AAAA,IACA,MAAM,cAAc,WAAW,IAAI,OAAO;AACxC,eAAS;AACT,YAAM,OAAO,MAAM,GAAG,KAAK,SAAS;AACpC,YAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AAC7C,UAAI,QAAQ,GAAI,QAAO;AACvB,YAAM,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,GAAG,OAAO,IAAI,KAAK,GAAG,EAAE,GAAG;AACxD,WAAK,GAAG,IAAI;AACZ,YAAM,GAAG,KAAK,WAAW,IAAI;AAC7B,aAAO;AAAA,IACT;AAAA,IACA,MAAM,cAAc,WAAW,IAAI;AACjC,eAAS;AACT,YAAM,OAAO,MAAM,GAAG,KAAK,SAAS;AACpC,YAAM,GAAG;AAAA,QACP;AAAA,QACA,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,MAChC;AAAA,IACF;AAAA,IACA,MAAM,OAAO,WAAW,UAAU;AAChC,eAAS;AACT,YAAM,GAAG,KAAK,WAAW,QAAQ;AAAA,IACnC;AAAA,IACA,MAAM,MAAM,WAAW;AACrB,YAAM,GAAG,MAAM,SAAS;AAAA,IAC1B;AAAA,EACF;AACF;;;ACpNA,IAAM,kBAAkB;AACxB,IAAM,cAAc;AACpB,IAAM,eAAe;AAEd,IAAM,sBAAN,MAAoD;AAAA,EAyBzD,YAAY,MAA2B;AAnBvC;AAAA,SAAQ,cAAkC;AAE1C;AAAA,SAAQ,cAA2C;AAEnD;AAAA,SAAQ,sBAAqC;AAQ7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,eAAe;AAKvB;AAAA;AAAA;AAAA;AAAA,SAAQ,qBAAoC;AAG1C,SAAK,YAAY,KAAK;AACtB,SAAK,UAAU,KAAK,UAAU,iBAAiB,QAAQ,OAAO,EAAE;AAChE,SAAK,aAAa,KAAK,eAAe;AACtC,SAAK,gBAAgB,KAAK;AAK1B,UAAM,WAAW,KAAK,cAAc;AACpC,QAAI,UAAU;AACZ,WAAK,cAAc;AACnB,WAAK,iBAAiB,QAAQ;AAAA,IAChC,OAAO;AAGL,WAAK,cAAc,KAAK,gBAAgB;AAAA,IAC1C;AACA,QAAI,KAAK,eAAe,CAAC,KAAK,UAAU,KAAK,WAAW,GAAG;AACzD,WAAK,gBAAgB,KAAK,WAAW;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,gBAAoC;AAC1C,QAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,UAAM,OAAO,OAAO,SAAS;AAC7B,QAAI,CAAC,QAAQ,KAAK,SAAS,EAAG,QAAO;AACrC,UAAM,SAAS,IAAI,gBAAgB,KAAK,MAAM,CAAC,CAAC;AAChD,UAAM,UAAU,OAAO,IAAI,WAAW;AACtC,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI;AAKF,YAAM,QAAQ,WAAW,KAAK,KAAK,OAAO,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACnE,YAAM,UAAU,IAAI,YAAY,OAAO,EAAE,OAAO,KAAK;AACrD,YAAM,OAAO,KAAK,MAAM,OAAO;AAQ/B,UAAI,CAAC,KAAK,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,EAAG,QAAO;AACrD,UAAI,KAAK,MAAM,KAAK,UAAW,QAAO;AACtC,YAAM,SAAsB;AAAA,QAC1B,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,MACE,OAAO,KAAK,MAAM,WACd;AAAA,UACE,IAAI,KAAK;AAAA,UACT,MAAM,KAAK;AAAA,UACX,WAAW,OAAO,KAAK,MAAM,WAAW,KAAK,IAAI;AAAA,QACnD,IACA;AAAA,MACR;AAEA,aAAO,OAAO,WAAW;AACzB,YAAM,YAAY,OAAO,SAAS;AAClC,YAAM,UAAU,YAAY,IAAI,SAAS,KAAK;AAC9C,UAAI;AACF,cAAM,SACJ,OAAO,SAAS,WAAW,OAAO,SAAS,SAAS;AACtD,eAAO,QAAQ,aAAa,MAAM,IAAI,MAAM;AAAA,MAC9C,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,gBAA+B;AACnC,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,iBAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,qBAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,oBAAgD;AACpD,QAAI,CAAC,KAAK,eAAe,KAAK,UAAU,KAAK,WAAW,EAAG,QAAO,CAAC;AACnE,QAAI;AACF,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,KAAK,SAAS,qBAAqB,mBAAmB,KAAK,SAAS,IAAI;AAAA,QACxE,EAAE,QAAQ,MAAM;AAAA,MAClB;AACA,UAAI,CAAC,IAAI,GAAI,QAAO,CAAC;AACrB,YAAM,OAAQ,MAAM,KAAK,UAAU,GAAG;AACtC,aAAO,MAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,IACpD,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,KAAK,YAA4C;AAMrD,QAAI,CAAC,KAAK,eAAe,KAAK,UAAU,KAAK,WAAW,GAAG;AAEzD,YAAM,QAAQ,KAAK,gBAAgB;AACnC,UAAI,CAAC,SAAS,KAAK,UAAU,KAAK,EAAG,QAAO,CAAC;AAC7C,WAAK,cAAc;AAAA,IACrB;AAEA,UAAM,MAAqB,CAAC;AAC5B,QAAI,SAAwB;AAC5B,OAAG;AACD,YAAM,MAAM,IAAI,IAAI,KAAK,SAAS,eAAe;AACjD,UAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,UAAI,aAAa,IAAI,SAAS,KAAK;AACnC,UAAI,OAAQ,KAAI,aAAa,IAAI,UAAU,MAAM;AACjD,YAAM,MAAM,MAAM,KAAK,YAAY,IAAI,SAAS,GAAG,EAAE,QAAQ,MAAM,CAAC;AACpE,YAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,UAAI,CAAC,IAAI,GAAI,OAAM,OAAO,IAAI,QAAQ,IAAI;AAC1C,YAAM,OAAO,MAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,WAAW,CAAC;AAC7D,UAAI,KAAK,GAAG,gBAAgB,IAAI,CAAC;AACjC,eAAS,OAAO,KAAK,eAAe,WAAW,KAAK,aAAa;AAAA,IACnE,SAAS;AACT,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,YAAoB,SAAqC;AACxE,UAAM,MAAM,MAAM,KAAK,YAAY,KAAK,SAAS,iBAAiB;AAAA,MAChE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,WAAW,KAAK,WAAW,QAAQ,CAAC;AAAA,IAC7D,CAAC;AACD,UAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,QAAI,CAAC,IAAI,GAAI,OAAM,OAAO,IAAI,QAAQ,IAAI;AAAA,EAC5C;AAAA,EAEA,MAAM,cACJ,YACA,IACA,OAC6B;AAC7B,UAAM,MAAM,IAAI,IAAI,KAAK,SAAS,mBAAmB,mBAAmB,EAAE,CAAC;AAC3E,QAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,UAAM,MAAM,MAAM,KAAK,YAAY,IAAI,SAAS,GAAG;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,KAAK;AAAA,IAC5B,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,QAAI,CAAC,IAAI,GAAI,OAAM,OAAO,IAAI,QAAQ,IAAI;AAC1C,WAAO,eAAe,KAAK,OAAO;AAAA,EACpC;AAAA,EAEA,MAAM,cAAc,YAAoB,IAA2B;AACjE,UAAM,MAAM,IAAI,IAAI,KAAK,SAAS,mBAAmB,mBAAmB,EAAE,CAAC;AAC3E,QAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,UAAM,MAAM,MAAM,KAAK,YAAY,IAAI,SAAS,GAAG,EAAE,QAAQ,SAAS,CAAC;AACvE,QAAI,IAAI,WAAW,IAAK;AACxB,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,YAAM,OAAO,IAAI,QAAQ,IAAI;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,WAAmB,UAAwC;AAGtE,UAAM,KAAK,MAAM,SAAS;AAC1B,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,WAAW,WAAW,CAAC;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,YAAmC;AAC7C,UAAM,MAAM,IAAI,IAAI,KAAK,SAAS,eAAe;AACjD,QAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,QAAI,aAAa,IAAI,OAAO,MAAM;AAClC,UAAM,MAAM,MAAM,KAAK,YAAY,IAAI,SAAS,GAAG,EAAE,QAAQ,SAAS,CAAC;AACvE,QAAI,IAAI,WAAW,IAAK;AACxB,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,YAAM,OAAO,IAAI,QAAQ,IAAI;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,YAAoB,WAAmB,OAA2C;AAC/F,UAAM,MAAM,IAAI,IAAI,KAAK,SAAS,mBAAmB,mBAAmB,SAAS,IAAI,UAAU;AAC/F,QAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,UAAM,MAAM,MAAM,KAAK,YAAY,IAAI,SAAS,GAAG;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,KAAK;AAAA,IAC5B,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,QAAI,CAAC,IAAI,GAAI,OAAM,OAAO,IAAI,QAAQ,IAAI;AAC1C,WAAO,MAAM,UAAU,eAAe,KAAK,OAAO,IAAI;AAAA,EACxD;AAAA,EAEA,MAAM,YAAY,YAAoB,WAAmB,SAA8C;AACrG,UAAM,MAAM,IAAI;AAAA,MACd,KAAK,SACH,mBACA,mBAAmB,SAAS,IAC5B,cACA,mBAAmB,OAAO;AAAA,IAC9B;AACA,QAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAChD,UAAM,MAAM,MAAM,KAAK,YAAY,IAAI,SAAS,GAAG,EAAE,QAAQ,SAAS,CAAC;AACvE,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,YAAM,OAAO,IAAI,QAAQ,IAAI;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,iBAAiB,YAAoB,MAAiC;AAC1E,UAAM,OAAO,IAAI,SAAS;AAC1B,SAAK,OAAO,QAAQ,MAAM,KAAK,IAAI;AAEnC,UAAM,MAAM,IAAI,IAAI,KAAK,SAAS,cAAc;AAChD,QAAI,aAAa,IAAI,aAAa,KAAK,SAAS;AAEhD,UAAM,MAAM,MAAM,KAAK,YAAY,IAAI,SAAS,GAAG;AAAA,MACjD,QAAQ;AAAA,MACR,MAAM;AAAA;AAAA,IAER,CAAC;AACD,UAAM,OAAO,MAAM,KAAK,UAAU,GAAG;AACrC,QAAI,CAAC,IAAI,GAAI,OAAM,OAAO,IAAI,QAAQ,IAAI;AAC1C,QAAI,CAAC,MAAM,OAAO,OAAO,KAAK,QAAQ,UAAU;AAC9C,YAAM,OAAO,KAAK,EAAE,OAAO,iBAAiB,SAAS,gCAAgC,CAAC;AAAA,IACxF;AACA,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,KAAK,KAAK;AAAA,MACV,UAAU,KAAK,QAAQ;AAAA,MACvB,WAAW,KAAK;AAAA,MAChB,YAAY,KAAK,IAAI;AAAA,MACrB,UAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,iBAAqC;AACnC,QAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,WAAO;AAAA,MACL,QAAQ,KAAK,YAAY;AAAA,MACzB,OAAO,KAAK,YAAY;AAAA,MACxB,WAAW,KAAK,YAAY;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAIQ,UAAU,GAAgC;AAChD,QAAI,CAAC,EAAG,QAAO;AACf,UAAM,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ;AAE1C,WAAO,CAAC,OAAO,SAAS,GAAG,KAAK,MAAM,KAAK,IAAI,IAAI;AAAA,EACrD;AAAA,EAEQ,kBAAsC;AAC5C,QAAI,OAAO,mBAAmB,YAAa,QAAO;AAClD,UAAM,MAAM,eAAe,QAAQ,KAAK,WAAW,CAAC;AACpD,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,KAAK,UAAU,MAAM,GAAG;AAC1B,uBAAe,WAAW,KAAK,WAAW,CAAC;AAC3C,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,iBAAiB,GAAsB;AAC7C,SAAK,cAAc;AACnB,QAAI,OAAO,mBAAmB,aAAa;AACzC,qBAAe,QAAQ,KAAK,WAAW,GAAG,KAAK,UAAU,CAAC,CAAC;AAAA,IAC7D;AAAA,EACF;AAAA,EAEQ,mBAAyB;AAC/B,SAAK,cAAc;AACnB,QAAI,OAAO,mBAAmB,aAAa;AACzC,qBAAe,WAAW,KAAK,WAAW,CAAC;AAAA,IAC7C;AAAA,EACF;AAAA,EAEQ,aAAqB;AAM3B,WAAO,uBAAuB,KAAK,SAAS;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,eAAqC;AACjD,QAAI,KAAK,eAAe,CAAC,KAAK,UAAU,KAAK,WAAW,EAAG,QAAO,KAAK;AAEvE,UAAM,QAAQ,KAAK,gBAAgB;AACnC,QAAI,OAAO;AACT,WAAK,cAAc;AACnB,WAAK,gBAAgB,KAAK;AAC1B,aAAO;AAAA,IACT;AACA,QAAI,KAAK,cAAc;AACrB,YAAM;AAAA,QACJ;AAAA,QACA,2BAA2B,KAAK,SAAS,MAAM,KAAK,sBAAsB,WAAW;AAAA,MACvF;AAAA,IACF;AACA,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,cAAc,iBAAiB,2CAA2C;AAAA,IAClF;AACA,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,cAAc,KAAK,gBAAgB,EACrC,KAAK,CAAC,MAAM;AACX,aAAK,iBAAiB,CAAC;AACvB,aAAK,gBAAgB,CAAC;AACtB,eAAO;AAAA,MACT,CAAC,EACA,QAAQ,MAAM;AACb,aAAK,cAAc;AAAA,MACrB,CAAC;AAAA,IACL;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,gBAAgB,OAA0B;AAChD,QAAI,KAAK,wBAAwB,MAAM,MAAO;AAC9C,SAAK,sBAAsB,MAAM;AACjC,QAAI;AACF,WAAK,gBAAgB,EAAE,QAAQ,MAAM,QAAQ,MAAM,MAAM,KAAK,CAAC;AAAA,IACjE,SAAS,KAAK;AACZ,UAAI,OAAO,YAAY,aAAa;AAClC,gBAAQ,KAAK,6CAA6C,GAAG;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,kBAAwC;AAC9C,WAAO,IAAI,QAAqB,CAAC,SAAS,WAAW;AACnD,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,OAAO,cAAc,oBAAoB,yCAAyC,CAAC;AAAA,MAC5F;AACA,YAAM,YAAY,IAAI,IAAI,KAAK,MAAM,EAAE;AACvC,YAAM,aAAa,OAAO,SAAS;AACnC,YAAM,WACJ,KAAK,SACL,wBACA,mBAAmB,KAAK,SAAS,IACjC,aACA,mBAAmB,UAAU;AAI/B,YAAM,OAAO,KAAK,IAAI,GAAG,KAAK,OAAO,OAAO,OAAO,QAAQ,eAAe,CAAC,CAAC;AAC5E,YAAM,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO,OAAO,OAAO,SAAS,gBAAgB,CAAC,CAAC;AAC7E,YAAM,QAAQ,OAAO;AAAA,QACnB;AAAA,QACA;AAAA,QACA,SAAS,WAAW,WAAW,YAAY,SAAS,IAAI,QAAQ,GAAG;AAAA,MACrE;AACA,UAAI,CAAC,OAAO;AAUV,YAAI;AACF,gBAAM,WAAW,OAAO,SAAS;AACjC,gBAAM,cACJ,KAAK,SACL,wBACA,mBAAmB,KAAK,SAAS,IACjC,aACA,mBAAmB,UAAU,IAC7B,gBACA,mBAAmB,QAAQ;AAC7B,iBAAO,SAAS,OAAO,WAAW;AAClC;AAAA,QACF,SAAS,KAAK;AACZ,iBAAO;AAAA,YACL;AAAA,cACE;AAAA,cACA,8DACG,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,YACpD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,WAAW;AACf,YAAM,YAAY,CAAC,MAAoB;AAErC,YAAI,EAAE,WAAW,UAAW;AAC5B,cAAM,OAAO,EAAE;AAWf,YAAI,CAAC,KAAM;AAIX,YAAI,KAAK,SAAS,sBAAsB,KAAK,cAAc,KAAK,WAAW;AACzE,qBAAW;AACX,kBAAQ;AACR,eAAK,eAAe;AACpB,eAAK,qBAAqB,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;AAC1E;AAAA,YACE;AAAA,cACE;AAAA,cACA,2BAA2B,KAAK,SAAS,MAAM,KAAK,kBAAkB;AAAA,YACxE;AAAA,UACF;AACA;AAAA,QACF;AACA,YAAI,KAAK,SAAS,YAAa;AAC/B,YAAI,KAAK,cAAc,KAAK,UAAW;AACvC,YAAI,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,cAAc,YAAY,OAAO,KAAK,WAAW,SAAU;AAC7G,mBAAW;AACX,gBAAQ;AAGR,cAAM,cAAe,KAA0E;AAC/F,cAAM,OACJ,eAAe,OAAO,YAAY,SAAS,WACvC;AAAA,UACE,IAAI,OAAO,YAAY,OAAO,WAAW,YAAY,KAAK,KAAK;AAAA,UAC/D,MAAM,YAAY;AAAA,UAClB,WACE,OAAO,YAAY,cAAc,WAC7B,YAAY,YACZ;AAAA,QACR,IACA;AACN,gBAAQ,EAAE,OAAO,KAAK,OAAO,WAAW,KAAK,WAAW,QAAQ,KAAK,QAAQ,KAAK,CAAC;AAAA,MACrF;AACA,YAAM,SAAS,OAAO,YAAY,MAAM;AACtC,YAAI,MAAM,QAAQ;AAChB,cAAI,CAAC,UAAU;AACb,oBAAQ;AACR,mBAAO,cAAc,iBAAiB,6CAA6C,CAAC;AAAA,UACtF;AAAA,QACF;AAAA,MACF,GAAG,GAAG;AACN,eAAS,UAAU;AACjB,eAAO,cAAc,MAAM;AAC3B,eAAO,oBAAoB,WAAW,SAAS;AAAA,MACjD;AACA,aAAO,iBAAiB,WAAW,SAAS;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,YAAY,KAAaA,OAAmB,UAAU,OAA0B;AAC5F,UAAM,QAAQ,MAAM,KAAK,aAAa;AACtC,UAAM,UAAU,IAAI,QAAQA,MAAK,OAAO;AACxC,YAAQ,IAAI,iBAAiB,UAAU,MAAM,KAAK,EAAE;AACpD,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,GAAGA,OAAM,SAAS,aAAa,OAAO,CAAC;AACtE,QAAI,IAAI,WAAW,OAAO,CAAC,SAAS;AAClC,WAAK,iBAAiB;AACtB,aAAO,KAAK,YAAY,KAAKA,OAAM,IAAI;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,UAAU,KAA6B;AACnD,QAAI;AACF,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO,OAAO,KAAK,MAAM,IAAI,IAAI;AAAA,IACnC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAqBA,SAAS,cAAc,MAAsB,SAA6B;AACxE,QAAM,MAAM,IAAI,MAAM,OAAO;AAC7B,MAAI,OAAO;AACX,SAAO;AACT;AACA,SAAS,OAAO,QAAgB,MAAuB;AACrD,QAAM,OACJ,MAAM,UAAU,uBACZ,uBACA,MAAM,UAAU,cAChB,wBACA;AACN,QAAM,MAAM,IAAI,MAAM,MAAM,WAAW,QAAQ,MAAM,EAAE;AACvD,MAAI,OAAO;AACX,MAAI,SAAS;AACb,MAAI,OAAO;AACX,SAAO;AACT;;;AC9qBA,IAAM,mBAAmB;AAOzB,IAAM,iBAAiB;AAUvB,IAAM,qBAAqB;AAU3B,IAAM,uBAAuB;AAC7B,IAAM,0BAA0B;AAEzB,SAAS,cAAc,IAAqB;AACjD,QAAM,QAAkB,CAAC;AACzB,MAAI,UAA0B;AAC9B,MAAI,QAAQ;AACZ,SAAO,WAAW,YAAY,SAAS,mBAAmB,QAAQ,GAAG;AACnE,QAAI,OAAO,QAAQ,QAAQ,YAAY;AACvC,QAAI,QAAQ,IAAI;AACd,YAAM,QAAQ,MAAM,IAAI,OAAO,QAAQ,EAAE,CAAC;AAC1C;AAAA,IACF;AACA,UAAM,MAAM,MAAM,KAAK,QAAQ,SAAS,EACrC,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,KAAK,CAAC,EAAE,WAAW,OAAO,CAAC,EAC5D,MAAM,GAAG,CAAC;AACb,QAAI,IAAI,OAAQ,SAAQ,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,GAAG;AACpE,UAAM,SAAyB,QAAQ;AACvC,QAAI,QAAQ;AACV,YAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,QAAS,OAAO;AACxF,UAAI,QAAQ,SAAS,GAAG;AACtB,cAAM,MAAM,QAAQ,QAAQ,OAAO;AACnC,gBAAQ,mBAAmB,MAAM,KAAK;AAAA,MACxC;AAAA,IACF;AACA,UAAM,QAAQ,IAAI;AAClB,cAAU;AACV;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEO,SAAS,YAAY,IAAiC;AAC3D,QAAM,OAAQ,GAAmB,aAAa,GAAG,eAAe;AAChE,QAAM,SAAS,GAAG;AAClB,MAAI,eAAe;AACnB,MAAI,QAAQ;AACV,mBAAe,MAAM,KAAK,OAAO,QAAQ,EAAE,QAAQ,EAAE;AAAA,EACvD;AACA,SAAO;AAAA,IACL,UAAU,cAAc,EAAE;AAAA,IAC1B,gBAAgB,SAAS,cAAc,MAAM,IAAI;AAAA,IACjD,SAAS,GAAG;AAAA,IACZ,aAAa,KAAK,KAAK,EAAE,MAAM,GAAG,gBAAgB;AAAA,IAClD;AAAA,IACA,YAAY;AAAA,MACV,IAAI,GAAG,MAAM;AAAA,MACb,WAAW,MAAM,KAAK,GAAG,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,KAAK,CAAC,EAAE,WAAW,OAAO,CAAC;AAAA,MAChG,MAAM,GAAG,aAAa,MAAM,KAAK;AAAA,MACjC,WAAW,GAAG,aAAa,YAAY,KAAK;AAAA,MAC5C,SAAS,GAAG,aAAa,eAAe,KAAK;AAAA,IAC/C;AAAA,IACA,eAAe,qBAAqB,IAAI,cAAc;AAAA,IACtD,kBAAkB;AAAA,EACpB;AACF;AAcO,SAAS,qBACd,IACA,OACuC;AACvC,QAAM,QAAwB,CAAC;AAC/B,MAAI,UAA0B,GAAG;AACjC,SAAO,WAAW,MAAM,SAAS,OAAO;AACtC,UAAM,SAAyB,QAAQ;AACvC,QAAI,YAAY;AAChB,QAAI,QAAQ;AACV,YAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,QAAS,OAAO;AACxF,kBAAY,QAAQ,QAAQ,OAAO,IAAI;AAAA,IACzC;AACA,UAAM,KAAK,EAAE,KAAK,QAAQ,SAAS,UAAU,CAAC;AAC9C,cAAU;AAAA,EACZ;AACA,SAAO,MAAM,SAAS,IAAI,EAAE,MAAM,IAAI;AACxC;AA8BO,SAAS,kBAAkB,IAAwB,OAAmB,UAA4B;AAEvG,MAAI,GAAG,WAAW,SAAS;AACzB,QAAI;AACF,YAAM,QAAQ,KAAK,cAAc,mBAAmB,IAAI,OAAO,GAAG,WAAW,OAAO,CAAC,IAAI;AACzF,UAAI,MAAO,QAAO,EAAE,SAAS,OAAO,YAAY,QAAQ;AAAA,IAC1D,QAAQ;AAAA,IAAC;AAIT,WAAO,EAAE,SAAS,MAAM,YAAY,WAAW;AAAA,EACjD;AAGA,MAAI,GAAG,WAAW,IAAI;AACpB,QAAI;AACF,YAAM,OAAO,KAAK,cAAc,MAAM,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;AAClE,UAAI,KAAM,QAAO,EAAE,SAAS,MAAM,YAAY,QAAQ;AAAA,IACxD,QAAQ;AAAA,IAAC;AAAA,EAGX;AAIA,QAAM,aAAiD,CAAC;AAExD,QAAM,UAAU,GAAG,WAAW;AAC9B,MAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,UAAM,IAAI,qBAAqB,MAAM,GAAG,SAAS,OAAO;AACxD,QAAI,GAAG,QAAS,YAAW,KAAK,EAAE,IAAI,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EAClE;AAEA,MAAI;AACF,UAAM,aAAa,KAAK,cAAc,GAAG,QAAQ;AACjD,QAAI,WAAY,YAAW,KAAK,EAAE,IAAI,YAAY,MAAM,WAAW,CAAC;AAAA,EACtE,QAAQ;AAAA,EAAC;AAET,MAAI,GAAG,iBAAiB,GAAG,cAAc,MAAM,SAAS,GAAG;AACzD,UAAM,IAAI,oBAAoB,MAAM,GAAG,SAAS,GAAG,cAAc,KAAK;AACtE,QAAI,GAAG,QAAS,YAAW,KAAK,EAAE,IAAI,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EAClE,OAAO;AAEL,QAAI;AACF,YAAM,SAAS,GAAG,iBACb,KAAK,cAAc,GAAG,cAAc,IACpC;AACL,UAAI,QAAQ;AACV,cAAM,YAAY,OAAO,SAAS,GAAG,YAAY;AACjD,YAAI,aAAa,UAAU,YAAY,GAAG,SAAS;AACjD,qBAAW,KAAK,EAAE,IAAI,WAAW,MAAM,QAAQ,CAAC;AAAA,QAClD;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,MAAI,GAAG,aAAa;AAClB,UAAM,IAAI,kBAAkB,MAAM,GAAG,SAAS,GAAG,WAAW;AAC5D,QAAI,GAAG,QAAS,YAAW,KAAK,EAAE,IAAI,EAAE,SAAS,MAAM,OAAO,CAAC;AAAA,EACjE;AAEA,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,EAAE,SAAS,MAAM,YAAY,WAAW;AAAA,EACjD;AAIA,QAAM,QAAQ,oBAAI,IAAwB;AAC1C,aAAW,KAAK,YAAY;AAC1B,QAAI,IAAI,MAAM,IAAI,EAAE,EAAE;AACtB,QAAI,CAAC,GAAG;AACN,UAAI,oBAAI,IAAU;AAClB,YAAM,IAAI,EAAE,IAAI,CAAC;AAAA,IACnB;AACA,MAAE,IAAI,EAAE,IAAI;AAAA,EACd;AAEA,MAAI,OAAiD;AACrD,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO;AAC/B,QAAI,CAAC,QAAQ,MAAM,OAAO,KAAK,MAAM,KAAM,QAAO,EAAE,IAAI,MAAM;AAAA,EAChE;AAEA,MAAI,CAAC,KAAM,QAAO,EAAE,SAAS,MAAM,YAAY,WAAW;AAC1D,MAAI,KAAK,MAAM,QAAQ,EAAG,QAAO,EAAE,SAAS,KAAK,IAAI,YAAY,SAAS;AAC1E,MAAI,KAAK,MAAM,QAAQ,EAAG,QAAO,EAAE,SAAS,KAAK,IAAI,YAAY,UAAU;AAM3E,MAAI,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,MAAM,IAAI,OAAO,GAAG;AACtD,WAAO,EAAE,SAAS,KAAK,IAAI,YAAY,UAAU;AAAA,EACnD;AACA,SAAO,EAAE,SAAS,MAAM,YAAY,WAAW;AACjD;AAYA,SAAS,qBACP,MACA,SACA,QACyB;AACzB,QAAM,YAAY,IAAI,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,KAAK,CAAC,EAAE,WAAW,OAAO,CAAC,CAAC;AAC9F,MAAI,UAAU,SAAS,EAAG,QAAO;AACjC,QAAM,MAAM,KAAK,iBAAiB,QAAQ,YAAY,CAAC;AACvD,MAAI,OAA8C;AAClD,MAAI,cAAc;AAClB,aAAW,MAAM,KAAK;AACpB,UAAM,WAAW,IAAI;AAAA,MACnB,MAAM,KAAK,GAAG,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,KAAK,CAAC,EAAE,WAAW,OAAO,CAAC;AAAA,IACvF;AACA,QAAI,SAAS,SAAS,EAAG;AACzB,UAAM,QAAQ,kBAAkB,WAAW,QAAQ;AACnD,QAAI,QAAQ,wBAAyB;AACrC,QAAI,CAAC,QAAQ,QAAQ,KAAK,OAAO;AAC/B,oBAAc,MAAM,SAAS;AAC7B,aAAO,EAAE,IAAI,MAAM;AAAA,IACrB,WAAW,QAAQ,aAAa;AAC9B,oBAAc;AAAA,IAChB;AAAA,EACF;AACA,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,QAAQ,cAAc,IAAK,QAAO;AAC3C,SAAO,EAAE,SAAS,KAAK,IAAI,YAAY,SAAS;AAClD;AASA,SAAS,oBACP,MACA,SACA,OACyB;AACzB,QAAM,MAAM,KAAK,iBAAiB,QAAQ,YAAY,CAAC;AACvD,MAAI,OAA8C;AAClD,aAAW,MAAM,KAAK;AACpB,UAAM,YAAY,qBAAqB,IAAI,MAAM,SAAS,kBAAkB;AAC5E,UAAM,QAAQ,2BAA2B,WAAW,OAAO,kBAAkB;AAC7E,QAAI,QAAQ,EAAG;AACf,QAAI,CAAC,QAAQ,QAAQ,KAAK,MAAO,QAAO,EAAE,IAAI,MAAM;AAAA,EACtD;AACA,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,SAAS,KAAK;AAAA,IACd,YAAY,KAAK,UAAU,MAAM,SAAS,WAAW;AAAA,EACvD;AACF;AAQA,SAAS,qBAAqB,IAAa,OAA+B;AACxE,QAAM,QAAwB,CAAC;AAC/B,MAAI,UAA0B,GAAG;AACjC,SAAO,WAAW,MAAM,SAAS,OAAO;AACtC,UAAM,SAAyB,QAAQ;AACvC,QAAI,MAAM;AACV,QAAI,QAAQ;AACV,YAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,QAAS,OAAO;AACxF,YAAM,QAAQ,QAAQ,OAAO,IAAI;AAAA,IACnC;AACA,UAAM,KAAK,EAAE,KAAK,QAAQ,SAAS,WAAW,IAAI,CAAC;AACnD,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAQA,SAAS,kBACP,MACA,SACA,SACyB;AACzB,QAAM,SAAS,cAAc,OAAO;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,MAAM,KAAK,iBAAiB,QAAQ,YAAY,CAAC;AACvD,MAAI,OAA8C;AAClD,aAAW,MAAM,KAAK;AACpB,UAAM,MAAO,GAAmB,aAAa,GAAG,eAAe;AAC/D,UAAM,YAAY,cAAc,IAAI,MAAM,GAAG,mBAAmB,CAAC,CAAC;AAClE,QAAI,CAAC,UAAW;AAChB,UAAM,QAAQ,iBAAiB,QAAQ,SAAS;AAChD,QAAI,QAAQ,qBAAsB;AAClC,QAAI,CAAC,QAAQ,QAAQ,KAAK,OAAO;AAC/B,aAAO,EAAE,IAAI,MAAM;AAAA,IACrB;AAAA,EACF;AACA,SAAO,OAAO,EAAE,SAAS,KAAK,IAAI,YAAY,UAAU,IAAI;AAC9D;AAUO,SAAS,cAAc,OAAuB;AACnD,SAAO,MAAM,YAAY,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACvD;AAUO,SAAS,iBAAiB,GAAW,GAAmB;AAC7D,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO,EAAE,WAAW,EAAE,SAAS,IAAI;AACzE,QAAM,SAAS,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AAC1C,QAAM,WAAW,oBAAoB,GAAG,CAAC;AACzC,SAAO,IAAI,WAAW;AACxB;AAEA,SAAS,oBAAoB,GAAW,GAAmB;AAEzD,MAAI,EAAE,SAAS,EAAE,QAAQ;AACvB,UAAM,MAAM;AACZ,QAAI;AACJ,QAAI;AAAA,EACN;AACA,QAAM,OAAO,IAAI,MAAc,EAAE,SAAS,CAAC;AAC3C,QAAM,OAAO,IAAI,MAAc,EAAE,SAAS,CAAC;AAC3C,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,IAAK,MAAK,CAAC,IAAI;AAC9C,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,SAAK,CAAC,IAAI;AACV,aAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,YAAM,OAAO,EAAE,WAAW,IAAI,CAAC,MAAM,EAAE,WAAW,IAAI,CAAC,IAAI,IAAI;AAC/D,WAAK,CAAC,IAAI,KAAK;AAAA,QACb,KAAK,IAAI,CAAC,IAAI;AAAA;AAAA,QACd,KAAK,CAAC,IAAI;AAAA;AAAA,QACV,KAAK,IAAI,CAAC,IAAI;AAAA;AAAA,MAChB;AAAA,IACF;AACA,aAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,IAAK,MAAK,CAAC,IAAI,KAAK,CAAC;AAAA,EACtD;AACA,SAAO,KAAK,EAAE,MAAM;AACtB;AAaO,SAAS,2BACd,QACA,UACA,UACQ;AACR,MAAI,OAAO;AACX,WAAS,SAAS,GAAG,UAAU,UAAU,UAAU;AACjD,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,IAAI,OAAO,IAAI,MAAM;AAC3B,UAAI,CAAC,EAAG;AACR,UAAI,EAAE,QAAQ,SAAS,CAAC,EAAE,OAAO,EAAE,cAAc,SAAS,CAAC,EAAE,WAAW;AACtE;AAAA,MACF;AAAA,IACF;AACA,QAAI,QAAQ,KAAM,QAAO;AAAA,EAC3B;AACA,SAAO;AACT;AAOO,SAAS,kBAAqB,GAAW,GAAmB;AACjE,MAAI,EAAE,SAAS,KAAK,EAAE,SAAS,EAAG,QAAO;AACzC,MAAI,eAAe;AACnB,aAAW,QAAQ,EAAG,KAAI,EAAE,IAAI,IAAI,EAAG;AACvC,QAAM,QAAQ,EAAE,OAAO,EAAE,OAAO;AAChC,SAAO,UAAU,IAAI,IAAI,eAAe;AAC1C;AAGO,SAAS,yBAAyB,IAAwB,MAAmC;AAClG,SAAO,kBAAkB,IAAI,IAAI,EAAE;AACrC;AAEO,SAAS,eAAe,IAAgC;AAC7D,SAAO,GAAG,WAAW,MAAM,GAAG;AAChC;AAGO,SAAS,iBAAiB,IAAgC;AAC/D,QAAM,MAAM,GAAG,QAAQ,YAAY;AACnC,MAAI,GAAG,aAAa;AAClB,UAAM,UAAU,GAAG,YAAY,SAAS,KAAK,GAAG,YAAY,MAAM,GAAG,EAAE,IAAI,WAAM,GAAG;AACpF,WAAO,MAAM,OAAO;AAAA,EACtB;AACA,MAAI,GAAG,WAAW,GAAI,QAAO,MAAM,MAAM,GAAG,WAAW;AACvD,MAAI,GAAG,WAAW,aAAa,GAAG,WAAW,UAAU,OAAQ,QAAO,MAAM,MAAM,GAAG,WAAW,UAAU,CAAC;AAC3G,SAAO;AACT;;;ACvdA,IAAM,kBAAkB;AAEjB,IAAM,cAAN,MAAkB;AAAA,EASvB,YACE,MACA,aACA,SACA;AAVF,SAAQ,UAA0B;AAGlC,SAAQ,SAAS;AACjB,SAAQ,SAAS;AAoDjB,SAAQ,aAAa,CAAC,MAAkB;AACtC,UAAI,KAAK,QAAQ;AAEf,aAAK,WAAW,IAAI;AACpB,aAAK,QAAQ,MAAM,UAAU;AAC7B;AAAA,MACF;AACA,YAAM,SAAS,SAAS,iBAAiB,EAAE,SAAS,EAAE,OAAO;AAC7D,UAAI,CAAC,UAAU,WAAW,KAAK,QAAS;AACxC,UAAI,KAAK,aAAa,MAAM,GAAG;AAC7B,aAAK,WAAW,IAAI;AACpB,aAAK,QAAQ,MAAM,UAAU;AAC7B;AAAA,MACF;AACA,WAAK,WAAW,MAAM;AACtB,WAAK,cAAc;AAAA,IACrB;AAEA,SAAQ,cAAc,CAAC,MAAkB;AACvC,YAAM,SAAS,SAAS,iBAAiB,EAAE,SAAS,EAAE,OAAO;AAC7D,UAAI,CAAC,OAAQ;AACb,UAAI,KAAK,aAAa,MAAM,EAAG;AAC/B,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,WAAK,QAAQ,QAAQ,CAAC;AAAA,IACxB;AAYA,SAAQ,eAAe,MAAM;AAC3B,UAAI,CAAC,KAAK,QAAS;AACnB,WAAK,cAAc;AAAA,IACrB;AArFE,SAAK,OAAO;AACZ,SAAK,cAAc;AACnB,SAAK,UAAU;AACf,SAAK,UAAU,SAAS,cAAc,KAAK;AAC3C,SAAK,QAAQ,YAAY;AACzB,SAAK,QAAQ,MAAM,UAAU;AAC7B,SAAK,KAAK,YAAY,KAAK,OAAO;AAAA,EACpC;AAAA,EAEQ,WAAW,IAA0B;AAC3C,QAAI,KAAK,YAAY,GAAI;AACzB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAQ;AACN,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,aAAS,iBAAiB,aAAa,KAAK,YAAY,IAAI;AAC5D,aAAS,iBAAiB,SAAS,KAAK,aAAa,IAAI;AACzD,aAAS,iBAAiB,UAAU,KAAK,cAAc,IAAI;AAC3D,WAAO,iBAAiB,UAAU,KAAK,YAAY;AAAA,EACrD;AAAA,EAEA,OAAO;AACL,QAAI,CAAC,KAAK,OAAQ;AAClB,SAAK,SAAS;AACd,aAAS,oBAAoB,aAAa,KAAK,YAAY,IAAI;AAC/D,aAAS,oBAAoB,SAAS,KAAK,aAAa,IAAI;AAC5D,aAAS,oBAAoB,UAAU,KAAK,cAAc,IAAI;AAC9D,WAAO,oBAAoB,UAAU,KAAK,YAAY;AACtD,SAAK,QAAQ,MAAM,UAAU;AAC7B,SAAK,WAAW,IAAI;AAAA,EACtB;AAAA,EAEQ,aAAa,IAAsB;AACzC,QAAI,OAAO,SAAS,mBAAmB,OAAO,SAAS,KAAM,QAAO;AACpE,QAAI,SAAS,IAAI,eAAe,EAAG,QAAO;AAC1C,eAAW,OAAO,KAAK,aAAa;AAClC,UAAI;AACF,YAAI,SAAS,IAAI,GAAG,EAAG,QAAO;AAAA,MAChC,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,WAAO;AAAA,EACT;AAAA,EA6BA,UAAU,QAAiB;AACzB,SAAK,SAAS;AACd,QAAI,QAAQ;AACV,WAAK,WAAW,IAAI;AACpB,WAAK,QAAQ,MAAM,UAAU;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,WAAoB;AAAE,WAAO,KAAK;AAAA,EAAQ;AAAA,EAOlC,gBAAgB;AACtB,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,OAAO,KAAK,QAAQ,sBAAsB;AAChD,QAAI,KAAK,UAAU,KAAK,KAAK,WAAW,GAAG;AACzC,WAAK,QAAQ,MAAM,UAAU;AAC7B;AAAA,IACF;AACA,SAAK,QAAQ,MAAM,UAAU;AAC7B,SAAK,QAAQ,MAAM,OAAO,KAAK,OAAO;AACtC,SAAK,QAAQ,MAAM,MAAM,KAAK,MAAM;AACpC,SAAK,QAAQ,MAAM,QAAQ,KAAK,QAAQ;AACxC,SAAK,QAAQ,MAAM,SAAS,KAAK,SAAS;AAAA,EAC5C;AAAA,EAEA,UAAU;AACR,SAAK,KAAK;AACV,SAAK,QAAQ,OAAO;AAAA,EACtB;AACF;;;AC5GA,SAAS,UAAU,GAAmB;AACpC,MAAI,OAAO,QAAQ,eAAe,OAAO,IAAI,WAAW,WAAY,QAAO,IAAI,OAAO,CAAC;AACvF,SAAO,EAAE,QAAQ,UAAU,MAAM;AACnC;AAEA,SAAS,kBAAkB,GAAwB;AACjD,QAAM,UAAU,EAAE,WAAW,CAAC;AAC9B,QAAM,QAAQ,QACX;AAAA,IACC,CAAC,MAAM;AAAA,6CACgC,WAAW,EAAE,EAAE,CAAC;AAAA,UACnD,EAAE,SAAS,kCAAkC,WAAW,EAAE,MAAM,CAAC,yCAAyC,WAAW,EAAE,OAAO,QAAQ,EAAE,OAAO,MAAM,MAAM,CAAC,kBAAkB,EAAE;AAAA,qCACrJ,WAAW,EAAE,IAAI,CAAC;AAAA;AAAA,kBAErC,WAAW,WAAW,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;AAAA,sFACnC,WAAW,EAAE,EAAE,CAAC,oBAAoB,WAAW,EAAE,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA,EAItI,EACC,KAAK,EAAE;AACV,SAAO;AAAA,+CACsC,WAAW,EAAE,EAAE,CAAC;AAAA,mCAC5B,KAAK;AAAA;AAAA,+FAEkD,WAAW,EAAE,EAAE,CAAC;AAAA,2FACf,WAAW,EAAE,EAAE,CAAC;AAAA;AAAA;AAAA;AAI3G;AAqCO,IAAM,UAAN,MAAc;AAAA,EAkBnB,YAAY,MAAmB,WAA6B;AAd5D,SAAQ,cAA+B;AAGvC;AAAA,SAAQ,gBAA2B,CAAC;AACpC,SAAQ,qBAA2C,CAAC;AAEpD;AAAA,SAAQ,cAAkD,CAAC;AAE3D;AAAA,SAAQ,qBAAmC,CAAC;AAE5C;AAAA,SAAQ,gBAAuC;AAE/C;AAAA,SAAQ,uBAA4C;AAwMpD,SAAQ,kBAAkB,CAAC,MAAqB;AAC9C,YAAM,SAAS,EAAE;AACjB,UAAI,EAAE,QAAQ,KAAK;AACjB,UAAE,eAAe;AACjB,cAAM,aAAa,KAAK,UAAU;AAClC,aAAK,UAAU;AAAA,UACb,CAAC,OAAO,KAAK,WAAW,IAAI,UAAU;AAAA,UACtC,MAAM;AACJ,mBAAO,MAAM;AACb,iBAAK,aAAa,UAAU;AAAA,UAC9B;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI,EAAE,QAAQ,YAAY,EAAE,WAAW,EAAE,UAAU;AACjD,UAAE,eAAe;AACjB,aAAK,KAAK;AACV;AAAA,MACF;AACA,UAAI,EAAE,QAAQ,UAAU;AACtB,UAAE,eAAe;AACjB,aAAK,UAAU,QAAQ;AAAA,MACzB;AAAA,IACF;AAEA,SAAQ,oBAAoB,CAAC,MAAsB;AAGjD,UAAI,KAAK,UAAU,qBAAqB,GAAG;AACzC,cAAM,QAAQ,EAAE,eAAe,SAAS;AACxC,YAAI,OAAO;AACT,gBAAM,aAAqB,CAAC;AAC5B,mBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,kBAAM,KAAK,MAAM,CAAC;AAClB,gBAAI,GAAG,SAAS,UAAU,GAAG,KAAK,WAAW,QAAQ,GAAG;AACtD,oBAAM,IAAI,GAAG,UAAU;AACvB,kBAAI,EAAG,YAAW,KAAK,CAAC;AAAA,YAC1B;AAAA,UACF;AACA,cAAI,WAAW,SAAS,GAAG;AACzB,cAAE,eAAe;AACjB,uBAAW,KAAK,WAAY,MAAK,KAAK,gBAAgB,CAAC;AACvD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,QAAE,eAAe;AACjB,YAAM,OAAO,EAAE,eAAe,QAAQ,YAAY,KAAK;AACvD,YAAM,MAAM,OAAO,aAAa;AAChC,UAAI,OAAO,IAAI,aAAa,GAAG;AAC7B,cAAM,IAAI,IAAI,WAAW,CAAC;AAC1B,UAAE,eAAe;AACjB,UAAE,WAAW,SAAS,eAAe,IAAI,CAAC;AAC1C,UAAE,SAAS,KAAK;AAChB,YAAI,gBAAgB;AACpB,YAAI,SAAS,CAAC;AAAA,MAChB;AAAA,IACF;AAIA;AAAA,SAAQ,iBAAiB,CAAC,MAAiB;AACzC,UAAI,CAAC,EAAE,gBAAgB,CAAC,MAAM,KAAK,EAAE,aAAa,KAAK,EAAE,SAAS,OAAO,EAAG;AAC5E,QAAE,eAAe;AACjB,YAAM,OAAO,KAAK,GAAG,cAAc,uBAAuB;AAC1D,YAAM,gBAAgB,QAAQ;AAAA,IAChC;AACA,SAAQ,kBAAkB,CAAC,MAAiB;AAE1C,YAAM,OAAO,EAAE;AACf,UAAI,EAAE,yBAAyB,QAAQ,KAAK,SAAS,EAAE,aAAa,EAAG;AACvE,YAAM,OAAO,KAAK,GAAG,cAAc,uBAAuB;AAC1D,YAAM,aAAa,UAAU,EAAE;AAAA,IACjC;AACA,SAAQ,aAAa,CAAC,MAAiB;AACrC,QAAE,eAAe;AACjB,YAAM,OAAO,KAAK,GAAG,cAAc,uBAAuB;AAC1D,YAAM,aAAa,UAAU,EAAE;AAC/B,UAAI,CAAC,EAAE,aAAc;AACrB,YAAM,QAAQ,MAAM,KAAK,EAAE,aAAa,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,QAAQ,CAAC;AACxF,iBAAW,KAAK,MAAO,MAAK,KAAK,gBAAgB,CAAC;AAAA,IACpD;AACA,SAAQ,mBAAmB,CAAC,MAAa;AACvC,YAAM,QAAQ,EAAE;AAChB,YAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AACvD,iBAAW,KAAK,MAAO,MAAK,KAAK,gBAAgB,CAAC;AAClD,YAAM,QAAQ;AAAA,IAChB;AAwJA,SAAQ,cAAc,CAAC,MAAkB;AACvC,YAAM,SAAS,EAAE;AAEjB,YAAM,YAAY,OAAO,QAAQ,2BAA2B;AAC5D,UAAI,WAAW;AACb,UAAE,gBAAgB;AAClB,cAAMC,OAAM,UAAU,aAAa,aAAa;AAChD,YAAIA,KAAK,MAAK,aAAaA,IAAG;AAC9B;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,QAAQ,iBAAiB;AAC9C,UAAI,OAAO;AACT,cAAM,YAAY,MAAM,aAAa,iBAAiB;AACtD,cAAM,YAAY,MAAM,aAAa,aAAa;AAClD,YAAI,aAAa,WAAW;AAC1B,YAAE,gBAAgB;AAClB,eAAK,UAAU,kBAAkB,WAAW,SAAS;AAAA,QACvD;AACA;AAAA,MACF;AACA,YAAM,SAAS,OAAO,aAAa,SAAS;AAC5C,UAAI,WAAW,QAAS,MAAK,UAAU,QAAQ;AAAA,eACtC,WAAW,OAAQ,MAAK,KAAK;AAAA,eAC7B,WAAW,WAAW;AAC7B,UAAE,eAAe;AACjB,aAAK,kBAAkB,MAAqB;AAAA,MAC9C,WAAW,WAAW,UAAU;AAC9B,UAAE,eAAe;AACjB,cAAM,QAAQ,KAAK,GAAG,cAAc,wBAAwB;AAC5D,eAAO,MAAM;AAAA,MACf,WAAW,WAAW,qBAAqB;AACzC,UAAE,eAAe;AACjB,cAAM,KAAK,OAAO,aAAa,oBAAoB;AACnD,YAAI,IAAI;AACN,eAAK,qBAAqB,KAAK,mBAAmB,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAC3E,eAAK,qBAAqB;AAAA,QAC5B;AAAA,MACF,WAAW,WAAW,UAAU;AAC9B,cAAM,KAAK,OAAO,aAAa,SAAS;AACxC,YAAI,GAAI,MAAK,UAAU,SAAS,EAAE;AAAA,MACpC,WAAW,WAAW,gBAAgB;AACpC,UAAE,eAAe;AACjB,cAAM,KAAK,OAAO,aAAa,SAAS;AACxC,YAAI,CAAC,GAAI;AACT,cAAM,QAAQ,KAAK,GAAG,cAAc,gCAAgC,UAAU,EAAE,CAAC,IAAI;AACrF,YAAI,CAAC,MAAO;AACZ,cAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,YAAI,UAAU;AACZ,gBAAM,gBAAgB,QAAQ;AAC9B,gBAAM,QAAQ,MAAM,cAAc,iBAAiB;AACnD,iBAAO,MAAM;AAAA,QACf,OAAO;AACL,gBAAM,aAAa,UAAU,EAAE;AAAA,QACjC;AAAA,MACF,WAAW,WAAW,cAAc;AAClC,UAAE,eAAe;AACjB,cAAM,KAAK,OAAO,aAAa,SAAS;AACxC,YAAI,CAAC,GAAI;AACT,cAAM,QAAQ,KAAK,GAAG;AAAA,UACpB,oCAAoC,UAAU,EAAE,CAAC;AAAA,QACnD;AACA,cAAM,OAAO,OAAO,MAAM,KAAK,KAAK;AACpC,YAAI,CAAC,KAAM;AACX,aAAK,UAAU,WAAW,IAAI,IAAI;AAClC,YAAI,MAAO,OAAM,QAAQ;AAAA,MAC3B,WAAW,WAAW,gBAAgB;AACpC,UAAE,eAAe;AACjB,cAAM,YAAY,OAAO,aAAa,iBAAiB;AACvD,cAAM,UAAU,OAAO,aAAa,eAAe;AACnD,YAAI,aAAa,QAAS,MAAK,UAAU,cAAc,WAAW,OAAO;AAAA,MAC3E;AAAA,IACF;AA7fE,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,KAAK,SAAS,cAAc,KAAK;AACtC,SAAK,GAAG,YAAY;AACpB,SAAK,GAAG,MAAM,UAAU;AACxB,SAAK,GAAG,iBAAiB,SAAS,KAAK,WAAW;AAClD,SAAK,KAAK,YAAY,KAAK,EAAE;AAAA,EAC/B;AAAA,EAEA,QAAQ,MAAuB;AAC7B,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,SAAoB,cAAoC,kBAAiC;AAC5F,SAAK,gBAAgB,CAAC,GAAG,OAAO;AAChC,SAAK,qBAAqB,CAAC,GAAG,YAAY;AAC1C,SAAK,cAAc,CAAC;AACpB,SAAK,OAAO,gBAAgB;AAC5B,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,OAAO,kBAAiC;AACtC,QAAI,CAAC,KAAK,cAAc,OAAQ;AAChC,SAAK,OAAO,gBAAgB;AAC5B,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,UAAU,IAAwB,QAAiC;AACjE,UAAMA,OAAM,eAAe,EAAE;AAC7B,QAAI,KAAK,mBAAmB,KAAK,CAAC,MAAM,eAAe,CAAC,MAAMA,IAAG,EAAG,QAAO;AAC3E,SAAK,mBAAmB,KAAK,EAAE;AAC/B,QAAI,OAAQ,MAAK,cAAc,KAAK,MAAM;AAC1C,SAAK,UAAU,iBAAiB,CAAC,GAAG,KAAK,kBAAkB,CAAC;AAC5D,SAAK,iBAAiB;AACtB,SAAK,aAAa;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAaA,MAAsB;AACjC,QAAI,KAAK,mBAAmB,UAAU,EAAG,QAAO;AAChD,UAAM,MAAM,KAAK,mBAAmB,UAAU,CAAC,MAAM,eAAe,CAAC,MAAMA,IAAG;AAC9E,QAAI,MAAM,EAAG,QAAO;AACpB,SAAK,mBAAmB,OAAO,KAAK,CAAC;AACrC,SAAK,cAAc,OAAO,KAAK,CAAC;AAChC,SAAK,UAAU,iBAAiB,CAAC,GAAG,KAAK,kBAAkB,CAAC;AAC5D,SAAK,iBAAiB;AACtB,SAAK,aAAa;AAClB,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe;AACrB,UAAM,SAAS,KAAK,GAAG,cAAc,qCAAqC;AAC1E,QAAI,CAAC,OAAQ;AACb,WAAO,cAAc,KAAK,mBAAmB,SAAS,IAClD,cAAc,KAAK,mBAAmB,MAAM,cAC5C;AAAA,EACN;AAAA;AAAA,EAGA,aAAa;AACX,QAAI,CAAC,KAAK,cAAc,OAAQ;AAChC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,QAAQ;AACN,SAAK,mBAAmB;AACxB,SAAK,GAAG,MAAM,UAAU;AACxB,SAAK,gBAAgB,CAAC;AACtB,SAAK,qBAAqB,CAAC;AAC3B,SAAK,cAAc,CAAC;AAAA,EACtB;AAAA,EAEA,SAAkB;AAChB,WAAO,KAAK,cAAc,SAAS;AAAA,EACrC;AAAA,EAEA,aAAmC;AACjC,WAAO,CAAC,GAAG,KAAK,kBAAkB;AAAA,EACpC;AAAA,EAEA,mBAA8B;AAC5B,WAAO,CAAC,GAAG,KAAK,aAAa;AAAA,EAC/B;AAAA,EAEQ,OAAO,UAAyB;AACtC,QAAI,CAAC,KAAK,cAAc,OAAQ;AAChC,UAAM,UAAU,KAAK,cAAc,CAAC;AACpC,UAAM,MAAM,MAAM,KAAK,QAAQ,SAAS,EACrC,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,KAAK,CAAC,EAAE,WAAW,OAAO,CAAC,EAC5D,MAAM,GAAG,CAAC;AACb,UAAM,QACJ,QAAQ,QAAQ,YAAY,KAC3B,QAAQ,KAAK,MAAM,QAAQ,KAAK,OAChC,IAAI,SAAS,MAAM,IAAI,KAAK,GAAG,IAAI;AACtC,UAAM,QAAS,QAAwB,aAAa,IAAI,KAAK;AAC7D,UAAM,cAAc,KAAK,MAAM,GAAG,EAAE;AAEpC,UAAM,gBAAgB,KAAK,cACvB,kCAAkC,WAAW,KAAK,WAAW,CAAC,yCAAyC,WAAW,KAAK,YAAY,IAAI,CAAC,wCACxI;AAEJ,SAAK,GAAG,YAAY;AAAA;AAAA,gBAER,KAAK,mBAAmB,SAAS,IAAI,cAAc,KAAK,mBAAmB,MAAM,cAAc,SAAS;AAAA;AAAA;AAAA,qCAGnF,WAAW,KAAK,CAAC,GAAG,cAAc,kBAAe,WAAW,WAAW,KAAK,KAAK,SAAS,KAAK,WAAM,MAAM,YAAY,EAAE;AAAA;AAAA;AAAA,UAIpJ,SAAS,WAAW,IAChB,iDACA,SACG;AAAA,MACC,CAAC,MAAM;AAAA,uDAC8B,WAAW,EAAE,EAAE,CAAC;AAAA,gBACvD,EAAE,SAAS,kCAAkC,WAAW,EAAE,MAAM,CAAC,yCAAyC,WAAW,EAAE,OAAO,QAAQ,EAAE,OAAO,MAAM,MAAM,CAAC,kBAAkB,EAAE;AAAA,qBAC3K,kBAAkB,EAAE,MAAM,EAAE,EAAE,CAAC;AAAA,gBACpC,sBAAsB,EAAE,WAAW,CAAC;AAAA;AAAA,wBAE5B,WAAW,WAAW,EAAE,SAAS,CAAC,CAAC;AAAA;AAAA,4FAEiC,WAAW,EAAE,EAAE,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK,IAAI,GAAG,EAAE,QAAS,MAAM,IAAI,EAAE,QAAS,WAAW,IAAI,UAAU,SAAS,KAAK,OAAO;AAAA,gFACjJ,WAAW,EAAE,EAAE,CAAC;AAAA;AAAA;AAAA,gBAGhF,kBAAkB,CAAC,CAAC;AAAA;AAAA;AAAA,IAGpB,EACC,KAAK,EAAE,CAChB;AAAA;AAAA,QAEA,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQX,KAAK,UAAU,qBAAqB,IAAI,gHAAgH,EAAE;AAAA,UAC1J,KAAK,cAAc,uHAAuH,EAAE;AAAA;AAAA;AAAA;AAAA;AAKlJ,SAAK,GAAG,MAAM,UAAU;AACxB,SAAK,iBAAiB;AACtB,SAAK,qBAAqB;AAC1B,UAAM,SAAS,KAAK,GAAG,cAAc,cAAc;AACnD,WAAO,MAAM;AACb,WAAO,iBAAiB,WAAW,KAAK,eAAe;AACvD,WAAO,iBAAiB,SAAS,KAAK,iBAAiB;AAIvD,UAAM,OAAO,KAAK,GAAG,cAAc,iBAAiB;AACpD,QAAI,QAAQ,KAAK,UAAU,qBAAqB,GAAG;AACjD,WAAK,iBAAiB,YAAY,KAAK,cAAc;AACrD,WAAK,iBAAiB,aAAa,KAAK,eAAe;AACvD,WAAK,iBAAiB,QAAQ,KAAK,UAAU;AAAA,IAC/C;AAEA,UAAM,YAAY,KAAK,GAAG,cAAc,wBAAwB;AAChE,QAAI,WAAW;AACb,gBAAU,iBAAiB,UAAU,KAAK,gBAAgB;AAAA,IAC5D;AAAA,EACF;AAAA,EAEQ,mBAAmB;AACzB,UAAM,OAAO,KAAK,GAAG,cAAc,sBAAsB;AACzD,QAAI,CAAC,KAAM;AACX,QAAI,KAAK,mBAAmB,UAAU,GAAG;AACvC,WAAK,YAAY;AACjB;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,mBAChB,IAAI,CAAC,OAAO;AACX,YAAMA,OAAM,eAAe,EAAE;AAC7B,YAAM,QAAQ,iBAAiB,EAAE;AACjC,aAAO,6CAA6C,WAAWA,IAAG,CAAC,KAAK,WAAW,KAAK,CAAC,8EAA8E,WAAWA,IAAG,CAAC;AAAA,IACxL,CAAC,EACA,KAAK,EAAE;AACV,SAAK,YAAY;AAAA,gDAC2B,KAAK,mBAAmB,MAAM;AAAA,oCAC1C,KAAK;AAAA;AAAA,EAEvC;AAAA,EA4FA,MAAc,gBAAgB,MAA2B;AACvD,QAAI,CAAC,KAAK,UAAU,qBAAqB,EAAG;AAG5C,UAAM,gBAAgB,YAAY,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AACvE,UAAM,cAA0B;AAAA,MAC9B,IAAI;AAAA,MACJ,KAAK;AAAA,MACL,UAAU,KAAK,QAAQ;AAAA,MACvB,WAAW,KAAK;AAAA,MAChB,YAAY,KAAK,IAAI;AAAA,MACrB,UAAU,KAAK;AAAA,IACjB;AACA,SAAK,mBAAmB,KAAK,WAAW;AACxC,SAAK,qBAAqB,aAAa;AAEvC,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,UAAU,mBAAmB,IAAI;AACxD,YAAM,MAAM,KAAK,mBAAmB,UAAU,CAAC,MAAM,EAAE,OAAO,aAAa;AAC3E,UAAI,OAAO,EAAG,MAAK,mBAAmB,GAAG,IAAI;AAAA,UACxC,MAAK,mBAAmB,KAAK,GAAG;AACrC,WAAK,qBAAqB;AAAA,IAC5B,SAAS,KAAK;AAEZ,WAAK,qBAAqB,KAAK,mBAAmB,OAAO,CAAC,MAAM,EAAE,OAAO,aAAa;AACtF,WAAK,qBAAqB;AAC1B,UAAI,OAAO,YAAY,aAAa;AAClC,gBAAQ,KAAK,mCAAmC,GAAG;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,qBAAqB,aAAsB;AACjD,UAAM,OAAO,KAAK,GAAG,cAAc,6BAA6B;AAChE,QAAI,CAAC,KAAM;AACX,QAAI,KAAK,mBAAmB,WAAW,GAAG;AACxC,WAAK,YAAY;AACjB;AAAA,IACF;AACA,SAAK,YAAY,KAAK,mBACnB,IAAI,CAAC,MAAM;AACV,YAAM,cAAc,EAAE,OAAO,eAAe,EAAE,QAAQ;AACtD,YAAM,UAAU,cACZ,8EACA,EAAE,SAAS,WAAW,QAAQ,IAC5B,yCAAyC,WAAW,EAAE,GAAG,CAAC,UAAU,WAAW,EAAE,YAAY,EAAE,CAAC,SAChG;AACN,aAAO;AAAA,gEACiD,WAAW,EAAE,EAAE,CAAC;AAAA,cAClE,OAAO;AAAA,mGAC8E,WAAW,EAAE,EAAE,CAAC;AAAA;AAAA;AAAA,IAG7G,CAAC,EACA,KAAK,EAAE;AAAA,EACZ;AAAA,EAEQ,YAA0B;AAChC,UAAM,MAAM,OAAO,aAAa;AAChC,QAAI,CAAC,OAAO,IAAI,eAAe,EAAG,QAAO;AACzC,WAAO,IAAI,WAAW,CAAC,EAAE,WAAW;AAAA,EACtC;AAAA,EAEQ,aAAa,GAAiB;AACpC,QAAI,CAAC,EAAG;AACR,UAAM,MAAM,OAAO,aAAa;AAChC,SAAK,gBAAgB;AACrB,SAAK,SAAS,CAAC;AAAA,EACjB;AAAA,EAEQ,WAAW,IAAwB,OAAqB;AAC9D,UAAM,MAAM,MAAU;AACtB,SAAK,YAAY,GAAG,IAAI;AACxB,UAAM,QAAQ,iBAAiB,EAAE;AACjC,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,YAAY;AACjB,SAAK,kBAAkB;AACvB,SAAK,aAAa,eAAe,GAAG;AACpC,SAAK,cAAc;AAEnB,UAAM,SAAS,KAAK,GAAG,cAAc,cAAc;AACnD,WAAO,MAAM;AACb,SAAK,aAAa,KAAK;AACvB,UAAM,MAAM,OAAO,aAAa;AAChC,QAAI,CAAC,OAAO,IAAI,eAAe,GAAG;AAChC,aAAO,YAAY,IAAI;AACvB,aAAO,YAAY,SAAS,eAAe,GAAG,CAAC;AAC/C,WAAK,gBAAgB,MAAM;AAC3B;AAAA,IACF;AACA,UAAM,IAAI,IAAI,WAAW,CAAC;AAC1B,MAAE,eAAe;AACjB,MAAE,WAAW,IAAI;AAEjB,UAAM,QAAQ,SAAS,eAAe,GAAG;AACzC,SAAK,MAAM,KAAK;AAChB,UAAM,WAAW,SAAS,YAAY;AACtC,aAAS,cAAc,KAAK;AAC5B,aAAS,SAAS,IAAI;AACtB,QAAI,gBAAgB;AACpB,QAAI,SAAS,QAAQ;AAAA,EACvB;AAAA,EAEQ,gBAAgB,IAAiB;AACvC,UAAM,QAAQ,SAAS,YAAY;AACnC,UAAM,mBAAmB,EAAE;AAC3B,UAAM,SAAS,KAAK;AACpB,UAAM,MAAM,OAAO,aAAa;AAChC,SAAK,gBAAgB;AACrB,SAAK,SAAS,KAAK;AAAA,EACrB;AAAA;AAAA,EAGQ,gBAAgB,QAA6B;AACnD,UAAM,QAAkB,CAAC;AACzB,UAAM,OAAO,CAAC,SAAe;AAC3B,UAAI,KAAK,aAAa,KAAK,WAAW;AACpC,cAAM,KAAK,KAAK,eAAe,EAAE;AACjC;AAAA,MACF;AACA,UAAI,KAAK,aAAa,KAAK,aAAc;AACzC,YAAM,KAAK;AACX,UAAI,GAAG,UAAU,SAAS,SAAS,GAAG;AACpC,cAAM,MAAM,GAAG,aAAa,iBAAiB;AAC7C,YAAI,KAAK;AAEP,gBAAM,MAAM,GAAG,eAAe;AAC9B,gBAAM,OAAO,IAAI,WAAW,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI;AAClD,gBAAM,KAAK,KAAK,IAAI,kBAAkB,GAAG,GAAG;AAC5C;AAAA,QACF;AACA,cAAM,MAAM,GAAG,aAAa,aAAa;AACzC,cAAM,QAAQ,GAAG,eAAe;AAChC,YAAI,IAAK,OAAM,KAAK,KAAK,KAAK,cAAc,GAAG,GAAG;AAClD;AAAA,MACF;AACA,UAAI,GAAG,YAAY,MAAM;AACvB,cAAM,KAAK,IAAI;AACf;AAAA,MACF;AACA,UAAI,GAAG,YAAY,SAAS,MAAM,UAAU,CAAC,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,IAAI,GAAG;AAEnF,cAAM,KAAK,IAAI;AAAA,MACjB;AACA,iBAAW,SAAS,GAAG,WAAY,MAAK,KAAK;AAAA,IAC/C;AACA,eAAW,SAAS,OAAO,WAAY,MAAK,KAAK;AACjD,WAAO,MAAM,KAAK,EAAE,EAAE,KAAK;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsFA,MAAc,kBAAkB,QAAqB;AACnD,SAAK,mBAAmB;AACxB,QAAI,CAAC,KAAK,UAAU,gBAAiB;AAMrC,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,YAAY;AACnB,WAAO,aAAa,kBAAkB,EAAE;AACxC,WAAO,YAAY;AACnB,SAAK,GAAG,YAAY,MAAM;AAC1B,SAAK,gBAAgB;AACrB,SAAK,sBAAsB,QAAQ,MAAM;AAEzC,QAAI,QAA2B,CAAC;AAChC,QAAI;AACF,cAAQ,MAAM,KAAK,UAAU,gBAAgB;AAAA,IAC/C,QAAQ;AAAA,IAER;AACA,QAAI,KAAK,kBAAkB,OAAQ;AAEnC,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO,YAAY;AACnB,WAAK,sBAAsB,QAAQ,MAAM;AACzC,WAAK,yBAAyB;AAC9B;AAAA,IACF;AAEA,UAAM,YAAY,MACf,IAAI,CAAC,MAAM;AACV,YAAM,SAAS,EAAE,YACb,uCAAuC,WAAW,EAAE,SAAS,CAAC,gBAC9D,8DAA8D,WAAW,SAAS,EAAE,IAAI,KAAK,GAAG,CAAC;AACrG,aAAO,kEAAkE,WAAW,EAAE,EAAE,CAAC,wBAAwB,WAAW,EAAE,IAAI,CAAC,KAAK,MAAM,sCAAsC,WAAW,EAAE,IAAI,CAAC;AAAA,IACxM,CAAC,EACA,KAAK,EAAE;AACV,WAAO,YAAY;AACnB,SAAK,sBAAsB,QAAQ,MAAM;AAEzC,WAAO,iBAAiB,SAAS,CAAC,MAAM;AACtC,YAAM,MAAO,EAAE,QAA2B,QAAqB,kBAAkB;AACjF,UAAI,CAAC,IAAK;AACV,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,YAAM,KAAK,IAAI,aAAa,iBAAiB,KAAK;AAClD,YAAM,OAAO,IAAI,aAAa,mBAAmB,KAAK;AACtD,UAAI,MAAM,KAAM,MAAK,kBAAkB,EAAE,IAAI,KAAK,CAAC;AACnD,WAAK,mBAAmB;AAAA,IAC1B,CAAC;AAED,SAAK,yBAAyB;AAAA,EAChC;AAAA;AAAA,EAGQ,2BAA2B;AACjC,QAAI,KAAK,qBAAsB;AAC/B,UAAM,QAAQ,CAAC,MAAqB;AAClC,UAAI,EAAE,QAAQ,UAAU;AACtB,UAAE,eAAe;AACjB,aAAK,mBAAmB;AAAA,MAC1B;AAAA,IACF;AACA,UAAM,YAAY,CAAC,MAAkB;AACnC,UAAI,CAAC,KAAK,cAAe;AACzB,UAAI,KAAK,cAAc,SAAS,EAAE,MAAc,EAAG;AACnD,WAAK,mBAAmB;AAAA,IAC1B;AACA,aAAS,iBAAiB,WAAW,OAAO,IAAI;AAChD,aAAS,iBAAiB,aAAa,WAAW,IAAI;AACtD,SAAK,uBAAuB,MAAM;AAChC,eAAS,oBAAoB,WAAW,OAAO,IAAI;AACnD,eAAS,oBAAoB,aAAa,WAAW,IAAI;AAAA,IAC3D;AAAA,EACF;AAAA,EAEQ,qBAAqB;AAC3B,QAAI,KAAK,sBAAsB;AAC7B,WAAK,qBAAqB;AAC1B,WAAK,uBAAuB;AAAA,IAC9B;AACA,QAAI,KAAK,eAAe;AACtB,WAAK,cAAc,OAAO;AAC1B,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAAsB,QAAwB,QAAqB;AACzE,UAAM,UAAU,KAAK,GAAG,sBAAsB;AAC9C,UAAM,UAAU,OAAO,sBAAsB;AAC7C,UAAM,OAAO,QAAQ,OAAO,QAAQ;AACpC,UAAM,MAAM,QAAQ,SAAS,QAAQ,MAAM;AAC3C,WAAO,MAAM,OAAO,GAAG,IAAI;AAC3B,WAAO,MAAM,MAAM,GAAG,GAAG;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBAAkB,MAA4C;AACpE,QAAI,CAAC,QAAQ,CAAC,KAAK,GAAI;AACvB,UAAM,SAAS,KAAK,GAAG,cAAc,cAAc;AACnD,QAAI,CAAC,OAAQ;AACb,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,YAAY;AACjB,SAAK,kBAAkB;AACvB,SAAK,aAAa,mBAAmB,KAAK,EAAE;AAC5C,SAAK,cAAc,MAAM,KAAK;AAE9B,WAAO,MAAM;AACb,UAAM,MAAM,OAAO,aAAa;AAChC,QAAI,CAAC,OAAO,IAAI,eAAe,GAAG;AAChC,aAAO,YAAY,IAAI;AACvB,aAAO,YAAY,SAAS,eAAe,GAAG,CAAC;AAC/C,WAAK,gBAAgB,MAAM;AAC3B;AAAA,IACF;AACA,UAAM,IAAI,IAAI,WAAW,CAAC;AAC1B,MAAE,eAAe;AACjB,MAAE,WAAW,IAAI;AACjB,UAAM,QAAQ,SAAS,eAAe,GAAG;AACzC,SAAK,MAAM,KAAK;AAChB,UAAM,WAAW,SAAS,YAAY;AACtC,aAAS,cAAc,KAAK;AAC5B,aAAS,SAAS,IAAI;AACtB,QAAI,gBAAgB;AACpB,QAAI,SAAS,QAAQ;AAAA,EACvB;AAAA,EAEQ,OAAO;AACb,UAAM,SAAS,KAAK,GAAG,cAAc,cAAc;AACnD,QAAI,CAAC,OAAQ;AACb,UAAM,OAAO,KAAK,gBAAgB,MAAM;AAGxC,UAAM,gBAAgB,KAAK,mBAAmB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE;AACnE,QAAI,CAAC,QAAQ,kBAAkB,EAAG;AAClC,UAAM,WAA+C,CAAC;AACtD,UAAM,KAAK;AACX,QAAI;AACJ,YAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,YAAM,MAAM,EAAE,CAAC;AACf,UAAI,KAAK,YAAY,GAAG,EAAG,UAAS,GAAG,IAAI,KAAK,YAAY,GAAG;AAAA,IACjE;AACA,UAAM,WAAW,gBAAgB,IAAI;AAErC,UAAM,sBAAsB,KAAK,mBAAmB,OAAO,CAAC,MAAM,EAAE,GAAG;AACvE,SAAK,UAAU;AAAA,MACb;AAAA,MACA,CAAC,GAAG,KAAK,kBAAkB;AAAA,MAC3B,OAAO,KAAK,QAAQ,EAAE,SAAS,WAAW;AAAA,MAC1C,SAAS,SAAS,WAAW;AAAA,MAC7B,oBAAoB,SAAS,sBAAsB;AAAA,IACrD;AACA,WAAO,YAAY;AACnB,SAAK,cAAc,CAAC;AACpB,SAAK,qBAAqB,CAAC;AAC3B,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEQ,WAAW;AACjB,UAAM,UAAU,KAAK,cAAc,CAAC;AACpC,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,QAAQ,sBAAsB;AAC3C,UAAM,UAAU,KAAK,GAAG,sBAAsB;AAC9C,UAAM,MAAM;AACZ,QAAI,MAAM,KAAK,SAAS;AACxB,QAAI,OAAO,KAAK;AAChB,QAAI,MAAM,QAAQ,SAAS,OAAO,cAAc,KAAK;AACnD,YAAM,KAAK,MAAM,QAAQ,SAAS;AAClC,UAAI,MAAM,IAAK,OAAM;AAAA,IACvB;AACA,QAAI,OAAO,QAAQ,QAAQ,OAAO,aAAa,KAAK;AAClD,aAAO,OAAO,aAAa,QAAQ,QAAQ;AAAA,IAC7C;AACA,QAAI,OAAO,IAAK,QAAO;AACvB,SAAK,GAAG,MAAM,MAAM,MAAM;AAC1B,SAAK,GAAG,MAAM,OAAO,OAAO;AAAA,EAC9B;AAAA,EAEA,UAAU;AACR,SAAK,mBAAmB;AACxB,SAAK,GAAG,oBAAoB,SAAS,KAAK,WAAW;AACrD,SAAK,GAAG,OAAO;AAAA,EACjB;AACF;;;ACpyBO,IAAM,OAAN,MAAW;AAAA,EAWhB,YAAY,MAAmB,WAA0B;AAPzD,SAAQ,cAAc;AACtB,SAAQ,UAAwB,CAAC;AACjC,SAAQ,gBAAgB;AACxB,SAAQ,OAAwB;AAChC,SAAQ,kBAAkB;AAC1B,SAAQ,iBAAiB;AAoGzB,SAAQ,cAAc,CAAC,MAAkB;AACvC,YAAM,MAAO,EAAE,OAAuB,QAAQ,WAAW;AACzD,UAAI,CAAC,IAAK;AACV,YAAM,IAAI,IAAI,aAAa,SAAS;AACpC,UAAI,MAAM,UAAW,MAAK,UAAU,gBAAgB;AAAA,eAC3C,MAAM,UAAW,MAAK,UAAU,UAAU;AAAA,eAC1C,MAAM,QAAS,MAAK,UAAU,oBAAoB;AAAA,eAClD,MAAM,mBAAoB,MAAK,UAAU,kBAAkB;AAAA,eAC3D,MAAM,kBAAmB,MAAK,UAAU,iBAAiB;AAAA,eACzD,MAAM,UAAU;AACvB,cAAM,KAAK,IAAI,aAAa,SAAS;AACrC,YAAI,GAAI,MAAK,UAAU,SAAS,EAAE;AAAA,MACpC;AAAA,IACF;AA9GE,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,KAAK,SAAS,cAAc,KAAK;AACtC,SAAK,GAAG,YAAY;AACpB,SAAK,GAAG,MAAM,UAAU;AACxB,SAAK,GAAG,iBAAiB,SAAS,KAAK,WAAW;AAClD,SAAK,KAAK,YAAY,KAAK,EAAE;AAAA,EAC/B;AAAA,EAEA,WAAW,SAAuB;AAChC,SAAK,UAAU;AACf,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,YAAY,UAAyB;AACnC,SAAK,gBAAgB,SAAS;AAC9B,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAQ,MAAuB;AAC7B,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,eAAe,MAAe;AAC5B,SAAK,cAAc;AACnB,UAAM,MAAM,KAAK,GAAG,cAA2B,qBAAqB;AACpE,QAAI,IAAK,KAAI,UAAU,OAAO,aAAa,IAAI;AAAA,EACjD;AAAA,EAEA,oBAAoB,MAAe,gBAAwB;AACzD,SAAK,kBAAkB;AACvB,SAAK,iBAAiB;AACtB,SAAK,GAAG,UAAU,OAAO,gBAAgB,QAAQ,iBAAiB,CAAC;AACnE,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,OAAO;AACL,SAAK,GAAG,MAAM,UAAU;AACxB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,OAAO;AACL,SAAK,GAAG,MAAM,UAAU;AAAA,EAC1B;AAAA,EAEQ,SAAS;AACf,QAAI,KAAK,GAAG,MAAM,YAAY,OAAQ;AACtC,UAAM,iBAAiB,KAAK,QAAQ;AAAA,MAAO,CAAC,MAC1C,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,KAAK,cAAc,CAAC,IAAI;AAAA,IACzE;AACA,UAAM,WAAW,KAAK,OAClB,oCAAoC,WAAW,KAAK,KAAK,IAAI,CAAC,KAAK,WAAW,KAAK,MAAM,qBAAqB,CAAC,IAAI,WAAW,KAAK,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC,CAAE,CAAC,gDAC5J;AACJ,UAAM,cAAc,eAAe,SAC/B,eACG;AAAA,MACC,CAAC,MACC,6BAA6B,EAAE,YAAY,YAAY,aAAa,EAAE,+BAA+B,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,WAAW,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,WAAW,EAAE,KAAK,CAAC;AAAA,IACpM,EACC,KAAK,EAAE,IAAI,0CACd;AAGJ,QAAI,SAAS;AACb,QAAI,KAAK,mBAAmB,KAAK,iBAAiB,GAAG;AACnD,eAAS;AAAA,oEACqD,KAAK,cAAc,WAAW,KAAK,mBAAmB,IAAI,KAAK,GAAG,KAAK,KAAK,cAAc;AAAA,oFAC1E,KAAK,cAAc;AAAA;AAAA;AAAA;AAAA,IAInG,OAAO;AACL,YAAM,QAAQ,KAAK;AACnB,eAAS;AAAA,2CAC4B,KAAK,cAAc,cAAc,EAAE,iDAAiD,KAAK,WAAW,UAAU,IAAI,KAAK,GAAG;AAAA;AAAA,UAE3J,QAAQ;AAAA,UACR,WAAW;AAAA;AAAA,IAEjB;AAEA,UAAM,cAAc,KAAK,kBACrB,2BACA;AAEJ,SAAK,GAAG,YAAY;AAAA;AAAA;AAAA,QAGhB,MAAM;AAAA,sCACwB,KAAK,kBAAkB,cAAc,EAAE,4BAA4B,WAAW,WAAW,CAAC,iBAAiB,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKtK;AAAA,EAiBA,MAAM,SAAiB;AACrB,UAAM,IAAI,SAAS,cAAc,KAAK;AACtC,MAAE,YAAY;AACd,MAAE,cAAc;AAChB,SAAK,KAAK,YAAY,CAAC;AACvB,eAAW,MAAM,EAAE,OAAO,GAAG,IAAI;AAAA,EACnC;AAAA,EAEA,UAAU;AACR,SAAK,GAAG,oBAAoB,SAAS,KAAK,WAAW;AACrD,SAAK,GAAG,OAAO;AAAA,EACjB;AACF;;;ACjIO,IAAM,UAAN,MAAc;AAAA,EASnB,YAAY,MAAmB,WAA6B;AAL5D,SAAQ,OAAO;AACf,SAAQ,SAAiB;AACzB,SAAQ,eAA8B,CAAC;AACvC,SAAQ,iBAAiB,oBAAI,IAA6B;AAqK1D,SAAQ,cAAc,CAAC,MAAkB;AACvC,YAAM,SAAS,EAAE;AACjB,YAAM,QAAQ,OAAO,QAAQ,iBAAiB;AAC9C,UAAI,OAAO;AACT,UAAE,gBAAgB;AAClB,cAAM,YAAY,MAAM,aAAa,iBAAiB;AACtD,cAAM,YAAY,MAAM,aAAa,aAAa;AAClD,YAAI,aAAa,UAAW,MAAK,UAAU,kBAAkB,WAAW,SAAS;AACjF;AAAA,MACF;AACA,YAAM,WAAW,OAAO,QAAQ,oBAAoB;AACpD,UAAI,UAAU;AACZ,UAAE,gBAAgB;AAClB,cAAM,IAAI,SAAS,aAAa,aAAa;AAC7C,YAAI,KAAK,MAAM,KAAK,QAAQ;AAC1B,eAAK,SAAS;AACd,eAAK,OAAO,KAAK,YAAY;AAAA,QAC/B;AACA;AAAA,MACF;AACA,YAAM,WAAW,OAAO,QAAQ,oBAAoB;AACpD,UAAI,UAAU;AACZ,UAAE,gBAAgB;AAClB,cAAM,KAAK,SAAS,aAAa,SAAS;AAC1C,cAAM,SAAS,SAAS,aAAa,aAAa;AAClD,YAAI,MAAM,OAAQ,MAAK,UAAU,eAAe,IAAI,MAAM;AAC1D;AAAA,MACF;AACA,YAAM,WAAW,OAAO,QAAQ,oBAAoB;AACpD,UAAI,UAAU;AACZ,UAAE,gBAAgB;AAClB,cAAM,KAAK,SAAS,aAAa,SAAS;AAC1C,YAAI,GAAI,MAAK,UAAU,SAAS,EAAE;AAClC;AAAA,MACF;AACA,UAAI,OAAO,QAAQ,mBAAmB,GAAG;AACvC,aAAK,UAAU,QAAQ;AACvB;AAAA,MACF;AACA,YAAM,SAAS,OAAO,QAAQ,qBAAqB;AACnD,UAAI,UAAU,OAAO,MAAM;AACzB,UAAE,gBAAgB;AAClB,aAAK,UAAU,SAAS,OAAO,IAAI;AACnC;AAAA,MACF;AACA,YAAM,OAAO,OAAO,QAAQ,kBAAkB;AAC9C,UAAI,QAAQ,KAAK,QAAS,MAAK,UAAU,SAAS,KAAK,QAAQ,EAAE;AAAA,IACnE;AAjNE,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,KAAK,SAAS,cAAc,KAAK;AACtC,SAAK,GAAG,YAAY;AACpB,SAAK,GAAG,iBAAiB,SAAS,KAAK,WAAW;AAClD,SAAK,KAAK,YAAY,KAAK,EAAE;AAAA,EAC/B;AAAA,EAEA,OAAO,UAAyB,YAA2C;AACzE,QAAI,KAAK,KAAM,MAAK,MAAM;AAAA,QACrB,MAAK,KAAK,UAAU,UAAU;AAAA,EACrC;AAAA,EACA,KAAK,UAAyB,YAA2C;AACvE,SAAK,OAAO,UAAU,UAAU;AAChC,0BAAsB,MAAM,KAAK,GAAG,UAAU,IAAI,SAAS,CAAC;AAC5D,SAAK,OAAO;AAAA,EACd;AAAA,EACA,OAAO,UAAyB,YAA2C;AACzE,QAAI,KAAK,KAAM,MAAK,OAAO,UAAU,UAAU;AAAA,EACjD;AAAA,EACA,QAAQ;AAAE,SAAK,GAAG,UAAU,OAAO,SAAS;AAAG,SAAK,OAAO;AAAA,EAAO;AAAA,EAClE,SAAkB;AAAE,WAAO,KAAK;AAAA,EAAM;AAAA,EAE9B,OAAO,UAAyB,YAA2C;AACjF,SAAK,eAAe;AACpB,QAAI,WAAY,MAAK,iBAAiB,IAAI,IAAI,UAAU;AACxD,UAAM,OAAO,KAAK;AAKlB,UAAM,UAAU,SAAS,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,EAAE,MAAM,UAAU;AACpE,UAAM,WAAW,SAAS,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,EAAE,MAAM,UAAU;AAIrE,UAAM,UAAU,KAAK,WAAW,SAC5B,SAAS,OAAO,CAAC,OAAO,EAAE,UAAU,YAAY,MAAM,IACtD;AACJ,UAAM,iBAAiB,KAAK,WAAW,SAAS,CAAC,IAAI;AAErD,UAAM,SAAS,cAAc,QAAQ;AAErC,QAAI,QAAQ,WAAW,KAAK,eAAe,WAAW,GAAG;AACvD,YAAM,YAAY,KAAK,WAAW,UAAU,SAAS,SAAS,IAC1D,iCAAiC,OAAO,WAAW,OAAO,OAAO,kBAAkB,OAAO,WAAW,OAAO,YAAY,IAAI,KAAK,GAAG,iDACpI;AACJ,WAAK,GAAG,YAAY;AAAA,UAChB,KAAK,WAAW,SAAS,QAAQ,MAAM,CAAC;AAAA;AAAA,0CAER,SAAS;AAAA;AAAA;AAG7C;AAAA,IACF;AAGA,UAAM,SAAS,oBAAI,IAAmB;AACtC,eAAW,KAAK,SAAS;AACvB,YAAM,MAAM,EAAE,gBAAgB,CAAC;AAC/B,YAAM,UAAU,IAAI,CAAC;AACrB,UAAI,CAAC,QAAS;AACd,YAAMC,OAAM,eAAe,OAAO;AAClC,YAAM,IAAI,OAAO,IAAIA,IAAG;AACxB,UAAI,EAAG,GAAE,SAAS,KAAK,CAAC;AAAA,UACnB,QAAO,IAAIA,MAAK,EAAE,IAAI,SAAS,UAAU,CAAC,CAAC,EAAE,CAAC;AAAA,IACrD;AAEA,UAAM,YAAsB,CAAC;AAC7B,QAAI,KAAK;AACT,eAAW,KAAK,OAAO,OAAO,GAAG;AAC/B,YAAM,UAAU,EAAE,GAAG,QAAQ,YAAY,KAAK,EAAE,GAAG,WAAW,KAAK,MAAM,EAAE,GAAG,WAAW,KAAK;AAC9F,YAAM,cAAc,EAAE,GAAG,YAAY,MAAM,GAAG,EAAE;AAChD,gBAAU,KAAK;AAAA,oDAC+B,EAAE;AAAA;AAAA,cAExC,WAAW,OAAO,CAAC;AAAA,cACnB,cAAc,6CAA6C,WAAW,WAAW,CAAC,GAAG,EAAE,GAAG,YAAY,SAAS,KAAK,WAAM,EAAE,kBAAkB,EAAE;AAAA;AAAA,YAElJ,EAAE,SACD,IAAI,CAAC,MAAM;AACV,cAAM,SAAS,EAAE,UAAU;AAC3B,cAAM,UAAU,EAAE,gBAAgB,CAAC,GAAG,MAAM,CAAC;AAC7C,cAAM,aAAa,OAAO,SACtB,sDAAsD,OACnD;AAAA,UACC,CAAC,IAAI,MAAM,+DAA+D,WAAW,eAAe,EAAE,CAAC,CAAC,mCAAmC,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,GAAG,MAAM,IAAI,WAAW,iBAAiB,EAAE,CAAC,CAAC;AAAA,QACvN,EACC,KAAK,EAAE,CAAC,WACX;AACJ,cAAM,QAAQ,KAAK,IAAI,EAAE,EAAE;AAC3B,cAAM,eAAe,UAAU,YAC3B,+JACA;AACJ,eAAO;AAAA,0CACqB,WAAW,SAAS,wBAAwB,EAAE,sBAAsB,WAAW,EAAE,EAAE,CAAC,gCAAgC,EAAE;AAAA;AAAA,kBAE9I,EAAE,SAAS,uCAAuC,WAAW,EAAE,MAAM,CAAC,8CAA8C,WAAW,EAAE,OAAO,IAAI,CAAC,kBAAkB,EAAE;AAAA,kBACjK,YAAY;AAAA,kBACZ,WAAW,SAAS,yCAAyC,MAAM,KAAK,MAAM,YAAY,EAAE;AAAA;AAAA,mDAE3D,kBAAkB,EAAE,MAAM,EAAE,EAAE,CAAC;AAAA,gBAClE,sBAAsB,EAAE,WAAW,CAAC;AAAA,gBACpC,UAAU;AAAA;AAAA,wBAEF,WAAW,WAAW,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,gBAAgB,CAAC,GAAG,SAAS,IAAI,mBAAgB,EAAE,gBAAgB,CAAC,GAAG,MAAM,MAAM,EAAE,IAAI,EAAE,SAAS,UAAU,KAAK,IAAI,SAAM,EAAE,QAAS,MAAM,IAAI,EAAE,QAAS,WAAW,IAAI,UAAU,SAAS,KAAK,EAAE;AAAA;AAAA,oBAEnP,kBAAkB,EAAE,IAAI,MAAM,CAAC;AAAA,qFACkC,WAAW,EAAE,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,MAKzF,CAAC,EACA,KAAK,EAAE,CAAC;AAAA;AAAA,OAEd;AACD;AAAA,IACF;AACA,UAAM,aAAa,eAAe,SAAS,IAAI,kBAAkB,cAAc,IAAI;AACnF,SAAK,GAAG,YAAY;AAAA,QAChB,KAAK,WAAW,SAAS,QAAQ,MAAM,CAAC;AAAA;AAAA,UAEtC,UAAU,KAAK,EAAE,CAAC;AAAA,UAClB,UAAU;AAAA;AAAA;AAIhB,UAAM,QAAQ,KAAK,GAAG,iBAAiB,kBAAkB;AACzD,UAAM,WAAW,CAAC,GAAG,OAAO,OAAO,CAAC;AACpC,eAAW,QAAQ,OAAO;AACxB,YAAM,aAAa,OAAQ,KAAqB,QAAQ,KAAK;AAC7D,MAAC,KAAa,UAAU,SAAS,UAAU;AAAA,IAC7C;AACA,UAAM,QAAQ,KAAK,GAAG,iBAAiB,qBAAqB;AAC5D,eAAW,QAAQ,OAAO;AACxB,YAAMA,OAAM,KAAK,aAAa,aAAa;AAC3C,YAAM,KAAK,qBAAqB,SAASA,QAAO,EAAE;AAClD,MAAC,KAAa,OAAO;AAAA,IACvB;AAAA,EACF;AAAA,EAEQ,WAAW,OAAe,QAAqE;AACrG,WAAO;AAAA;AAAA;AAAA;AAAA,iDAIsC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,2CAKX,KAAK,WAAW,SAAS,cAAc,EAAE,mEAAmE,KAAK,WAAW,MAAM;AAAA,uDACtH,OAAO,IAAI;AAAA;AAAA,2CAEvB,KAAK,WAAW,QAAQ,cAAc,EAAE,kEAAkE,KAAK,WAAW,KAAK;AAAA,sDACpH,KAAK;AAAA;AAAA;AAAA;AAAA,EAIzD;AAAA,EAmDA,UAAU;AACR,SAAK,GAAG,oBAAoB,SAAS,KAAK,WAAW;AACrD,SAAK,GAAG,OAAO;AAAA,EACjB;AACF;AAIA,SAAS,cAAc,UAA8E;AACnG,QAAM,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,EAAE;AAC7C,aAAW,KAAK,UAAU;AACxB,UAAM,IAAI,EAAE,UAAU;AACtB,MAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK;AAAA,EACvB;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,IAAY,SAAgC;AACrE,QAAM,OAAO,WAAW,EAAE;AAC1B,MAAI,YAAY,QAAQ;AACtB,WAAO;AAAA,yEAC8D,IAAI;AAAA,yEACJ,IAAI;AAAA;AAAA,EAE3E;AAEA,SAAO,oEAAoE,IAAI;AACjF;AAWA,SAAS,kBAAkB,SAAgC;AACzD,QAAM,QAAQ,QACX,IAAI,CAAC,MAAM;AACV,UAAM,SAAS,EAAE,UAAU;AAC3B,UAAM,WAAW,EAAE,gBAAgB,CAAC,GAAG,CAAC;AACxC,UAAM,QAAQ,UAAU,QAAQ,QAAQ,YAAY,IAAI;AACxD,UAAM,UAAU,SAAS,aAAa,MAAM,GAAG,EAAE,KAAK;AACtD,WAAO;AAAA,+EACkE,WAAW,EAAE,EAAE,CAAC;AAAA;AAAA,cAEjF,EAAE,SAAS,uCAAuC,WAAW,EAAE,MAAM,CAAC,8CAA8C,WAAW,EAAE,OAAO,IAAI,CAAC,kBAAkB,EAAE;AAAA;AAAA;AAAA;AAAA,2BAIpJ,WAAW,KAAK,CAAC,UAAU,UAAU,WAAW,WAAW,OAAO,CAAC,IAAI,SAAS,aAAa,UAAU,KAAK,KAAK,WAAM,EAAE,YAAY,EAAE;AAAA;AAAA,+CAEnH,kBAAkB,EAAE,MAAM,EAAE,EAAE,CAAC;AAAA,YAClE,sBAAsB,EAAE,WAAW,CAAC;AAAA;AAAA,oBAE5B,WAAW,WAAW,EAAE,SAAS,CAAC,CAAC,GAAG,WAAW,SAAS,SAAM,MAAM,KAAK,EAAE;AAAA;AAAA,iFAEhB,WAAW,EAAE,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7F,CAAC,EACA,KAAK,EAAE;AACV,SAAO;AAAA;AAAA;AAAA,wBAGY,QAAQ,MAAM;AAAA;AAAA;AAAA,QAG3B,KAAK;AAAA;AAAA;AAGb;AAEA,SAAS,qBAAqB,UAAyBA,MAAwC;AAC7F,aAAW,KAAK,UAAU;AACxB,eAAW,MAAM,EAAE,gBAAgB,CAAC,GAAG;AACrC,UAAI,eAAe,EAAE,MAAMA,KAAK,QAAO;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;;;ACpUO,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["init","key","key","refId","fp","key","c"]}
1
+ {"version":3,"sources":["../src/auto.ts","../src/types.ts","../src/util.ts","../src/storage.ts","../src/cloud.ts","../src/fingerprint.ts","../src/highlighter.ts","../src/popover.ts","../src/pill.ts","../src/sidebar.ts","../src/styles.ts","../src/shortcut.ts","../src/events.ts","../src/index.ts"],"sourcesContent":["import { Vizu, type VizuOptions, type StorageOption } from './index';\n\ndeclare global {\n interface Window {\n __vizu?: Vizu;\n Vizu?: typeof Vizu;\n }\n}\n\n/**\n * Script-tag drop-in. Constructs a Vizu instance from `data-*` attributes on the\n * script tag and exposes it as `window.__vizu`. The instance has NO actions\n * registered by default — integrators call `window.__vizu.addAction({...})` to\n * wire up Copy-as-prompt / JSON export / Clear / send-to-backend / anything else.\n *\n * Vizu is a UI for collecting annotations. Formatting and persisting them is the\n * host application's job: subscribe to events (`comment:added`, etc.) and react.\n */\nfunction findScript(): HTMLScriptElement | null {\n if (typeof document === 'undefined') return null;\n const current = document.currentScript as HTMLScriptElement | null;\n if (current) return current;\n const scripts = Array.from(document.scripts);\n return (\n scripts.find((s) => s.dataset && s.dataset.vizuAuto !== undefined) ||\n scripts.find((s) => s.src && /vizu(\\.min)?\\.js/.test(s.src)) ||\n null\n );\n}\n\nfunction init() {\n if (typeof window === 'undefined') return;\n if (window.__vizu) return;\n const script = findScript();\n const ds = script?.dataset || ({} as DOMStringMap);\n\n const storage: StorageOption | undefined = ds.storage\n ? (['local', 'memory', 'none'] as const).includes(ds.storage as any)\n ? (ds.storage as StorageOption)\n : 'local'\n : 'local';\n\n const user = ds.userName\n ? {\n id: ds.userId,\n name: ds.userName,\n avatarUrl: ds.userAvatar,\n email: ds.userEmail,\n }\n : undefined;\n\n const opts: VizuOptions = {\n shortcut: ds.shortcut || 'mod+shift+e',\n namespace: ds.namespace || 'default',\n pageVersion: ds.version,\n accent: ds.accent,\n startEnabled: ds.startEnabled === 'true' || ds.startEnabled === '' || ds.startEnabled === '1',\n ignoreSelectors: ds.ignore\n ? ds.ignore.split(',').map((s) => s.trim()).filter(Boolean)\n : [],\n storage,\n user,\n };\n // Script-tag mode: prefer `local` storage as the convenient default (programmatic API defaults to `memory`).\n const v = new (Vizu as any)(opts, 'local') as Vizu;\n\n window.__vizu = v;\n window.Vizu = Vizu;\n}\n\ninit();\n\nexport { Vizu } from './index';\n","/**\n * Single rung of the ancestor chain: the element's tag and its\n * `nth-of-type` index inside its parent (1-indexed). Captured root-near\n * → closer-to-target so the matcher can shift-search through wrapper\n * additions.\n */\nexport interface AncestorStep {\n tag: string;\n nthOfType: number;\n}\n\nexport interface ElementFingerprint {\n selector: string;\n parentSelector: string;\n tagName: string;\n textSnippet: string;\n siblingIndex: number;\n attributes: {\n id?: string;\n classList?: string[];\n role?: string;\n ariaLabel?: string;\n dataKey?: string;\n };\n /**\n * 3-step ancestor chain. Added in algorithm v2 (Phase 11.4.2). Order:\n * immediate parent first, then grandparent, then great-grandparent.\n * Legacy fingerprints written before this field lack it; the matcher\n * falls back to the parent+sibling-index+tag heuristic for those.\n */\n ancestorChain?: { steps: AncestorStep[] };\n /**\n * Algorithm version that produced this fingerprint. Absent (or `1`) on\n * legacy captures; new captures stamp `2` (Phase 11.4 hardened matcher\n * — class-signature rung, multi-ancestor chain, Levenshtein text scan).\n * The matcher routes on the presence of {@link ancestorChain} today, so\n * this field is informational; it gives us a stable handle to migrate\n * data when v3 ships without re-introspecting fingerprint shape.\n */\n algorithmVersion?: 1 | 2;\n}\n\nexport interface VizuUser {\n id?: string;\n name: string;\n avatarUrl?: string;\n email?: string;\n /** Free-form host-defined extras (team, role, etc.) preserved on each comment. */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Slim shape for the mention picker. The host can @-mention any of\n * these in a comment body. Returned by `Vizu.searchMentionable()` and\n * cached by the popover dropdown. Name + avatar is all the UI needs;\n * email/role intentionally omitted to keep the cross-origin surface\n * minimal.\n */\nexport interface MentionableUser {\n id: string;\n name: string;\n avatarUrl?: string;\n}\n\n/** Current schema version. Bump + add a migration when the shape changes. */\nexport const SCHEMA_VERSION = 2 as const;\nexport type SchemaVersion = typeof SCHEMA_VERSION;\n\n/**\n * Triage state. Cloud workspaces use this for filtering;\n * the local sidebar treats `resolved` / `wontfix` as \"dim\".\n */\nexport type CommentStatus = 'open' | 'resolved' | 'wontfix';\n\n/**\n * Outcome of resolving a comment's fingerprint against the current DOM.\n *\n * - `exact` : `data-vizu-key` or `id` matched. We trust this 100%.\n * - `likely` : The full CSS selector path matched. Probably the right\n * element but not guaranteed if the page has dynamic\n * duplicates.\n * - `drifted` : Only the structural fallback (parent + sibling-index)\n * or the page-wide text snippet matched. The page has\n * changed since the comment was written. Render with a\n * visible warning so visitors don't act on a stale match.\n * - `orphaned` : Nothing matched. The element is gone. Comment is shown\n * in a separate sidebar section.\n *\n * Returned by `findByFingerprint`; emitted via the `anchor:resolved` event.\n */\nexport type MatchConfidence = 'exact' | 'likely' | 'drifted' | 'orphaned';\n\n/** Result of a fingerprint resolution. `element` is null iff `confidence === 'orphaned'`. */\nexport interface FingerprintMatch {\n element: Element | null;\n confidence: MatchConfidence;\n}\n\n/**\n * Embedded reply in a comment thread. Threading UI ships in v1.1 — the\n * shape is stamped in v2 so writes are already future-correct.\n */\nexport interface Reply {\n id: string;\n text: string;\n createdAt: number;\n author?: VizuUser;\n /** Identity provider's user id (Clerk, etc.) when known. */\n authorId?: string;\n /** Clerk user ids mentioned in this reply. */\n mentions?: string[];\n}\n\n/**\n * File attachment metadata. Storage backend (Vercel Blob, S3, …) is\n * decided by the host / cloud adapter; the comment only carries the\n * resolved URL.\n */\nexport interface Attachment {\n id: string;\n url: string;\n mimeType: string;\n sizeBytes: number;\n uploadedAt: number;\n /** Original filename the user uploaded. */\n filename?: string;\n}\n\nexport interface VizuComment {\n id: string;\n /** Schema version. Set to {@link SCHEMA_VERSION} on every new write. */\n schemaVersion: SchemaVersion;\n /**\n * One or more elements this comment is anchored to. Length 1 = single-anchor\n * (the common case); >1 = \"grouped\" comment that applies to multiple\n * elements together (e.g., \"align these three CTAs to the same baseline\").\n *\n * Always has length ≥ 1. Legacy v1 comments (with a singular `fingerprint`\n * field) are auto-migrated to `[fingerprint]` on load — see `migrateComment`.\n */\n fingerprints: ElementFingerprint[];\n /** @deprecated v1 only — kept on the type so older payloads parse cleanly; migrated at load time. */\n fingerprint?: ElementFingerprint;\n text: string;\n createdAt: number;\n pageVersion?: string;\n /** @deprecated use `pageUrl` — kept for backwards-compat read of v1 payloads. */\n url?: string;\n /** Page URL where the comment was anchored. Cloud reads filter on this. */\n pageUrl?: string;\n /** Captured from `Vizu.setUser` / `options.user` at the moment the comment was created. */\n author?: VizuUser;\n /** Identity provider's user id (Clerk, etc.) when known. Cloud writes set this from the session. */\n authorId?: string;\n /**\n * Element references the user inserted via `#` mentions. Keyed by short refId.\n * The comment text contains tokens like `[#label](vizu-ref:refId)` that resolve\n * to these fingerprints; clicking a rendered reference jumps to the element.\n */\n references?: Record<string, ElementFingerprint>;\n /** Triage state. Defaults to `'open'` on new comments. */\n status: CommentStatus;\n /** Threaded replies. UI ships in v1.1; the array is always present. */\n replies: Reply[];\n /** File attachments. UI ships in v1.2; the array is always present. */\n attachments: Attachment[];\n /** Clerk user ids mentioned in this comment. */\n mentions: string[];\n /**\n * Timestamp (ms) of the last self-healing re-fingerprint pass. Set by\n * the Vizu class when a `drifted` resolution lands on an existing\n * element and the fingerprint is rewritten in place. Reads as null\n * for comments that have never drifted. Surface in the dashboard if\n * you want to flag \"auto-healed\" rows; ignore otherwise.\n */\n fingerprintsRefreshedAt?: number | null;\n}\n\n/* ─── Storage adapter (v2) ─────────────────────────────────────────────── */\n\n/**\n * Storage event emitted by adapters that support push (cloud adapters).\n * Local / memory adapters never emit these.\n */\nexport type StorageEvent =\n | { type: 'added'; comment: VizuComment }\n | { type: 'updated'; comment: VizuComment }\n | { type: 'removed'; id: string }\n | { type: 'cleared' };\n\n/**\n * Optional authorization context an adapter may surface to the host.\n * Cloud adapters set this after the user signs in; the Vizu class\n * forwards it via `vizu.getAuthContext()` for hosts that need to call\n * the same backend with the same session.\n */\nexport interface AuthContext {\n /** Identity provider's user id (e.g. Clerk user id). */\n userId: string;\n /** Short-lived bearer token. Adapter is responsible for refresh. */\n token: string;\n /** ISO timestamp the token expires; informational. */\n expiresAt?: string;\n}\n\n/**\n * Storage contract v2. Per-comment operations replace v1's full-array\n * `save(comments[])`. Cloud-friendly: each mutation is one network call,\n * the adapter can do optimistic-concurrency / conflict resolution\n * without re-shipping the whole list.\n *\n * Hosts that wrote against v1 (`load / save / clear`) are still accepted —\n * see `wrapV1Adapter` in `storage.ts` for the back-compat shim.\n *\n * Reply ops (`addReply` / `removeReply`) are optional: adapters that don't\n * implement them fall back to `updateComment` with a manually-patched\n * `replies` array. Wrapping logic lives in the Vizu class, not here.\n */\nexport interface StorageAdapter {\n /** Load all comments for this namespace. Called once at boot, plus on `setComments`. */\n load(namespace: string): Promise<VizuComment[]>;\n /** Persist a new comment. */\n addComment(namespace: string, comment: VizuComment): Promise<void>;\n /** Patch an existing comment by id. Adapter returns the updated doc or `null` if not found. */\n updateComment(namespace: string, id: string, patch: Partial<VizuComment>): Promise<VizuComment | null>;\n /** Remove a comment by id. */\n removeComment(namespace: string, id: string): Promise<void>;\n /** Replace the entire list (used by hosts that hydrate from their own backend). */\n setAll(namespace: string, comments: VizuComment[]): Promise<void>;\n /** Wipe the namespace. */\n clear(namespace: string): Promise<void>;\n /** Append a reply to a comment. Adapter returns the parent or null if not found. */\n addReply?(namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null>;\n /** Remove a reply by id from a comment. Adapter returns the parent or null if not found. */\n removeReply?(namespace: string, commentId: string, replyId: string): Promise<VizuComment | null>;\n /**\n * Upload a file attachment. Returns the resolved {@link Attachment} with\n * a public URL the host can persist on a comment. Adapters that don't\n * implement this surface report `vizu.canUploadAttachments() === false`\n * and the popover hides the upload UI.\n */\n uploadAttachment?(namespace: string, file: File): Promise<Attachment>;\n /** Optional remote-change subscription. Returns an unsubscribe function. */\n subscribe?(namespace: string, handler: (event: StorageEvent) => void): () => void;\n /** Optional auth context (cloud adapters). */\n getAuthContext?(): AuthContext | null;\n}\n\n/**\n * Legacy v1 adapter shape. Kept exported so hosts can still type their\n * existing adapters; the resolver wraps them automatically.\n */\nexport interface StorageAdapterV1 {\n load(namespace: string): Promise<VizuComment[]>;\n save(namespace: string, comments: VizuComment[]): Promise<void>;\n clear(namespace: string): Promise<void>;\n}\n\nexport type StorageOption = 'local' | 'memory' | 'none' | StorageAdapter | StorageAdapterV1;\n\n/* ─── Action API ───────────────────────────────────────────────────────── */\n\n/** Context handed to every action's onClick. Has the data and the side-effect helpers. */\nexport interface ActionContext {\n comments: VizuComment[];\n pageHtml: string;\n pageUrl?: string;\n pageVersion?: string;\n user?: VizuUser;\n timestamp: number;\n /** Quick copy-to-clipboard helper. */\n copyToClipboard: (text: string) => void;\n /** Briefly show a toast above the pill. */\n toast: (message: string) => void;\n}\n\nexport interface VizuAction {\n id: string;\n label: string;\n /** Tooltip / aria-label */\n title?: string;\n variant?: 'primary' | 'default' | 'ghost';\n /** Placement in the pill. Defaults to 'main'. */\n position?: 'main';\n onClick: (ctx: ActionContext) => void | Promise<void>;\n /** Only render the action when this returns true. */\n visibleWhen?: (ctx: { commentsCount: number }) => boolean;\n}\n\n/* ─── Cloud option ─────────────────────────────────────────────────────── */\n\n/**\n * Cloud connection options. When set, the resolver constructs the built-in\n * CloudStorageAdapter (Phase 3) and uses it instead of `storage`. If both\n * `cloud` and `storage` are provided, `cloud` wins and a `console.warn`\n * is emitted at construction time.\n */\nexport interface VizuCloudOptions {\n /** Workspace slug. Required. Created in the Vizu dashboard. */\n workspace: string;\n /** Override the API base URL. Defaults to `'https://vizu.unhingged.com'`. */\n apiUrl?: string;\n /**\n * Auto-open the popup sign-in window when a write is attempted by an\n * unauthenticated user. Defaults to `true`.\n */\n autoSignIn?: boolean;\n}\n\nexport interface VizuOptions {\n shortcut?: string;\n namespace?: string;\n pageVersion?: string;\n /**\n * Storage strategy. Default behavior:\n * - **Programmatic** (`new Vizu()`): defaults to `'memory'`; host should listen to events to persist.\n * - **Script-tag auto-init**: defaults to `'local'` for zero-config drop-in (override via `data-storage=\"memory\"` or `data-storage=\"none\"`).\n *\n * When `cloud` is set this option is ignored.\n */\n storage?: StorageOption;\n /** Cloud workspace connection. Overrides `storage`. See {@link VizuCloudOptions}. */\n cloud?: VizuCloudOptions;\n ignoreSelectors?: string[];\n accent?: string;\n startEnabled?: boolean;\n user?: VizuUser;\n /** Actions to register on construction. You can also call `vizu.addAction(...)` later. */\n actions?: VizuAction[];\n /**\n * Convenience callbacks. These are aliases for `.on('comment:added', ...)` etc.\n * The event API is preferred for new code.\n */\n onCommentAdded?: (c: VizuComment) => void;\n onCommentRemoved?: (id: string) => void;\n}\n\n/* ─── Event bus ────────────────────────────────────────────────────────── */\n\n/**\n * Typed event map. Every event payload is an object (never bare values) so the\n * shape is forward-compatible.\n */\nexport interface VizuEventMap {\n 'enabled': Record<string, never>;\n 'disabled': Record<string, never>;\n 'mounted': Record<string, never>;\n 'unmounted': Record<string, never>;\n 'comment:added': { comment: VizuComment };\n 'comment:removed': { id: string; comment: VizuComment };\n 'comment:updated': { comment: VizuComment };\n 'comments:cleared': { previous: VizuComment[] };\n 'comments:set': { comments: VizuComment[] };\n 'comments:loaded': { comments: VizuComment[] };\n 'element:selected': { target: Element; fingerprint: ElementFingerprint };\n 'element:deselected': Record<string, never>;\n 'sidebar:opened': Record<string, never>;\n 'sidebar:closed': Record<string, never>;\n 'action:invoked': { id: string };\n 'user:changed': { user: VizuUser | null };\n /**\n * Emitted once per comment when the lib resolves (or fails to resolve)\n * its fingerprint against the current DOM. `confidence: 'orphaned'`\n * means the comment is in the sidebar's orphaned section.\n */\n 'anchor:resolved': {\n comment: VizuComment;\n confidence: MatchConfidence;\n element: Element | null;\n };\n}\n\nexport type VizuEventName = keyof VizuEventMap;\nexport type VizuEventHandler<E extends VizuEventName> = (payload: VizuEventMap[E]) => void;\n","import { SCHEMA_VERSION } from './types';\nimport type { VizuUser, VizuComment, ElementFingerprint, CommentStatus, Attachment } from './types';\n\nexport function uuid(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return crypto.randomUUID();\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nexport function isInside(target: Element, selector: string): boolean {\n return !!target.closest(selector);\n}\n\nexport function escapeHtml(s: string | null | undefined): string {\n if (s == null) return '';\n return String(s).replace(/[&<>\"']/g, (ch) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[ch]!));\n}\n\nexport function formatTime(ts: number): string {\n const d = new Date(ts);\n const now = Date.now();\n const diff = now - ts;\n if (diff < 60_000) return 'just now';\n if (diff < 3_600_000) return Math.floor(diff / 60_000) + 'm ago';\n if (diff < 86_400_000) return Math.floor(diff / 3_600_000) + 'h ago';\n return d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n}\n\nexport function initials(name: string | null | undefined): string {\n if (!name) return '';\n return String(name)\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((p) => p[0]!.toUpperCase())\n .join('');\n}\n\nexport function avatarHtml(user: VizuUser | undefined | null, className = 'vz-comment-author-avatar'): string {\n if (!user) return `<span class=\"${className}\">·</span>`;\n // Defensive: author payloads from older clients / migrated data may\n // omit `name`. Fall back through id → 'User' so we never feed\n // undefined to escapeHtml or initials and crash the whole render.\n const displayName = user.name || user.id || 'User';\n if (user.avatarUrl) {\n return `<span class=\"${className}\"><img src=\"${escapeHtml(user.avatarUrl)}\" alt=\"${escapeHtml(displayName)}\" /></span>`;\n }\n return `<span class=\"${className}\">${escapeHtml(initials(displayName) || '?')}</span>`;\n}\n\n/** Short alphanumeric id for ref tokens. */\nexport function refId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nconst REF_TOKEN_RE = /\\[#([^\\]]+)\\]\\(vizu-ref:([a-zA-Z0-9_-]+)\\)/g;\nconst MENTION_TOKEN_RE = /\\[@([^\\]]+)\\]\\(vizu-mention:([a-zA-Z0-9_-]+)\\)/g;\nconst COMBINED_TOKEN_SPLIT_RE = /(\\[#[^\\]]+\\]\\(vizu-ref:[a-zA-Z0-9_-]+\\)|\\[@[^\\]]+\\]\\(vizu-mention:[a-zA-Z0-9_-]+\\))/g;\n\n/**\n * Render a comment's text as HTML, replacing inline tokens with chips:\n *\n * - `[#label](vizu-ref:refId)` → element-reference chip (delegates clicks\n * to find `[data-vz=\"ref\"]` and looks up `comment.references[refId]`).\n * - `[@display name](vizu-mention:userId)` → mention chip. Static for v1\n * (no click target); shows the display name with an @ prefix.\n */\nexport function renderCommentText(text: string, commentId: string): string {\n const parts = text.split(COMBINED_TOKEN_SPLIT_RE);\n return parts\n .map((part) => {\n const refMatch = part.match(/^\\[#([^\\]]+)\\]\\(vizu-ref:([a-zA-Z0-9_-]+)\\)$/);\n if (refMatch) {\n return `<a class=\"vz-ref\" data-vz=\"ref\" data-comment-id=\"${escapeHtml(commentId)}\" data-ref-id=\"${escapeHtml(refMatch[2])}\" role=\"button\" tabindex=\"0\">${escapeHtml(refMatch[1])}</a>`;\n }\n const mentionMatch = part.match(/^\\[@([^\\]]+)\\]\\(vizu-mention:([a-zA-Z0-9_-]+)\\)$/);\n if (mentionMatch) {\n return `<span class=\"vz-mention\" data-vz=\"mention\" data-mention-id=\"${escapeHtml(mentionMatch[2])}\">@${escapeHtml(mentionMatch[1])}</span>`;\n }\n return escapeHtml(part);\n })\n .join('');\n}\n\n/**\n * Extract Clerk user ids from `[@name](vizu-mention:userId)` tokens in a\n * comment's text. Used at save time to populate `comment.mentions[]` so\n * downstream consumers (the dashboard, the notification path) can find\n * who was mentioned without re-parsing the text.\n */\nexport function extractMentions(text: string): string[] {\n const out: string[] = [];\n for (const m of text.matchAll(MENTION_TOKEN_RE)) {\n if (!out.includes(m[2])) out.push(m[2]);\n }\n return out;\n}\n\nexport { REF_TOKEN_RE, MENTION_TOKEN_RE };\n\n/**\n * Render a comment's attachment list as HTML — image thumbnails for\n * image MIME types, a generic file chip otherwise. Click opens the URL\n * in a new tab. Used in both popover + sidebar so the look matches.\n *\n * Returns the empty string when the comment has no attachments so the\n * caller can interpolate unconditionally.\n */\nexport function renderAttachmentsHtml(attachments: Attachment[] | undefined): string {\n if (!Array.isArray(attachments) || attachments.length === 0) return '';\n const items = attachments\n .map((a) => {\n const isImage = a.mimeType?.startsWith('image/');\n const filename = a.filename ?? a.url.split('/').pop() ?? 'file';\n if (isImage) {\n return `\n <a class=\"vz-attachment-display\" href=\"${escapeHtml(a.url)}\" target=\"_blank\" rel=\"noreferrer\" title=\"${escapeHtml(filename)}\">\n <img src=\"${escapeHtml(a.url)}\" alt=\"${escapeHtml(filename)}\" loading=\"lazy\" />\n </a>\n `;\n }\n return `\n <a class=\"vz-attachment-display vz-attachment-file\" href=\"${escapeHtml(a.url)}\" target=\"_blank\" rel=\"noreferrer\">\n <span class=\"vz-attachment-icon\" aria-hidden=\"true\">📄</span>\n <span class=\"vz-attachment-filename\">${escapeHtml(filename)}</span>\n </a>\n `;\n })\n .join('');\n return `<div class=\"vz-attachment-grid\">${items}</div>`;\n}\n\n/**\n * Forward-migrate a comment from any prior schema version to the current one.\n *\n * Idempotent: passing an already-v{@link SCHEMA_VERSION} comment returns a\n * structurally-equivalent value (extra/legacy fields stripped, missing\n * defaults filled). Safe to call on every read; cost is one shallow clone.\n *\n * Versions:\n * - **v1** (unstamped): had a singular `fingerprint`; no status/replies/attachments/mentions.\n * - **v2** (current): `fingerprints[]`, `schemaVersion`, `status`, `replies`,\n * `attachments`, `mentions`. `pageUrl` replaces the older `url`.\n */\nexport function migrateComment(raw: any): VizuComment {\n if (!raw || typeof raw !== 'object') return raw as VizuComment;\n\n // Start from a shallow clone; we'll mutate a new object so callers never\n // observe partial migration.\n const next: any = { ...raw };\n\n // ─── v1 → v2: singular fingerprint becomes fingerprints[].\n if (!Array.isArray(next.fingerprints) || next.fingerprints.length === 0) {\n if (next.fingerprint) {\n next.fingerprints = [next.fingerprint as ElementFingerprint];\n }\n }\n delete next.fingerprint;\n\n // ─── v1 → v2: `url` (host page URL at write time) becomes `pageUrl`.\n if (next.url && !next.pageUrl) {\n next.pageUrl = next.url;\n }\n\n // ─── Default the v2-required fields that v1 payloads don't carry.\n if (!('status' in next) || !isValidStatus(next.status)) next.status = 'open' as CommentStatus;\n if (!Array.isArray(next.replies)) next.replies = [];\n if (!Array.isArray(next.attachments)) next.attachments = [];\n if (!Array.isArray(next.mentions)) next.mentions = [];\n // ─── Phase 11.4.4.3: self-healing timestamp. Optional; null for comments\n // that haven't been re-fingerprinted. Don't overwrite an existing value.\n if (!('fingerprintsRefreshedAt' in next)) next.fingerprintsRefreshedAt = null;\n\n // ─── Always stamp the current schema version.\n next.schemaVersion = SCHEMA_VERSION;\n\n return next as VizuComment;\n}\n\nfunction isValidStatus(s: unknown): s is CommentStatus {\n return s === 'open' || s === 'resolved' || s === 'wontfix';\n}\n\n/** Forward-migrate an array of comments in one call. Skips falsy entries. */\nexport function migrateComments(raws: unknown[]): VizuComment[] {\n if (!Array.isArray(raws)) return [];\n const out: VizuComment[] = [];\n for (const raw of raws) {\n if (!raw || typeof raw !== 'object') continue;\n out.push(migrateComment(raw));\n }\n return out;\n}\n\n/**\n * Returns true if any comment in the list is older than the current schema\n * (e.g. is missing `schemaVersion`, has the singular `fingerprint`, or is\n * missing one of the v2 default arrays). Used by the load path to decide\n * whether to re-persist after migration.\n */\nexport function needsPersist(raws: unknown[]): boolean {\n if (!Array.isArray(raws)) return false;\n for (const raw of raws) {\n if (!raw || typeof raw !== 'object') continue;\n const c = raw as Record<string, unknown>;\n if (c.schemaVersion !== SCHEMA_VERSION) return true;\n if ('fingerprint' in c) return true;\n if (!Array.isArray(c.replies)) return true;\n if (!Array.isArray(c.attachments)) return true;\n if (!Array.isArray(c.mentions)) return true;\n }\n return false;\n}\n","import type {\n StorageAdapter,\n StorageAdapterV1,\n StorageEvent,\n VizuComment,\n Reply,\n} from './types';\nimport { migrateComments } from './util';\n\nconst key = (ns: string) => `vizu:comments:${ns}`;\n\n/**\n * Persist comments to the host page's localStorage. One JSON document per\n * namespace. Migrations are applied on read.\n *\n * v2 per-comment ops are emulated via read-modify-write of the underlying\n * array — fine for localStorage's <1ms cost. Cloud adapters do real\n * single-document writes.\n */\nexport class LocalStorageAdapter implements StorageAdapter {\n async load(namespace: string): Promise<VizuComment[]> {\n return readArray(namespace);\n }\n\n async addComment(namespace: string, comment: VizuComment): Promise<void> {\n const list = readArray(namespace);\n list.push(comment);\n writeArray(namespace, list);\n }\n\n async updateComment(\n namespace: string,\n id: string,\n patch: Partial<VizuComment>,\n ): Promise<VizuComment | null> {\n const list = readArray(namespace);\n const idx = list.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n const next = { ...list[idx], ...patch, id: list[idx].id } as VizuComment;\n list[idx] = next;\n writeArray(namespace, list);\n return next;\n }\n\n async removeComment(namespace: string, id: string): Promise<void> {\n const list = readArray(namespace);\n writeArray(namespace, list.filter((c) => c.id !== id));\n }\n\n async setAll(namespace: string, comments: VizuComment[]): Promise<void> {\n writeArray(namespace, comments);\n }\n\n async clear(namespace: string): Promise<void> {\n if (typeof localStorage === 'undefined') return;\n localStorage.removeItem(key(namespace));\n }\n\n async addReply(namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null> {\n const list = readArray(namespace);\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = { ...list[idx], replies: [...(list[idx].replies ?? []), reply] } as VizuComment;\n list[idx] = next;\n writeArray(namespace, list);\n return next;\n }\n\n async removeReply(namespace: string, commentId: string, replyId: string): Promise<VizuComment | null> {\n const list = readArray(namespace);\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = {\n ...list[idx],\n replies: (list[idx].replies ?? []).filter((r) => r.id !== replyId),\n } as VizuComment;\n list[idx] = next;\n writeArray(namespace, list);\n return next;\n }\n}\n\nfunction readArray(namespace: string): VizuComment[] {\n if (typeof localStorage === 'undefined') return [];\n const raw = localStorage.getItem(key(namespace));\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? migrateComments(parsed) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeArray(namespace: string, comments: VizuComment[]): void {\n if (typeof localStorage === 'undefined') return;\n localStorage.setItem(key(namespace), JSON.stringify(comments));\n}\n\n/**\n * Stores comments in RAM only — wipes on reload. Useful when the host owns\n * persistence via events (the integrator listens to `comment:added` and\n * forwards to its own backend).\n */\nexport class InMemoryStorageAdapter implements StorageAdapter {\n private store = new Map<string, VizuComment[]>();\n\n async load(namespace: string): Promise<VizuComment[]> {\n return [...(this.store.get(namespace) || [])];\n }\n\n async addComment(namespace: string, comment: VizuComment): Promise<void> {\n const list = this.store.get(namespace) ?? [];\n this.store.set(namespace, [...list, comment]);\n }\n\n async updateComment(\n namespace: string,\n id: string,\n patch: Partial<VizuComment>,\n ): Promise<VizuComment | null> {\n const list = this.store.get(namespace) ?? [];\n const idx = list.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n const next = { ...list[idx], ...patch, id: list[idx].id } as VizuComment;\n const copy = [...list];\n copy[idx] = next;\n this.store.set(namespace, copy);\n return next;\n }\n\n async removeComment(namespace: string, id: string): Promise<void> {\n const list = this.store.get(namespace) ?? [];\n this.store.set(\n namespace,\n list.filter((c) => c.id !== id),\n );\n }\n\n async setAll(namespace: string, comments: VizuComment[]): Promise<void> {\n this.store.set(namespace, [...comments]);\n }\n\n async clear(namespace: string): Promise<void> {\n this.store.delete(namespace);\n }\n\n async addReply(namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null> {\n const list = this.store.get(namespace) ?? [];\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = { ...list[idx], replies: [...(list[idx].replies ?? []), reply] } as VizuComment;\n const copy = [...list];\n copy[idx] = next;\n this.store.set(namespace, copy);\n return next;\n }\n\n async removeReply(namespace: string, commentId: string, replyId: string): Promise<VizuComment | null> {\n const list = this.store.get(namespace) ?? [];\n const idx = list.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const next = {\n ...list[idx],\n replies: (list[idx].replies ?? []).filter((r) => r.id !== replyId),\n } as VizuComment;\n const copy = [...list];\n copy[idx] = next;\n this.store.set(namespace, copy);\n return next;\n }\n}\n\n/**\n * No-op adapter — never persists. The host hydrates via `setComments` and\n * listens to events to drive its own storage.\n */\nexport class NullStorageAdapter implements StorageAdapter {\n async load(): Promise<VizuComment[]> {\n return [];\n }\n async addComment(): Promise<void> {}\n async updateComment(): Promise<VizuComment | null> {\n return null;\n }\n async removeComment(): Promise<void> {}\n async setAll(): Promise<void> {}\n async clear(): Promise<void> {}\n}\n\n/* ─── v1 back-compat shim ──────────────────────────────────────────────── */\n\n/**\n * Detect whether a user-supplied object satisfies the v1 adapter contract\n * (`load / save / clear`) but is missing v2's per-comment ops. We treat\n * any object with a `save` method that lacks `addComment` as v1.\n */\nexport function isV1Adapter(x: unknown): x is StorageAdapterV1 {\n if (!x || typeof x !== 'object') return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.load === 'function' &&\n typeof o.save === 'function' &&\n typeof o.clear === 'function' &&\n typeof o.addComment !== 'function'\n );\n}\n\n/**\n * Wrap a v1 adapter so it satisfies the v2 interface. Per-comment ops are\n * emulated by read-modify-write of the full array — slow but correct.\n * Hosts get a one-time `console.warn` so they know to upgrade.\n *\n * Idempotent: wrapping an already-v2 adapter returns it unchanged.\n */\nexport function wrapV1Adapter(v1: StorageAdapterV1): StorageAdapter {\n if (!isV1Adapter(v1)) return v1 as unknown as StorageAdapter;\n let warned = false;\n const warnOnce = () => {\n if (warned) return;\n warned = true;\n if (typeof console !== 'undefined') {\n console.warn(\n '[vizu] StorageAdapter v1 detected. Per-comment ops are emulated via full-array rewrites — ' +\n 'upgrade your adapter to the v2 interface (addComment / updateComment / removeComment / setAll / load / clear) ' +\n 'when you can.',\n );\n }\n };\n\n return {\n async load(namespace: string) {\n warnOnce();\n return v1.load(namespace);\n },\n async addComment(namespace, comment) {\n warnOnce();\n const list = await v1.load(namespace);\n list.push(comment);\n await v1.save(namespace, list);\n },\n async updateComment(namespace, id, patch) {\n warnOnce();\n const list = await v1.load(namespace);\n const idx = list.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n const next = { ...list[idx], ...patch, id: list[idx].id } as VizuComment;\n list[idx] = next;\n await v1.save(namespace, list);\n return next;\n },\n async removeComment(namespace, id) {\n warnOnce();\n const list = await v1.load(namespace);\n await v1.save(\n namespace,\n list.filter((c) => c.id !== id),\n );\n },\n async setAll(namespace, comments) {\n warnOnce();\n await v1.save(namespace, comments);\n },\n async clear(namespace) {\n await v1.clear(namespace);\n },\n };\n}\n\n// Re-export the event type so adapter authors only need one import.\nexport type { StorageEvent };\n","import type {\n StorageAdapter,\n StorageEvent,\n VizuComment,\n VizuUser,\n AuthContext,\n Reply,\n Attachment,\n MentionableUser,\n} from './types';\nimport { migrateComments, migrateComment, uuid } from './util';\n\n/**\n * CloudStorageAdapter — talks to the Vizu hosted backend.\n *\n * Lifecycle:\n * - Reads are unauthenticated-attempted first (the dashboard's own\n * reads are same-origin Clerk session). If the backend says 401, we\n * open the popup once and retry.\n * - Writes require a host-token. First write opens the popup; the\n * resulting JWT is stored in sessionStorage keyed by workspace slug\n * and reused for the tab's lifetime (or until `exp` lapses).\n *\n * Token storage = sessionStorage (per-tab). Cross-tab is intentional:\n * a stolen token from one tab can't leak to another via storage events.\n * The cost is one popup per tab; cheap for the safety win.\n *\n * The popup is hosted at `${apiUrl}/connect?workspace=&origin=`. It\n * handles the Clerk sign-in handshake and posts the token back via\n * `postMessage` targeted at the host origin (never `*`).\n */\nexport interface CloudStorageOptions {\n /** Workspace slug — required. */\n workspace: string;\n /** API base URL (defaults to https://vizu.unhingged.com). */\n apiUrl?: string;\n /** Open the sign-in popup automatically on the first auth-needed write. Defaults to true. */\n autoSignIn?: boolean;\n /**\n * Fired whenever the cached host-token transitions to authenticated —\n * either freshly from the popup or rehydrated from sessionStorage.\n * Vizu wires this to `setUser()` so comments attributed after sign-in\n * carry the proper display name + avatar.\n */\n onAuthChanged?: (info: { userId: string; user: VizuUser | null }) => void;\n}\n\ninterface StoredToken {\n token: string;\n expiresAt: string; // ISO\n userId: string;\n /** Display info from the popup's postMessage; may be null on legacy popups. */\n user: VizuUser | null;\n}\n\nconst DEFAULT_API_URL = 'https://vizu.unhingged.com';\nconst POPUP_WIDTH = 480;\nconst POPUP_HEIGHT = 640;\n\nexport class CloudStorageAdapter implements StorageAdapter {\n private readonly apiUrl: string;\n private readonly workspace: string;\n private readonly autoSignIn: boolean;\n private onAuthChanged: CloudStorageOptions['onAuthChanged'];\n /** Cached current-tab token. Mirrored to sessionStorage. */\n private cachedToken: StoredToken | null = null;\n /** Single-flight: at most one popup at a time. Subsequent requests await this. */\n private pendingAuth: Promise<StoredToken> | null = null;\n /** Whether we've already fired onAuthChanged for the current token. */\n private lastNotifiedTokenId: string | null = null;\n /**\n * Sticky flag set when /connect tells us this workspace is inaccessible\n * to the signed-in user (no role, origin not allowed, workspace gone).\n * Future resolveToken calls reject immediately instead of reopening the\n * popup — the answer won't change without a Clerk account switch, which\n * a page reload covers. Cleared only by reload (or future explicit API).\n */\n private accessDenied = false;\n /**\n * Last denial reason from /connect's postMessage — useful for hosts that\n * want to render a workspace-level \"no access\" banner. Null until denied.\n */\n private accessDeniedReason: string | null = null;\n\n constructor(opts: CloudStorageOptions) {\n this.workspace = opts.workspace;\n this.apiUrl = (opts.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, '');\n this.autoSignIn = opts.autoSignIn !== false;\n this.onAuthChanged = opts.onAuthChanged;\n // Redirect-flow hand-off: when the page reloaded after /connect's\n // redirect-back (popup-blocked fallback), the token rides in the\n // URL fragment. Hydrate first so it wins over any stale value, then\n // strip the fragment so it doesn't leak into history / bookmarks.\n const fromHash = this.readHashToken();\n if (fromHash) {\n this.cachedToken = fromHash;\n this.writeStoredToken(fromHash);\n } else {\n // Popup-flow + warm-reload path: sessionStorage already holds a\n // valid token from a prior popup hand-off.\n this.cachedToken = this.readStoredToken();\n }\n if (this.cachedToken && !this.isExpired(this.cachedToken)) {\n this.fireAuthChanged(this.cachedToken);\n }\n }\n\n /**\n * Pull a token out of the URL fragment if /connect just redirected us\n * back here. Strips the fragment on success so the token doesn't sit\n * in browser history or get shared if the user copies the URL.\n *\n * The fragment is base64-encoded JSON with compact keys; see\n * apps/landing/app/connect/RedirectComplete.tsx for the producer side.\n */\n private readHashToken(): StoredToken | null {\n if (typeof window === 'undefined') return null;\n const hash = window.location.hash;\n if (!hash || hash.length < 2) return null;\n const params = new URLSearchParams(hash.slice(1));\n const payload = params.get('vizu_auth');\n if (!payload) return null;\n try {\n // atob returns a binary string (one Latin-1 char per byte). The\n // server encoded the JSON as UTF-8 before base64-ing, so naked\n // atob+JSON.parse mojibake's non-ASCII names (á → á, etc).\n // Round-trip through TextDecoder to recover the original UTF-8.\n const bytes = Uint8Array.from(atob(payload), (c) => c.charCodeAt(0));\n const decoded = new TextDecoder('utf-8').decode(bytes);\n const json = JSON.parse(decoded) as {\n t?: string;\n e?: string;\n u?: string;\n w?: string;\n n?: string;\n a?: string;\n };\n if (!json.t || !json.e || !json.u || !json.w) return null;\n if (json.w !== this.workspace) return null;\n const stored: StoredToken = {\n token: json.t,\n expiresAt: json.e,\n userId: json.u,\n user:\n typeof json.n === 'string'\n ? {\n id: json.u,\n name: json.n,\n avatarUrl: typeof json.a === 'string' ? json.a : undefined,\n }\n : null,\n };\n // Strip vizu_auth from the hash; preserve any other fragment.\n params.delete('vizu_auth');\n const remaining = params.toString();\n const newHash = remaining ? `#${remaining}` : '';\n try {\n const newUrl =\n window.location.pathname + window.location.search + newHash;\n window.history.replaceState(null, '', newUrl);\n } catch {\n /* history API may be restricted (sandbox); not fatal */\n }\n return stored;\n } catch {\n return null;\n }\n }\n\n /**\n * Open the sign-in popup right now (or no-op if a valid token is\n * cached). Vizu calls this on enable() so the user signs in BEFORE\n * trying to leave a comment — no surprise modal mid-comment.\n *\n * Throws auth_canceled if the user closes the popup. Caller can\n * decide whether to disable the surface or leave it in a read-only\n * state.\n */\n async preflightAuth(): Promise<void> {\n await this.resolveToken();\n }\n\n /**\n * Whether /connect has already told us the user can't access this\n * workspace. Vizu's write gate checks this before reopening the popup —\n * a denied state means clicking again will yield the same \"No access\"\n * page, so we suppress the retry until a page reload (or explicit API\n * call in the future).\n */\n isAccessDenied(): boolean {\n return this.accessDenied;\n }\n\n /** Last denial reason; null when not denied. */\n accessDenialReason(): string | null {\n return this.accessDeniedReason;\n }\n\n /**\n * Fetch the list of users who can be @-mentioned on this workspace.\n * Used by the popover's mention dropdown — returns owner + joined\n * editors/viewers, sorted owner-first. Silent on no auth (returns [])\n * so popover doesn't open a popup just to render an empty dropdown.\n */\n async searchMentionable(): Promise<MentionableUser[]> {\n if (!this.cachedToken || this.isExpired(this.cachedToken)) return [];\n try {\n const res = await this.fetchAuthed(\n this.apiUrl + '/api/workspaces/' + encodeURIComponent(this.workspace) + '/mentionable',\n { method: 'GET' },\n );\n if (!res.ok) return [];\n const json = (await this.parseJson(res)) as { users?: MentionableUser[] };\n return Array.isArray(json?.users) ? json.users : [];\n } catch {\n return [];\n }\n }\n\n /* ─── StorageAdapter v2 ───────────────────────────────────────────── */\n\n async load(_namespace: string): Promise<VizuComment[]> {\n // Silent on no auth: reading comments at page load must NEVER open\n // a sign-in popup. The user hasn't shown intent yet — Vizu may not\n // even be enabled. If no token is cached, return empty; the host\n // will re-call load() via the onAuthChanged callback once the user\n // explicitly signs in (shortcut or click-to-comment).\n if (!this.cachedToken || this.isExpired(this.cachedToken)) {\n // Cheap second look — another tab may have refreshed sessionStorage.\n const fresh = this.readStoredToken();\n if (!fresh || this.isExpired(fresh)) return [];\n this.cachedToken = fresh;\n }\n // Pagination: pull all pages until nextCursor === null.\n const out: VizuComment[] = [];\n let cursor: string | null = null;\n do {\n const url = new URL(this.apiUrl + '/api/comments');\n url.searchParams.set('workspace', this.workspace);\n url.searchParams.set('limit', '100');\n if (cursor) url.searchParams.set('cursor', cursor);\n const res = await this.fetchAuthed(url.toString(), { method: 'GET' });\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n const page = Array.isArray(json.comments) ? json.comments : [];\n out.push(...migrateComments(page));\n cursor = typeof json.nextCursor === 'string' ? json.nextCursor : null;\n } while (cursor);\n return out;\n }\n\n async addComment(_namespace: string, comment: VizuComment): Promise<void> {\n const res = await this.fetchAuthed(this.apiUrl + '/api/comments', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ workspace: this.workspace, comment }),\n });\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n }\n\n async updateComment(\n _namespace: string,\n id: string,\n patch: Partial<VizuComment>,\n ): Promise<VizuComment | null> {\n const url = new URL(this.apiUrl + '/api/comments/' + encodeURIComponent(id));\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(patch),\n });\n if (res.status === 404) return null;\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n return migrateComment(json.comment);\n }\n\n async removeComment(_namespace: string, id: string): Promise<void> {\n const url = new URL(this.apiUrl + '/api/comments/' + encodeURIComponent(id));\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), { method: 'DELETE' });\n if (res.status === 404) return; // already gone — fine\n if (!res.ok) {\n const json = await this.parseJson(res);\n throw apiErr(res.status, json);\n }\n }\n\n async setAll(namespace: string, comments: VizuComment[]): Promise<void> {\n // No bulk-replace endpoint by design (rare op; expensive in Mongo).\n // Wipe then re-add sequentially.\n await this.clear(namespace);\n for (const c of comments) {\n await this.addComment(namespace, c);\n }\n }\n\n async clear(_namespace: string): Promise<void> {\n const url = new URL(this.apiUrl + '/api/comments');\n url.searchParams.set('workspace', this.workspace);\n url.searchParams.set('all', 'true');\n const res = await this.fetchAuthed(url.toString(), { method: 'DELETE' });\n if (res.status === 404) return;\n if (!res.ok) {\n const json = await this.parseJson(res);\n throw apiErr(res.status, json);\n }\n }\n\n async addReply(_namespace: string, commentId: string, reply: Reply): Promise<VizuComment | null> {\n const url = new URL(this.apiUrl + '/api/comments/' + encodeURIComponent(commentId) + '/replies');\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(reply),\n });\n if (res.status === 404) return null;\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n return json?.comment ? migrateComment(json.comment) : null;\n }\n\n async removeReply(_namespace: string, commentId: string, replyId: string): Promise<VizuComment | null> {\n const url = new URL(\n this.apiUrl +\n '/api/comments/' +\n encodeURIComponent(commentId) +\n '/replies/' +\n encodeURIComponent(replyId),\n );\n url.searchParams.set('workspace', this.workspace);\n const res = await this.fetchAuthed(url.toString(), { method: 'DELETE' });\n if (res.status === 404) return null;\n if (!res.ok) {\n const json = await this.parseJson(res);\n throw apiErr(res.status, json);\n }\n // Backend returns 204 on success; caller re-reads if it needs the latest doc.\n return null;\n }\n\n /**\n * Upload a file as multipart/form-data to the host backend, which\n * forwards to Vercel Blob server-side. Stays under the 4.5 MB Vercel\n * function body cap (our own 5 MB attachment cap will be enforced\n * server-side before that's hit).\n *\n * Deliberately doesn't use `@vercel/blob/client`'s direct-upload flow:\n * - keeps `@vizu/core` free of any `@vercel/blob` dependency\n * (script-tag and local-only consumers don't pull blob code)\n * - the multipart path is one round-trip; the direct-upload flow\n * is two (signed URL + upload). For attachments capped at 5 MB,\n * the savings don't justify the dep cost.\n */\n async uploadAttachment(_namespace: string, file: File): Promise<Attachment> {\n const form = new FormData();\n form.append('file', file, file.name);\n\n const url = new URL(this.apiUrl + '/api/uploads');\n url.searchParams.set('workspace', this.workspace);\n\n const res = await this.fetchAuthed(url.toString(), {\n method: 'POST',\n body: form,\n // Don't set content-type; the browser fills in the multipart boundary.\n });\n const json = await this.parseJson(res);\n if (!res.ok) throw apiErr(res.status, json);\n if (!json?.url || typeof json.url !== 'string') {\n throw apiErr(500, { error: 'upload_failed', message: 'Backend did not return a URL.' });\n }\n return {\n id: uuid(),\n url: json.url,\n mimeType: file.type || 'application/octet-stream',\n sizeBytes: file.size,\n uploadedAt: Date.now(),\n filename: file.name,\n };\n }\n\n getAuthContext(): AuthContext | null {\n if (!this.cachedToken) return null;\n return {\n userId: this.cachedToken.userId,\n token: this.cachedToken.token,\n expiresAt: this.cachedToken.expiresAt,\n };\n }\n\n /* ─── Auth ────────────────────────────────────────────────────────── */\n\n private isExpired(t: StoredToken | null): boolean {\n if (!t) return true;\n const exp = new Date(t.expiresAt).getTime();\n // Treat tokens within 30s of expiry as expired (slop for in-flight requests).\n return !Number.isFinite(exp) || exp - Date.now() < 30_000;\n }\n\n private readStoredToken(): StoredToken | null {\n if (typeof sessionStorage === 'undefined') return null;\n const raw = sessionStorage.getItem(this.sessionKey());\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as StoredToken;\n if (this.isExpired(parsed)) {\n sessionStorage.removeItem(this.sessionKey());\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n }\n\n private writeStoredToken(t: StoredToken): void {\n this.cachedToken = t;\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.setItem(this.sessionKey(), JSON.stringify(t));\n }\n }\n\n private clearStoredToken(): void {\n this.cachedToken = null;\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.removeItem(this.sessionKey());\n }\n }\n\n private sessionKey(): string {\n // v2: keyspace bumped in 0.1.2 to drop pre-0.1.1 tokens that lack the\n // user payload (the popup didn't postMessage `user` until 0.1.1, so any\n // older sessionStorage entry rehydrates with user: null and the host\n // can't refresh setUser → comments come out as Anonymous for ~15 min).\n // Old keys silently abandoned; they expire on their own.\n return `vizu:cloud-token:v2:${this.workspace}`;\n }\n\n /**\n * Resolve a valid token, opening the popup if needed.\n *\n * Single-flight: simultaneous requests await one popup, not N.\n * Caller-friendly: throws a Error with `.code === 'auth_canceled'`\n * if the user closes the popup; hosts can catch this and present\n * a friendly message instead of a blank failure.\n */\n private async resolveToken(): Promise<StoredToken> {\n if (this.cachedToken && !this.isExpired(this.cachedToken)) return this.cachedToken;\n // Re-check sessionStorage in case another tab refreshed it.\n const fresh = this.readStoredToken();\n if (fresh) {\n this.cachedToken = fresh;\n this.fireAuthChanged(fresh);\n return fresh;\n }\n if (this.accessDenied) {\n throw makeAuthError(\n 'access_denied',\n `No access to workspace \"${this.workspace}\" (${this.accessDeniedReason ?? 'no_access'}).`,\n );\n }\n if (!this.autoSignIn) {\n throw makeAuthError('auth_required', 'Sign-in required; autoSignIn is disabled.');\n }\n if (!this.pendingAuth) {\n this.pendingAuth = this.openSignInPopup()\n .then((t) => {\n this.writeStoredToken(t);\n this.fireAuthChanged(t);\n return t;\n })\n .finally(() => {\n this.pendingAuth = null;\n });\n }\n return this.pendingAuth;\n }\n\n /**\n * Notify the host that the authenticated identity changed. De-duped\n * via the token string so multiple resolveToken() hits with the same\n * cached token don't re-call setUser on every fetch.\n */\n private fireAuthChanged(token: StoredToken): void {\n if (this.lastNotifiedTokenId === token.token) return;\n this.lastNotifiedTokenId = token.token;\n try {\n this.onAuthChanged?.({ userId: token.userId, user: token.user });\n } catch (err) {\n if (typeof console !== 'undefined') {\n console.warn('[vizu/cloud] onAuthChanged callback threw', err);\n }\n }\n }\n\n private openSignInPopup(): Promise<StoredToken> {\n return new Promise<StoredToken>((resolve, reject) => {\n if (typeof window === 'undefined') {\n return reject(makeAuthError('auth_unavailable', 'Sign-in requires a browser environment.'));\n }\n const apiOrigin = new URL(this.apiUrl).origin;\n const hostOrigin = window.location.origin;\n const popupUrl =\n this.apiUrl +\n '/connect?workspace=' +\n encodeURIComponent(this.workspace) +\n '&origin=' +\n encodeURIComponent(hostOrigin);\n\n // Center the popup. Browsers reposition off-screen popups but a\n // sane default is friendlier on multi-monitor setups.\n const left = Math.max(0, Math.round((window.screen.width - POPUP_WIDTH) / 2));\n const top = Math.max(0, Math.round((window.screen.height - POPUP_HEIGHT) / 2));\n const popup = window.open(\n popupUrl,\n 'vizu-connect',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},noopener=no,popup=yes`,\n );\n if (!popup) {\n // Fallback: blocked popup → full-page redirect to /connect.\n // /connect signs the user in, then redirects back to the host\n // page with the token packed into the URL fragment. The post-\n // redirect page load constructs a fresh adapter, which picks\n // the token up via readHashToken() above.\n //\n // This promise stays pending — the page is navigating away.\n // Anything awaiting resolveToken() is about to be torn down\n // along with the rest of the host JS.\n try {\n const returnTo = window.location.href;\n const redirectUrl =\n this.apiUrl +\n '/connect?workspace=' +\n encodeURIComponent(this.workspace) +\n '&origin=' +\n encodeURIComponent(hostOrigin) +\n '&return_to=' +\n encodeURIComponent(returnTo);\n window.location.assign(redirectUrl);\n return;\n } catch (err) {\n return reject(\n makeAuthError(\n 'popup_blocked',\n 'Sign-in popup was blocked and redirect fallback failed: ' +\n (err instanceof Error ? err.message : String(err)),\n ),\n );\n }\n }\n\n let resolved = false;\n const onMessage = (e: MessageEvent) => {\n // Strict origin check — only our /connect popup can satisfy this.\n if (e.origin !== apiOrigin) return;\n const data = e.data as\n | {\n type?: string;\n version?: number;\n token?: string;\n expiresAt?: string;\n userId?: string;\n workspace?: string;\n reason?: string;\n }\n | undefined;\n if (!data) return;\n // Denial: /connect resolved the access check to \"no\" and told us\n // why. Sticky-mark the adapter so we don't retry — same workspace\n // + same Clerk session = same answer.\n if (data.type === 'vizu:auth-denied' && data.workspace === this.workspace) {\n resolved = true;\n cleanup();\n this.accessDenied = true;\n this.accessDeniedReason = typeof data.reason === 'string' ? data.reason : 'no_access';\n reject(\n makeAuthError(\n 'access_denied',\n `No access to workspace \"${this.workspace}\" (${this.accessDeniedReason}).`,\n ),\n );\n return;\n }\n if (data.type !== 'vizu:auth') return;\n if (data.workspace !== this.workspace) return;\n if (typeof data.token !== 'string' || typeof data.expiresAt !== 'string' || typeof data.userId !== 'string') return;\n resolved = true;\n cleanup();\n // Parse the optional `user` payload — sent by the post-v0.1.0\n // /connect popup. Older popups omit it; we attribute as null.\n const userPayload = (data as { user?: { id?: unknown; name?: unknown; avatarUrl?: unknown } }).user;\n const user: VizuUser | null =\n userPayload && typeof userPayload.name === 'string'\n ? {\n id: typeof userPayload.id === 'string' ? userPayload.id : data.userId,\n name: userPayload.name,\n avatarUrl:\n typeof userPayload.avatarUrl === 'string'\n ? userPayload.avatarUrl\n : undefined,\n }\n : null;\n resolve({ token: data.token, expiresAt: data.expiresAt, userId: data.userId, user });\n };\n const onPoll = window.setInterval(() => {\n if (popup.closed) {\n if (!resolved) {\n cleanup();\n reject(makeAuthError('auth_canceled', 'Sign-in popup was closed before completing.'));\n }\n }\n }, 500);\n function cleanup() {\n window.clearInterval(onPoll);\n window.removeEventListener('message', onMessage);\n }\n window.addEventListener('message', onMessage);\n });\n }\n\n /* ─── Fetch wrapper ───────────────────────────────────────────────── */\n\n /**\n * Perform an authenticated fetch with one-shot retry on 401 (re-opens\n * the popup if the token expired during the request). 4xx other than\n * 401 / 404 throw; 5xx throws; 2xx returns.\n */\n private async fetchAuthed(url: string, init: RequestInit, retried = false): Promise<Response> {\n const token = await this.resolveToken();\n const headers = new Headers(init.headers);\n headers.set('authorization', `Bearer ${token.token}`);\n const res = await fetch(url, { ...init, headers, credentials: 'omit' });\n if (res.status === 401 && !retried) {\n this.clearStoredToken();\n return this.fetchAuthed(url, init, true);\n }\n return res;\n }\n\n private async parseJson(res: Response): Promise<any> {\n try {\n const text = await res.text();\n return text ? JSON.parse(text) : null;\n } catch {\n return null;\n }\n }\n}\n\n/* ─── Errors ──────────────────────────────────────────────────────────── */\n\ntype CloudErrorCode =\n | 'auth_required'\n | 'auth_canceled'\n | 'auth_unavailable'\n | 'popup_blocked'\n | 'origin_not_allowed'\n | 'workspace_not_found'\n /** /connect resolved access for the signed-in user → no role on the workspace. Sticky for the session. */\n | 'access_denied'\n | 'http_error';\n\nexport interface CloudError extends Error {\n code: CloudErrorCode;\n status?: number;\n body?: unknown;\n}\n\nfunction makeAuthError(code: CloudErrorCode, message: string): CloudError {\n const err = new Error(message) as CloudError;\n err.code = code;\n return err;\n}\nfunction apiErr(status: number, body: any): CloudError {\n const code: CloudErrorCode =\n body?.error === 'origin_not_allowed'\n ? 'origin_not_allowed'\n : body?.error === 'not_found'\n ? 'workspace_not_found'\n : 'http_error';\n const err = new Error(body?.message ?? `HTTP ${status}`) as CloudError;\n err.code = code;\n err.status = status;\n err.body = body;\n return err;\n}\n\nexport type { StoredToken };\n","import type { AncestorStep, ElementFingerprint, FingerprintMatch, MatchConfidence } from './types';\n\nconst TEXT_SNIPPET_MAX = 80;\n\n/**\n * Depth of the ancestor chain we capture and match against. Four steps\n * gives deep elements one extra disambiguating entry, and lets shallow\n * elements reach all the way to `<html>` for shift-search anchor.\n */\nconst ANCESTOR_DEPTH = 4;\n\n/**\n * Maximum number of wrapper additions we tolerate when matching an\n * ancestor chain. A wrapper insertion shifts the captured chain up by\n * one in the live tree; we shift-search through 0..N offsets to absorb\n * that without losing the match. Set to 3 to cover most real-world\n * refactors (Tailwind component extraction, A/B-test wrappers, design-\n * system migrations typically add 1-2 layers).\n */\nconst ANCESTOR_MAX_SHIFT = 3;\n\n/**\n * Tunable thresholds for the fuzzy matchers below. Tradeoff: lower =\n * more false-positive \"drifted\" matches (already wear a warning chip),\n * higher = more orphaned real matches (worse UX: comment leaves the\n * page). Both failure modes are visible to the user; the lower setting\n * loses less work. The stress harness (Phase 11.4.3) is the source of\n * truth for these values; do not relax them further without numbers.\n */\nconst TEXT_RATIO_THRESHOLD = 0.75;\nconst CLASS_JACCARD_THRESHOLD = 0.6;\n\nexport function buildSelector(el: Element): string {\n const parts: string[] = [];\n let current: Element | null = el;\n let depth = 0;\n while (current && current !== document.documentElement && depth < 8) {\n let part = current.tagName.toLowerCase();\n if (current.id) {\n parts.unshift('#' + CSS.escape(current.id));\n break;\n }\n const cls = Array.from(current.classList)\n .filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-'))\n .slice(0, 2);\n if (cls.length) part += '.' + cls.map((c) => CSS.escape(c)).join('.');\n const parent: Element | null = current.parentElement;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === current!.tagName);\n if (sameTag.length > 1) {\n const idx = sameTag.indexOf(current);\n part += ':nth-of-type(' + (idx + 1) + ')';\n }\n }\n parts.unshift(part);\n current = parent;\n depth++;\n }\n return parts.join(' > ');\n}\n\nexport function fingerprint(el: Element): ElementFingerprint {\n const text = (el as HTMLElement).innerText || el.textContent || '';\n const parent = el.parentElement;\n let siblingIndex = 0;\n if (parent) {\n siblingIndex = Array.from(parent.children).indexOf(el);\n }\n return {\n selector: buildSelector(el),\n parentSelector: parent ? buildSelector(parent) : '',\n tagName: el.tagName,\n textSnippet: text.trim().slice(0, TEXT_SNIPPET_MAX),\n siblingIndex,\n attributes: {\n id: el.id || undefined,\n classList: Array.from(el.classList).filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-')),\n role: el.getAttribute('role') || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n dataKey: el.getAttribute('data-vizu-key') || undefined,\n },\n ancestorChain: captureAncestorChain(el, ANCESTOR_DEPTH),\n algorithmVersion: 2,\n };\n}\n\n/**\n * Walk up from the element, collecting up to `depth` ancestors as\n * `{tag, nthOfType}` pairs. Includes `<html>` so shallow elements\n * (top-level sections, page-level h1s) still capture three chain\n * entries — without HTML, those bottom out at `[MAIN, BODY]` which\n * isn't long enough to score ≥2 after a single wrapper insertion.\n * `<html>` is always nthOfType 1; the value adds no discriminating\n * info on its own, but provides a fixed anchor for shift-search to\n * align against.\n *\n * Returns undefined when the element is detached.\n */\nexport function captureAncestorChain(\n el: Element,\n depth: number,\n): { steps: AncestorStep[] } | undefined {\n const steps: AncestorStep[] = [];\n let current: Element | null = el.parentElement;\n while (current && steps.length < depth) {\n const parent: Element | null = current.parentElement;\n let nthOfType = 1;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === current!.tagName);\n nthOfType = sameTag.indexOf(current) + 1;\n }\n steps.push({ tag: current.tagName, nthOfType });\n current = parent;\n }\n return steps.length > 0 ? { steps } : undefined;\n}\n\n/**\n * Resolve a fingerprint to a live DOM element with a confidence tag.\n *\n * Two-phase ladder:\n *\n * **Strong identifiers (short-circuit).** `data-vizu-key` and `#id`\n * are host-explicit anchors — if the captured key exists in the DOM\n * we trust it 100% and return **exact**. For `data-vizu-key`, the\n * *absence* of the captured key is itself evidence: the host marked\n * that element explicitly and the mark is gone, so we **orphan**\n * without consulting weaker rungs. `id` gets the gentler treatment\n * (fall through on miss) because ids get renamed without the element\n * itself going away.\n *\n * **Fuzzy rungs vote (Phase 11.4.4 / #135).** When no strong\n * identifier resolves, every fuzzy rung (class signature, CSS selector,\n * ancestor chain, text snippet) returns its best candidate. We tally\n * votes per element and require ≥ 2 rungs to agree on the same target\n * before returning a match. 3+ votes → **likely**, 2 votes → **drifted**,\n * ≤ 1 vote → **orphaned**.\n *\n * The vote requirement is what catches `element_deleted`: the actual\n * element is gone, so each fuzzy rung finds a *different* nearby\n * element. Votes scatter, no consensus, orphan. Before this, the\n * matcher returned the first rung's hit and confidently mis-anchored\n * the comment to a similar element nearby — the false-positive\n * surfaced by the Phase 11.4.3 stress harness.\n */\nexport function findByFingerprint(fp: ElementFingerprint, root: ParentNode = document): FingerprintMatch {\n // ─── Strong identifier: data-vizu-key ─────────────────────────────\n if (fp.attributes.dataKey) {\n try {\n const byKey = root.querySelector(`[data-vizu-key=\"${CSS.escape(fp.attributes.dataKey)}\"]`);\n if (byKey) return { element: byKey, confidence: 'exact' };\n } catch {}\n // Strong-identifier honor: the host explicitly anchored this element\n // with a key. The key is gone. Don't fuzzy-guess to a similar\n // element nearby — that's the worst kind of false positive.\n return { element: null, confidence: 'orphaned' };\n }\n\n // ─── Strong identifier: #id ───────────────────────────────────────\n if (fp.attributes.id) {\n try {\n const byId = root.querySelector('#' + CSS.escape(fp.attributes.id));\n if (byId) return { element: byId, confidence: 'exact' };\n } catch {}\n // Don't orphan on missing id — ids get renamed in design-system\n // migrations without the element going away. Fall through.\n }\n\n // ─── Fuzzy rungs cast votes ───────────────────────────────────────\n type Rung = 'class' | 'selector' | 'chain' | 'text';\n const candidates: Array<{ el: Element; rung: Rung }> = [];\n\n const classes = fp.attributes.classList;\n if (classes && classes.length > 0) {\n const m = findByClassSignature(root, fp.tagName, classes);\n if (m?.element) candidates.push({ el: m.element, rung: 'class' });\n }\n\n try {\n const bySelector = root.querySelector(fp.selector);\n if (bySelector) candidates.push({ el: bySelector, rung: 'selector' });\n } catch {}\n\n if (fp.ancestorChain && fp.ancestorChain.steps.length > 0) {\n const m = findByAncestorChain(root, fp.tagName, fp.ancestorChain.steps);\n if (m?.element) candidates.push({ el: m.element, rung: 'chain' });\n } else {\n // Legacy v1 fingerprint: structural fallback (parent + sibling index + tag).\n try {\n const parent = fp.parentSelector\n ? (root.querySelector(fp.parentSelector) as Element | null)\n : (root as Element);\n if (parent) {\n const candidate = parent.children[fp.siblingIndex];\n if (candidate && candidate.tagName === fp.tagName) {\n candidates.push({ el: candidate, rung: 'chain' });\n }\n }\n } catch {}\n }\n\n if (fp.textSnippet) {\n const m = findByTextSnippet(root, fp.tagName, fp.textSnippet);\n if (m?.element) candidates.push({ el: m.element, rung: 'text' });\n }\n\n if (candidates.length === 0) {\n return { element: null, confidence: 'orphaned' };\n }\n\n // Tally votes per element. We dedupe via a Map keyed by element identity\n // (Map handles Element references natively — no need to materialize keys).\n const votes = new Map<Element, Set<Rung>>();\n for (const c of candidates) {\n let s = votes.get(c.el);\n if (!s) {\n s = new Set<Rung>();\n votes.set(c.el, s);\n }\n s.add(c.rung);\n }\n\n let best: { el: Element; rungs: Set<Rung> } | null = null;\n for (const [el, rungs] of votes) {\n if (!best || rungs.size > best.rungs.size) best = { el, rungs };\n }\n\n if (!best) return { element: null, confidence: 'orphaned' };\n if (best.rungs.size >= 3) return { element: best.el, confidence: 'likely' };\n if (best.rungs.size >= 2) return { element: best.el, confidence: 'drifted' };\n // Single vote. Trust it only when the rung is structurally precise:\n // - class signature already filters out ambiguous matches upstream\n // - ancestor chain requires ≥2/3 score\n // Selector-alone and text-alone stay too noisy — they're the rungs\n // that confidently mis-anchor when the captured element is gone.\n if (best.rungs.has('class') || best.rungs.has('chain')) {\n return { element: best.el, confidence: 'drifted' };\n }\n return { element: null, confidence: 'orphaned' };\n}\n\n/**\n * Scan elements of the captured tag, score each by classList Jaccard\n * similarity, and return the best match only when it's UNAMBIGUOUSLY\n * the top candidate. Ambiguous results fall through to the more\n * specific selector / chain / text rungs — a misfire here would assign\n * the comment to the wrong element, which is worse than orphaning.\n *\n * \"Unambiguous\" = best score ≥ {@link CLASS_JACCARD_THRESHOLD} AND\n * gap to the runner-up is ≥ 0.1. Returns \"likely\" or null.\n */\nfunction findByClassSignature(\n root: ParentNode,\n tagName: string,\n needle: string[],\n): FingerprintMatch | null {\n const needleSet = new Set(needle.filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-')));\n if (needleSet.size === 0) return null;\n const all = root.querySelectorAll(tagName.toLowerCase());\n let best: { el: Element; score: number } | null = null;\n let secondScore = 0;\n for (const el of all) {\n const haystack = new Set(\n Array.from(el.classList).filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-')),\n );\n if (haystack.size === 0) continue;\n const score = jaccardSimilarity(needleSet, haystack);\n if (score < CLASS_JACCARD_THRESHOLD) continue;\n if (!best || score > best.score) {\n secondScore = best?.score ?? 0;\n best = { el, score };\n } else if (score > secondScore) {\n secondScore = score;\n }\n }\n if (!best) return null;\n if (best.score - secondScore < 0.1) return null; // ambiguous — fall through\n return { element: best.el, confidence: 'likely' };\n}\n\n/**\n * Match candidates of the captured tag by walking their actual ancestor\n * chain and scoring against the captured chain. Returns \"likely\" when\n * every captured step lines up (perfect chain) at some shift offset,\n * \"drifted\" when at least 2/3 line up at the best offset. Anything less\n * is not returned — fall through to the fuzzy text matcher below.\n */\nfunction findByAncestorChain(\n root: ParentNode,\n tagName: string,\n steps: AncestorStep[],\n): FingerprintMatch | null {\n const all = root.querySelectorAll(tagName.toLowerCase());\n let best: { el: Element; score: number } | null = null;\n for (const el of all) {\n const ancestors = collectAncestorSteps(el, steps.length + ANCESTOR_MAX_SHIFT);\n const score = scoreChainAgainstAncestors(ancestors, steps, ANCESTOR_MAX_SHIFT);\n if (score < 2) continue; // threshold: at least 2 ancestors line up\n if (!best || score > best.score) best = { el, score };\n }\n if (!best) return null;\n return {\n element: best.el,\n confidence: best.score === steps.length ? 'likely' : 'drifted',\n };\n}\n\n/**\n * Walk up from the candidate, collecting `limit` ancestors as\n * `AncestorStep` records. Same shape as {@link captureAncestorChain},\n * extracted so the matcher can collect more than the captured depth\n * (to allow shift-search through wrapper insertions).\n */\nfunction collectAncestorSteps(el: Element, limit: number): AncestorStep[] {\n const steps: AncestorStep[] = [];\n let current: Element | null = el.parentElement;\n while (current && steps.length < limit) {\n const parent: Element | null = current.parentElement;\n let nth = 1;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === current!.tagName);\n nth = sameTag.indexOf(current) + 1;\n }\n steps.push({ tag: current.tagName, nthOfType: nth });\n current = parent;\n }\n return steps;\n}\n\n/**\n * Levenshtein-scored scan of every element matching the captured tag.\n * Both the candidate text and the needle are whitespace-normalized\n * before scoring so trailing commas, line-wraps, and adjacent emoji\n * don't kill the match.\n */\nfunction findByTextSnippet(\n root: ParentNode,\n tagName: string,\n snippet: string,\n): FingerprintMatch | null {\n const needle = normalizeText(snippet);\n if (!needle) return null;\n const all = root.querySelectorAll(tagName.toLowerCase());\n let best: { el: Element; score: number } | null = null;\n for (const el of all) {\n const raw = (el as HTMLElement).innerText || el.textContent || '';\n const candidate = normalizeText(raw.slice(0, TEXT_SNIPPET_MAX * 2));\n if (!candidate) continue;\n const score = levenshteinRatio(needle, candidate);\n if (score < TEXT_RATIO_THRESHOLD) continue;\n if (!best || score > best.score) {\n best = { el, score };\n }\n }\n return best ? { element: best.el, confidence: 'drifted' } : null;\n}\n\n/* ─── Pure helpers (testable without a DOM) ────────────────────────────── */\n\n/**\n * Lowercase, collapse internal whitespace, trim. Stable input to the\n * Levenshtein scorer — without it, \"Try it now\" vs \"Try it now\\n\"\n * would score as different strings even though a human reads them\n * identically.\n */\nexport function normalizeText(input: string): string {\n return input.toLowerCase().replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * Normalized Levenshtein similarity in [0, 1]. 1 means identical, 0\n * means completely different. Computed as\n * `1 - distance / max(len)`. Two empty strings score 1.0.\n *\n * O(n*m) time / O(min(n,m)) space — fine for our 80-char snippets even\n * on pages with thousands of candidates.\n */\nexport function levenshteinRatio(a: string, b: string): number {\n if (a === b) return 1;\n if (a.length === 0 || b.length === 0) return a.length === b.length ? 1 : 0;\n const maxLen = Math.max(a.length, b.length);\n const distance = levenshteinDistance(a, b);\n return 1 - distance / maxLen;\n}\n\nfunction levenshteinDistance(a: string, b: string): number {\n // Make `b` the longer one to keep the row buffer small.\n if (a.length > b.length) {\n const tmp = a;\n a = b;\n b = tmp;\n }\n const prev = new Array<number>(a.length + 1);\n const curr = new Array<number>(a.length + 1);\n for (let i = 0; i <= a.length; i++) prev[i] = i;\n for (let j = 1; j <= b.length; j++) {\n curr[0] = j;\n for (let i = 1; i <= a.length; i++) {\n const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;\n curr[i] = Math.min(\n curr[i - 1] + 1, // insert\n prev[i] + 1, // delete\n prev[i - 1] + cost, // substitute\n );\n }\n for (let i = 0; i <= a.length; i++) prev[i] = curr[i];\n }\n return prev[a.length];\n}\n\n/**\n * Score how many captured ancestor steps line up against an actual\n * ancestor list. Tries every offset in `[0, maxShift]` (to absorb up\n * to `maxShift` wrapper insertions between target and root) and\n * returns the best score across offsets.\n *\n * Pure helper — testable without a DOM. The DOM-aware\n * {@link findByAncestorChain} feeds it the captured chain plus the\n * candidate's actual ancestor list (collected one level deeper than\n * the captured chain so the shift-search has room to slide).\n */\nexport function scoreChainAgainstAncestors(\n actual: AncestorStep[],\n captured: AncestorStep[],\n maxShift: number,\n): number {\n let best = 0;\n for (let offset = 0; offset <= maxShift; offset++) {\n let score = 0;\n for (let i = 0; i < captured.length; i++) {\n const a = actual[i + offset];\n if (!a) break;\n if (a.tag === captured[i].tag && a.nthOfType === captured[i].nthOfType) {\n score++;\n }\n }\n if (score > best) best = score;\n }\n return best;\n}\n\n/**\n * Jaccard similarity = |A ∩ B| / |A ∪ B|. Returns 1 for identical\n * non-empty sets, 0 when disjoint. Two empty sets score 1.0 — the\n * caller checks size beforehand for that case.\n */\nexport function jaccardSimilarity<T>(a: Set<T>, b: Set<T>): number {\n if (a.size === 0 && b.size === 0) return 1;\n let intersection = 0;\n for (const item of a) if (b.has(item)) intersection++;\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\n/** Backwards-compat: callers that only need the element. */\nexport function findElementByFingerprint(fp: ElementFingerprint, root?: ParentNode): Element | null {\n return findByFingerprint(fp, root).element;\n}\n\nexport function fingerprintKey(fp: ElementFingerprint): string {\n return fp.selector + '|' + fp.textSnippet;\n}\n\n/** Short human-readable label for an element, used in `#` reference chips. */\nexport function fingerprintLabel(fp: ElementFingerprint): string {\n const tag = fp.tagName.toLowerCase();\n if (fp.textSnippet) {\n const snippet = fp.textSnippet.length > 24 ? fp.textSnippet.slice(0, 24) + '…' : fp.textSnippet;\n return tag + ': ' + snippet;\n }\n if (fp.attributes.id) return tag + '#' + fp.attributes.id;\n if (fp.attributes.classList && fp.attributes.classList.length) return tag + '.' + fp.attributes.classList[0];\n return tag;\n}\n","import { isInside } from './util';\n\nconst IGNORE_SELECTOR = '[data-vizu-root], [data-vizu-ignore], script, style, link, meta, head';\n\nexport class Highlighter {\n private overlay: HTMLDivElement;\n private root: HTMLElement;\n private current: Element | null = null;\n private extraIgnore: string[];\n private onClick: (el: Element, mouseEvent: MouseEvent) => void;\n private active = false;\n private paused = false;\n\n constructor(\n root: HTMLElement,\n extraIgnore: string[],\n onClick: (el: Element, mouseEvent: MouseEvent) => void,\n ) {\n this.root = root;\n this.extraIgnore = extraIgnore;\n this.onClick = onClick;\n this.overlay = document.createElement('div');\n this.overlay.className = 'vz-highlight';\n this.overlay.style.display = 'none';\n this.root.appendChild(this.overlay);\n }\n\n private setCurrent(el: Element | null): void {\n if (this.current === el) return;\n this.current = el;\n }\n\n start() {\n if (this.active) return;\n this.active = true;\n document.addEventListener('mousemove', this.handleMove, true);\n document.addEventListener('click', this.handleClick, true);\n document.addEventListener('scroll', this.handleScroll, true);\n window.addEventListener('resize', this.handleScroll);\n }\n\n stop() {\n if (!this.active) return;\n this.active = false;\n document.removeEventListener('mousemove', this.handleMove, true);\n document.removeEventListener('click', this.handleClick, true);\n document.removeEventListener('scroll', this.handleScroll, true);\n window.removeEventListener('resize', this.handleScroll);\n this.overlay.style.display = 'none';\n this.setCurrent(null);\n }\n\n private shouldIgnore(el: Element): boolean {\n if (el === document.documentElement || el === document.body) return true;\n if (isInside(el, IGNORE_SELECTOR)) return true;\n for (const sel of this.extraIgnore) {\n try {\n if (isInside(el, sel)) return true;\n } catch {}\n }\n return false;\n }\n\n private handleMove = (e: MouseEvent) => {\n if (this.paused) {\n // Move tracking is suspended (e.g., popover open) — keep overlay hidden\n this.setCurrent(null);\n this.overlay.style.display = 'none';\n return;\n }\n const target = document.elementFromPoint(e.clientX, e.clientY);\n if (!target || target === this.current) return;\n if (this.shouldIgnore(target)) {\n this.setCurrent(null);\n this.overlay.style.display = 'none';\n return;\n }\n this.setCurrent(target);\n this.updateOverlay();\n };\n\n private handleClick = (e: MouseEvent) => {\n const target = document.elementFromPoint(e.clientX, e.clientY);\n if (!target) return;\n if (this.shouldIgnore(target)) return;\n e.preventDefault();\n e.stopPropagation();\n this.onClick(target, e);\n };\n\n setPaused(paused: boolean) {\n this.paused = paused;\n if (paused) {\n this.setCurrent(null);\n this.overlay.style.display = 'none';\n }\n }\n\n isPaused(): boolean { return this.paused; }\n\n private handleScroll = () => {\n if (!this.current) return;\n this.updateOverlay();\n };\n\n private updateOverlay() {\n if (!this.current) return;\n const rect = this.current.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) {\n this.overlay.style.display = 'none';\n return;\n }\n this.overlay.style.display = 'block';\n this.overlay.style.left = rect.left + 'px';\n this.overlay.style.top = rect.top + 'px';\n this.overlay.style.width = rect.width + 'px';\n this.overlay.style.height = rect.height + 'px';\n }\n\n destroy() {\n this.stop();\n this.overlay.remove();\n }\n}\n","import type { VizuComment, VizuUser, ElementFingerprint, Reply, Attachment, MentionableUser } from './types';\nimport { escapeHtml, formatTime, avatarHtml, refId as makeRefId, renderCommentText, extractMentions, renderAttachmentsHtml, initials } from './util';\nimport { fingerprintKey, fingerprintLabel } from './fingerprint';\n\n/**\n * Render the inline replies block for a comment in the popover. The\n * reply input is initially hidden and toggled by `data-vz=\"toggle-reply\"`.\n * Reply ids come from the server; the local UI references them via\n * `data-reply-id` for delete operations.\n */\n/**\n * Tiny CSS attribute-value escaper for the few places we build selectors\n * with user-supplied ids. Mirrors `CSS.escape` semantics but works in\n * older environments too.\n */\nfunction cssEscape(s: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') return CSS.escape(s);\n return s.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\nfunction renderRepliesHtml(c: VizuComment): string {\n const replies = c.replies ?? [];\n const items = replies\n .map(\n (r) => `\n <div class=\"vz-reply\" data-reply-id=\"${escapeHtml(r.id)}\">\n ${r.author ? `<div class=\"vz-comment-author\">${avatarHtml(r.author)} <span class=\"vz-comment-author-name\">${escapeHtml(r.author.name || r.author.id || 'User')}</span></div>` : ''}\n <div class=\"vz-reply-text\">${escapeHtml(r.text)}</div>\n <div class=\"vz-comment-meta\">\n <span>${escapeHtml(formatTime(typeof r.createdAt === 'number' ? r.createdAt : new Date(r.createdAt).getTime()))}</span>\n <button class=\"vz-comment-delete\" data-vz=\"delete-reply\" data-comment-id=\"${escapeHtml(c.id)}\" data-reply-id=\"${escapeHtml(r.id)}\">delete</button>\n </div>\n </div>\n `,\n )\n .join('');\n return `\n <div class=\"vz-replies\" data-comment-id=\"${escapeHtml(c.id)}\" hidden>\n <div class=\"vz-reply-list\">${items}</div>\n <div class=\"vz-reply-form\">\n <textarea class=\"vz-reply-input\" placeholder=\"Reply…\" rows=\"2\" data-comment-id=\"${escapeHtml(c.id)}\"></textarea>\n <button class=\"vz-btn vz-btn-primary vz-btn-reply\" data-vz=\"send-reply\" data-id=\"${escapeHtml(c.id)}\">Send</button>\n </div>\n </div>\n `;\n}\n\nexport interface PopoverCallbacks {\n /**\n * Save a comment with one or more anchored fingerprints and the parsed\n * reference map. Refs are inline tokens in `text` and resolved via `references`.\n */\n onAdd: (\n text: string,\n fingerprints: ElementFingerprint[],\n references?: Record<string, ElementFingerprint>,\n mentions?: string[],\n attachments?: Attachment[],\n ) => void;\n onDelete: (id: string) => void;\n onAddReply: (commentId: string, text: string) => void;\n onDeleteReply: (commentId: string, replyId: string) => void;\n /** Upload a file via the active storage adapter; returns the resolved Attachment. */\n onUploadAttachment: (file: File) => Promise<Attachment>;\n /** True iff the active storage adapter supports uploads (popover hides the UI otherwise). */\n canUploadAttachments: () => boolean;\n onClose: () => void;\n onStartReferencePick: (\n onPicked: (fp: ElementFingerprint) => void,\n onCancel: () => void,\n ) => void;\n onJumpToReference: (commentId: string, refId: string) => void;\n onAnchorsChanged: (fingerprints: ElementFingerprint[]) => void;\n /**\n * Fetch the workspace's mentionable users for the @ picker dropdown.\n * Called every time the user opens the picker — keeps the list fresh\n * if members were added/removed in another tab. Returns [] for hosts\n * that don't support mentions (local/memory storage adapters).\n */\n onMentionSearch?: () => Promise<MentionableUser[]>;\n}\n\nexport class Popover {\n private el: HTMLDivElement;\n private root: HTMLElement;\n private callbacks: PopoverCallbacks;\n private currentUser: VizuUser | null = null;\n\n /** All elements this in-progress comment is anchored to. First one positions the popover. */\n private anchorTargets: Element[] = [];\n private anchorFingerprints: ElementFingerprint[] = [];\n /** Refs the user inserted via # picker during this session, keyed by refId. */\n private pendingRefs: Record<string, ElementFingerprint> = {};\n /** Attachments uploaded during this session, persisted into `comment.attachments` on save. */\n private pendingAttachments: Attachment[] = [];\n /** Open mention picker element, if any. Held so the next openMentionPicker / close calls can tear it down. */\n private mentionPicker: HTMLDivElement | null = null;\n /** Dismiss handlers wired to document for the open picker; cleared in closeMentionPicker. */\n private mentionPickerDismiss: (() => void) | null = null;\n /** All mentionable users fetched for the open picker — pre-filter list. */\n private mentionPickerUsers: MentionableUser[] = [];\n /** Currently visible subset of `mentionPickerUsers` after the user's filter. */\n private mentionPickerFiltered: MentionableUser[] = [];\n /** Highlighted index in `mentionPickerFiltered`. Up/Down arrows mutate this. */\n private mentionPickerSelected = 0;\n /**\n * 'manual' = opened from the @ mention button (separate search input).\n * 'editor' = opened by typing `@` in the editor; filter is derived\n * from the text after `@` in the editor itself, no search input.\n */\n private mentionPickerMode: 'manual' | 'editor' = 'manual';\n /**\n * In editor mode: a Range covering `@<query>` in the editor's text.\n * Used to replace that span with the mention chip on commit, and to\n * recompute the bounding rect when re-positioning after typing.\n */\n private mentionPickerEditorRange: Range | null = null;\n\n constructor(root: HTMLElement, callbacks: PopoverCallbacks) {\n this.root = root;\n this.callbacks = callbacks;\n this.el = document.createElement('div');\n this.el.className = 'vz-popover';\n this.el.style.display = 'none';\n this.el.addEventListener('click', this.handleClick);\n this.root.appendChild(this.el);\n }\n\n setUser(user: VizuUser | null) {\n this.currentUser = user;\n }\n\n /**\n * Open the popover anchored to one OR more elements. The popover positions\n * itself relative to `targets[0]` but commits with every fingerprint in `fingerprints`.\n */\n open(targets: Element[], fingerprints: ElementFingerprint[], existingComments: VizuComment[]) {\n this.anchorTargets = [...targets];\n this.anchorFingerprints = [...fingerprints];\n this.pendingRefs = {};\n this.render(existingComments);\n this.position();\n }\n\n /** Refresh the rendered comment list (called by host when comments change). */\n update(existingComments: VizuComment[]) {\n if (!this.anchorTargets.length) return;\n this.render(existingComments);\n this.position();\n }\n\n /** Add another anchor to the in-progress comment (Shift+Click flow). */\n addAnchor(fp: ElementFingerprint, target: Element | null): boolean {\n const key = fingerprintKey(fp);\n if (this.anchorFingerprints.some((x) => fingerprintKey(x) === key)) return false;\n this.anchorFingerprints.push(fp);\n if (target) this.anchorTargets.push(target);\n this.callbacks.onAnchorsChanged([...this.anchorFingerprints]);\n this.renderAnchorList();\n this.updateHeader();\n return true;\n }\n\n /** Remove an anchor by fingerprint key. Never removes the last anchor. */\n removeAnchor(key: string): boolean {\n if (this.anchorFingerprints.length <= 1) return false;\n const idx = this.anchorFingerprints.findIndex((x) => fingerprintKey(x) === key);\n if (idx < 0) return false;\n this.anchorFingerprints.splice(idx, 1);\n this.anchorTargets.splice(idx, 1);\n this.callbacks.onAnchorsChanged([...this.anchorFingerprints]);\n this.renderAnchorList();\n this.updateHeader();\n return true;\n }\n\n private updateHeader() {\n const header = this.el.querySelector('.vz-popover-header span:first-child');\n if (!header) return;\n header.textContent = this.anchorFingerprints.length > 1\n ? `Comment on ${this.anchorFingerprints.length} elements`\n : 'Comment';\n }\n\n /** Recompute viewport position; called by host on scroll/resize. */\n reposition() {\n if (!this.anchorTargets.length) return;\n this.position();\n }\n\n close() {\n this.closeMentionPicker();\n this.el.style.display = 'none';\n this.anchorTargets = [];\n this.anchorFingerprints = [];\n this.pendingRefs = {};\n }\n\n isOpen(): boolean {\n return this.anchorTargets.length > 0;\n }\n\n getAnchors(): ElementFingerprint[] {\n return [...this.anchorFingerprints];\n }\n\n getAnchorTargets(): Element[] {\n return [...this.anchorTargets];\n }\n\n private render(comments: VizuComment[]) {\n if (!this.anchorTargets.length) return;\n const primary = this.anchorTargets[0];\n const cls = Array.from(primary.classList)\n .filter((c) => !c.startsWith('vz-') && !c.startsWith('vizu-'))\n .slice(0, 2);\n const label =\n primary.tagName.toLowerCase() +\n (primary.id ? '#' + primary.id : '') +\n (cls.length ? '.' + cls.join('.') : '');\n const text = ((primary as HTMLElement).innerText || '').trim();\n const textPreview = text.slice(0, 60);\n\n const authoringLine = this.currentUser\n ? `<div class=\"vz-comment-author\">${avatarHtml(this.currentUser)} <span class=\"vz-comment-author-name\">${escapeHtml(this.currentUser.name)}</span> <span>· you</span></div>`\n : '';\n\n this.el.innerHTML = `\n <div class=\"vz-popover-header\">\n <span>${this.anchorFingerprints.length > 1 ? `Comment on ${this.anchorFingerprints.length} elements` : 'Comment'}</span>\n <button class=\"vz-popover-close\" data-vz=\"close\" aria-label=\"Close\">×</button>\n </div>\n <div class=\"vz-target-label\">${escapeHtml(label)}${textPreview ? ' · &ldquo;' + escapeHtml(textPreview) + (text.length > 60 ? '…' : '') + '&rdquo;' : ''}</div>\n <div class=\"vz-anchor-list-wrap\"></div>\n <div class=\"vz-comment-list\">\n ${\n comments.length === 0\n ? '<div class=\"vz-empty\">No comments yet.</div>'\n : comments\n .map(\n (c) => `\n <div class=\"vz-comment\" data-comment-id=\"${escapeHtml(c.id)}\">\n ${c.author ? `<div class=\"vz-comment-author\">${avatarHtml(c.author)} <span class=\"vz-comment-author-name\">${escapeHtml(c.author.name || c.author.id || 'User')}</span></div>` : ''}\n <div>${renderCommentText(c.text, c.id)}</div>\n ${renderAttachmentsHtml(c.attachments)}\n <div class=\"vz-comment-meta\">\n <span>${escapeHtml(formatTime(c.createdAt))}</span>\n <span class=\"vz-comment-actions\">\n <button class=\"vz-comment-reply-toggle\" data-vz=\"toggle-reply\" data-id=\"${escapeHtml(c.id)}\">${(c.replies?.length ?? 0) > 0 ? `${c.replies!.length} ${c.replies!.length === 1 ? 'reply' : 'replies'}` : 'reply'}</button>\n <button class=\"vz-comment-delete\" data-vz=\"delete\" data-id=\"${escapeHtml(c.id)}\">delete</button>\n </span>\n </div>\n ${renderRepliesHtml(c)}\n </div>\n `,\n )\n .join('')\n }\n </div>\n ${authoringLine}\n <div class=\"vz-editor-wrap\">\n <div class=\"vz-textarea\" contenteditable=\"true\" data-placeholder=\"Leave a comment… # to reference. ⌘/Ctrl+Enter to save. Shift+Click another element to anchor to it too.\"></div>\n <div class=\"vz-attachment-drop-hint\" data-vz=\"drop-hint\" hidden>Drop image to attach</div>\n </div>\n <div class=\"vz-attachment-list\" data-vz=\"attachment-list\"></div>\n <div class=\"vz-popover-actions\">\n <button class=\"vz-btn vz-btn-ghost\" data-vz=\"close\">Close</button>\n ${this.callbacks.canUploadAttachments() ? `<button class=\"vz-btn vz-btn-ghost vz-btn-upload\" data-vz=\"upload\" title=\"Attach an image\">+ image</button>` : ''}\n ${this.currentUser ? `<button class=\"vz-btn vz-btn-ghost vz-btn-mention\" data-vz=\"mention\" title=\"Mention a teammate\">@ mention</button>` : ''}\n <button class=\"vz-btn vz-btn-primary\" data-vz=\"save\">Save</button>\n </div>\n <input type=\"file\" class=\"vz-file-input\" data-vz=\"file-input\" accept=\"image/png,image/jpeg,image/webp,image/gif,image/svg+xml\" hidden />\n `;\n this.el.style.display = 'flex';\n this.renderAnchorList();\n this.renderAttachmentList();\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement;\n editor.focus();\n editor.addEventListener('keydown', this.handleEditorKey);\n editor.addEventListener('input', this.handleEditorInput);\n editor.addEventListener('paste', this.handleEditorPaste);\n\n // Drag-and-drop for image attachments. We attach to the wrap so the\n // hint can cover the editor without intercepting type-events.\n const wrap = this.el.querySelector('.vz-editor-wrap') as HTMLElement | null;\n if (wrap && this.callbacks.canUploadAttachments()) {\n wrap.addEventListener('dragover', this.handleDragOver);\n wrap.addEventListener('dragleave', this.handleDragLeave);\n wrap.addEventListener('drop', this.handleDrop);\n }\n\n const fileInput = this.el.querySelector('[data-vz=\"file-input\"]') as HTMLInputElement | null;\n if (fileInput) {\n fileInput.addEventListener('change', this.handleFileChange);\n }\n }\n\n private renderAnchorList() {\n const wrap = this.el.querySelector('.vz-anchor-list-wrap');\n if (!wrap) return;\n if (this.anchorFingerprints.length <= 1) {\n wrap.innerHTML = '';\n return;\n }\n const chips = this.anchorFingerprints\n .map((fp) => {\n const key = fingerprintKey(fp);\n const label = fingerprintLabel(fp);\n return `<span class=\"vz-anchor-chip\" data-fp-key=\"${escapeHtml(key)}\">${escapeHtml(label)}<button class=\"vz-anchor-chip-remove\" data-vz=\"remove-anchor\" data-fp-key=\"${escapeHtml(key)}\" aria-label=\"Remove anchor\">×</button></span>`;\n })\n .join('');\n wrap.innerHTML = `\n <div class=\"vz-anchor-hint\">Anchored to ${this.anchorFingerprints.length} elements:</div>\n <div class=\"vz-anchor-list\">${chips}</div>\n `;\n }\n\n private handleEditorKey = (e: KeyboardEvent) => {\n const editor = e.currentTarget as HTMLElement;\n // Mention picker (editor-trigger mode) intercepts navigation/commit\n // keys before they reach the editor. Other keys pass through so the\n // `@<query>` text continues to be edited and `handleEditorInput`\n // re-runs detectMentionTrigger to refresh the filter.\n if (this.mentionPicker && this.mentionPickerMode === 'editor') {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n this.moveMentionPickerSelection(1);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n this.moveMentionPickerSelection(-1);\n return;\n }\n if (e.key === 'Enter' || e.key === 'Tab') {\n if (this.mentionPickerFiltered.length > 0) {\n e.preventDefault();\n this.commitMentionPickerSelection();\n return;\n }\n // Empty filtered list → fall through to default Enter behaviour.\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n this.closeMentionPicker();\n return;\n }\n }\n if (e.key === '#') {\n e.preventDefault();\n const savedRange = this.saveRange();\n this.callbacks.onStartReferencePick(\n (fp) => this.insertChip(fp, savedRange),\n () => {\n editor.focus();\n this.restoreRange(savedRange);\n },\n );\n return;\n }\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n this.save();\n return;\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n this.callbacks.onClose();\n }\n };\n\n /**\n * Fires after every keystroke in the editor (post-insertion). Looks\n * for an `@<query>` pattern adjacent to the caret. If found, opens\n * (or refreshes) the mention picker in editor mode. If the trigger\n * disappears (user typed a space, backspaced over @, etc.), closes\n * the picker.\n */\n private handleEditorInput = () => {\n const trigger = this.detectMentionTrigger();\n if (trigger) {\n void this.openOrUpdateEditorMentionPicker(trigger.range, trigger.query);\n } else if (this.mentionPicker && this.mentionPickerMode === 'editor') {\n this.closeMentionPicker();\n }\n };\n\n /**\n * Walk back from the caret looking for the most recent `@` that\n * starts a mention trigger — must be at start-of-text-node or\n * preceded by whitespace, and there must be no whitespace between\n * `@` and the caret. Returns the Range covering `@<query>` plus\n * the bare query string.\n */\n private detectMentionTrigger(): { range: Range; query: string } | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return null;\n const caret = sel.getRangeAt(0);\n const node = caret.startContainer;\n if (node.nodeType !== Node.TEXT_NODE) return null;\n const text = node.textContent ?? '';\n const offset = caret.startOffset;\n for (let i = offset - 1; i >= 0; i--) {\n const ch = text[i];\n if (ch === '@') {\n const prev = i > 0 ? text[i - 1] : '';\n if (prev !== '' && !/\\s/.test(prev)) return null;\n const query = text.slice(i + 1, offset);\n if (/\\s/.test(query)) return null;\n const range = document.createRange();\n range.setStart(node, i);\n range.setEnd(node, offset);\n return { range, query };\n }\n if (/\\s/.test(ch)) return null;\n }\n return null;\n }\n\n private handleEditorPaste = (e: ClipboardEvent) => {\n // If clipboard carries image data and uploads are enabled, swallow\n // the paste and upload the image instead of inserting any text/binary.\n if (this.callbacks.canUploadAttachments()) {\n const items = e.clipboardData?.items ?? null;\n if (items) {\n const imageFiles: File[] = [];\n for (let i = 0; i < items.length; i++) {\n const it = items[i];\n if (it.kind === 'file' && it.type.startsWith('image/')) {\n const f = it.getAsFile();\n if (f) imageFiles.push(f);\n }\n }\n if (imageFiles.length > 0) {\n e.preventDefault();\n for (const f of imageFiles) void this.uploadAndAppend(f);\n return;\n }\n }\n }\n\n e.preventDefault();\n const text = e.clipboardData?.getData('text/plain') ?? '';\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n const r = sel.getRangeAt(0);\n r.deleteContents();\n r.insertNode(document.createTextNode(text));\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n }\n };\n\n /* ─── Attachment handlers ────────────────────────────────────────── */\n\n private handleDragOver = (e: DragEvent) => {\n if (!e.dataTransfer || !Array.from(e.dataTransfer.types).includes('Files')) return;\n e.preventDefault();\n const hint = this.el.querySelector('[data-vz=\"drop-hint\"]') as HTMLElement | null;\n hint?.removeAttribute('hidden');\n };\n private handleDragLeave = (e: DragEvent) => {\n // Only hide when leaving the wrap entirely (relatedTarget outside the wrap).\n const wrap = e.currentTarget as HTMLElement;\n if (e.relatedTarget instanceof Node && wrap.contains(e.relatedTarget)) return;\n const hint = this.el.querySelector('[data-vz=\"drop-hint\"]') as HTMLElement | null;\n hint?.setAttribute('hidden', '');\n };\n private handleDrop = (e: DragEvent) => {\n e.preventDefault();\n const hint = this.el.querySelector('[data-vz=\"drop-hint\"]') as HTMLElement | null;\n hint?.setAttribute('hidden', '');\n if (!e.dataTransfer) return;\n const files = Array.from(e.dataTransfer.files).filter((f) => f.type.startsWith('image/'));\n for (const f of files) void this.uploadAndAppend(f);\n };\n private handleFileChange = (e: Event) => {\n const input = e.currentTarget as HTMLInputElement;\n const files = input.files ? Array.from(input.files) : [];\n for (const f of files) void this.uploadAndAppend(f);\n input.value = ''; // allow re-selecting the same file\n };\n\n private async uploadAndAppend(file: File): Promise<void> {\n if (!this.callbacks.canUploadAttachments()) return;\n // Show an optimistic placeholder while the upload runs. We keep a\n // tracking entry on `pendingAttachments` so callers can see it.\n const placeholderId = 'upload-' + Math.random().toString(36).slice(2, 9);\n const placeholder: Attachment = {\n id: placeholderId,\n url: '',\n mimeType: file.type || 'application/octet-stream',\n sizeBytes: file.size,\n uploadedAt: Date.now(),\n filename: file.name,\n };\n this.pendingAttachments.push(placeholder);\n this.renderAttachmentList(placeholderId);\n\n try {\n const att = await this.callbacks.onUploadAttachment(file);\n const idx = this.pendingAttachments.findIndex((a) => a.id === placeholderId);\n if (idx >= 0) this.pendingAttachments[idx] = att;\n else this.pendingAttachments.push(att);\n this.renderAttachmentList();\n } catch (err) {\n // Surface the failure inline and drop the placeholder.\n this.pendingAttachments = this.pendingAttachments.filter((a) => a.id !== placeholderId);\n this.renderAttachmentList();\n if (typeof console !== 'undefined') {\n console.warn('[vizu] attachment upload failed', err);\n }\n }\n }\n\n private renderAttachmentList(uploadingId?: string) {\n const list = this.el.querySelector('[data-vz=\"attachment-list\"]') as HTMLElement | null;\n if (!list) return;\n if (this.pendingAttachments.length === 0) {\n list.innerHTML = '';\n return;\n }\n list.innerHTML = this.pendingAttachments\n .map((a) => {\n const isUploading = a.id === uploadingId || a.url === '';\n const preview = isUploading\n ? `<div class=\"vz-attachment-thumb vz-attachment-thumb-loading\">…</div>`\n : a.mimeType.startsWith('image/')\n ? `<img class=\"vz-attachment-thumb\" src=\"${escapeHtml(a.url)}\" alt=\"${escapeHtml(a.filename ?? '')}\" />`\n : `<div class=\"vz-attachment-thumb\">📄</div>`;\n return `\n <div class=\"vz-attachment-item\" data-attachment-id=\"${escapeHtml(a.id)}\">\n ${preview}\n <button class=\"vz-attachment-remove\" data-vz=\"remove-attachment\" data-attachment-id=\"${escapeHtml(a.id)}\" aria-label=\"Remove attachment\">×</button>\n </div>\n `;\n })\n .join('');\n }\n\n private saveRange(): Range | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n return sel.getRangeAt(0).cloneRange();\n }\n\n private restoreRange(r: Range | null) {\n if (!r) return;\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(r);\n }\n\n private insertChip(fp: ElementFingerprint, range: Range | null) {\n const rid = makeRefId();\n this.pendingRefs[rid] = fp;\n const label = fingerprintLabel(fp);\n const chip = document.createElement('span');\n chip.className = 'vz-chip';\n chip.contentEditable = 'false';\n chip.setAttribute('data-ref-id', rid);\n chip.textContent = label;\n\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement;\n editor.focus();\n this.restoreRange(range);\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) {\n editor.appendChild(chip);\n editor.appendChild(document.createTextNode(' '));\n this.moveCursorToEnd(editor);\n return;\n }\n const r = sel.getRangeAt(0);\n r.deleteContents();\n r.insertNode(chip);\n // Trailing space so the cursor doesn't get stuck inside chip's neighbour boundary\n const space = document.createTextNode(' ');\n chip.after(space);\n const newRange = document.createRange();\n newRange.setStartAfter(space);\n newRange.collapse(true);\n sel.removeAllRanges();\n sel.addRange(newRange);\n }\n\n private moveCursorToEnd(el: HTMLElement) {\n const range = document.createRange();\n range.selectNodeContents(el);\n range.collapse(false);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n\n /** Walk the editor and produce the markdown-ish string with ref tokens. */\n private serializeEditor(editor: HTMLElement): string {\n const parts: string[] = [];\n const walk = (node: Node) => {\n if (node.nodeType === Node.TEXT_NODE) {\n parts.push(node.textContent || '');\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n const el = node as HTMLElement;\n if (el.classList.contains('vz-chip')) {\n const mid = el.getAttribute('data-mention-id');\n if (mid) {\n // Mention chip — label includes the leading \"@\", strip for token.\n const raw = el.textContent || '';\n const name = raw.startsWith('@') ? raw.slice(1) : raw;\n parts.push(`[@${name}](vizu-mention:${mid})`);\n return;\n }\n const rid = el.getAttribute('data-ref-id');\n const label = el.textContent || '';\n if (rid) parts.push(`[#${label}](vizu-ref:${rid})`);\n return;\n }\n if (el.tagName === 'BR') {\n parts.push('\\n');\n return;\n }\n if (el.tagName === 'DIV' && parts.length && !parts[parts.length - 1].endsWith('\\n')) {\n // <div> often produced by browsers when user presses Enter inside contenteditable\n parts.push('\\n');\n }\n for (const child of el.childNodes) walk(child);\n };\n for (const child of editor.childNodes) walk(child);\n return parts.join('').trim();\n }\n\n private handleClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n // Anchor chip remove\n const removeBtn = target.closest('[data-vz=\"remove-anchor\"]') as HTMLElement | null;\n if (removeBtn) {\n e.stopPropagation();\n const key = removeBtn.getAttribute('data-fp-key');\n if (key) this.removeAnchor(key);\n return;\n }\n // Inline reference chip click → jump\n const refEl = target.closest('[data-vz=\"ref\"]') as HTMLElement | null;\n if (refEl) {\n const commentId = refEl.getAttribute('data-comment-id');\n const refIdAttr = refEl.getAttribute('data-ref-id');\n if (commentId && refIdAttr) {\n e.stopPropagation();\n this.callbacks.onJumpToReference(commentId, refIdAttr);\n }\n return;\n }\n const action = target.getAttribute('data-vz');\n if (action === 'close') this.callbacks.onClose();\n else if (action === 'save') this.save();\n else if (action === 'mention') {\n e.preventDefault();\n this.openMentionPicker(target as HTMLElement);\n } else if (action === 'upload') {\n e.preventDefault();\n const input = this.el.querySelector('[data-vz=\"file-input\"]') as HTMLInputElement | null;\n input?.click();\n } else if (action === 'remove-attachment') {\n e.preventDefault();\n const id = target.getAttribute('data-attachment-id');\n if (id) {\n this.pendingAttachments = this.pendingAttachments.filter((a) => a.id !== id);\n this.renderAttachmentList();\n }\n } else if (action === 'delete') {\n const id = target.getAttribute('data-id');\n if (id) this.callbacks.onDelete(id);\n } else if (action === 'toggle-reply') {\n e.preventDefault();\n const id = target.getAttribute('data-id');\n if (!id) return;\n const block = this.el.querySelector(`.vz-replies[data-comment-id=\"${cssEscape(id)}\"]`) as HTMLElement | null;\n if (!block) return;\n const isHidden = block.hasAttribute('hidden');\n if (isHidden) {\n block.removeAttribute('hidden');\n const input = block.querySelector('.vz-reply-input') as HTMLTextAreaElement | null;\n input?.focus();\n } else {\n block.setAttribute('hidden', '');\n }\n } else if (action === 'send-reply') {\n e.preventDefault();\n const id = target.getAttribute('data-id');\n if (!id) return;\n const input = this.el.querySelector(\n `.vz-reply-input[data-comment-id=\"${cssEscape(id)}\"]`,\n ) as HTMLTextAreaElement | null;\n const text = input?.value.trim() ?? '';\n if (!text) return;\n this.callbacks.onAddReply(id, text);\n if (input) input.value = '';\n } else if (action === 'delete-reply') {\n e.preventDefault();\n const commentId = target.getAttribute('data-comment-id');\n const replyId = target.getAttribute('data-reply-id');\n if (commentId && replyId) this.callbacks.onDeleteReply(commentId, replyId);\n }\n };\n\n /**\n * Manual-mode picker: opens from the @ mention button. Renders a\n * search input above the list; the user types into the input to\n * filter, arrows + Enter to pick. The selection inserts a mention\n * chip at the editor's current caret (no `@<query>` replacement —\n * the user didn't type one).\n */\n private async openMentionPicker(anchor: HTMLElement) {\n this.closeMentionPicker();\n if (!this.callbacks.onMentionSearch) return;\n this.mentionPickerMode = 'manual';\n this.mentionPickerEditorRange = null;\n const picker = this.createMentionPickerShell();\n this.positionMentionPickerByElement(picker, anchor);\n // Manual mode owns its search input; focus it after fetch resolves.\n const users = await this.fetchMentionables();\n if (this.mentionPicker !== picker) return;\n this.mentionPickerUsers = users;\n this.renderMentionPickerBody('');\n this.positionMentionPickerByElement(picker, anchor);\n const input = picker.querySelector<HTMLInputElement>('.vz-mention-search');\n input?.focus();\n this.wireMentionPickerDismiss();\n }\n\n /**\n * Editor-mode picker: opens because the user typed `@` in the\n * editor. No search input — the editor IS the search; the query is\n * the text after `@`. On commit, replaces the `@<query>` range with\n * the chip.\n */\n private async openOrUpdateEditorMentionPicker(range: Range, query: string) {\n this.mentionPickerEditorRange = range;\n // First time: build the shell + fetch users. Subsequent calls\n // (user keeps typing) just re-render the filtered body.\n if (!this.mentionPicker || this.mentionPickerMode !== 'editor') {\n this.closeMentionPicker();\n this.mentionPickerMode = 'editor';\n const picker = this.createMentionPickerShell({ showSearch: false });\n this.positionMentionPickerByRange(picker, range);\n const users = await this.fetchMentionables();\n if (this.mentionPicker !== picker) return; // closed during fetch\n this.mentionPickerUsers = users;\n this.renderMentionPickerBody(query);\n this.positionMentionPickerByRange(picker, range);\n this.wireMentionPickerDismiss();\n return;\n }\n // Already open: just re-filter and re-position to the new range.\n this.renderMentionPickerBody(query);\n this.positionMentionPickerByRange(this.mentionPicker, range);\n }\n\n /**\n * Build the picker DOM scaffold and attach it to the popover. Does\n * NOT fetch users or render the body — caller's responsibility.\n * Search input is hidden in editor mode where the user already has\n * a perfectly good text-entry surface (the editor itself).\n */\n private createMentionPickerShell({ showSearch = true }: { showSearch?: boolean } = {}): HTMLDivElement {\n const picker = document.createElement('div');\n picker.className = 'vz-mention-picker';\n picker.setAttribute('data-vz-ignore', '');\n picker.innerHTML = `\n ${showSearch ? `<input class=\"vz-mention-search\" type=\"text\" placeholder=\"Search teammates…\" autocomplete=\"off\" spellcheck=\"false\" />` : ''}\n <div class=\"vz-mention-list\" data-vz=\"mention-list\">\n <div class=\"vz-mention-loading\">Loading…</div>\n </div>\n `;\n this.el.appendChild(picker);\n this.mentionPicker = picker;\n this.mentionPickerFiltered = [];\n this.mentionPickerSelected = 0;\n\n // Mouse hover updates selection so click feels predictable.\n picker.addEventListener('mousemove', (e) => {\n const btn = (e.target as Element | null)?.closest<HTMLElement>('.vz-mention-item');\n if (!btn) return;\n const idx = Number(btn.getAttribute('data-mention-index') ?? -1);\n if (idx >= 0 && idx !== this.mentionPickerSelected) {\n this.mentionPickerSelected = idx;\n this.refreshMentionPickerHighlight();\n }\n });\n picker.addEventListener('click', (e) => {\n const btn = (e.target as Element | null)?.closest<HTMLElement>('.vz-mention-item');\n if (!btn) return;\n e.preventDefault();\n e.stopPropagation();\n const idx = Number(btn.getAttribute('data-mention-index') ?? -1);\n if (idx >= 0) {\n this.mentionPickerSelected = idx;\n this.commitMentionPickerSelection();\n }\n });\n\n // Search input wiring — only present in manual mode.\n if (showSearch) {\n const input = picker.querySelector<HTMLInputElement>('.vz-mention-search');\n input?.addEventListener('input', () => this.renderMentionPickerBody(input.value));\n input?.addEventListener('keydown', (e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n this.moveMentionPickerSelection(1);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n this.moveMentionPickerSelection(-1);\n } else if (e.key === 'Enter' || e.key === 'Tab') {\n if (this.mentionPickerFiltered.length > 0) {\n e.preventDefault();\n this.commitMentionPickerSelection();\n }\n } else if (e.key === 'Escape') {\n e.preventDefault();\n this.closeMentionPicker();\n }\n });\n }\n\n return picker;\n }\n\n /** Fetch the mentionable list. Swallows errors → []. */\n private async fetchMentionables(): Promise<MentionableUser[]> {\n if (!this.callbacks.onMentionSearch) return [];\n try {\n return await this.callbacks.onMentionSearch();\n } catch {\n return [];\n }\n }\n\n /**\n * Re-filter the cached users by `query`, render the list, reset\n * selected to 0. Called on every search input change and on every\n * editor input event while in editor mode.\n */\n private renderMentionPickerBody(query: string) {\n const picker = this.mentionPicker;\n if (!picker) return;\n const list = picker.querySelector<HTMLElement>('[data-vz=\"mention-list\"]');\n if (!list) return;\n const q = query.toLowerCase().trim();\n this.mentionPickerFiltered = q\n ? this.mentionPickerUsers.filter((u) => u.name.toLowerCase().includes(q))\n : this.mentionPickerUsers.slice();\n this.mentionPickerSelected = 0;\n\n if (this.mentionPickerFiltered.length === 0) {\n list.innerHTML = `<div class=\"vz-mention-empty\">${\n this.mentionPickerUsers.length === 0\n ? 'No teammates to mention yet.'\n : 'No teammates match.'\n }</div>`;\n return;\n }\n\n list.innerHTML = this.mentionPickerFiltered\n .map((u, i) => {\n const avatar = u.avatarUrl\n ? `<img class=\"vz-mention-avatar\" src=\"${escapeHtml(u.avatarUrl)}\" alt=\"\" />`\n : `<span class=\"vz-mention-avatar vz-mention-avatar-initials\">${escapeHtml(initials(u.name) || '?')}</span>`;\n return `<button class=\"vz-mention-item${i === 0 ? ' is-selected' : ''}\" type=\"button\" data-mention-index=\"${i}\">${avatar}<span class=\"vz-mention-item-name\">${escapeHtml(u.name)}</span></button>`;\n })\n .join('');\n }\n\n private moveMentionPickerSelection(delta: number) {\n if (this.mentionPickerFiltered.length === 0) return;\n const n = this.mentionPickerFiltered.length;\n this.mentionPickerSelected = (this.mentionPickerSelected + delta + n) % n;\n this.refreshMentionPickerHighlight();\n }\n\n private refreshMentionPickerHighlight() {\n const picker = this.mentionPicker;\n if (!picker) return;\n const items = picker.querySelectorAll<HTMLElement>('.vz-mention-item');\n items.forEach((el, i) => el.classList.toggle('is-selected', i === this.mentionPickerSelected));\n items[this.mentionPickerSelected]?.scrollIntoView({ block: 'nearest' });\n }\n\n private commitMentionPickerSelection() {\n const user = this.mentionPickerFiltered[this.mentionPickerSelected];\n if (!user) return;\n if (this.mentionPickerMode === 'editor' && this.mentionPickerEditorRange) {\n // Replace `@<query>` in the editor with the chip. Done before\n // closing the picker so the saved Range still resolves.\n this.replaceRangeWithMentionChip(this.mentionPickerEditorRange, user);\n } else {\n this.insertMentionChip(user);\n }\n this.closeMentionPicker();\n }\n\n /**\n * Swap a Range covering `@<query>` for a mention chip + trailing\n * space, then place the caret after the space so the user can keep\n * typing. Used only in editor-trigger mode.\n */\n private replaceRangeWithMentionChip(range: Range, user: MentionableUser) {\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement | null;\n if (!editor) return;\n const chip = document.createElement('span');\n chip.className = 'vz-chip vz-chip-mention';\n chip.contentEditable = 'false';\n chip.setAttribute('data-mention-id', user.id);\n chip.textContent = '@' + user.name;\n range.deleteContents();\n range.insertNode(chip);\n const space = document.createTextNode(' ');\n chip.after(space);\n const sel = window.getSelection();\n if (sel) {\n const after = document.createRange();\n after.setStartAfter(space);\n after.collapse(true);\n sel.removeAllRanges();\n sel.addRange(after);\n }\n editor.focus();\n }\n\n /** Wires Esc + outside-click handlers to close the open picker. */\n private wireMentionPickerDismiss() {\n if (this.mentionPickerDismiss) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n this.closeMentionPicker();\n }\n };\n const onOutside = (e: MouseEvent) => {\n if (!this.mentionPicker) return;\n if (this.mentionPicker.contains(e.target as Node)) return;\n // In editor mode, clicks inside the editor shouldn't close the\n // picker — the user might be repositioning the caret to keep\n // typing the @ query.\n if (this.mentionPickerMode === 'editor') {\n const editor = this.el.querySelector('.vz-textarea');\n if (editor && editor.contains(e.target as Node)) return;\n }\n this.closeMentionPicker();\n };\n document.addEventListener('keydown', onKey, true);\n document.addEventListener('mousedown', onOutside, true);\n this.mentionPickerDismiss = () => {\n document.removeEventListener('keydown', onKey, true);\n document.removeEventListener('mousedown', onOutside, true);\n };\n }\n\n private closeMentionPicker() {\n if (this.mentionPickerDismiss) {\n this.mentionPickerDismiss();\n this.mentionPickerDismiss = null;\n }\n if (this.mentionPicker) {\n this.mentionPicker.remove();\n this.mentionPicker = null;\n }\n this.mentionPickerFiltered = [];\n this.mentionPickerSelected = 0;\n this.mentionPickerEditorRange = null;\n }\n\n /** Position the picker below the given anchor (the @ mention button). */\n private positionMentionPickerByElement(picker: HTMLDivElement, anchor: HTMLElement) {\n const popRect = this.el.getBoundingClientRect();\n const btnRect = anchor.getBoundingClientRect();\n picker.style.left = `${btnRect.left - popRect.left}px`;\n picker.style.top = `${btnRect.bottom - popRect.top + 4}px`;\n }\n\n /** Position the picker below the `@<query>` text range in the editor. */\n private positionMentionPickerByRange(picker: HTMLDivElement, range: Range) {\n const popRect = this.el.getBoundingClientRect();\n const rect = range.getBoundingClientRect();\n picker.style.left = `${rect.left - popRect.left}px`;\n picker.style.top = `${rect.bottom - popRect.top + 4}px`;\n }\n\n /**\n * Insert a mention chip for the given user into the editor at the\n * current caret position. Accepts any user-like shape with a required\n * `id` + `name` — covers both the current VizuUser (for self-mention)\n * and MentionableUser (from the workspace member picker).\n */\n private insertMentionChip(user: { id?: string; name: string } | null) {\n if (!user || !user.id) return;\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement | null;\n if (!editor) return;\n const chip = document.createElement('span');\n chip.className = 'vz-chip vz-chip-mention';\n chip.contentEditable = 'false';\n chip.setAttribute('data-mention-id', user.id);\n chip.textContent = '@' + user.name;\n\n editor.focus();\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) {\n editor.appendChild(chip);\n editor.appendChild(document.createTextNode(' '));\n this.moveCursorToEnd(editor);\n return;\n }\n const r = sel.getRangeAt(0);\n r.deleteContents();\n r.insertNode(chip);\n const space = document.createTextNode(' ');\n chip.after(space);\n const newRange = document.createRange();\n newRange.setStartAfter(space);\n newRange.collapse(true);\n sel.removeAllRanges();\n sel.addRange(newRange);\n }\n\n private save() {\n const editor = this.el.querySelector('.vz-textarea') as HTMLElement | null;\n if (!editor) return;\n const text = this.serializeEditor(editor);\n // Allow attachment-only comments: only block save when there's no text\n // AND no finished attachment uploads.\n const finishedCount = this.pendingAttachments.filter((a) => a.url).length;\n if (!text && finishedCount === 0) return;\n const usedRefs: Record<string, ElementFingerprint> = {};\n const re = /\\[#[^\\]]+\\]\\(vizu-ref:([a-zA-Z0-9_-]+)\\)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n const rid = m[1];\n if (this.pendingRefs[rid]) usedRefs[rid] = this.pendingRefs[rid];\n }\n const mentions = extractMentions(text);\n // Only include attachments that finished uploading (have a real URL).\n const finishedAttachments = this.pendingAttachments.filter((a) => a.url);\n this.callbacks.onAdd(\n text,\n [...this.anchorFingerprints],\n Object.keys(usedRefs).length ? usedRefs : undefined,\n mentions.length ? mentions : undefined,\n finishedAttachments.length ? finishedAttachments : undefined,\n );\n editor.innerHTML = '';\n this.pendingRefs = {};\n this.pendingAttachments = [];\n this.renderAttachmentList();\n }\n\n private position() {\n const primary = this.anchorTargets[0];\n if (!primary) return;\n const rect = primary.getBoundingClientRect();\n const popRect = this.el.getBoundingClientRect();\n const pad = 8;\n let top = rect.bottom + pad;\n let left = rect.left;\n if (top + popRect.height > window.innerHeight - pad) {\n top = rect.top - popRect.height - pad;\n if (top < pad) top = pad;\n }\n if (left + popRect.width > window.innerWidth - pad) {\n left = window.innerWidth - popRect.width - pad;\n }\n if (left < pad) left = pad;\n this.el.style.top = top + 'px';\n this.el.style.left = left + 'px';\n }\n\n destroy() {\n this.closeMentionPicker();\n this.el.removeEventListener('click', this.handleClick);\n this.el.remove();\n }\n}\n","import type { VizuAction, VizuComment, VizuUser } from './types';\nimport { escapeHtml, avatarHtml } from './util';\n\nexport interface PillCallbacks {\n onToggleSidebar: () => void;\n onDisable: () => void;\n onAction: (id: string) => void;\n onToggleMultiSelect: () => void;\n onCommitSelection: () => void;\n onClearSelection: () => void;\n}\n\nexport class Pill {\n private el: HTMLDivElement;\n private root: HTMLElement;\n private callbacks: PillCallbacks;\n private sidebarOpen = false;\n private actions: VizuAction[] = [];\n private commentsCount = 0;\n private user: VizuUser | null = null;\n private multiSelectMode = false;\n private selectionCount = 0;\n\n constructor(root: HTMLElement, callbacks: PillCallbacks) {\n this.root = root;\n this.callbacks = callbacks;\n this.el = document.createElement('div');\n this.el.className = 'vz-pill';\n this.el.style.display = 'none';\n this.el.addEventListener('click', this.handleClick);\n this.root.appendChild(this.el);\n }\n\n setActions(actions: VizuAction[]) {\n this.actions = actions;\n this.render();\n }\n\n setComments(comments: VizuComment[]) {\n this.commentsCount = comments.length;\n this.render();\n }\n\n setUser(user: VizuUser | null) {\n this.user = user;\n this.render();\n }\n\n setSidebarOpen(open: boolean) {\n this.sidebarOpen = open;\n const btn = this.el.querySelector<HTMLElement>('[data-vz=\"sidebar\"]');\n if (btn) btn.classList.toggle('is-active', open);\n }\n\n setMultiSelectState(mode: boolean, selectionCount: number) {\n this.multiSelectMode = mode;\n this.selectionCount = selectionCount;\n this.el.classList.toggle('is-selecting', mode && selectionCount > 0);\n this.render();\n }\n\n show() {\n this.el.style.display = 'flex';\n this.render();\n }\n\n hide() {\n this.el.style.display = 'none';\n }\n\n private render() {\n if (this.el.style.display === 'none') return;\n const visibleActions = this.actions.filter((a) =>\n a.visibleWhen ? a.visibleWhen({ commentsCount: this.commentsCount }) : true,\n );\n const userHtml = this.user\n ? `<div class=\"vz-pill-user\" title=\"${escapeHtml(this.user.name)}\">${avatarHtml(this.user, 'vz-pill-user-avatar')} ${escapeHtml(this.user.name.split(' ')[0]!)}</div><span class=\"vz-pill-divider\"></span>`\n : '';\n const actionsHtml = visibleActions.length\n ? visibleActions\n .map(\n (a) =>\n `<button class=\"vz-pill-btn${a.variant === 'primary' ? ' primary' : ''}\" data-vz=\"action\" data-id=\"${escapeHtml(a.id)}\"${a.title ? ` title=\"${escapeHtml(a.title)}\"` : ''}>${escapeHtml(a.label)}</button>`,\n )\n .join('') + '<span class=\"vz-pill-divider\"></span>'\n : '';\n\n // Multi-select state overrides the \"N comments\" badge area with selection + commit\n let middle = '';\n if (this.multiSelectMode && this.selectionCount > 0) {\n middle = `\n <span class=\"vz-pill-count-btn is-active\" title=\"Selected ${this.selectionCount} element${this.selectionCount === 1 ? '' : 's'}\">${this.selectionCount} selected</span>\n <button class=\"vz-pill-btn primary\" data-vz=\"commit-selection\">Comment on ${this.selectionCount} →</button>\n <button class=\"vz-pill-btn\" data-vz=\"clear-selection\">Cancel</button>\n <span class=\"vz-pill-divider\"></span>\n `;\n } else {\n const count = this.commentsCount;\n middle = `\n <button class=\"vz-pill-count-btn ${this.sidebarOpen ? 'is-active' : ''}\" data-vz=\"sidebar\" title=\"Show all comments\">${count} comment${count === 1 ? '' : 's'}</button>\n <span class=\"vz-pill-divider\"></span>\n ${userHtml}\n ${actionsHtml}\n `;\n }\n\n const toggleTitle = this.multiSelectMode\n ? 'Exit multi-select mode'\n : 'Multi-select: click multiple elements, then comment on the group (or hold Shift while clicking)';\n\n this.el.innerHTML = `\n <span class=\"vz-pill-dot\" aria-hidden=\"true\"></span>\n <span class=\"vz-pill-label\">Vizu</span>\n ${middle}\n <button class=\"vz-pill-toggle ${this.multiSelectMode ? 'is-active' : ''}\" data-vz=\"multi\" title=\"${escapeHtml(toggleTitle)}\" aria-label=\"${escapeHtml(toggleTitle)}\">\n <svg viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\"><rect x=\"1\" y=\"1\" width=\"5\" height=\"5\"/><rect x=\"8\" y=\"1\" width=\"5\" height=\"5\"/><rect x=\"1\" y=\"8\" width=\"5\" height=\"5\"/><rect x=\"8\" y=\"8\" width=\"5\" height=\"5\"/></svg>\n </button>\n <button class=\"vz-pill-icon-btn\" data-vz=\"disable\" title=\"Disable Vizu (toggle with shortcut)\" aria-label=\"Disable\">×</button>\n `;\n }\n\n private handleClick = (e: MouseEvent) => {\n const btn = (e.target as HTMLElement).closest('[data-vz]') as HTMLElement | null;\n if (!btn) return;\n const a = btn.getAttribute('data-vz');\n if (a === 'sidebar') this.callbacks.onToggleSidebar();\n else if (a === 'disable') this.callbacks.onDisable();\n else if (a === 'multi') this.callbacks.onToggleMultiSelect();\n else if (a === 'commit-selection') this.callbacks.onCommitSelection();\n else if (a === 'clear-selection') this.callbacks.onClearSelection();\n else if (a === 'action') {\n const id = btn.getAttribute('data-id');\n if (id) this.callbacks.onAction(id);\n }\n };\n\n toast(message: string) {\n const t = document.createElement('div');\n t.className = 'vz-toast';\n t.textContent = message;\n this.root.appendChild(t);\n setTimeout(() => t.remove(), 1800);\n }\n\n destroy() {\n this.el.removeEventListener('click', this.handleClick);\n this.el.remove();\n }\n}\n","import type { VizuComment, ElementFingerprint, CommentStatus, MatchConfidence } from './types';\nimport { escapeHtml, formatTime, avatarHtml, renderCommentText, renderAttachmentsHtml } from './util';\nimport { fingerprintKey, fingerprintLabel } from './fingerprint';\n\nexport interface SidebarCallbacks {\n onJumpTo: (fp: ElementFingerprint) => void;\n onJumpToReference: (commentId: string, refId: string) => void;\n onDelete: (id: string) => void;\n onUpdateStatus: (id: string, status: CommentStatus) => void;\n onClose: () => void;\n}\n\ninterface Group {\n fp: ElementFingerprint;\n comments: VizuComment[];\n}\n\ntype Filter = 'open' | 'all';\n\nexport class Sidebar {\n private el: HTMLDivElement;\n private root: HTMLElement;\n private callbacks: SidebarCallbacks;\n private open = false;\n private filter: Filter = 'open';\n private lastComments: VizuComment[] = [];\n private lastConfidence = new Map<string, MatchConfidence>();\n\n constructor(root: HTMLElement, callbacks: SidebarCallbacks) {\n this.root = root;\n this.callbacks = callbacks;\n this.el = document.createElement('div');\n this.el.className = 'vz-sidebar';\n this.el.addEventListener('click', this.handleClick);\n this.root.appendChild(this.el);\n }\n\n toggle(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n if (this.open) this.close();\n else this.show(comments, confidence);\n }\n show(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n this.render(comments, confidence);\n requestAnimationFrame(() => this.el.classList.add('is-open'));\n this.open = true;\n }\n update(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n if (this.open) this.render(comments, confidence);\n }\n close() { this.el.classList.remove('is-open'); this.open = false; }\n isOpen(): boolean { return this.open; }\n\n private render(comments: VizuComment[], confidence?: Map<string, MatchConfidence>) {\n this.lastComments = comments;\n if (confidence) this.lastConfidence = new Map(confidence);\n const conf = this.lastConfidence;\n\n // Partition into active (exact + likely + drifted) vs orphaned. Active\n // are grouped by the element they anchor to; orphaned get their own\n // section at the bottom with the fingerprint text snippet as label.\n const orphans = comments.filter((c) => conf.get(c.id) === 'orphaned');\n const anchored = comments.filter((c) => conf.get(c.id) !== 'orphaned');\n\n // Apply status filter to the anchored set only. Orphans always show\n // when 'All' is active (they're a separate problem); hidden under 'Open'.\n const visible = this.filter === 'open'\n ? anchored.filter((c) => (c.status ?? 'open') === 'open')\n : anchored;\n const visibleOrphans = this.filter === 'open' ? [] : orphans;\n\n const counts = countByStatus(comments);\n\n if (visible.length === 0 && visibleOrphans.length === 0) {\n const emptyCopy = this.filter === 'open' && comments.length > 0\n ? `<strong>Nothing open.</strong>${counts.resolved + counts.wontfix} closed comment${counts.resolved + counts.wontfix === 1 ? '' : 's'} hidden. Switch to <em>All</em> to see them.`\n : '<strong>No comments yet</strong>Click any element on the page to leave one.';\n this.el.innerHTML = `\n ${this.headerHtml(comments.length, counts)}\n <div class=\"vz-sidebar-body\">\n <div class=\"vz-sidebar-empty\">${emptyCopy}</div>\n </div>\n `;\n return;\n }\n // Group by PRIMARY anchor (first fingerprint). Other anchors of a multi-anchor\n // comment are shown as chips inside the item.\n const groups = new Map<string, Group>();\n for (const c of visible) {\n const fps = c.fingerprints || [];\n const primary = fps[0];\n if (!primary) continue;\n const key = fingerprintKey(primary);\n const g = groups.get(key);\n if (g) g.comments.push(c);\n else groups.set(key, { fp: primary, comments: [c] });\n }\n\n const groupHtml: string[] = [];\n let gi = 0;\n for (const g of groups.values()) {\n const elLabel = g.fp.tagName.toLowerCase() + (g.fp.attributes.id ? '#' + g.fp.attributes.id : '');\n const textPreview = g.fp.textSnippet.slice(0, 60);\n groupHtml.push(`\n <div class=\"vz-sidebar-group\" data-group=\"${gi}\">\n <div class=\"vz-sidebar-group-head\">\n ${escapeHtml(elLabel)}\n ${textPreview ? `<div class=\"vz-sidebar-group-text\">&ldquo;${escapeHtml(textPreview)}${g.fp.textSnippet.length > 60 ? '…' : ''}&rdquo;</div>` : ''}\n </div>\n ${g.comments\n .map((c) => {\n const status = c.status ?? 'open';\n const others = (c.fingerprints || []).slice(1);\n const otherChips = others.length\n ? `<div class=\"vz-anchor-list\" style=\"margin-top:6px\">${others\n .map(\n (fp, i) => `<span class=\"vz-anchor-chip\" data-vz=\"jump-fp\" data-fp-key=\"${escapeHtml(fingerprintKey(fp))}\" title=\"Jump to this element\">+${i + 2}/${(c.fingerprints || []).length} ${escapeHtml(fingerprintLabel(fp))}</span>`,\n )\n .join('')}</div>`\n : '';\n const cConf = conf.get(c.id);\n const driftedBadge = cConf === 'drifted'\n ? `<span class=\"vz-status-pill vz-status-drifted\" title=\"Anchored via a fallback match — the original element may have moved or changed.\">drifted</span>`\n : '';\n return `\n <div class=\"vz-sidebar-item ${status !== 'open' ? 'vz-sidebar-item-dim' : ''}\" data-comment-id=\"${escapeHtml(c.id)}\" data-vz=\"jump\" data-group=\"${gi}\" role=\"button\" tabindex=\"0\">\n <div class=\"vz-sidebar-item-row\">\n ${c.author ? `<div class=\"vz-sidebar-item-author\">${avatarHtml(c.author)} <span class=\"vz-sidebar-item-author-name\">${escapeHtml(c.author.name)}</span></div>` : ''}\n ${driftedBadge}\n ${status !== 'open' ? `<span class=\"vz-status-pill vz-status-${status}\">${status}</span>` : ''}\n </div>\n <span class=\"vz-sidebar-item-text\">${renderCommentText(c.text, c.id)}</span>\n ${renderAttachmentsHtml(c.attachments)}\n ${otherChips}\n <div class=\"vz-sidebar-item-meta\">\n <span>${escapeHtml(formatTime(c.createdAt))}${(c.fingerprints || []).length > 1 ? ` · grouped (${(c.fingerprints || []).length})` : ''}${(c.replies?.length ?? 0) > 0 ? ` · ${c.replies!.length} ${c.replies!.length === 1 ? 'reply' : 'replies'}` : ''}</span>\n <span class=\"vz-sidebar-item-actions\">\n ${statusActionsHtml(c.id, status)}\n <button class=\"vz-sidebar-item-delete\" data-vz=\"delete\" data-id=\"${escapeHtml(c.id)}\" title=\"Delete comment\">delete</button>\n </span>\n </div>\n </div>\n `;\n })\n .join('')}\n </div>\n `);\n gi++;\n }\n const orphanHtml = visibleOrphans.length > 0 ? renderOrphansHtml(visibleOrphans) : '';\n this.el.innerHTML = `\n ${this.headerHtml(comments.length, counts)}\n <div class=\"vz-sidebar-body\">\n ${groupHtml.join('')}\n ${orphanHtml}\n </div>\n `;\n // Stash group payload + per-fp lookup for anchor-chip jumps\n const items = this.el.querySelectorAll('[data-vz=\"jump\"]');\n const groupArr = [...groups.values()];\n for (const item of items) {\n const groupIndex = Number((item as HTMLElement).dataset.group);\n (item as any).__group = groupArr[groupIndex];\n }\n const chips = this.el.querySelectorAll('[data-vz=\"jump-fp\"]');\n for (const chip of chips) {\n const key = chip.getAttribute('data-fp-key');\n const fp = findFingerprintByKey(visible, key || '');\n (chip as any).__fp = fp;\n }\n }\n\n private headerHtml(total: number, counts: { open: number; resolved: number; wontfix: number }): string {\n return `\n <div class=\"vz-sidebar-header\">\n <div class=\"vz-sidebar-title\">\n Comments\n <span class=\"vz-sidebar-title-count\">${total}</span>\n </div>\n <button class=\"vz-sidebar-close\" data-vz=\"close\" aria-label=\"Close sidebar\">×</button>\n </div>\n <div class=\"vz-sidebar-filters\" role=\"tablist\" aria-label=\"Status filter\">\n <button class=\"vz-sidebar-filter ${this.filter === 'open' ? 'is-active' : ''}\" data-vz=\"filter\" data-filter=\"open\" role=\"tab\" aria-selected=\"${this.filter === 'open'}\">\n Open <span class=\"vz-sidebar-filter-count\">${counts.open}</span>\n </button>\n <button class=\"vz-sidebar-filter ${this.filter === 'all' ? 'is-active' : ''}\" data-vz=\"filter\" data-filter=\"all\" role=\"tab\" aria-selected=\"${this.filter === 'all'}\">\n All <span class=\"vz-sidebar-filter-count\">${total}</span>\n </button>\n </div>\n `;\n }\n\n private handleClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n const refEl = target.closest('[data-vz=\"ref\"]') as HTMLElement | null;\n if (refEl) {\n e.stopPropagation();\n const commentId = refEl.getAttribute('data-comment-id');\n const refIdAttr = refEl.getAttribute('data-ref-id');\n if (commentId && refIdAttr) this.callbacks.onJumpToReference(commentId, refIdAttr);\n return;\n }\n const filterEl = target.closest('[data-vz=\"filter\"]') as HTMLElement | null;\n if (filterEl) {\n e.stopPropagation();\n const f = filterEl.getAttribute('data-filter') as Filter | null;\n if (f && f !== this.filter) {\n this.filter = f;\n this.render(this.lastComments);\n }\n return;\n }\n const statusEl = target.closest('[data-vz=\"status\"]') as HTMLElement | null;\n if (statusEl) {\n e.stopPropagation();\n const id = statusEl.getAttribute('data-id');\n const status = statusEl.getAttribute('data-status') as CommentStatus | null;\n if (id && status) this.callbacks.onUpdateStatus(id, status);\n return;\n }\n const deleteEl = target.closest('[data-vz=\"delete\"]') as HTMLElement | null;\n if (deleteEl) {\n e.stopPropagation();\n const id = deleteEl.getAttribute('data-id');\n if (id) this.callbacks.onDelete(id);\n return;\n }\n if (target.closest('[data-vz=\"close\"]')) {\n this.callbacks.onClose();\n return;\n }\n const fpChip = target.closest('[data-vz=\"jump-fp\"]') as any;\n if (fpChip && fpChip.__fp) {\n e.stopPropagation();\n this.callbacks.onJumpTo(fpChip.__fp);\n return;\n }\n const item = target.closest('[data-vz=\"jump\"]') as any;\n if (item && item.__group) this.callbacks.onJumpTo(item.__group.fp);\n };\n\n destroy() {\n this.el.removeEventListener('click', this.handleClick);\n this.el.remove();\n }\n}\n\n/* ─── helpers ──────────────────────────────────────────────────────────── */\n\nfunction countByStatus(comments: VizuComment[]): { open: number; resolved: number; wontfix: number } {\n const c = { open: 0, resolved: 0, wontfix: 0 };\n for (const x of comments) {\n const s = x.status ?? 'open';\n c[s] = (c[s] ?? 0) + 1;\n }\n return c;\n}\n\nfunction statusActionsHtml(id: string, current: CommentStatus): string {\n const safe = escapeHtml(id);\n if (current === 'open') {\n return `\n <button class=\"vz-sidebar-item-status\" data-vz=\"status\" data-id=\"${safe}\" data-status=\"resolved\" title=\"Mark resolved\">resolve</button>\n <button class=\"vz-sidebar-item-status\" data-vz=\"status\" data-id=\"${safe}\" data-status=\"wontfix\" title=\"Won't fix\">won&apos;t fix</button>\n `;\n }\n // For closed states, the only action is reopening.\n return `<button class=\"vz-sidebar-item-status\" data-vz=\"status\" data-id=\"${safe}\" data-status=\"open\" title=\"Reopen\">reopen</button>`;\n}\n\n/**\n * Render the orphaned-comments section. These are comments whose\n * fingerprints didn't resolve to any DOM element on the current page —\n * either because the element was removed, the page was re-laid out\n * significantly, or the comment was migrated from a different deploy.\n *\n * The label is whatever we know about the original anchor: tagName +\n * the captured textSnippet truncated to 60 chars.\n */\nfunction renderOrphansHtml(orphans: VizuComment[]): string {\n const items = orphans\n .map((c) => {\n const status = c.status ?? 'open';\n const primary = (c.fingerprints || [])[0];\n const label = primary ? primary.tagName.toLowerCase() : 'comment';\n const snippet = primary?.textSnippet?.slice(0, 60) ?? '';\n return `\n <div class=\"vz-sidebar-item vz-sidebar-item-orphan\" data-comment-id=\"${escapeHtml(c.id)}\">\n <div class=\"vz-sidebar-item-row\">\n ${c.author ? `<div class=\"vz-sidebar-item-author\">${avatarHtml(c.author)} <span class=\"vz-sidebar-item-author-name\">${escapeHtml(c.author.name)}</span></div>` : ''}\n <span class=\"vz-status-pill vz-status-orphan\">orphaned</span>\n </div>\n <div class=\"vz-sidebar-orphan-anchor\">\n was on <code>${escapeHtml(label)}</code>${snippet ? ` &ldquo;${escapeHtml(snippet)}${(primary?.textSnippet?.length ?? 0) > 60 ? '…' : ''}&rdquo;` : ''}\n </div>\n <span class=\"vz-sidebar-item-text\">${renderCommentText(c.text, c.id)}</span>\n ${renderAttachmentsHtml(c.attachments)}\n <div class=\"vz-sidebar-item-meta\">\n <span>${escapeHtml(formatTime(c.createdAt))}${status !== 'open' ? ` · ${status}` : ''}</span>\n <span class=\"vz-sidebar-item-actions\">\n <button class=\"vz-sidebar-item-delete\" data-vz=\"delete\" data-id=\"${escapeHtml(c.id)}\" title=\"Delete comment\">delete</button>\n </span>\n </div>\n </div>\n `;\n })\n .join('');\n return `\n <div class=\"vz-sidebar-orphans\">\n <div class=\"vz-sidebar-orphans-head\">\n Orphaned · ${orphans.length}\n <span class=\"vz-sidebar-orphans-sub\">couldn't find the original element on this page</span>\n </div>\n ${items}\n </div>\n `;\n}\n\nfunction findFingerprintByKey(comments: VizuComment[], key: string): ElementFingerprint | null {\n for (const c of comments) {\n for (const fp of c.fingerprints || []) {\n if (fingerprintKey(fp) === key) return fp;\n }\n }\n return null;\n}\n","export const STYLES = `\n[data-vizu-root] {\n position: fixed; top: 0; left: 0; width: 0; height: 0;\n z-index: 2147483647;\n pointer-events: none;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif;\n font-size: 13px; line-height: 1.5; color: #fafafa;\n --vz-accent: #FF6647;\n --vz-bg: #0A0A0A;\n --vz-border: #2A2A2A;\n --vz-bg-hover: #1A1A1A;\n --vz-text-muted: #888;\n --vz-marker-fg: #FFFFFF;\n}\n[data-vizu-root] * { box-sizing: border-box; }\n/* Button reset uses :where() to drop its specificity to 0,0,1 so any single-class\n selector like .vz-marker / .vz-pill-btn / .vz-btn that sets background or\n color still wins, without needing to redeclare those everywhere. */\n:where([data-vizu-root]) button {\n font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0;\n}\n\n/* Hover highlight (transient) */\n.vz-highlight {\n position: fixed;\n border: 2px dashed var(--vz-accent);\n background: color-mix(in srgb, var(--vz-accent) 8%, transparent);\n pointer-events: none;\n transition: all 90ms ease-out;\n border-radius: 2px;\n z-index: 1;\n}\n\n/* Always-on outline for commented elements */\n.vz-comment-outline {\n position: fixed;\n border: 1px dashed color-mix(in srgb, var(--vz-accent) 55%, transparent);\n background: color-mix(in srgb, var(--vz-accent) 4%, transparent);\n pointer-events: none;\n border-radius: 2px;\n z-index: 1;\n}\n.vz-comment-outline.is-selected-pending {\n border: 1.5px dashed var(--vz-accent);\n background: color-mix(in srgb, var(--vz-accent) 10%, transparent);\n}\n/* Drifted: the anchor resolved only via a fuzzy fallback (sibling-index\n or page-wide text snippet). Amber border warns visitors the match\n might be wrong. */\n.vz-comment-outline-drifted {\n border-color: rgb(252, 211, 77);\n background: rgba(245, 158, 11, 0.06);\n}\n.vz-comment-outline.is-pulsing {\n animation: vz-pulse 1500ms ease-out;\n}\n@keyframes vz-pulse {\n 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--vz-accent) 60%, transparent); border-color: var(--vz-accent); }\n 60% { box-shadow: 0 0 0 22px color-mix(in srgb, var(--vz-accent) 0%, transparent); border-color: var(--vz-accent); }\n 100% { box-shadow: 0 0 0 0 transparent; border-color: color-mix(in srgb, var(--vz-accent) 55%, transparent); }\n}\n\n/* Strong \"spotlight\" rendered when the user jumps to an element (sidebar item,\n anchor chip, or # reference click). Visible even on elements that don't have\n a saved comment yet. */\n.vz-jump-spotlight {\n position: fixed;\n border: 2.5px solid var(--vz-accent);\n background: color-mix(in srgb, var(--vz-accent) 16%, transparent);\n pointer-events: none;\n border-radius: 4px;\n z-index: 4;\n animation: vz-spotlight 1700ms cubic-bezier(0.22, 0.61, 0.36, 1) forwards;\n}\n@keyframes vz-spotlight {\n 0% { opacity: 0; box-shadow: 0 0 0 0 transparent; }\n 10% { opacity: 1; box-shadow: 0 0 0 6px color-mix(in srgb, var(--vz-accent) 55%, transparent); }\n 45% { opacity: 1; box-shadow: 0 0 0 28px color-mix(in srgb, var(--vz-accent) 10%, transparent); }\n 80% { opacity: 0.9; box-shadow: 0 0 0 36px color-mix(in srgb, var(--vz-accent) 0%, transparent); }\n 100% { opacity: 0; box-shadow: 0 0 0 36px transparent; background: color-mix(in srgb, var(--vz-accent) 0%, transparent); }\n}\n\n/* Figma-style speech-bubble marker — pinned at top-right of element, tail pointing in */\n.vz-marker {\n position: fixed;\n min-width: 28px; height: 26px;\n padding: 0 9px;\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n font-size: 11px; font-weight: 700;\n letter-spacing: 0.01em;\n display: inline-flex; align-items: center; justify-content: center;\n cursor: pointer;\n pointer-events: auto;\n /* Asymmetric radius = the \"pin\" tail: sharp at bottom-left, rounded elsewhere */\n border-radius: 14px 14px 14px 3px;\n /* Solid Vizu-colored halo via 2px ring + drop shadow — visible on any backdrop */\n box-shadow:\n 0 4px 12px rgba(0,0,0,0.28),\n 0 0 0 2px var(--vz-bg);\n /* Center the marker on the element's top-right corner, half outside the element */\n transform: translate(-50%, -50%);\n transition: transform 120ms cubic-bezier(0.34, 1.56, 0.64, 1);\n z-index: 2;\n}\n.vz-marker.is-group-member { font-size: 10px; padding: 0 7px; }\n.vz-marker.is-sibling-pulsing { animation: vz-marker-pulse 600ms ease-out; }\n\n/* Drifted marker: amber-tinted fill + dashed ring instead of solid halo\n so visitors can see at a glance \"this might not be the right element\". */\n.vz-marker-drifted {\n background: rgb(252, 211, 77);\n color: rgba(0, 0, 0, 0.78);\n box-shadow:\n 0 4px 12px rgba(0,0,0,0.28),\n 0 0 0 2px var(--vz-bg),\n 0 0 0 3px rgb(252, 211, 77);\n}\n@keyframes vz-marker-pulse {\n 0% { box-shadow: 0 4px 12px rgba(0,0,0,0.28), 0 0 0 2px var(--vz-bg), 0 0 0 0 var(--vz-accent); }\n 60% { box-shadow: 0 4px 12px rgba(0,0,0,0.28), 0 0 0 2px var(--vz-bg), 0 0 0 10px color-mix(in srgb, var(--vz-accent) 0%, transparent); }\n 100% { box-shadow: 0 4px 12px rgba(0,0,0,0.28), 0 0 0 2px var(--vz-bg), 0 0 0 0 transparent; }\n}\n.vz-marker:hover {\n transform: translate(-50%, -50%) scale(1.1);\n}\n.vz-marker-avatar {\n width: 100%; height: 100%;\n border-radius: 50% 50% 50% 3px;\n object-fit: cover;\n display: block;\n}\n.vz-marker.has-avatar {\n padding: 0;\n width: 28px; min-width: 28px; height: 28px;\n background: var(--vz-bg);\n border-radius: 50% 50% 50% 3px;\n overflow: hidden;\n}\n.vz-marker.has-avatar .vz-marker-count {\n position: absolute;\n bottom: -4px; right: -4px;\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n min-width: 16px; height: 16px;\n border-radius: 8px;\n font-size: 9px; font-weight: 700;\n display: inline-flex; align-items: center; justify-content: center;\n padding: 0 4px;\n box-shadow: 0 0 0 2px var(--vz-bg);\n}\n.vz-marker-initials {\n font-size: 11px; font-weight: 700;\n color: #fafafa;\n background: var(--vz-bg-hover);\n width: 100%; height: 100%;\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50% 50% 50% 3px;\n}\n\n/* Floating pill */\n.vz-pill {\n position: fixed;\n bottom: 24px; left: 50%; transform: translateX(-50%);\n background: var(--vz-bg);\n border: 1px solid var(--vz-border);\n border-radius: 999px;\n padding: 7px 8px 7px 18px;\n display: flex; align-items: center; gap: 10px;\n pointer-events: auto;\n box-shadow: 0 8px 24px rgba(0,0,0,.4), inset 0 0 0 1px rgba(255,255,255,.04);\n user-select: none;\n max-width: calc(100vw - 32px);\n z-index: 5;\n}\n.vz-pill.is-selecting { border-color: var(--vz-accent); }\n.vz-pill-toggle {\n width: 28px; height: 28px;\n border-radius: 6px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--vz-text-muted);\n transition: background 100ms, color 100ms;\n}\n.vz-pill-toggle:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-pill-toggle.is-active { background: color-mix(in srgb, var(--vz-accent) 22%, var(--vz-bg-hover)); color: var(--vz-accent); }\n.vz-pill-toggle svg { width: 14px; height: 14px; }\n.vz-pill-dot {\n width: 8px; height: 8px; border-radius: 4px;\n background: var(--vz-accent);\n box-shadow: 0 0 8px var(--vz-accent);\n flex-shrink: 0;\n}\n.vz-pill-label { font-size: 12px; font-weight: 600; letter-spacing: 0.02em; }\n.vz-pill-count-btn {\n font-size: 11px; color: var(--vz-text-muted);\n padding: 4px 10px;\n border-radius: 999px;\n transition: background 100ms, color 100ms;\n}\n.vz-pill-count-btn:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-pill-count-btn.is-active { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-pill-btn {\n padding: 7px 14px;\n border-radius: 999px;\n font-size: 12px; font-weight: 500;\n color: #fafafa;\n transition: background 100ms;\n white-space: nowrap;\n}\n.vz-pill-btn:hover { background: var(--vz-bg-hover); }\n.vz-pill-btn.primary {\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n font-weight: 600;\n padding: 7px 16px;\n}\n.vz-pill-btn.primary:hover { background: color-mix(in srgb, var(--vz-accent) 88%, white); }\n.vz-pill-divider { width: 1px; height: 18px; background: var(--vz-border); flex-shrink: 0; }\n.vz-pill-icon-btn {\n width: 28px; height: 28px;\n border-radius: 50%;\n display: inline-flex; align-items: center; justify-content: center;\n color: #fafafa;\n transition: background 100ms;\n}\n.vz-pill-icon-btn:hover { background: var(--vz-bg-hover); }\n.vz-pill-user {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 3px 10px 3px 4px;\n background: var(--vz-bg-hover);\n border-radius: 999px;\n font-size: 11px;\n color: #fafafa;\n}\n.vz-pill-user-avatar {\n width: 22px; height: 22px;\n border-radius: 50%;\n object-fit: cover;\n background: var(--vz-border);\n font-size: 10px; font-weight: 700;\n color: #fafafa;\n display: inline-flex; align-items: center; justify-content: center;\n}\n\n/* Popover */\n.vz-popover {\n position: fixed;\n background: var(--vz-bg);\n border: 1px solid var(--vz-border);\n border-radius: 8px;\n padding: 12px;\n min-width: 280px; max-width: 360px;\n pointer-events: auto;\n box-shadow: 0 12px 32px rgba(0,0,0,.5);\n display: flex; flex-direction: column; gap: 8px;\n z-index: 6;\n}\n.vz-popover-header {\n font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;\n color: var(--vz-text-muted);\n display: flex; justify-content: space-between; align-items: center;\n}\n.vz-popover-close {\n font-size: 18px; line-height: 1;\n color: #666;\n width: 22px; height: 22px;\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: 4px;\n}\n.vz-popover-close:hover { color: #fafafa; background: var(--vz-bg-hover); }\n.vz-target-label {\n font-size: 11px; color: var(--vz-text-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n word-break: break-all;\n padding: 6px 8px;\n background: var(--vz-bg-hover);\n border-radius: 4px;\n}\n.vz-comment-list {\n display: flex; flex-direction: column; gap: 6px;\n max-height: 200px; overflow-y: auto;\n}\n.vz-comment {\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n border-radius: 6px;\n padding: 8px 10px;\n font-size: 13px;\n display: flex; flex-direction: column; gap: 6px;\n}\n.vz-comment-author {\n display: flex; align-items: center; gap: 6px;\n font-size: 11px;\n color: var(--vz-text-muted);\n}\n.vz-comment-author-avatar {\n width: 18px; height: 18px;\n border-radius: 50%;\n background: var(--vz-border);\n display: inline-flex; align-items: center; justify-content: center;\n font-size: 9px; font-weight: 700;\n color: #fafafa;\n overflow: hidden;\n}\n.vz-comment-author-avatar img { width: 100%; height: 100%; object-fit: cover; }\n.vz-comment-author-name { color: #fafafa; font-weight: 500; }\n.vz-comment-meta {\n font-size: 10px; color: #666;\n display: flex; justify-content: space-between;\n}\n.vz-comment-delete { color: #666; font-size: 10px; text-decoration: underline; }\n.vz-comment-delete:hover { color: #ff7676; }\n.vz-empty { font-size: 12px; color: var(--vz-text-muted); padding: 4px 0; }\n.vz-textarea {\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n color: #fafafa;\n border-radius: 6px;\n padding: 8px 10px;\n font: inherit;\n font-size: 13px;\n resize: vertical;\n min-height: 60px;\n max-height: 160px;\n overflow-y: auto;\n outline: none;\n width: 100%;\n line-height: 1.5;\n word-break: break-word;\n white-space: pre-wrap;\n}\n.vz-textarea:focus { border-color: var(--vz-accent); }\n.vz-textarea:empty::before {\n content: attr(data-placeholder);\n color: var(--vz-text-muted);\n pointer-events: none;\n}\n/* Inline chip rendered live inside the contenteditable comment editor */\n.vz-chip {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 1px 8px 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-size: 12px; font-weight: 500;\n cursor: default;\n user-select: none;\n margin: 0 2px;\n vertical-align: baseline;\n white-space: nowrap;\n}\n.vz-chip::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--vz-accent); flex-shrink: 0; }\n\n/* Popover anchor list (multi-anchor comment header) */\n.vz-anchor-list {\n display: flex; flex-wrap: wrap; gap: 4px;\n padding: 6px 0 0;\n}\n.vz-anchor-chip {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 3px 8px 3px 6px;\n border-radius: 999px;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n font-size: 11px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #fafafa;\n cursor: pointer;\n}\n.vz-anchor-chip::before {\n content: '';\n width: 5px; height: 5px;\n border-radius: 50%;\n background: var(--vz-accent);\n}\n.vz-anchor-chip:hover { border-color: var(--vz-accent); }\n.vz-anchor-chip-remove {\n font-size: 12px; color: var(--vz-text-muted);\n margin-left: 2px;\n background: none; border: 0; cursor: pointer;\n padding: 0 2px;\n}\n.vz-anchor-chip-remove:hover { color: #ff7676; }\n.vz-anchor-hint { font-size: 11px; color: var(--vz-text-muted); padding: 4px 0; }\n.vz-popover-actions { display: flex; justify-content: flex-end; gap: 6px; }\n.vz-btn {\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 12px; font-weight: 500;\n transition: background 100ms;\n}\n.vz-btn-ghost { color: var(--vz-text-muted); }\n.vz-btn-ghost:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-btn-primary {\n background: var(--vz-accent);\n color: var(--vz-marker-fg);\n font-weight: 600;\n}\n.vz-btn-primary:hover { background: color-mix(in srgb, var(--vz-accent) 88%, white); }\n\n/* Mention picker — floating dropdown anchored under the @ mention button */\n.vz-mention-picker {\n position: absolute;\n z-index: 10;\n min-width: 200px;\n max-width: 280px;\n max-height: 240px;\n overflow-y: auto;\n padding: 4px;\n background: var(--vz-bg-popover, #1c1c1c);\n border: 1px solid var(--vz-border, rgba(255,255,255,0.08));\n border-radius: 8px;\n box-shadow: 0 12px 28px rgba(0,0,0,0.4), 0 2px 6px rgba(0,0,0,0.3);\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n.vz-mention-search {\n width: 100%;\n padding: 7px 9px;\n margin-bottom: 4px;\n background: rgba(255,255,255,0.04);\n border: 1px solid var(--vz-border, rgba(255,255,255,0.08));\n border-radius: 6px;\n color: #fafafa;\n font-size: 13px;\n outline: none;\n box-sizing: border-box;\n}\n.vz-mention-search:focus { border-color: var(--vz-accent, #ff8b6e); }\n.vz-mention-search::placeholder { color: var(--vz-text-muted); }\n.vz-mention-list {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-height: 0;\n}\n.vz-mention-loading,\n.vz-mention-empty {\n padding: 10px 12px;\n font-size: 12px;\n color: var(--vz-text-muted);\n text-align: center;\n}\n.vz-mention-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 10px;\n border-radius: 6px;\n background: transparent;\n border: none;\n color: #fafafa;\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n transition: background 80ms;\n}\n.vz-mention-item.is-selected,\n.vz-mention-item:focus-visible {\n background: var(--vz-bg-hover, rgba(255,255,255,0.08));\n outline: none;\n}\n.vz-mention-avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px; height: 22px;\n border-radius: 50%;\n font-size: 10px; font-weight: 600;\n color: var(--vz-marker-fg, #0a0a0a);\n background: var(--vz-accent, #ff8b6e);\n flex-shrink: 0;\n overflow: hidden;\n}\n.vz-mention-avatar img {\n width: 100%; height: 100%; object-fit: cover;\n}\n.vz-mention-item-name {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Sidebar */\n.vz-sidebar {\n position: fixed;\n top: 0; right: 0;\n width: 360px;\n height: 100vh;\n background: var(--vz-bg);\n border-left: 1px solid var(--vz-border);\n pointer-events: auto;\n display: flex; flex-direction: column;\n transform: translateX(100%);\n transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1);\n box-shadow: -16px 0 48px rgba(0,0,0,0.35);\n z-index: 7;\n}\n.vz-sidebar.is-open { transform: translateX(0); }\n.vz-sidebar-header {\n padding: 16px 18px;\n border-bottom: 1px solid var(--vz-border);\n display: flex; align-items: center; gap: 10px;\n flex-shrink: 0;\n}\n.vz-sidebar-title {\n font-size: 13px; font-weight: 600;\n display: flex; align-items: center; gap: 8px;\n}\n.vz-sidebar-title-count {\n font-size: 11px;\n color: var(--vz-text-muted);\n font-weight: 500;\n padding: 2px 8px;\n background: var(--vz-bg-hover);\n border-radius: 999px;\n}\n.vz-sidebar-close {\n margin-left: auto;\n width: 28px; height: 28px;\n border-radius: 6px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--vz-text-muted);\n font-size: 18px; line-height: 1;\n}\n.vz-sidebar-close:hover { background: var(--vz-bg-hover); color: #fafafa; }\n.vz-sidebar-body {\n flex: 1 1 auto;\n overflow-y: auto;\n padding: 12px;\n}\n.vz-sidebar-empty {\n padding: 32px 18px;\n text-align: center;\n color: var(--vz-text-muted);\n font-size: 13px;\n}\n.vz-sidebar-empty strong { color: #fafafa; display: block; margin-bottom: 4px; font-weight: 600; }\n.vz-sidebar-group {\n margin-bottom: 16px;\n}\n.vz-sidebar-group-head {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10px;\n text-transform: lowercase;\n color: var(--vz-text-muted);\n padding: 4px 6px;\n margin-bottom: 4px;\n word-break: break-all;\n}\n.vz-sidebar-group-text {\n font-family: inherit;\n text-transform: none;\n font-size: 11px;\n color: #c4c4c4;\n margin-top: 2px;\n}\n.vz-sidebar-item {\n display: block;\n width: 100%;\n text-align: left;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n border-radius: 8px;\n padding: 10px 12px;\n margin-bottom: 6px;\n font-size: 13px;\n color: #fafafa;\n transition: border-color 100ms, transform 100ms;\n}\n.vz-sidebar-item:hover {\n border-color: color-mix(in srgb, var(--vz-accent) 50%, var(--vz-border));\n transform: translateX(-2px);\n}\n.vz-sidebar-item-author {\n display: flex; align-items: center; gap: 6px;\n font-size: 11px;\n color: var(--vz-text-muted);\n margin-bottom: 4px;\n}\n.vz-sidebar-item-author .vz-comment-author-avatar { width: 16px; height: 16px; font-size: 8px; }\n.vz-sidebar-item-author-name { color: #fafafa; font-weight: 500; }\n.vz-sidebar-item-text {\n display: block;\n line-height: 1.45;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.vz-sidebar-item-meta {\n display: flex; justify-content: space-between; align-items: center;\n margin-top: 6px;\n font-size: 10px;\n color: #666;\n}\n.vz-sidebar-item-delete {\n color: #666;\n background: transparent;\n border: 0;\n padding: 0;\n cursor: pointer;\n text-decoration: underline;\n font-size: 10px;\n font-family: inherit;\n}\n.vz-sidebar-item-delete:hover { color: #ff7676; }\n\n/* Status filter tabs in the sidebar header */\n.vz-sidebar-filters {\n display: flex;\n gap: 4px;\n padding: 6px 16px 12px;\n border-bottom: 1px solid var(--vz-border);\n}\n.vz-sidebar-filter {\n background: transparent;\n border: 1px solid transparent;\n color: var(--vz-text-soft);\n font: inherit;\n font-size: 10.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n padding: 4px 10px;\n border-radius: 999px;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: color 100ms, background 100ms, border-color 100ms;\n}\n.vz-sidebar-filter:hover { color: var(--vz-text); }\n.vz-sidebar-filter.is-active {\n background: color-mix(in srgb, var(--vz-accent) 15%, transparent);\n color: var(--vz-accent);\n border-color: color-mix(in srgb, var(--vz-accent) 30%, transparent);\n}\n.vz-sidebar-filter-count {\n color: var(--vz-text-faint);\n font-size: 9.5px;\n}\n.vz-sidebar-filter.is-active .vz-sidebar-filter-count {\n color: inherit;\n opacity: 0.7;\n}\n\n/* Per-item status pill (resolved / wontfix) shown at top-right of dim items */\n.vz-sidebar-item-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 6px;\n}\n.vz-status-pill {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 9.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n font-weight: 600;\n}\n.vz-status-resolved {\n background: rgba(34, 197, 94, 0.18);\n color: rgb(74, 222, 128);\n}\n.vz-status-wontfix {\n background: rgba(113, 113, 122, 0.22);\n color: rgb(161, 161, 170);\n}\n/* Drifted anchor — fell through to a fuzzy fallback. Render amber. */\n.vz-status-drifted {\n background: rgba(245, 158, 11, 0.18);\n color: rgb(252, 211, 77);\n}\n/* Orphaned — anchor element no longer on the page. Quiet zinc, dim text. */\n.vz-status-orphan {\n background: rgba(113, 113, 122, 0.24);\n color: rgb(212, 212, 216);\n}\n\n/* Dim non-open items in 'All' view */\n.vz-sidebar-item-dim { opacity: 0.55; }\n.vz-sidebar-item-dim:hover { opacity: 0.85; }\n\n/* Orphaned-comments section — separate from the active comment list. */\n.vz-sidebar-orphans {\n margin-top: 18px;\n padding-top: 14px;\n border-top: 1px dashed var(--vz-border);\n}\n.vz-sidebar-orphans-head {\n padding: 0 16px;\n font-family: var(--vz-mono, ui-monospace, monospace);\n font-size: 10.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: rgb(212, 212, 216);\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin-bottom: 10px;\n}\n.vz-sidebar-orphans-sub {\n font-size: 9.5px;\n letter-spacing: 0.06em;\n text-transform: none;\n color: var(--vz-text-faint);\n}\n.vz-sidebar-item-orphan {\n opacity: 0.78;\n}\n.vz-sidebar-item-orphan:hover { opacity: 1; }\n.vz-sidebar-orphan-anchor {\n margin-top: 4px;\n margin-bottom: 6px;\n font-size: 11px;\n color: var(--vz-text-faint);\n line-height: 1.5;\n}\n.vz-sidebar-orphan-anchor code {\n background: var(--vz-bg-hover);\n padding: 1px 5px;\n border-radius: 3px;\n font-size: 10.5px;\n}\n\n/* Per-item action buttons (resolve / reopen / wontfix) */\n.vz-sidebar-item-actions {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n}\n.vz-sidebar-item-status {\n background: transparent;\n border: 0;\n padding: 0;\n cursor: pointer;\n color: var(--vz-text-soft);\n text-decoration: underline;\n font-size: 10px;\n font-family: inherit;\n}\n.vz-sidebar-item-status:hover { color: var(--vz-accent); }\n\n/* # element-reference chip (rendered inside comments) */\n.vz-ref {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 1px 8px 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-size: 12px; font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n transition: background 100ms, transform 100ms;\n vertical-align: baseline;\n margin: 0 1px;\n}\n.vz-ref:hover { background: color-mix(in srgb, var(--vz-accent) 30%, var(--vz-bg-hover)); transform: translateY(-1px); }\n.vz-ref::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--vz-accent); flex-shrink: 0; }\n\n/* Inline @mention chip (rendered inside displayed comments) */\n.vz-mention {\n display: inline-flex; align-items: center;\n padding: 1px 8px 1px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-size: 12px; font-weight: 600;\n vertical-align: baseline;\n margin: 0 1px;\n}\n\n/* Mention chip inside the editor (contenteditable=false token) */\n.vz-chip-mention {\n background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));\n color: var(--vz-accent);\n font-weight: 600;\n}\n.vz-chip-mention::before { display: none; }\n\n/* @-mention button in the popover action row */\n.vz-btn-mention { color: var(--vz-accent); }\n\n/* Reply count + reply toggle in a comment's action row */\n.vz-comment-actions { display: inline-flex; align-items: center; gap: 10px; }\n.vz-comment-reply-toggle {\n background: transparent;\n border: 0;\n padding: 0;\n cursor: pointer;\n color: var(--vz-text-soft);\n text-decoration: underline;\n font-size: 10px;\n font-family: inherit;\n}\n.vz-comment-reply-toggle:hover { color: var(--vz-accent); }\n\n/* Inline reply list + form (shown when toggle-reply opens it) */\n.vz-replies {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px dashed var(--vz-border);\n}\n.vz-reply-list { display: flex; flex-direction: column; gap: 8px; }\n.vz-reply {\n padding: 6px 10px;\n border-radius: 8px;\n background: var(--vz-bg-hover);\n}\n.vz-reply-text {\n font-size: 13px;\n color: var(--vz-text);\n line-height: 1.45;\n white-space: pre-wrap;\n}\n.vz-reply-form {\n display: flex;\n gap: 6px;\n margin-top: 8px;\n}\n.vz-reply-input {\n flex: 1;\n resize: vertical;\n min-height: 36px;\n padding: 6px 8px;\n font-family: inherit;\n font-size: 12.5px;\n background: var(--vz-bg);\n color: var(--vz-text);\n border: 1px solid var(--vz-border);\n border-radius: 6px;\n outline: none;\n}\n.vz-reply-input:focus { border-color: var(--vz-accent); }\n.vz-btn-reply { padding: 4px 12px; font-size: 12px; }\n\n/* ─── Attachment upload UI (popover) ─────────────────────────────────── */\n\n.vz-editor-wrap {\n position: relative;\n}\n.vz-attachment-drop-hint {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--vz-accent) 18%, var(--vz-bg));\n border: 2px dashed var(--vz-accent);\n border-radius: 8px;\n color: var(--vz-accent);\n font-size: 13px;\n font-weight: 600;\n pointer-events: none;\n z-index: 1;\n}\n\n/* Pending attachments (uploaded but not yet saved) */\n.vz-attachment-list {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.vz-attachment-list:empty { display: none; }\n.vz-attachment-item {\n position: relative;\n width: 64px;\n height: 64px;\n border-radius: 6px;\n overflow: hidden;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n}\n.vz-attachment-thumb {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n color: var(--vz-text-soft);\n font-size: 11px;\n text-align: center;\n line-height: 64px;\n}\n.vz-attachment-thumb-loading {\n background: var(--vz-bg-hover);\n animation: vz-attachment-pulse 1.4s ease-in-out infinite;\n}\n@keyframes vz-attachment-pulse {\n 0%, 100% { opacity: 0.4; }\n 50% { opacity: 1; }\n}\n.vz-attachment-remove {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: rgba(0, 0, 0, 0.72);\n color: #fafafa;\n border: 0;\n cursor: pointer;\n font-size: 12px;\n line-height: 14px;\n padding: 0;\n}\n.vz-attachment-remove:hover { background: rgba(0, 0, 0, 0.9); }\n\n/* Display-side attachment grid (rendered inside comments) */\n.vz-attachment-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));\n gap: 4px;\n margin-top: 6px;\n}\n.vz-attachment-display {\n display: block;\n border-radius: 4px;\n overflow: hidden;\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n text-decoration: none;\n color: var(--vz-text-soft);\n}\n.vz-attachment-display img {\n display: block;\n width: 100%;\n height: 60px;\n object-fit: cover;\n}\n.vz-attachment-display:hover { border-color: var(--vz-accent); }\n.vz-attachment-file {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n font-size: 11px;\n}\n.vz-attachment-icon { font-size: 14px; }\n.vz-attachment-filename {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n/* Banner shown while user is picking an element to reference */\n.vz-picking-banner {\n position: fixed;\n top: 24px; left: 50%; transform: translateX(-50%);\n background: var(--vz-bg);\n border: 1px solid var(--vz-accent);\n border-radius: 999px;\n padding: 8px 8px 8px 18px;\n pointer-events: auto;\n box-shadow: 0 8px 24px rgba(0,0,0,0.5);\n display: flex; align-items: center; gap: 12px;\n font-size: 13px;\n color: #fafafa;\n z-index: 8;\n}\n.vz-picking-banner-hint {\n color: var(--vz-text-muted); font-size: 11px;\n display: inline-flex; align-items: center; gap: 6px;\n}\n.vz-picking-banner-kbd {\n background: var(--vz-bg-hover);\n border: 1px solid var(--vz-border);\n border-radius: 4px;\n padding: 1px 6px;\n font-family: ui-monospace, monospace;\n font-size: 10px;\n color: #fafafa;\n}\n.vz-picking-banner-cancel {\n padding: 4px 12px;\n font-size: 12px;\n background: var(--vz-bg-hover);\n border-radius: 999px;\n color: var(--vz-text-muted);\n}\n.vz-picking-banner-cancel:hover { color: #fafafa; }\n\n.vz-toast {\n position: fixed;\n bottom: 80px; left: 50%; transform: translateX(-50%);\n background: var(--vz-bg);\n border: 1px solid var(--vz-border);\n border-radius: 6px;\n padding: 10px 14px;\n font-size: 12px;\n color: #fafafa;\n pointer-events: none;\n animation: vz-toast-in 200ms ease-out;\n z-index: 9;\n}\n@keyframes vz-toast-in {\n from { opacity: 0; transform: translateX(-50%) translateY(8px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n`;\n\nexport function injectStyles() {\n if (typeof document === 'undefined') return;\n if (document.getElementById('vizu-styles')) return;\n const style = document.createElement('style');\n style.id = 'vizu-styles';\n style.textContent = STYLES;\n (document.head || document.documentElement).appendChild(style);\n}\n","export interface ParsedShortcut {\n key: string;\n mod: boolean;\n shift: boolean;\n alt: boolean;\n ctrl: boolean;\n}\n\nexport function parseShortcut(spec: string): ParsedShortcut {\n const parts = spec.toLowerCase().split('+').map((s) => s.trim());\n const last = parts.pop() || '';\n return {\n key: last,\n mod: parts.includes('mod') || parts.includes('cmdorctrl'),\n shift: parts.includes('shift'),\n alt: parts.includes('alt') || parts.includes('option'),\n ctrl: parts.includes('ctrl') || parts.includes('control'),\n };\n}\n\nexport function matches(e: KeyboardEvent, p: ParsedShortcut): boolean {\n const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform);\n if (e.key.toLowerCase() !== p.key) return false;\n if (p.mod) {\n const modPressed = isMac ? e.metaKey : e.ctrlKey;\n if (!modPressed) return false;\n } else if (p.ctrl && !e.ctrlKey) {\n return false;\n }\n if (p.shift !== e.shiftKey) return false;\n if (p.alt !== e.altKey) return false;\n return true;\n}\n","import type { VizuEventMap, VizuEventName, VizuEventHandler } from './types';\n\ntype AnyHandler = (payload: unknown) => void;\n\nexport class EventBus {\n private listeners = new Map<string, Set<AnyHandler>>();\n\n on<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(handler as AnyHandler);\n return () => this.off(event, handler);\n }\n\n off<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): void {\n this.listeners.get(event)?.delete(handler as AnyHandler);\n }\n\n emit<E extends VizuEventName>(event: E, payload: VizuEventMap[E]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n // Snapshot to allow handlers to unsubscribe during iteration without skipping siblings\n for (const handler of [...set]) {\n try {\n handler(payload);\n } catch (err) {\n // Listener failures should not break Vizu's own state machine\n // eslint-disable-next-line no-console\n console.error('[vizu] event handler error for', event, err);\n }\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n","import type {\n VizuOptions,\n VizuComment,\n ElementFingerprint,\n StorageAdapter,\n StorageOption,\n VizuUser,\n VizuAction,\n ActionContext,\n VizuEventName,\n VizuEventHandler,\n VizuCloudOptions,\n AuthContext,\n Reply,\n Attachment,\n MatchConfidence,\n MentionableUser,\n} from './types';\nimport { SCHEMA_VERSION } from './types';\nimport {\n LocalStorageAdapter,\n InMemoryStorageAdapter,\n NullStorageAdapter,\n isV1Adapter,\n wrapV1Adapter,\n} from './storage';\nimport { CloudStorageAdapter, type CloudError } from './cloud';\nimport { fingerprint, findByFingerprint, findElementByFingerprint, fingerprintKey, fingerprintLabel } from './fingerprint';\nimport { Highlighter } from './highlighter';\nimport { Popover } from './popover';\nimport { Pill } from './pill';\nimport { Sidebar } from './sidebar';\nimport { injectStyles } from './styles';\nimport { parseShortcut, matches as shortcutMatches } from './shortcut';\nimport { uuid, initials, escapeHtml, migrateComment, migrateComments, needsPersist } from './util';\nimport { EventBus } from './events';\n\nexport type {\n VizuOptions,\n VizuComment,\n ElementFingerprint,\n StorageAdapter,\n StorageOption,\n VizuUser,\n VizuAction,\n ActionContext,\n VizuEventName,\n VizuEventHandler,\n VizuCloudOptions,\n AuthContext,\n Reply,\n Attachment,\n MatchConfidence,\n};\nexport { SCHEMA_VERSION };\nexport { LocalStorageAdapter, InMemoryStorageAdapter, NullStorageAdapter };\nexport { CloudStorageAdapter };\nexport type { CloudError };\nexport { fingerprint, findByFingerprint, findElementByFingerprint, fingerprintKey, fingerprintLabel } from './fingerprint';\n\nconst DEFAULTS = {\n shortcut: 'mod+shift+e',\n namespace: 'default',\n accent: '#FF6647',\n};\n\n/** One rendered marker per element that has any comments anchored to it. */\ninterface MarkerEntry {\n marker: HTMLElement;\n target: Element | null;\n fp: ElementFingerprint;\n /** Comment IDs that touch this element (one entry per anchoring). */\n commentIds: string[];\n}\n/** One always-on outline per unique element that has comments anchored to it. */\ninterface OutlineEntry {\n outline: HTMLElement;\n target: Element | null;\n fp: ElementFingerprint;\n}\n\nfunction resolveStorage(\n storage: StorageOption | undefined,\n cloud: VizuCloudOptions | undefined,\n defaultMode: 'memory' | 'local',\n onAuthChanged?: (info: { userId: string; user: VizuUser | null }) => void,\n): StorageAdapter {\n if (cloud) {\n if (storage && typeof console !== 'undefined') {\n console.warn('[vizu] Both `cloud` and `storage` set; `cloud` wins. Drop `storage` to silence this.');\n }\n return new CloudStorageAdapter({\n workspace: cloud.workspace,\n apiUrl: cloud.apiUrl,\n autoSignIn: cloud.autoSignIn,\n onAuthChanged,\n });\n }\n\n if (!storage) return defaultMode === 'local' ? new LocalStorageAdapter() : new InMemoryStorageAdapter();\n if (storage === 'local') return new LocalStorageAdapter();\n if (storage === 'memory') return new InMemoryStorageAdapter();\n if (storage === 'none') return new NullStorageAdapter();\n // Custom adapter passed in. Detect v1 shape and wrap if needed.\n if (isV1Adapter(storage)) return wrapV1Adapter(storage);\n return storage as StorageAdapter;\n}\n\nexport class Vizu {\n private opts: VizuOptions;\n private parsedShortcut;\n private storage: StorageAdapter;\n private root: HTMLDivElement | null = null;\n private highlighter: Highlighter | null = null;\n private popover: Popover | null = null;\n private pill: Pill | null = null;\n private sidebar: Sidebar | null = null;\n /** Saved-comment markers: keyed by `${commentId}:${anchorIndex}` */\n private markers = new Map<string, MarkerEntry>();\n /** Persistent outlines for commented elements: keyed by fingerprintKey */\n private outlines = new Map<string, OutlineEntry>();\n /** Temporary \"active anchor\" outlines shown while popover is open OR\n * while a multi-select session is pending. Stores the fp so we can\n * reposition without needing to consult popover/pending state. */\n private activeAnchorOutlines = new Map<string, { el: HTMLElement; fp: ElementFingerprint }>();\n /** Transient spotlights rendered on jump (click sidebar item / anchor chip / # ref). */\n private spotlights = new Set<{ el: HTMLElement; target: Element }>();\n private comments: VizuComment[] = [];\n /**\n * Best confidence each comment was resolved at on the most recent\n * marker render. Used by the sidebar to surface orphaned + drifted\n * comments separately, and exposed publicly via {@link getCommentConfidence}.\n */\n private resolvedConfidence = new Map<string, MatchConfidence>();\n /**\n * Comment ids the matcher has already re-fingerprinted in this session.\n * Phase 11.4.4.3 self-healing: when a comment lands as `drifted` and\n * we have a live element to re-fingerprint, we rewrite the stored\n * fingerprint so the next load resolves as exact/likely. Tracked\n * here so the same render loop doesn't re-heal on every redraw.\n * Cleared on `destroy()`; survives `disable()`/`enable()`.\n */\n private selfHealedThisSession = new Set<string>();\n private actions: VizuAction[] = [];\n private user: VizuUser | null = null;\n /**\n * Flipped true once the initial loadComments() at construction has\n * settled (success or failure). The cloud onAuthChanged callback uses\n * this to decide whether a subsequent auth change should trigger a\n * re-load: skipped for the initial fire (the explicit loadComments\n * below handles it), fired for every later token acquisition.\n */\n private hasLoadedComments = false;\n private enabled = false;\n private rafScheduled = false;\n private bus = new EventBus();\n\n /** Picker: next element click is consumed as a reference instead of opening a popover. */\n private picking: { onSelect: (fp: ElementFingerprint) => void; onCancel: () => void } | null = null;\n private pickingBanner: HTMLDivElement | null = null;\n\n /** Multi-select state — pending selection NOT yet attached to a popover. */\n private multiSelectMode = false;\n private pendingFingerprints: ElementFingerprint[] = [];\n private pendingTargets: Element[] = [];\n\n constructor(options: VizuOptions = {}, _defaultStorage: 'memory' | 'local' = 'memory') {\n this.opts = { ...options };\n this.opts.shortcut = options.shortcut ?? DEFAULTS.shortcut;\n this.opts.namespace = options.namespace ?? DEFAULTS.namespace;\n this.opts.accent = options.accent ?? DEFAULTS.accent;\n this.parsedShortcut = parseShortcut(this.opts.shortcut);\n // Initialize this.user BEFORE resolveStorage runs — the\n // CloudStorageAdapter constructor synchronously fires\n // onAuthChanged when it rehydrates a sessionStorage token, which\n // calls this.setUser(). If we assigned this.user afterwards we'd\n // clobber the rehydrated identity with null and the user would\n // appear logged-out despite a perfectly good cached token.\n this.user = options.user ?? null;\n // Pass the auth-changed handler so cloud-mode auth (popup or rehydrated\n // sessionStorage token) refreshes `this.user` automatically — comments\n // saved after sign-in carry the proper display name + avatar.\n //\n // The handler ALSO re-runs loadComments() when auth changes after the\n // initial mount — important because the cloud adapter's load() now\n // returns [] silently when there's no token (to avoid page-load auth\n // ambushes). So when the user signs in via the shortcut later, the\n // formerly-empty comment list gets backfilled here.\n this.storage = resolveStorage(\n options.storage,\n options.cloud,\n _defaultStorage,\n (info) => {\n if (info.user) this.setUser(info.user);\n if (this.hasLoadedComments) {\n void this.loadComments();\n }\n },\n );\n if (options.actions) this.actions = [...options.actions];\n\n if (options.onCommentAdded) this.bus.on('comment:added', (p) => options.onCommentAdded!(p.comment));\n if (options.onCommentRemoved) this.bus.on('comment:removed', (p) => options.onCommentRemoved!(p.id));\n\n if (typeof window !== 'undefined') {\n window.addEventListener('keydown', this.onKeyDown);\n void this.loadComments()\n .catch(() => {\n /* logged in loadComments itself / non-fatal */\n })\n .finally(() => {\n this.hasLoadedComments = true;\n this.subscribeStorage();\n });\n if (options.startEnabled) this.deferred(() => this.enable());\n }\n }\n\n /* ===== Event API ===== */\n on<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): () => void {\n return this.bus.on(event, handler);\n }\n off<E extends VizuEventName>(event: E, handler: VizuEventHandler<E>): void {\n this.bus.off(event, handler);\n }\n\n /* ===== User API ===== */\n setUser(user: VizuUser | null): void {\n this.user = user;\n this.popover?.setUser(user);\n this.pill?.setUser(user);\n this.bus.emit('user:changed', { user });\n // Deferred-mount path: if the user pressed the shortcut to enable\n // Vizu while signed-out, we held off on mount() and just kicked\n // preflightAuth. Now that auth completed, finish what they started\n // — mount the pill + highlighter + markers in one go.\n if (this.enabled && !this.root && this.shouldMount()) {\n this.deferred(() => this.mount());\n }\n }\n getUser(): VizuUser | null { return this.user; }\n\n /**\n * Fetch the list of users who can be @-mentioned on the current\n * workspace. Used by the popover's mention dropdown; hosts can also\n * call this directly to build their own picker. Returns [] for\n * non-cloud adapters (no workspace, no member list to fetch from).\n */\n async searchMentionable(): Promise<MentionableUser[]> {\n if (this.storage instanceof CloudStorageAdapter) {\n return this.storage.searchMentionable();\n }\n return [];\n }\n\n /**\n * Whether the full Vizu UI (pill, highlighter, markers, popover) can\n * be mounted right now. Cloud-mode workspaces hold off until the\n * user is signed in; non-cloud usage (local / memory storage) has no\n * auth concept and mounts immediately. Re-checked from `enable()`\n * and from `setUser()` so that the moment auth resolves, the UI\n * appears without the user needing to press the shortcut again.\n */\n private shouldMount(): boolean {\n if (!(this.storage instanceof CloudStorageAdapter)) return true;\n return this.user !== null;\n }\n\n /**\n * Cloud-mode write gate. Every entry point that creates or modifies a\n * comment first checks that we have a known user. Without one, the\n * write would land as Anonymous (or 401 on the backend) — neither is\n * the right UX. So we re-trigger the sign-in popup and refuse the\n * action. The user signs in, this.user gets set via onAuthChanged,\n * then they can retry the click themselves.\n *\n * Returns true when the caller may proceed.\n */\n private requireUserOrAuth(): boolean {\n if (!(this.storage instanceof CloudStorageAdapter)) return true;\n if (this.user) return true;\n // Sticky denial from /connect: same workspace + same Clerk session\n // will get the same \"no access\" answer, so reopening the popup just\n // shows the user the same error. Refuse the click silently — a page\n // reload (or a sign-out to a different Clerk account) clears it.\n if (this.storage.isAccessDenied()) return false;\n void this.storage.preflightAuth().catch(() => {\n /* canceled / blocked — UI stays unauthed; next attempt retries */\n });\n return false;\n }\n\n /* ===== Lifecycle ===== */\n enable() {\n if (this.enabled) return;\n this.enabled = true;\n this.bus.emit('enabled', {});\n // Mount is gated on shouldMount(): cloud-mode workspaces wait for\n // a signed-in user before drawing any DOM (no pill, no highlighter,\n // no markers — comments are private to the workspace, so showing\n // them to an unauthenticated viewer would leak data). Once auth\n // resolves, setUser() above re-checks and fires mount.\n //\n // No preflightAuth here on purpose. Auto-init at page load\n // (`startEnabled: true`, or `enable()` from a host script) must NOT\n // throw a popup or redirect at the user before they've shown intent.\n // Sign-in fires from two interactive paths instead:\n // 1. onKeyDown — when the user presses the shortcut to toggle\n // Vizu on, the handler explicitly kicks preflightAuth.\n // 2. requireUserOrAuth — when the user clicks an element to leave\n // a comment without being signed in, the gate kicks preflight.\n // Both are user-driven; neither fires on page load.\n if (this.shouldMount()) {\n this.deferred(() => this.mount());\n }\n }\n disable() {\n if (!this.enabled) return;\n this.enabled = false;\n this.unmount();\n this.bus.emit('disabled', {});\n }\n toggle() { if (this.enabled) this.disable(); else this.enable(); }\n isEnabled(): boolean { return this.enabled; }\n destroy() {\n this.disable();\n if (typeof window !== 'undefined') window.removeEventListener('keydown', this.onKeyDown);\n this.unsubscribeStorage?.();\n this.unsubscribeStorage = null;\n this.selfHealedThisSession.clear();\n this.bus.clear();\n }\n\n /* ===== Action API ===== */\n addAction(action: VizuAction): void {\n const existing = this.actions.findIndex((a) => a.id === action.id);\n if (existing >= 0) this.actions[existing] = action;\n else this.actions.push(action);\n this.pill?.setActions(this.actions);\n }\n removeAction(id: string): void {\n this.actions = this.actions.filter((a) => a.id !== id);\n this.pill?.setActions(this.actions);\n }\n getActions(): VizuAction[] { return [...this.actions]; }\n invokeAction(id: string): void {\n const a = this.actions.find((x) => x.id === id);\n if (!a) return;\n const ctx = this.actionContext();\n this.bus.emit('action:invoked', { id });\n void a.onClick(ctx);\n }\n\n /* ===== Comment API ===== */\n getComments(): VizuComment[] { return [...this.comments]; }\n\n async setComments(comments: VizuComment[], opts?: { persist?: boolean }): Promise<void> {\n this.comments = migrateComments(comments);\n if (opts?.persist !== false) await this.storage.setAll(this.opts.namespace!, this.comments);\n this.bus.emit('comments:set', { comments: this.getComments() });\n this.refreshUi();\n }\n\n /**\n * Patch a single comment. Used by status changes (resolve / reopen),\n * future threading mutations, etc. Returns the updated comment or\n * `null` if no comment with that id exists.\n */\n async updateComment(id: string, patch: Partial<VizuComment>): Promise<VizuComment | null> {\n const idx = this.comments.findIndex((c) => c.id === id);\n if (idx === -1) return null;\n // Never let callers overwrite identity or schema-version fields.\n const { id: _i, schemaVersion: _s, ...safe } = patch as Partial<VizuComment>;\n const next = { ...this.comments[idx], ...safe } as VizuComment;\n this.comments[idx] = next;\n const persisted = await this.storage.updateComment(this.opts.namespace!, id, safe);\n this.bus.emit('comment:updated', { comment: persisted ?? next });\n this.refreshUi();\n return next;\n }\n\n /** Auth context surfaced by the active storage adapter, if any (cloud adapters set this). */\n getAuthContext(): AuthContext | null {\n return this.storage.getAuthContext?.() ?? null;\n }\n\n /**\n * Re-fingerprint a comment's anchors against the live DOM and persist.\n * Called from the render loop when a comment lands `drifted` with a\n * matched element — rewriting the fingerprint here makes the next\n * load resolve via a cleaner rung (data-vizu-key / id / class) instead\n * of falling back to fuzzy text.\n *\n * Marks the comment as healed for this session so the same render\n * doesn't kick the same write twice. Failures are logged; the next\n * page load gets another chance.\n */\n private async selfHealComment(\n c: VizuComment,\n byElement: Map<string, { target: Element | null }>,\n ): Promise<void> {\n this.selfHealedThisSession.add(c.id);\n const next: ElementFingerprint[] = [];\n let changed = false;\n for (const fp of c.fingerprints || []) {\n const bucket = byElement.get(fingerprintKey(fp));\n if (bucket?.target) {\n const refreshed = fingerprint(bucket.target);\n next.push(refreshed);\n if (!changed && fingerprintKey(refreshed) !== fingerprintKey(fp)) changed = true;\n } else {\n // Anchor didn't resolve — keep the old fingerprint, no element to learn from.\n next.push(fp);\n }\n }\n // Skip the write if nothing actually changed (e.g. all anchors were\n // already optimal and the drift came from a co-anchored comment).\n if (!changed) return;\n const patch: Partial<VizuComment> = {\n fingerprints: next,\n fingerprintsRefreshedAt: Date.now(),\n };\n // Mirror locally so subsequent renders see the new fingerprints\n // without waiting for the round-trip.\n const idx = this.comments.findIndex((x) => x.id === c.id);\n if (idx !== -1) this.comments[idx] = { ...this.comments[idx], ...patch };\n try {\n await this.storage.updateComment(this.opts.namespace!, c.id, patch);\n } catch (err) {\n if (typeof console !== 'undefined') {\n console.warn('[vizu] self-heal write failed for', c.id, err);\n }\n }\n }\n\n /**\n * Resolution confidence for a comment as of the last marker render.\n * `'orphaned'` means the comment's fingerprints didn't resolve to any\n * live DOM element — the sidebar surfaces it in the orphaned section.\n * Returns `undefined` for unknown comment ids.\n */\n getCommentConfidence(commentId: string): MatchConfidence | undefined {\n return this.resolvedConfidence.get(commentId);\n }\n\n /** Returns the full Map of comment id → confidence. Useful for the dashboard. */\n getResolvedConfidence(): Map<string, MatchConfidence> {\n return new Map(this.resolvedConfidence);\n }\n\n /**\n * True iff the active storage adapter implements `uploadAttachment`.\n * The popover hides its upload UI when this returns false (local /\n * memory modes don't support attachments today).\n */\n canUploadAttachments(): boolean {\n return typeof this.storage.uploadAttachment === 'function';\n }\n\n /**\n * Upload an attachment via the active storage adapter. Throws if the\n * adapter doesn't implement uploads. Caller is responsible for adding\n * the returned Attachment to a comment (popover does this automatically).\n */\n async uploadAttachment(file: File): Promise<Attachment> {\n if (!this.storage.uploadAttachment) {\n throw new Error(\n '[vizu] active storage adapter does not support uploadAttachment; only the CloudStorageAdapter does in v1',\n );\n }\n return this.storage.uploadAttachment(this.opts.namespace!, file);\n }\n\n /**\n * Append a reply to a comment. Returns the parent comment or null if\n * the comment id doesn't exist. Routes through the storage adapter's\n * native `addReply` when available, falling back to an updateComment\n * patch for adapters that don't implement reply ops.\n */\n async addReply(commentId: string, text: string, mentions?: string[]): Promise<VizuComment | null> {\n if (!this.requireUserOrAuth()) return null;\n const idx = this.comments.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const reply: Reply = {\n id: uuid(),\n text: text.trim(),\n createdAt: Date.now(),\n author: this.user ?? undefined,\n authorId: this.user?.id,\n mentions: mentions && mentions.length ? mentions : undefined,\n };\n if (!reply.text) return null;\n // Optimistic local update.\n const updated = {\n ...this.comments[idx],\n replies: [...(this.comments[idx].replies ?? []), reply],\n } as VizuComment;\n this.comments[idx] = updated;\n // Persist via native reply op if available, else fall back to a\n // wholesale replies[] patch through updateComment.\n if (this.storage.addReply) {\n await this.storage.addReply(this.opts.namespace!, commentId, reply);\n } else {\n await this.storage.updateComment(this.opts.namespace!, commentId, { replies: updated.replies });\n }\n this.bus.emit('comment:updated', { comment: updated });\n this.refreshUi();\n if (this.popover?.isOpen()) {\n const anchors = this.popover.getAnchors();\n if (anchors[0]) this.popover.update(this.commentsForElement(anchors[0]));\n }\n return updated;\n }\n\n /**\n * Remove a reply from a comment. Returns the parent comment or null\n * if either id is unknown.\n */\n async removeReply(commentId: string, replyId: string): Promise<VizuComment | null> {\n const idx = this.comments.findIndex((c) => c.id === commentId);\n if (idx === -1) return null;\n const filtered = (this.comments[idx].replies ?? []).filter((r) => r.id !== replyId);\n if (filtered.length === (this.comments[idx].replies ?? []).length) return null;\n const updated = { ...this.comments[idx], replies: filtered } as VizuComment;\n this.comments[idx] = updated;\n if (this.storage.removeReply) {\n await this.storage.removeReply(this.opts.namespace!, commentId, replyId);\n } else {\n await this.storage.updateComment(this.opts.namespace!, commentId, { replies: filtered });\n }\n this.bus.emit('comment:updated', { comment: updated });\n this.refreshUi();\n if (this.popover?.isOpen()) {\n const anchors = this.popover.getAnchors();\n if (anchors[0]) this.popover.update(this.commentsForElement(anchors[0]));\n }\n return updated;\n }\n\n async clearAll(): Promise<void> {\n const previous = this.comments;\n this.comments = [];\n await this.storage.clear(this.opts.namespace!);\n this.bus.emit('comments:cleared', { previous });\n this.refreshUi();\n this.popover?.close();\n this.clearActiveAnchorOutlines();\n this.highlighter?.setPaused(false);\n }\n\n /* ===== Multi-select API ===== */\n /** Toggle multi-select mode. When on, plain clicks accumulate into the selection. */\n toggleMultiSelectMode(): void {\n if (!this.requireUserOrAuth()) return;\n this.multiSelectMode = !this.multiSelectMode;\n if (!this.multiSelectMode) this.clearSelection();\n this.pill?.setMultiSelectState(this.multiSelectMode, this.pendingFingerprints.length);\n }\n /** Discard the in-progress selection AND exit multi-select mode. */\n clearSelection(): void {\n this.pendingFingerprints = [];\n this.pendingTargets = [];\n this.multiSelectMode = false;\n this.clearActiveAnchorOutlines();\n this.pill?.setMultiSelectState(false, 0);\n }\n /** Open the popover anchored to the current pending selection so the user can write the comment. */\n commitSelection(): void {\n if (this.pendingFingerprints.length === 0) return;\n const targets = [...this.pendingTargets];\n const fps = [...this.pendingFingerprints];\n this.pendingFingerprints = [];\n this.pendingTargets = [];\n this.multiSelectMode = false;\n this.pill?.setMultiSelectState(false, 0);\n // Find an existing element to anchor the popover position\n const target = targets.find((t) => !!t) ?? findElementByFingerprint(fps[0]);\n if (!target) return;\n this.openPopoverFor(targets.filter(Boolean) as Element[], fps);\n }\n getSelection(): ElementFingerprint[] { return [...this.pendingFingerprints]; }\n\n /* ===== Internal ===== */\n private async loadComments() {\n const raw = await this.storage.load(this.opts.namespace!);\n this.comments = migrateComments(raw);\n // If any comment in storage was pre-v2 shape, re-persist the migrated\n // array so the next read doesn't pay the migration cost again.\n if (needsPersist(raw)) {\n void this.storage.setAll(this.opts.namespace!, this.comments).catch(() => {\n // Non-fatal; next mutation will fix it.\n });\n }\n this.bus.emit('comments:loaded', { comments: this.getComments() });\n if (this.enabled) this.refreshUi();\n }\n\n /**\n * Subscribe to remote-change notifications from the active storage\n * adapter (cloud adapters only). Local adapters never emit. The\n * subscription is torn down on `destroy()`.\n */\n private unsubscribeStorage: (() => void) | null = null;\n private subscribeStorage() {\n if (this.unsubscribeStorage) return;\n if (!this.storage.subscribe) return;\n this.unsubscribeStorage = this.storage.subscribe(this.opts.namespace!, (e) => {\n if (e.type === 'added') {\n const fresh = migrateComment(e.comment);\n if (!this.comments.some((c) => c.id === fresh.id)) {\n this.comments.push(fresh);\n this.bus.emit('comment:added', { comment: fresh });\n this.refreshUi();\n }\n } else if (e.type === 'updated') {\n const idx = this.comments.findIndex((c) => c.id === e.comment.id);\n const fresh = migrateComment(e.comment);\n if (idx >= 0) {\n this.comments[idx] = fresh;\n this.bus.emit('comment:updated', { comment: fresh });\n this.refreshUi();\n }\n } else if (e.type === 'removed') {\n const idx = this.comments.findIndex((c) => c.id === e.id);\n if (idx >= 0) {\n const [removed] = this.comments.splice(idx, 1);\n this.bus.emit('comment:removed', { id: e.id, comment: removed });\n this.refreshUi();\n }\n } else if (e.type === 'cleared') {\n const previous = this.comments;\n this.comments = [];\n this.bus.emit('comments:cleared', { previous });\n this.refreshUi();\n }\n });\n }\n\n private deferred(fn: () => void) {\n if (typeof document === 'undefined') return;\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn, { once: true });\n else fn();\n }\n\n private onKeyDown = (e: KeyboardEvent) => {\n if (this.picking && e.key === 'Escape') {\n e.preventDefault();\n this.cancelPicking();\n return;\n }\n // Esc clears in-progress selection\n if (this.pendingFingerprints.length > 0 && e.key === 'Escape' && !this.popover?.isOpen()) {\n e.preventDefault();\n this.clearSelection();\n return;\n }\n if (shortcutMatches(e, this.parsedShortcut)) {\n e.preventDefault();\n const wasEnabled = this.enabled;\n this.toggle();\n // Cloud-mode: shortcut-triggered enable is an explicit user\n // intent, so kick the sign-in popup (or redirect fallback) right\n // away. Auto-init via startEnabled goes through enable() too but\n // skips this branch — it never sees the shortcut.\n if (!wasEnabled && this.enabled && this.storage instanceof CloudStorageAdapter) {\n void this.storage.preflightAuth().catch((err: { code?: string }) => {\n if (err?.code === 'auth_canceled' || err?.code === 'popup_blocked') return;\n if (typeof console !== 'undefined') {\n console.warn('[vizu] preflight auth failed:', err);\n }\n });\n }\n }\n };\n\n private mount() {\n if (this.root) return;\n injectStyles();\n this.root = document.createElement('div');\n this.root.setAttribute('data-vizu-root', '');\n this.root.style.setProperty('--vz-accent', this.opts.accent!);\n document.body.appendChild(this.root);\n\n this.popover = new Popover(this.root, {\n onAdd: (text, fps, refs, mentions, atts) =>\n void this.addCommentFromPopover(text, fps, refs, mentions, atts),\n onUploadAttachment: (file) => this.uploadAttachment(file),\n canUploadAttachments: () => this.canUploadAttachments(),\n onDelete: (id) => void this.removeComment(id),\n onAddReply: (commentId, text) => void this.addReply(commentId, text),\n onDeleteReply: (commentId, replyId) => void this.removeReply(commentId, replyId),\n onClose: () => this.closePopover(),\n onStartReferencePick: (onPicked, onCancel) => this.startPicking(onPicked, onCancel),\n onJumpToReference: (commentId, refId) => this.jumpToReference(commentId, refId),\n onAnchorsChanged: (fps) => this.syncActiveAnchorOutlines(fps),\n onMentionSearch: () => this.searchMentionable(),\n });\n this.popover.setUser(this.user);\n\n this.sidebar = new Sidebar(this.root, {\n onJumpTo: (fp) => this.jumpToFingerprint(fp),\n onJumpToReference: (commentId, refId) => this.jumpToReference(commentId, refId),\n onDelete: (id) => void this.removeComment(id),\n onUpdateStatus: (id, status) => void this.updateComment(id, { status }),\n onClose: () => this.closeSidebar(),\n });\n\n this.pill = new Pill(this.root, {\n onToggleSidebar: () => this.toggleSidebar(),\n onDisable: () => this.disable(),\n onAction: (id) => this.invokeAction(id),\n onToggleMultiSelect: () => this.toggleMultiSelectMode(),\n onCommitSelection: () => this.commitSelection(),\n onClearSelection: () => this.clearSelection(),\n });\n this.pill.setActions(this.actions);\n this.pill.setUser(this.user);\n this.pill.setComments(this.comments);\n this.pill.setMultiSelectState(this.multiSelectMode, this.pendingFingerprints.length);\n // Unconditional show — mount only runs when shouldMount() is true,\n // which in cloud mode means we have a user. No pill-visibility\n // toggle needed elsewhere.\n this.pill.show();\n\n this.highlighter = new Highlighter(\n this.root,\n this.opts.ignoreSelectors ?? [],\n (el, e) => this.onElementClick(el, e),\n );\n this.highlighter.start();\n\n this.renderAllMarkers();\n window.addEventListener('scroll', this.scheduleReposition, true);\n window.addEventListener('resize', this.scheduleReposition);\n this.bus.emit('mounted', {});\n }\n\n private unmount() {\n window.removeEventListener('scroll', this.scheduleReposition, true);\n window.removeEventListener('resize', this.scheduleReposition);\n this.highlighter?.destroy();\n this.popover?.destroy();\n this.pill?.destroy();\n this.sidebar?.destroy();\n this.root?.remove();\n this.root = null;\n this.highlighter = null;\n this.popover = null;\n this.pill = null;\n this.sidebar = null;\n this.markers.clear();\n this.outlines.clear();\n this.activeAnchorOutlines.clear();\n for (const sp of this.spotlights) sp.el.remove();\n this.spotlights.clear();\n this.pendingFingerprints = [];\n this.pendingTargets = [];\n this.bus.emit('unmounted', {});\n }\n\n private onElementClick(el: Element, e: MouseEvent) {\n // Picker mode steals the next click for a # reference\n if (this.picking) {\n const fp = fingerprint(el);\n const p = this.picking;\n this.picking = null;\n this.hidePickingBanner();\n // Restore the popover's paused state once picking ends\n this.highlighter?.setPaused(this.popover?.isOpen() ?? false);\n p.onSelect(fp);\n return;\n }\n\n // Cloud-mode write gate: signed-out users can't open the popover —\n // we reopen the sign-in popup and swallow the click. Existing\n // comment markers stay visible (read access); the user signs in,\n // then clicks again to comment.\n if (!this.requireUserOrAuth()) return;\n\n const fp = fingerprint(el);\n\n // FLOW A: Popover is already open AND user shift-clicks → add another anchor in-flow\n if (this.popover?.isOpen() && e.shiftKey) {\n this.popover.addAnchor(fp, el);\n return;\n }\n\n // FLOW B: multi-select mode OR shift-click without an open popover → accumulate selection\n if (!this.popover?.isOpen() && (this.multiSelectMode || e.shiftKey)) {\n this.addToSelection(fp, el);\n return;\n }\n\n // Default: open popover anchored to this single element\n this.bus.emit('element:selected', { target: el, fingerprint: fp });\n const elComments = this.commentsForElement(fp);\n this.openPopoverFor([el], [fp], elComments);\n }\n\n private openPopoverFor(targets: Element[], fps: ElementFingerprint[], existingComments?: VizuComment[]) {\n if (!this.popover) return;\n // When opening, find existing comments on the primary fingerprint\n const comments = existingComments ?? this.commentsForElement(fps[0]);\n this.popover.open(targets, fps, comments);\n this.syncActiveAnchorOutlines(fps);\n // Lock hover-highlighting while popover is open — no more move tracking unless picking\n this.highlighter?.setPaused(true);\n }\n\n private closePopover() {\n this.popover?.close();\n this.clearActiveAnchorOutlines();\n this.highlighter?.setPaused(false);\n this.bus.emit('element:deselected', {});\n }\n\n private addToSelection(fp: ElementFingerprint, el: Element) {\n const key = fingerprintKey(fp);\n if (this.pendingFingerprints.some((x) => fingerprintKey(x) === key)) {\n // Toggle off: shift-clicking an already-selected element removes it\n this.pendingFingerprints = this.pendingFingerprints.filter((x) => fingerprintKey(x) !== key);\n this.pendingTargets = this.pendingTargets.filter((t) => t !== el);\n } else {\n this.pendingFingerprints.push(fp);\n this.pendingTargets.push(el);\n }\n this.syncActiveAnchorOutlines(this.pendingFingerprints);\n this.pill?.setMultiSelectState(true, this.pendingFingerprints.length);\n // Implicit mode: any selection makes the pill reflect multi-select\n if (this.pendingFingerprints.length > 0) this.multiSelectMode = true;\n else this.multiSelectMode = false;\n }\n\n private commentsForElement(fp: ElementFingerprint): VizuComment[] {\n const key = fingerprintKey(fp);\n return this.comments.filter((c) =>\n (c.fingerprints || []).some((f) => fingerprintKey(f) === key),\n );\n }\n\n private async addCommentFromPopover(\n text: string,\n fps: ElementFingerprint[],\n references?: Record<string, ElementFingerprint>,\n mentions?: string[],\n attachments?: Attachment[],\n ) {\n if (!this.requireUserOrAuth()) return;\n if (fps.length === 0) return;\n const pageUrl = typeof location !== 'undefined' ? location.href : undefined;\n const c: VizuComment = {\n id: uuid(),\n schemaVersion: SCHEMA_VERSION,\n fingerprints: fps,\n text,\n createdAt: Date.now(),\n pageVersion: this.opts.pageVersion,\n pageUrl,\n author: this.user ?? undefined,\n authorId: this.user?.id,\n references: references && Object.keys(references).length ? references : undefined,\n status: 'open',\n replies: [],\n attachments: attachments ?? [],\n mentions: mentions ?? [],\n };\n this.comments.push(c);\n await this.storage.addComment(this.opts.namespace!, c);\n this.bus.emit('comment:added', { comment: c });\n // Refresh popover so the new comment appears in its list\n this.popover?.update(this.commentsForElement(fps[0]));\n this.refreshUi();\n }\n\n private async removeComment(id: string) {\n const comment = this.comments.find((c) => c.id === id);\n if (!comment) return;\n this.comments = this.comments.filter((c) => c.id !== id);\n await this.storage.removeComment(this.opts.namespace!, id);\n this.bus.emit('comment:removed', { id, comment });\n if (this.popover?.isOpen()) {\n const anchors = this.popover.getAnchors();\n if (anchors[0]) this.popover.update(this.commentsForElement(anchors[0]));\n }\n this.refreshUi();\n }\n\n private refreshUi() {\n this.pill?.setComments(this.comments);\n // Marker render populates resolvedConfidence; sidebar reads it.\n this.renderAllMarkers();\n this.sidebar?.update(this.comments, this.resolvedConfidence);\n }\n\n /* ===== Sidebar ===== */\n private toggleSidebar() {\n if (!this.sidebar) return;\n // Re-resolve fingerprints against the current DOM before opening so\n // the orphan / drifted state reflects the latest page (host apps\n // mutate the DOM all the time; confidence captured on the previous\n // render may be stale).\n if (!this.sidebar.isOpen()) this.renderAllMarkers();\n this.sidebar.toggle(this.comments, this.resolvedConfidence);\n const open = this.sidebar.isOpen();\n this.pill?.setSidebarOpen(open);\n this.bus.emit(open ? 'sidebar:opened' : 'sidebar:closed', {});\n }\n private closeSidebar() {\n this.sidebar?.close();\n this.pill?.setSidebarOpen(false);\n this.bus.emit('sidebar:closed', {});\n }\n\n /* ===== Picking ===== */\n startPicking(onSelect: (fp: ElementFingerprint) => void, onCancel: () => void): void {\n if (this.picking) this.picking.onCancel();\n this.picking = { onSelect, onCancel };\n // Resume hover-tracking during picking even if popover is open\n this.highlighter?.setPaused(false);\n this.showPickingBanner();\n }\n cancelPicking(): void {\n if (!this.picking) return;\n const p = this.picking;\n this.picking = null;\n this.hidePickingBanner();\n this.highlighter?.setPaused(this.popover?.isOpen() ?? false);\n p.onCancel();\n }\n private showPickingBanner() {\n if (!this.root) return;\n this.pickingBanner = document.createElement('div');\n this.pickingBanner.className = 'vz-picking-banner';\n this.pickingBanner.innerHTML = `\n <span>Click any element to reference</span>\n <span class=\"vz-picking-banner-hint\">or press <span class=\"vz-picking-banner-kbd\">Esc</span></span>\n <button class=\"vz-picking-banner-cancel\" data-vz=\"cancel-pick\">Cancel</button>\n `;\n this.pickingBanner.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('[data-vz=\"cancel-pick\"]')) this.cancelPicking();\n });\n this.root.appendChild(this.pickingBanner);\n }\n private hidePickingBanner() {\n this.pickingBanner?.remove();\n this.pickingBanner = null;\n }\n private jumpToReference(commentId: string, refId: string) {\n const c = this.comments.find((x) => x.id === commentId);\n const fp = c?.references?.[refId];\n if (fp) this.jumpToFingerprint(fp);\n }\n jumpToFingerprint(fp: ElementFingerprint): void {\n const target = findElementByFingerprint(fp);\n if (!target) {\n this.pill?.toast('Element not found on this page');\n return;\n }\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n\n // Always render a spotlight at the destination — works even for elements\n // that have no saved comments (e.g., elements linked via # reference).\n if (this.root) {\n const spotlight = document.createElement('div');\n spotlight.className = 'vz-jump-spotlight';\n this.root.appendChild(spotlight);\n this.positionOutline(spotlight, target);\n const entry = { el: spotlight, target };\n this.spotlights.add(entry);\n setTimeout(() => {\n spotlight.remove();\n this.spotlights.delete(entry);\n }, 1800);\n }\n\n // Additionally pulse the saved-comment outline if the element has one\n const key = fingerprintKey(fp);\n const outline = this.outlines.get(key)?.outline;\n if (outline) {\n // Wait for scroll to settle so the pulse is visible\n setTimeout(() => {\n outline.classList.remove('is-pulsing');\n void outline.offsetWidth;\n outline.classList.add('is-pulsing');\n setTimeout(() => outline.classList.remove('is-pulsing'), 1500);\n }, 250);\n }\n }\n\n /* ===== Marker / outline rendering ===== */\n private renderAllMarkers() {\n if (!this.root) return;\n\n // Tear down existing\n for (const { marker } of this.markers.values()) marker.remove();\n for (const { outline } of this.outlines.values()) outline.remove();\n this.markers.clear();\n this.outlines.clear();\n\n // Build: per element fpKey → all comment IDs that anchor to it, plus\n // the resolution confidence for that anchor (best wins per element).\n interface PerElement {\n fp: ElementFingerprint;\n target: Element | null;\n confidence: MatchConfidence;\n commentIds: string[];\n }\n const byElement = new Map<string, PerElement>();\n // Per-comment best confidence across all its anchors.\n const perComment = new Map<string, MatchConfidence>();\n const rankConf = (c: MatchConfidence): number =>\n c === 'exact' ? 3 : c === 'likely' ? 2 : c === 'drifted' ? 1 : 0;\n const bestConf = (a: MatchConfidence | undefined, b: MatchConfidence): MatchConfidence =>\n a === undefined ? b : rankConf(b) > rankConf(a) ? b : a;\n\n for (const c of this.comments) {\n for (const fp of c.fingerprints || []) {\n const key = fingerprintKey(fp);\n let bucket = byElement.get(key);\n if (!bucket) {\n const match = findByFingerprint(fp);\n bucket = {\n fp,\n target: match.element,\n confidence: match.confidence,\n commentIds: [],\n };\n byElement.set(key, bucket);\n }\n bucket.commentIds.push(c.id);\n perComment.set(c.id, bestConf(perComment.get(c.id), bucket.confidence));\n }\n }\n\n // Commit per-comment confidence + emit a one-shot event each render.\n this.resolvedConfidence.clear();\n for (const c of this.comments) {\n const conf = perComment.get(c.id) ?? 'orphaned';\n this.resolvedConfidence.set(c.id, conf);\n // Only emit once per comment per render.\n const firstFp = (c.fingerprints || [])[0];\n const elForEvent = firstFp ? byElement.get(fingerprintKey(firstFp))?.target ?? null : null;\n this.bus.emit('anchor:resolved', { comment: c, confidence: conf, element: elForEvent });\n }\n\n // Self-heal: any comment that landed `drifted` with live elements\n // gets its fingerprints rewritten in place. The next load resolves\n // those anchors via the cleaner rungs (data-vizu-key / id / class\n // signature / selector) instead of the fuzzy fallback. Fire-and-forget\n // — failures don't block the render and the next page load retries.\n for (const c of this.comments) {\n if (this.selfHealedThisSession.has(c.id)) continue;\n if (this.resolvedConfidence.get(c.id) !== 'drifted') continue;\n void this.selfHealComment(c, byElement);\n }\n\n for (const [key, info] of byElement) {\n // Skip rendering marker + outline for orphaned anchors — there's no\n // element to position against. Those comments still appear in the\n // sidebar's orphan section.\n if (info.confidence === 'orphaned' || !info.target) continue;\n\n // One outline per element\n const outlineEl = document.createElement('div');\n outlineEl.className = 'vz-comment-outline';\n if (info.confidence === 'drifted') outlineEl.classList.add('vz-comment-outline-drifted');\n this.root.appendChild(outlineEl);\n this.outlines.set(key, { outline: outlineEl, target: info.target, fp: info.fp });\n\n // One marker per element. Label rule:\n // - 1 comment → empty flag (just the Vizu-colored pin shape)\n // - N comments → \"N\" (count)\n // Multi-anchor relationships (one comment touching multiple elements) are\n // surfaced via hover-pulse on siblings + the sidebar grouping.\n const marker = document.createElement('button');\n marker.className = 'vz-marker';\n if (info.confidence === 'drifted') marker.classList.add('vz-marker-drifted');\n marker.setAttribute('data-element-key', key);\n marker.setAttribute('data-confidence', info.confidence);\n const total = info.commentIds.length;\n if (total > 1) {\n marker.textContent = String(total);\n marker.title = total + ' comments';\n } else {\n marker.textContent = '';\n const c = this.comments.find((c) => c.id === info.commentIds[0]);\n marker.title = c?.author ? 'Comment by ' + c.author.name : 'Comment';\n }\n marker.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (info.target) {\n const elComments = this.commentsForElement(info.fp);\n this.openPopoverFor([info.target], [info.fp], elComments);\n }\n });\n marker.addEventListener('mouseenter', () => this.pulseRelatedElements(info.commentIds, key));\n this.root.appendChild(marker);\n\n this.markers.set(key, { marker, target: info.target, fp: info.fp, commentIds: info.commentIds });\n\n if (info.target) {\n this.positionMarker(marker, info.target);\n this.positionOutline(outlineEl, info.target);\n } else {\n marker.style.display = 'none';\n outlineEl.style.display = 'none';\n }\n }\n }\n\n /** Pulse outlines of OTHER elements that share a multi-anchor comment with this one. */\n private pulseRelatedElements(commentIds: string[], selfKey: string) {\n const relatedKeys = new Set<string>();\n for (const cid of commentIds) {\n const c = this.comments.find((c) => c.id === cid);\n if (!c) continue;\n const fps = c.fingerprints || [];\n if (fps.length <= 1) continue; // single-anchor comment has no siblings\n for (const fp of fps) {\n const k = fingerprintKey(fp);\n if (k !== selfKey) relatedKeys.add(k);\n }\n }\n for (const key of relatedKeys) {\n const outline = this.outlines.get(key)?.outline;\n if (!outline) continue;\n outline.classList.remove('is-pulsing');\n void outline.offsetWidth;\n outline.classList.add('is-pulsing');\n setTimeout(() => outline.classList.remove('is-pulsing'), 900);\n }\n }\n\n private positionOutline(outline: HTMLElement, target: Element) {\n const rect = target.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) { outline.style.display = 'none'; return; }\n outline.style.display = '';\n outline.style.left = rect.left + 'px';\n outline.style.top = rect.top + 'px';\n outline.style.width = rect.width + 'px';\n outline.style.height = rect.height + 'px';\n }\n\n private positionMarker(marker: HTMLElement, target: Element) {\n const rect = target.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) { marker.style.display = 'none'; return; }\n marker.style.display = '';\n marker.style.left = rect.right + 'px';\n marker.style.top = rect.top + 'px';\n }\n\n /* ===== Active anchor outlines (transient, while popover open or multi-select pending) ===== */\n private syncActiveAnchorOutlines(fps: ElementFingerprint[]) {\n if (!this.root) return;\n const wanted = new Set(fps.map((fp) => fingerprintKey(fp)));\n // Remove outlines that are no longer wanted\n for (const [key, info] of this.activeAnchorOutlines) {\n if (!wanted.has(key)) {\n info.el.remove();\n this.activeAnchorOutlines.delete(key);\n }\n }\n // Add missing\n for (const fp of fps) {\n const key = fingerprintKey(fp);\n if (this.activeAnchorOutlines.has(key)) continue;\n const outline = document.createElement('div');\n outline.className = 'vz-comment-outline is-selected-pending';\n this.root.appendChild(outline);\n const target = findElementByFingerprint(fp);\n if (target) this.positionOutline(outline, target);\n else outline.style.display = 'none';\n this.activeAnchorOutlines.set(key, { el: outline, fp });\n }\n }\n private clearActiveAnchorOutlines() {\n for (const info of this.activeAnchorOutlines.values()) info.el.remove();\n this.activeAnchorOutlines.clear();\n }\n\n /* ===== Reposition on scroll/resize ===== */\n private scheduleReposition = () => {\n if (this.rafScheduled) return;\n this.rafScheduled = true;\n requestAnimationFrame(() => {\n this.rafScheduled = false;\n // Outlines + markers (one per element each)\n for (const [, info] of this.outlines) {\n if (info.target) this.positionOutline(info.outline, info.target);\n }\n for (const [, entry] of this.markers) {\n if (entry.target) this.positionMarker(entry.marker, entry.target);\n }\n // Active anchor outlines — reposition every one using its stored fp.\n // (Critical for pending multi-select selections to track elements on scroll.)\n for (const [, info] of this.activeAnchorOutlines) {\n const target = findElementByFingerprint(info.fp);\n if (target) this.positionOutline(info.el, target);\n }\n // Jump spotlights (transient)\n for (const sp of this.spotlights) {\n if (sp.target) this.positionOutline(sp.el, sp.target);\n }\n // Popover stays anchored to its primary target\n this.popover?.reposition();\n });\n };\n\n /* ===== Helpers ===== */\n private actionContext(): ActionContext {\n return {\n comments: this.getComments(),\n pageHtml: this.snapshotHtml(),\n pageUrl: typeof location !== 'undefined' ? location.href : undefined,\n pageVersion: this.opts.pageVersion,\n user: this.user ?? undefined,\n timestamp: Date.now(),\n copyToClipboard: (text) => this.copyToClipboard(text),\n toast: (msg) => this.pill?.toast(msg),\n };\n }\n\n snapshotHtml(): string {\n const clone = document.documentElement.cloneNode(true) as HTMLElement;\n for (const node of clone.querySelectorAll('[data-vizu-root], #vizu-styles')) node.remove();\n const attrs = document.documentElement\n .getAttributeNames()\n .map((n) => ` ${n}=\"${(document.documentElement.getAttribute(n) || '').replace(/\"/g, '&quot;')}\"`)\n .join('');\n return '<!DOCTYPE html>\\n<html' + attrs + '>\\n' + clone.innerHTML + '\\n</html>';\n }\n\n private copyToClipboard(text: string) {\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).catch(() => fallbackCopy(text));\n } else fallbackCopy(text);\n }\n}\n\nfunction fallbackCopy(text: string) {\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n try { document.execCommand('copy'); } catch {}\n ta.remove();\n}\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["init","key","key","refId","fp","key","c"]}