@agent-native/core 0.45.1 → 0.46.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.
Files changed (65) hide show
  1. package/README.md +1 -0
  2. package/dist/client/components/LiveCursorOverlay.d.ts +46 -0
  3. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -0
  4. package/dist/client/components/LiveCursorOverlay.js +137 -0
  5. package/dist/client/components/LiveCursorOverlay.js.map +1 -0
  6. package/dist/client/components/PresenceBar.d.ts +11 -1
  7. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  8. package/dist/client/components/PresenceBar.js +39 -7
  9. package/dist/client/components/PresenceBar.js.map +1 -1
  10. package/dist/client/components/RemoteSelectionRings.d.ts +43 -0
  11. package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -0
  12. package/dist/client/components/RemoteSelectionRings.js +116 -0
  13. package/dist/client/components/RemoteSelectionRings.js.map +1 -0
  14. package/dist/client/index.d.ts +4 -0
  15. package/dist/client/index.d.ts.map +1 -1
  16. package/dist/client/index.js +5 -0
  17. package/dist/client/index.js.map +1 -1
  18. package/dist/collab/awareness.d.ts +25 -0
  19. package/dist/collab/awareness.d.ts.map +1 -1
  20. package/dist/collab/awareness.js +42 -5
  21. package/dist/collab/awareness.js.map +1 -1
  22. package/dist/collab/client.d.ts +19 -1
  23. package/dist/collab/client.d.ts.map +1 -1
  24. package/dist/collab/client.js +362 -57
  25. package/dist/collab/client.js.map +1 -1
  26. package/dist/collab/follow-mode.d.ts +56 -0
  27. package/dist/collab/follow-mode.d.ts.map +1 -0
  28. package/dist/collab/follow-mode.js +54 -0
  29. package/dist/collab/follow-mode.js.map +1 -0
  30. package/dist/collab/index.d.ts +3 -1
  31. package/dist/collab/index.d.ts.map +1 -1
  32. package/dist/collab/index.js +5 -1
  33. package/dist/collab/index.js.map +1 -1
  34. package/dist/collab/presence.d.ts +56 -0
  35. package/dist/collab/presence.d.ts.map +1 -0
  36. package/dist/collab/presence.js +98 -0
  37. package/dist/collab/presence.js.map +1 -0
  38. package/dist/collab/routes.d.ts.map +1 -1
  39. package/dist/collab/routes.js +33 -6
  40. package/dist/collab/routes.js.map +1 -1
  41. package/dist/collab/struct-routes.d.ts.map +1 -1
  42. package/dist/collab/struct-routes.js +24 -4
  43. package/dist/collab/struct-routes.js.map +1 -1
  44. package/dist/collab/ydoc-manager.d.ts +13 -0
  45. package/dist/collab/ydoc-manager.d.ts.map +1 -1
  46. package/dist/collab/ydoc-manager.js +51 -15
  47. package/dist/collab/ydoc-manager.js.map +1 -1
  48. package/dist/server/collab-plugin.d.ts +6 -0
  49. package/dist/server/collab-plugin.d.ts.map +1 -1
  50. package/dist/server/collab-plugin.js +105 -5
  51. package/dist/server/collab-plugin.js.map +1 -1
  52. package/dist/server/poll-events.d.ts +5 -0
  53. package/dist/server/poll-events.d.ts.map +1 -1
  54. package/dist/server/poll-events.js +27 -4
  55. package/dist/server/poll-events.js.map +1 -1
  56. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +185 -37
  57. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +12 -2
  58. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +185 -37
  59. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +12 -2
  60. package/docs/content/real-time-collaboration.md +481 -97
  61. package/package.json +1 -1
  62. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +185 -37
  63. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +12 -2
  64. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +185 -37
  65. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +12 -2
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n */\n\nimport { useEffect, useRef, useState, useMemo } from \"react\";\nimport * as Y from \"yjs\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport { agentNativePath } from \"../client/api-path.js\";\nimport { AGENT_CLIENT_ID } from \"./agent-identity.js\";\n\nexport interface CollabUser {\n name: string;\n email: string;\n color: string;\n}\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms. Default: 2000 */\n pollInterval?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\n// Consistent color palette for user cursors\nconst CURSOR_COLORS = [\n \"#f87171\",\n \"#fb923c\",\n \"#fbbf24\",\n \"#a3e635\",\n \"#34d399\",\n \"#22d3ee\",\n \"#60a5fa\",\n \"#14b8a6\",\n \"#f472b6\",\n \"#e879f9\",\n];\n\n/** Hash a string to a consistent color from the palette. */\nexport function emailToColor(email: string): string {\n let hash = 0;\n for (let i = 0; i < email.length; i++) {\n hash = ((hash << 5) - hash + email.charCodeAt(i)) | 0;\n }\n return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];\n}\n\n/** Derive a display name from an email address. */\nexport function emailToName(email: string): string {\n const local = email.split(\"@\")[0] || email;\n return local.charAt(0).toUpperCase() + local.slice(1);\n}\n\nfunction normalizeCollabEmail(email: string): string {\n return email.trim().toLowerCase();\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\nexport function dedupeCollabUsersByEmail(users: CollabUser[]): CollabUser[] {\n const byEmail = new Map<string, CollabUser>();\n for (const user of users) {\n const email = normalizeCollabEmail(user.email);\n if (!email || byEmail.has(email)) continue;\n byEmail.set(email, {\n name: user.name || emailToName(email),\n email,\n color: user.color || emailToColor(email),\n });\n }\n return Array.from(byEmail.values());\n}\n\n/**\n * Leader election for applying authoritative external snapshots into a shared\n * collaborative document.\n *\n * When the agent (or a Notion pull, or any full-document rewrite) writes new\n * content to SQL, the open editor reconciles it into the live Y.Doc with\n * `setContent`. If EVERY connected client did that independently, each would\n * diff the same snapshot into the CRDT and the changed region would be inserted\n * N times (concurrent inserts at the same position → duplicated text). So only\n * ONE client — the \"lead\" — applies the snapshot; every other client receives\n * the result through normal Yjs sync.\n *\n * The lead is the present client with the lowest Yjs `clientID`. The agent's\n * awareness entry uses `AGENT_CLIENT_ID` (max int) so it can never be the lead,\n * and a client editing alone is always the lead. This is deterministic across\n * clients with no coordination round-trip.\n */\nexport function isReconcileLeadClient(\n awareness: Awareness | null | undefined,\n localClientId: number | null | undefined,\n): boolean {\n if (localClientId == null) return false;\n if (!awareness) return true; // standalone / tests — act alone\n\n let hasPeer = false;\n let minVisible = localClientId;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === AGENT_CLIENT_ID) return; // agent never leads\n if (clientId === localClientId) return;\n const s = state as { user?: unknown; visible?: boolean };\n if (!s || !s.user) return; // skip empty/stale entries\n hasPeer = true;\n // Only VISIBLE peers can act; a peer published `visible: false` (backgrounded)\n // is skipped. A peer that hasn't published the field is treated as visible.\n if (s.visible !== false && clientId < minVisible) minVisible = clientId;\n });\n\n // Sole client: always the applier — no other client can duplicate the edit,\n // so single-user agent edits apply even if this tab reports hidden.\n if (!hasPeer) return true;\n\n // With peers present, exactly one VISIBLE client applies (the lowest clientId\n // among visible ones). A backgrounded tab pauses its poll and can't reliably\n // act, so it yields — otherwise an agent edit would never reach the tab the\n // user is actually looking at. The caller re-elects on visibility change.\n const localHidden =\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\";\n if (localHidden) return false;\n return localClientId <= minVisible;\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Stable Y.Doc per docId\n const ydoc = useMemo(() => {\n if (!docId) return null;\n return new Y.Doc();\n }, [docId]);\n\n // Stable Awareness per ydoc\n const awareness = useMemo(() => {\n if (!ydoc) return null;\n return new Awareness(ydoc);\n }, [ydoc]);\n\n const [isLoading, setIsLoading] = useState(!!docId);\n const [isSynced, setIsSynced] = useState(false);\n const [activeUsers, setActiveUsers] = useState<CollabUser[]>([]);\n const [agentActive, setAgentActive] = useState(false);\n const [agentPresent, setAgentPresent] = useState(false);\n // Set when the initial state fetch returns 404/403 — stops the awareness\n // poll so we don't spam the console with errors against a doc that doesn't\n // exist or isn't accessible.\n const [docMissing, setDocMissing] = useState(false);\n const agentTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pollVersionRef = useRef(0);\n\n // Set local awareness state (user info for cursor labels). Also publish this\n // tab's visibility so peers can elect a VISIBLE client to apply external\n // snapshots (see isReconcileLeadClient) — a backgrounded tab pauses its poll\n // and must not hold that role.\n useEffect(() => {\n if (!awareness || !user) return;\n awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n awareness.setLocalStateField(\"visible\", !isDocumentHidden());\n }, [awareness, user?.name, user?.email, user?.color]);\n\n // Track active users from awareness changes\n useEffect(() => {\n if (!awareness) return;\n\n const updateUsers = () => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === ydoc?.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n setActiveUsers(dedupeCollabUsersByEmail(users));\n setAgentPresent(hasAgent);\n };\n\n awareness.on(\"change\", updateUsers);\n return () => {\n awareness.off(\"change\", updateUsers);\n };\n }, [awareness, ydoc]);\n\n // Clean up on unmount or docId change\n useEffect(() => {\n return () => {\n awareness?.destroy();\n ydoc?.destroy();\n };\n }, [ydoc, awareness]);\n\n // Fetch server state and apply to existing doc\n useEffect(() => {\n if (!ydoc || !docId) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setIsSynced(false);\n setDocMissing(false);\n\n fetch(`${baseUrl}/${docId}/state`)\n .then(async (res) => {\n if (cancelled) return;\n if (res.status === 404 || res.status === 403) {\n setDocMissing(true);\n setIsLoading(false);\n setIsSynced(true);\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(ydoc, binary, \"remote\");\n }\n }\n setIsLoading(false);\n setIsSynced(true);\n })\n .catch(() => {\n if (cancelled) return;\n setIsLoading(false);\n setIsSynced(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [ydoc, docId, baseUrl]);\n\n // Send local updates to server\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === \"remote\") return;\n\n fetch(`${baseUrl}/${docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(update),\n requestSource,\n }),\n });\n };\n\n ydoc.on(\"update\", handler);\n return () => {\n ydoc.off(\"update\", handler);\n };\n }, [ydoc, docId, baseUrl, requestSource, docMissing]);\n\n // Poll for remote doc updates + awareness sync\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function schedulePoll() {\n if (stopped) return;\n if (pauseWhenHidden && isDocumentHidden()) return;\n timer = setTimeout(poll, pollInterval);\n }\n\n async function poll() {\n if (stopped) return;\n try {\n // Poll for document updates\n const res = await fetch(\n agentNativePath(\n `/_agent-native/poll?since=${pollVersionRef.current}`,\n ),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n const data = await res.json();\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === docId && evt.update) {\n if (requestSource && evt.requestSource === requestSource) continue;\n Y.applyUpdate(ydoc, base64ToUint8Array(evt.update), \"remote\");\n\n // Show agent presence indicator briefly\n if (evt.requestSource === \"agent\") {\n setAgentActive(true);\n if (agentTimerRef.current) clearTimeout(agentTimerRef.current);\n agentTimerRef.current = setTimeout(\n () => setAgentActive(false),\n 3000,\n );\n }\n }\n }\n\n pollVersionRef.current = version;\n\n try {\n // The poll ring buffer is process-local. Fetching a state-vector diff\n // makes collaboration durable across serverless invocations, process\n // restarts, or any missed poll event.\n const stateVector = uint8ArrayToBase64(Y.encodeStateVector(ydoc));\n const stateRes = await fetch(\n `${baseUrl}/${docId}/state?stateVector=${encodeURIComponent(\n stateVector,\n )}`,\n );\n if (stateRes.ok) {\n const stateData = (await stateRes.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (stateData?.state) {\n const binary = base64ToUint8Array(stateData.state);\n if (binary.length > 2) {\n Y.applyUpdate(ydoc, binary, \"remote\");\n }\n }\n }\n } catch {\n // The next poll retries; awareness should still sync below.\n }\n\n // Sync awareness (cursor positions)\n if (awareness) {\n const localState = awareness.getLocalState();\n if (localState) {\n const awarenessRes = await fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n });\n if (awarenessRes.ok) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n awareness.getStates() as Map<number, unknown>,\n ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n }\n }\n } catch {\n // Network error — retry next interval\n }\n schedulePoll();\n }\n\n function pollNow() {\n if (pauseWhenHidden && isDocumentHidden()) return;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n void poll();\n }\n\n // Publish this tab's visibility to peers. A hidden tab pauses its poll, so\n // we push the state immediately (keepalive) instead of waiting for the next\n // cycle — otherwise peers keep treating a backgrounded tab as the visible\n // lead and an agent edit never lands on the tab the user is looking at.\n function publishVisibility(visible: boolean) {\n if (!awareness) return;\n awareness.setLocalStateField(\"visible\", visible);\n const localState = awareness.getLocalState();\n if (!localState) return;\n fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n keepalive: true,\n }).catch(() => {});\n }\n\n function handleVisibilityChange() {\n const visible = document.visibilityState === \"visible\";\n publishVisibility(visible);\n if (visible) {\n pollNow();\n } else if (pauseWhenHidden && timer) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n if (!pauseWhenHidden || !isDocumentHidden()) {\n void poll();\n }\n window.addEventListener(\"focus\", pollNow);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n window.removeEventListener(\"focus\", pollNow);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [\n ydoc,\n awareness,\n docId,\n pollInterval,\n pauseWhenHidden,\n requestSource,\n baseUrl,\n docMissing,\n ]);\n\n return {\n ydoc,\n awareness,\n isLoading,\n isSynced,\n activeUsers,\n agentActive,\n agentPresent,\n };\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n *\n * Transport improvements (vs previous version):\n * - Local update POSTs are debounced and coalesced with Y.mergeUpdates (~80ms)\n * to avoid per-keystroke requests. The batch is flushed immediately on\n * visibilitychange/pagehide and before each poll/awareness cycle.\n * - GET state?stateVector= is NOT fetched on every poll cycle. It is fetched:\n * (a) on (re)connect / initial load, (b) when a poll response indicates a\n * gap (version jump > ring-buffer size), (c) after applying an update fails,\n * and (d) as a low-frequency safety net every STATE_VECTOR_FETCH_INTERVAL\n * poll cycles (~15×).\n * - Network errors use exponential backoff with jitter (cap ~15s), reset on\n * success.\n * - SSE fast-path: collab events are received push-style from\n * /_agent-native/poll-events (the existing SSE stream). While SSE is\n * healthy the poll loop relaxes to a slow cadence (10–15s). If SSE is\n * unavailable the 2s poll resumes automatically.\n */\n\nimport { useEffect, useRef, useState, useMemo } from \"react\";\nimport * as Y from \"yjs\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport { agentNativePath } from \"../client/api-path.js\";\nimport { AGENT_CLIENT_ID } from \"./agent-identity.js\";\n\nexport interface CollabUser {\n name: string;\n email: string;\n color: string;\n}\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms when SSE is unavailable. Default: 2000 */\n pollInterval?: number;\n /** Poll interval in ms while SSE is healthy. Default: 12000 */\n pollIntervalWithSse?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\n// Consistent color palette for user cursors\nconst CURSOR_COLORS = [\n \"#f87171\",\n \"#fb923c\",\n \"#fbbf24\",\n \"#a3e635\",\n \"#34d399\",\n \"#22d3ee\",\n \"#60a5fa\",\n \"#14b8a6\",\n \"#f472b6\",\n \"#e879f9\",\n];\n\n/** Hash a string to a consistent color from the palette. */\nexport function emailToColor(email: string): string {\n let hash = 0;\n for (let i = 0; i < email.length; i++) {\n hash = ((hash << 5) - hash + email.charCodeAt(i)) | 0;\n }\n return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];\n}\n\n/** Derive a display name from an email address. */\nexport function emailToName(email: string): string {\n const local = email.split(\"@\")[0] || email;\n return local.charAt(0).toUpperCase() + local.slice(1);\n}\n\nfunction normalizeCollabEmail(email: string): string {\n return email.trim().toLowerCase();\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\nexport function dedupeCollabUsersByEmail(users: CollabUser[]): CollabUser[] {\n const byEmail = new Map<string, CollabUser>();\n for (const user of users) {\n const email = normalizeCollabEmail(user.email);\n if (!email || byEmail.has(email)) continue;\n byEmail.set(email, {\n name: user.name || emailToName(email),\n email,\n color: user.color || emailToColor(email),\n });\n }\n return Array.from(byEmail.values());\n}\n\n/**\n * Leader election for applying authoritative external snapshots into a shared\n * collaborative document.\n *\n * When the agent (or a Notion pull, or any full-document rewrite) writes new\n * content to SQL, the open editor reconciles it into the live Y.Doc with\n * `setContent`. If EVERY connected client did that independently, each would\n * diff the same snapshot into the CRDT and the changed region would be inserted\n * N times (concurrent inserts at the same position → duplicated text). So only\n * ONE client — the \"lead\" — applies the snapshot; every other client receives\n * the result through normal Yjs sync.\n *\n * The lead is the present client with the lowest Yjs `clientID`. The agent's\n * awareness entry uses `AGENT_CLIENT_ID` (max int) so it can never be the lead,\n * and a client editing alone is always the lead. This is deterministic across\n * clients with no coordination round-trip.\n */\nexport function isReconcileLeadClient(\n awareness: Awareness | null | undefined,\n localClientId: number | null | undefined,\n): boolean {\n if (localClientId == null) return false;\n if (!awareness) return true; // standalone / tests — act alone\n\n let hasPeer = false;\n let minVisible = localClientId;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === AGENT_CLIENT_ID) return; // agent never leads\n if (clientId === localClientId) return;\n const s = state as { user?: unknown; visible?: boolean };\n if (!s || !s.user) return; // skip empty/stale entries\n hasPeer = true;\n // Only VISIBLE peers can act; a peer published `visible: false` (backgrounded)\n // is skipped. A peer that hasn't published the field is treated as visible.\n if (s.visible !== false && clientId < minVisible) minVisible = clientId;\n });\n\n // Sole client: always the applier — no other client can duplicate the edit,\n // so single-user agent edits apply even if this tab reports hidden.\n if (!hasPeer) return true;\n\n // With peers present, exactly one VISIBLE client applies (the lowest clientId\n // among visible ones). A backgrounded tab pauses its poll and can't reliably\n // act, so it yields — otherwise an agent edit would never reach the tab the\n // user is actually looking at. The caller re-elects on visibility change.\n const localHidden =\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\";\n if (localHidden) return false;\n return localClientId <= minVisible;\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\n/** Debounce delay for coalescing local Yjs update POSTs (ms). */\nconst UPDATE_DEBOUNCE_MS = 80;\n\n/** Fetch state-vector every N poll cycles as a low-frequency safety net. */\nconst STATE_VECTOR_FETCH_INTERVAL = 15;\n\n/** Poll ring-buffer size on the server (MAX_BUFFER in poll.ts). */\nconst POLL_RING_BUFFER_SIZE = 200;\n\n/** Exponential backoff: base delay (ms), multiplier, cap (ms). */\nconst BACKOFF_BASE_MS = 500;\nconst BACKOFF_MAX_MS = 15_000;\n\nfunction calcBackoff(consecutiveErrors: number): number {\n const exp = Math.min(consecutiveErrors, 10);\n const delay = BACKOFF_BASE_MS * Math.pow(2, exp);\n // Add jitter: ±25%\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\n return Math.min(delay + jitter, BACKOFF_MAX_MS);\n}\n\n// ---------------------------------------------------------------------------\n// Fast awareness helper — throttled per (docId, ydocId) pair so multiple\n// setLocalStateField calls within a 150ms window are coalesced into one POST.\n// ---------------------------------------------------------------------------\n\nconst _awarenessThrottleTimers = new Map<\n string,\n ReturnType<typeof setTimeout>\n>();\n\nfunction scheduleAwarenessPush(\n baseUrl: string,\n docId: string,\n clientId: number,\n getState: () => Record<string, unknown> | null,\n): void {\n if (typeof window === \"undefined\") return;\n const key = `${docId}::${clientId}`;\n if (_awarenessThrottleTimers.has(key)) return; // already scheduled\n\n const timer = setTimeout(() => {\n _awarenessThrottleTimers.delete(key);\n const state = getState();\n if (!state) return;\n fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ clientId, state: JSON.stringify(state) }),\n }).catch(() => {}); // best-effort; poll cycle is the baseline fallback\n }, 150);\n\n _awarenessThrottleTimers.set(key, timer);\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pollIntervalWithSse = 12000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Stable Y.Doc per docId\n const ydoc = useMemo(() => {\n if (!docId) return null;\n return new Y.Doc();\n }, [docId]);\n\n // Stable Awareness per ydoc\n const awareness = useMemo(() => {\n if (!ydoc) return null;\n return new Awareness(ydoc);\n }, [ydoc]);\n\n const [isLoading, setIsLoading] = useState(!!docId);\n const [isSynced, setIsSynced] = useState(false);\n const [activeUsers, setActiveUsers] = useState<CollabUser[]>([]);\n const [agentActive, setAgentActive] = useState(false);\n const [agentPresent, setAgentPresent] = useState(false);\n // Set when the initial state fetch returns 404/403 — stops the awareness\n // poll so we don't spam the console with errors against a doc that doesn't\n // exist or isn't accessible.\n const [docMissing, setDocMissing] = useState(false);\n const agentTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pollVersionRef = useRef(0);\n\n // Set local awareness state (user info for cursor labels). Also publish this\n // tab's visibility so peers can elect a VISIBLE client to apply external\n // snapshots (see isReconcileLeadClient) — a backgrounded tab pauses its poll\n // and must not hold that role.\n useEffect(() => {\n if (!awareness || !user) return;\n awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n awareness.setLocalStateField(\"visible\", !isDocumentHidden());\n }, [awareness, user?.name, user?.email, user?.color]);\n\n // Fast awareness push: whenever local state changes (e.g. cursor moves,\n // setPresence() calls), schedule a throttled POST so peers receive updates\n // at ~150ms instead of waiting for the next 2s poll cycle. The poll cycle\n // remains the authoritative baseline (cursors degrade gracefully without SSE).\n useEffect(() => {\n if (!awareness || !ydoc || !docId || !user) return;\n const clientId = ydoc.clientID;\n\n const onLocalStateChange = () => {\n scheduleAwarenessPush(\n baseUrl,\n docId,\n clientId,\n () => awareness.getLocalState() as Record<string, unknown> | null,\n );\n };\n\n // awareness emits \"change\" for local state changes too (when origin is \"local\").\n awareness.on(\"change\", onLocalStateChange);\n return () => {\n awareness.off(\"change\", onLocalStateChange);\n };\n }, [awareness, ydoc, docId, baseUrl, user]);\n\n // Track active users from awareness changes\n useEffect(() => {\n if (!awareness) return;\n\n const updateUsers = () => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === ydoc?.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n setActiveUsers(dedupeCollabUsersByEmail(users));\n setAgentPresent(hasAgent);\n };\n\n awareness.on(\"change\", updateUsers);\n return () => {\n awareness.off(\"change\", updateUsers);\n };\n }, [awareness, ydoc]);\n\n // Clean up on unmount or docId change\n useEffect(() => {\n return () => {\n awareness?.destroy();\n ydoc?.destroy();\n };\n }, [ydoc, awareness]);\n\n // Fetch server state and apply to existing doc\n useEffect(() => {\n if (!ydoc || !docId) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setIsSynced(false);\n setDocMissing(false);\n\n fetch(`${baseUrl}/${docId}/state`)\n .then(async (res) => {\n if (cancelled) return;\n if (res.status === 404 || res.status === 403) {\n setDocMissing(true);\n setIsLoading(false);\n setIsSynced(true);\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(ydoc, binary, \"remote\");\n }\n }\n setIsLoading(false);\n setIsSynced(true);\n })\n .catch(() => {\n if (cancelled) return;\n setIsLoading(false);\n setIsSynced(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [ydoc, docId, baseUrl]);\n\n // Send local updates to server — debounced and coalesced with Y.mergeUpdates.\n //\n // Instead of firing one POST per Yjs update (one per keystroke), we accumulate\n // updates in a buffer for UPDATE_DEBOUNCE_MS then merge them into a single\n // request. The buffer is also flushed immediately on visibilitychange/pagehide\n // and before each poll/awareness cycle so we don't hold stale local state.\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n let pendingUpdates: Uint8Array[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n const flushPendingUpdates = (keepalive = false) => {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (pendingUpdates.length === 0) return;\n const toSend = pendingUpdates;\n pendingUpdates = [];\n\n const merged = toSend.length === 1 ? toSend[0] : Y.mergeUpdates(toSend);\n fetch(`${baseUrl}/${docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(merged),\n requestSource,\n }),\n ...(keepalive ? { keepalive: true } : {}),\n }).catch(() => {});\n };\n\n // Expose flush to the poll loop via a ref so it can flush before each cycle.\n // We store the flusher in a closure-captured variable; the poll effect\n // below reads it through the shared `pendingFlushRef`.\n (ydoc as any).__collabFlush = flushPendingUpdates;\n\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === \"remote\") return;\n pendingUpdates.push(update);\n if (flushTimer) clearTimeout(flushTimer);\n flushTimer = setTimeout(flushPendingUpdates, UPDATE_DEBOUNCE_MS);\n };\n\n const handlePageHide = () => {\n flushPendingUpdates(true /* keepalive */);\n };\n\n ydoc.on(\"update\", handler);\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", handlePageHide);\n }\n\n return () => {\n ydoc.off(\"update\", handler);\n delete (ydoc as any).__collabFlush;\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pagehide\", handlePageHide);\n }\n // Flush any remaining updates on teardown\n flushPendingUpdates(true);\n };\n }, [ydoc, docId, baseUrl, requestSource, docMissing]);\n\n // Poll for remote doc updates + awareness sync, with SSE fast-path.\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let consecutiveErrors = 0;\n let pollCycleCount = 0;\n // Track the last version we successfully polled. Used to detect ring-buffer\n // overflow (version gap larger than the ring buffer).\n let lastPolledVersion = pollVersionRef.current;\n\n // SSE connection state. When SSE is healthy, poll interval is relaxed.\n let sseActive = false;\n let sseEventSource: EventSource | null = null;\n\n // ── SSE fast-path ────────────────────────────────────────────────\n // Wire into the existing /_agent-native/poll-events SSE stream.\n // Collab update events arrive push-style; we apply them immediately,\n // avoiding ~2s polling latency for peer edits.\n //\n // NOTE: SSE events are subject to the same server-side access scoping as\n // polling — the server only pushes events that canSeeChangeForUser allows.\n // The server tags collab events with owner/orgId (security commit).\n function initSSE() {\n if (typeof EventSource === \"undefined\") return;\n try {\n const es = new EventSource(\n agentNativePath(\"/_agent-native/poll-events\"),\n );\n sseEventSource = es;\n\n es.onopen = () => {\n sseActive = true;\n consecutiveErrors = 0;\n };\n\n es.onmessage = (ev) => {\n try {\n const change = JSON.parse(ev.data) as {\n source?: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n version?: number;\n };\n\n if (\n change.source === \"collab\" &&\n change.docId === docId &&\n change.update\n ) {\n if (requestSource && change.requestSource === requestSource)\n return;\n try {\n Y.applyUpdate(\n ydoc,\n base64ToUint8Array(change.update),\n \"remote\",\n );\n } catch {\n // Malformed update — trigger state-vector fetch on next poll\n }\n\n if (change.requestSource === \"agent\") {\n setAgentActive(true);\n if (agentTimerRef.current) clearTimeout(agentTimerRef.current);\n agentTimerRef.current = setTimeout(\n () => setAgentActive(false),\n 3000,\n );\n }\n }\n\n // Keep pollVersionRef updated from SSE events so the poll loop\n // starts from the right version when SSE drops.\n if (typeof change.version === \"number\") {\n pollVersionRef.current = Math.max(\n pollVersionRef.current,\n change.version,\n );\n }\n } catch {\n // Ignore malformed events\n }\n };\n\n es.onerror = () => {\n sseActive = false;\n es.close();\n sseEventSource = null;\n // Retry SSE after a short delay\n if (!stopped) {\n setTimeout(initSSE, 5_000 + Math.random() * 5_000);\n }\n };\n } catch {\n // SSE not available (edge runtime, etc.) — fall back to polling only\n sseActive = false;\n }\n }\n\n // Only set up SSE in browser environments that support it.\n if (typeof EventSource !== \"undefined\") {\n initSSE();\n }\n\n // ── Poll loop ───────────────────────────────────────────────────\n function getActivePollInterval(): number {\n return sseActive ? pollIntervalWithSse : pollInterval;\n }\n\n function schedulePoll() {\n if (stopped) return;\n if (pauseWhenHidden && isDocumentHidden()) return;\n timer = setTimeout(poll, getActivePollInterval());\n }\n\n async function fetchStateVector(): Promise<void> {\n try {\n const stateVector = uint8ArrayToBase64(Y.encodeStateVector(ydoc));\n const stateRes = await fetch(\n `${baseUrl}/${docId}/state?stateVector=${encodeURIComponent(stateVector)}`,\n );\n if (stateRes.ok) {\n const stateData = (await stateRes.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (stateData?.state) {\n const binary = base64ToUint8Array(stateData.state);\n if (binary.length > 2) {\n Y.applyUpdate(ydoc, binary, \"remote\");\n }\n }\n }\n } catch {\n // Non-fatal; the next poll cycle will retry\n }\n }\n\n async function poll() {\n if (stopped) return;\n\n // Flush any pending local updates before polling so the server has the\n // latest state before we read remote changes.\n const flush = (ydoc as any).__collabFlush as\n | ((keepalive?: boolean) => void)\n | undefined;\n flush?.();\n\n try {\n const res = await fetch(\n agentNativePath(\n `/_agent-native/poll?since=${pollVersionRef.current}`,\n ),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n\n const data = await res.json();\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n // Detect ring-buffer overflow: if the version jumped by more than the\n // ring buffer size, some events were evicted and we need a state-vector\n // fetch to reconcile the gap.\n const versionGap = version - lastPolledVersion;\n const hadGap = versionGap > POLL_RING_BUFFER_SIZE;\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === docId && evt.update) {\n if (requestSource && evt.requestSource === requestSource) continue;\n try {\n Y.applyUpdate(ydoc, base64ToUint8Array(evt.update), \"remote\");\n } catch {\n // Failed to apply — fetch full state-vector below\n await fetchStateVector();\n }\n\n if (evt.requestSource === \"agent\") {\n setAgentActive(true);\n if (agentTimerRef.current) clearTimeout(agentTimerRef.current);\n agentTimerRef.current = setTimeout(\n () => setAgentActive(false),\n 3000,\n );\n }\n }\n }\n\n pollVersionRef.current = version;\n lastPolledVersion = version;\n pollCycleCount++;\n consecutiveErrors = 0;\n\n // Fetch state-vector only when needed:\n // 1. Ring-buffer overflow detected (missed events).\n // 2. Low-frequency safety net every STATE_VECTOR_FETCH_INTERVAL cycles.\n // 3. NOT on every cycle (the previous behavior causing 3 requests/cycle).\n const shouldFetchStateVector =\n hadGap || pollCycleCount % STATE_VECTOR_FETCH_INTERVAL === 0;\n\n if (shouldFetchStateVector) {\n await fetchStateVector();\n }\n\n // Sync awareness (cursor positions)\n if (awareness) {\n const localState = awareness.getLocalState();\n if (localState) {\n try {\n const awarenessRes = await fetch(\n `${baseUrl}/${docId}/awareness`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n },\n );\n if (awarenessRes.ok) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n awareness.getStates() as Map<number, unknown>,\n ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n } catch {\n // Awareness sync failure is non-fatal\n }\n }\n }\n } catch {\n // Network error — exponential backoff\n consecutiveErrors++;\n const backoff = calcBackoff(consecutiveErrors);\n if (!stopped) {\n timer = setTimeout(poll, backoff);\n return;\n }\n }\n\n schedulePoll();\n }\n\n function pollNow() {\n if (pauseWhenHidden && isDocumentHidden()) return;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n void poll();\n }\n\n // Publish this tab's visibility to peers. A hidden tab pauses its poll, so\n // we push the state immediately (keepalive) instead of waiting for the next\n // cycle — otherwise peers keep treating a backgrounded tab as the visible\n // lead and an agent edit never lands on the tab the user is actually viewing.\n function publishVisibility(visible: boolean) {\n if (!awareness) return;\n awareness.setLocalStateField(\"visible\", visible);\n const localState = awareness.getLocalState();\n if (!localState) return;\n fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n keepalive: true,\n }).catch(() => {});\n }\n\n function handleVisibilityChange() {\n const visible = document.visibilityState === \"visible\";\n publishVisibility(visible);\n if (visible) {\n // Also flush any pending updates when coming back into view\n const flush = (ydoc as any).__collabFlush as\n | ((keepalive?: boolean) => void)\n | undefined;\n flush?.();\n pollNow();\n } else if (pauseWhenHidden && timer) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n if (!pauseWhenHidden || !isDocumentHidden()) {\n void poll();\n }\n window.addEventListener(\"focus\", pollNow);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n if (sseEventSource) {\n sseEventSource.close();\n sseEventSource = null;\n }\n window.removeEventListener(\"focus\", pollNow);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [\n ydoc,\n awareness,\n docId,\n pollInterval,\n pollIntervalWithSse,\n pauseWhenHidden,\n requestSource,\n baseUrl,\n docMissing,\n ]);\n\n // SSE fast-path for awareness: subscribe to the poll-events stream and\n // apply any awareness-change events immediately so peers receive cursor\n // moves push-style without waiting for the next poll cycle.\n // Polling fallback keeps working when SSE is unavailable.\n useEffect(() => {\n if (!ydoc || !docId || !awareness || typeof EventSource === \"undefined\") {\n return;\n }\n const sseUrl = agentNativePath(\"/_agent-native/poll-events\");\n let source: EventSource | null = null;\n let stopped = false;\n\n function connect() {\n if (stopped || source) return;\n source = new EventSource(sseUrl);\n source.onmessage = (msg) => {\n if (stopped) return;\n try {\n const data = JSON.parse(msg.data as string) as {\n source?: string;\n type?: string;\n docId?: string;\n states?: Array<{ clientId: number; state: string }>;\n };\n if (\n data.source !== \"awareness\" ||\n data.type !== \"awareness-change\" ||\n data.docId !== docId\n ) {\n return;\n }\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of data.states ?? []) {\n try {\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: JSON.parse(remote.state),\n });\n } catch {\n // Invalid state entry — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n awareness.getStates() as Map<number, unknown>,\n ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n awareness.emit(\"change\", [changes, \"remote\"]);\n }\n } catch {\n // Ignore malformed SSE frames; poll cycle is the safety net.\n }\n };\n source.onerror = () => {\n // On permanent close let go of the ref so re-focus can reconnect.\n if (source && source.readyState === EventSource.CLOSED) {\n source = null;\n }\n };\n }\n\n connect();\n\n function onFocus() {\n if (!source || source.readyState === EventSource.CLOSED) {\n source?.close();\n source = null;\n connect();\n }\n }\n\n window.addEventListener(\"focus\", onFocus);\n return () => {\n stopped = true;\n source?.close();\n source = null;\n window.removeEventListener(\"focus\", onFocus);\n };\n }, [ydoc, docId, awareness]);\n\n return {\n ydoc,\n awareness,\n isLoading,\n isSynced,\n activeUsers,\n agentActive,\n agentPresent,\n };\n}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Follow-mode — let a user "follow" another participant so their viewport
3
+ * tracks wherever that participant navigates.
4
+ *
5
+ * The followed participant publishes their viewport via setPresence().
6
+ * useFollowUser watches their presence and calls a callback whenever it
7
+ * changes so the consumer can scroll/zoom to match.
8
+ *
9
+ * Viewport publishing is opt-in via setPresence("viewport", { ... }).
10
+ * This module handles the consumer (follower) side only.
11
+ */
12
+ import type { OtherPresence } from "./presence.js";
13
+ export interface ViewportDescriptor {
14
+ /** Active document / file ID being viewed. */
15
+ fileId?: string;
16
+ /** Scroll position. */
17
+ scrollX?: number;
18
+ scrollY?: number;
19
+ /** Zoom level (1.0 = 100%). */
20
+ zoom?: number;
21
+ /** Normalized cursor last seen (0–1). */
22
+ cursorX?: number;
23
+ cursorY?: number;
24
+ }
25
+ export interface UseFollowUserOptions {
26
+ /** Remote participants list from usePresence. */
27
+ others: OtherPresence[];
28
+ /**
29
+ * The client ID to follow. Pass null / undefined to stop following.
30
+ */
31
+ followingId: number | null | undefined;
32
+ /**
33
+ * Key inside presence payload that carries viewport info.
34
+ * Default: "viewport"
35
+ */
36
+ viewportKey?: string;
37
+ /**
38
+ * Called whenever the followed participant's viewport changes.
39
+ * Consumers should scroll/zoom to the described viewport in here.
40
+ */
41
+ onViewport: (viewport: ViewportDescriptor) => void;
42
+ }
43
+ export interface UseFollowUserResult {
44
+ /** The currently followed client ID, or null. */
45
+ followingId: number | null;
46
+ /** True when actively following someone. */
47
+ isFollowing: boolean;
48
+ /** Stop following the current target. */
49
+ stopFollowing: () => void;
50
+ }
51
+ /**
52
+ * Watch a specific participant's viewport presence and invoke onViewport
53
+ * when it changes.
54
+ */
55
+ export declare function useFollowUser({ others, followingId, viewportKey, onViewport, }: UseFollowUserOptions): UseFollowUserResult;
56
+ //# sourceMappingURL=follow-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow-mode.d.ts","sourceRoot":"","sources":["../../src/collab/follow-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB;;OAEG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4CAA4C;IAC5C,WAAW,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,WAAW,EACX,WAAwB,EACxB,UAAU,GACX,EAAE,oBAAoB,GAAG,mBAAmB,CAwC5C"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Follow-mode — let a user "follow" another participant so their viewport
3
+ * tracks wherever that participant navigates.
4
+ *
5
+ * The followed participant publishes their viewport via setPresence().
6
+ * useFollowUser watches their presence and calls a callback whenever it
7
+ * changes so the consumer can scroll/zoom to match.
8
+ *
9
+ * Viewport publishing is opt-in via setPresence("viewport", { ... }).
10
+ * This module handles the consumer (follower) side only.
11
+ */
12
+ import { useEffect, useRef, useCallback, useState } from "react";
13
+ /**
14
+ * Watch a specific participant's viewport presence and invoke onViewport
15
+ * when it changes.
16
+ */
17
+ export function useFollowUser({ others, followingId, viewportKey = "viewport", onViewport, }) {
18
+ // Stable ref so the effect doesn't re-subscribe on every callback identity change.
19
+ const onViewportRef = useRef(onViewport);
20
+ onViewportRef.current = onViewport;
21
+ const [activeId, setActiveId] = useState(followingId ?? null);
22
+ // Sync activeId when followingId prop changes.
23
+ useEffect(() => {
24
+ setActiveId(followingId ?? null);
25
+ }, [followingId]);
26
+ const stopFollowing = useCallback(() => {
27
+ setActiveId(null);
28
+ }, []);
29
+ // Watch the target's viewport in the others array.
30
+ const prevViewportRef = useRef(null);
31
+ useEffect(() => {
32
+ if (activeId == null) {
33
+ prevViewportRef.current = null;
34
+ return;
35
+ }
36
+ const target = others.find((o) => o.clientId === activeId);
37
+ if (!target)
38
+ return;
39
+ const vp = target.presence[viewportKey];
40
+ if (!vp)
41
+ return;
42
+ const serialized = JSON.stringify(vp);
43
+ if (serialized === prevViewportRef.current)
44
+ return; // No change.
45
+ prevViewportRef.current = serialized;
46
+ onViewportRef.current(vp);
47
+ }, [others, activeId, viewportKey]);
48
+ return {
49
+ followingId: activeId,
50
+ isFollowing: activeId != null,
51
+ stopFollowing,
52
+ };
53
+ }
54
+ //# sourceMappingURL=follow-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow-mode.js","sourceRoot":"","sources":["../../src/collab/follow-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AA4CjE;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,MAAM,EACN,WAAW,EACX,WAAW,GAAG,UAAU,EACxB,UAAU,GACW;IACrB,mFAAmF;IACnF,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACzC,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC;IAEnC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAgB,WAAW,IAAI,IAAI,CAAC,CAAC;IAE7E,+CAA+C;IAC/C,SAAS,CAAC,GAAG,EAAE;QACb,WAAW,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;IACnC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,mDAAmD;IACnD,MAAM,eAAe,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAC;IACpD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,eAAe,CAAC,OAAO,GAAG,IAAI,CAAC;YAC/B,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAmC,CAAC;QAC1E,IAAI,CAAC,EAAE;YAAE,OAAO;QAEhB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,UAAU,KAAK,eAAe,CAAC,OAAO;YAAE,OAAO,CAAC,aAAa;QACjE,eAAe,CAAC,OAAO,GAAG,UAAU,CAAC;QACrC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IAEpC,OAAO;QACL,WAAW,EAAE,QAAQ;QACrB,WAAW,EAAE,QAAQ,IAAI,IAAI;QAC7B,aAAa;KACd,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Follow-mode — let a user \"follow\" another participant so their viewport\n * tracks wherever that participant navigates.\n *\n * The followed participant publishes their viewport via setPresence().\n * useFollowUser watches their presence and calls a callback whenever it\n * changes so the consumer can scroll/zoom to match.\n *\n * Viewport publishing is opt-in via setPresence(\"viewport\", { ... }).\n * This module handles the consumer (follower) side only.\n */\n\nimport { useEffect, useRef, useCallback, useState } from \"react\";\nimport type { OtherPresence } from \"./presence.js\";\n\nexport interface ViewportDescriptor {\n /** Active document / file ID being viewed. */\n fileId?: string;\n /** Scroll position. */\n scrollX?: number;\n scrollY?: number;\n /** Zoom level (1.0 = 100%). */\n zoom?: number;\n /** Normalized cursor last seen (0–1). */\n cursorX?: number;\n cursorY?: number;\n}\n\nexport interface UseFollowUserOptions {\n /** Remote participants list from usePresence. */\n others: OtherPresence[];\n /**\n * The client ID to follow. Pass null / undefined to stop following.\n */\n followingId: number | null | undefined;\n /**\n * Key inside presence payload that carries viewport info.\n * Default: \"viewport\"\n */\n viewportKey?: string;\n /**\n * Called whenever the followed participant's viewport changes.\n * Consumers should scroll/zoom to the described viewport in here.\n */\n onViewport: (viewport: ViewportDescriptor) => void;\n}\n\nexport interface UseFollowUserResult {\n /** The currently followed client ID, or null. */\n followingId: number | null;\n /** True when actively following someone. */\n isFollowing: boolean;\n /** Stop following the current target. */\n stopFollowing: () => void;\n}\n\n/**\n * Watch a specific participant's viewport presence and invoke onViewport\n * when it changes.\n */\nexport function useFollowUser({\n others,\n followingId,\n viewportKey = \"viewport\",\n onViewport,\n}: UseFollowUserOptions): UseFollowUserResult {\n // Stable ref so the effect doesn't re-subscribe on every callback identity change.\n const onViewportRef = useRef(onViewport);\n onViewportRef.current = onViewport;\n\n const [activeId, setActiveId] = useState<number | null>(followingId ?? null);\n\n // Sync activeId when followingId prop changes.\n useEffect(() => {\n setActiveId(followingId ?? null);\n }, [followingId]);\n\n const stopFollowing = useCallback(() => {\n setActiveId(null);\n }, []);\n\n // Watch the target's viewport in the others array.\n const prevViewportRef = useRef<string | null>(null);\n useEffect(() => {\n if (activeId == null) {\n prevViewportRef.current = null;\n return;\n }\n const target = others.find((o) => o.clientId === activeId);\n if (!target) return;\n\n const vp = target.presence[viewportKey] as ViewportDescriptor | undefined;\n if (!vp) return;\n\n const serialized = JSON.stringify(vp);\n if (serialized === prevViewportRef.current) return; // No change.\n prevViewportRef.current = serialized;\n onViewportRef.current(vp);\n }, [others, activeId, viewportKey]);\n\n return {\n followingId: activeId,\n isFollowing: activeId != null,\n stopFollowing,\n };\n}\n"]}
@@ -8,5 +8,7 @@ export { seedYDocFromJson, yMapToJson, yArrayToJson, yDocToJson, applyJsonDiff,
8
8
  export { postCollabJson, getCollabJson, postCollabPatch, } from "./struct-routes.js";
9
9
  export { AGENT_CLIENT_ID, DEFAULT_AGENT_IDENTITY, type AgentIdentity, } from "./agent-identity.js";
10
10
  export { agentEnterDocument, agentLeaveDocument, agentUpdateSelection, agentApplyEditsIncrementally, agentApplyPatchesIncrementally, } from "./agent-presence.js";
11
- export { getDocAwareness, type AwarenessEntry } from "./awareness.js";
11
+ export { getDocAwareness, getAwarenessEmitter, emitAwarenessChange, AWARENESS_CHANGE_EVENT, type AwarenessEntry, type AwarenessChangeEvent, } from "./awareness.js";
12
+ export { usePresence, toNormalized, fromNormalized, type OtherPresence, type PresencePayload, type UsePresenceResult, type NormalizedPoint, } from "./presence.js";
13
+ export { useFollowUser, type UseFollowUserOptions, type UseFollowUserResult, type ViewportDescriptor, } from "./follow-mode.js";
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collab/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EACb,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,MAAM,EACN,WAAW,EACX,SAAS,EACT,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,OAAO,EACP,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG3E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGrE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,KAAK,OAAO,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collab/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EACb,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,MAAM,EACN,WAAW,EACX,SAAS,EACT,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,OAAO,EACP,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG3E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGrE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,KAAK,OAAO,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAC"}
@@ -20,5 +20,9 @@ export { AGENT_CLIENT_ID, DEFAULT_AGENT_IDENTITY, } from "./agent-identity.js";
20
20
  // Agent presence lifecycle
21
21
  export { agentEnterDocument, agentLeaveDocument, agentUpdateSelection, agentApplyEditsIncrementally, agentApplyPatchesIncrementally, } from "./agent-presence.js";
22
22
  // Awareness (re-export for agent-presence consumers)
23
- export { getDocAwareness } from "./awareness.js";
23
+ export { getDocAwareness, getAwarenessEmitter, emitAwarenessChange, AWARENESS_CHANGE_EVENT, } from "./awareness.js";
24
+ // Presence kit
25
+ export { usePresence, toNormalized, fromNormalized, } from "./presence.js";
26
+ // Follow mode
27
+ export { useFollowUser, } from "./follow-mode.js";
24
28
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/collab/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAE3C,UAAU;AACV,OAAO,EACL,aAAa,EACb,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAEtB,eAAe;AACf,OAAO,EACL,MAAM,EACN,WAAW,EACX,SAAS,EACT,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,OAAO,EACP,YAAY,GACb,MAAM,mBAAmB,CAAC;AAE3B,yBAAyB;AACzB,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE3E,qBAAqB;AACrB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAErE,UAAU;AACV,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,cAAc,CAAC;AAEtB,iBAAiB;AACjB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB,uCAAuC;AACvC,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,GAEjB,MAAM,kBAAkB,CAAC;AAE1B,iCAAiC;AACjC,OAAO,EACL,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,iBAAiB;AACjB,OAAO,EACL,eAAe,EACf,sBAAsB,GAEvB,MAAM,qBAAqB,CAAC;AAE7B,2BAA2B;AAC3B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAE7B,qDAAqD;AACrD,OAAO,EAAE,eAAe,EAAuB,MAAM,gBAAgB,CAAC","sourcesContent":["// Public API for @agent-native/core/collab\n\n// Storage\nexport {\n loadYDocState,\n saveYDocState,\n hasCollabState,\n deleteCollabState,\n uint8ArrayToBase64,\n base64ToUint8Array,\n} from \"./storage.js\";\n\n// YDoc manager\nexport {\n getDoc,\n applyUpdate,\n applyText,\n getText,\n getState,\n getIncUpdate,\n seedFromText,\n releaseDoc,\n searchAndReplace,\n applyJson,\n applyPatchOps,\n getJson,\n seedFromJson,\n} from \"./ydoc-manager.js\";\n\n// XmlFragment operations\nexport { searchAndReplaceInYXml, extractTextFromYXml } from \"./xml-ops.js\";\n\n// Text-to-Yjs bridge\nexport { applyTextToYDoc, initYDocWithText } from \"./text-to-yjs.js\";\n\n// Emitter\nexport {\n getCollabEmitter,\n emitCollabUpdate,\n type CollabEvent,\n} from \"./emitter.js\";\n\n// Route handlers\nexport {\n getCollabState,\n postCollabUpdate,\n postCollabText,\n postCollabSearchReplace,\n} from \"./routes.js\";\n\n// JSON-to-Yjs bridge (structured data)\nexport {\n seedYDocFromJson,\n yMapToJson,\n yArrayToJson,\n yDocToJson,\n applyJsonDiff,\n applyJsonPatch,\n initYDocWithJson,\n type PatchOp,\n} from \"./json-to-yjs.js\";\n\n// Structured data route handlers\nexport {\n postCollabJson,\n getCollabJson,\n postCollabPatch,\n} from \"./struct-routes.js\";\n\n// Agent identity\nexport {\n AGENT_CLIENT_ID,\n DEFAULT_AGENT_IDENTITY,\n type AgentIdentity,\n} from \"./agent-identity.js\";\n\n// Agent presence lifecycle\nexport {\n agentEnterDocument,\n agentLeaveDocument,\n agentUpdateSelection,\n agentApplyEditsIncrementally,\n agentApplyPatchesIncrementally,\n} from \"./agent-presence.js\";\n\n// Awareness (re-export for agent-presence consumers)\nexport { getDocAwareness, type AwarenessEntry } from \"./awareness.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/collab/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAE3C,UAAU;AACV,OAAO,EACL,aAAa,EACb,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAEtB,eAAe;AACf,OAAO,EACL,MAAM,EACN,WAAW,EACX,SAAS,EACT,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,OAAO,EACP,YAAY,GACb,MAAM,mBAAmB,CAAC;AAE3B,yBAAyB;AACzB,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE3E,qBAAqB;AACrB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAErE,UAAU;AACV,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,cAAc,CAAC;AAEtB,iBAAiB;AACjB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB,uCAAuC;AACvC,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,GAEjB,MAAM,kBAAkB,CAAC;AAE1B,iCAAiC;AACjC,OAAO,EACL,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,iBAAiB;AACjB,OAAO,EACL,eAAe,EACf,sBAAsB,GAEvB,MAAM,qBAAqB,CAAC;AAE7B,2BAA2B;AAC3B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAE7B,qDAAqD;AACrD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,GAGvB,MAAM,gBAAgB,CAAC;AAExB,eAAe;AACf,OAAO,EACL,WAAW,EACX,YAAY,EACZ,cAAc,GAKf,MAAM,eAAe,CAAC;AAEvB,cAAc;AACd,OAAO,EACL,aAAa,GAId,MAAM,kBAAkB,CAAC","sourcesContent":["// Public API for @agent-native/core/collab\n\n// Storage\nexport {\n loadYDocState,\n saveYDocState,\n hasCollabState,\n deleteCollabState,\n uint8ArrayToBase64,\n base64ToUint8Array,\n} from \"./storage.js\";\n\n// YDoc manager\nexport {\n getDoc,\n applyUpdate,\n applyText,\n getText,\n getState,\n getIncUpdate,\n seedFromText,\n releaseDoc,\n searchAndReplace,\n applyJson,\n applyPatchOps,\n getJson,\n seedFromJson,\n} from \"./ydoc-manager.js\";\n\n// XmlFragment operations\nexport { searchAndReplaceInYXml, extractTextFromYXml } from \"./xml-ops.js\";\n\n// Text-to-Yjs bridge\nexport { applyTextToYDoc, initYDocWithText } from \"./text-to-yjs.js\";\n\n// Emitter\nexport {\n getCollabEmitter,\n emitCollabUpdate,\n type CollabEvent,\n} from \"./emitter.js\";\n\n// Route handlers\nexport {\n getCollabState,\n postCollabUpdate,\n postCollabText,\n postCollabSearchReplace,\n} from \"./routes.js\";\n\n// JSON-to-Yjs bridge (structured data)\nexport {\n seedYDocFromJson,\n yMapToJson,\n yArrayToJson,\n yDocToJson,\n applyJsonDiff,\n applyJsonPatch,\n initYDocWithJson,\n type PatchOp,\n} from \"./json-to-yjs.js\";\n\n// Structured data route handlers\nexport {\n postCollabJson,\n getCollabJson,\n postCollabPatch,\n} from \"./struct-routes.js\";\n\n// Agent identity\nexport {\n AGENT_CLIENT_ID,\n DEFAULT_AGENT_IDENTITY,\n type AgentIdentity,\n} from \"./agent-identity.js\";\n\n// Agent presence lifecycle\nexport {\n agentEnterDocument,\n agentLeaveDocument,\n agentUpdateSelection,\n agentApplyEditsIncrementally,\n agentApplyPatchesIncrementally,\n} from \"./agent-presence.js\";\n\n// Awareness (re-export for agent-presence consumers)\nexport {\n getDocAwareness,\n getAwarenessEmitter,\n emitAwarenessChange,\n AWARENESS_CHANGE_EVENT,\n type AwarenessEntry,\n type AwarenessChangeEvent,\n} from \"./awareness.js\";\n\n// Presence kit\nexport {\n usePresence,\n toNormalized,\n fromNormalized,\n type OtherPresence,\n type PresencePayload,\n type UsePresenceResult,\n type NormalizedPoint,\n} from \"./presence.js\";\n\n// Follow mode\nexport {\n useFollowUser,\n type UseFollowUserOptions,\n type UseFollowUserResult,\n type ViewportDescriptor,\n} from \"./follow-mode.js\";\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Presence kit — Liveblocks/Figma-grade presence primitives.
3
+ *
4
+ * usePresence(awareness) returns:
5
+ * - others: reactive array of remote participants (human + agent)
6
+ * - setPresence(partial): merge fields into local awareness state
7
+ *
8
+ * The hook re-renders on every awareness change event and always includes
9
+ * the agent participant (AGENT_CLIENT_ID) as isAgent: true.
10
+ */
11
+ import type { Awareness } from "y-protocols/awareness";
12
+ import type { CollabUser } from "./client.js";
13
+ /** Arbitrary JSON presence payload published by a participant. */
14
+ export type PresencePayload = Record<string, unknown>;
15
+ /** A remote participant's full presence snapshot. */
16
+ export interface OtherPresence {
17
+ /** Yjs client ID. */
18
+ clientId: number;
19
+ /** User identity (from awareness `user` field). */
20
+ user: CollabUser;
21
+ /** Arbitrary presence fields set via setPresence() / agentUpdateSelection(). */
22
+ presence: PresencePayload;
23
+ /** True when this participant is the AI agent. */
24
+ isAgent: boolean;
25
+ }
26
+ export interface UsePresenceResult {
27
+ /** All remote participants (excludes local client). */
28
+ others: OtherPresence[];
29
+ /**
30
+ * Merge fields into the local awareness state. These are broadcast to
31
+ * peers by the fast-awareness path in useCollaborativeDoc.
32
+ * Call this to publish cursor position, viewport, or selection.
33
+ */
34
+ setPresence: (partial: PresencePayload) => void;
35
+ }
36
+ /**
37
+ * Derive OtherPresence entries from an Awareness instance.
38
+ *
39
+ * @param awareness Awareness instance from useCollaborativeDoc.
40
+ * @param localClientId The local Yjs client ID (to exclude self).
41
+ */
42
+ export declare function usePresence(awareness: Awareness | null | undefined, localClientId: number | null | undefined): UsePresenceResult;
43
+ export interface NormalizedPoint {
44
+ /** 0–1 fraction of container width. */
45
+ x: number;
46
+ /** 0–1 fraction of container height. */
47
+ y: number;
48
+ }
49
+ /** Convert a pointer event offset to a normalized point. */
50
+ export declare function toNormalized(clientX: number, clientY: number, container: DOMRect): NormalizedPoint;
51
+ /** Convert a normalized point back to absolute offset within a container. */
52
+ export declare function fromNormalized(point: NormalizedPoint, container: DOMRect): {
53
+ x: number;
54
+ y: number;
55
+ };
56
+ //# sourceMappingURL=presence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/collab/presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,IAAI,EAAE,UAAU,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,eAAe,CAAC;IAC1B,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB;;;;OAIG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;CACjD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACvC,iBAAiB,CAuEnB;AAMD,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,CAAC,EAAE,MAAM,CAAC;IACV,wCAAwC;IACxC,CAAC,EAAE,MAAM,CAAC;CACX;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,OAAO,GACjB,eAAe,CAKjB;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,OAAO,GACjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Presence kit — Liveblocks/Figma-grade presence primitives.
3
+ *
4
+ * usePresence(awareness) returns:
5
+ * - others: reactive array of remote participants (human + agent)
6
+ * - setPresence(partial): merge fields into local awareness state
7
+ *
8
+ * The hook re-renders on every awareness change event and always includes
9
+ * the agent participant (AGENT_CLIENT_ID) as isAgent: true.
10
+ */
11
+ import { useState, useEffect, useCallback, useRef } from "react";
12
+ import { AGENT_CLIENT_ID } from "./agent-identity.js";
13
+ /**
14
+ * Derive OtherPresence entries from an Awareness instance.
15
+ *
16
+ * @param awareness Awareness instance from useCollaborativeDoc.
17
+ * @param localClientId The local Yjs client ID (to exclude self).
18
+ */
19
+ export function usePresence(awareness, localClientId) {
20
+ const [others, setOthers] = useState([]);
21
+ // Keep the latest awareness ref so setPresence closure doesn't go stale.
22
+ const awarenessRef = useRef(awareness);
23
+ awarenessRef.current = awareness;
24
+ useEffect(() => {
25
+ if (!awareness) {
26
+ setOthers([]);
27
+ return;
28
+ }
29
+ function derive() {
30
+ const result = [];
31
+ awareness.getStates().forEach((state, clientId) => {
32
+ if (clientId === localClientId)
33
+ return; // skip self
34
+ const s = state;
35
+ const isAgent = clientId === AGENT_CLIENT_ID;
36
+ // User identity — fall back to agent defaults or anonymous.
37
+ let user;
38
+ if (isAgent) {
39
+ user = {
40
+ name: s.user?.name ?? "AI Assistant",
41
+ email: s.user?.email ?? "agent@system",
42
+ color: s.user?.color ?? "#00B5FF",
43
+ };
44
+ }
45
+ else {
46
+ const u = s.user;
47
+ user = {
48
+ name: u?.name ?? "Unknown",
49
+ email: u?.email ?? `client-${clientId}`,
50
+ color: u?.color ?? "#94a3b8",
51
+ };
52
+ }
53
+ // Everything that isn't `user` or `visible` is presence payload.
54
+ const presence = {};
55
+ for (const [k, v] of Object.entries(s)) {
56
+ if (k !== "user" && k !== "visible") {
57
+ presence[k] = v;
58
+ }
59
+ }
60
+ result.push({ clientId, user, presence, isAgent });
61
+ });
62
+ return result;
63
+ }
64
+ function onAwarenessChange() {
65
+ setOthers(derive());
66
+ }
67
+ // Derive immediately.
68
+ setOthers(derive());
69
+ awareness.on("change", onAwarenessChange);
70
+ return () => {
71
+ awareness.off("change", onAwarenessChange);
72
+ };
73
+ }, [awareness, localClientId]);
74
+ const setPresence = useCallback((partial) => {
75
+ const aw = awarenessRef.current;
76
+ if (!aw)
77
+ return;
78
+ for (const [k, v] of Object.entries(partial)) {
79
+ aw.setLocalStateField(k, v);
80
+ }
81
+ }, []);
82
+ return { others, setPresence };
83
+ }
84
+ /** Convert a pointer event offset to a normalized point. */
85
+ export function toNormalized(clientX, clientY, container) {
86
+ return {
87
+ x: Math.max(0, Math.min(1, (clientX - container.left) / container.width)),
88
+ y: Math.max(0, Math.min(1, (clientY - container.top) / container.height)),
89
+ };
90
+ }
91
+ /** Convert a normalized point back to absolute offset within a container. */
92
+ export function fromNormalized(point, container) {
93
+ return {
94
+ x: point.x * container.width,
95
+ y: point.y * container.height,
96
+ };
97
+ }
98
+ //# sourceMappingURL=presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/collab/presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AA6BtD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,SAAuC,EACvC,aAAwC;IAExC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAkB,EAAE,CAAC,CAAC;IAE1D,yEAAyE;IACzE,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;IAEjC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,SAAS,CAAC,EAAE,CAAC,CAAC;YACd,OAAO;QACT,CAAC;QAED,SAAS,MAAM;YACb,MAAM,MAAM,GAAoB,EAAE,CAAC;YACnC,SAAU,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;gBACjD,IAAI,QAAQ,KAAK,aAAa;oBAAE,OAAO,CAAC,YAAY;gBACpD,MAAM,CAAC,GAAG,KAAgC,CAAC;gBAC3C,MAAM,OAAO,GAAG,QAAQ,KAAK,eAAe,CAAC;gBAE7C,4DAA4D;gBAC5D,IAAI,IAAgB,CAAC;gBACrB,IAAI,OAAO,EAAE,CAAC;oBACZ,IAAI,GAAG;wBACL,IAAI,EAAG,CAAC,CAAC,IAAmB,EAAE,IAAI,IAAI,cAAc;wBACpD,KAAK,EAAG,CAAC,CAAC,IAAmB,EAAE,KAAK,IAAI,cAAc;wBACtD,KAAK,EAAG,CAAC,CAAC,IAAmB,EAAE,KAAK,IAAI,SAAS;qBAClD,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,GAAG,CAAC,CAAC,IAA8B,CAAC;oBAC3C,IAAI,GAAG;wBACL,IAAI,EAAE,CAAC,EAAE,IAAI,IAAI,SAAS;wBAC1B,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,UAAU,QAAQ,EAAE;wBACvC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,SAAS;qBAC7B,CAAC;gBACJ,CAAC;gBAED,iEAAiE;gBACjE,MAAM,QAAQ,GAAoB,EAAE,CAAC;gBACrC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;wBACpC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;oBAClB,CAAC;gBACH,CAAC;gBAED,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,SAAS,iBAAiB;YACxB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;QACtB,CAAC;QAED,sBAAsB;QACtB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;QACpB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAC1C,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAC7C,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;IAE/B,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,OAAwB,EAAE,EAAE;QAC3D,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7C,EAAE,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACjC,CAAC;AAaD,4DAA4D;AAC5D,MAAM,UAAU,YAAY,CAC1B,OAAe,EACf,OAAe,EACf,SAAkB;IAElB,OAAO;QACL,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAC5B,KAAsB,EACtB,SAAkB;IAElB,OAAO;QACL,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,KAAK;QAC5B,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM;KAC9B,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Presence kit — Liveblocks/Figma-grade presence primitives.\n *\n * usePresence(awareness) returns:\n * - others: reactive array of remote participants (human + agent)\n * - setPresence(partial): merge fields into local awareness state\n *\n * The hook re-renders on every awareness change event and always includes\n * the agent participant (AGENT_CLIENT_ID) as isAgent: true.\n */\n\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport { AGENT_CLIENT_ID } from \"./agent-identity.js\";\nimport type { CollabUser } from \"./client.js\";\n\n/** Arbitrary JSON presence payload published by a participant. */\nexport type PresencePayload = Record<string, unknown>;\n\n/** A remote participant's full presence snapshot. */\nexport interface OtherPresence {\n /** Yjs client ID. */\n clientId: number;\n /** User identity (from awareness `user` field). */\n user: CollabUser;\n /** Arbitrary presence fields set via setPresence() / agentUpdateSelection(). */\n presence: PresencePayload;\n /** True when this participant is the AI agent. */\n isAgent: boolean;\n}\n\nexport interface UsePresenceResult {\n /** All remote participants (excludes local client). */\n others: OtherPresence[];\n /**\n * Merge fields into the local awareness state. These are broadcast to\n * peers by the fast-awareness path in useCollaborativeDoc.\n * Call this to publish cursor position, viewport, or selection.\n */\n setPresence: (partial: PresencePayload) => void;\n}\n\n/**\n * Derive OtherPresence entries from an Awareness instance.\n *\n * @param awareness Awareness instance from useCollaborativeDoc.\n * @param localClientId The local Yjs client ID (to exclude self).\n */\nexport function usePresence(\n awareness: Awareness | null | undefined,\n localClientId: number | null | undefined,\n): UsePresenceResult {\n const [others, setOthers] = useState<OtherPresence[]>([]);\n\n // Keep the latest awareness ref so setPresence closure doesn't go stale.\n const awarenessRef = useRef(awareness);\n awarenessRef.current = awareness;\n\n useEffect(() => {\n if (!awareness) {\n setOthers([]);\n return;\n }\n\n function derive(): OtherPresence[] {\n const result: OtherPresence[] = [];\n awareness!.getStates().forEach((state, clientId) => {\n if (clientId === localClientId) return; // skip self\n const s = state as Record<string, unknown>;\n const isAgent = clientId === AGENT_CLIENT_ID;\n\n // User identity — fall back to agent defaults or anonymous.\n let user: CollabUser;\n if (isAgent) {\n user = {\n name: (s.user as CollabUser)?.name ?? \"AI Assistant\",\n email: (s.user as CollabUser)?.email ?? \"agent@system\",\n color: (s.user as CollabUser)?.color ?? \"#00B5FF\",\n };\n } else {\n const u = s.user as CollabUser | undefined;\n user = {\n name: u?.name ?? \"Unknown\",\n email: u?.email ?? `client-${clientId}`,\n color: u?.color ?? \"#94a3b8\",\n };\n }\n\n // Everything that isn't `user` or `visible` is presence payload.\n const presence: PresencePayload = {};\n for (const [k, v] of Object.entries(s)) {\n if (k !== \"user\" && k !== \"visible\") {\n presence[k] = v;\n }\n }\n\n result.push({ clientId, user, presence, isAgent });\n });\n return result;\n }\n\n function onAwarenessChange() {\n setOthers(derive());\n }\n\n // Derive immediately.\n setOthers(derive());\n awareness.on(\"change\", onAwarenessChange);\n return () => {\n awareness.off(\"change\", onAwarenessChange);\n };\n }, [awareness, localClientId]);\n\n const setPresence = useCallback((partial: PresencePayload) => {\n const aw = awarenessRef.current;\n if (!aw) return;\n for (const [k, v] of Object.entries(partial)) {\n aw.setLocalStateField(k, v);\n }\n }, []);\n\n return { others, setPresence };\n}\n\n// ---------------------------------------------------------------------------\n// Normalized cursor coordinate helpers\n// ---------------------------------------------------------------------------\n\nexport interface NormalizedPoint {\n /** 0–1 fraction of container width. */\n x: number;\n /** 0–1 fraction of container height. */\n y: number;\n}\n\n/** Convert a pointer event offset to a normalized point. */\nexport function toNormalized(\n clientX: number,\n clientY: number,\n container: DOMRect,\n): NormalizedPoint {\n return {\n x: Math.max(0, Math.min(1, (clientX - container.left) / container.width)),\n y: Math.max(0, Math.min(1, (clientY - container.top) / container.height)),\n };\n}\n\n/** Convert a normalized point back to absolute offset within a container. */\nexport function fromNormalized(\n point: NormalizedPoint,\n container: DOMRect,\n): { x: number; y: number } {\n return {\n x: point.x * container.width,\n y: point.y * container.height,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/collab/routes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;GA4BzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;GAsB3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;GA2BzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;GA6BnC,CAAC"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/collab/routes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;GA4BzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;GAyB3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;GA8BzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;GAgCnC,CAAC"}