synops/docs/features/universell_overfoering.md
vegard 9fefa0a8c2 Arbeidsflaten: workspace+tools-modell erstatter vokse-modellen
Ny retning: arbeidsflaten.md — spatial canvas med verktøy-paneler.
Drag-and-drop mellom verktøy oppretter nye noder med source_material-edges.
Noder muterer ikke — de føder nye noder.

Oppdatert docs:
- universell_input.md: "retyping" → "nye noder fra eksisterende"
- rom_ikke_forum.md: "bli" → "føde", siloer → verktøy-paneler
- universell_overfoering.md: blokker → verktøy-paneler, dual-modell
- meldingsboks.md: multi-rolle → visning i flere kontekster

Nye docs:
- arbeidsflaten.md: retning med kompatibilitetsmatrise og inkompatibilitet
- artikkelverktoy.md: langform TipTap-editor med drag-and-drop mottak

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 01:18:35 +00:00

9.3 KiB

Feature: Universell overføring — flytt innhold mellom verktøy

Filsti: docs/features/universell_overfoering.md

1. Konsept

Universell overføring er kjerneinteraksjonen på arbeidsflaten: drag-and-drop mellom verktøy-paneler. Se arbeidsflaten for den overordnede retningen.

1.1 To moduser

Modus Hva skjer Eksempel
Innholdstransfer Ny node opprettes med source_material-edge til kilden Chatboble → artikkelverktøy: ny artikkelnode
Lettvekts-triage Eksisterende node får ny edge/plassering Chatboble → kanban: noden vises i kanban OG chat

Default avhenger av verktøy-paret. Se kompatibilitetsmatrisen i arbeidsflaten. Bruker kan overstyre med modifier-tast (Shift = alltid ny node).

1.2 Grunnprinsipp

Bruker drar kort fra Chat → slipper på Artikkelverktøyet
  → Ny artikkelnode opprettes med sitert tekst
  → source_material-edge kobler artikkel → chatboble
  → Chatboblen forblir uendret i chatten

Bruker drar kort fra Chat → slipper på Kanban-brettet
  → Noden får board-edge + status-edge (lettvekts)
  → Noden vises i kanban OG chat (samme node, to kontekster)

Bruker kan alltid flytte (fjerne fra kilde) i stedet for å kopiere (beholde begge). Kontekstmeny gir valget.

2. Plasseringsrelasjon (Placement)

Når en melding vises i en kontekst (chat, kanban, storyboard, kalender), trenger vi metadata om hvordan den vises der. Dette er plasseringsrelasjonen — en edge i grafen mellom meldingen og konteksten, med metadata.

2.1 Datamodell

CREATE TABLE message_placements (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id   UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
    context_type TEXT NOT NULL,  -- 'chat', 'kanban', 'storyboard', 'calendar', 'notes'
    context_id   UUID NOT NULL,  -- channel_id, board_id, episode_id, calendar_id, note_id
    entered_at   TIMESTAMPTZ NOT NULL DEFAULT now(),  -- når objektet ankom denne konteksten
    position     JSONB,          -- kontekst-spesifikk posisjon (se §2.2)
    UNIQUE (message_id, context_type, context_id)
);

CREATE INDEX idx_placements_context ON message_placements(context_type, context_id, entered_at);
CREATE INDEX idx_placements_message ON message_placements(message_id);

2.2 Posisjonsdata per kontekst

position-feltet er JSONB og inneholder kontekst-spesifikk plassering:

Kontekst position-innhold Eksempel
Chat null (sorteres etter entered_at) null
Kanban { "column_id": "...", "position": 1.5 } Kolonne + rekkefølge
Storyboard { "x": 340, "y": 120 } Fritt canvas-posisjon
Kalender { "date": "2026-03-20", "all_day": true } Dato + tidspunkt
Notes { "position": 3 } Rekkefølge i notatet

2.3 Forhold til eksisterende view-configs

message_placements erstatter ikke de eksisterende view-config-tabellene (kanban_card_view, calendar_event_view) umiddelbart. Strategien er:

  1. Fase 1: message_placements brukes for nye kontekster (storyboard, notes) og for overføringsmekanikken
  2. Fase 2: Eksisterende view-configs migreres gradvis til message_placements (kanban-posisjon, kalender-dato)
  3. Fase 3: kanban_card_view og calendar_event_view kan fjernes når all logikk bruker placements

2.4 entered_at vs created_at

  • messages.created_at = når meldingen ble skrevet
  • message_placements.entered_at = når meldingen ankom denne konteksten

En melding opprettet mandag i chatten som dras til storyboardet onsdag har created_at = mandag og storyboard-plassering med entered_at = onsdag. I chatten sorteres den etter sin chat-plasserings entered_at — som er mandag (opprinnelig plassering). I storyboardet vises den på canvas-posisjonen, uavhengig av tid.

3. Sende-mekanikk (source)

3.1 Drag-and-drop

Brukeren drar et objekt ut av en verktøy-panel. Når objektet forlater verktøy-panel-grensen:

  1. Objektet blir en "ghost" (semi-transparent drag-representasjon)
  2. Andre verktøy-paneler som kan motta objektet highlighter sin mottakssone
  3. Slipp på en mottakssone → overføring

3.2 Kontekstmeny: "Send til..."

For situasjoner der drag-and-drop er upraktisk (mobil, lang avstand):

