From 5b0881d5d9324b624a22d882a71839221da80c6b Mon Sep 17 00:00:00 2001 From: vegard Date: Wed, 18 Mar 2026 08:12:54 +0000 Subject: [PATCH] Implementer BlockReceiver i alle trait-komponenter (oppgave 20.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hver trait-komponent (Chat, Kanban, Kalender, Editor, Studio) har nå en BlockReceiver med canReceive() som sjekker kompatibilitetsmatrisen. Inkompatible drops viser forklaring og forslag til alternativ. Endringer: - transfer.ts: Per-verktøy compat-sjekker (checkChatCompat, checkKanbanCompat, checkCalendarCompat, checkEditorCompat, checkStudioCompat) + createBlockReceiver factory - types.ts: BlockReceiver utvidet med optional receive() + PlacementIntent type - BlockShell.svelte: Validerer payload på faktisk drop (ikke bare drag-over) - Alle 5 traits: Eksporterer BlockReceiver med canReceive + receive - workspace/+page.svelte: Kobler receivers til BlockShell i spatial canvas - Doc oppdatert til å reflektere faktisk implementasjon Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/features/universell_overfoering.md | 28 +++- .../components/blockshell/BlockShell.svelte | 14 ++ .../src/lib/components/blockshell/types.ts | 20 ++- .../components/traits/CalendarTrait.svelte | 30 +++- .../lib/components/traits/ChatTrait.svelte | 27 +++- .../lib/components/traits/EditorTrait.svelte | 32 +++- .../lib/components/traits/KanbanTrait.svelte | 28 +++- .../lib/components/traits/StudioTrait.svelte | 26 +++- frontend/src/lib/transfer.ts | 138 ++++++++++++++++++ frontend/src/routes/workspace/+page.svelte | 34 +++++ tasks.md | 3 +- 11 files changed, 363 insertions(+), 17 deletions(-) diff --git a/docs/features/universell_overfoering.md b/docs/features/universell_overfoering.md index 4a754d5..5348f7a 100644 --- a/docs/features/universell_overfoering.md +++ b/docs/features/universell_overfoering.md @@ -142,17 +142,31 @@ for komplett oversikt over hva som kan dras til hva. ```typescript interface BlockReceiver { /** Kan denne verktøy-panelen motta dette objektet? */ - canReceive(message: Message): boolean; + canReceive(payload: DragPayload): CompatResult; - /** Opprett plassering for mottatt objekt */ - receive(message: Message, dropPosition?: { x: number, y: number }): Placement; - - /** Visuell feedback for aktiv drag */ - renderDropZone(): void; + /** Opprett plassering for mottatt objekt (optional — full impl i transfer service) */ + receive?(payload: DragPayload, dropPosition?: { x: number; y: number }): PlacementIntent; } ``` -Alle verktøy-panel-typer implementerer dette interfacet. +Alle verktøy-panel-typer implementerer `canReceive()`. `receive()` er optional — +den fulle overføringslogikken (ny node + edges) håndteres av transfer service (§ 1). + +**Drop-zone rendering** (`renderDropZone`) håndteres av `BlockShell`, ikke +av trait-komponentene selv. BlockShell bruker `receiver.canReceive()` for å +bestemme visuell tilstand (`compatible` / `incompatible`), og viser overlay +med forklaring ved inkompatibilitet. + +**Kompatibilitetssjekker** per verktøy-type finnes i `$lib/transfer.ts`: +- `checkChatCompat()` — Chat aksepterer alt unntatt fra egen panel +- `checkKanbanCompat()` — Kanban aksepterer kommunikasjon og innhold +- `checkCalendarCompat()` — Kalender aksepterer kommunikasjon og innhold +- `checkEditorCompat()` — Artikkelverktøy aksepterer tekst og media +- `checkStudioCompat()` — Studio aksepterer kun lyd +- `checkAiToolCompat()` — AI-verktøy aksepterer kun tekst + +Factory-funksjon `createBlockReceiver(toolType)` oppretter en `BlockReceiver` +for en gitt verktøy-type. ## 5. SpacetimeDB-integrasjon diff --git a/frontend/src/lib/components/blockshell/BlockShell.svelte b/frontend/src/lib/components/blockshell/BlockShell.svelte index 41e18b4..f3b2ddb 100644 --- a/frontend/src/lib/components/blockshell/BlockShell.svelte +++ b/frontend/src/lib/components/blockshell/BlockShell.svelte @@ -257,6 +257,20 @@ if (dropZoneState === 'compatible' && e.dataTransfer) { const payload = getDragPayload(e.dataTransfer); if (payload) { + // Validate on actual drop (we couldn't read payload during drag) + if (receiver) { + const result = receiver.canReceive(payload); + if (!result.compatible) { + // Show incompatible feedback briefly + dropZoneState = 'incompatible'; + dropFeedback = result.reason ?? 'Kan ikke motta dette innholdet'; + setTimeout(() => { + dropZoneState = 'idle'; + dropFeedback = ''; + }, 1500); + return; + } + } onDrop?.(payload); } } diff --git a/frontend/src/lib/components/blockshell/types.ts b/frontend/src/lib/components/blockshell/types.ts index c6dd218..9a9534d 100644 --- a/frontend/src/lib/components/blockshell/types.ts +++ b/frontend/src/lib/components/blockshell/types.ts @@ -8,7 +8,19 @@ * Ref: docs/features/universell_overfoering.md § 8.1 */ -import type { DragPayload, CompatResult } from '$lib/transfer.js'; +import type { DragPayload, CompatResult, ToolType } from '$lib/transfer.js'; + +/** Placement intent returned by BlockReceiver.receive() */ +export interface PlacementIntent { + /** What happens on receive: new node or new edge/placement */ + mode: 'innholdstransfer' | 'lettvekts-triage'; + /** Target context ID (collection, board, calendar, channel) */ + contextId: string; + /** Context type for placement table */ + contextType: string; + /** Position data (column for kanban, date for calendar, etc.) */ + position?: Record; +} /** Size constraints for a panel */ export interface SizeConstraints { @@ -40,10 +52,16 @@ export type DropZoneState = 'idle' | 'compatible' | 'incompatible'; /** * BlockReceiver interface — implemented by tool panels that accept drops. * Ref: docs/features/universell_overfoering.md § 4.3 + * + * - canReceive(): compatibility check (called during drag-over) + * - receive(): creates placement/edge (called on drop) + * - renderDropZone is handled by BlockShell (visual feedback via CSS) */ export interface BlockReceiver { /** Can this panel receive this payload? */ canReceive(payload: DragPayload): CompatResult; + /** Process a received drop — returns placement intent for the transfer service */ + receive?(payload: DragPayload, dropPosition?: { x: number; y: number }): PlacementIntent; } /** Events emitted by BlockShell */ diff --git a/frontend/src/lib/components/traits/CalendarTrait.svelte b/frontend/src/lib/components/traits/CalendarTrait.svelte index c5cfb05..1692efb 100644 --- a/frontend/src/lib/components/traits/CalendarTrait.svelte +++ b/frontend/src/lib/components/traits/CalendarTrait.svelte @@ -1,15 +1,43 @@ diff --git a/frontend/src/lib/components/traits/StudioTrait.svelte b/frontend/src/lib/components/traits/StudioTrait.svelte index 945ce06..229e779 100644 --- a/frontend/src/lib/components/traits/StudioTrait.svelte +++ b/frontend/src/lib/components/traits/StudioTrait.svelte @@ -1,15 +1,39 @@ @@ -516,9 +548,11 @@ width={panel?.width ?? obj.width} height={panel?.height ?? obj.height} minimized={panel?.minimized ?? false} + receiver={getReceiverForTrait(trait)} onResize={(w, h) => handlePanelResize(trait, w, h)} onClose={() => handlePanelClose(trait)} onMinimizeChange={(m) => handlePanelMinimize(trait, m)} + onDrop={(payload) => handlePanelDrop(trait, payload)} > {#if knownTraits.has(trait)} {#if trait === 'editor'} diff --git a/tasks.md b/tasks.md index 284c9b5..0a781cc 100644 --- a/tasks.md +++ b/tasks.md @@ -225,8 +225,7 @@ Ref: `docs/features/universell_overfoering.md`, `docs/retninger/arbeidsflaten.md - [x] 20.1 message_placements tabell: PG-migrasjon + SpacetimeDB-modul med `place_message`, `remove_placement`, `move_on_canvas` reducers. Synk STDB→PG. Ref: `docs/features/universell_overfoering.md` § 2. - [x] 20.2 source_material edge-type: legg til i edge-skjema + maskinrommet-validering. Støtt kontekst-metadata (quoted, summarized, referenced) og excerpt-felt. Ref: `docs/retninger/arbeidsflaten.md` § "source_material-edge". -- [~] 20.3 BlockReceiver interface: implementer `canReceive()`, `receive()`, `renderDropZone()` i alle trait-komponenter (Chat, Kanban, Kalender, Editor, Studio). Kompatibilitetsmatrise bestemmer godkjente drops. Ref: `docs/features/universell_overfoering.md` § 4–5. - > Påbegynt: 2026-03-18T08:05 +- [x] 20.3 BlockReceiver interface: implementer `canReceive()`, `receive()`, `renderDropZone()` i alle trait-komponenter (Chat, Kanban, Kalender, Editor, Studio). Kompatibilitetsmatrise bestemmer godkjente drops. Ref: `docs/features/universell_overfoering.md` § 4–5. - [ ] 20.4 Transfer service: `innholdstransfer`-modus (ny node + source_material edge) og `lettvekts-triage` (eksisterende node + ny edge/placement). Bestem modus fra verktøy-par. Shift-modifier for override. Ref: `docs/features/universell_overfoering.md` § 1, 3. - [ ] 20.5 Panelrework — Chat: gjør ChatTrait til fullverdig BlockShell-panel med BlockReceiver, fullskjerm-toggle, og responsivt design innenfor begrenset container. - [ ] 20.6 Panelrework — Kanban: gjør KanbanTrait til BlockShell-panel med drag-and-drop aksept fra andre paneler, fullskjerm, responsivt.