/** Drop a member from the typing roster (self-exclusion). Pure — tested * directly without rendering the effect-driven hook. */ export declare const activeTypers: (members: ReadonlyArray, selfKey: string | undefined) => ReadonlyArray; /** * The dumb pub/sub shape `@voltro/local-first`'s presence rooms consume — * restated STRUCTURALLY (three members) rather than imported, so this package * carries no dependency edge to the local-first package; a type-parity test * over there pins the two against each other. */ export declare interface LocalFirstPresenceChannel { readonly kind: 'presence-channel'; readonly publish: (room: string, payload: string) => void; readonly subscribe: (room: string, handler: (payload: string) => void) => () => void; } export declare interface PresenceMember { readonly key: string; readonly meta: Record | null; } export declare interface TypingState { /** Other members currently typing in this channel (self excluded). */ readonly active: ReadonlyArray; /** Whether THIS client is currently broadcasting "typing". */ readonly isTyping: boolean; readonly start: () => void; readonly stop: () => void; } /** Presence in `channel`: returns the live (push-driven) roster + heartbeats * while mounted. The roster updates over the subscription transport whenever * the presence table changes — no polling. */ export declare const usePresence: (channel: string, options?: UsePresenceOptions) => ReadonlyArray; /** * ONE presence wire (plan 02 phase 1): a `PresenceChannel` for * `@voltro/local-first`'s rooms that rides the framework's EXISTING presence * lane — `presence.heartbeat` carries the payload as member meta, the * push-driven `presence.list` roster delivers it — instead of opening a second * transport. Cross-replica fan-out is therefore plugin-broadcast's, exactly as * for every other presence consumer. * * Semantics match the channel contract: delivery is fire-and-forget and may * COALESCE (the roster push is value-compared per member, so of two rapid * publishes only the latest is guaranteed to reach peers) — presence loss is * tolerable by design; the next publish corrects it. The hook binds ONE room: * `publish`/`subscribe` must be called with the same `roomId` the hook was * given (a mismatched room throws — a silent cross-room delivery would be * worse). */ export declare const usePresenceChannel: (roomId: string, options: UsePresenceChannelOptions) => LocalFirstPresenceChannel; export declare interface UsePresenceChannelOptions { /** THIS peer's key — required, and must equal the `peerId` handed to the * local-first room, because self-exclusion happens on it (a broker never * echoes your own publish back; the roster includes you). */ readonly selfKey: string; readonly apiName?: string; } export declare interface UsePresenceOptions { /** Per-member metadata published with each heartbeat (status, cursor, …). */ readonly meta?: Record; /** Explicit presence key (for anonymous members); defaults to the subject id. */ readonly key?: string; /** Heartbeat cadence (ms). Default 15s — keep it < the server timeout. */ readonly heartbeatMs?: number; readonly apiName?: string; } /** * Typing indicator over a short-TTL presence lane (`typing:`). While * `isTyping`, the client heartbeats into the lane; `stop()` / unmount leaves * it, so a typer falls off the roster within the heartbeat window. `active` is * the other members currently typing. Built on the SAME presence primitive — * no new transport. */ export declare const useTyping: (channel: string, options?: UseTypingOptions) => TypingState; export declare interface UseTypingOptions { /** Exclude this key (the current user) from the returned roster. */ readonly selfKey?: string; /** Heartbeat cadence while typing (ms). Default 3s — keep it short so a * typer drops off the roster quickly after they stop. */ readonly heartbeatMs?: number; readonly apiName?: string; } export { }