import * as _ai_matrx_data_realtime from '@ai-matrx/data/realtime'; import { RealtimeManager, ChannelSpec } from '@ai-matrx/data/realtime'; import { OpLedger, RecordsRealtimePort } from '../core/index.js'; import '../field-IlJ97y8H.js'; import '../record-C3thPhpR.js'; declare const storeTablePresenceChannel: _ai_matrx_data_realtime.ChannelNamespace; /** The cell a person has selected. */ interface PresenceCell { recordId: string; fieldKey: string; } /** One other person looking at the table. */ interface TablePresencePerson { /** Stable per person (their user id, else the session key). */ key: string; /** The published user id (null for an anonymous session). The reader names the person from it. */ userId: string | null; /** "Someone" here: the reader resolves the id to a name through the members roster. Never read from the sender. */ name: string; /** Always null here: the reader resolves the id through the members roster. Never read from the sender. */ avatarUrl: string | null; /** An outline / ring color, the same on every screen for the same person. */ color: string; cell: PresenceCell | null; } interface PresenceSelf { userId: string | null; /** Ignored: a name is never published (the reader resolves the id). Accepted so older hosts still type-check. */ name?: string; /** Ignored, as `name`. */ avatarUrl?: string | null; } interface TablePresenceSession { /** Everyone else looking right now (never this person, even from a second tab). */ getSnapshot(): readonly TablePresencePerson[]; subscribe(listener: () => void): () => void; /** Tell the others which cell is selected (null = none). */ selectCell(cell: PresenceCell | null): void; close(): void; } interface RecordsPresencePort { join(args: { tableId: string; }): TablePresenceSession; } interface CreateRecordsPresencePortOptions { /** Who this person is, read at join time (the host's signed-in profile). Null = not known yet: watch, and join once it is. */ self: () => PresenceSelf | null; /** * Tell the port when `self()` may have changed (the signed-in user lands after the page mounted). A viewer * whose user was not loaded at join otherwise never announced itself and never saw anybody (ADV-1 F10). * Returns the unsubscribe. Left out, `self()` is read at join only. */ onSelfChange?: (listener: () => void) => () => void; /** The host's realtime manager. Left out, the AMBIENT manager is used (joins when one is mounted). */ manager?: RealtimeManager; } type RosterState = Record & { userId: string | null; color: string; cell: PresenceCell | null; }; /** The same color for the same person on every screen (a hue from their key; fixed saturation and lightness). */ declare function presenceColor(key: string): string; /** * The roster as the screen wants it: one entry per PERSON (two tabs of one person are one avatar), never * this person, ordered by name so the avatars do not shuffle on every heartbeat. */ declare function peopleOf(members: ReadonlyArray<{ key: string; isSelf: boolean; state: Partial; }>, selfUserId: string | null): TablePresencePerson[]; declare function createRecordsPresencePort(options: CreateRecordsPresencePortOptions): RecordsPresencePort; /** * The topic is a CONTRACT WITH THE DATABASE (a Postgres trigger writes it, an RLS policy parses it), * so it is built in one place through the realtime package's foreign-topic door. */ declare const storeTableChannel: _ai_matrx_data_realtime.ChannelNamespace; /** The broadcast event the store sends on a table's topic. */ declare const STORE_NOTICE_EVENT = "records.changed"; /** Coalesce a burst into one re-read. A statement is already one notice; this catches two. */ declare const NUDGE_DEBOUNCE_MS = 150; interface StoreNotice { table_id?: string; kind?: "record" | "field" | "table"; op?: "created" | "updated" | "deleted"; /** The client operation that caused it (`_op_id`), or null for a server, agent or foreign write. */ op_id?: string | null; /** `null` MEANS "re-read the page" — above the cap the database stops listing ids. */ record_ids?: string[] | null; fields_changed?: boolean; at?: string; } /** The notice's own identity: a redelivered notice is the same statement at the same instant. */ declare function noticeKey(notice: StoreNotice): string | undefined; interface CreateRecordsRealtimePortOptions { /** * The host's realtime manager (`@ai-matrx/data/realtime`). Left out, the AMBIENT manager is used — the one * the app mounted (`setAmbientRealtimeManager` / ``); the port joins when one is * mounted and follows it if it is replaced. Never a second socket of the port's own. */ manager?: RealtimeManager; /** * The own-write memory of the client this port listens for (`createOpLedger()`, the same one handed * to `createRecordsClient({ opLedger })`). Absent ⇒ the realm's default ledger. */ ledger?: OpLedger; } /** The channel spec for one table: private, echo-guarded, deduped, backfilled. */ declare function storeTableChannelSpec(tableId: string, onNotice: (notice: StoreNotice) => void, onBackfill: () => void, ledger?: OpLedger): ChannelSpec; /** * The store's live-updates port, bound to a realtime manager. Hand it to `RecordsConfig.realtime`; the * `@ai-matrx/records/data` port turns its sink calls into `rows:` events. */ declare function createRecordsRealtimePort(options?: CreateRecordsRealtimePortOptions): RecordsRealtimePort; export { NUDGE_DEBOUNCE_MS, STORE_NOTICE_EVENT, createRecordsPresencePort, createRecordsRealtimePort, noticeKey, peopleOf, presenceColor, storeTableChannel, storeTableChannelSpec, storeTablePresenceChannel }; export type { CreateRecordsPresencePortOptions, CreateRecordsRealtimePortOptions, PresenceCell, PresenceSelf, RecordsPresencePort, StoreNotice, TablePresencePerson, TablePresenceSession };