1"use client"; 2 3/** 4 * Presence caret store (TASK-0093) — the single caret of the page currently 5 * in view. 6 * 7 * One page is mounted per tab (route `[id]`), and exactly one Tiptap 8 * editor is focused at a time, so a single optional caret is the whole 9 * client-side state. The focused editor writes it (on focus + every 10 * selection update), every blur clears it, and the per-page 11 * `usePresence` hook (hooks/use-presence.ts) forwards it to the 12 * `PresenceEmitter`, which throttles it into `presence` frames. 13 * 14 * A store (instead of context/props) matches the existing cross-component 15 * state here (editor-store, auth-store, block-patch-store) and keeps the 16 * recursive BlockRenderer untouched. 17 */ 18import { create } from "zustand"; 19import type { PresenceSelection } from "@/lib/types"; 20 21/** The caret inside one block (offset within the block's text). */ 22export interface PresenceCaret { 23 blockId: string; 24 offset: number; 25 /** A non-collapsed selection (MVP: inside this one block's doc). */ 26 selection?: PresenceSelection; 27} 28 29interface PresenceCaretState { 30 /** The focused editor's caret, or null (nothing focused / no page). */ 31 caret: PresenceCaret | null; 32 setCaret: (caret: PresenceCaret | null) => void; 33} 34 35export const usePresenceCaretStore = create<PresenceCaretState>()((set) => ({ 36 caret: null, 37 setCaret: (caret) => set({ caret }), 38})); 39