apostil 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/adapters/rest.ts","../src/utils.ts","../src/debug.ts","../src/hooks/use-comments.ts","../src/hooks/use-comment-mode.ts","../src/components/comment-overlay.tsx","../src/components/comment-pin.tsx","../src/components/comment-thread.tsx","../src/icons.tsx","../src/components/comment-composer.tsx","../src/components/user-prompt.tsx","../src/components/comment-toggle.tsx","../src/components/comment-sidebar.tsx"],"sourcesContent":["// Apostil — Pin-and-comment feedback overlay\n// https://github.com/batzorigco/apostil\n\n// Provider & hooks\nexport { ApostilProvider, useApostil } from \"./context\";\nexport { useComments } from \"./hooks/use-comments\";\nexport { useCommentMode } from \"./hooks/use-comment-mode\";\n\n// Components\nexport { CommentOverlay } from \"./components/comment-overlay\";\nexport { CommentToggle } from \"./components/comment-toggle\";\nexport { CommentSidebar } from \"./components/comment-sidebar\";\n\n// Debug\nexport { debug } from \"./debug\";\n\n// Types\nexport type {\n ApostilUser,\n ApostilComment,\n ApostilThread,\n ApostilStorage,\n} from \"./types\";\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useRef,\n type ReactNode,\n} from \"react\";\nimport type { ApostilThread, ApostilUser, ApostilStorage } from \"./types\";\nimport { createRestAdapter } from \"./adapters/rest\";\nimport { generateId, loadUser, saveUser, getRandomColor } from \"./utils\";\nimport { debug } from \"./debug\";\n\n// Stable default adapter (created once, not per render)\nconst defaultAdapter = createRestAdapter(\"/api/apostil\");\n\ntype ApostilContextValue = {\n threads: ApostilThread[];\n user: ApostilUser | null;\n commentMode: boolean;\n activeThreadId: string | null;\n sidebarOpen: boolean;\n brandColor: string;\n setCommentMode: (on: boolean) => void;\n setActiveThreadId: (id: string | null) => void;\n setSidebarOpen: (open: boolean) => void;\n addThread: (pinX: number, pinY: number, body: string, targetId?: string, targetLabel?: string) => void;\n addReply: (threadId: string, body: string) => void;\n resolveThread: (threadId: string) => void;\n deleteThread: (threadId: string) => void;\n setUser: (name: string) => void;\n unresolvedCount: number;\n};\n\nconst ApostilContext = createContext<ApostilContextValue | null>(null);\n\nexport function ApostilProvider({\n pageId,\n storage,\n brandColor = \"#171717\",\n children,\n}: {\n pageId: string;\n storage?: ApostilStorage;\n brandColor?: string;\n children: ReactNode;\n}) {\n const adapter = storage ?? defaultAdapter;\n const [threads, setThreads] = useState<ApostilThread[]>([]);\n const [user, setUserState] = useState<ApostilUser | null>(null);\n const [commentMode, setCommentMode] = useState(false);\n const [activeThreadId, setActiveThreadId] = useState<string | null>(null);\n const [sidebarOpen, setSidebarOpen] = useState(false);\n const [loaded, setLoaded] = useState(false);\n\n useEffect(() => {\n const saved = loadUser();\n if (saved) setUserState(saved);\n }, []);\n\n // Track current pageId to avoid saving stale data during transitions\n const pageIdRef = useRef(pageId);\n\n useEffect(() => {\n pageIdRef.current = pageId;\n setLoaded(false);\n hadThreadsRef.current = false;\n debug.log(\"loading threads for pageId:\", pageId);\n adapter.load(pageId).then((t) => {\n // Only apply if pageId hasn't changed during the fetch\n if (pageIdRef.current === pageId) {\n debug.log(\"loaded\", t.length, \"threads for\", pageId);\n setThreads(t);\n setLoaded(true);\n }\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pageId]);\n\n // Track whether we've ever had threads on this page (to know if empty means \"deleted all\")\n const hadThreadsRef = useRef(false);\n\n useEffect(() => {\n if (!loaded || pageIdRef.current !== pageId) return;\n // Save when there are threads, or when threads were cleared (deletion)\n if (threads.length > 0) {\n hadThreadsRef.current = true;\n debug.log(\"saving\", threads.length, \"threads for pageId:\", pageId);\n adapter.save(pageId, threads);\n } else if (hadThreadsRef.current) {\n debug.log(\"saving empty threads for pageId:\", pageId);\n adapter.save(pageId, threads);\n hadThreadsRef.current = false;\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [threads, pageId, loaded]);\n\n const setUser = useCallback((name: string) => {\n const u: ApostilUser = { id: generateId(), name, color: getRandomColor() };\n setUserState(u);\n saveUser(u);\n }, []);\n\n const addThread = useCallback(\n (pinX: number, pinY: number, body: string, targetId?: string, targetLabel?: string) => {\n if (!user) return;\n const threadId = generateId();\n const thread: ApostilThread = {\n id: threadId,\n pageId,\n pinX, pinY,\n targetId, targetLabel,\n resolved: false,\n createdAt: new Date().toISOString(),\n comments: [{\n id: generateId(), threadId, author: user, body,\n createdAt: new Date().toISOString(),\n }],\n };\n debug.log(\"new thread:\", { threadId, pinX, pinY, targetId, targetLabel, body });\n setThreads((prev) => [...prev, thread]);\n setActiveThreadId(threadId);\n setCommentMode(false);\n },\n [user, pageId]\n );\n\n const addReply = useCallback(\n (threadId: string, body: string) => {\n if (!user) return;\n setThreads((prev) =>\n prev.map((t) =>\n t.id === threadId\n ? { ...t, comments: [...t.comments, { id: generateId(), threadId, author: user, body, createdAt: new Date().toISOString() }] }\n : t\n )\n );\n },\n [user]\n );\n\n const resolveThread = useCallback((threadId: string) => {\n setThreads((prev) => prev.map((t) => t.id === threadId ? { ...t, resolved: !t.resolved } : t));\n setActiveThreadId(null);\n }, []);\n\n const deleteThread = useCallback((threadId: string) => {\n setThreads((prev) => prev.filter((t) => t.id !== threadId));\n setActiveThreadId(null);\n }, []);\n\n const unresolvedCount = threads.filter((t) => !t.resolved).length;\n\n return (\n <ApostilContext.Provider value={{\n threads, user, commentMode, activeThreadId, sidebarOpen, brandColor,\n setCommentMode, setActiveThreadId, setSidebarOpen,\n addThread, addReply, resolveThread, deleteThread, setUser,\n unresolvedCount,\n }}>\n {children}\n </ApostilContext.Provider>\n );\n}\n\nexport function useApostil() {\n const ctx = useContext(ApostilContext);\n if (!ctx) throw new Error(\"useApostil must be used within ApostilProvider\");\n return ctx;\n}\n","import type { ApostilStorage, ApostilThread } from \"../types\";\n\n/**\n * REST API storage adapter.\n * Works with any backend that implements GET/POST for threads.\n */\nexport function createRestAdapter(baseUrl: string): ApostilStorage {\n return {\n async load(pageId: string): Promise<ApostilThread[]> {\n const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;\n try {\n const res = await fetch(url);\n if (!res.ok) {\n console.warn(`[apostil] load failed: ${res.status} ${res.statusText} — ${url}`);\n return [];\n }\n const data = await res.json();\n return data;\n } catch (e) {\n console.warn(`[apostil] load error:`, e, `— ${url}`);\n return [];\n }\n },\n\n async save(pageId: string, threads: ApostilThread[]): Promise<void> {\n const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(threads),\n });\n if (!res.ok) {\n console.warn(`[apostil] save failed: ${res.status} ${res.statusText} — ${url}`);\n }\n } catch (e) {\n console.warn(`[apostil] save error:`, e, `— ${url}`);\n }\n },\n };\n}\n","let counter = 0;\n\nexport function generateId(): string {\n return `${Date.now()}-${++counter}-${Math.random().toString(36).slice(2, 7)}`;\n}\n\nconst USER_KEY = \"apostil-user\";\n\nexport function loadUser(): { id: string; name: string; color: string } | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(USER_KEY);\n return raw ? JSON.parse(raw) : null;\n } catch {\n return null;\n }\n}\n\nexport function saveUser(user: { id: string; name: string; color: string }) {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(USER_KEY, JSON.stringify(user));\n}\n\nconst USER_COLORS = [\n \"#df461c\", \"#2563eb\", \"#16a34a\", \"#9333ea\", \"#ea580c\",\n \"#0891b2\", \"#c026d3\", \"#4f46e5\", \"#059669\", \"#dc2626\",\n];\n\nexport function getRandomColor(): string {\n return USER_COLORS[Math.floor(Math.random() * USER_COLORS.length)];\n}\n","const PREFIX = \"[apostil]\";\n\nfunction isDebug(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return localStorage.getItem(\"apostil-debug\") === \"true\";\n } catch {\n return false;\n }\n}\n\nexport const debug = {\n log(...args: unknown[]) {\n if (isDebug()) console.log(PREFIX, ...args);\n },\n warn(...args: unknown[]) {\n if (isDebug()) console.warn(PREFIX, ...args);\n },\n error(...args: unknown[]) {\n console.error(PREFIX, ...args);\n },\n /** Enable/disable debug logging */\n enable() {\n if (typeof window !== \"undefined\") {\n localStorage.setItem(\"apostil-debug\", \"true\");\n console.log(PREFIX, \"debug logging enabled — reload to take effect\");\n }\n },\n disable() {\n if (typeof window !== \"undefined\") {\n localStorage.removeItem(\"apostil-debug\");\n console.log(PREFIX, \"debug logging disabled\");\n }\n },\n};\n\n// Expose globally for easy toggling from console\nif (typeof window !== \"undefined\") {\n (window as unknown as Record<string, unknown>).__apostil_debug = debug;\n}\n","\"use client\";\n\nimport { useApostil } from \"../context\";\n\nexport function useComments() {\n const { threads, addThread, addReply, resolveThread, deleteThread, unresolvedCount } = useApostil();\n\n return {\n threads,\n openThreads: threads.filter((t) => !t.resolved),\n resolvedThreads: threads.filter((t) => t.resolved),\n addThread,\n addReply,\n resolveThread,\n deleteThread,\n unresolvedCount,\n };\n}\n","\"use client\";\n\nimport { useApostil } from \"../context\";\n\nexport function useCommentMode() {\n const { commentMode, setCommentMode, sidebarOpen, setSidebarOpen } = useApostil();\n\n return {\n commentMode,\n setCommentMode,\n toggleCommentMode: () => setCommentMode(!commentMode),\n sidebarOpen,\n setSidebarOpen,\n toggleSidebar: () => setSidebarOpen(!sidebarOpen),\n };\n}\n","\"use client\";\n\nimport { useState, useCallback, useRef, useEffect } from \"react\";\nimport { useApostil } from \"../context\";\nimport { debug } from \"../debug\";\nimport { CommentPin } from \"./comment-pin\";\nimport { ApostilThreadPopover } from \"./comment-thread\";\nimport { CommentComposer } from \"./comment-composer\";\nimport { UserPrompt } from \"./user-prompt\";\n\ntype PendingPin = {\n x: number;\n y: number;\n targetId?: string;\n targetLabel?: string;\n};\n\n// Find the highest z-index on the page (cached per toggle)\nlet cachedHighZ = 0;\nlet cacheTimestamp = 0;\nfunction getHighestZIndex(): number {\n const now = Date.now();\n if (now - cacheTimestamp < 500) return cachedHighZ; // cache for 500ms\n let max = 0;\n const els = document.querySelectorAll(\"[style*='z-index'], [class*='z-']\");\n for (let i = 0; i < els.length; i++) {\n const z = parseInt(getComputedStyle(els[i]).zIndex, 10);\n if (!isNaN(z) && z > max) max = z;\n }\n cachedHighZ = Math.max(max, 100); // minimum 100\n cacheTimestamp = now;\n return cachedHighZ;\n}\n\n// Semantic elements that are meaningful containers\nconst SEMANTIC_TAGS = new Set([\n \"SECTION\", \"NAV\", \"ASIDE\", \"HEADER\", \"FOOTER\", \"MAIN\",\n \"ARTICLE\", \"FORM\", \"DIALOG\", \"DETAILS\",\n]);\n\n// Min size for a target, and max size ratio to viewport to avoid matching the whole page\nconst MIN_TARGET_SIZE = 50;\nconst MAX_VIEWPORT_RATIO = 0.85; // skip elements covering >85% of viewport in both dimensions\n\n/**\n * Check if a div is a \"visual panel\" — a scrollable area, flex/grid child with defined bounds,\n * or a container with border/background that forms a distinct visual region.\n */\nfunction isVisualPanel(el: HTMLElement): boolean {\n const style = getComputedStyle(el);\n\n // Scrollable container\n if (/auto|scroll/.test(style.overflow + style.overflowY + style.overflowX)) return true;\n\n // Has border or distinct background (likely a card/panel)\n if (style.borderWidth && style.borderWidth !== \"0px\" && style.borderStyle !== \"none\") return true;\n if (style.borderRadius && style.borderRadius !== \"0px\") return true;\n if (style.backgroundColor && style.backgroundColor !== \"rgba(0, 0, 0, 0)\" && style.backgroundColor !== \"transparent\") return true;\n if (style.boxShadow && style.boxShadow !== \"none\") return true;\n\n return false;\n}\n\n/**\n * Try to get a readable label from an element's content.\n * Looks for headings, strong text, or first significant text node nearby.\n */\nfunction inferLabel(el: HTMLElement): string | null {\n // Check for a heading child\n const heading = el.querySelector(\"h1, h2, h3, h4, h5, h6, [class*='title'], [class*='heading']\");\n if (heading) {\n const text = heading.textContent?.trim();\n if (text && text.length <= 40) return text;\n }\n\n // Check for a label-like first child text\n const firstText = el.querySelector(\"span, p, label, strong\");\n if (firstText) {\n const text = firstText.textContent?.trim();\n if (text && text.length <= 30) return text;\n }\n\n return null;\n}\n\n/**\n * Build a stable CSS selector path for an element.\n */\nfunction getElementId(el: HTMLElement): string {\n const manual = el.getAttribute(\"data-comment-target\");\n if (manual) return manual;\n\n if (el.id) return `#${el.id}`;\n\n const label = el.getAttribute(\"aria-label\");\n if (label) return `${el.tagName.toLowerCase()}[aria-label=\"${label}\"]`;\n\n // nth-child path from nearest identifiable ancestor\n const parts: string[] = [];\n let cur: HTMLElement | null = el;\n for (let depth = 0; cur && depth < 5; depth++) {\n if (cur.id) {\n parts.unshift(`#${cur.id}`);\n break;\n }\n const p: HTMLElement | null = cur.parentElement;\n if (p) {\n const siblings = Array.from(p.children);\n const idx = siblings.indexOf(cur);\n parts.unshift(`${cur.tagName.toLowerCase()}:nth-child(${idx + 1})`);\n } else {\n parts.unshift(cur.tagName.toLowerCase());\n }\n cur = p;\n }\n return parts.join(\" > \");\n}\n\n/**\n * Derive a human-readable label for an element.\n */\nfunction getElementLabel(el: HTMLElement): string {\n const manual = el.getAttribute(\"data-comment-label\");\n if (manual) return manual;\n\n const ariaLabel = el.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n // Try to infer from content\n const inferred = inferLabel(el);\n if (inferred) return inferred;\n\n const role = el.getAttribute(\"role\");\n\n if (el.id) {\n const pretty = el.id\n .replace(/[-_]/g, \" \")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n return role ? `${pretty} (${role})` : pretty;\n }\n\n const tag = el.tagName.toLowerCase();\n if (SEMANTIC_TAGS.has(el.tagName)) return role ? `${tag} (${role})` : tag;\n if (role) return role;\n\n return tag;\n}\n\n/**\n * Walk up from the clicked element and find the best container to anchor the comment to.\n * Scores candidates by specificity — prefers the innermost meaningful panel.\n */\nfunction findCommentTarget(el: HTMLElement, boundary: HTMLElement | null) {\n let current: HTMLElement | null = el;\n const candidates: { el: HTMLElement; score: number; depth: number }[] = [];\n let depth = 0;\n\n while (current && current !== boundary && current !== document.body) {\n const rect = current.getBoundingClientRect();\n const isBigEnough = rect.width >= MIN_TARGET_SIZE && rect.height >= MIN_TARGET_SIZE;\n const isTooBig =\n rect.width > window.innerWidth * MAX_VIEWPORT_RATIO &&\n rect.height > window.innerHeight * MAX_VIEWPORT_RATIO;\n\n if (isBigEnough && !isTooBig) {\n let score = 0;\n\n // Manual target — highest priority\n if (current.getAttribute(\"data-comment-target\")) score = 100;\n // id\n else if (current.id) score = 80;\n // aria-label\n else if (current.getAttribute(\"aria-label\")) score = 70;\n // role attribute\n else if (current.getAttribute(\"role\")) score = 60;\n // semantic HTML tag\n else if (SEMANTIC_TAGS.has(current.tagName)) score = 50;\n // Visual panel (scrollable, bordered, shadowed, rounded)\n else if (isVisualPanel(current)) score = 40;\n\n if (score > 0) {\n candidates.push({ el: current, score, depth });\n }\n }\n\n current = current.parentElement;\n depth++;\n }\n\n if (candidates.length === 0) return null;\n\n // Prefer innermost among equally-scored candidates.\n // Among different scores: higher score wins, but add a bonus for being closer to the click.\n candidates.sort((a, b) => {\n const scoreA = a.score + Math.max(0, 10 - a.depth);\n const scoreB = b.score + Math.max(0, 10 - b.depth);\n return scoreB - scoreA;\n });\n\n const best = candidates[0];\n const targetId = getElementId(best.el);\n const targetLabel = getElementLabel(best.el);\n\n debug.log(\" candidates:\", candidates.map((c) => ({\n tag: c.el.tagName.toLowerCase(),\n id: c.el.id || undefined,\n class: c.el.className?.toString().slice(0, 60) || undefined,\n score: c.score,\n depth: c.depth,\n label: getElementLabel(c.el),\n })));\n\n return {\n targetId,\n targetLabel,\n element: best.el,\n };\n}\n\nexport function CommentOverlay() {\n const { threads, commentMode, setCommentMode, user, addThread, setActiveThreadId, brandColor } =\n useApostil();\n const overlayRef = useRef<HTMLDivElement>(null);\n const [pendingPin, setPendingPin] = useState<PendingPin | null>(null);\n const [pendingPixel, setPendingPixel] = useState<{ left: number; top: number } | null>(null);\n const pendingRef = useRef<HTMLDivElement>(null);\n const [pendingFlip, setPendingFlip] = useState<{ x: boolean; y: boolean }>({ x: false, y: false });\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (!commentMode || !overlayRef.current) return;\n\n const overlayRect = overlayRef.current.getBoundingClientRect();\n\n // Temporarily hide overlay so elementFromPoint hits the actual content\n const overlay = overlayRef.current;\n overlay.style.pointerEvents = \"none\";\n const elementBelow = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null;\n overlay.style.pointerEvents = \"\";\n\n debug.log(\" click at\", { clientX: e.clientX, clientY: e.clientY });\n debug.log(\" element below overlay:\", elementBelow);\n\n // Log the DOM path for debugging\n if (elementBelow) {\n const path: string[] = [];\n let walk: HTMLElement | null = elementBelow;\n while (walk && walk !== document.body) {\n const attrs: string[] = [walk.tagName.toLowerCase()];\n if (walk.id) attrs.push(`#${walk.id}`);\n if (walk.getAttribute(\"data-comment-target\")) attrs.push(`[data-comment-target=\"${walk.getAttribute(\"data-comment-target\")}\"]`);\n if (walk.getAttribute(\"aria-label\")) attrs.push(`[aria-label=\"${walk.getAttribute(\"aria-label\")}\"]`);\n if (walk.getAttribute(\"role\")) attrs.push(`[role=\"${walk.getAttribute(\"role\")}\"]`);\n if (SEMANTIC_TAGS.has(walk.tagName)) attrs.push(\"(semantic)\");\n path.push(attrs.join(\"\"));\n walk = walk.parentElement;\n }\n debug.log(\" DOM path:\", path.join(\" → \"));\n }\n\n const target = elementBelow ? findCommentTarget(elementBelow, overlayRef.current) : null;\n\n if (target) {\n const targetRect = target.element.getBoundingClientRect();\n const x = ((e.clientX - targetRect.left) / targetRect.width) * 100;\n const y = ((e.clientY - targetRect.top) / targetRect.height) * 100;\n debug.log(\" ✅ target found:\", {\n targetId: target.targetId,\n targetLabel: target.targetLabel,\n element: target.element,\n relativePos: { x: x.toFixed(1), y: y.toFixed(1) },\n targetRect: { w: targetRect.width, h: targetRect.height },\n });\n setPendingPin({ x, y, targetId: target.targetId, targetLabel: target.targetLabel });\n } else {\n const x = ((e.clientX - overlayRect.left) / overlayRect.width) * 100;\n const y = ((e.clientY - overlayRect.top) / overlayRect.height) * 100;\n debug.log(\" ⚠️ no target found, using overlay-relative position:\", {\n x: x.toFixed(1),\n y: y.toFixed(1),\n });\n setPendingPin({ x, y });\n }\n\n setPendingPixel({\n left: e.clientX - overlayRect.left,\n top: e.clientY - overlayRect.top,\n });\n setActiveThreadId(null);\n },\n [commentMode, setActiveThreadId]\n );\n\n const handleNewComment = useCallback(\n (body: string) => {\n if (!pendingPin) return;\n debug.log(\" saving thread:\", {\n pinX: pendingPin.x.toFixed(1),\n pinY: pendingPin.y.toFixed(1),\n targetId: pendingPin.targetId ?? \"(none)\",\n targetLabel: pendingPin.targetLabel ?? \"(none)\",\n body,\n });\n addThread(pendingPin.x, pendingPin.y, body, pendingPin.targetId, pendingPin.targetLabel);\n setPendingPin(null);\n setPendingPixel(null);\n },\n [pendingPin, addThread]\n );\n\n const cancelPending = useCallback(() => {\n setPendingPin(null);\n setPendingPixel(null);\n setCommentMode(false);\n }, [setCommentMode]);\n\n // Check if pending popover overflows viewport\n useEffect(() => {\n if (!pendingPin || !pendingRef.current) return;\n requestAnimationFrame(() => {\n if (!pendingRef.current) return;\n const rect = pendingRef.current.getBoundingClientRect();\n setPendingFlip({\n x: rect.right > window.innerWidth,\n y: rect.bottom > window.innerHeight,\n });\n });\n }, [pendingPin]);\n\n // Open thread from URL hash (e.g. #apostil-threadId)\n useEffect(() => {\n const hash = window.location.hash;\n console.log(\"[apostil] hash check:\", hash, \"threads:\", threads.length, threads.map(t => t.id));\n if (!hash.startsWith(\"#apostil-\")) return;\n const threadId = hash.replace(\"#apostil-\", \"\");\n console.log(\"[apostil] looking for thread:\", threadId);\n // Wait for threads to load before activating\n if (threads.length === 0) {\n console.log(\"[apostil] no threads loaded yet, waiting...\");\n return;\n }\n const found = threads.find((t) => t.id === threadId);\n console.log(\"[apostil] found thread:\", found ? \"yes\" : \"no\");\n if (found) {\n setActiveThreadId(threadId);\n // Clean hash from URL without triggering navigation\n window.history.replaceState(null, \"\", window.location.pathname);\n }\n }, [threads, setActiveThreadId]);\n\n // Keyboard shortcuts\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n const tag = (e.target as HTMLElement)?.tagName;\n\n // Escape always works — even when typing\n if (e.key === \"Escape\") {\n if (pendingPin) {\n setPendingPin(null);\n setPendingPixel(null);\n setCommentMode(false);\n } else if (commentMode) {\n setCommentMode(false);\n }\n return;\n }\n\n // Other shortcuts only when not typing, and not with modifier keys (Cmd+C, Ctrl+C, etc.)\n if (tag === \"INPUT\" || tag === \"TEXTAREA\") return;\n if (e.metaKey || e.ctrlKey || e.altKey) return;\n\n if (e.key === \"c\" || e.key === \"C\") {\n e.preventDefault(); // prevent \"c\" from leaking into any input that gains focus\n if (!commentMode) {\n debug.log(\" comment mode ON\");\n setActiveThreadId(null);\n setCommentMode(true);\n } else {\n debug.log(\" comment mode OFF\");\n setCommentMode(false);\n }\n }\n };\n document.addEventListener(\"keydown\", handler);\n return () => document.removeEventListener(\"keydown\", handler);\n }, [commentMode, pendingPin, setCommentMode, setActiveThreadId]);\n\n // Sort: unresolved first, then resolved\n const sortedThreads = [...threads].sort((a, b) => {\n if (a.resolved !== b.resolved) return a.resolved ? 1 : -1;\n return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();\n });\n\n // When user prompt is showing, disable the overlay click handling\n const showingUserPrompt = commentMode && !user;\n\n return (\n <>\n {/* Overlay layer */}\n <div\n ref={overlayRef}\n className={`fixed inset-0 ${\n commentMode && !showingUserPrompt\n ? \"cursor-crosshair pointer-events-auto\"\n : \"pointer-events-none\"\n }`}\n style={{ zIndex: commentMode ? getHighestZIndex() + 10 : 55 }}\n onMouseDown={handleClick}\n >\n {/* Existing pins */}\n {sortedThreads.map((thread, i) => (\n <div key={thread.id} className=\"pointer-events-auto\">\n <CommentPin thread={thread} index={i} overlayRef={overlayRef} />\n <ApostilThreadPopover thread={thread} overlayRef={overlayRef} />\n </div>\n ))}\n\n {/* Pending pin */}\n {pendingPin && pendingPixel && user && (\n <div\n className=\"absolute z-[70] pointer-events-auto\"\n style={{\n left: pendingPixel.left,\n top: pendingPixel.top,\n }}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n className=\"absolute -translate-x-1/2 -translate-y-1/2 w-7 h-7 rounded-full\n flex items-center justify-center text-white text-xs font-semibold\n shadow-lg ring-2 ring-white ring-offset-2 animate-bounce\"\n style={{ backgroundColor: user.color }}\n >\n +\n </div>\n <div\n ref={pendingRef}\n className=\"absolute w-72\"\n style={{\n marginLeft: pendingFlip.x ? -308 : 20,\n marginTop: pendingFlip.y ? undefined : -12,\n ...(pendingFlip.y ? { bottom: 0 } : {}),\n }}\n >\n <div className=\"bg-white rounded-xl shadow-2xl border border-neutral-200 p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <p className=\"text-xs text-neutral-500\">New comment</p>\n {pendingPin.targetLabel && (\n <span className=\"text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium\">\n {pendingPin.targetLabel}\n </span>\n )}\n </div>\n <CommentComposer\n onSubmit={handleNewComment}\n placeholder=\"What's on your mind?\"\n autoFocus\n />\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Comment mode hint */}\n {commentMode && !pendingPin && !showingUserPrompt && (\n <div className=\"absolute bottom-6 left-1/2 -translate-x-1/2 z-[60] pointer-events-none\">\n <div\n className=\"text-white text-sm px-4 py-2 rounded-full backdrop-blur-sm\"\n style={{ backgroundColor: `color-mix(in oklab, ${brandColor} 80%, transparent)` }}\n >\n Click anywhere to add a comment\n </div>\n </div>\n )}\n\n {/* User prompt — rendered above overlay so clicks work */}\n <UserPrompt />\n </>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect, useCallback, useRef, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useApostil } from \"../context\";\nimport type { ApostilThread } from \"../types\";\n\n/**\n * Find the target element using the stored targetId.\n * targetId can be a CSS selector or a data-comment-target value.\n */\nfunction findTargetElement(targetId: string): HTMLElement | null {\n // First try as a CSS selector\n try {\n const el = document.querySelector(targetId);\n if (el instanceof HTMLElement) return el;\n } catch {\n // Invalid selector — fall through\n }\n\n // Then try as a data-comment-target value\n try {\n const el = document.querySelector(`[data-comment-target=\"${CSS.escape(targetId)}\"]`);\n if (el instanceof HTMLElement) return el;\n } catch {\n // noop\n }\n\n return null;\n}\n\n/**\n * Resolves pixel position for a thread pin relative to the overlay.\n * Used only for NON-targeted pins (no targetId).\n */\nfunction resolveOverlayPosition(\n thread: ApostilThread,\n overlayEl: HTMLElement | null\n): { left: number; top: number } | null {\n if (!overlayEl) return null;\n const overlayRect = overlayEl.getBoundingClientRect();\n return {\n left: (thread.pinX / 100) * overlayRect.width,\n top: (thread.pinY / 100) * overlayRect.height,\n };\n}\n\n/**\n * Resolves pixel position relative to the overlay, for targeted pins.\n * Used by the thread popover which always renders in the overlay.\n */\nfunction resolvePosition(\n thread: ApostilThread,\n overlayEl: HTMLElement | null\n): { left: number; top: number } | null {\n if (!overlayEl) return null;\n const overlayRect = overlayEl.getBoundingClientRect();\n\n if (thread.targetId) {\n const target = findTargetElement(thread.targetId);\n if (target) {\n const targetRect = target.getBoundingClientRect();\n return {\n left: targetRect.left - overlayRect.left + (thread.pinX / 100) * targetRect.width,\n top: targetRect.top - overlayRect.top + (thread.pinY / 100) * targetRect.height,\n };\n }\n return null;\n }\n\n return {\n left: (thread.pinX / 100) * overlayRect.width,\n top: (thread.pinY / 100) * overlayRect.height,\n };\n}\n\n// ─── Pin button (shared rendering) ────────────────────────────────\n\nfunction PinButton({\n thread,\n index,\n isActive,\n onClick,\n}: {\n thread: ApostilThread;\n index: number;\n isActive: boolean;\n onClick: (e: React.MouseEvent) => void;\n}) {\n const authorColor = thread.comments[0]?.author.color ?? \"#df461c\";\n const [hovered, setHovered] = useState(false);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [tooltipPos, setTooltipPos] = useState<{ left: number; top: number } | null>(null);\n\n useEffect(() => {\n if (!hovered || !buttonRef.current) {\n setTooltipPos(null);\n return;\n }\n const rect = buttonRef.current.getBoundingClientRect();\n setTooltipPos({\n left: rect.left + rect.width / 2,\n top: rect.bottom + 4,\n });\n }, [hovered]);\n\n return (\n <>\n <button\n ref={buttonRef}\n onClick={onClick}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n style={{ position: \"relative\" }}\n >\n <div\n className={`\n flex items-center justify-center\n w-7 h-7 rounded-full text-white text-xs font-semibold\n shadow-lg cursor-pointer\n transition-all duration-200\n ${isActive ? \"scale-125 ring-2 ring-white ring-offset-2\" : \"hover:scale-110\"}\n ${thread.resolved ? \"opacity-40\" : \"\"}\n `}\n style={{ backgroundColor: authorColor }}\n >\n {index + 1}\n </div>\n {!thread.resolved && !isActive && (\n <div\n className=\"absolute inset-0 rounded-full animate-ping opacity-20\"\n style={{ backgroundColor: authorColor }}\n />\n )}\n </button>\n {thread.targetLabel && hovered && tooltipPos && createPortal(\n <div\n className=\"fixed whitespace-nowrap text-[10px] bg-neutral-800 text-white px-1.5 py-0.5 rounded pointer-events-none\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n transform: \"translateX(-50%)\",\n zIndex: 999999,\n }}\n >\n {thread.targetLabel}\n </div>,\n document.body\n )}\n </>\n );\n}\n\n// ─── Targeted pin (portals into the target element) ───────────────\n\nfunction TargetedPin({\n thread,\n index,\n}: {\n thread: ApostilThread;\n index: number;\n}) {\n const { activeThreadId, setActiveThreadId } = useApostil();\n const isActive = activeThreadId === thread.id;\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!thread.targetId) return;\n\n function tryFind() {\n const el = findTargetElement(thread.targetId!);\n if (el) {\n const pos = getComputedStyle(el).position;\n if (pos === \"static\") el.style.position = \"relative\";\n setTargetEl(el);\n } else {\n setTargetEl(null);\n }\n }\n\n tryFind();\n\n // Watch for DOM changes — target element may appear/disappear (popovers, dialogs)\n const observer = new MutationObserver(() => tryFind());\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => observer.disconnect();\n }, [thread.targetId]);\n\n if (!targetEl) return null;\n\n return createPortal(\n <div\n className=\"absolute pointer-events-auto\"\n style={{\n left: `${thread.pinX}%`,\n top: `${thread.pinY}%`,\n transform: \"translate(-50%, -50%)\",\n zIndex: 10,\n }}\n >\n <PinButton\n thread={thread}\n index={index}\n isActive={isActive}\n onClick={(e) => {\n e.stopPropagation();\n setActiveThreadId(isActive ? null : thread.id);\n }}\n />\n </div>,\n targetEl\n );\n}\n\n// ─── Overlay pin (non-targeted, positioned in the overlay) ────────\n\nfunction OverlayPin({\n thread,\n index,\n overlayRef,\n}: {\n thread: ApostilThread;\n index: number;\n overlayRef: RefObject<HTMLDivElement | null>;\n}) {\n const { activeThreadId, setActiveThreadId } = useApostil();\n const isActive = activeThreadId === thread.id;\n const [pos, setPos] = useState<{ left: number; top: number } | null>(null);\n\n const updatePos = useCallback(() => {\n setPos(resolveOverlayPosition(thread, overlayRef.current));\n }, [thread, overlayRef]);\n\n useEffect(() => {\n updatePos();\n window.addEventListener(\"resize\", updatePos);\n return () => window.removeEventListener(\"resize\", updatePos);\n }, [updatePos]);\n\n if (!pos) return null;\n\n return (\n <div\n className=\"absolute pointer-events-auto\"\n style={{\n left: pos.left,\n top: pos.top,\n transform: \"translate(-50%, -50%)\",\n zIndex: 60,\n }}\n >\n <PinButton\n thread={thread}\n index={index}\n isActive={isActive}\n onClick={(e) => {\n e.stopPropagation();\n setActiveThreadId(isActive ? null : thread.id);\n }}\n />\n </div>\n );\n}\n\n// ─── Public CommentPin (delegates to targeted or overlay) ─────────\n\nexport function CommentPin({\n thread,\n index,\n overlayRef,\n}: {\n thread: ApostilThread;\n index: number;\n overlayRef: RefObject<HTMLDivElement | null>;\n}) {\n if (thread.targetId) {\n return <TargetedPin key={`targeted-${thread.id}`} thread={thread} index={index} />;\n }\n return <OverlayPin key={`overlay-${thread.id}`} thread={thread} index={index} overlayRef={overlayRef} />;\n}\n\nexport { resolvePosition, findTargetElement };\n","\"use client\";\n\nimport { useEffect, useRef, useState, useCallback, type RefObject } from \"react\";\nimport { Check, Trash2, Undo2 } from \"../icons\";\nimport { useApostil } from \"../context\";\nimport { CommentComposer } from \"./comment-composer\";\nimport { resolvePosition } from \"./comment-pin\";\nimport type { ApostilThread as ApostilThreadType } from \"../types\";\n\nfunction timeAgo(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return \"just now\";\n if (mins < 60) return `${mins}m ago`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n\nexport function ApostilThreadPopover({\n thread,\n overlayRef,\n}: {\n thread: ApostilThreadType;\n overlayRef: RefObject<HTMLDivElement | null>;\n}) {\n const { activeThreadId, setActiveThreadId, addReply, resolveThread, deleteThread, user } =\n useApostil();\n const ref = useRef<HTMLDivElement>(null);\n const isOpen = activeThreadId === thread.id;\n\n const [pos, setPos] = useState<{ left: number; top: number } | null>(null);\n const [flip, setFlip] = useState<{ x: boolean; y: boolean }>({ x: false, y: false });\n\n const updatePos = useCallback(() => {\n setPos(resolvePosition(thread, overlayRef.current));\n }, [thread, overlayRef]);\n\n // Check if popover overflows viewport and flip accordingly\n useEffect(() => {\n if (!isOpen || !pos || !ref.current) return;\n const rect = ref.current.getBoundingClientRect();\n const flipX = rect.right > window.innerWidth;\n const flipY = rect.bottom > window.innerHeight;\n setFlip({ x: flipX, y: flipY });\n }, [isOpen, pos]);\n\n useEffect(() => {\n if (!isOpen) return;\n updatePos();\n window.addEventListener(\"resize\", updatePos);\n // Track scroll on any container so popover follows the pin\n document.addEventListener(\"scroll\", updatePos, true);\n return () => {\n window.removeEventListener(\"resize\", updatePos);\n document.removeEventListener(\"scroll\", updatePos, true);\n };\n }, [isOpen, updatePos]);\n\n useEffect(() => {\n if (!isOpen) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setActiveThreadId(null);\n }\n };\n const timer = setTimeout(() => document.addEventListener(\"mousedown\", handler), 0);\n return () => {\n clearTimeout(timer);\n document.removeEventListener(\"mousedown\", handler);\n };\n }, [isOpen, setActiveThreadId]);\n\n if (!isOpen || !pos) return null;\n\n return (\n <div\n ref={ref}\n className=\"absolute z-[70]\"\n style={{\n left: pos.left,\n top: pos.top,\n marginLeft: flip.x ? -340 : 20,\n marginTop: flip.y ? -12 : -12,\n ...(flip.y ? { transform: \"translateY(-100%)\" } : {}),\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"w-80 bg-white rounded-xl shadow-2xl border border-neutral-200 overflow-hidden\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-2.5 border-b border-neutral-100 bg-neutral-50\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-neutral-500\">\n {thread.comments.length} {thread.comments.length === 1 ? \"comment\" : \"comments\"}\n </span>\n {thread.targetLabel && (\n <span className=\"text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium\">\n {thread.targetLabel}\n </span>\n )}\n {thread.resolved && (\n <span className=\"text-[10px] text-emerald-600 font-medium\">Resolved</span>\n )}\n </div>\n <div className=\"flex gap-1\">\n <button\n onClick={() => resolveThread(thread.id)}\n className=\"p-1 rounded hover:bg-neutral-200 transition-colors\"\n title={thread.resolved ? \"Reopen\" : \"Resolve\"}\n >\n {thread.resolved ? (\n <Undo2 className=\"w-3.5 h-3.5 text-neutral-500\" />\n ) : (\n <Check className=\"w-3.5 h-3.5 text-emerald-600\" />\n )}\n </button>\n <button\n onClick={() => deleteThread(thread.id)}\n className=\"p-1 rounded hover:bg-red-50 transition-colors\"\n title=\"Delete thread\"\n >\n <Trash2 className=\"w-3.5 h-3.5 text-neutral-400 hover:text-red-500\" />\n </button>\n </div>\n </div>\n\n {/* Comments */}\n <div className=\"max-h-64 overflow-y-auto\">\n {thread.comments.map((comment) => (\n <div key={comment.id} className=\"px-4 py-3 border-b border-neutral-50 last:border-0\">\n <div className=\"flex items-center gap-2 mb-1\">\n <div\n className=\"w-5 h-5 rounded-full flex items-center justify-center text-white text-[10px] font-semibold shrink-0\"\n style={{ backgroundColor: comment.author.color }}\n >\n {comment.author.name[0]?.toUpperCase()}\n </div>\n <span className=\"text-xs font-medium text-neutral-800\">\n {comment.author.name}\n </span>\n <span className=\"text-[10px] text-neutral-400 ml-auto\">\n {timeAgo(comment.createdAt)}\n </span>\n </div>\n <p className=\"text-sm text-neutral-700 leading-relaxed pl-7\">\n {comment.body}\n </p>\n </div>\n ))}\n </div>\n\n {/* Reply */}\n {user && !thread.resolved && (\n <div className=\"px-3 py-2.5 border-t border-neutral-100 bg-neutral-50/50\">\n <CommentComposer\n onSubmit={(body) => addReply(thread.id, body)}\n placeholder=\"Reply...\"\n autoFocus\n />\n </div>\n )}\n </div>\n </div>\n );\n}\n","import React from \"react\";\n\ntype IconProps = { className?: string };\n\nexport function Send({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m22 2-7 20-4-9-9-4Z\" />\n <path d=\"M22 2 11 13\" />\n </svg>\n );\n}\n\nexport function MessageSquare({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nexport function List({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"8\" x2=\"21\" y1=\"6\" y2=\"6\" />\n <line x1=\"8\" x2=\"21\" y1=\"12\" y2=\"12\" />\n <line x1=\"8\" x2=\"21\" y1=\"18\" y2=\"18\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"6\" y2=\"6\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"12\" y2=\"12\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\nexport function X({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\nexport function Check({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\nexport function Undo2({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M9 14 4 9l5-5\" />\n <path d=\"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11\" />\n </svg>\n );\n}\n\nexport function Globe({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\" />\n <path d=\"M2 12h20\" />\n </svg>\n );\n}\n\nexport function FileText({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 9H8\" />\n <path d=\"M16 13H8\" />\n <path d=\"M16 17H8\" />\n </svg>\n );\n}\n\nexport function Trash2({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" />\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\n <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\" />\n <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\" />\n </svg>\n );\n}\n","\"use client\";\n\nimport { useState, useRef, useEffect } from \"react\";\nimport { Send } from \"../icons\";\nimport { useApostil } from \"../context\";\n\nexport function CommentComposer({\n onSubmit,\n placeholder = \"Add a comment...\",\n autoFocus = false,\n}: {\n onSubmit: (body: string) => void;\n placeholder?: string;\n autoFocus?: boolean;\n}) {\n const { brandColor } = useApostil();\n const [value, setValue] = useState(\"\");\n const inputRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n if (autoFocus) {\n // Delay focus so it lands after the overlay click event completes\n requestAnimationFrame(() => {\n inputRef.current?.focus();\n });\n }\n }, [autoFocus]);\n\n const handleSubmit = () => {\n const trimmed = value.trim();\n if (!trimmed) return;\n onSubmit(trimmed);\n setValue(\"\");\n };\n\n return (\n <div className=\"flex gap-2 items-end\">\n <textarea\n ref={inputRef}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n }}\n placeholder={placeholder}\n rows={1}\n className=\"flex-1 resize-none rounded-lg border border-neutral-200 bg-white px-3 py-2 text-sm\n placeholder:text-neutral-400 focus:outline-none focus:ring-2 focus:ring-neutral-300\n min-h-[36px] max-h-[120px]\"\n />\n <button\n onClick={handleSubmit}\n disabled={!value.trim()}\n className=\"flex items-center justify-center w-8 h-8 rounded-lg\n text-white disabled:opacity-30\n transition-colors shrink-0\"\n style={{ backgroundColor: brandColor }}\n >\n <Send className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { useApostil } from \"../context\";\n\nexport function UserPrompt() {\n const { user, setUser, commentMode, brandColor } = useApostil();\n const [name, setName] = useState(\"\");\n\n // Only show when comment mode is activated and no user is set\n if (user || !commentMode) return null;\n\n const handleSubmit = () => {\n const trimmed = name.trim();\n if (!trimmed) return;\n setUser(trimmed);\n };\n\n return (\n <div\n className=\"fixed inset-0 flex items-center justify-center bg-black/20 backdrop-blur-[2px]\"\n style={{ zIndex: 99999 }}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"bg-white rounded-xl shadow-2xl border border-neutral-200 p-6 w-80\">\n <h3 className=\"text-sm font-semibold text-neutral-900 mb-1\">\n What&apos;s your name?\n </h3>\n <p className=\"text-xs text-neutral-500 mb-4\">\n This will be shown with your comments.\n </p>\n <input\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSubmit();\n }}\n placeholder=\"Enter your name\"\n autoFocus\n className=\"w-full rounded-lg border border-neutral-200 px-3 py-2 text-sm\n placeholder:text-neutral-400 focus:outline-none focus:ring-2 focus:ring-neutral-300 mb-3\"\n />\n <button\n onClick={handleSubmit}\n disabled={!name.trim()}\n className=\"w-full py-2 rounded-lg text-white text-sm font-medium\n disabled:opacity-30 transition-colors\"\n style={{ backgroundColor: brandColor }}\n >\n Continue\n </button>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { MessageSquare, List, X } from \"../icons\";\nimport { useApostil } from \"../context\";\n\nexport function CommentToggle() {\n const {\n commentMode,\n setCommentMode,\n sidebarOpen,\n setSidebarOpen,\n unresolvedCount,\n setActiveThreadId,\n brandColor,\n } = useApostil();\n\n return (\n <div className=\"absolute bottom-5 right-5 z-[65] flex flex-col gap-2 items-end\">\n {/* Sidebar toggle */}\n <button\n onClick={() => setSidebarOpen(!sidebarOpen)}\n className={`flex items-center justify-center w-10 h-10 rounded-full shadow-lg\n transition-all duration-200 hover:scale-105\n ${sidebarOpen\n ? \"text-white\"\n : \"bg-white text-neutral-600 hover:bg-neutral-50 border border-neutral-200\"\n }`}\n style={sidebarOpen ? { backgroundColor: brandColor } : undefined}\n title=\"Toggle comment list\"\n >\n <List className=\"w-4 h-4\" />\n </button>\n\n {/* Comment mode toggle */}\n <button\n onClick={() => {\n if (commentMode) {\n setCommentMode(false);\n } else {\n setActiveThreadId(null);\n setCommentMode(true);\n }\n }}\n className={`relative flex items-center justify-center w-12 h-12 rounded-full shadow-lg\n transition-all duration-200 hover:scale-105\n ${commentMode\n ? \"text-white ring-2 ring-neutral-400\"\n : \"bg-white text-neutral-700 hover:bg-neutral-50 border border-neutral-200\"\n }`}\n style={commentMode ? { backgroundColor: brandColor } : undefined}\n title={commentMode ? \"Exit comment mode\" : \"Add comment\"}\n >\n {commentMode ? (\n <X className=\"w-5 h-5\" />\n ) : (\n <MessageSquare className=\"w-5 h-5\" />\n )}\n {/* Unresolved badge */}\n {unresolvedCount > 0 && !commentMode && (\n <span className=\"absolute -top-1 -right-1 min-w-[18px] h-[18px] rounded-full\n bg-red-500 text-white text-[10px] font-semibold\n flex items-center justify-center px-1\">\n {unresolvedCount}\n </span>\n )}\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { X, Check, Undo2, MessageSquare, Globe, FileText } from \"../icons\";\nimport { useApostil } from \"../context\";\nimport type { ApostilThread } from \"../types\";\n\nfunction timeAgo(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return \"just now\";\n if (mins < 60) return `${mins}m ago`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n\nfunction pageIdToDisplay(pageId: string): string {\n return pageId.replace(/--/g, \"/\").replace(/-/g, \".\");\n}\n\ntype AllPagesData = { pageId: string; threads: ApostilThread[] }[];\n\nexport function CommentSidebar() {\n const {\n threads,\n sidebarOpen,\n setSidebarOpen,\n setActiveThreadId,\n resolveThread,\n brandColor,\n } = useApostil();\n\n const [tab, setTab] = useState<\"page\" | \"all\">(\"page\");\n const [allPages, setAllPages] = useState<AllPagesData>([]);\n const [loadingAll, setLoadingAll] = useState(false);\n\n // Fetch all pages when \"All Pages\" tab is selected\n useEffect(() => {\n if (!sidebarOpen || tab !== \"all\") return;\n setLoadingAll(true);\n\n // Try CLI server first, then local API\n async function fetchAll() {\n // Fetch from the app's own API route (no pageId = returns all pages)\n try {\n const res = await fetch(\"/api/apostil\");\n if (res.ok) {\n const data = await res.json();\n setAllPages(data);\n }\n } catch {}\n setLoadingAll(false);\n }\n fetchAll();\n }, [sidebarOpen, tab]);\n\n if (!sidebarOpen) return null;\n\n const openThreads = threads.filter((t) => !t.resolved);\n const resolvedThreads = threads.filter((t) => t.resolved);\n\n return (\n <div className=\"absolute top-0 right-0 bottom-0 w-80 z-[75] bg-white border-l border-neutral-200 shadow-xl flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-neutral-100\">\n <div className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-4 h-4 text-neutral-500\" />\n <span className=\"text-sm font-semibold text-neutral-900\">\n Comments\n </span>\n </div>\n <button\n onClick={() => setSidebarOpen(false)}\n className=\"p-1 rounded hover:bg-neutral-100 transition-colors\"\n >\n <X className=\"w-4 h-4 text-neutral-500\" />\n </button>\n </div>\n\n {/* Tabs */}\n <div className=\"flex border-b border-neutral-100\">\n <button\n onClick={() => setTab(\"page\")}\n className={`flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${\n tab === \"page\"\n ? \"border-b-2\"\n : \"text-neutral-400 hover:text-neutral-600\"\n }`}\n style={tab === \"page\" ? { color: brandColor, borderColor: brandColor } : undefined}\n >\n <FileText className=\"w-3 h-3\" />\n This Page\n {openThreads.length > 0 && (\n <span className=\"text-[10px] bg-red-50 text-red-600 px-1.5 py-px rounded-full\">\n {openThreads.length}\n </span>\n )}\n </button>\n <button\n onClick={() => setTab(\"all\")}\n className={`flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${\n tab === \"all\"\n ? \"border-b-2\"\n : \"text-neutral-400 hover:text-neutral-600\"\n }`}\n style={tab === \"all\" ? { color: brandColor, borderColor: brandColor } : undefined}\n >\n <Globe className=\"w-3 h-3\" />\n All Pages\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto\">\n {tab === \"page\" ? (\n <PageThreads\n threads={threads}\n openThreads={openThreads}\n resolvedThreads={resolvedThreads}\n onSelect={setActiveThreadId}\n onResolve={resolveThread}\n />\n ) : (\n <AllPagesView\n pages={allPages}\n loading={loadingAll}\n />\n )}\n </div>\n </div>\n );\n}\n\n// --- This Page tab ---\n\nfunction PageThreads({\n threads,\n openThreads,\n resolvedThreads,\n onSelect,\n onResolve,\n}: {\n threads: ApostilThread[];\n openThreads: ApostilThread[];\n resolvedThreads: ApostilThread[];\n onSelect: (id: string) => void;\n onResolve: (id: string) => void;\n}) {\n if (threads.length === 0) {\n return (\n <div className=\"p-6 text-center text-sm text-neutral-400\">\n No comments on this page.\n </div>\n );\n }\n\n return (\n <>\n {openThreads.length > 0 && (\n <div>\n <div className=\"px-4 py-2 text-[10px] font-semibold text-neutral-400 uppercase tracking-wider\">\n Open ({openThreads.length})\n </div>\n {openThreads.map((thread) => (\n <ThreadItem\n key={thread.id}\n thread={thread}\n onSelect={() => onSelect(thread.id)}\n onResolve={() => onResolve(thread.id)}\n />\n ))}\n </div>\n )}\n\n {resolvedThreads.length > 0 && (\n <div>\n <div className=\"px-4 py-2 text-[10px] font-semibold text-neutral-400 uppercase tracking-wider\">\n Resolved ({resolvedThreads.length})\n </div>\n {resolvedThreads.map((thread) => (\n <ThreadItem\n key={thread.id}\n thread={thread}\n onSelect={() => onSelect(thread.id)}\n onResolve={() => onResolve(thread.id)}\n resolved\n />\n ))}\n </div>\n )}\n </>\n );\n}\n\n// --- All Pages tab ---\n\nfunction AllPagesView({\n pages,\n loading,\n}: {\n pages: AllPagesData;\n loading: boolean;\n}) {\n if (loading) {\n return (\n <div className=\"p-6 text-center text-sm text-neutral-400\">\n Loading...\n </div>\n );\n }\n\n if (pages.length === 0) {\n return (\n <div className=\"p-6 text-center text-sm text-neutral-400\">\n No comments in this project yet.\n </div>\n );\n }\n\n const totalOpen = pages.reduce((s, p) => s + p.threads.filter((t) => !t.resolved).length, 0);\n\n return (\n <>\n {totalOpen > 0 && (\n <div className=\"px-4 py-2 text-[10px] font-semibold text-neutral-400 uppercase tracking-wider\">\n {totalOpen} open across {pages.length} pages\n </div>\n )}\n\n {pages.map((page) => {\n const open = page.threads.filter((t) => !t.resolved);\n const resolved = page.threads.filter((t) => t.resolved);\n const displayName = pageIdToDisplay(page.pageId);\n\n return (\n <div key={page.pageId} className=\"border-b border-neutral-50\">\n {/* Page header */}\n <div className=\"px-4 py-2.5 flex items-center justify-between bg-neutral-50/50\">\n <span className=\"text-xs font-semibold text-neutral-700 truncate\">\n {displayName}\n </span>\n <div className=\"flex items-center gap-1.5\">\n {open.length > 0 && (\n <span className=\"text-[10px] bg-red-50 text-red-600 px-1.5 py-px rounded-full font-medium\">\n {open.length}\n </span>\n )}\n {resolved.length > 0 && (\n <span className=\"text-[10px] bg-neutral-100 text-neutral-500 px-1.5 py-px rounded-full font-medium\">\n {resolved.length}\n </span>\n )}\n </div>\n </div>\n\n {/* Threads for this page */}\n {[...open, ...resolved].map((thread) => {\n const firstComment = thread.comments[0];\n if (!firstComment) return null;\n const isResolved = thread.resolved;\n\n return (\n <div\n key={thread.id}\n onClick={() => {\n // Navigate to the page with the comment hash\n const path = page.pageId === \"home\" ? \"/\" : \"/\" + page.pageId.replace(/--/g, \"/\");\n window.location.href = path + \"#apostil-\" + thread.id;\n }}\n className={`px-4 py-2.5 border-b border-neutral-50 cursor-pointer hover:bg-neutral-50 transition-colors ${\n isResolved ? \"opacity-50\" : \"\"\n }`}\n >\n <div className=\"flex items-center justify-between mb-1\">\n <div className=\"flex items-center gap-2\">\n <div\n className=\"w-4 h-4 rounded-full flex items-center justify-center text-white text-[8px] font-semibold\"\n style={{ backgroundColor: firstComment.author.color }}\n >\n {firstComment.author.name[0]?.toUpperCase()}\n </div>\n <span className=\"text-xs font-medium text-neutral-700\">\n {firstComment.author.name}\n </span>\n </div>\n <span className=\"text-[10px] text-neutral-400\">\n {timeAgo(firstComment.createdAt)}\n </span>\n </div>\n <p className=\"text-xs text-neutral-600 line-clamp-2 pl-6\">\n {firstComment.body}\n </p>\n {thread.comments.length > 1 && (\n <span className=\"text-[10px] text-neutral-400 pl-6\">\n {thread.comments.length - 1} {thread.comments.length - 1 === 1 ? \"reply\" : \"replies\"}\n </span>\n )}\n </div>\n );\n })}\n </div>\n );\n })}\n </>\n );\n}\n\n// --- Shared thread item ---\n\nfunction ThreadItem({\n thread,\n onSelect,\n onResolve,\n resolved,\n}: {\n thread: { id: string; targetLabel?: string; comments: { author: { name: string; color: string }; body: string; createdAt: string }[] };\n onSelect: () => void;\n onResolve: () => void;\n resolved?: boolean;\n}) {\n const firstComment = thread.comments[0];\n if (!firstComment) return null;\n\n return (\n <div\n onClick={onSelect}\n className={`px-4 py-3 border-b border-neutral-50 cursor-pointer hover:bg-neutral-50 transition-colors\n ${resolved ? \"opacity-60\" : \"\"}`}\n >\n <div className=\"flex items-center justify-between mb-1\">\n <div className=\"flex items-center gap-2\">\n <div\n className=\"w-4 h-4 rounded-full flex items-center justify-center text-white text-[8px] font-semibold\"\n style={{ backgroundColor: firstComment.author.color }}\n >\n {firstComment.author.name[0]?.toUpperCase()}\n </div>\n <span className=\"text-xs font-medium text-neutral-700\">\n {firstComment.author.name}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-neutral-400\">\n {timeAgo(firstComment.createdAt)}\n </span>\n <button\n onClick={(e) => {\n e.stopPropagation();\n onResolve();\n }}\n className=\"p-0.5 rounded hover:bg-neutral-200 transition-colors\"\n >\n {resolved ? (\n <Undo2 className=\"w-3 h-3 text-neutral-400\" />\n ) : (\n <Check className=\"w-3 h-3 text-emerald-600\" />\n )}\n </button>\n </div>\n </div>\n {thread.targetLabel && (\n <span className=\"inline-block text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium ml-6 mb-1\">\n {thread.targetLabel}\n </span>\n )}\n <p className=\"text-xs text-neutral-600 line-clamp-2 pl-6\">\n {firstComment.body}\n </p>\n {thread.comments.length > 1 && (\n <span className=\"text-[10px] text-neutral-400 pl-6\">\n {thread.comments.length - 1} {thread.comments.length - 1 === 1 ? \"reply\" : \"replies\"}\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","timeAgo"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/adapters/rest.ts","../src/utils.ts","../src/debug.ts","../src/hooks/use-comments.ts","../src/hooks/use-comment-mode.ts","../src/components/comment-overlay.tsx","../src/components/comment-pin.tsx","../src/components/comment-thread.tsx","../src/icons.tsx","../src/components/comment-composer.tsx","../src/components/user-prompt.tsx","../src/components/comment-toggle.tsx","../src/components/comment-sidebar.tsx"],"sourcesContent":["// Apostil — Pin-and-comment feedback overlay\n// https://github.com/batzorigco/apostil\n\n// Provider & hooks\nexport { ApostilProvider, useApostil } from \"./context\";\nexport { useComments } from \"./hooks/use-comments\";\nexport { useCommentMode } from \"./hooks/use-comment-mode\";\n\n// Components\nexport { CommentOverlay } from \"./components/comment-overlay\";\nexport { CommentToggle } from \"./components/comment-toggle\";\nexport { CommentSidebar } from \"./components/comment-sidebar\";\n\n// Debug\nexport { debug } from \"./debug\";\n\n// Types\nexport type {\n ApostilUser,\n ApostilComment,\n ApostilThread,\n ApostilStorage,\n} from \"./types\";\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useRef,\n type ReactNode,\n} from \"react\";\nimport type { ApostilThread, ApostilUser, ApostilStorage } from \"./types\";\nimport { createRestAdapter } from \"./adapters/rest\";\nimport { generateId, loadUser, saveUser, getRandomColor } from \"./utils\";\nimport { debug } from \"./debug\";\n\n// Stable default adapter (created once, not per render)\nconst defaultAdapter = createRestAdapter(\"/api/apostil\");\n\ntype ApostilContextValue = {\n threads: ApostilThread[];\n user: ApostilUser | null;\n commentMode: boolean;\n activeThreadId: string | null;\n sidebarOpen: boolean;\n brandColor: string;\n setCommentMode: (on: boolean) => void;\n setActiveThreadId: (id: string | null) => void;\n setSidebarOpen: (open: boolean) => void;\n addThread: (pinX: number, pinY: number, body: string, targetId?: string, targetLabel?: string) => void;\n addReply: (threadId: string, body: string) => void;\n resolveThread: (threadId: string) => void;\n deleteThread: (threadId: string) => void;\n setUser: (name: string) => void;\n unresolvedCount: number;\n};\n\nconst ApostilContext = createContext<ApostilContextValue | null>(null);\n\nexport function ApostilProvider({\n pageId,\n storage,\n brandColor = \"#171717\",\n children,\n}: {\n pageId: string;\n storage?: ApostilStorage;\n brandColor?: string;\n children: ReactNode;\n}) {\n const adapter = storage ?? defaultAdapter;\n const [threads, setThreads] = useState<ApostilThread[]>([]);\n const [user, setUserState] = useState<ApostilUser | null>(null);\n const [commentMode, setCommentMode] = useState(false);\n const [activeThreadId, setActiveThreadId] = useState<string | null>(null);\n const [sidebarOpen, setSidebarOpen] = useState(false);\n const [loaded, setLoaded] = useState(false);\n\n useEffect(() => {\n const saved = loadUser();\n if (saved) setUserState(saved);\n }, []);\n\n // Track current pageId to avoid saving stale data during transitions\n const pageIdRef = useRef(pageId);\n\n useEffect(() => {\n pageIdRef.current = pageId;\n setLoaded(false);\n hadThreadsRef.current = false;\n debug.log(\"loading threads for pageId:\", pageId);\n adapter.load(pageId).then((t) => {\n // Only apply if pageId hasn't changed during the fetch\n if (pageIdRef.current === pageId) {\n debug.log(\"loaded\", t.length, \"threads for\", pageId);\n setThreads(t);\n setLoaded(true);\n }\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pageId]);\n\n // Track whether we've ever had threads on this page (to know if empty means \"deleted all\")\n const hadThreadsRef = useRef(false);\n\n useEffect(() => {\n if (!loaded || pageIdRef.current !== pageId) return;\n // Save when there are threads, or when threads were cleared (deletion)\n if (threads.length > 0) {\n hadThreadsRef.current = true;\n debug.log(\"saving\", threads.length, \"threads for pageId:\", pageId);\n adapter.save(pageId, threads);\n } else if (hadThreadsRef.current) {\n debug.log(\"saving empty threads for pageId:\", pageId);\n adapter.save(pageId, threads);\n hadThreadsRef.current = false;\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [threads, pageId, loaded]);\n\n const setUser = useCallback((name: string) => {\n const u: ApostilUser = { id: generateId(), name, color: getRandomColor() };\n setUserState(u);\n saveUser(u);\n }, []);\n\n const addThread = useCallback(\n (pinX: number, pinY: number, body: string, targetId?: string, targetLabel?: string) => {\n if (!user) return;\n const threadId = generateId();\n const thread: ApostilThread = {\n id: threadId,\n pageId,\n pinX, pinY,\n targetId, targetLabel,\n resolved: false,\n createdAt: new Date().toISOString(),\n comments: [{\n id: generateId(), threadId, author: user, body,\n createdAt: new Date().toISOString(),\n }],\n };\n debug.log(\"new thread:\", { threadId, pinX, pinY, targetId, targetLabel, body });\n setThreads((prev) => [...prev, thread]);\n setActiveThreadId(threadId);\n setCommentMode(false);\n },\n [user, pageId]\n );\n\n const addReply = useCallback(\n (threadId: string, body: string) => {\n if (!user) return;\n setThreads((prev) =>\n prev.map((t) =>\n t.id === threadId\n ? { ...t, comments: [...t.comments, { id: generateId(), threadId, author: user, body, createdAt: new Date().toISOString() }] }\n : t\n )\n );\n },\n [user]\n );\n\n const resolveThread = useCallback((threadId: string) => {\n setThreads((prev) => prev.map((t) => t.id === threadId ? { ...t, resolved: !t.resolved } : t));\n setActiveThreadId(null);\n }, []);\n\n const deleteThread = useCallback((threadId: string) => {\n setThreads((prev) => prev.filter((t) => t.id !== threadId));\n setActiveThreadId(null);\n }, []);\n\n const unresolvedCount = threads.filter((t) => !t.resolved).length;\n\n return (\n <ApostilContext.Provider value={{\n threads, user, commentMode, activeThreadId, sidebarOpen, brandColor,\n setCommentMode, setActiveThreadId, setSidebarOpen,\n addThread, addReply, resolveThread, deleteThread, setUser,\n unresolvedCount,\n }}>\n {children}\n </ApostilContext.Provider>\n );\n}\n\nexport function useApostil() {\n const ctx = useContext(ApostilContext);\n if (!ctx) throw new Error(\"useApostil must be used within ApostilProvider\");\n return ctx;\n}\n","import type { ApostilStorage, ApostilThread } from \"../types\";\n\n/**\n * REST API storage adapter.\n * Works with any backend that implements GET/POST for threads.\n */\nexport function createRestAdapter(baseUrl: string): ApostilStorage {\n return {\n async load(pageId: string): Promise<ApostilThread[]> {\n const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;\n try {\n const res = await fetch(url);\n if (!res.ok) {\n console.warn(`[apostil] load failed: ${res.status} ${res.statusText} — ${url}`);\n return [];\n }\n const data = await res.json();\n return data;\n } catch (e) {\n console.warn(`[apostil] load error:`, e, `— ${url}`);\n return [];\n }\n },\n\n async save(pageId: string, threads: ApostilThread[]): Promise<void> {\n const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(threads),\n });\n if (!res.ok) {\n console.warn(`[apostil] save failed: ${res.status} ${res.statusText} — ${url}`);\n }\n } catch (e) {\n console.warn(`[apostil] save error:`, e, `— ${url}`);\n }\n },\n };\n}\n","let counter = 0;\n\nexport function generateId(): string {\n return `${Date.now()}-${++counter}-${Math.random().toString(36).slice(2, 7)}`;\n}\n\nconst USER_KEY = \"apostil-user\";\n\nexport function loadUser(): { id: string; name: string; color: string } | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(USER_KEY);\n return raw ? JSON.parse(raw) : null;\n } catch {\n return null;\n }\n}\n\nexport function saveUser(user: { id: string; name: string; color: string }) {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(USER_KEY, JSON.stringify(user));\n}\n\nconst USER_COLORS = [\n \"#df461c\", \"#2563eb\", \"#16a34a\", \"#9333ea\", \"#ea580c\",\n \"#0891b2\", \"#c026d3\", \"#4f46e5\", \"#059669\", \"#dc2626\",\n];\n\nexport function getRandomColor(): string {\n return USER_COLORS[Math.floor(Math.random() * USER_COLORS.length)];\n}\n","const PREFIX = \"[apostil]\";\n\nfunction isDebug(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return localStorage.getItem(\"apostil-debug\") === \"true\";\n } catch {\n return false;\n }\n}\n\nexport const debug = {\n log(...args: unknown[]) {\n if (isDebug()) console.log(PREFIX, ...args);\n },\n warn(...args: unknown[]) {\n if (isDebug()) console.warn(PREFIX, ...args);\n },\n error(...args: unknown[]) {\n console.error(PREFIX, ...args);\n },\n /** Enable/disable debug logging */\n enable() {\n if (typeof window !== \"undefined\") {\n localStorage.setItem(\"apostil-debug\", \"true\");\n console.log(PREFIX, \"debug logging enabled — reload to take effect\");\n }\n },\n disable() {\n if (typeof window !== \"undefined\") {\n localStorage.removeItem(\"apostil-debug\");\n console.log(PREFIX, \"debug logging disabled\");\n }\n },\n};\n\n// Expose globally for easy toggling from console\nif (typeof window !== \"undefined\") {\n (window as unknown as Record<string, unknown>).__apostil_debug = debug;\n}\n","\"use client\";\n\nimport { useApostil } from \"../context\";\n\nexport function useComments() {\n const { threads, addThread, addReply, resolveThread, deleteThread, unresolvedCount } = useApostil();\n\n return {\n threads,\n openThreads: threads.filter((t) => !t.resolved),\n resolvedThreads: threads.filter((t) => t.resolved),\n addThread,\n addReply,\n resolveThread,\n deleteThread,\n unresolvedCount,\n };\n}\n","\"use client\";\n\nimport { useApostil } from \"../context\";\n\nexport function useCommentMode() {\n const { commentMode, setCommentMode, sidebarOpen, setSidebarOpen } = useApostil();\n\n return {\n commentMode,\n setCommentMode,\n toggleCommentMode: () => setCommentMode(!commentMode),\n sidebarOpen,\n setSidebarOpen,\n toggleSidebar: () => setSidebarOpen(!sidebarOpen),\n };\n}\n","\"use client\";\n\nimport { useState, useCallback, useRef, useEffect } from \"react\";\nimport { useApostil } from \"../context\";\nimport { debug } from \"../debug\";\nimport { CommentPin } from \"./comment-pin\";\nimport { ApostilThreadPopover } from \"./comment-thread\";\nimport { CommentComposer } from \"./comment-composer\";\nimport { UserPrompt } from \"./user-prompt\";\n\ntype PendingPin = {\n x: number;\n y: number;\n targetId?: string;\n targetLabel?: string;\n};\n\n// Find the highest z-index on the page (cached per toggle)\nlet cachedHighZ = 0;\nlet cacheTimestamp = 0;\nfunction getHighestZIndex(): number {\n const now = Date.now();\n if (now - cacheTimestamp < 500) return cachedHighZ; // cache for 500ms\n let max = 0;\n const els = document.querySelectorAll(\"[style*='z-index'], [class*='z-']\");\n for (let i = 0; i < els.length; i++) {\n const z = parseInt(getComputedStyle(els[i]).zIndex, 10);\n if (!isNaN(z) && z > max) max = z;\n }\n cachedHighZ = Math.max(max, 100); // minimum 100\n cacheTimestamp = now;\n return cachedHighZ;\n}\n\n// Semantic elements that are meaningful containers\nconst SEMANTIC_TAGS = new Set([\n \"SECTION\", \"NAV\", \"ASIDE\", \"HEADER\", \"FOOTER\", \"MAIN\",\n \"ARTICLE\", \"FORM\", \"DIALOG\", \"DETAILS\",\n]);\n\n// Min size for a target, and max size ratio to viewport to avoid matching the whole page\nconst MIN_TARGET_SIZE = 50;\nconst MAX_VIEWPORT_RATIO = 0.85; // skip elements covering >85% of viewport in both dimensions\n\n/**\n * Check if a div is a \"visual panel\" — a scrollable area, flex/grid child with defined bounds,\n * or a container with border/background that forms a distinct visual region.\n */\nfunction isVisualPanel(el: HTMLElement): boolean {\n const style = getComputedStyle(el);\n\n // Scrollable container\n if (/auto|scroll/.test(style.overflow + style.overflowY + style.overflowX)) return true;\n\n // Has border or distinct background (likely a card/panel)\n if (style.borderWidth && style.borderWidth !== \"0px\" && style.borderStyle !== \"none\") return true;\n if (style.borderRadius && style.borderRadius !== \"0px\") return true;\n if (style.backgroundColor && style.backgroundColor !== \"rgba(0, 0, 0, 0)\" && style.backgroundColor !== \"transparent\") return true;\n if (style.boxShadow && style.boxShadow !== \"none\") return true;\n\n return false;\n}\n\n/**\n * Try to get a readable label from an element's content.\n * Looks for headings, strong text, or first significant text node nearby.\n */\nfunction inferLabel(el: HTMLElement): string | null {\n // Check for a heading child\n const heading = el.querySelector(\"h1, h2, h3, h4, h5, h6, [class*='title'], [class*='heading']\");\n if (heading) {\n const text = heading.textContent?.trim();\n if (text && text.length <= 40) return text;\n }\n\n // Check for a label-like first child text\n const firstText = el.querySelector(\"span, p, label, strong\");\n if (firstText) {\n const text = firstText.textContent?.trim();\n if (text && text.length <= 30) return text;\n }\n\n return null;\n}\n\n/**\n * Build a stable CSS selector path for an element.\n */\nfunction getElementId(el: HTMLElement): string {\n const manual = el.getAttribute(\"data-comment-target\");\n if (manual) return manual;\n\n if (el.id) return `#${el.id}`;\n\n const label = el.getAttribute(\"aria-label\");\n if (label) return `${el.tagName.toLowerCase()}[aria-label=\"${label}\"]`;\n\n // nth-child path from nearest identifiable ancestor\n const parts: string[] = [];\n let cur: HTMLElement | null = el;\n for (let depth = 0; cur && depth < 5; depth++) {\n if (cur.id) {\n parts.unshift(`#${cur.id}`);\n break;\n }\n const p: HTMLElement | null = cur.parentElement;\n if (p) {\n const siblings = Array.from(p.children);\n const idx = siblings.indexOf(cur);\n parts.unshift(`${cur.tagName.toLowerCase()}:nth-child(${idx + 1})`);\n } else {\n parts.unshift(cur.tagName.toLowerCase());\n }\n cur = p;\n }\n return parts.join(\" > \");\n}\n\n/**\n * Derive a human-readable label for an element.\n */\nfunction getElementLabel(el: HTMLElement): string {\n const manual = el.getAttribute(\"data-comment-label\");\n if (manual) return manual;\n\n const ariaLabel = el.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n // Try to infer from content\n const inferred = inferLabel(el);\n if (inferred) return inferred;\n\n const role = el.getAttribute(\"role\");\n\n if (el.id) {\n const pretty = el.id\n .replace(/[-_]/g, \" \")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n return role ? `${pretty} (${role})` : pretty;\n }\n\n const tag = el.tagName.toLowerCase();\n if (SEMANTIC_TAGS.has(el.tagName)) return role ? `${tag} (${role})` : tag;\n if (role) return role;\n\n return tag;\n}\n\n/**\n * Walk up from the clicked element and find the best container to anchor the comment to.\n * Scores candidates by specificity — prefers the innermost meaningful panel.\n */\nfunction findCommentTarget(el: HTMLElement, boundary: HTMLElement | null) {\n let current: HTMLElement | null = el;\n const candidates: { el: HTMLElement; score: number; depth: number }[] = [];\n let depth = 0;\n\n while (current && current !== boundary && current !== document.body) {\n const rect = current.getBoundingClientRect();\n const isBigEnough = rect.width >= MIN_TARGET_SIZE && rect.height >= MIN_TARGET_SIZE;\n const isTooBig =\n rect.width > window.innerWidth * MAX_VIEWPORT_RATIO &&\n rect.height > window.innerHeight * MAX_VIEWPORT_RATIO;\n\n if (isBigEnough && !isTooBig) {\n let score = 0;\n\n // Manual target — highest priority\n if (current.getAttribute(\"data-comment-target\")) score = 100;\n // id\n else if (current.id) score = 80;\n // aria-label\n else if (current.getAttribute(\"aria-label\")) score = 70;\n // role attribute\n else if (current.getAttribute(\"role\")) score = 60;\n // semantic HTML tag\n else if (SEMANTIC_TAGS.has(current.tagName)) score = 50;\n // Visual panel (scrollable, bordered, shadowed, rounded)\n else if (isVisualPanel(current)) score = 40;\n\n if (score > 0) {\n candidates.push({ el: current, score, depth });\n }\n }\n\n current = current.parentElement;\n depth++;\n }\n\n if (candidates.length === 0) return null;\n\n // Prefer innermost among equally-scored candidates.\n // Among different scores: higher score wins, but add a bonus for being closer to the click.\n candidates.sort((a, b) => {\n const scoreA = a.score + Math.max(0, 10 - a.depth);\n const scoreB = b.score + Math.max(0, 10 - b.depth);\n return scoreB - scoreA;\n });\n\n const best = candidates[0];\n const targetId = getElementId(best.el);\n const targetLabel = getElementLabel(best.el);\n\n debug.log(\" candidates:\", candidates.map((c) => ({\n tag: c.el.tagName.toLowerCase(),\n id: c.el.id || undefined,\n class: c.el.className?.toString().slice(0, 60) || undefined,\n score: c.score,\n depth: c.depth,\n label: getElementLabel(c.el),\n })));\n\n return {\n targetId,\n targetLabel,\n element: best.el,\n };\n}\n\nexport function CommentOverlay() {\n const { threads, commentMode, setCommentMode, user, addThread, activeThreadId, setActiveThreadId, brandColor } =\n useApostil();\n const overlayRef = useRef<HTMLDivElement>(null);\n const [pendingPin, setPendingPin] = useState<PendingPin | null>(null);\n const [pendingPixel, setPendingPixel] = useState<{ left: number; top: number } | null>(null);\n const pendingRef = useRef<HTMLDivElement>(null);\n const [pendingFlip, setPendingFlip] = useState<{ x: boolean; y: boolean }>({ x: false, y: false });\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (!commentMode || !overlayRef.current) return;\n\n const overlayRect = overlayRef.current.getBoundingClientRect();\n\n // Find element below overlay using elementsFromPoint — skip all apostil elements\n const overlay = overlayRef.current;\n const elements = document.elementsFromPoint(e.clientX, e.clientY);\n let elementBelow: HTMLElement | null = null;\n for (const el of elements) {\n if (el === overlay || overlay.contains(el)) continue;\n if (el instanceof HTMLElement) {\n elementBelow = el;\n break;\n }\n }\n\n debug.log(\" click at\", { clientX: e.clientX, clientY: e.clientY });\n debug.log(\" element below overlay:\", elementBelow);\n\n // Log the DOM path for debugging\n if (elementBelow) {\n const path: string[] = [];\n let walk: HTMLElement | null = elementBelow;\n while (walk && walk !== document.body) {\n const attrs: string[] = [walk.tagName.toLowerCase()];\n if (walk.id) attrs.push(`#${walk.id}`);\n if (walk.getAttribute(\"data-comment-target\")) attrs.push(`[data-comment-target=\"${walk.getAttribute(\"data-comment-target\")}\"]`);\n if (walk.getAttribute(\"aria-label\")) attrs.push(`[aria-label=\"${walk.getAttribute(\"aria-label\")}\"]`);\n if (walk.getAttribute(\"role\")) attrs.push(`[role=\"${walk.getAttribute(\"role\")}\"]`);\n if (SEMANTIC_TAGS.has(walk.tagName)) attrs.push(\"(semantic)\");\n path.push(attrs.join(\"\"));\n walk = walk.parentElement;\n }\n debug.log(\" DOM path:\", path.join(\" → \"));\n }\n\n const target = elementBelow ? findCommentTarget(elementBelow, overlayRef.current) : null;\n\n if (target) {\n const targetRect = target.element.getBoundingClientRect();\n const x = ((e.clientX - targetRect.left) / targetRect.width) * 100;\n const y = ((e.clientY - targetRect.top) / targetRect.height) * 100;\n debug.log(\" ✅ target found:\", {\n targetId: target.targetId,\n targetLabel: target.targetLabel,\n element: target.element,\n relativePos: { x: x.toFixed(1), y: y.toFixed(1) },\n targetRect: { w: targetRect.width, h: targetRect.height },\n });\n setPendingPin({ x, y, targetId: target.targetId, targetLabel: target.targetLabel });\n } else {\n const x = ((e.clientX - overlayRect.left) / overlayRect.width) * 100;\n const y = ((e.clientY - overlayRect.top) / overlayRect.height) * 100;\n debug.log(\" ⚠️ no target found, using overlay-relative position:\", {\n x: x.toFixed(1),\n y: y.toFixed(1),\n });\n setPendingPin({ x, y });\n }\n\n setPendingPixel({\n left: e.clientX - overlayRect.left,\n top: e.clientY - overlayRect.top,\n });\n setActiveThreadId(null);\n },\n [commentMode, setActiveThreadId]\n );\n\n const handleNewComment = useCallback(\n (body: string) => {\n if (!pendingPin) return;\n debug.log(\" saving thread:\", {\n pinX: pendingPin.x.toFixed(1),\n pinY: pendingPin.y.toFixed(1),\n targetId: pendingPin.targetId ?? \"(none)\",\n targetLabel: pendingPin.targetLabel ?? \"(none)\",\n body,\n });\n addThread(pendingPin.x, pendingPin.y, body, pendingPin.targetId, pendingPin.targetLabel);\n setPendingPin(null);\n setPendingPixel(null);\n },\n [pendingPin, addThread]\n );\n\n const cancelPending = useCallback(() => {\n setPendingPin(null);\n setPendingPixel(null);\n setCommentMode(false);\n }, [setCommentMode]);\n\n // Calculate flip based on click position relative to viewport\n useEffect(() => {\n if (!pendingPixel || !overlayRef.current) return;\n const overlayRect = overlayRef.current.getBoundingClientRect();\n const clickX = overlayRect.left + pendingPixel.left;\n const clickY = overlayRect.top + pendingPixel.top;\n // Popover is w-72 (288px) + 20px margin, ~200px tall\n setPendingFlip({\n x: clickX + 308 > window.innerWidth,\n y: clickY + 200 > window.innerHeight,\n });\n }, [pendingPixel, overlayRef]);\n\n // Open thread from URL hash (e.g. #apostil-threadId)\n useEffect(() => {\n const hash = window.location.hash;\n console.log(\"[apostil] hash check:\", hash, \"threads:\", threads.length, threads.map(t => t.id));\n if (!hash.startsWith(\"#apostil-\")) return;\n const threadId = hash.replace(\"#apostil-\", \"\");\n console.log(\"[apostil] looking for thread:\", threadId);\n // Wait for threads to load before activating\n if (threads.length === 0) {\n console.log(\"[apostil] no threads loaded yet, waiting...\");\n return;\n }\n const found = threads.find((t) => t.id === threadId);\n console.log(\"[apostil] found thread:\", found ? \"yes\" : \"no\");\n if (found) {\n setActiveThreadId(threadId);\n // Clean hash from URL without triggering navigation\n window.history.replaceState(null, \"\", window.location.pathname);\n }\n }, [threads, setActiveThreadId]);\n\n // Keyboard shortcuts\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n const tag = (e.target as HTMLElement)?.tagName;\n\n // Escape always works — even when typing\n if (e.key === \"Escape\") {\n if (pendingPin) {\n setPendingPin(null);\n setPendingPixel(null);\n setCommentMode(false);\n } else if (activeThreadId) {\n setActiveThreadId(null);\n } else if (commentMode) {\n setCommentMode(false);\n }\n return;\n }\n\n // Other shortcuts only when not typing, and not with modifier keys (Cmd+C, Ctrl+C, etc.)\n if (tag === \"INPUT\" || tag === \"TEXTAREA\") return;\n if (e.metaKey || e.ctrlKey || e.altKey) return;\n\n if (e.key === \"c\" || e.key === \"C\") {\n e.preventDefault(); // prevent \"c\" from leaking into any input that gains focus\n if (!commentMode) {\n debug.log(\" comment mode ON\");\n setActiveThreadId(null);\n setCommentMode(true);\n } else {\n debug.log(\" comment mode OFF\");\n setCommentMode(false);\n }\n }\n };\n document.addEventListener(\"keydown\", handler);\n return () => document.removeEventListener(\"keydown\", handler);\n }, [commentMode, pendingPin, activeThreadId, setCommentMode, setActiveThreadId]);\n\n // Only show unresolved threads as pins — resolved ones live in the sidebar\n const visibleThreads = threads\n .filter((t) => !t.resolved)\n .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());\n\n // When user prompt is showing, disable the overlay click handling\n const showingUserPrompt = commentMode && !user;\n\n return (\n <>\n {/* Overlay layer */}\n <div\n ref={overlayRef}\n className={`fixed inset-0 ${\n commentMode && !showingUserPrompt\n ? \"cursor-crosshair pointer-events-auto\"\n : \"pointer-events-none\"\n }`}\n style={{ zIndex: commentMode ? getHighestZIndex() + 10 : 55 }}\n onMouseDown={handleClick}\n >\n {/* Existing pins */}\n {visibleThreads.map((thread, i) => (\n <div key={thread.id} className=\"pointer-events-auto\">\n <CommentPin thread={thread} index={i} overlayRef={overlayRef} />\n <ApostilThreadPopover thread={thread} overlayRef={overlayRef} />\n </div>\n ))}\n\n {/* Pending pin */}\n {pendingPin && pendingPixel && user && (\n <div\n className=\"absolute z-[70] pointer-events-auto\"\n style={{\n left: pendingPixel.left,\n top: pendingPixel.top,\n }}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n className=\"absolute -translate-x-1/2 -translate-y-1/2 w-7 h-7 rounded-full\n flex items-center justify-center text-white text-xs font-semibold\n shadow-lg ring-2 ring-white ring-offset-2 animate-bounce\"\n style={{ backgroundColor: user.color }}\n >\n +\n </div>\n <div\n ref={pendingRef}\n className=\"absolute w-72\"\n style={{\n marginLeft: pendingFlip.x ? -308 : 20,\n marginTop: pendingFlip.y ? undefined : -12,\n ...(pendingFlip.y ? { bottom: 0 } : {}),\n }}\n >\n <div className=\"bg-white rounded-xl shadow-2xl border border-neutral-200 p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <p className=\"text-xs text-neutral-500\">New comment</p>\n {pendingPin.targetLabel && (\n <span className=\"text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium\">\n {pendingPin.targetLabel}\n </span>\n )}\n </div>\n <CommentComposer\n onSubmit={handleNewComment}\n placeholder=\"What's on your mind?\"\n autoFocus\n />\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Comment mode hint */}\n {commentMode && !pendingPin && !showingUserPrompt && (\n <div className=\"absolute bottom-6 left-1/2 -translate-x-1/2 z-[60] pointer-events-none\">\n <div\n className=\"text-white text-sm px-4 py-2 rounded-full backdrop-blur-sm\"\n style={{ backgroundColor: `color-mix(in oklab, ${brandColor} 80%, transparent)` }}\n >\n Click anywhere to add a comment\n </div>\n </div>\n )}\n\n {/* User prompt — rendered above overlay so clicks work */}\n <UserPrompt />\n </>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect, useCallback, useRef, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useApostil } from \"../context\";\nimport type { ApostilThread } from \"../types\";\n\n/**\n * Find the target element using the stored targetId.\n * targetId can be a CSS selector or a data-comment-target value.\n */\nfunction findTargetElement(targetId: string): HTMLElement | null {\n // First try as a CSS selector\n try {\n const el = document.querySelector(targetId);\n if (el instanceof HTMLElement) return el;\n } catch {\n // Invalid selector — fall through\n }\n\n // Then try as a data-comment-target value\n try {\n const el = document.querySelector(`[data-comment-target=\"${CSS.escape(targetId)}\"]`);\n if (el instanceof HTMLElement) return el;\n } catch {\n // noop\n }\n\n return null;\n}\n\n/**\n * Resolves pixel position for a thread pin relative to the overlay.\n * Used only for NON-targeted pins (no targetId).\n */\nfunction resolveOverlayPosition(\n thread: ApostilThread,\n overlayEl: HTMLElement | null\n): { left: number; top: number } | null {\n if (!overlayEl) return null;\n const overlayRect = overlayEl.getBoundingClientRect();\n return {\n left: (thread.pinX / 100) * overlayRect.width,\n top: (thread.pinY / 100) * overlayRect.height,\n };\n}\n\n/**\n * Resolves pixel position relative to the overlay, for targeted pins.\n * Used by the thread popover which always renders in the overlay.\n */\nfunction resolvePosition(\n thread: ApostilThread,\n overlayEl: HTMLElement | null\n): { left: number; top: number } | null {\n if (!overlayEl) return null;\n const overlayRect = overlayEl.getBoundingClientRect();\n\n if (thread.targetId) {\n const target = findTargetElement(thread.targetId);\n if (target) {\n const targetRect = target.getBoundingClientRect();\n return {\n left: targetRect.left - overlayRect.left + (thread.pinX / 100) * targetRect.width,\n top: targetRect.top - overlayRect.top + (thread.pinY / 100) * targetRect.height,\n };\n }\n return null;\n }\n\n return {\n left: (thread.pinX / 100) * overlayRect.width,\n top: (thread.pinY / 100) * overlayRect.height,\n };\n}\n\n// ─── Pin button (shared rendering) ────────────────────────────────\n\nfunction PinButton({\n thread,\n index,\n isActive,\n onClick,\n}: {\n thread: ApostilThread;\n index: number;\n isActive: boolean;\n onClick: (e: React.MouseEvent) => void;\n}) {\n const authorColor = thread.comments[0]?.author.color ?? \"#df461c\";\n const [hovered, setHovered] = useState(false);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [tooltipPos, setTooltipPos] = useState<{ left: number; top: number } | null>(null);\n\n useEffect(() => {\n if (!hovered || !buttonRef.current) {\n setTooltipPos(null);\n return;\n }\n const rect = buttonRef.current.getBoundingClientRect();\n setTooltipPos({\n left: rect.left + rect.width / 2,\n top: rect.bottom + 4,\n });\n }, [hovered]);\n\n return (\n <>\n <button\n ref={buttonRef}\n onClick={onClick}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n style={{ position: \"relative\" }}\n >\n <div\n className={`\n flex items-center justify-center\n w-7 h-7 rounded-full text-white text-xs font-semibold\n shadow-lg cursor-pointer\n transition-all duration-200\n ${isActive ? \"scale-125 ring-2 ring-white ring-offset-2\" : \"hover:scale-110\"}\n ${thread.resolved ? \"opacity-40\" : \"\"}\n `}\n style={{ backgroundColor: authorColor }}\n >\n {index + 1}\n </div>\n {!thread.resolved && !isActive && (\n <div\n className=\"absolute inset-0 rounded-full animate-ping opacity-20\"\n style={{ backgroundColor: authorColor }}\n />\n )}\n </button>\n {thread.targetLabel && hovered && tooltipPos && createPortal(\n <div\n className=\"fixed whitespace-nowrap text-[10px] bg-neutral-800 text-white px-1.5 py-0.5 rounded pointer-events-none\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n transform: \"translateX(-50%)\",\n zIndex: 999999,\n }}\n >\n {thread.targetLabel}\n </div>,\n document.body\n )}\n </>\n );\n}\n\n// ─── Targeted pin (portals into the target element) ───────────────\n\nfunction TargetedPin({\n thread,\n index,\n}: {\n thread: ApostilThread;\n index: number;\n}) {\n const { activeThreadId, setActiveThreadId } = useApostil();\n const isActive = activeThreadId === thread.id;\n const [targetEl, setTargetEl] = useState<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!thread.targetId) return;\n\n function tryFind() {\n const el = findTargetElement(thread.targetId!);\n if (el) {\n const pos = getComputedStyle(el).position;\n if (pos === \"static\") el.style.position = \"relative\";\n setTargetEl(el);\n } else {\n setTargetEl(null);\n }\n }\n\n tryFind();\n\n // Watch for DOM changes — target element may appear/disappear (popovers, dialogs)\n const observer = new MutationObserver(() => tryFind());\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => observer.disconnect();\n }, [thread.targetId]);\n\n if (!targetEl) return null;\n\n return createPortal(\n <div\n className=\"absolute pointer-events-auto\"\n style={{\n left: `${thread.pinX}%`,\n top: `${thread.pinY}%`,\n transform: \"translate(-50%, -50%)\",\n zIndex: 10,\n }}\n >\n <PinButton\n thread={thread}\n index={index}\n isActive={isActive}\n onClick={(e) => {\n e.stopPropagation();\n setActiveThreadId(isActive ? null : thread.id);\n }}\n />\n </div>,\n targetEl\n );\n}\n\n// ─── Overlay pin (non-targeted, positioned in the overlay) ────────\n\nfunction OverlayPin({\n thread,\n index,\n overlayRef,\n}: {\n thread: ApostilThread;\n index: number;\n overlayRef: RefObject<HTMLDivElement | null>;\n}) {\n const { activeThreadId, setActiveThreadId } = useApostil();\n const isActive = activeThreadId === thread.id;\n const [pos, setPos] = useState<{ left: number; top: number } | null>(null);\n\n const updatePos = useCallback(() => {\n setPos(resolveOverlayPosition(thread, overlayRef.current));\n }, [thread, overlayRef]);\n\n useEffect(() => {\n updatePos();\n window.addEventListener(\"resize\", updatePos);\n document.addEventListener(\"scroll\", updatePos, true);\n return () => {\n window.removeEventListener(\"resize\", updatePos);\n document.removeEventListener(\"scroll\", updatePos, true);\n };\n }, [updatePos]);\n\n if (!pos) return null;\n\n return (\n <div\n className=\"absolute pointer-events-auto\"\n style={{\n left: pos.left,\n top: pos.top,\n transform: \"translate(-50%, -50%)\",\n zIndex: 60,\n }}\n >\n <PinButton\n thread={thread}\n index={index}\n isActive={isActive}\n onClick={(e) => {\n e.stopPropagation();\n setActiveThreadId(isActive ? null : thread.id);\n }}\n />\n </div>\n );\n}\n\n// ─── Public CommentPin (delegates to targeted or overlay) ─────────\n\nexport function CommentPin({\n thread,\n index,\n overlayRef,\n}: {\n thread: ApostilThread;\n index: number;\n overlayRef: RefObject<HTMLDivElement | null>;\n}) {\n if (thread.resolved) return null;\n if (thread.targetId) {\n return <TargetedPin key={`targeted-${thread.id}`} thread={thread} index={index} />;\n }\n return <OverlayPin key={`overlay-${thread.id}`} thread={thread} index={index} overlayRef={overlayRef} />;\n}\n\nexport { resolvePosition, findTargetElement };\n","\"use client\";\n\nimport { useEffect, useRef, useState, useCallback, type RefObject } from \"react\";\nimport { Check, Trash2, Undo2 } from \"../icons\";\nimport { useApostil } from \"../context\";\nimport { CommentComposer } from \"./comment-composer\";\nimport { resolvePosition } from \"./comment-pin\";\nimport type { ApostilThread as ApostilThreadType } from \"../types\";\n\nfunction timeAgo(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return \"just now\";\n if (mins < 60) return `${mins}m ago`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n\nexport function ApostilThreadPopover({\n thread,\n overlayRef,\n}: {\n thread: ApostilThreadType;\n overlayRef: RefObject<HTMLDivElement | null>;\n}) {\n const { activeThreadId, setActiveThreadId, addReply, resolveThread, deleteThread, user } =\n useApostil();\n const ref = useRef<HTMLDivElement>(null);\n const isOpen = activeThreadId === thread.id;\n\n const [pos, setPos] = useState<{ left: number; top: number } | null>(null);\n const [flip, setFlip] = useState<{ x: boolean; y: boolean }>({ x: false, y: false });\n\n const updatePos = useCallback(() => {\n setPos(resolvePosition(thread, overlayRef.current));\n }, [thread, overlayRef]);\n\n // Check if popover overflows viewport and flip accordingly\n useEffect(() => {\n if (!isOpen || !pos || !ref.current) return;\n const rect = ref.current.getBoundingClientRect();\n const flipX = rect.right > window.innerWidth;\n const flipY = rect.bottom > window.innerHeight;\n setFlip({ x: flipX, y: flipY });\n }, [isOpen, pos]);\n\n useEffect(() => {\n if (!isOpen) return;\n updatePos();\n window.addEventListener(\"resize\", updatePos);\n // Track scroll on any container so popover follows the pin\n document.addEventListener(\"scroll\", updatePos, true);\n return () => {\n window.removeEventListener(\"resize\", updatePos);\n document.removeEventListener(\"scroll\", updatePos, true);\n };\n }, [isOpen, updatePos]);\n\n useEffect(() => {\n if (!isOpen) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setActiveThreadId(null);\n }\n };\n const timer = setTimeout(() => document.addEventListener(\"mousedown\", handler), 0);\n return () => {\n clearTimeout(timer);\n document.removeEventListener(\"mousedown\", handler);\n };\n }, [isOpen, setActiveThreadId]);\n\n if (!isOpen || !pos) return null;\n\n return (\n <div\n ref={ref}\n className=\"absolute z-[70]\"\n style={{\n left: pos.left,\n top: pos.top,\n marginLeft: flip.x ? -340 : 20,\n marginTop: flip.y ? -12 : -12,\n ...(flip.y ? { transform: \"translateY(-100%)\" } : {}),\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"w-80 bg-white rounded-xl shadow-2xl border border-neutral-200 overflow-hidden\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-2.5 border-b border-neutral-100 bg-neutral-50\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-neutral-500\">\n {thread.comments.length} {thread.comments.length === 1 ? \"comment\" : \"comments\"}\n </span>\n {thread.targetLabel && (\n <span className=\"text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium\">\n {thread.targetLabel}\n </span>\n )}\n {thread.resolved && (\n <span className=\"text-[10px] text-emerald-600 font-medium\">Resolved</span>\n )}\n </div>\n <div className=\"flex gap-1\">\n <button\n onClick={() => resolveThread(thread.id)}\n className=\"p-1 rounded hover:bg-neutral-200 transition-colors\"\n title={thread.resolved ? \"Reopen\" : \"Resolve\"}\n >\n {thread.resolved ? (\n <Undo2 className=\"w-3.5 h-3.5 text-neutral-500\" />\n ) : (\n <Check className=\"w-3.5 h-3.5 text-emerald-600\" />\n )}\n </button>\n <button\n onClick={() => deleteThread(thread.id)}\n className=\"p-1 rounded hover:bg-red-50 transition-colors\"\n title=\"Delete thread\"\n >\n <Trash2 className=\"w-3.5 h-3.5 text-neutral-400 hover:text-red-500\" />\n </button>\n </div>\n </div>\n\n {/* Comments */}\n <div className=\"max-h-64 overflow-y-auto\">\n {thread.comments.map((comment) => (\n <div key={comment.id} className=\"px-4 py-3 border-b border-neutral-50 last:border-0\">\n <div className=\"flex items-center gap-2 mb-1\">\n <div\n className=\"w-5 h-5 rounded-full flex items-center justify-center text-white text-[10px] font-semibold shrink-0\"\n style={{ backgroundColor: comment.author.color }}\n >\n {comment.author.name[0]?.toUpperCase()}\n </div>\n <span className=\"text-xs font-medium text-neutral-800\">\n {comment.author.name}\n </span>\n <span className=\"text-[10px] text-neutral-400 ml-auto\">\n {timeAgo(comment.createdAt)}\n </span>\n </div>\n <p className=\"text-sm text-neutral-700 leading-relaxed pl-7\">\n {comment.body}\n </p>\n </div>\n ))}\n </div>\n\n {/* Reply */}\n {user && !thread.resolved && (\n <div className=\"px-3 py-2.5 border-t border-neutral-100 bg-neutral-50/50\">\n <CommentComposer\n onSubmit={(body) => addReply(thread.id, body)}\n placeholder=\"Reply...\"\n autoFocus\n />\n </div>\n )}\n </div>\n </div>\n );\n}\n","import React from \"react\";\n\ntype IconProps = { className?: string };\n\nexport function Send({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m22 2-7 20-4-9-9-4Z\" />\n <path d=\"M22 2 11 13\" />\n </svg>\n );\n}\n\nexport function MessageSquare({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nexport function List({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"8\" x2=\"21\" y1=\"6\" y2=\"6\" />\n <line x1=\"8\" x2=\"21\" y1=\"12\" y2=\"12\" />\n <line x1=\"8\" x2=\"21\" y1=\"18\" y2=\"18\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"6\" y2=\"6\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"12\" y2=\"12\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\nexport function X({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\nexport function Check({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\nexport function Undo2({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M9 14 4 9l5-5\" />\n <path d=\"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11\" />\n </svg>\n );\n}\n\nexport function Globe({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\" />\n <path d=\"M2 12h20\" />\n </svg>\n );\n}\n\nexport function FileText({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 9H8\" />\n <path d=\"M16 13H8\" />\n <path d=\"M16 17H8\" />\n </svg>\n );\n}\n\nexport function Trash2({ className = \"w-4 h-4\" }: IconProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" />\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\n <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\" />\n <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\" />\n </svg>\n );\n}\n","\"use client\";\n\nimport { useState, useRef, useEffect } from \"react\";\nimport { Send } from \"../icons\";\nimport { useApostil } from \"../context\";\n\nexport function CommentComposer({\n onSubmit,\n placeholder = \"Add a comment...\",\n autoFocus = false,\n}: {\n onSubmit: (body: string) => void;\n placeholder?: string;\n autoFocus?: boolean;\n}) {\n const { brandColor } = useApostil();\n const [value, setValue] = useState(\"\");\n const inputRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n if (autoFocus) {\n // Delay focus past the full click cycle (mousedown → mouseup → click)\n const timer = setTimeout(() => {\n inputRef.current?.focus();\n }, 50);\n return () => clearTimeout(timer);\n }\n }, [autoFocus]);\n\n const handleSubmit = () => {\n const trimmed = value.trim();\n if (!trimmed) return;\n onSubmit(trimmed);\n setValue(\"\");\n };\n\n return (\n <div className=\"flex gap-2 items-end\">\n <textarea\n ref={inputRef}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n }}\n placeholder={placeholder}\n rows={1}\n className=\"flex-1 resize-none rounded-lg border border-neutral-200 bg-white px-3 py-1.5 text-sm\n placeholder:text-neutral-400 focus:outline-none focus:ring-2 focus:ring-neutral-300\n min-h-[36px] max-h-[120px]\"\n />\n <button\n onClick={handleSubmit}\n disabled={!value.trim()}\n className=\"flex items-center justify-center w-9 h-9 rounded-lg\n text-white disabled:opacity-30\n transition-colors shrink-0\"\n style={{ backgroundColor: brandColor }}\n >\n <Send className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { useApostil } from \"../context\";\n\nexport function UserPrompt() {\n const { user, setUser, commentMode, brandColor } = useApostil();\n const [name, setName] = useState(\"\");\n\n // Only show when comment mode is activated and no user is set\n if (user || !commentMode) return null;\n\n const handleSubmit = () => {\n const trimmed = name.trim();\n if (!trimmed) return;\n setUser(trimmed);\n };\n\n return (\n <div\n className=\"fixed inset-0 flex items-center justify-center bg-black/20 backdrop-blur-[2px]\"\n style={{ zIndex: 99999 }}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"bg-white rounded-xl shadow-2xl border border-neutral-200 p-6 w-80\">\n <h3 className=\"text-sm font-semibold text-neutral-900 mb-1\">\n What&apos;s your name?\n </h3>\n <p className=\"text-xs text-neutral-500 mb-4\">\n This will be shown with your comments.\n </p>\n <input\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSubmit();\n }}\n placeholder=\"Enter your name\"\n autoFocus\n className=\"w-full rounded-lg border border-neutral-200 px-3 py-2 text-sm\n placeholder:text-neutral-400 focus:outline-none focus:ring-2 focus:ring-neutral-300 mb-3\"\n />\n <button\n onClick={handleSubmit}\n disabled={!name.trim()}\n className=\"w-full py-2 rounded-lg text-white text-sm font-medium\n disabled:opacity-30 transition-colors\"\n style={{ backgroundColor: brandColor }}\n >\n Continue\n </button>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { MessageSquare, List, X } from \"../icons\";\nimport { useApostil } from \"../context\";\n\nexport function CommentToggle() {\n const {\n commentMode,\n setCommentMode,\n sidebarOpen,\n setSidebarOpen,\n unresolvedCount,\n setActiveThreadId,\n brandColor,\n } = useApostil();\n\n return (\n <div className=\"absolute bottom-5 right-5 z-[65] flex flex-col gap-2 items-end\">\n {/* Sidebar toggle */}\n <button\n onClick={() => setSidebarOpen(!sidebarOpen)}\n className={`flex items-center justify-center w-10 h-10 rounded-full shadow-lg\n transition-all duration-200 hover:scale-105\n ${sidebarOpen\n ? \"text-white\"\n : \"bg-white text-neutral-600 hover:bg-neutral-50 border border-neutral-200\"\n }`}\n style={sidebarOpen ? { backgroundColor: brandColor } : undefined}\n title=\"Toggle comment list\"\n >\n <List className=\"w-4 h-4\" />\n </button>\n\n {/* Comment mode toggle */}\n <button\n onClick={() => {\n if (commentMode) {\n setCommentMode(false);\n } else {\n setActiveThreadId(null);\n setCommentMode(true);\n }\n }}\n className={`relative flex items-center justify-center w-12 h-12 rounded-full shadow-lg\n transition-all duration-200 hover:scale-105\n ${commentMode\n ? \"text-white ring-2 ring-neutral-400\"\n : \"bg-white text-neutral-700 hover:bg-neutral-50 border border-neutral-200\"\n }`}\n style={commentMode ? { backgroundColor: brandColor } : undefined}\n title={commentMode ? \"Exit comment mode\" : \"Add comment\"}\n >\n {commentMode ? (\n <X className=\"w-5 h-5\" />\n ) : (\n <MessageSquare className=\"w-5 h-5\" />\n )}\n {/* Unresolved badge */}\n {unresolvedCount > 0 && !commentMode && (\n <span className=\"absolute -top-1 -right-1 min-w-[18px] h-[18px] rounded-full\n bg-red-500 text-white text-[10px] font-semibold\n flex items-center justify-center px-1\">\n {unresolvedCount}\n </span>\n )}\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { X, Check, Undo2, MessageSquare, Globe, FileText } from \"../icons\";\nimport { useApostil } from \"../context\";\nimport type { ApostilThread } from \"../types\";\n\nfunction timeAgo(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return \"just now\";\n if (mins < 60) return `${mins}m ago`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n\nfunction pageIdToDisplay(pageId: string): string {\n return pageId.replace(/--/g, \"/\").replace(/-/g, \".\");\n}\n\ntype AllPagesData = { pageId: string; threads: ApostilThread[] }[];\n\nexport function CommentSidebar() {\n const {\n threads,\n sidebarOpen,\n setSidebarOpen,\n setActiveThreadId,\n resolveThread,\n brandColor,\n } = useApostil();\n\n const [tab, setTab] = useState<\"page\" | \"all\">(\"page\");\n const [allPages, setAllPages] = useState<AllPagesData>([]);\n const [loadingAll, setLoadingAll] = useState(false);\n\n // Fetch all pages when \"All Pages\" tab is selected\n useEffect(() => {\n if (!sidebarOpen || tab !== \"all\") return;\n setLoadingAll(true);\n\n // Try CLI server first, then local API\n async function fetchAll() {\n // Fetch from the app's own API route (no pageId = returns all pages)\n try {\n const res = await fetch(\"/api/apostil\");\n if (res.ok) {\n const data = await res.json();\n setAllPages(data);\n }\n } catch {}\n setLoadingAll(false);\n }\n fetchAll();\n }, [sidebarOpen, tab]);\n\n if (!sidebarOpen) return null;\n\n const openThreads = threads.filter((t) => !t.resolved);\n const resolvedThreads = threads.filter((t) => t.resolved);\n\n return (\n <div className=\"absolute top-0 right-0 bottom-0 w-80 z-[75] bg-white border-l border-neutral-200 shadow-xl flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-neutral-100\">\n <div className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-4 h-4 text-neutral-500\" />\n <span className=\"text-sm font-semibold text-neutral-900\">\n Comments\n </span>\n </div>\n <button\n onClick={() => setSidebarOpen(false)}\n className=\"p-1 rounded hover:bg-neutral-100 transition-colors\"\n >\n <X className=\"w-4 h-4 text-neutral-500\" />\n </button>\n </div>\n\n {/* Tabs */}\n <div className=\"flex border-b border-neutral-100\">\n <button\n onClick={() => setTab(\"page\")}\n className={`flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${\n tab === \"page\"\n ? \"border-b-2\"\n : \"text-neutral-400 hover:text-neutral-600\"\n }`}\n style={tab === \"page\" ? { color: brandColor, borderColor: brandColor } : undefined}\n >\n <FileText className=\"w-3 h-3\" />\n This Page\n {openThreads.length > 0 && (\n <span className=\"text-[10px] bg-red-50 text-red-600 px-1.5 py-px rounded-full\">\n {openThreads.length}\n </span>\n )}\n </button>\n <button\n onClick={() => setTab(\"all\")}\n className={`flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${\n tab === \"all\"\n ? \"border-b-2\"\n : \"text-neutral-400 hover:text-neutral-600\"\n }`}\n style={tab === \"all\" ? { color: brandColor, borderColor: brandColor } : undefined}\n >\n <Globe className=\"w-3 h-3\" />\n All Pages\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto\">\n {tab === \"page\" ? (\n <PageThreads\n threads={threads}\n openThreads={openThreads}\n resolvedThreads={resolvedThreads}\n onSelect={setActiveThreadId}\n onResolve={resolveThread}\n />\n ) : (\n <AllPagesView\n pages={allPages}\n loading={loadingAll}\n />\n )}\n </div>\n </div>\n );\n}\n\n// --- This Page tab ---\n\nfunction PageThreads({\n threads,\n openThreads,\n resolvedThreads,\n onSelect,\n onResolve,\n}: {\n threads: ApostilThread[];\n openThreads: ApostilThread[];\n resolvedThreads: ApostilThread[];\n onSelect: (id: string) => void;\n onResolve: (id: string) => void;\n}) {\n if (threads.length === 0) {\n return (\n <div className=\"p-6 text-center text-sm text-neutral-400\">\n No comments on this page.\n </div>\n );\n }\n\n return (\n <>\n {openThreads.length > 0 && (\n <div>\n <div className=\"px-4 py-2 text-[10px] font-semibold text-neutral-400 uppercase tracking-wider\">\n Open ({openThreads.length})\n </div>\n {openThreads.map((thread) => (\n <ThreadItem\n key={thread.id}\n thread={thread}\n onSelect={() => onSelect(thread.id)}\n onResolve={() => onResolve(thread.id)}\n />\n ))}\n </div>\n )}\n\n {resolvedThreads.length > 0 && (\n <div>\n <div className=\"px-4 py-2 text-[10px] font-semibold text-neutral-400 uppercase tracking-wider\">\n Resolved ({resolvedThreads.length})\n </div>\n {resolvedThreads.map((thread) => (\n <ThreadItem\n key={thread.id}\n thread={thread}\n onSelect={() => onSelect(thread.id)}\n onResolve={() => onResolve(thread.id)}\n resolved\n />\n ))}\n </div>\n )}\n </>\n );\n}\n\n// --- All Pages tab ---\n\nfunction AllPagesView({\n pages,\n loading,\n}: {\n pages: AllPagesData;\n loading: boolean;\n}) {\n if (loading) {\n return (\n <div className=\"p-6 text-center text-sm text-neutral-400\">\n Loading...\n </div>\n );\n }\n\n if (pages.length === 0) {\n return (\n <div className=\"p-6 text-center text-sm text-neutral-400\">\n No comments in this project yet.\n </div>\n );\n }\n\n const totalOpen = pages.reduce((s, p) => s + p.threads.filter((t) => !t.resolved).length, 0);\n\n return (\n <>\n {totalOpen > 0 && (\n <div className=\"px-4 py-2 text-[10px] font-semibold text-neutral-400 uppercase tracking-wider\">\n {totalOpen} open across {pages.length} pages\n </div>\n )}\n\n {pages.map((page) => {\n const open = page.threads.filter((t) => !t.resolved);\n const resolved = page.threads.filter((t) => t.resolved);\n const displayName = pageIdToDisplay(page.pageId);\n\n return (\n <div key={page.pageId} className=\"border-b border-neutral-50\">\n {/* Page header */}\n <div className=\"px-4 py-2.5 flex items-center justify-between bg-neutral-50/50\">\n <span className=\"text-xs font-semibold text-neutral-700 truncate\">\n {displayName}\n </span>\n <div className=\"flex items-center gap-1.5\">\n {open.length > 0 && (\n <span className=\"text-[10px] bg-red-50 text-red-600 px-1.5 py-px rounded-full font-medium\">\n {open.length}\n </span>\n )}\n {resolved.length > 0 && (\n <span className=\"text-[10px] bg-neutral-100 text-neutral-500 px-1.5 py-px rounded-full font-medium\">\n {resolved.length}\n </span>\n )}\n </div>\n </div>\n\n {/* Threads for this page */}\n {[...open, ...resolved].map((thread) => {\n const firstComment = thread.comments[0];\n if (!firstComment) return null;\n const isResolved = thread.resolved;\n\n return (\n <div\n key={thread.id}\n onClick={() => {\n // Navigate to the page with the comment hash\n const path = page.pageId === \"home\" ? \"/\" : \"/\" + page.pageId.replace(/--/g, \"/\");\n window.location.href = path + \"#apostil-\" + thread.id;\n }}\n className={`px-4 py-2.5 border-b border-neutral-50 cursor-pointer hover:bg-neutral-50 transition-colors ${\n isResolved ? \"opacity-50\" : \"\"\n }`}\n >\n <div className=\"flex items-center justify-between mb-1\">\n <div className=\"flex items-center gap-2\">\n <div\n className=\"w-4 h-4 rounded-full flex items-center justify-center text-white text-[8px] font-semibold\"\n style={{ backgroundColor: firstComment.author.color }}\n >\n {firstComment.author.name[0]?.toUpperCase()}\n </div>\n <span className=\"text-xs font-medium text-neutral-700\">\n {firstComment.author.name}\n </span>\n </div>\n <span className=\"text-[10px] text-neutral-400\">\n {timeAgo(firstComment.createdAt)}\n </span>\n </div>\n <p className=\"text-xs text-neutral-600 line-clamp-2 pl-6\">\n {firstComment.body}\n </p>\n {thread.comments.length > 1 && (\n <span className=\"text-[10px] text-neutral-400 pl-6\">\n {thread.comments.length - 1} {thread.comments.length - 1 === 1 ? \"reply\" : \"replies\"}\n </span>\n )}\n </div>\n );\n })}\n </div>\n );\n })}\n </>\n );\n}\n\n// --- Shared thread item ---\n\nfunction ThreadItem({\n thread,\n onSelect,\n onResolve,\n resolved,\n}: {\n thread: { id: string; targetLabel?: string; comments: { author: { name: string; color: string }; body: string; createdAt: string }[] };\n onSelect: () => void;\n onResolve: () => void;\n resolved?: boolean;\n}) {\n const firstComment = thread.comments[0];\n if (!firstComment) return null;\n\n return (\n <div\n onClick={onSelect}\n className={`px-4 py-3 border-b border-neutral-50 cursor-pointer hover:bg-neutral-50 transition-colors\n ${resolved ? \"opacity-60\" : \"\"}`}\n >\n <div className=\"flex items-center justify-between mb-1\">\n <div className=\"flex items-center gap-2\">\n <div\n className=\"w-4 h-4 rounded-full flex items-center justify-center text-white text-[8px] font-semibold\"\n style={{ backgroundColor: firstComment.author.color }}\n >\n {firstComment.author.name[0]?.toUpperCase()}\n </div>\n <span className=\"text-xs font-medium text-neutral-700\">\n {firstComment.author.name}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-neutral-400\">\n {timeAgo(firstComment.createdAt)}\n </span>\n <button\n onClick={(e) => {\n e.stopPropagation();\n onResolve();\n }}\n className=\"p-0.5 rounded hover:bg-neutral-200 transition-colors\"\n >\n {resolved ? (\n <Undo2 className=\"w-3 h-3 text-neutral-400\" />\n ) : (\n <Check className=\"w-3 h-3 text-emerald-600\" />\n )}\n </button>\n </div>\n </div>\n {thread.targetLabel && (\n <span className=\"inline-block text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium ml-6 mb-1\">\n {thread.targetLabel}\n </span>\n )}\n <p className=\"text-xs text-neutral-600 line-clamp-2 pl-6\">\n {firstComment.body}\n </p>\n {thread.comments.length > 1 && (\n <span className=\"text-[10px] text-neutral-400 pl-6\">\n {thread.comments.length - 1} {thread.comments.length - 1 === 1 ? \"reply\" : \"replies\"}\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","timeAgo"]}