Høyreklikk kort → "Send til..." →
  ├── 📋 Kanban: Episodeplanlegging
  ├── 💬 Chat: #studio-diskusjon
  ├── 📅 Kalender: Redaksjonskalender
  └── 🎬 Storyboard: Episode 47

Listen viser alle verktøy-paneler brukeren har tilgang til som kan motta meldinger.

3.3 Flytt vs. kopier

  • Kopier (default): Meldingen får en ny plassering i mål-konteksten, beholder plasseringen i kilde-konteksten
  • Flytt (hold Shift ved drag, eller velg i kontekstmeny): Plasseringen i kilde-konteksten fjernes, ny plassering opprettes i mål

4. Mottak-mekanikk (target)

Hver verktøy-panel-type definerer en mottaker som bestemmer hva som skjer når et objekt ankommer:

4.1 Mottaker per verktøy-panel-type

Verktøy-panel Default-plassering Visuell feedback
Chat Ny melding i bunnen, entered_at = now() Kort blinker inn i chatflyten
Kanban Første kolonne (eller "Innboks"), posisjon øverst Kort glir inn i kolonnen
Storyboard Senter av viewport (eller slipp-posisjon) Kort fader inn
Kalender Dagens dato, heldagshendelse Dato highlighter
Notes Ny verktøy-panel i bunnen av notatet Tekst fades inn

4.2 Mottakssone-rendering og inkompatibilitet

Hver verktøy-panel rendrer en visuell drop-target når en drag er aktiv:

  • Kompatibel: Panelet lyser opp med subtil border/glow
  • Spesifikke soner (f.eks. en kolonne i kanban) highlighter ved hover
  • Inkompatibel: Drop-sonen er rød/grå. Tooltip forklarer hvorfor og foreslår alternativ der det gir mening.

Se kompatibilitetsmatrisen i arbeidsflaten for komplett oversikt over hva som kan dras til hva.

4.3 Mottaker-interface

interface BlockReceiver {
    /** Kan denne verktøy-panelen motta dette objektet? */
    canReceive(message: Message): boolean;

    /** Opprett plassering for mottatt objekt */
    receive(message: Message, dropPosition?: { x: number, y: number }): Placement;

    /** Visuell feedback for aktiv drag */
    renderDropZone(): void;
}

Alle verktøy-panel-typer implementerer dette interfacet.

5. SpacetimeDB-integrasjon

Plasseringsdata for sanntidskontekster (storyboard, kanban) eies av SpacetimeDB:

#[table(name = message_placement, public)]
pub struct MessagePlacement {
    #[primary_key]
    pub id: String,
    pub message_id: String,
    pub context_type: String,
    pub context_id: String,
    pub entered_at: Timestamp,
    pub position_json: String,  // JSON-serialisert posisjon
}

#[reducer]
pub fn place_message(ctx: &ReducerContext, placement: MessagePlacement) { ... }

#[reducer]
pub fn remove_placement(ctx: &ReducerContext, message_id: String, context_type: String, context_id: String) { ... }

#[reducer]
pub fn move_on_canvas(ctx: &ReducerContext, placement_id: String, new_position_json: String) { ... }

Sync-workeren persisterer til PG message_placements-tabellen.

6. Responsivt design

  • Desktop: Drag-and-drop mellom verktøy-paneler fungerer naturlig
  • Tablet: Drag-and-drop fungerer med touch, men "Send til..."-meny er primær
  • Mobil: Kun "Send til..."-meny (verktøy-paneler er stacked i én kolonne, drag mellom dem er upraktisk)

7. Bygger på

  • Meldingsboks (meldingsboks.md): Alle overførte objekter er meldingsbokser
  • Kunnskapsgraf (kunnskapsgraf_og_relasjoner.md): Plasseringer er relasjoner i grafen
  • BlockShell / PageGrid: Verktøy-panel-rammen som rendrer mottakssoner
  • SpacetimeDB (synkronisering.md): Sanntidssynk av plasseringer

8. Konsekvenser for eksisterende kode

8.1 BlockShell utvidelse

BlockShell trenger:

  • onDragEnter/onDragLeave/onDrop handlers for visuell feedback
  • Prop for receiver: BlockReceiver fra innholdsverktøy-panelen
  • Fullskjerm-toggle (knapp i header + Esc for å lukke)

8.2 Ny plasserings-tabell

message_placements er ny. Eksisterende kanban_card_view og calendar_event_view lever parallelt inntil migrering.

8.3 SpacetimeDB-modul

Ny tabell message_placement med reducers for place/remove/move.

9. Instruks for Claude Code

  • Innholdstransfer (ny node) brukes når innholdet transformeres (chat → artikkel)
  • Lettvekts-triage (ny edge/plassering) brukes når kontekst legges til (chat → kanban)
  • source_material-edge kobler ny node tilbake til kilden ved innholdstransfer
  • entered_at er alltid now() ved plassering, aldri kopiert fra kilden
  • En melding uten noen plasseringer er "løs" — UI advarer ved siste fjerning
  • Inkompatible drops gir visuell feedback (rød/grå) + tooltip med forklaring
  • Drag-and-drop bruker HTML5 API mellom paneler, pointer events intra-canvas
  • Hold overføringslogikken i en sentral transferService
  • Mottaker-interfacet er obligatorisk for alle verktøy-panel-typer