{"version":3,"sources":["../src/sheets-adapter.ts"],"names":["useState","useRef","useCallback","useMemo","useEffect","onActivity"],"mappings":"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","file":"sheets-adapter.cjs","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { SheetsBridgeAdapter } from \"./bridges/sheets\";\nimport { onActivity } from \"./presence/registry\";\nimport type { AgentActivityEvent } from \"./presence/types\";\n\n/**\n * Shared-session helpers for `@particle-academy/fancy-sheets`.\n *\n * fancy-sheets' `SheetWorkbook` is already controlled (`data` + `onChange`).\n * The two missing pieces for a clean shared-session experience are:\n *\n *   1. an adapter object the host can hand to {@link registerSheetsBridge}\n *      without writing boilerplate, and\n *   2. a derived `CellHighlightMap` so agent edits visibly pulse on the\n *      humans' screens — wired from the presence registry's per-bridge\n *      activity stream.\n *\n * These are kept as host-side hooks (not part of the bridge itself) so\n * agent-integrations keeps zero hard deps on fancy-sheets. The host\n * imports SheetWorkbook directly and feeds these hooks' outputs into\n * its props.\n *\n *   const wb = useSheetsAdapter(initial, { screenId: \"deal-sheet\" });\n *   const highlights = useSheetsActivityHighlights({ screenId: \"deal-sheet\" });\n *\n *   useEffect(() => {\n *     const bridge = registerSheetsBridge(host, { adapter: wb.adapter });\n *     return bridge.dispose;\n *   }, [host, wb.adapter]);\n *\n *   <SheetWorkbook\n *     data={wb.workbook}\n *     onChange={wb.setWorkbook}\n *     highlights={highlights}\n *     onActiveCellChange={wb.onActiveCellChange}\n *   />\n */\n\n// Loose structural mirror of fancy-sheets' WorkbookData — kept local so\n// this helper doesn't pull a runtime dep on the package. The constraint\n// only names the fields the hook itself reads (`sheets` + `activeSheetId`);\n// it does NOT add a `[k: string]: unknown` index signature, so consumers\n// can pass the real `WorkbookData` from fancy-sheets through without\n// triggering an \"index signature missing\" error.\nexport type WorkbookLike = {\n  sheets: Array<{ id: string; name: string }>;\n  activeSheetId: string;\n};\n\nexport type SheetsAdapterOptions = {\n  /** Tags the bridge's screen id so presence events route correctly. */\n  screenId?: string;\n};\n\nexport type UseSheetsAdapterResult<W extends WorkbookLike> = {\n  /** Controlled workbook state. Wire to `<SheetWorkbook data={…} />`. */\n  workbook: W;\n  /** Setter for the controlled state. Wire to `<SheetWorkbook onChange={…} />`. */\n  setWorkbook: (next: W) => void;\n  /** Wire to `<SheetWorkbook onActiveCellChange={…} />` to track focus. */\n  onActiveCellChange: (address: string) => void;\n  /** Stable adapter to hand to `registerSheetsBridge({ adapter })`. */\n  adapter: SheetsBridgeAdapter;\n  /** Imperative: set the active sheet + cell. Mirrors the adapter's hook. */\n  setActiveCell: (sheetId: string, address: string) => void;\n  /** Read-only: the address last focused (any source). */\n  activeCell: string | null;\n};\n\n/**\n * useSheetsAdapter — one-liner glue between fancy-sheets' SheetWorkbook\n * and the sheets bridge.\n *\n *   const wb = useSheetsAdapter(initialWorkbook, { screenId: \"...\" });\n *\n *   useEffect(() => registerSheetsBridge(host, { adapter: wb.adapter }).dispose,\n *             [host, wb.adapter]);\n *\n *   <SheetWorkbook\n *     data={wb.workbook}\n *     onChange={wb.setWorkbook}\n *     onActiveCellChange={wb.onActiveCellChange}\n *   />\n */\nexport function useSheetsAdapter<W extends WorkbookLike>(\n  initial: W,\n  options: SheetsAdapterOptions = {},\n): UseSheetsAdapterResult<W> {\n  const [workbook, setWorkbook] = useState<W>(initial);\n  const [activeCell, setActiveCellState] = useState<string | null>(null);\n  const workbookRef = useRef(workbook);\n  workbookRef.current = workbook;\n\n  const setActiveCell = useCallback((sheetId: string, address: string) => {\n    setWorkbook((cur) => (cur.activeSheetId === sheetId ? cur : { ...cur, activeSheetId: sheetId }));\n    setActiveCellState(address);\n  }, []);\n\n  const onActiveCellChange = useCallback((address: string) => {\n    setActiveCellState(address);\n  }, []);\n\n  // Adapter must be stable across renders so the bridge's tool catalog\n  // doesn't churn — bind it to refs that hold the latest state + setter.\n  const setWorkbookRef = useRef(setWorkbook);\n  setWorkbookRef.current = setWorkbook;\n\n  const adapter = useMemo<SheetsBridgeAdapter>(\n    () => ({\n      screenId: options.screenId,\n      getWorkbook: () => workbookRef.current as unknown as ReturnType<SheetsBridgeAdapter[\"getWorkbook\"]>,\n      setWorkbook: (next) => setWorkbookRef.current(next as unknown as W),\n      setActiveCell,\n    }),\n    [options.screenId, setActiveCell],\n  );\n\n  return {\n    workbook,\n    setWorkbook,\n    onActiveCellChange,\n    adapter,\n    setActiveCell,\n    activeCell,\n  };\n}\n\n/**\n * Mirror of fancy-sheets' `CellHighlight` — structurally identical so the\n * map returned from {@link useSheetsActivityHighlights} can be passed\n * straight into `<SheetWorkbook highlights={…} />` without any casts.\n *\n * Fields match `@particle-academy/fancy-sheets` exactly:\n *   - `color: string` (required) — border/outline color\n *   - `backgroundColor?: string` — auto-derived from `color` if omitted\n *   - `label?: string` — small badge in the cell's top-left corner\n */\nexport type SheetsCellHighlight = {\n  /** Border/outline color (any CSS color value). Required. */\n  color: string;\n  /** Background tint; if omitted, derived from `color` at low alpha. */\n  backgroundColor?: string;\n  /** Optional label rendered in a chip on the cell. */\n  label?: string;\n};\n\nexport type SheetsCellHighlightMap = Record<string, SheetsCellHighlight>;\n\nexport type SheetsHighlightOptions = {\n  /** Only include events for this screen (recommended). */\n  screenId?: string;\n  /** Highlight TTL in ms before a hit fades from the map. Default 2200. */\n  ttlMs?: number;\n};\n\n/**\n * useSheetsActivityHighlights — subscribe to the presence registry,\n * produce a CellHighlightMap reflecting recent sheet-bridge activity.\n *\n * Pass the result straight into `<SheetWorkbook highlights={…} />`. Each\n * agent edit pulses in the agent's color for `ttlMs` then fades out.\n *\n * The bridge's target shape is `${sheetId}!${address}` — this hook\n * filters for the currently-active sheet and exposes only its cells.\n *\n *   const highlights = useSheetsActivityHighlights({ screenId: \"deal-sheet\" });\n *   <SheetWorkbook highlights={highlights} … />\n */\nexport function useSheetsActivityHighlights(\n  options: SheetsHighlightOptions = {},\n): SheetsCellHighlightMap {\n  const ttlMs = options.ttlMs ?? 2200;\n  const screenId = options.screenId;\n  const [, force] = useState(0);\n  const hitsRef = useRef<\n    Map<string, { event: AgentActivityEvent; expiresAt: number }>\n  >(new Map());\n\n  useEffect(() => {\n    const off = onActivity((event) => {\n      if (event.target?.kind !== \"sheet\") return;\n      if (screenId && event.target.screenId && event.target.screenId !== screenId) return;\n      const elementId = event.target.elementId;\n      if (!elementId || !elementId.includes(\"!\")) return;\n      hitsRef.current.set(elementId, { event, expiresAt: Date.now() + ttlMs });\n      force((n) => n + 1);\n    });\n    return off;\n  }, [screenId, ttlMs]);\n\n  // Periodic GC — drop expired entries and force a re-render.\n  useEffect(() => {\n    const t = window.setInterval(() => {\n      const now = Date.now();\n      let dirty = false;\n      for (const [k, v] of hitsRef.current) {\n        if (v.expiresAt < now) {\n          hitsRef.current.delete(k);\n          dirty = true;\n        }\n      }\n      if (dirty) force((n) => n + 1);\n    }, 500);\n    return () => window.clearInterval(t);\n  }, []);\n\n  // Re-derived on every render — the listener + GC timer above call\n  // `force` so renders happen exactly when the map changes.\n  const out: SheetsCellHighlightMap = {};\n  for (const [elementId, { event }] of hitsRef.current) {\n    const idx = elementId.indexOf(\"!\");\n    const address = elementId.slice(idx + 1);\n    if (!address) continue;\n    const color = event.agentColor ?? \"#a855f7\";\n    out[address] = {\n      color,\n      backgroundColor: color + \"33\",\n      label: event.agentName ?? event.agentId ?? \"agent\",\n    };\n  }\n  return out;\n}\n"]}