synops/docs/features/canvas_primitiv.md
vegard b5aa5bb243 Fjern SpacetimeDB komplett (oppgave 22.4)
SpacetimeDB er nå helt fjernet fra Synops. Sanntid håndteres av
PG LISTEN/NOTIFY + WebSocket i portvokteren (maskinrommet).

Kode fjernet:
- spacetimedb/ Rust-modul og spacetime.json
- maskinrommet/src/stdb.rs (HTTP-klient for STDB-reducers)
- frontend module_bindings/ (23 auto-genererte filer)
- spacetimedb npm-avhengighet fra package.json
- scripts/test-sanntid.sh (testet STDB-flyt)

Infrastruktur:
- Docker-container stoppet og fjernet fra docker-compose.yml
- Caddy: fjernet /spacetime/* reverse proxy
- maskinrommet-env.sh: fjernet STDB_IP og SPACETIMEDB_*-variabler
- .env.example: fjernet SpacetimeDB-seksjoner

Dokumentasjon oppdatert:
- CLAUDE.md: stack, lagmodell, kjerneprinsipper, driftsmodell
- docs/arkitektur.md: skrivestien, lesestien, datalag, teknologivalg
- docs/retninger/datalaget.md: migrasjonshistorikk, status "fjernet"
- 37 andre docs oppdatert (features, concepts, infra, ops, retninger)
- Alle kode-kommentarer med STDB-referanser oppdatert

Verifisert: maskinrommet bygger og starter OK, frontend bygger OK,
helsesjekk returnerer 200. Caddy reloadet.
2026-03-18 13:39:09 +00:00

217 lines
8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature: Canvas-primitiv — felles fritt-canvas underlag
**Filsti:** `docs/features/canvas_primitiv.md`
## 1. Konsept
Canvas-primitivet er den felles underliggende komponenten for alle friform-views i Sidelinja: whiteboard (tegning), storyboard (kort-canvas), og fremtidige canvas-baserte visninger. Det håndterer kamera (pan, zoom), viewport-styring, objekt-plassering og interaksjon — men vet ingenting om *hva* som rendres.
### 1.1 Hvorfor et felles primitiv?
Whiteboard og storyboard har identisk infrastruktur-behov:
- Uendelig canvas med pan og zoom
- Objekter med `(x, y)`-posisjon
- Drag-and-drop av objekter
- Viewport culling (ikke render det som er utenfor synsfeltet)
- Touch-støtte (pinch-zoom, to-finger-pan)
- Responsivt design (fungerer på mobil, tablet, desktop)
Forskjellen er *innholdet*: whiteboard rendrer streker/figurer, storyboard rendrer meldingsboks-kort. Primitivet abstraherer det felles, slik at begge views gjenbruker 100 % av canvas-logikken.
### 1.2 Arkitekturprinsipp
```
Canvas-primitiv (felles)
├── Kamera: pan, zoom, transform matrix
├── Viewport: culling, synlige objekter
├── Interaksjon: pointer events, touch, drag
├── Grid: valgfri snap, hjelpelinje
└── Render-delegering: slot/callback for innhold
Whiteboard (consumer)
├── Tegneverktøy: penn, linje, rektangel, tekst
├── Strøk-modell: SVG paths / canvas paths
└── Sanntid: strøk-synkronisering via WebSocket
Storyboard (consumer)
├── Kort-rendering: <MessageBox> i kompakt modus
├── Status-modell: Klar / Tatt opp / Droppet / Arkivert
├── Portal-soner: overføringsmekanikk til andre blokker
└── Sanntid: kort-posisjon + status-synkronisering via WebSocket
```
## 2. Kamera-modell
### 2.1 Transform
Kameraet representeres som en 2D affin transformasjon:
```typescript
interface Camera {
x: number; // pan offset X (world coords)
y: number; // pan offset Y (world coords)
zoom: number; // scale factor (1.0 = 100%)
}
```
Rendring via CSS `transform` på en wrapper-div:
```css
.canvas-world {
transform: translate(calc(var(--cam-x) * 1px), calc(var(--cam-y) * 1px))
scale(var(--cam-zoom));
transform-origin: 0 0;
}
```
### 2.2 Zoom-begrensning
- Min zoom: `0.1` (10 % — fugleperspektiv, brukes av Pinboard Mode)
- Max zoom: `3.0` (300 % — detalj)
- Default: `1.0`
- Zoom pivoterer rundt musepeker/finger-midtpunkt
### 2.3 Pan
- **Desktop:** Hold mellomknapp eller mellomrom + dra. Alternativt: to-finger-drag på trackpad.
- **Touch:** To-finger-pan (én finger = dra objekter, to fingre = pan).
- **Edge-pan:** Når man drar et objekt nær kanten av viewport, scroller canvaset automatisk i den retningen.
## 3. Viewport Culling
Bare objekter som overlapper med det synlige viewport-rektangelet rendres i DOM. For storyboard med 50200 kort er dette en optimalisering som holder DOM-et lett.
```typescript
function visibleObjects(objects: CanvasObject[], camera: Camera, viewportSize: { w: number, h: number }): CanvasObject[] {
const worldRect = screenToWorld(camera, viewportSize);
return objects.filter(obj => intersects(obj.bounds, worldRect));
}
```
En margin (f.eks. 200px i world-space) legges til for å unngå pop-in ved pan.
## 4. Objektmodell
Canvas-primitivet opererer på generiske objekter:
```typescript
interface CanvasObject {
id: string;
x: number;
y: number;
width: number;
height: number;
// Consumer-spesifikk data håndteres via generics/props
}
```
Consumer (whiteboard, storyboard) bestemmer *hva* som rendres for hvert objekt via en render-callback eller Svelte snippet:
```svelte
<Canvas objects={cards} let:object>
<!-- Consumer bestemmer innholdet -->
<StoryboardCard card={object} />
</Canvas>
```
## 5. Interaksjon
### 5.1 Pointer events
All interaksjon håndteres via pointer events (unified mouse + touch):
| Gest | Desktop | Touch | Handling |
|------|---------|-------|----------|
| Pan | Mellomknapp-drag / Space+drag | To-finger-drag | Flytt kamera |
| Zoom | Scroll wheel | Pinch | Zoom inn/ut |
| Velg | Klikk | Tap | Velg objekt |
| Flytt | Venstreklikk-drag på objekt | Én-finger-drag på objekt | Flytt objekt |
| Multi-select | Shift+klikk / lasso | Lang-trykk + drag | Velg flere |
### 5.2 Snap-to-grid (valgfri)
Når aktivert, snapper objekter til et rutenett ved drag-slipp:
```typescript
function snap(value: number, gridSize: number): number {
return Math.round(value / gridSize) * gridSize;
}
```
Default: av. Kan toggles via hurtigtast eller toolbar.
### 5.3 Seleksjon
- Klikk på tom flate: deselect alle
- Klikk på objekt: velg det (deselect andre)
- Shift+klikk: toggle seleksjon
- Lasso: dra på tom flate uten Space = tegn seleksjonsboks
## 6. Responsivt design
Canvas-primitivet skal fungere på alle skjermstørrelser:
| Skjerm | Tilpasning |
|--------|-----------|
| Desktop (>1024px) | Full interaksjon, alle hurtigtaster |
| Tablet (7681024px) | Touch-gester, toolbar i bunn |
| Mobil (<768px) | Forenklet toolbar, større treffområder for objekter, ingen lasso |
Touch-treffområder skal være minimum 44x44px (WCAG 2.5.5).
## 7. Fullskjerm-modus (BlockShell-feature)
Enhver blokk i `BlockShell` kan i fullskjerm. Dette er en generell feature, ikke spesifikk for canvas:
- **Toggle:** Dobbeltklikk blokk-headeren, eller knapp i header
- **Implementering:** Blokken settes til `position: fixed; inset: 0; z-index: 50`
- **Escape:** Trykk Esc eller klikk "minimer"-knapp for å tilbake
- **URL-state:** Fullskjerm-tilstand lagres ikke i URL det er en visuell modus, ikke en side
## 8. Sanntidsintegrasjon
Canvas-primitivet selv har ingen sanntidskobling det er consumer-ens ansvar. Men primitivet eksponerer events som consumeren kan koble til WebSocket:
```typescript
interface CanvasEvents {
onObjectMove: (id: string, x: number, y: number) => void;
onObjectResize: (id: string, w: number, h: number) => void;
onCameraChange: (camera: Camera) => void;
onSelectionChange: (ids: string[]) => void;
}
```
Storyboard-consumeren bruker `onObjectMove` til å oppdatere PG via maskinrommet, som propagerer endringen til andre klienter via WebSocket.
## 9. Bygger på
- **SvelteKit:** Svelte 5 `$state`/`$derived` for reaktiv kamera- og objekt-state
- **CSS transforms:** Ingen Canvas2D eller WebGL DOM-basert rendering for å beholde Svelte-komponent-rendering inne i objektene
- **Pointer Events API:** Unified input for mus og touch
## 10. Implementeringsstrategi
### Fase 1: Kjerne-primitiv ✅ (implementert)
- `<Canvas>` Svelte-komponent med kamera (pan/zoom), viewport culling, og objekt-drag
- Touch-støtte (pinch-zoom, to-finger-pan)
- Fullskjerm-toggle (i canvas-toolbar, `position: fixed`)
- **Filer:** `src/lib/components/canvas/Canvas.svelte`, `types.ts`, `index.ts`
- **Bruk:** `import { Canvas } from '$lib/components/canvas'`
- **API:** `renderObject` snippet for consumer-rendering, events via props (`onObjectMove`, `onCameraChange`, `onSelectionChange`)
- **Eksport:** `getCamera()`, `setCamera()`, `zoomToFit()` metoder
### Fase 2: Storyboard som første consumer
- `<StoryboardCard>` rendrer meldingsboks-kort canvaset
- Sanntidssynk for posisjon og status via WebSocket
- Portal-soner for overføring
### Fase 3: Whiteboard-migrering
- Migrere eksisterende whiteboard-spec til å bruke canvas-primitivet
- Tegneverktøy som overlay oppå primitivet
## 11. Instruks for Claude Code
- Canvas-primitivet er en ren Svelte-komponent uten backend-avhengigheter
- Bruk CSS transforms, ikke Canvas2D innholdet inne i objekter er vanlige Svelte-komponenter
- All state styres via Svelte 5 `$state` og `$derived` ingen external state management
- Pointer events, ikke mouse events unified input
- Test med touch-emulering i DevTools for responsivitet
- Viewport culling er påkrevd fra dag 1 ikke optimaliser bort