feat: F5.4 presence: who else is in the project

Over Yjs awareness each editor tells who it is (F names the person in
the access message), the device it has chosen and the windows it has
open, and nothing else: no pointer, nothing that changes without a
cause, sent only when it changed. Others' states are read with care.
A frame in their colour round a device they chose, a dot on a window
they have open too, a circle with initials each beside the mode
buttons; the bridge tells the dashboard who is there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 16:09:34 +00:00
co-authored by Claude Opus 5.5
parent f52852493a
commit 47beda6b85
22 changed files with 600 additions and 33 deletions
@@ -19,6 +19,8 @@ export const COMMANDS = {
export const EVENTS = {
/** The editor listens. */
READY: 'ready',
/** `{ people: [{ id, name, color }] }`: the others in the project, each time they change (plan §7.4: the dashboard's header shows them). */
PEOPLE: 'people',
} as const;
export interface BridgeHandlers {
@@ -45,6 +47,11 @@ export function startBridge(window: Window, parentOrigin: string, handlers: Brid
}
};
window.addEventListener('message', listener);
window.parent.postMessage({ v: BRIDGE_VERSION, type: EVENTS.READY }, parentOrigin);
tellDashboard(window, parentOrigin, EVENTS.READY);
return () => window.removeEventListener('message', listener);
}
/** Tells the dashboard at `parentOrigin` something the editor says (EVENTS). */
export function tellDashboard(window: Pick<Window, 'parent'>, parentOrigin: string, type: (typeof EVENTS)[keyof typeof EVENTS], fields: Readonly<Record<string, unknown>> = {}): void {
window.parent.postMessage({ v: BRIDGE_VERSION, type, ...fields }, parentOrigin);
}