- Omorganiser docs/: konsepter, features, infra og proposals i egne mapper - Ny docs/erfaringer/ med lærdommer fra chat-implementering (Svelte 5, SpacetimeDB, adapter-mønster) - Oppdater ARCHITECTURE.md: Lag 1 status, ny §10 Erfaringslogg, SpacetimeDB i lokal dev - Oppdater synkronisering.md med implementeringsstatus og designvalg - Oppdater lokal.md med SpacetimeDB og AI Gateway - Utvid PG-skjema med channels, messages, media_files, message_revisions - Legg til seed_dev.sql, migration_safety.md, .env.example - Nye feature-specs: chat, kanban, whiteboard, live_ai, lydmeldinger m.fl. - Nye konsept-specs: studioet, møterommet, redaksjonen, den asynkrone gjesten m.fl. - SpacetimeDB og AI Gateway i docker-compose.dev.yml - collect-docs.sh inkluderer erfaringer/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8.3 KiB
Feature: Chat (Channels & Meldinger)
Filsti: docs/features/chat.md
1. Konsept
En universell, sanntids meldingskomponent bygget på SpacetimeDB. Chat er ikke bundet til én kontekst — den kan knyttes til enhver node i Kunnskapsgrafen via channels. Ulike konsepter bruker chat med ulik konfigurasjon, men all infrastruktur er delt.
2. Channels-modellen
En channel er en meldingsstrøm knyttet til en vilkårlig node (parent) i grafen. Channelen er selv en node (node_type = 'channel'), noe som betyr at den deltar i grafen og arver workspace-isolasjon.
┌─────────────┐ parent_id ┌─────────────┐
│ Channel │ ──────────────────► │ Vilkårlig │
│ (node) │ │ node │
└──────┬──────┘ └─────────────┘
│
│ channel_id
▼
┌─────────────┐
│ Meldinger │
│ (nodes) │
└─────────────┘
En node kan ha flere channels. Eksempler:
- Et Tema har "Diskusjon" (default) + "Research-dump"
- En Episode har "Redaksjonelt" (intern kommentartråd)
- Et Møte har "Scratchpad" (flyktig, TTL)
- En Aktør har "Notater" (valgfri)
2.1 Channel-konfigurasjon
Hver channel har en config (JSONB) som styrer hvilke capabilities den støtter:
{
"threads": true, // reply-to-tråder
"mentions": true, // #/@ parsing + automatiske graph_edges
"attachments": true, // filopplasting
"research_clips": true, // research_clip meldingstype (AI-prosessert)
"ttl_days": null // null = permanent, tall = auto-slett etter N dager
}
2.2 Standard channel-presets per konsept
| Konsept | Channel-navn | Config |
|---|---|---|
| Redaksjonen (Tema) | "Diskusjon" | threads: true, mentions: true, attachments: true, research_clips: true, ttl_days: null |
| Redaksjonen (Tema) | "Research" (valgfri) | threads: false, mentions: true, attachments: true, research_clips: true, ttl_days: null |
| Møterommet | "Scratchpad" | threads: false, mentions: false, attachments: false, research_clips: false, ttl_days: 90 |
| Studioet | "Studio-chat" | threads: false, mentions: false, attachments: false, research_clips: false, ttl_days: 30 |
| Episode | "Redaksjonelt" | threads: true, mentions: true, attachments: true, research_clips: false, ttl_days: null |
Presets er kun defaults — en workspace-admin kan justere config per channel.
2.3 Automatisk opprettelse
Når en node opprettes som forventes å ha chat (Tema, Episode, Møte), oppretter systemet automatisk en default-channel. Dette skjer i SvelteKit server-side som en del av node-opprettelsestransaksjonen.
3. Meldinger
3.1 Datamodell (PostgreSQL)
Meldinger er noder i Kunnskapsgrafen (node_type = 'melding'):
messages (
id UUID PK → nodes(id),
channel_id UUID NOT NULL → channels(id),
reply_to UUID → messages(id), -- tråder (hvis config.threads = true)
author_id TEXT NOT NULL → users,
message_type message_type, -- 'text', 'research_clip', 'factoid', 'system'
body TEXT NOT NULL,
metadata JSONB, -- ekstra data (research-klipp AI-resultat, etc.)
edited_at TIMESTAMPTZ,
created_at TIMESTAMPTZ
)
3.2 SpacetimeDB (sanntid)
SpacetimeDB holder aktive channels' meldinger i minnet. Nye meldinger sendes via SpacetimeDB og kringkastes til alle tilkoblede klienter i samme workspace og channel. Synkes til PostgreSQL med ~5 sek forsinkelse (se docs/infra/synkronisering.md).
4. Mentions & Autocomplete
Kun aktive når config.mentions = true.
- Trigger-tegn:
#(Temaer/Aktører fra Kunnskapsgrafen),@(brukere/redaksjonsmedlemmer),/(kommandoer). - Filtrering: Svelte-klienten filtrerer den lokale SpacetimeDB-cachen umiddelbart. Skriver man
#Ha...vises en klikkbar liste ("Hans Petter Sjøli", "Høyre"). - Grafkobling: Ved
#-mention opprettes automatiskMENTIONS-edges igraph_edgesmellom meldingen og den nevnte noden. - Mobil-optimalisert: Autocomplete-listen er tappbar og tilpasset mindre skjermer.
5. Tråder
Kun aktive når config.threads = true. Meldinger kan ha en reply_to-referanse. Frontend viser tråder som innrykk eller ekspanderbare grupper.
6. Vedlegg
Kun aktive når config.attachments = true. Meldinger kan ha vedlegg via message_attachments → media_files. Whiteboard-eksport kan knyttes som vedlegg.
7. Versjonshistorikk
Alle meldinger støtter redigering med full historikk via message_revisions. Original tekst bevares alltid.
8. Tale-til-tekst (Voice-to-text)
Mobilvennlig diktering for situasjoner der tastatur er upraktisk. Brukeren trykker en mikrofon-knapp, snakker, og får teksten tilbake som en vanlig melding klar til redigering og sending.
8.1 Flyt
- Bruker trykker og holder (eller toggler) mikrofon-knappen i chat-feltet.
- Nettleseren fanger lyd via
MediaRecorderAPI (WebM/Opus). - Lydklippet sendes til Whisper (
POST /v1/audio/transcriptions,response_format=text,language=no) via SvelteKit server-side. - Transkripsjonen settes inn i meldingsfeltet — brukeren kan redigere før sending.
- Ingen lagring av lydfilen — den kastes etter transkripsjon.
8.2 Avgrensning
- Dette er ikke en lydmelding-feature (à la WhatsApp). Lyden er et transportmiddel for tekst. Kun teksten lagres.
- Whisper-kallet er kort (<30 sek tale) og kan rutes direkte til Whisper-serveren uten jobbkø.
- Bruk
small-modellen for lav latens. Navnenøyaktighet er mindre viktig for korte chatmeldinger.
9. TTL (automatisk opprydding)
Channels med config.ttl_days satt til et tall får sine meldinger automatisk slettet av en nattlig jobbkø-jobb. Brukes for flyktige kontekster (scratchpads, studio-chat).
10. Implementeringsstatus
Ferdig (mars 2025)
- ChatBlock.svelte: Refaktorert til adapter-mønster via
createChat()factory - PG-adapter (
pg.svelte.ts): Polling hvert 3. sek, full-fetch (ingen akkumulering). Fungerer som selvstendig fallback. - SpacetimeDB hybrid-adapter (
spacetime.svelte.ts): Henter historikk fra PG via REST + lytter på SpacetimeDB WebSocket for sanntidspush. Dedupliserer mot PG-data. Faller tilbake til PG REST ved sending hvis SpacetimeDB er nede. - Factory (
create.svelte.ts): Velger adapter basert påVITE_SPACETIMEDB_URL. SSR-safe medbrowser-guard. - Shared types (
types.ts):MessageogChatConnectioninterface brukt av begge adaptere. - SpacetimeDB Rust-modul (
spacetimedb/src/lib.rs):ChatMessage-tabell,SyncOutbox,send_message-reducer. Publisert somsidelinja-realtime. - TypeScript-bindings: Generert fra SpacetimeDB-modulen, camelCase-properties.
- Autoscroll:
$effectsom scroller ned ved nye meldinger. - Datogruppering: Visuell datoseparator ("I dag", "I går", dato).
Gjenstår
- Sync-worker (SpacetimeDB → PG): Rust-worker som poller
sync_outboxog persisterer til PostgreSQL. Sedocs/infra/synkronisering.md§5.1. - Tråder, mentions, vedlegg, TTL — avventer Kunnskapsgraf CRUD (Lag 2).
- Autentisering:
author_name/author_idsettes ikke automatisk ennå (avventer Authentik-integrasjon).
11. Instruks for Claude Code
- Opprettelsesrekkefølge: Opprett
nodes-rad →channels-rad → (for meldinger)nodes-rad →messages-rad. Alt i én transaksjon med riktigworkspace_id. - Channel-opprettelse: Når en Tema, Episode eller Møte opprettes, opprett alltid en default-channel i samme transaksjon.
- Mentions-parsing: Skjer i SvelteKit server-side ved mottak av meldingen. Parse
#- og@-tags, valider mot noder i workspace-et, og opprettgraph_edges. - Config-respekt: Frontend-komponenten må lese
channel.configog slå av/på UI-elementer (tråd-knapp, vedlegg-knapp, mention-autocomplete) basert på config. - SpacetimeDB er autoritativ for aktive meldinger, PG for historikk.
- Alt er workspace-scopet. Channels arver workspace via
nodes.workspace_id.