synops/docs/proposals/collaborative_cursors.md
vegard db95cd33d1 Collaborative cursors: portert fra SpacetimeDB til WebSocket
SpacetimeDB er fjernet (mars 2026). Oppdatert til å bruke
eksisterende WebSocket/portvokter med ephemeral cursor_move-
meldinger (ikke persistert i PG). In-memory HashMap i Rust.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:57:47 +00:00

2.4 KiB

Collaborative Cursors — Sanntids-pekere for flerbrukermiljø

Idé

Alle brukere som er på samme arbeidsflate ser hverandres musepekere som fargede prikker med navn. Fungerer på spatial canvas, storyboard, kanban, whiteboard og kalender.

Hvorfor interessant?

Gir "jamming together"-følelse under innspilling og planlegging. Produsent og host ser hverandre jobbe i sanntid uten å snakke om det. Essensielt for delte arbeidsflater (møterom, studio).

Fungerer slik

  1. Klient sender { user_id, x, y, context_id } via WebSocket ved musebevegelse (throttlet til ~10 Hz)
  2. Portvokteren broadcaster til andre klienter i samme kontekst
  3. Andre klienter renderer fargede SVG-sirkler med brukernavn
  4. Prikken fader ut etter 5 sekunder uten bevegelse
  5. Valgfritt: kort "trail" som viser bevegelsesretning

Teknisk implementering

Kanal

Bruker eksisterende WebSocket-tilkobling til portvokteren. Ny meldingstype cursor_move som ikke persisteres i PG — dette er ephemeral data som kun lever i minne.

Klient → WS → portvokteren → broadcast til andre i samme context_id

Portvokteren holder en in-memory map:

cursors: HashMap<(ContextId, UserId), CursorPosition>

Ryddes automatisk ved disconnect eller timeout (5s).

Throttling

Klientside: requestAnimationFrame + 100ms minimum intervall. ~10 meldinger per sekund per bruker. Ved 10 samtidige brukere i ett rom: ~100 meldinger/sekund — trivielt for WebSocket.

Rendering

Canvas-laget (over objektene, under toolbar):

{#each cursors as cursor (cursor.userId)}
  <div
    class="cursor-indicator"
    style:left="{cursor.x}px"
    style:top="{cursor.y}px"
    style:background={cursor.color}
    style:opacity={cursor.fresh ? 1 : 0}
  >
    <span class="cursor-label">{cursor.name}</span>
  </div>
{/each}

Bygger på

  • WebSocket / portvokteren (erstatter SpacetimeDB)
  • Canvas-komponent (cursor-lag over objekter)
  • Svelte ($state for cursor-map)

Innsats

Lav — under 100 linjer Svelte + ~50 linjer Rust i portvokteren.

Wow-faktor

Middels-høy — visuelt tiltalende og essensielt for delte arbeidsflater (møterom, studio, samarbeid).

Åpne spørsmål

  • Bør pekere vises i chat-visning også, eller bare canvas? (Sannsynligvis bare canvas og andre spatial views)
  • Fargetildeling: per bruker (konsistent) eller per sesjon? (Per bruker, lagret i person-node metadata)