@objectifthunes/whiteboard 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/WhiteboardShell.tsx","../src/ZoomBar.tsx","../src/icons.tsx","../src/store.ts","../src/grid.ts","../src/Minimap.tsx","../src/FloatingPanel.tsx","../src/ConfirmDialog.tsx","../src/PanelErrorBoundary.tsx","../src/useWhiteboardLayout.ts","../src/cn.ts"],"sourcesContent":["// Components\nexport { WhiteboardShell } from './WhiteboardShell'\nexport { FloatingPanel, usePanelRect, belowPanel } from './FloatingPanel'\nexport { ZoomBar } from './ZoomBar'\nexport { Minimap } from './Minimap'\nexport { ConfirmDialog } from './ConfirmDialog'\nexport { PanelErrorBoundary } from './PanelErrorBoundary'\n\n// Store\nexport {\n useWhiteboardStore,\n computeWhiteboardFit,\n computeWhiteboardRectFocus,\n} from './store'\nexport type { PanelRect, WhiteboardStore } from './store'\n\n// Hooks\nexport { useWhiteboardLayout } from './useWhiteboardLayout'\n\n// Utilities\nexport { WHITEBOARD_GRID, snapToWhiteboardGrid } from './grid'\nexport { cn } from './cn'\n","'use client'\n\nimport { useRef, useCallback, useEffect, type ReactNode, type PointerEvent, type WheelEvent } from 'react'\nimport { ZoomBar } from './ZoomBar'\nimport { Minimap } from './Minimap'\nimport { useWhiteboardStore } from './store'\n\ninterface Props {\n children: ReactNode\n showMinimap?: boolean\n minimapLoading?: boolean\n /** Extra action buttons rendered inside the ZoomBar (e.g. a theme toggle) */\n extraActions?: ReactNode\n}\n\nexport function WhiteboardShell({ children, showMinimap = true, minimapLoading = false, extraActions }: Props) {\n const offset = useWhiteboardStore(s => s.offset)\n const scale = useWhiteboardStore(s => s.scale)\n const registryVersion = useWhiteboardStore(s => s.registryVersion)\n const viewportSize = useWhiteboardStore(s => s.viewportSize)\n const setOffset = useWhiteboardStore(s => s.setOffset)\n const setScale = useWhiteboardStore(s => s.setScale)\n const setViewportSize = useWhiteboardStore(s => s.setViewportSize)\n\n const shellRef = useRef<HTMLDivElement>(null)\n const panning = useRef(false)\n const last = useRef({ x: 0, y: 0 })\n const activePointerId = useRef<number | null>(null)\n const scaleRef = useRef(scale)\n const hasFitted = useRef(false)\n\n // Reset stale state when leaving the page so the next mount starts clean.\n useEffect(() => {\n return () => { useWhiteboardStore.getState().resetSession() }\n }, [])\n\n useEffect(() => {\n scaleRef.current = scale\n }, [scale])\n\n useEffect(() => {\n const shell = shellRef.current\n if (!shell) return\n\n const updateViewport = () => {\n const rect = shell.getBoundingClientRect()\n setViewportSize({\n width: Math.max(0, rect.width),\n height: Math.max(0, rect.height),\n })\n }\n\n updateViewport()\n if (typeof ResizeObserver === 'undefined') return\n\n const observer = new ResizeObserver(updateViewport)\n observer.observe(shell)\n return () => observer.disconnect()\n }, [setViewportSize])\n\n // Auto-fit on first meaningful panel registration.\n useEffect(() => {\n if (hasFitted.current) return\n const { panels } = useWhiteboardStore.getState()\n if (panels.size === 0 || viewportSize.width <= 0 || viewportSize.height <= 0) return\n hasFitted.current = true\n requestAnimationFrame(() => {\n useWhiteboardStore.getState().fitToContent()\n })\n }, [registryVersion, viewportSize])\n\n const onDown = useCallback((e: PointerEvent) => {\n if (e.target !== e.currentTarget) return\n if (e.button === 0 || e.button === 1) {\n panning.current = true\n activePointerId.current = e.pointerId\n last.current = { x: e.clientX, y: e.clientY }\n e.currentTarget.setPointerCapture(e.pointerId)\n e.preventDefault()\n }\n }, [])\n\n const onMove = useCallback((e: PointerEvent) => {\n if (!panning.current || activePointerId.current !== e.pointerId) return\n const dx = e.movementX ?? e.clientX - last.current.x\n const dy = e.movementY ?? e.clientY - last.current.y\n setOffset(p => ({ x: p.x + dx, y: p.y + dy }))\n last.current = { x: e.clientX, y: e.clientY }\n }, [setOffset])\n\n const stopPan = useCallback((e: PointerEvent) => {\n if (activePointerId.current !== null) {\n try {\n e.currentTarget.releasePointerCapture(activePointerId.current)\n } catch {\n // ignore if capture was already released\n }\n }\n panning.current = false\n activePointerId.current = null\n }, [])\n\n const onWheel = useCallback((e: WheelEvent) => {\n const s = scaleRef.current\n const rect = e.currentTarget.getBoundingClientRect()\n const anchor = { x: e.clientX - rect.left, y: e.clientY - rect.top }\n const nextScale = Math.min(3, Math.max(0.2, s * (e.deltaY > 0 ? 0.9 : 1.1)))\n const ratio = nextScale / s\n\n setOffset(prev => ({\n x: anchor.x - (anchor.x - prev.x) * ratio,\n y: anchor.y - (anchor.y - prev.y) * ratio,\n }))\n setScale(nextScale)\n }, [setOffset, setScale])\n\n return (\n <>\n <div\n ref={shellRef}\n onPointerDown={onDown}\n onPointerMove={onMove}\n onPointerUp={stopPan}\n onPointerCancel={stopPan}\n onWheel={onWheel}\n onContextMenu={e => e.preventDefault()}\n className=\"whiteboard-shell\"\n >\n <div\n className=\"whiteboard-canvas\"\n style={{ transform: `translate(${offset.x}px, ${offset.y}px) scale(${scale})` }}\n >\n <div className=\"whiteboard-grid\" aria-hidden />\n {children}\n </div>\n </div>\n <ZoomBar extraActions={extraActions} />\n {showMinimap ? <Minimap loading={minimapLoading} /> : null}\n </>\n )\n}\n","'use client'\n\nimport { useEffect, type ReactNode } from 'react'\nimport { Grid3x3, Minus, Plus, RotateCcw, ScanSearch } from './icons'\nimport { useWhiteboardStore } from './store'\n\ninterface Props {\n /** Extra action buttons rendered at the bottom of the zoom bar */\n extraActions?: ReactNode\n}\n\nexport function ZoomBar({ extraActions }: Props) {\n const scale = useWhiteboardStore(s => s.scale)\n const viewportSize = useWhiteboardStore(s => s.viewportSize)\n const snapToGrid = useWhiteboardStore(s => s.snapToGrid)\n const setScale = useWhiteboardStore(s => s.setScale)\n const setOffset = useWhiteboardStore(s => s.setOffset)\n const setSnapToGrid = useWhiteboardStore(s => s.setSnapToGrid)\n const fitToContent = useWhiteboardStore(s => s.fitToContent)\n const resetWidgets = useWhiteboardStore(s => s.resetWidgets)\n\n useEffect(() => {\n if (!snapToGrid) return\n window.dispatchEvent(new Event('whiteboard-snap-now'))\n }, [snapToGrid])\n\n const zoomTo = (nextScale: number) => {\n const clamped = Math.min(3, Math.max(0.2, nextScale))\n const ratio = clamped / scale\n const cx = (viewportSize.width || window.innerWidth) / 2\n const cy = (viewportSize.height || window.innerHeight) / 2\n setOffset(prev => ({\n x: cx - (cx - prev.x) * ratio,\n y: cy - (cy - prev.y) * ratio,\n }))\n setScale(clamped)\n }\n\n return (\n <div\n className=\"zoom-bar\"\n onPointerDown={e => e.stopPropagation()}\n onWheel={e => e.stopPropagation()}\n >\n <button type=\"button\" className=\"wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__icon\" onClick={() => zoomTo(scale * 0.8)} title=\"Zoom out\" aria-label=\"Zoom out\">\n <Minus size={14} />\n </button>\n <span className=\"zoom-bar__value\">\n {Math.round(scale * 100)}%\n </span>\n <button type=\"button\" className=\"wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__icon\" onClick={() => zoomTo(scale * 1.2)} title=\"Zoom in\" aria-label=\"Zoom in\">\n <Plus size={14} />\n </button>\n <button type=\"button\" className=\"wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action\" onClick={fitToContent} title=\"Fit camera to all panels\" aria-label=\"Fit camera to all panels\">\n <ScanSearch size={14} />\n </button>\n <button type=\"button\" className=\"wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action\" onClick={resetWidgets} title=\"Reset panel positions\" aria-label=\"Reset panel positions\">\n <RotateCcw size={14} />\n </button>\n <button\n type=\"button\"\n className={`wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action${snapToGrid ? ' is-active' : ''}`}\n onClick={() => setSnapToGrid((prev) => !prev)}\n title={snapToGrid ? 'Disable snap to grid' : 'Enable snap to grid'}\n aria-label={snapToGrid ? 'Disable snap to grid' : 'Enable snap to grid'}\n >\n <Grid3x3 size={14} />\n </button>\n {extraActions}\n </div>\n )\n}\n","import type { SVGProps } from 'react'\n\ntype IconProps = SVGProps<SVGSVGElement> & { size?: number }\n\nconst defaults = (size = 14): SVGProps<SVGSVGElement> => ({\n xmlns: 'http://www.w3.org/2000/svg',\n width: size,\n height: size,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n})\n\nexport function Minus({ size, ...props }: IconProps) {\n return <svg {...defaults(size)} {...props}><path d=\"M5 12h14\" /></svg>\n}\n\nexport function Plus({ size, ...props }: IconProps) {\n return <svg {...defaults(size)} {...props}><path d=\"M5 12h14\" /><path d=\"M12 5v14\" /></svg>\n}\n\nexport function ScanSearch({ size, ...props }: IconProps) {\n return (\n <svg {...defaults(size)} {...props}>\n <path d=\"M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z\" fill=\"none\" stroke=\"none\" />\n <path d=\"M3 7V5a2 2 0 0 1 2-2h2\" />\n <path d=\"M17 3h2a2 2 0 0 1 2 2v2\" />\n <path d=\"M21 17v2a2 2 0 0 1-2 2h-2\" />\n <path d=\"M7 21H5a2 2 0 0 1-2-2v-2\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"m16 16-1.9-1.9\" />\n </svg>\n )\n}\n\nexport function RotateCcw({ size, ...props }: IconProps) {\n return (\n <svg {...defaults(size)} {...props}>\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </svg>\n )\n}\n\nexport function Grid3x3({ size, ...props }: IconProps) {\n return (\n <svg {...defaults(size)} {...props}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n <path d=\"M3 9h18\" />\n <path d=\"M3 15h18\" />\n <path d=\"M9 3v18\" />\n <path d=\"M15 3v18\" />\n </svg>\n )\n}\n\nexport function Maximize2({ size, ...props }: IconProps) {\n return (\n <svg {...defaults(size)} {...props}>\n <polyline points=\"15 3 21 3 21 9\" />\n <polyline points=\"9 21 3 21 3 15\" />\n <line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\" />\n <line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\" />\n </svg>\n )\n}\n\nexport function Loader2({ size, ...props }: IconProps) {\n return (\n <svg {...defaults(size)} {...props}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n )\n}\n\nexport function AlertTriangle({ size, ...props }: IconProps) {\n return (\n <svg {...defaults(size)} {...props}>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </svg>\n )\n}\n\nexport function Check({ size, ...props }: IconProps) {\n return <svg {...defaults(size)} {...props}><path d=\"M20 6 9 17l-5-5\" /></svg>\n}\n\nexport function X({ size, ...props }: IconProps) {\n return <svg {...defaults(size)} {...props}><path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>\n}\n","'use client'\n\nimport { create } from 'zustand'\nimport { WHITEBOARD_GRID } from './grid'\n\n// ── Types ───────────────────────────────────────────────\n\nexport type PanelRect = {\n x: number\n y: number\n width: number\n height: number\n focusPadding?: number\n focusMaxScale?: number\n}\n\n// ── Geometry helpers ───────────────────────────────────\n\nexport function computeWhiteboardFit(\n panels: Map<string, PanelRect>,\n viewportSize: { width: number; height: number },\n padding = 60,\n) {\n if (panels.size === 0) return null\n\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const r of panels.values()) {\n minX = Math.min(minX, r.x)\n minY = Math.min(minY, r.y)\n maxX = Math.max(maxX, r.x + r.width)\n maxY = Math.max(maxY, r.y + r.height)\n }\n\n const contentW = maxX - minX + padding * 2\n const contentH = maxY - minY + padding * 2\n const vpW = viewportSize.width || window.innerWidth\n const vpH = viewportSize.height || window.innerHeight\n const fitScale = Math.min(vpW / contentW, vpH / contentH, 1.5)\n const centerX = (minX + maxX) / 2\n const centerY = (minY + maxY) / 2\n\n return {\n scale: Math.min(3, Math.max(0.2, fitScale)),\n offset: {\n x: vpW / 2 - centerX * fitScale,\n y: vpH / 2 - centerY * fitScale,\n },\n }\n}\n\nexport function computeWhiteboardRectFocus(\n rect: PanelRect,\n viewportSize: { width: number; height: number },\n padding = 40,\n maxScale = 1.5,\n) {\n const vpW = viewportSize.width || window.innerWidth\n const vpH = viewportSize.height || window.innerHeight\n const safeWidth = Math.max(1, rect.width)\n const safeHeight = Math.max(1, rect.height)\n const fitScale = Math.min(\n (vpW - padding * 2) / safeWidth,\n (vpH - padding * 2) / safeHeight,\n maxScale,\n )\n const nextScale = Math.min(3, Math.max(0.2, fitScale))\n\n return {\n scale: nextScale,\n offset: {\n x: vpW / 2 - (rect.x + safeWidth / 2) * nextScale,\n y: vpH / 2 - (rect.y + safeHeight / 2) * nextScale,\n },\n }\n}\n\n// ── Store ──────────────────────────────────────────────\n\nexport interface WhiteboardStore {\n offset: { x: number; y: number }\n scale: number\n viewportSize: { width: number; height: number }\n snapToGrid: boolean\n snapGridSize: number\n panels: Map<string, PanelRect>\n resetFns: Map<string, () => void>\n /** Incremented each time the panel registry changes; subscribe to trigger re-renders. */\n registryVersion: number\n\n setOffset: (v: { x: number; y: number } | ((prev: { x: number; y: number }) => { x: number; y: number })) => void\n setScale: (v: number | ((prev: number) => number)) => void\n setViewportSize: (v: { width: number; height: number }) => void\n setSnapToGrid: (v: boolean | ((prev: boolean) => boolean)) => void\n\n register: (id: string, rect: PanelRect) => void\n unregister: (id: string) => void\n registerReset: (id: string, fn: () => void) => void\n unregisterReset: (id: string) => void\n\n fitToContent: () => void\n focusPanel: (rect: PanelRect, options?: { padding?: number; maxScale?: number }) => void\n resetWidgets: () => void\n\n /** Call on WhiteboardShell mount to discard any stale state from the previous session. */\n resetSession: () => void\n}\n\nexport const useWhiteboardStore = create<WhiteboardStore>((set, get) => ({\n offset: { x: 0, y: 0 },\n scale: 1,\n viewportSize: { width: 0, height: 0 },\n snapToGrid: false,\n snapGridSize: WHITEBOARD_GRID,\n panels: new Map(),\n resetFns: new Map(),\n registryVersion: 0,\n\n setOffset: (v) => set((s) => ({ offset: typeof v === 'function' ? v(s.offset) : v })),\n setScale: (v) => set((s) => ({ scale: typeof v === 'function' ? v(s.scale) : v })),\n setViewportSize: (v) => set({ viewportSize: v }),\n setSnapToGrid: (v) => set((s) => ({ snapToGrid: typeof v === 'function' ? v(s.snapToGrid) : v })),\n\n register: (id, rect) => {\n get().panels.set(id, rect)\n set((s) => ({ registryVersion: s.registryVersion + 1 }))\n },\n unregister: (id) => {\n get().panels.delete(id)\n set((s) => ({ registryVersion: s.registryVersion + 1 }))\n },\n registerReset: (id, fn) => { get().resetFns.set(id, fn) },\n unregisterReset: (id) => { get().resetFns.delete(id) },\n\n fitToContent: () => {\n const { panels, viewportSize } = get()\n const fit = computeWhiteboardFit(panels, viewportSize)\n if (fit) set({ scale: fit.scale, offset: fit.offset })\n },\n\n focusPanel: (rect, options) => {\n const { viewportSize } = get()\n const fit = computeWhiteboardRectFocus(\n rect, viewportSize, options?.padding ?? 40, options?.maxScale ?? 1.5,\n )\n set({ scale: fit.scale, offset: fit.offset })\n },\n\n resetWidgets: () => {\n for (const fn of get().resetFns.values()) fn()\n const attempt = (tries = 0) => {\n const { panels, viewportSize } = get()\n const fit = computeWhiteboardFit(panels, viewportSize)\n if (fit) { set({ scale: fit.scale, offset: fit.offset }); return }\n if (tries < 6) requestAnimationFrame(() => attempt(tries + 1))\n }\n requestAnimationFrame(() => requestAnimationFrame(() => attempt()))\n },\n\n resetSession: () => set({\n offset: { x: 0, y: 0 },\n scale: 1,\n viewportSize: { width: 0, height: 0 },\n snapToGrid: false,\n registryVersion: 0,\n panels: new Map(),\n resetFns: new Map(),\n }),\n}))\n","export const WHITEBOARD_GRID = 20\n\nexport function snapToWhiteboardGrid(value: number) {\n return Math.round(value / WHITEBOARD_GRID) * WHITEBOARD_GRID\n}\n","'use client'\n\nimport { useRef, type PointerEvent, type WheelEvent } from 'react'\nimport { Loader2 } from './icons'\nimport { useWhiteboardStore } from './store'\n\nconst MAP_W = 200\nconst MAP_H = 150\nconst PADDING = 40\nconst MIN_WORLD_SIZE = 1\n\ninterface Props {\n loading?: boolean\n}\n\nexport function Minimap({ loading = false }: Props) {\n // Subscribe to registryVersion so the minimap re-renders when panels change.\n useWhiteboardStore(s => s.registryVersion)\n const offset = useWhiteboardStore(s => s.offset)\n const scale = useWhiteboardStore(s => s.scale)\n const viewportSize = useWhiteboardStore(s => s.viewportSize)\n const panels = useWhiteboardStore(s => s.panels)\n const setOffset = useWhiteboardStore(s => s.setOffset)\n const setScale = useWhiteboardStore(s => s.setScale)\n const focusPanel = useWhiteboardStore(s => s.focusPanel)\n const containerRef = useRef<HTMLDivElement>(null)\n const dragging = useRef(false)\n const lastPanelTapRef = useRef<{ id: string; time: number } | null>(null)\n\n const rectEntries = Array.from(panels.entries())\n const visibleRectEntries = rectEntries.filter(([, rect]) => Number.isFinite(rect.width) && Number.isFinite(rect.height))\n const rects = visibleRectEntries.map(([, rect]) => rect)\n\n if (loading || rects.length === 0) {\n return (\n <div className=\"minimap minimap--loading\" style={{ width: MAP_W, height: MAP_H }}>\n <div className=\"minimap__loading\">\n <Loader2 size={14} className=\"icon-spin\" />\n </div>\n </div>\n )\n }\n\n const viewportWidth = viewportSize.width || window.innerWidth\n const viewportHeight = viewportSize.height || window.innerHeight\n const vpW = viewportWidth / scale\n const vpH = viewportHeight / scale\n const vpX = -offset.x / scale\n const vpY = -offset.y / scale\n\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const r of rects) {\n minX = Math.min(minX, r.x)\n minY = Math.min(minY, r.y)\n maxX = Math.max(maxX, r.x + r.width)\n maxY = Math.max(maxY, r.y + r.height)\n }\n\n // Keep minimap anchored to actual panel content bounds to avoid disappearing widgets.\n minX -= PADDING\n minY -= PADDING\n maxX += PADDING\n maxY += PADDING\n\n const worldW = Math.max(MIN_WORLD_SIZE, maxX - minX)\n const worldH = Math.max(MIN_WORLD_SIZE, maxY - minY)\n const mapScale = Math.min(MAP_W / worldW, MAP_H / worldH)\n\n const contentW = worldW * mapScale\n const contentH = worldH * mapScale\n const mapOffsetX = (MAP_W - contentW) / 2\n const mapOffsetY = (MAP_H - contentH) / 2\n\n const toMapX = (wx: number) => mapOffsetX + (wx - minX) * mapScale\n const toMapY = (wy: number) => mapOffsetY + (wy - minY) * mapScale\n\n const clientToWorld = (clientX: number, clientY: number) => {\n if (!containerRef.current) return\n\n const mapRect = containerRef.current.getBoundingClientRect()\n const localX = Math.min(MAP_W, Math.max(0, clientX - mapRect.left))\n const localY = Math.min(MAP_H, Math.max(0, clientY - mapRect.top))\n\n const boundedX = Math.min(contentW + mapOffsetX, Math.max(mapOffsetX, localX))\n const boundedY = Math.min(contentH + mapOffsetY, Math.max(mapOffsetY, localY))\n\n const worldX = (boundedX - mapOffsetX) / mapScale + minX\n const worldY = (boundedY - mapOffsetY) / mapScale + minY\n\n return { worldX, worldY }\n }\n\n const centerToWorld = (worldX: number, worldY: number, targetScale: number) => {\n const clampedScale = Math.min(3, Math.max(0.2, targetScale))\n setScale(clampedScale)\n setOffset({\n x: viewportWidth / 2 - worldX * clampedScale,\n y: viewportHeight / 2 - worldY * clampedScale,\n })\n }\n\n const panTo = (clientX: number, clientY: number) => {\n const world = clientToWorld(clientX, clientY)\n if (!world) return\n centerToWorld(world.worldX, world.worldY, scale)\n }\n\n const onDown = (e: PointerEvent) => {\n e.stopPropagation()\n e.preventDefault()\n dragging.current = true\n e.currentTarget.setPointerCapture(e.pointerId)\n panTo(e.clientX, e.clientY)\n }\n\n const onMove = (e: PointerEvent) => {\n if (!dragging.current) return\n panTo(e.clientX, e.clientY)\n }\n\n const onUp = () => {\n dragging.current = false\n }\n\n const onWheel = (e: WheelEvent<HTMLDivElement>) => {\n e.preventDefault()\n e.stopPropagation()\n const world = clientToWorld(e.clientX, e.clientY)\n if (!world) return\n const factor = e.deltaY > 0 ? 0.9 : 1.1\n centerToWorld(world.worldX, world.worldY, scale * factor)\n }\n\n return (\n <div\n ref={containerRef}\n onPointerDown={onDown}\n onPointerMove={onMove}\n onPointerUp={onUp}\n onPointerCancel={onUp}\n onWheel={onWheel}\n className=\"minimap\"\n style={{\n width: MAP_W,\n height: MAP_H,\n }}\n >\n {visibleRectEntries.map(([id, r]) => (\n <div\n key={id}\n className=\"minimap__panel\"\n onPointerDown={(event) => {\n event.stopPropagation()\n }}\n onPointerUp={(event) => {\n event.stopPropagation()\n const now = Date.now()\n const last = lastPanelTapRef.current\n if (last && last.id === id && now - last.time < 300) {\n event.preventDefault()\n focusPanel(r, { padding: r.focusPadding, maxScale: r.focusMaxScale })\n lastPanelTapRef.current = null\n return\n }\n lastPanelTapRef.current = { id, time: now }\n }}\n onDoubleClick={(event) => {\n event.preventDefault()\n event.stopPropagation()\n focusPanel(r, { padding: r.focusPadding, maxScale: r.focusMaxScale })\n }}\n style={{\n left: toMapX(r.x),\n top: toMapY(r.y),\n width: Math.max(1, r.width * mapScale),\n height: Math.max(1, r.height * mapScale),\n }}\n />\n ))}\n <div\n className=\"minimap__viewport\"\n style={{\n left: toMapX(vpX),\n top: toMapY(vpY),\n width: vpW * mapScale,\n height: vpH * mapScale,\n }}\n />\n </div>\n )\n}\n","'use client'\n\nimport { useRef, useId, useState, useCallback, useEffect, useLayoutEffect, memo, type ReactNode, type PointerEvent, type MutableRefObject } from 'react'\nimport { Maximize2 } from './icons'\nimport { useWhiteboardStore } from './store'\nimport type { PanelRect } from './store'\nimport { WHITEBOARD_GRID } from './grid'\n\ninterface Props {\n title: ReactNode\n defaultPosition: { x: number; y: number }\n width?: number\n className?: string\n /** Ref that stays in sync with the panel's current position and rendered size */\n trackRect?: MutableRefObject<PanelRect>\n /** Show a focus button that zooms the whiteboard to this panel */\n focusable?: boolean\n /** Optional camera framing controls used by the focus button */\n focusPadding?: number\n focusMaxScale?: number\n /** Extra action buttons rendered in the header bar next to the focus button */\n headerActions?: ReactNode\n children: ReactNode\n}\n\nexport const FloatingPanel = memo(function FloatingPanel({\n title,\n defaultPosition,\n width = 300,\n className,\n trackRect: trackRectRef,\n focusable,\n focusPadding = 40,\n focusMaxScale = 1.5,\n headerActions,\n children,\n}: Props) {\n const panelId = useId()\n const [pos, setPos] = useState(defaultPosition)\n const dragging = useRef(false)\n const posRef = useRef(pos)\n const elRef = useRef<HTMLDivElement>(null)\n const lastRegisteredRectRef = useRef<PanelRect | null>(null)\n const scale = useWhiteboardStore(s => s.scale)\n const snapToGrid = useWhiteboardStore(s => s.snapToGrid)\n const snapGridSize = useWhiteboardStore(s => s.snapGridSize)\n const register = useWhiteboardStore(s => s.register)\n const unregister = useWhiteboardStore(s => s.unregister)\n const registerReset = useWhiteboardStore(s => s.registerReset)\n const unregisterReset = useWhiteboardStore(s => s.unregisterReset)\n const focusPanel = useWhiteboardStore(s => s.focusPanel)\n const scaleRef = useRef(scale)\n const snapToGridRef = useRef(false)\n const snapGridSizeRef = useRef(20)\n const defaultPosRef = useRef(defaultPosition)\n const cleanupRef = useRef<(() => void) | null>(null)\n const lastTapRef = useRef<{ time: number; x: number; y: number } | null>(null)\n\n useEffect(() => {\n scaleRef.current = scale\n }, [scale])\n\n useEffect(() => {\n snapToGridRef.current = snapToGrid\n snapGridSizeRef.current = snapGridSize\n }, [snapToGrid, snapGridSize])\n\n useEffect(() => {\n const onSnapNow = () => {\n const snapSize = snapGridSizeRef.current\n setPos((current) => {\n const next = {\n x: Math.round(current.x / snapSize) * snapSize,\n y: Math.round(current.y / snapSize) * snapSize,\n }\n return next.x === current.x && next.y === current.y ? current : next\n })\n }\n\n window.addEventListener('whiteboard-snap-now', onSnapNow)\n return () => window.removeEventListener('whiteboard-snap-now', onSnapNow)\n }, [])\n\n useEffect(() => {\n posRef.current = pos\n }, [pos])\n\n const registerRect = useCallback(() => {\n const el = elRef.current\n if (!el) return\n\n const nextRect: PanelRect = {\n x: posRef.current.x,\n y: posRef.current.y,\n width: el.offsetWidth,\n height: el.offsetHeight,\n focusPadding,\n focusMaxScale,\n }\n\n const prev = lastRegisteredRectRef.current\n if (\n prev &&\n prev.x === nextRect.x &&\n prev.y === nextRect.y &&\n prev.width === nextRect.width &&\n prev.height === nextRect.height &&\n prev.focusPadding === nextRect.focusPadding &&\n prev.focusMaxScale === nextRect.focusMaxScale\n ) {\n return\n }\n\n lastRegisteredRectRef.current = nextRect\n register(panelId, nextRect)\n if (trackRectRef) {\n trackRectRef.current = nextRect\n }\n }, [focusMaxScale, focusPadding, panelId, register, trackRectRef])\n\n const getCurrentRect = useCallback((): PanelRect | null => {\n const el = elRef.current\n if (!el) return null\n return {\n x: posRef.current.x,\n y: posRef.current.y,\n width: el.offsetWidth,\n height: el.offsetHeight,\n focusPadding,\n focusMaxScale,\n }\n }, [focusMaxScale, focusPadding])\n\n const handleFocus = useCallback(() => {\n const rect = getCurrentRect()\n if (!rect) return\n focusPanel(rect, { padding: focusPadding, maxScale: focusMaxScale })\n }, [focusPanel, focusPadding, focusMaxScale, getCurrentRect])\n\n // Register reset function + cleanup on unmount\n useEffect(() => {\n registerReset(panelId, () => setPos(defaultPosRef.current))\n return () => {\n cleanupRef.current?.()\n unregister(panelId)\n unregisterReset(panelId)\n lastRegisteredRectRef.current = null\n }\n }, [panelId, registerReset, unregister, unregisterReset])\n\n // Measure actual DOM size after paint + register in panel registry.\n useLayoutEffect(() => {\n registerRect()\n }, [pos.x, pos.y, width, registerRect])\n\n // Track content-driven size changes (e.g. skeleton → real content).\n useEffect(() => {\n const el = elRef.current\n if (!el) return\n if (typeof ResizeObserver === 'undefined') return\n\n const obs = new ResizeObserver(() => {\n registerRect()\n })\n obs.observe(el)\n return () => obs.disconnect()\n }, [registerRect])\n\n const onDown = useCallback((e: PointerEvent) => {\n if (e.button !== 0) return\n dragging.current = true\n const startX = e.clientX\n const startY = e.clientY\n const startPosX = posRef.current.x\n const startPosY = posRef.current.y\n const startScale = scaleRef.current\n e.preventDefault()\n e.stopPropagation()\n ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n\n const move = (ev: globalThis.PointerEvent) => {\n if (!dragging.current) return\n const rawX = startPosX + (ev.clientX - startX) / startScale\n const rawY = startPosY + (ev.clientY - startY) / startScale\n const snapSize = snapGridSizeRef.current\n const nextX = snapToGridRef.current ? Math.round(rawX / snapSize) * snapSize : rawX\n const nextY = snapToGridRef.current ? Math.round(rawY / snapSize) * snapSize : rawY\n setPos({\n x: nextX,\n y: nextY,\n })\n }\n const up = () => {\n dragging.current = false\n window.removeEventListener('pointermove', move)\n window.removeEventListener('pointerup', up)\n cleanupRef.current = null\n }\n // Clean previous listeners if any\n cleanupRef.current?.()\n window.addEventListener('pointermove', move)\n window.addEventListener('pointerup', up)\n cleanupRef.current = up\n }, [])\n\n const panelClassName = className ? `floating-panel ${className}` : 'floating-panel'\n\n return (\n <div\n ref={elRef}\n className={panelClassName}\n style={{ left: pos.x, top: pos.y, width }}\n onPointerDown={e => e.stopPropagation()}\n onPointerUp={(e) => {\n if (dragging.current) return\n\n const now = Date.now()\n const last = lastTapRef.current\n if (last && now - last.time < 300) {\n const dx = e.clientX - last.x\n const dy = e.clientY - last.y\n if (dx * dx + dy * dy < 100) {\n e.stopPropagation()\n handleFocus()\n lastTapRef.current = null\n return\n }\n }\n lastTapRef.current = { time: now, x: e.clientX, y: e.clientY }\n }}\n onWheel={e => e.stopPropagation()}\n onDoubleClick={(e) => {\n e.stopPropagation()\n handleFocus()\n }}\n >\n <div onPointerDown={onDown} className=\"floating-panel__header\">\n <strong className=\"floating-panel__title\">{title}</strong>\n {headerActions}\n {focusable && (\n <button\n type=\"button\"\n className=\"wb-btn wb-btn--secondary wb-btn--icon-only floating-panel__focus\"\n onClick={handleFocus}\n onPointerDown={e => e.stopPropagation()}\n title=\"Focus on this panel\"\n aria-label=\"Focus on this panel\"\n >\n <Maximize2 size={14} />\n </button>\n )}\n </div>\n <div className=\"floating-panel__body\">\n {children}\n </div>\n </div>\n )\n})\n\n/** Helper: create a stable rect ref for use with trackRect */\nexport function usePanelRect(initial: { x: number; y: number }): MutableRefObject<PanelRect> {\n const ref = useRef<PanelRect>({ ...initial, width: 0, height: 0 })\n return ref\n}\n\n/** Get a position just below a tracked panel */\nexport function belowPanel(rect: PanelRect, gap = WHITEBOARD_GRID): { x: number; y: number } {\n return { x: rect.x, y: rect.y + rect.height + gap }\n}\n","'use client'\n\nimport { useEffect } from 'react'\nimport { createPortal } from 'react-dom'\nimport { AlertTriangle, Check, X } from './icons'\n\ninterface Props {\n open: boolean\n title: string\n message: string\n onConfirm: () => void\n onCancel: () => void\n confirmLabel?: string\n loading?: boolean\n error?: string | null\n}\n\nexport function ConfirmDialog({\n open,\n title,\n message,\n onConfirm,\n onCancel,\n confirmLabel = 'Confirm',\n loading,\n error,\n}: Props) {\n useEffect(() => {\n if (!open) return\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onCancel()\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [open, onCancel])\n\n if (!open || typeof document === 'undefined') return null\n\n return createPortal(\n <div className=\"confirm-modal-overlay\" onMouseDown={onCancel}>\n <div\n className=\"confirm-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onMouseDown={(e) => e.stopPropagation()}\n >\n <div className=\"confirm-modal__header\">\n <span className=\"confirm-modal__title\">\n <AlertTriangle size={16} />\n {title}\n </span>\n <button type=\"button\" className=\"wb-btn wb-btn--secondary wb-btn--icon-only\" onClick={onCancel} aria-label=\"Close dialog\">\n <X size={14} />\n </button>\n </div>\n <p className=\"confirm-modal__message\">{message}</p>\n {error && <div className=\"wb-alert wb-alert--error\">{error}</div>}\n <div className=\"wb-btn-row\">\n <button type=\"button\" className=\"wb-btn wb-btn--secondary\" onClick={onCancel}>\n Cancel\n </button>\n <button type=\"button\" className=\"wb-btn wb-btn--danger\" onClick={onConfirm} disabled={loading}>\n {loading ? (\n 'Deleting...'\n ) : (\n <>\n <Check size={14} />\n {confirmLabel}\n </>\n )}\n </button>\n </div>\n </div>\n </div>,\n document.body\n )\n}\n","'use client'\n\nimport { Component, type ReactNode } from 'react'\n\ninterface Props {\n children: ReactNode\n fallbackMessage?: string\n}\n\ninterface State {\n error: Error | null\n}\n\nexport class PanelErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(error: Error): State {\n return { error }\n }\n\n render() {\n if (this.state.error) {\n return (\n <div className=\"wb-stack wb-stack--sm\">\n <div className=\"wb-alert wb-alert--error\">\n {this.props.fallbackMessage ?? 'This panel crashed.'}\n </div>\n <button\n type=\"button\"\n className=\"wb-btn wb-btn--secondary\"\n onClick={() => this.setState({ error: null })}\n >\n Retry\n </button>\n </div>\n )\n }\n return this.props.children\n }\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport { snapToWhiteboardGrid } from './grid'\n\ntype WidthMap = Record<string, number>\n\ninterface UseWhiteboardLayoutOptions<T extends WidthMap> {\n widths: T\n startX?: number\n y?: number\n gap?: number\n}\n\ntype PositionMap<T extends WidthMap> = { [K in keyof T]: { x: number; y: number } }\n\nexport function useWhiteboardLayout<T extends WidthMap>({\n widths,\n startX = 20,\n y = 40,\n gap = 20,\n}: UseWhiteboardLayoutOptions<T>) {\n const panelWidth = useMemo(() => {\n const normalized: WidthMap = {}\n for (const [key, value] of Object.entries(widths)) {\n normalized[key] = snapToWhiteboardGrid(value)\n }\n return normalized as T\n }, [widths])\n\n const layout = useMemo(\n () => ({\n startX: snapToWhiteboardGrid(startX),\n y: snapToWhiteboardGrid(y),\n gap: snapToWhiteboardGrid(gap),\n }),\n [startX, y, gap]\n )\n\n const positions = useMemo(() => {\n const next = {} as PositionMap<T>\n let x = layout.startX\n for (const [key, width] of Object.entries(panelWidth)) {\n ;(next as Record<string, { x: number; y: number }>)[key] = { x, y: layout.y }\n x += width + layout.gap\n }\n return next\n }, [layout.gap, layout.startX, layout.y, panelWidth])\n\n return { layout, panelWidth, positions }\n}\n","export function cn(...args: (string | false | null | undefined)[]): string {\n return args.filter(Boolean).join(' ')\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","FloatingPanel","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react"]}
@@ -0,0 +1,185 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode, MutableRefObject, Component } from 'react';
4
+ import * as zustand from 'zustand';
5
+
6
+ interface Props$5 {
7
+ children: ReactNode;
8
+ showMinimap?: boolean;
9
+ minimapLoading?: boolean;
10
+ /** Extra action buttons rendered inside the ZoomBar (e.g. a theme toggle) */
11
+ extraActions?: ReactNode;
12
+ }
13
+ declare function WhiteboardShell({ children, showMinimap, minimapLoading, extraActions }: Props$5): react_jsx_runtime.JSX.Element;
14
+
15
+ type PanelRect = {
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ focusPadding?: number;
21
+ focusMaxScale?: number;
22
+ };
23
+ declare function computeWhiteboardFit(panels: Map<string, PanelRect>, viewportSize: {
24
+ width: number;
25
+ height: number;
26
+ }, padding?: number): {
27
+ scale: number;
28
+ offset: {
29
+ x: number;
30
+ y: number;
31
+ };
32
+ } | null;
33
+ declare function computeWhiteboardRectFocus(rect: PanelRect, viewportSize: {
34
+ width: number;
35
+ height: number;
36
+ }, padding?: number, maxScale?: number): {
37
+ scale: number;
38
+ offset: {
39
+ x: number;
40
+ y: number;
41
+ };
42
+ };
43
+ interface WhiteboardStore {
44
+ offset: {
45
+ x: number;
46
+ y: number;
47
+ };
48
+ scale: number;
49
+ viewportSize: {
50
+ width: number;
51
+ height: number;
52
+ };
53
+ snapToGrid: boolean;
54
+ snapGridSize: number;
55
+ panels: Map<string, PanelRect>;
56
+ resetFns: Map<string, () => void>;
57
+ /** Incremented each time the panel registry changes; subscribe to trigger re-renders. */
58
+ registryVersion: number;
59
+ setOffset: (v: {
60
+ x: number;
61
+ y: number;
62
+ } | ((prev: {
63
+ x: number;
64
+ y: number;
65
+ }) => {
66
+ x: number;
67
+ y: number;
68
+ })) => void;
69
+ setScale: (v: number | ((prev: number) => number)) => void;
70
+ setViewportSize: (v: {
71
+ width: number;
72
+ height: number;
73
+ }) => void;
74
+ setSnapToGrid: (v: boolean | ((prev: boolean) => boolean)) => void;
75
+ register: (id: string, rect: PanelRect) => void;
76
+ unregister: (id: string) => void;
77
+ registerReset: (id: string, fn: () => void) => void;
78
+ unregisterReset: (id: string) => void;
79
+ fitToContent: () => void;
80
+ focusPanel: (rect: PanelRect, options?: {
81
+ padding?: number;
82
+ maxScale?: number;
83
+ }) => void;
84
+ resetWidgets: () => void;
85
+ /** Call on WhiteboardShell mount to discard any stale state from the previous session. */
86
+ resetSession: () => void;
87
+ }
88
+ declare const useWhiteboardStore: zustand.UseBoundStore<zustand.StoreApi<WhiteboardStore>>;
89
+
90
+ interface Props$4 {
91
+ title: ReactNode;
92
+ defaultPosition: {
93
+ x: number;
94
+ y: number;
95
+ };
96
+ width?: number;
97
+ className?: string;
98
+ /** Ref that stays in sync with the panel's current position and rendered size */
99
+ trackRect?: MutableRefObject<PanelRect>;
100
+ /** Show a focus button that zooms the whiteboard to this panel */
101
+ focusable?: boolean;
102
+ /** Optional camera framing controls used by the focus button */
103
+ focusPadding?: number;
104
+ focusMaxScale?: number;
105
+ /** Extra action buttons rendered in the header bar next to the focus button */
106
+ headerActions?: ReactNode;
107
+ children: ReactNode;
108
+ }
109
+ declare const FloatingPanel: react.NamedExoticComponent<Props$4>;
110
+ /** Helper: create a stable rect ref for use with trackRect */
111
+ declare function usePanelRect(initial: {
112
+ x: number;
113
+ y: number;
114
+ }): MutableRefObject<PanelRect>;
115
+ /** Get a position just below a tracked panel */
116
+ declare function belowPanel(rect: PanelRect, gap?: number): {
117
+ x: number;
118
+ y: number;
119
+ };
120
+
121
+ interface Props$3 {
122
+ /** Extra action buttons rendered at the bottom of the zoom bar */
123
+ extraActions?: ReactNode;
124
+ }
125
+ declare function ZoomBar({ extraActions }: Props$3): react_jsx_runtime.JSX.Element;
126
+
127
+ interface Props$2 {
128
+ loading?: boolean;
129
+ }
130
+ declare function Minimap({ loading }: Props$2): react_jsx_runtime.JSX.Element;
131
+
132
+ interface Props$1 {
133
+ open: boolean;
134
+ title: string;
135
+ message: string;
136
+ onConfirm: () => void;
137
+ onCancel: () => void;
138
+ confirmLabel?: string;
139
+ loading?: boolean;
140
+ error?: string | null;
141
+ }
142
+ declare function ConfirmDialog({ open, title, message, onConfirm, onCancel, confirmLabel, loading, error, }: Props$1): react.ReactPortal | null;
143
+
144
+ interface Props {
145
+ children: ReactNode;
146
+ fallbackMessage?: string;
147
+ }
148
+ interface State {
149
+ error: Error | null;
150
+ }
151
+ declare class PanelErrorBoundary extends Component<Props, State> {
152
+ state: State;
153
+ static getDerivedStateFromError(error: Error): State;
154
+ render(): string | number | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | null | undefined;
155
+ }
156
+
157
+ type WidthMap = Record<string, number>;
158
+ interface UseWhiteboardLayoutOptions<T extends WidthMap> {
159
+ widths: T;
160
+ startX?: number;
161
+ y?: number;
162
+ gap?: number;
163
+ }
164
+ type PositionMap<T extends WidthMap> = {
165
+ [K in keyof T]: {
166
+ x: number;
167
+ y: number;
168
+ };
169
+ };
170
+ declare function useWhiteboardLayout<T extends WidthMap>({ widths, startX, y, gap, }: UseWhiteboardLayoutOptions<T>): {
171
+ layout: {
172
+ startX: number;
173
+ y: number;
174
+ gap: number;
175
+ };
176
+ panelWidth: T;
177
+ positions: PositionMap<T>;
178
+ };
179
+
180
+ declare const WHITEBOARD_GRID = 20;
181
+ declare function snapToWhiteboardGrid(value: number): number;
182
+
183
+ declare function cn(...args: (string | false | null | undefined)[]): string;
184
+
185
+ export { ConfirmDialog, FloatingPanel, Minimap, PanelErrorBoundary, type PanelRect, WHITEBOARD_GRID, WhiteboardShell, type WhiteboardStore, ZoomBar, belowPanel, cn, computeWhiteboardFit, computeWhiteboardRectFocus, snapToWhiteboardGrid, usePanelRect, useWhiteboardLayout, useWhiteboardStore };
@@ -0,0 +1,185 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode, MutableRefObject, Component } from 'react';
4
+ import * as zustand from 'zustand';
5
+
6
+ interface Props$5 {
7
+ children: ReactNode;
8
+ showMinimap?: boolean;
9
+ minimapLoading?: boolean;
10
+ /** Extra action buttons rendered inside the ZoomBar (e.g. a theme toggle) */
11
+ extraActions?: ReactNode;
12
+ }
13
+ declare function WhiteboardShell({ children, showMinimap, minimapLoading, extraActions }: Props$5): react_jsx_runtime.JSX.Element;
14
+
15
+ type PanelRect = {
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ focusPadding?: number;
21
+ focusMaxScale?: number;
22
+ };
23
+ declare function computeWhiteboardFit(panels: Map<string, PanelRect>, viewportSize: {
24
+ width: number;
25
+ height: number;
26
+ }, padding?: number): {
27
+ scale: number;
28
+ offset: {
29
+ x: number;
30
+ y: number;
31
+ };
32
+ } | null;
33
+ declare function computeWhiteboardRectFocus(rect: PanelRect, viewportSize: {
34
+ width: number;
35
+ height: number;
36
+ }, padding?: number, maxScale?: number): {
37
+ scale: number;
38
+ offset: {
39
+ x: number;
40
+ y: number;
41
+ };
42
+ };
43
+ interface WhiteboardStore {
44
+ offset: {
45
+ x: number;
46
+ y: number;
47
+ };
48
+ scale: number;
49
+ viewportSize: {
50
+ width: number;
51
+ height: number;
52
+ };
53
+ snapToGrid: boolean;
54
+ snapGridSize: number;
55
+ panels: Map<string, PanelRect>;
56
+ resetFns: Map<string, () => void>;
57
+ /** Incremented each time the panel registry changes; subscribe to trigger re-renders. */
58
+ registryVersion: number;
59
+ setOffset: (v: {
60
+ x: number;
61
+ y: number;
62
+ } | ((prev: {
63
+ x: number;
64
+ y: number;
65
+ }) => {
66
+ x: number;
67
+ y: number;
68
+ })) => void;
69
+ setScale: (v: number | ((prev: number) => number)) => void;
70
+ setViewportSize: (v: {
71
+ width: number;
72
+ height: number;
73
+ }) => void;
74
+ setSnapToGrid: (v: boolean | ((prev: boolean) => boolean)) => void;
75
+ register: (id: string, rect: PanelRect) => void;
76
+ unregister: (id: string) => void;
77
+ registerReset: (id: string, fn: () => void) => void;
78
+ unregisterReset: (id: string) => void;
79
+ fitToContent: () => void;
80
+ focusPanel: (rect: PanelRect, options?: {
81
+ padding?: number;
82
+ maxScale?: number;
83
+ }) => void;
84
+ resetWidgets: () => void;
85
+ /** Call on WhiteboardShell mount to discard any stale state from the previous session. */
86
+ resetSession: () => void;
87
+ }
88
+ declare const useWhiteboardStore: zustand.UseBoundStore<zustand.StoreApi<WhiteboardStore>>;
89
+
90
+ interface Props$4 {
91
+ title: ReactNode;
92
+ defaultPosition: {
93
+ x: number;
94
+ y: number;
95
+ };
96
+ width?: number;
97
+ className?: string;
98
+ /** Ref that stays in sync with the panel's current position and rendered size */
99
+ trackRect?: MutableRefObject<PanelRect>;
100
+ /** Show a focus button that zooms the whiteboard to this panel */
101
+ focusable?: boolean;
102
+ /** Optional camera framing controls used by the focus button */
103
+ focusPadding?: number;
104
+ focusMaxScale?: number;
105
+ /** Extra action buttons rendered in the header bar next to the focus button */
106
+ headerActions?: ReactNode;
107
+ children: ReactNode;
108
+ }
109
+ declare const FloatingPanel: react.NamedExoticComponent<Props$4>;
110
+ /** Helper: create a stable rect ref for use with trackRect */
111
+ declare function usePanelRect(initial: {
112
+ x: number;
113
+ y: number;
114
+ }): MutableRefObject<PanelRect>;
115
+ /** Get a position just below a tracked panel */
116
+ declare function belowPanel(rect: PanelRect, gap?: number): {
117
+ x: number;
118
+ y: number;
119
+ };
120
+
121
+ interface Props$3 {
122
+ /** Extra action buttons rendered at the bottom of the zoom bar */
123
+ extraActions?: ReactNode;
124
+ }
125
+ declare function ZoomBar({ extraActions }: Props$3): react_jsx_runtime.JSX.Element;
126
+
127
+ interface Props$2 {
128
+ loading?: boolean;
129
+ }
130
+ declare function Minimap({ loading }: Props$2): react_jsx_runtime.JSX.Element;
131
+
132
+ interface Props$1 {
133
+ open: boolean;
134
+ title: string;
135
+ message: string;
136
+ onConfirm: () => void;
137
+ onCancel: () => void;
138
+ confirmLabel?: string;
139
+ loading?: boolean;
140
+ error?: string | null;
141
+ }
142
+ declare function ConfirmDialog({ open, title, message, onConfirm, onCancel, confirmLabel, loading, error, }: Props$1): react.ReactPortal | null;
143
+
144
+ interface Props {
145
+ children: ReactNode;
146
+ fallbackMessage?: string;
147
+ }
148
+ interface State {
149
+ error: Error | null;
150
+ }
151
+ declare class PanelErrorBoundary extends Component<Props, State> {
152
+ state: State;
153
+ static getDerivedStateFromError(error: Error): State;
154
+ render(): string | number | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | null | undefined;
155
+ }
156
+
157
+ type WidthMap = Record<string, number>;
158
+ interface UseWhiteboardLayoutOptions<T extends WidthMap> {
159
+ widths: T;
160
+ startX?: number;
161
+ y?: number;
162
+ gap?: number;
163
+ }
164
+ type PositionMap<T extends WidthMap> = {
165
+ [K in keyof T]: {
166
+ x: number;
167
+ y: number;
168
+ };
169
+ };
170
+ declare function useWhiteboardLayout<T extends WidthMap>({ widths, startX, y, gap, }: UseWhiteboardLayoutOptions<T>): {
171
+ layout: {
172
+ startX: number;
173
+ y: number;
174
+ gap: number;
175
+ };
176
+ panelWidth: T;
177
+ positions: PositionMap<T>;
178
+ };
179
+
180
+ declare const WHITEBOARD_GRID = 20;
181
+ declare function snapToWhiteboardGrid(value: number): number;
182
+
183
+ declare function cn(...args: (string | false | null | undefined)[]): string;
184
+
185
+ export { ConfirmDialog, FloatingPanel, Minimap, PanelErrorBoundary, type PanelRect, WHITEBOARD_GRID, WhiteboardShell, type WhiteboardStore, ZoomBar, belowPanel, cn, computeWhiteboardFit, computeWhiteboardRectFocus, snapToWhiteboardGrid, usePanelRect, useWhiteboardLayout, useWhiteboardStore };