export type BrowserPreviewCursor = { x: number; y: number; label: string; }; type Listener = (activities: ReadonlyMap) => void; /** * In-memory cursors for the desktop browser-preview panel. * The shell publishes working bots here; the panel subscribes. */ class BrowserPreviewActivity { private readonly activities = new Map(); private readonly listeners = new Set(); subscribe(listener: Listener): () => void { this.listeners.add(listener); listener(this.snapshot()); return () => { this.listeners.delete(listener); }; } snapshot(): ReadonlyMap { return new Map(this.activities); } set(id: string, x: number, y: number, label: string) { const current = this.activities.get(id); if (current && current.x === x && current.y === y && current.label === label) return; this.activities.set(id, { x, y, label }); this.emit(); } clear(id: string) { if (!this.activities.delete(id)) return; this.emit(); } private emit() { const snapshot = this.snapshot(); for (const listener of this.listeners) listener(snapshot); } } export const browserPreviewActivity = new BrowserPreviewActivity();