@unhingged/vizu-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +428 -0
- package/dist/auto.cjs +5005 -0
- package/dist/auto.cjs.map +1 -0
- package/dist/auto.js +3626 -0
- package/dist/auto.js.map +1 -0
- package/dist/chunk-OMIFOSQ2.js +295 -0
- package/dist/chunk-OMIFOSQ2.js.map +1 -0
- package/dist/index.cjs +4989 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +736 -0
- package/dist/index.d.ts +736 -0
- package/dist/index.js +3600 -0
- package/dist/index.js.map +1 -0
- package/dist/live-collab-IRUNFAPE.js +1033 -0
- package/dist/live-collab-IRUNFAPE.js.map +1 -0
- package/dist/vizu.min.js +1281 -0
- package/dist/vizu.min.js.map +1 -0
- package/package.json +60 -0
package/dist/auto.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/util.ts","../src/storage.ts","../src/cloud.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","../src/auto.ts"],"sourcesContent":["/**\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/** 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\n/**\n * Live collaboration config. Both keys are needed — the public key\n * identifies the Liveblocks project to the browser SDK; the auth\n * endpoint mints scoped tokens that gate room access against the\n * workspace's role + plan.\n *\n * Wired in Slice 1 of plans/live-collab.md; cursor + follow rendering\n * arrives in Slice 2+.\n */\nexport interface VizuLiveblocksOptions {\n /** Liveblocks public key from the dashboard (pk_dev_… / pk_prod_…). */\n publicKey: string;\n /**\n * URL of the auth endpoint that mints room tokens. Same-origin path\n * (`/api/liveblocks-auth`) for dashboard usage; full URL for hosts\n * embedding @vizu/core cross-origin.\n */\n authEndpoint: string;\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 /**\n * Optional live collaboration config. When set AND the workspace's\n * plan carries the `live_collab` feature, @vizu/core opens a\n * Liveblocks room scoped to (workspaceSlug, pageUrl) and renders\n * presence + cursors + follow mode. Without this, all live-collab\n * machinery stays inert — standalone usage is unaffected.\n *\n * See plans/live-collab.md.\n */\n liveblocks?: VizuLiveblocksOptions;\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): string {\n return s.replace(/[&<>\"']/g, (ch) => ({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[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): string {\n return 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, className = 'vz-comment-author-avatar'): string {\n if (!user) return `<span class=\"${className}\">·</span>`;\n if (user.avatarUrl) {\n return `<span class=\"${className}\"><img src=\"${escapeHtml(user.avatarUrl)}\" alt=\"${escapeHtml(user.name)}\" /></span>`;\n }\n return `<span class=\"${className}\">${escapeHtml(initials(user.name) || '?')}</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 AuthContext,\n Reply,\n Attachment,\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\ninterface StoredToken {\n token: string;\n expiresAt: string; // ISO\n userId: string;\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 /** 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\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 // Try to hydrate from sessionStorage.\n this.cachedToken = this.readStoredToken();\n }\n\n /* ─── StorageAdapter v2 ───────────────────────────────────────────── */\n\n async load(_namespace: string): Promise<VizuComment[]> {\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 return `vizu:cloud-token:${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 return fresh;\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 return t;\n })\n .finally(() => {\n this.pendingAuth = null;\n });\n }\n return this.pendingAuth;\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 return reject(\n makeAuthError(\n 'popup_blocked',\n 'Sign-in popup was blocked by the browser. Allow popups for this site and try again.',\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 | { type?: string; version?: number; token?: string; expiresAt?: string; userId?: string; workspace?: string }\n | undefined;\n if (!data || 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 resolve({ token: data.token, expiresAt: data.expiresAt, userId: data.userId });\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 | '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 { 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 /**\n * Fires whenever the currently-hovered element changes (including to\n * null). Used by live-collab's selection-sync to broadcast the local\n * user's hover target so other reviewers see a colored ghost outline\n * on the same DOM element. See plans/live-collab.md → Slice 5.\n */\n private onCurrentChange: ((el: Element | null) => void) | null;\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 onCurrentChange?: (el: Element | null) => void,\n ) {\n this.root = root;\n this.extraIgnore = extraIgnore;\n this.onClick = onClick;\n this.onCurrentChange = onCurrentChange ?? null;\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 this.onCurrentChange?.(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 } from './types';\nimport { escapeHtml, formatTime, avatarHtml, refId as makeRefId, renderCommentText, extractMentions, renderAttachmentsHtml } 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)}</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\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\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.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 ? ' · “' + escapeHtml(textPreview) + (text.length > 60 ? '…' : '') + '”' : ''}</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)}</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 ${escapeHtml(this.currentUser.name)}\">@ 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.insertMentionChip(this.currentUser);\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 * Insert a mention chip for the given user into the editor at the\n * current caret position. Owner-only ACL in v1: the only callable\n * \"mentionable\" user is the current Vizu user (= workspace owner).\n * Future versions can expand this with an autocomplete dropdown.\n */\n private insertMentionChip(user: VizuUser | 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.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\">“${escapeHtml(textPreview)}${g.fp.textSnippet.length > 60 ? '…' : ''}”</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'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 ? ` “${escapeHtml(snippet)}${(primary?.textSnippet?.length ?? 0) > 60 ? '…' : ''}”` : ''}\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/* 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} 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): 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 });\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 /** Live-collab module — non-null only when `opts.liveblocks` and `opts.cloud` are both set AND the plan carries `live_collab`. Stays null otherwise. */\n private liveCollab: import('./live-collab').LiveCollab | null = null;\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 this.storage = resolveStorage(options.storage, options.cloud, _defaultStorage);\n this.user = options.user ?? null;\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 // Live collaboration: opt-in via opts.liveblocks AND requires a cloud\n // workspace (we need a workspace slug to scope the Liveblocks room).\n // Plan-gating happens server-side at /api/liveblocks-auth — if the\n // workspace doesn't have `live_collab`, the auth call fails 402 and\n // the module cleans up silently. See plans/live-collab.md.\n if (options.liveblocks && options.cloud) {\n const live = options.liveblocks;\n const cloud = options.cloud;\n void import('./live-collab').then(({ LiveCollab }) => {\n this.liveCollab = new LiveCollab({\n publicKey: live.publicKey,\n authEndpoint: live.authEndpoint,\n workspaceSlug: cloud.workspace,\n });\n void this.liveCollab.start();\n });\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('keydown', this.onKeyDown);\n void this.loadComments().then(() => this.subscribeStorage());\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 }\n getUser(): VizuUser | null { return this.user; }\n\n /* ===== Lifecycle ===== */\n enable() {\n if (this.enabled) return;\n this.enabled = true;\n this.bus.emit('enabled', {});\n this.deferred(() => this.mount());\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.liveCollab?.destroy();\n this.liveCollab = null;\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 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 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 this.toggle();\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 });\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 this.pill.show();\n\n this.highlighter = new Highlighter(\n this.root,\n this.opts.ignoreSelectors ?? [],\n (el, e) => this.onElementClick(el, e),\n (el) => {\n // Live-collab selection sync (Slice 5 of plans/live-collab.md):\n // fingerprint the currently-hovered element and broadcast it so\n // other reviewers see a ghost outline on the same DOM node.\n // No-op when liveCollab isn't initialized.\n if (!this.liveCollab) return;\n this.liveCollab.setLocalSelection(el ? fingerprint(el) : null);\n },\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 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 (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, '"')}\"`)\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","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"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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"]}
|