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>
This commit is contained in:
vegard 2026-03-19 16:57:47 +00:00
parent 2d663f8f67
commit db95cd33d1

View file

@ -2,28 +2,85 @@
## Idé
Alle brukere som er på samme side ser hverandres musepekere som fargede prikker med navn. Fungerer på storyboard, kanban, whiteboard og kalender.
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.
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, page }` til SpacetimeDB ved musebevegelse (throttlet til ~10 Hz)
2. Andre klienter renderer fargede SVG-sirkler med brukernavn
3. Prikken fader ut etter 5 sekunder uten bevegelse
4. Valgfritt: kort "trail" som viser bevegelsesretning
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:
```rust
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):
```svelte
{#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å
- SpacetimeDB (pub/sub for posisjoner)
- Svelte ($state store for cursor-map)
- WebSocket / portvokteren (erstatter SpacetimeDB)
- Canvas-komponent (cursor-lag over objekter)
- Svelte ($state for cursor-map)
## Innsats
Lav — under 50 linjer Svelte + en SpacetimeDB-reducer.
Lav — under 100 linjer Svelte + ~50 linjer Rust i portvokteren.
## Wow-faktor
Middels — visuelt tiltalende, men ikke kritisk funksjonalitet.
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-baserte views?
- Throttling-strategi: SpacetimeDB-reducer eller klient-side debounce?
- 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)