Commit graph

95 commits

Author SHA1 Message Date
b190c79fb3 Mørk canvas-bakgrunn + subtile indigo grid-linjer
Canvas var #f8f9fa (nesten hvit). Nå #0a0a0b.
Grid-linjer fra svart 5% til indigo 6% for synlighet på mørk bakgrunn.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 02:29:18 +00:00
543b0ca29f Mørkt tema på alle sider: workspace, canvas, blockshell, traits, collection
Erstattet alle hardkodede lyse farger (white, #f0f2f5, #f3f4f6)
med mørke (#0a0a0b, #1c1c20, #242428) i alle Svelte-komponenter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 02:27:15 +00:00
ab30ab8c65 Fiks mørkt tema: force bakgrunn på html/body/min-h-screen, lysere surface
Bakgrunnen var fortsatt hvit fordi Tailwind-klasser overstyrte body.
Nå forcert med !important på html, body, .min-h-screen.
Surface justert fra #141416 til #1c1c20 for bedre kontrast på kort.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 02:19:12 +00:00
98a7b0ab62 Mørkt tema: matcher landingssiden (accent indigo, surface #141416)
Overstyr Tailwind grays med mørke fargevariabler. Inputs, borders,
shadows, scrollbar, knapper og tekst tilpasset. Samme palett som
synops.no landingssiden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 02:16:32 +00:00
c5239d2923 Feed-redirect: 301 for podcast som flyttes til ny host (oppgave 30.8)
Når redirect_feed er satt i podcast-trait, returnerer maskinrommet
HTTP 301 Moved Permanently med Location-header i stedet for å serve
feeden. iTunes new-feed-url-taggen bevares også i RSS-en for klienter
som ikke følger 301.

Admin-UI: erstatter det enkle tekstfeltet med tre tilstander:
- Inaktiv: knapp "Flytt podcast til annen plattform..."
- Bekreftelse: advarsel + URL-felt + rød "Aktiver redirect"-knapp
- Aktiv: gul statusindikator med deaktiver-knapp

Backend: sjekker redirect_feed tidlig i generate_feed() og returnerer
301 før noe annet arbeid gjøres (DB-oppslag for episodes osv).
2026-03-19 00:31:39 +00:00
62b1ecd0b6 Podcast import wizard: backend + frontend (oppgave 30.7)
Backend (maskinrommet):
- Nytt modul podcast_import.rs med 4 endepunkter:
  POST /admin/podcast/import-preview (dry-run via CLI)
  POST /admin/podcast/import (starter jobb i køen)
  GET /admin/podcast/import-status (poll jobbstatus)
  GET /admin/podcast/collections (samlinger med podcast-trait)
- Ny jobbtype import_podcast i jobs.rs dispatcher

Frontend:
- Ny wizard-side /admin/podcast-import med 5 steg:
  1. RSS-URL + samling → forhåndsvisning
  2. Import (spinner med jobbstatus-polling)
  3. Resultat med sammenligning av feeds
  4. Re-import for nye episoder
  5. 301-redirect-info
- API-funksjoner i api.ts
- Navigasjonslenke i admin-panelet
2026-03-19 00:19:24 +00:00
3e57adce46 Podcast-statistikk dashboard i admin-panelet (oppgave 30.4)
Nytt dashboard under /admin/podcast-stats som viser:
- Nøkkeltall: totale nedlastinger, unike lyttere, antall episoder
- Daglig trend med horisontale bar charts
- Topp-episoder rangert etter nedlastinger
- Klientfordeling (Apple Podcasts, Spotify, etc.) med stacked bar

Backend: GET /admin/podcast/stats spør podcast_download_stats-tabellen
(fylt av synops-stats CLI fra oppgave 30.3) og aggregerer per episode,
per dag, og per klient via jsonb_each_text.

Filtrering på tidsperiode (7/30/90/365 dager) og enkelt-episode.
2026-03-18 23:42:23 +00:00
6aeb8aa783 Podcast-trait admin-UI og utvidet RSS-metadata (oppgave 30.2)
Dedikert admin-UI for podcast-trait med riktige skjemafelt:
- iTunes Author, Category (med underkategori-dropdown), Language
- Explicit-avkrysning, Redirect Feed URL
- Erstatter generisk nøkkel/verdi-editor for podcast-traitet

RSS-utvidelser:
- itunes:category støtter nå nested subcategory-element
- itunes:new-feed-url for feed-migrasjon via redirect_feed
- Oppdatert både maskinrommet og synops-rss CLI-verktøy
2026-03-18 23:21:15 +00:00
4d03ab7271 Tegne-input: enkel canvas-basert skisseflate i chat-input (oppgave 29.10)
Ny DrawingInput-komponent som lar brukeren tegne en rask skisse direkte
fra chat-inputen. Åpner fullskjerm-canvas med fargevelger, penseltykkelse,
angre og tøm. Eksporterer som PNG → CAS → media-node med metadata
source=drawing. Følger samme mønster som VoiceRecorder/VideoRecorder.

Ikke whiteboard — dette er "post-it-skisse som input".
2026-03-18 22:43:19 +00:00
6729a35435 Lokasjon-input: del posisjon i chat med kartvisning (oppgave 29.9)
Ny «Del posisjon»-knapp i ChatInput ved siden av tale/video-knappene.
Bruker Geolocation API for å hente brukerens posisjon, oppretter en
content-node med metadata.location { lat, lon, address }.

Reverse geocoding via Nominatim (best-effort) gir adresse i metadata.
Kartvisning i chat via Leaflet/OpenStreetMap viser posisjonen inline.

Komponenter:
- LocationShare.svelte: knapp + geolocation + geocoding + node-opprettelse
- LocationMap.svelte: Leaflet-kart med markør og adresse-popup
- Leaflet lastes via CDN (unpkg) i app.html
2026-03-18 22:36:08 +00:00
fd40d51466 Video-opptak i frontend: webcam/skjermopptak via MediaRecorder API (oppgave 29.7)
VideoRecorder-komponent med to moduser:
- Kamera: getUserMedia med video+lyd, 720p
- Skjerm: getDisplayMedia med valgfri lyd, 1080p

Funksjoner:
- Modus-velger (kamera/skjerm) før opptak starter
- Live forhåndsvisning under opptak
- Konfigurerbar maks varighet (default 5 min), advarsel siste 30 sek
- Automatisk stopp ved maks varighet
- Upload til CAS → media-node med metadata (source, record_type, duration)
- Integrert i ChatInput ved siden av VoiceRecorder
2026-03-18 22:19:30 +00:00
a3dfa3b254 Webhook-templates: forhåndsdefinerte mappinger for kjente tjenester (oppgave 29.6)
Legger til et template-system for webhooks som vet hvordan kjente
tjenester strukturerer sine JSON-payloads, og mapper dem til
meningsfulle node title/content/metadata.

Templates:
- github-push: Commits med repo, branch, pusher, formaterte meldinger
- github-issues: Issue-hendelser med nummer, labels, state
- github-pull-request: PR-hendelser med branch-info, merge-status
- slack-message: Slack Event API-meldinger med kanal og bruker
- ci-build: Generisk CI/CD (GitHub Actions, GitLab CI, Jenkins)

Backend:
- webhook_templates.rs: Template-definisjoner og apply-logikk
- webhook.rs: Bruker template fra webhook-nodens metadata.template_id
- webhook_admin.rs: GET /admin/webhooks/templates, POST set_template,
  template_id i create og list

Frontend:
- Template-velger i opprett-skjema og på hver webhook-kort
- Kan bytte template på eksisterende webhooks

6 unit-tester for alle templates. Verifisert med curl mot live endpoint.
2026-03-18 22:10:33 +00:00
097ef02aea Webhook-admin: UI for å opprette/administrere webhooks (oppgave 29.5)
Backend (maskinrommet):
- GET /admin/webhooks — liste alle webhooks med aktivitetsinfo
- GET /admin/webhooks/events?webhook_id=... — siste hendelser
- POST /admin/webhooks/create — opprett webhook for samling
- POST /admin/webhooks/regenerate_token — nytt token
- POST /admin/webhooks/delete — slett webhook

Frontend:
- /admin/webhooks side med full CRUD
- Vis token, mål-samling, hendelsesteller, siste aktivitet
- Kopier token/URL til utklippstavle
- Utfellbar hendelseslogg per webhook med payload-visning
- Regenerer token med bekreftelse
- Slett med bekreftelse
- Nav-lenke fra admin-hub
2026-03-18 21:55:24 +00:00
1d8ebf259b Skjermklipp-input: paste screenshot i chat → CAS → media-node (oppgave 29.1)
Frontend:
- ChatInput: paste-handler detekterer bilder fra clipboard (ClipboardEvent),
  laster opp til CAS via uploadMedia med metadata_extra { source: "screenshot" }
- Chat-side: viser bildenoder inline med AI-beskrivelse når tilgjengelig
- api.ts: uploadMedia støtter nå metadata_extra for ekstra node-metadata

Backend (maskinrommet):
- upload_media: nytt metadata_extra multipart-felt som merges inn i
  media-nodens metadata (f.eks. source, description)
- describe_image: ny jobbtype — enqueuues automatisk for screenshot-uploads,
  kaller synops-ai med --image for AI-beskrivelse av bildet
- Beskrivelsen lagres tilbake i media-nodens metadata.description

synops-ai:
- Nytt --image flag for multimodal LLM-kall (vision) via LiteLLM
- Sender bilde som base64 data-URL i OpenAI-kompatibelt format
- Brukes av describe_image-jobben for bildbeskrivelse
2026-03-18 21:07:00 +00:00
a06b79478a AI-rutingskontroll i admin: 13 kontekster konfigurerbare uten redeploy (oppgave 28.2)
Utvider /admin/ai med full kontroll over hvilken modellalias som brukes
per AI-kontekst. Admin kan bytte modell for orkestrering, bot-chat,
oppsummering, edge-forslag, klassifisering osv. uten å restarte
maskinrommet.

Endringer:
- Migration 028: seeder 7 nye kontekster i ai_job_routing
  (orchestration_script/dream, bot_chat/triage, summarize, suggest_edges, classify)
- Backend: resolve_routing_or_default() i ai_admin.rs — felles oppslag
  mot ai_job_routing med fallback til sidelinja/rutine
- Dispatchers (ai_edges, summarize) bruker nå routing-tabellen i stedet
  for hardkodede env-variabler — endringer trer i kraft umiddelbart
- Frontend: Ruting-tab omskrevet med kategoriserte kontekster
  (Orkestrering, Bot & chat, Analyse, Prosessering), beskrivelser per
  kontekst, og støtte for egendefinerte regler
- Docs: ai_gateway.md §3.4 oppdatert med alle 13 kontekster
2026-03-18 20:06:50 +00:00
8f7d2d7fe8 MindMap-trait for samlingsnoder (oppgave 27.3)
Gjør mindmap til et offisielt trait som kan velges ved opprettelse
av samlingsnoder. Frontend-komponenten (27.1) og BlockShell-panelet
(27.2) var allerede på plass — dette kobler traiten inn i backend-
validering og pakke-definisjoner.

Endringer:
- Lagt til "mindmap" i VALID_TRAITS (intentions.rs)
- Validering av konfig: default_depth 1-3, layout radial/tree
- Inkludert mindmap i Podcaststudio- og Wiki-pakker (traits.ts)
- Oppdatert trait-katalog i docs/primitiver/traits.md
2026-03-18 19:44:30 +00:00
6c5cdf817d MindMap BlockShell-panel i arbeidsflaten (oppgave 27.2)
Legger til MindMap-støtte i workspace-siden slik at tankekartet
kan brukes som et fritt plassert BlockShell-panel med fullskjerm,
resize og drag-handle — på linje med alle andre trait-paneler.

Samlingssiden hadde allerede full MindMap-integrasjon fra 27.1.
Workspace-siden manglet import, knownTraits-oppføring og
rendering i både mobil- og desktop-modus.
2026-03-18 19:38:49 +00:00
e6b55543b5 MindMap Svelte-komponent med radial/tree-layout (oppgave 27.1)
Ny MindMapTrait-komponent som viser noder og edges fra WebSocket-stores
i radial eller hierarkisk tree-layout via d3-hierarchy.

- Bygger trestruktur fra rotnode med konfigurerbar dybde (1-3 hopp)
- Radial layout (trigonometrisk) og tree layout (hierarkisk) med toggle
- Pan/zoom via d3-zoom, auto-fit ved oppstart
- Klikk node = ny rot (med tilbake-historikk), dobbeltklikk = åpne
- Edge-type-filtrering og visuell stil (farge, stiplet linje)
- Privacy-markering på private noder
- Responsivt: forenklet toolbar på mobil
- Registrert som 'mindmap' trait i katalog og workspace-typer
- Koblet inn i collection-side (desktop BlockShell + mobil tabs)
2026-03-18 19:32:17 +00:00
25fc1a1b59 Username i auth_identities: Authentik-synk ved login (oppgave 26.1)
Legger til username-kolonne i auth_identities med UNIQUE constraint.
Ved innlogging sender SvelteKit preferred_username fra Authentik til
maskinrommet POST /auth/sync, som oppdaterer kolonnen. Grunnlaget
for epost-ruting i fase 26: vegard@synops.no → username-oppslag.
2026-03-18 19:04:57 +00:00
d18dfc260f AI-assistert oppretting: synops-ai genererer orkestreringsscript fra fritekst (oppgave 24.7)
Nytt CLI-verktøy `synops-ai` som leser cli_tool-noder fra PG, bygger
en systemprompt med tilgjengelige verktøy og script-grammatikk, og
bruker LLM til å foreslå orkestreringsscript fra fritekst-beskrivelse.

Tre moduser:
- Synkron: --description "..." → LLM genererer script → JSON output
- System prompt: --generate-system-prompt → skriver auto-generert prompt
- Eventually: --eventually → lagrer som work_item for Claude Code

Maskinrommet: nytt endepunkt POST /intentions/ai_suggest_script som
kaller synops-ai, validerer resultatet med script_compiler, og returnerer
script + kompileringsresultat til frontend.

Frontend: AI-assistent-knapp i OrchestrationTrait med fritekst-input,
generer-knapp, og feilvisning. Generert script settes direkte i editoren.

Migration: synops-ai seeded som cli_tool-node med norske verb-alias.
2026-03-18 17:47:32 +00:00
376bf7ee62 Orchestration UI: editor med tre visninger, sanntids kompilering, testkjøring (oppgave 24.6)
Backend:
- POST /intentions/compile_script — kompilerer script, returnerer diagnostikk + kompilert resultat
- POST /intentions/test_orchestration — trigger manuell testkjøring via jobbkø
- GET /query/orchestration_log — henter kjørehistorikk for en orkestrering
- "orchestration" lagt til som gyldig trait for samlingsnoder

Frontend:
- OrchestrationTrait.svelte — BlockShell-panel med:
  - Tre tabber: Enkel (editor), Teknisk (kompilert CLI), Kompilert (JSON)
  - Sanntids kompilering med 500ms debounce og diagnostikk-visning
  - Trigger-velger (6 event-typer) og executor-velger (script/bot/dream)
  - "Test kjøring"-knapp (lagrer + oppretter testjobb i køen)
  - Kjørehistorikk-panel med steg-status, varighet, feilmeldinger
  - Responsiv: container queries + mobile viewport fallback
- Registrert i collection-page (mobil + desktop), workspace/types.ts
- API-funksjoner: compileScript, testOrchestration, fetchOrchestrationLog
2026-03-18 17:30:52 +00:00
e8a1a80652 Valider fase 22: STDB-migrering fullført, ingen rester i aktiv kode
Validering av fase 22 (SpacetimeDB-migrering) bekrefter:

1. WebSocket-sanntid fungerer:
   - maskinrommet lytter på PG NOTIFY-kanaler (node_changed, edge_changed,
     access_changed, mixer_channel_changed)
   - Enrichment av events med fulle rader fra PG
   - Broadcast via tokio::broadcast til WebSocket-klienter
   - Tilgangskontroll filtrerer events per bruker
   - Frontend kobler til /ws med JWT, mottar initial_sync + inkrementelle events

2. PG LISTEN/NOTIFY-triggere verifisert i database:
   - 4 notify-funksjoner: notify_node_change, notify_edge_change,
     notify_access_change, notify_mixer_channel_change
   - 4 triggere: nodes_notify, edges_notify, node_access_notify,
     mixer_channels_notify

3. Ingen STDB-rester i aktiv kode/konfig:
   - maskinrommet/src/: rent
   - Cargo.toml: ingen spacetimedb-avhengigheter
   - docker-compose.yml: ingen spacetimedb-tjeneste
   - Caddyfile: ingen spacetimedb-proxy
   - Eneste funn: frontend/src/lib/spacetime/ katalognavn —
     omdøpt til frontend/src/lib/realtime/ (32 filer oppdatert)
   - Historiske referanser i docs/arkiv og scripts/synops.md er OK
2026-03-18 16:31:16 +00:00
15dd23b873 Valider fase 19–20: arbeidsflaten + universell overføring bestått
Fikser funnet under validering:
- Gjør collection-prop valgfri i alle trait-komponenter slik at de
  fungerer i personlig arbeidsflate uten collection-kontekst
- Legger til null-guards for collection.id i alle derived-blokker
  og funksjoner som oppretter edges
- Fjerner microsSinceUnixEpoch-rester fra STDB-migrasjonen —
  createdAt er nå et tall (Unix µs), ikke et objekt
- Retter saveTimeout-lekkasje i collection-sida: timer ryddes nå
  ved navigasjon mellom samlinger
- Fikser TypeScript-feil i editorial (number vs string, uoppnåelig
  'scheduled'-sammenligning), studio (bigint vs number),
  RecordingTrait ($state-generics)
- Typefeil redusert fra 55 → 4 (gjenværende er pre-eksisterende
  i mixer.ts/livekit.ts, ikke fase 19-20)

Validert: Canvas pan/zoom, BlockShell, layout-persistering,
snarveier, transfer service, alle panelreworks. Frontend bygger OK.
2026-03-18 16:03:17 +00:00
b5f419486a Valider fase 15–16: fiks minnelekkasje, skrivefeil + marker ferdig
- SoundPadGrid: spor setTimeout-IDer og rydd opp ved komponent-destroy
  (unngår oppdatering av tilstand etter unmount)
- Admin usage: fiks skrivefeil "1 ar" → "1 år"
- tasks.md: marker 23.7 som fullført
2026-03-18 15:42:34 +00:00
382f93132f Valider fase 9–10: fiks typefeil, konsistens og feilhåndtering
Fase 9 (visninger):
- graph: fiks TypeScript-feil (string|undefined → string|null)
- kanban board: les kolonner fra metadata.traits.kanban.columns
  (konsistent med KanbanTrait), behold fallback til metadata.columns
- dagbok: fiks createdAt-type (number, ikke BigInt med microsSinceUnixEpoch)

Fase 10 (AI):
- summarize.rs: refaktorer til cli_dispatch helper (DRY, konsistent
  med ai_edges.rs og tts.rs)
- ai_process.rs: feil tidlig hvis LITELLM_MASTER_KEY mangler
  (var unwrap_or_default → tom streng → forvirrende 401)

Alle 61 maskinrommet-tester bestått, alle CLI-verktøy kompilerer.
LiteLLM kjører med riktig modellruting (rutine/resonering).
2026-03-18 15:09:03 +00:00
5dfaeff53c Valider fase 3–4: fiks belongs_to-tilgangspropagering og mottakssortering
Validering av fase 3 (frontend) og fase 4 (tilgangskontroll) avdekket to bugs:

1. belongs_to-access-gap: Når en belongs_to-edge opprettes ETTER at
   noen allerede har tilgang til foreldrenoden, fikk ikke barnenoden
   tilgangsoppføringer i node_access-matrisen. F.eks. kunne Vegard (eier
   av en kommunikasjonsnode) ikke se innhold opprettet av Claude med
   belongs_to-edge til den noden.

   Løsning: Ny PG-funksjon propagate_belongs_to_access() som kopierer
   forelderens tilgangsrader til barnet. Kalles fra maskinrommet ved
   opprettelse av belongs_to-edges (create_node m/context, create_edge,
   create_communication m/context). Retroaktiv fiks for eksisterende data.

2. Mottaksflate-sortering: Brukte .microsSinceUnixEpoch (SpacetimeDB-
   BigInt-arv) på vanlig number-felt, ga alltid 0n → ingen sortering.
   Fikset til direkte number-sammenligning.

Verifisert: SvelteKit + maskinrommet bygger og kjører. PG-skjema, OIDC,
WebSocket/NOTIFY, RLS-policies, team-transitivitet og visibility fungerer.
2026-03-18 14:41:20 +00:00
b5aa5bb243 Fjern SpacetimeDB komplett (oppgave 22.4)
SpacetimeDB er nå helt fjernet fra Synops. Sanntid håndteres av
PG LISTEN/NOTIFY + WebSocket i portvokteren (maskinrommet).

Kode fjernet:
- spacetimedb/ Rust-modul og spacetime.json
- maskinrommet/src/stdb.rs (HTTP-klient for STDB-reducers)
- frontend module_bindings/ (23 auto-genererte filer)
- spacetimedb npm-avhengighet fra package.json
- scripts/test-sanntid.sh (testet STDB-flyt)

Infrastruktur:
- Docker-container stoppet og fjernet fra docker-compose.yml
- Caddy: fjernet /spacetime/* reverse proxy
- maskinrommet-env.sh: fjernet STDB_IP og SPACETIMEDB_*-variabler
- .env.example: fjernet SpacetimeDB-seksjoner

Dokumentasjon oppdatert:
- CLAUDE.md: stack, lagmodell, kjerneprinsipper, driftsmodell
- docs/arkitektur.md: skrivestien, lesestien, datalag, teknologivalg
- docs/retninger/datalaget.md: migrasjonshistorikk, status "fjernet"
- 37 andre docs oppdatert (features, concepts, infra, ops, retninger)
- Alle kode-kommentarer med STDB-referanser oppdatert

Verifisert: maskinrommet bygger og starter OK, frontend bygger OK,
helsesjekk returnerer 200. Caddy reloadet.
2026-03-18 13:39:09 +00:00
8e80102f6b Fullfør oppgave 22.2: Frontend-migrering fra SpacetimeDB til portvokteren
Frontend bruker nå kun portvokterens WebSocket for sanntidsdata.
SpacetimeDB-klienten er erstattet med en enkel WebSocket-klient
som kobler til /ws-endepunktet og oppdaterer reactive stores direkte.

Frontend-endringer:
- Nye lokale typer (types.ts) erstatter STDB module_bindings
- connection.svelte.ts: WebSocket til portvokteren med auto-reconnect
- stores.svelte.ts: Prosesserer WS-meldinger (initial_sync + events)
- MixerTrait: STDB-reducers erstattet med HTTP API-kall
- api.ts: Nye mixer-endepunkter (create, gain, mute, effect, role)
- +layout.svelte: Fjernet dual-tilkobling, kun portvokterens WS
- pg-ws.svelte.ts: Slettet (erstattet av connection.svelte.ts)

Dokumentasjon:
- datalaget.md: Fase M1+M2 markert som fullført
- api_grensesnitt.md: Oppdatert arkitekturdiagram, nye mixer-endepunkter
2026-03-18 12:26:33 +00:00
d7a9f3816d Fullfør oppgave 22.1: WebSocket-lag med PG LISTEN/NOTIFY og frontend dual-tilkobling
Backend:
- ws.rs: Fikset WS auth via query-param (browser kan ikke sende headers ved WS upgrade)
- auth.rs: Gjort decoding_key pub for gjenbruk i ws-modulen

Frontend:
- pg-ws.svelte.ts: Ny PG WebSocket-klient med auto-reconnect og event-logging
- index.ts: Eksporterer pgWsConnect/pgWsDisconnect/pgWsState
- +layout.svelte: Kobler til PG WS i parallell med STDB for verifisering

Docs:
- api_grensesnitt.md: Dokumentert /ws endepunkt og sanntidsarkitektur
- tasks.md: Merket 22.1 som ferdig

Deploy: Krever restart av maskinrommet + rebuild av frontend.
2026-03-18 12:01:10 +00:00
5babad9e59 Gjør StudioTrait til fullverdig BlockShell-panel med lydstudio-funksjonalitet (oppgave 20.9)
StudioTrait var en minimal liste med TraitPanel-wrapper. Nå er den et
fullverdig BlockShell-panel som følger mønsteret fra CalendarTrait (20.7)
og EditorTrait (20.8):

- Fjernet TraitPanel-wrapper, bruker egen flex-layout som fyller BlockShell
- Viser lydfiler med metadata: format-tag, varighet, størrelse, dato
- Drag-and-drop ut: lydfiler kan dras til andre paneler (editor, kalender)
- Drop-aksept: mottar lydfiler fra andre paneler via BlockReceiver
- Viser prosesserte versjoner (derived_from edges) per fil
- Responsivt med @container queries (360px, 500px) og @media fallback
- Lagt til `studio` trait i trait-katalogen i docs/primitiver/traits.md
- accessToken sendes nå fra collection page til StudioTrait

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:52:41 +00:00
82dd61549b Gjør EditorTrait til fullverdig BlockShell-panel med inline editor og kilder (oppgave 20.8)
Fjerner TraitPanel-wrapper og erstatter med selvstendig panel-layout
som matcher KanbanTrait/CalendarTrait-mønsteret:

- Selvstendig layout som fyller BlockShell content area
- Listevisning med klikk-for-å-redigere (inline tittel + innhold)
- Source_material-edges vises som "Kilder"-sidebar i editor-visningen
- Visuell venstrekant-markering på artikler med kilder
- Opprett ny artikkel direkte fra panelet
- Container queries for responsiv tilpasning (small/medium/mobile)
- Beholder all eksisterende funksjonalitet: AI preset drops, publisering,
  drag-and-drop ut av panelet

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:47:20 +00:00
9035f09667 Gjør CalendarTrait til fullverdig BlockShell-panel med inline kalender (oppgave 20.7)
CalendarTrait var tidligere en stub med lenke til /calendar-ruten.
Nå er hele kalender-UI-et innebygd direkte i panelet:

- Månedsgrid med navigasjon (forrige/neste/i dag)
- Hendelser vises som fargekodede chips i dagceller
- Drag-and-drop mellom datoer for å flytte hendelser
- Inline opprettelse av nye hendelser (tittel + valgfri tid)
- setDragPayload for å dra hendelser til andre paneler
- BlockReceiver: aksepterer content/communication-noder fra chat/kanban
- Responsivt med @container queries for små paneler
- Hendelsesliste under grid for oversikt over måneden

Følger samme mønster som ChatTrait (20.5) og KanbanTrait (20.6).
accessToken sendes nå til CalendarTrait fra collection-siden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:40:36 +00:00
ccc7d59b7f Gjør KanbanTrait til fullverdig BlockShell-panel med inline brett (oppgave 20.6)
KanbanTrait var tidligere bare en lenke til /board/[id]. Nå viser den
kolonner og kort direkte i BlockShell-panelet, med full funksjonalitet:

- Inline kanban-brett med kolonner (todo/in_progress/done) fra metadata
- Intern drag-and-drop mellom kolonner (status-edge oppdatering)
- Cross-panel drag: kort bruker setDragPayload for drag til andre paneler
- BlockReceiver: aksepterer drops fra chat/editor (innholdstransfer-modus)
- Opprett kort direkte i kolonne (inline input)
- Responsivt: kolonner stables vertikalt på smale paneler (@container + @media)
- accessToken-prop lagt til for mutasjoner

/board/[id]-ruten beholdes som frittstående visning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:34:43 +00:00
9484f831ce Gjør ChatTrait til fullverdig BlockShell-panel med inline chat (oppgave 20.5)
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>
2026-03-18 08:28:49 +00:00
b5f47daa63 Implementer transfer service med innholdstransfer og lettvekts-triage (oppgave 20.4)
Transfer service bestemmer overføringsmodus basert på verktøy-par:
- innholdstransfer (ny node + source_material edge) for transformasjoner
  (f.eks. chat → editor: chatboble blir kilde for ny artikkel)
- lettvekts-triage (ny edge/plassering) for konteksttillegg
  (f.eks. chat → kanban: noden vises i begge kontekster)

Shift-modifier overstyrer alltid til innholdstransfer (ny node).

Endringer:
- transfer.ts: resolveTransferMode() med verktøy-par-matrise,
  executeTransfer() som kaller API for node/edge-opprettelse
- BlockShell: sender e.shiftKey til onDrop-callback
- Workspace handlePanelDrop: kobler sammen modus-resolving og API-kall
- Docs: oppdatert universell_overfoering.md med implementasjonsstatus

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:21:35 +00:00
5b0881d5d9 Implementer BlockReceiver i alle trait-komponenter (oppgave 20.3)
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) <noreply@anthropic.com>
2026-03-18 08:12:54 +00:00
4eef7d79bb Implementer personlig arbeidsflate (oppgave 19.6)
Brukerens standard arbeidsflate med node_kind='workspace'.
Vises på /workspace når ikke koblet til en samling.

Backend:
- GET /my/workspace: finn eller opprett brukerens workspace-node
- Automatisk provisjonering ved første besøk (STDB + async PG)
- Owner-edge fra bruker til workspace

Frontend:
- /workspace rute med Canvas + BlockShell (gjenbruker spatial canvas)
- Fritt valg av verktøy-paneler fra verktøymeny
- Layout persisteres i workspace-nodens metadata via updateNode
- Tom-tilstand med verktøy-velger for nye brukere
- Responsivt: stacked tabs på mobil, spatial canvas på desktop
- Kontekst-velger i header for navigering til samlinger

Navigasjon:
- "Min flate"-knapp på mottak-siden
- "Min arbeidsflate" i ContextHeader dropdown for samlingssider

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:52:52 +00:00
4e9481edf3 Fullfører oppgave 19.5: Paneler kan minimeres til kompakt header
Dobbeltklikk på panel-header toggler minimert tilstand. Minimerte
paneler viser kun header (ikon + tittel), skjuler innhold og
resize-handles. Posisjon, bredde og full høyde bevares i layout
og gjenopprettes ved nytt dobbeltklikk. Tilstanden persisteres
i edge metadata sammen med resten av workspace-layouten.

Endringer:
- PanelLayout: nytt `minimized?`-felt
- BlockShellEvents: nytt `onMinimizeChange`-event
- BlockShell: minimized-prop, dblclick-handler, minimize-knapp,
  skjuler content/resize når minimert
- Workspace-side: håndterer minimize-state, oppdaterer canvas-
  objekthøyde til PANEL_HEADER_HEIGHT når minimert

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:43:19 +00:00
17bda8d20f Fullfører oppgave 19.4: Kontekst-header med kontekst-velger og verktøymeny
Erstatter den statiske headeren på arbeidsflaten med en interaktiv
kontekst-header som lar brukeren:

- Bytte mellom samlinger via nedtrekksmeny (kontekst-velger)
- Søke blant tilgjengelige samlinger
- Se mest brukte samlinger øverst (frekvens + recency-scoring)
- Legge til nye verktøy-paneler via verktøymenyen
- Se hvilke paneler som allerede er aktive på flaten

Nye filer:
- ContextHeader.svelte: header-komponent med kontekst-velger og verktøymeny
- workspace/recency.ts: localStorage-basert frekvens/recency-tracking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:38:15 +00:00
0caa8eb126 Fullfører oppgave 19.3: Arbeidsflaten layout med Canvas + BlockShell
Skriver om /collection/[id] fra vertikal stack til spatial workspace:

- Desktop: trait-paneler vises som BlockShell-wrappers på Canvas med
  fri pan/zoom, drag-repositionering og resize
- Mobil (<768px): tab-navigasjon med ett synlig panel om gangen
- Tre-lags layout-modell: personlig (edge metadata) > node-default
  (generert fra traits) > plattform-default (grid fallback)
- Layout persisteres debounced (1s) til brukerens owner/member_of
  edge metadata via updateEdge API
- Nye traits legges automatisk til eksisterende layout
- Fjernede traits filtreres ut ved resolving

Ny fil: frontend/src/lib/workspace/types.ts
  - PanelLayout/WorkspaceLayout typer
  - TRAIT_PANEL_INFO med default størrelse/ikon per trait
  - generateDefaultLayout(): grid-arrangement fra trait-liste
  - resolveLayout(): tre-lags merging med saved layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:31:40 +00:00
28a1bf8e89 Fullfører oppgave 19.2: BlockShell wrapper-komponent
BlockShell er panelrammen for arbeidsflaten — wrapper rundt hvert
verktøy-panel (trait). Gir konsistent header med tittel, fullskjerm-
og lukk-knapper, drag-handles for repositionering via header,
resize-handles på alle kanter og hjørner, og drop-sone med visuell
feedback (blå glow for kompatibel, rød for inkompatibel).

Responsivt: min/max constraints, mobil-tilpasning (stacked layout,
større touch-targets, ingen resize-handles). Bruker HTML5 drag-and-drop
API for overføring mellom paneler, pointer events for repositionering.

Filer:
- blockshell/types.ts — SizeConstraints, BlockReceiver, DropZoneState
- blockshell/BlockShell.svelte — selve wrapper-komponenten
- blockshell/index.ts — eksporter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:25:33 +00:00
3be2a57f88 Fullfører oppgave 19.1: Canvas-primitiv Svelte-komponent
Implementerer det felles canvas-underlaget som whiteboard, storyboard
og fremtidige canvas-views skal bruke. Ren Svelte 5 komponent uten
backend-avhengigheter.

Funksjoner:
- Pan/zoom kamera med CSS transforms (transform-origin: 0 0)
- Viewport culling med 200px margin for smooth scrolling
- Pointer events (unified mus + touch)
- Pinch-zoom og to-finger-pan for touch
- Snap-to-grid (toggle med G-tast eller toolbar)
- Fullskjermsmodus (fixed positioning)
- Lasso-seleksjon og shift+klikk multi-select
- Edge-pan ved drag nær kanter
- Responsivt: 44px touch targets på mobil, tilpasset toolbar
- zoomToFit() for å sentrere alle objekter
- Consumer-rendering via Svelte 5 snippets

Filer:
- frontend/src/lib/components/canvas/Canvas.svelte
- frontend/src/lib/components/canvas/types.ts
- frontend/src/lib/components/canvas/index.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:18:29 +00:00
8b5425fb59 Fullfører oppgave 18.6: Egendefinerte AI-presets
Brukere kan nå opprette egne AI-preset-noder med custom prompt,
dele dem med samlinger/team via shared_with-edges, og redigere/slette
egne presets. Modellprofil (flash/standard) er låst — kun admin
kan oppgradere fra flash til standard.

Backend:
- POST /intentions/create_ai_preset: Oppretter custom preset med
  tvunget category=custom og model_profile=flash. Valgfri deling
  til samling i samme kall.
- update_node: Beskytter model_profile mot endring av ikke-admin.
  Forhindrer kategori-endring fra custom til standard.

Frontend:
- AiToolPanel: "+ Nytt preset"-knapp, opprett/rediger-skjema med
  tittel, prompt, retning, ikon og farge. Rediger/slett/del-knapper
  for egne custom presets. Egendefinerte presets markert med "egn."
- createAiPreset() i api.ts.

Dokumentasjon:
- ai_verktoy.md: Oppdatert fasestatus, ny § 10 om egendefinerte
  presets med API-eksempler og modellprofil-beskyttelse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:11:34 +00:00
35d39962dd Fullfører oppgave 18.5: Drag-and-drop integrasjon mellom verktøy
Implementerer toveis drag-and-drop mellom AI-verktøypanelet og
innholdspaneler, med visuell feedback og kompatibilitetssjekking.

Nye filer:
- transfer.ts: Sentralisert transfer-tjeneste med kompatibilitetsmatrise,
  DragPayload-format (application/x-synops-transfer), og
  inkompatibilitets-meldinger for lyd/bilde-noder.

Endringer:
- AiToolPanel: Drop-sone bruker presetets farge (dynamisk border/bg),
  viser rød sone med forklaring ved inkompatible noder (lyd/bilde),
  presets er draggable ut (for tool_to_node retning).
- EditorTrait: Aksepterer AI-preset drops på innholdsnoder (tool_to_node),
  viser visuell feedback (lilla=kompatibel, rød=inkompatibel),
  trigger in-place revisjon via aiProcess API.
- ChatTrait: Kommunikasjonsnoder er nå draggable til AI-verktøyet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:58:15 +00:00
d4de8b3619 Fullfører oppgave 18.4: AI-verktøy panel (frontend)
Svelte-komponent for AI-prosessering i arbeidsflaten:
- AiToolPanel.svelte: Viser ai_preset-noder fra STDB som velgbare
  verktøy, med modell-indikator (flash/standard) og prompt-forhåndsvisning
- Drag-and-drop mottak for tekstnoder med visuell feedback
- Validerer kompatibilitet (kun content/communication-noder)
- Kaller /intentions/ai_process via ny aiProcess()-funksjon i api.ts
- Integrert i collection-siden, tilgjengelig for alle innloggede brukere
- EditorTrait: innholdsnoder er nå draggable for AI-prosessering
- Fritekst-felt for egendefinert instruksjon (backend-støtte kommer i 18.6)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:50:29 +00:00
a3cdfa9dc2 Fullfører oppgave 17.7: FFmpeg feilmeldinger til bruker
Tre endringer som sammen gir brukeren innsyn i FFmpeg-feil:

1. Backend: Nytt GET /query/job_status-endepunkt i queries.rs.
   Frontenden pollet allerede denne URLen, men endepunktet manglet.
   Returnerer status, result og error_msg fra job_queue.

2. RenderDialog: Ny error-tilstand med formatFfmpegError() som
   trekker ut lesbar feilmelding fra FFmpeg stderr-dump. Viser
   kort oppsummering + ekspanderbar full feilmelding via <details>.

3. Studio-side: Sender renderError til RenderDialog som errorMessage.
   Toast-varselet vises kun når dialogen er lukket (unngår duplisering).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:03:59 +00:00
6f50f66431 Fullfører oppgave 17.5: Job-polling opprydding i lydstudioet
Tre forbedringer i studio-siden:

1. Interval/timeout-opprydding ved navigering: Polling-interval og
   timeout lagres i komponent-variabler og ryddes opp via $effect
   cleanup når komponenten demonteres. Forhindrer memory leaks og
   ghost-requests etter navigering bort fra studio-siden.

2. Feilmelding etter N mislykkede polling-forsøk: Etter 5 feilede
   statussjekker (nettverksfeil eller HTTP-feil) vises en
   feilmelding til brukeren i stedet for stille ignorering.
   Timeout-feil og jobb-feil vises også i UI.

3. Metadata JSON.parse i try/catch: Hindrer at ugyldig metadata-JSON
   krasjer hele studio-siden. Logger feilen og returnerer null.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 05:53:28 +00:00
6e04f3c013 Fullfører oppgave 17.4: Frontend input-begrensninger
Legger til min/max-attributter på alle tallfelter i OperationPanel
slik at nettleseren hindrer ugyldig input før det sendes til backend.
Grensene matcher backend-valideringen i audio.rs:

- silenceThreshold: -96 til 0 dB
- silenceMinMs: 1 til 60000 ms
- normTarget: -70 til 0 LUFS
- fadeInMs/fadeOutMs: 1 til 300000 ms
- compThreshold: -60 til 0 dB
- compRatio: 1 til 20 (max lagt til, min fantes allerede)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 05:49:42 +00:00
9f66f114ff Fullfører oppgave 17.1: Responsivt studio-layout
Lydstudioet var kun desktop-optimalisert med fast sidebar (w-72).
Nå responsivt med to moduser:

- Desktop (lg+): Sidebar med verktøypanel til høyre, som før
- Mobil/tablet (< lg): Waveform fyller full bredde, verktøypanel
  tilgjengelig via flytende knapp som åpner bottom sheet (modal).
  Operation-badge på knappen viser antall aktive operasjoner.

Header: Kompaktere padding på mobil, tittel truncates, audio-info
skjules på små skjermer. min-w-0 på hovedområdet hindrer overflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 05:39:19 +00:00
ac8f8c508d Fullfører oppgave 16.7: Stemmeeffekter med robot og monster voice
Robotstemme: Ring-modulasjon via OscillatorNode som modulerer
GainNode.gain — gir metallisk, Dalek-aktig effekt. Justerbar
frekvens (30–300 Hz) og modulasjonsdybde (0–100%).

Monsterstemme: Egenutviklet AudioWorkletProcessor med phase vocoder
for sanntids pitch-shifting. Bruker overlap-add med 2048-sample FFT
og 4x overlap for ~42ms latens ved 48kHz. Pitch-faktor 0.5x–2.0x.

UI: Effektvelger-knapper (Robot/Monster) i FX-seksjon per kanal,
med fargekodede parametersliders som vises når effekten er aktiv.
On/off-state synkroniseres via STDB toggle_effect, parametere er
per-klient (ulike brukere kan ha forskjellige monitorinnstillinger).

STDB: Lagt til set_effect_param reducer for fremtidig param-synk
(krever spacetime CLI for publish — ikke deployet ennå).

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