- ContextHeader brukes nå på både personlig flate og samlinger
- Ny theme.ts: ThemeConfig med hue+saturation per farge, presets
(Standard, Hav, Skog, Solnedgang, Lavendel, Monokrom)
- Fargevelger med hue-stripe (regnbue) + saturation-slider + swatch
per farge (bakgrunn, overflate, aksent)
- Slett arbeidsflate med bekreftelsesdialog og innholdstelling
- Kontekst-velger: grupper, rename, ny, slett — fungerer overalt
- +page.svelte forenklet: ~500 linjer CSS fjernet, bruker ContextHeader
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Lasso-seleksjon → dra flytter alle valgte paneler sammen
- Grid on/off lagres i workspace-metadata (huskes mellom besøk)
- Zoom lagres allerede via kameraposisjon (x, y, zoom)
- ZOOM_MIN senket til 5% for spredte layouts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Klikk-hold-dra på tom bakgrunn = pan (erstatter midtre museknapp)
- Shift+dra = lasso-seleksjon (som før)
- Kameraposisjon (x, y, zoom) lagres i workspace-metadata og
gjenopprettes ved neste besøk
- Senket ZOOM_MIN til 5% for å tillate zoom ut på store canvas
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Toolbar-knapper (zoom, tilpass, grid, fullskjerm) blokkert av Canvas
sin pointerdown-handler. Fikset med closest('.canvas-toolbar')-sjekk.
- Dobbeltklikk på tom flate kjører zoomToFit() for rask navigasjon.
- Seleksjon-ramme dempet: 1px rgba(accent, 0.4) i stedet for 2px solid.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Forrige versjon sendte total offset fra startposisjon på hvert
pointermove-event, som ble addert gjentatte ganger og akselererte
panelet ut av bildet. Nå beregnes delta fra forrige frame.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Når man drar toppen eller venstre kant av et panel, ble bare størrelsen
endret mens posisjon sto fast — som ga inntrykk av at feil kant beveget
seg. Nå beregner BlockShell posisjons-delta (dx, dy) og parent justerer
x/y tilsvarende, slik at den kanten du drar i følger musepekeren.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resize-handles ble aldri vist fordi isMobile var true for alle paneler
(MOBILE_BREAKPOINT=768 > typisk panelbredde 400-600px). Fjernet
isMobile-sjekken for resize — handles vises nå alltid unntatt i
fullskjerm og minimert modus.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resize-handlene i BlockShell bruker negative offsets (-3px) som stikker
utenfor canvas-object divens bounds. Uten overflow: visible fanges ikke
pointer events på kantene.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
overflow: hidden på .blockshell klippet resize-handles (negative offsets).
Endret til overflow: visible, border-radius på header/content i stedet.
Lagt til subtile visulle indikatorer ved hover: L-merker på hjørner,
tynne linjer på kanter. Hardkodede farger → CSS custom properties.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Erstattet hardkodede hex-farger med var(--color-*) i alle style-blokker
slik at tema-sliderne faktisk endrer bakgrunn, overflater, border og aksent
- Ny arbeidsflate opprettes med chat/editor/kanban/kalender som default
traits i stedet for tom samling uten traits
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AI-verktøy og Ressursforbruk registrert som BlockShell-paneler
i verktøymenyen (🤖 og 📊)
- Innstillingsmeny (⚙️) lengst til høyre i header: tre hue-slidere
(bakgrunn, overflate, aksent) + logg ut. Lagres i workspace-metadata.
- Kontekst-velger: to grupper (Mine flater / Delte flater),
inline rename (✏️), "+ Ny arbeidsflate"-knapp
- Mørke overrides for manglende Tailwind bg-farger i app.css
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- «Mottak» → «Arbeidsflaten» i alle tilbake-lenker
- goto('/workspace') → goto('/') i ContextHeader
- Slettet NodeEditor.svelte og NewChatDialog.svelte (kun brukt av mottak)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Flyttet workspace-innholdet til rotpagen. Mottak er utdatert —
alt bor i arbeidsflaten som paneler. Fjernet /workspace-rute
og tilbake-pilen i headeren.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hvert subdomain har én jobb. Offentlig og internt er separate domener.
- synops.no = statisk landingsside + /pub/* + /media/*
- ws.synops.no = appen (SvelteKit + /api/* → maskinrommet)
- workspace.synops.no → redirect til ws.synops.no (legacy)
- Fjernet hostname-sjekker fra hooks.server.ts
- Fjernet LandingPage.svelte (landingsside er statisk HTML)
- Alle API-URLer peker til 127.0.0.1:3100 (ikke sidelinja.org)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Host-basert logikk i hooks: workspace.* sender uautentiserte til
login og autentiserte til /workspace. synops.no viser landingsside.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auth-cookies settes på .synops.no slik at de deles mellom
synops.no, workspace.synops.no, og fremtidige subdomener.
Caddy proxyer workspace.synops.no til SvelteKit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uautentiserte ser landingssiden, innloggede sendes rett til
arbeidsflaten. Mottak er tilgjengelig som eget panel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LandingPage.svelte med all CSS og HTML innbakt. Ingen iframe,
ingen ekstern fil-avhengighet. Fungerer i alle nettlesere.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Unngår å duplisere landingssiden i SvelteKit — iframe loader
den statiske filen fra Caddy /landing-ruten.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
hooks.server.ts: / unntatt fra auth-redirect
+page.svelte: viser landingsside for uautentiserte, mottak for innloggede
Ingen separat statisk fil — alt i SvelteKit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Footer-feltet fjernet fra workspace og collection-sider.
AI-verktøy og ressursforbruk blir paneler i canvas (fremtidig).
Canvas får full høyde.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Canvas sin CSS transform bryter position:fixed (kjent CSS-gotcha).
Fullskjerm flytter nå DOM-elementet til body, og tilbake ved exit.
z-index 9999 og bakgrunnsfarge #0a0a0b.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Minimer, maksimer og lukk-knapper ble fanget av header-dragging
pga setPointerCapture. Nå: stopPropagation på controls-div og
individuelle knapper, pluss .closest('.blockshell-controls') sjekk.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
WorkspaceRow.metadata var String, men PG-kolonnen er JSONB.
Første opprettelse fungerte (inserter Value), men oppslag
feilet ved andre besøk.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vaktmesteren kan nå sende epost-varsler og WebSocket-push til brukere
via synops-notify, med respekt for brukerens preferanser.
Endringer:
- jobs.rs: send_notification jobbtype som delegerer til synops-notify CLI
- synops-notify: preferansesjekk fra metadata.preferences.notifications
(opt-out-modell, per-kanal og per-type bryter, --skip-preferences)
- intentions.rs: POST /intentions/send_notification (admin-only)
- Dokumentasjon: docs/features/varsler.md
Preferanseskjema (i brukernodens metadata):
preferences.notifications.email: bool (global epost-bryter)
preferences.notifications.ws: bool (global WS-bryter)
preferences.notifications.<type>: bool (per-type, f.eks. task_assigned)
- Fjernet "Kjære vaktmester"-krav: avsender-verifisering via
auth_identities.email er tilstrekkelig spam-filter
- Domene-alias: mottaker-username oppslås i auth_identities
uavhengig av domene. vegard@synops.no, vegard@sidelinja.org,
vegard@vegard.info ruter til samme bruker
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementert fullstendig epost-mottak pipeline:
- Parser raw RFC 5322 epost fra stdin via mailparse
- Sjekk 1: Envelope-sender matches auth_identities.email (case-insensitive)
- Sjekk 2: Body starter med konfigurerbar aktiveringsfrase (default: "Kjære vaktmester")
- Begge sjekker må bestå — ellers forkastes eposten stille (exit 0, ingen bounce)
- Ved match: oppretter content-node med visibility=hidden, created_by=bruker
- Metadata lagrer source=email, from, to, subject for sporbarhet
- UTF-8 håndtering: prøver raw bytes som UTF-8 først, faller tilbake til mailparse charset
- Aktiveringsfrase konfigurerbar via --phrase eller SYNOPS_MAIL_PHRASE env
Postfix installert og konfigurert som lokal MTA kun for epost-mottak.
Ingen relay, ingen utgående kø — utgående bruker msmtp/Brevo som før.
Konfigurasjon:
- virtual_mailbox_domains: synops.no, sidelinja.org, vegard.info
- Catch-all: alle adresser under domenene aksepteres
- virtual_transport → synops-pipe: pipe(8) leverer til synops-mail
- default_transport = error: blokkerer utgående SMTP
- synops-mail --receive stub: leser stdin, logger, exit 0
Verifisert: lokal SMTP-test viser at epost aksepteres, pipes til
synops-mail, og logges korrekt i /var/log/mail.log.
Port 25/tcp åpnet i UFW for innkommende SMTP (forutsetning for
epost-mottak). Dokumentert nøyaktige MX/A/SPF-records som trengs
i docs/setup/produksjon.md.
Selve DNS-endringene må gjøres manuelt i Hetzner DNS Console
(dns.hetzner.com) — Claude har ikke browser eller API-token.
Detaljerte instruksjoner i tasks.md.
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).
- Inkluder samlinger med rss-trait (ikke bare podcast-trait) i dropdown
- Fiks slug-lesing fra traits.publishing.slug
- Installer synops-import-podcast til /usr/local/bin
- Marker oppgave 30.7 som ferdig i tasks.md