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>
156 lines
11 KiB
Markdown
156 lines
11 KiB
Markdown
# 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:
|
|
|
|
```json
|
|
{
|
|
"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'`):
|
|
|
|
```sql
|
|
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 automatisk `MENTIONS`-edges i `graph_edges` mellom 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
|
|
1. Bruker trykker og holder (eller toggler) mikrofon-knappen i chat-feltet.
|
|
2. Nettleseren fanger lyd via `MediaRecorder` API (WebM/Opus).
|
|
3. Lydklippet sendes til Whisper (`POST /v1/audio/transcriptions`, `response_format=text`, `language=no`) via SvelteKit server-side.
|
|
4. Transkripsjonen settes inn i meldingsfeltet — brukeren kan redigere før sending.
|
|
5. 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. Bruker `chat.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 fra `message_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 med `browser`-guard.
|
|
- **Shared types (`types.ts`):** `ChatConnection` interface med `send`, `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.config` og slå av/på UI-elementer. `channels.config` inneholder også `warmup_mode`/`warmup_value` for 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.
|