ChatTrait viser nå meldinger, input og taleopptak direkte i panelet i stedet for bare lenker til /chat/[id]. Ved flere kanaler vises kanalliste med klikk-navigasjon; ved én kanal åpnes chatten direkte. Endringer: - ChatTrait: inline meldingsvisning, ChatInput, AudioPlayer, auto-scroll - BlockReceiver peker nå til aktiv kanal (ikke bare collection) - Meldingsbobler er draggable ut til andre paneler - Responsivt flex-layout som tilpasser seg container-størrelse - accessToken-prop lagt til (trengs for meldingssending) - Forelder-sider oppdatert til å sende accessToken - /chat/[id]-ruten beholdes som frittstående fullside-visning Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
11 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 tilgangsstyring via node_access-matrisen.
┌─────────────┐ 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 admin for samlings-noden 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 som varm cache foran PG. Ved oppstart gjør worker warmup fra PG → SpacetimeDB (per-kanal konfigurasjon). Nye meldinger sendes via SpacetimeDB-reducers og kringkastes til alle tilkoblede klienter. Synkes til PostgreSQL med ~1 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 grupperer meldinger i tråder (rot + svar) med visuell skillelinje mellom hver tråd. Svar vises med innrykk og vertikal linje under rot-meldingen, uten ekstra skillelinje mellom rot og svar.
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. AI-behandlede meldinger har en revisjons-toggle i UI — brukeren kan veksle mellom AI-versjon og original tekst. AI-output rendres som Markdown via marked.
7.1 Meldingsvisning
Lange meldinger (mer enn 2 linjer) kollapses automatisk med en "Vis mer"-knapp. Ved ekspandering vises "Vis mindre" både over og under meldingen, slik at man slipper å scrolle for å kollapse igjen.
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 2026)
- ChatBlock.svelte: Adapter-mønster via
createChat()factory. Brukerchat.edit(),chat.delete(),chat.react()— ingen direkte PG API-kall. - SpacetimeDB-adapter (
spacetime.svelte.ts): Ren SpacetimeDB-adapter. All data fra SpacetimeDB (historikk via warmup + sanntid). Reaksjoner framessage_reaction-tabellen. - PG-adapter (
pg.svelte.ts): Polling hvert 3. sek. Readonly fallback når SpacetimeDB ikke er konfigurert. - Factory (
create.svelte.ts): Velger adapter basert påVITE_SPACETIMEDB_URL. SSR-safe medbrowser-guard. - Shared types (
types.ts):ChatConnectioninterface medsend,edit,delete,react,readonly. - SpacetimeDB Rust-modul (
spacetimedb/src/lib.rs):ChatMessage,MessageReaction,SyncOutbox-tabeller. Reducers:send_message,delete_message,edit_message,add_reaction,remove_reaction,load_messages,load_reactions,clear_channel,mark_synced. - Worker warmup (
worker/src/warmup.rs): PG → SpacetimeDB ved oppstart. Per-kanal konfig (all/messages/days/none). Trådbasert henting. - Worker sync (
worker/src/sync.rs): SpacetimeDB → PG hvert sekund. Insert/delete/update meldinger + reaksjoner. - Admin-side (
/admin/channels): Per-kanal warmup-konfigurasjon. - Tråder: Komplett trådvisning med datogruppering, autoscroll og visuell skillelinje mellom tråder.
- Reaksjoner: Via SpacetimeDB-reducers, synket til PG.
- Meldingskollaps: Lange meldinger begrenses til 2 linjer med "Vis mer"/"Vis mindre".
- AI-behandling: Meldinger kan AI-behandles (✨-knapp, eldre modell). Revisjons-toggle viser original vs. AI-versjon. Markdown-rendering for AI-output. NB: Erstattes av frittstående AI-verktøy på arbeidsflaten — se
docs/features/ai_verktoy.md. - Konvertering: Meldinger kan opprettes som kanban-kort eller kalenderhendelse (dialog sier "Opprett", ikke "Konverter" — meldingen beholdes i chatten).
ChatTrait panel (oppgave 20.5, mars 2026)
- Inline panel: ChatTrait er nå et fullverdig BlockShell-panel som viser meldinger, input og taleopptak direkte i panelet — ikke bare lenker til
/chat/[id]. - Kanalliste → chatvisning: Ved flere kanaler vises kanalliste, klikk åpner inline chat. Ved én kanal åpnes chatten direkte.
- BlockReceiver: Aksepterer drops fra alle andre paneler (
lettvekts-triage-modus). Droppet innhold knyttes til aktiv kanal. - Drag-out: Meldingsbobler er draggable — kan dras til andre paneler (kanban, editor, etc.).
- Responsivt: Tilpasser seg container-størrelse via flex-layout. Fungerer i både BlockShell-panel (desktop) og mobilfane.
- Fullskjerm-toggle: Via BlockShell-wrapperen (forelder-side wrapper ChatTrait i BlockShell).
/chat/[id]-ruten beholdes som frittstående fullside-visning for direktelenker og deling.
Gjenstår
- Vedlegg, TTL — avventer implementering.
- Tilgangsfiltrering: SpacetimeDB-laget må filtrere basert på
node_access-matrisen. - Pin/konvertering: Går fortsatt direkte til PG API (ikke via SpacetimeDB).
11. Instruks for Claude Code
- Opprettelsesrekkefølge: Opprett
nodes-rad →channels-rad → (for meldinger)nodes-rad →messages-rad. Alt i én transaksjon. - Channel-opprettelse: Når en Tema, Episode eller Møte opprettes, opprett alltid en default-channel i samme transaksjon.
- Mentions-parsing: Skjer i sync-workeren ved persistering til PG. Parser mention-UUIDs fra HTML body og oppretter
graph_edges. - Config-respekt: Frontend-komponenten må lese
channel.configog slå av/på UI-elementer.channels.configinneholder ogsåwarmup_mode/warmup_valuefor SpacetimeDB-oppvarming. - PG er autoritativ — SpacetimeDB er varm cache. Frontend snakker kun med SpacetimeDB.
- Tilgang styres via
node_access-matrisen. Channels arver tilgang fra sin parent-node via edges.