Neue Drag-Handles zwischen Sidebar/Liste und Liste/Lesebereich (nur Desktop): Spaltenbreite durch Ziehen verstellen, Doppelklick setzt auf Standard zurueck
Breiten werden in localStorage gespeichert und beim Laden wiederhergestellt (mail.sidebarWidth / mail.listWidth), mit Min/Max-Grenzen geclampt
Pointer Capture haelt den Drag auch ueber dem Mail-iframe und ausserhalb des Fensters stabil
Zusammenspiel mit dem Einklappen unveraendert: eingeklappte Spalte blendet ihren Handle aus
v3.3.0
Mail-Anhaenge per Drag-und-Drop in lokale Ordner ziehen
Anhang-Chips in der Leseansicht sind jetzt draggable: beim Ziehen aus dem Browser in den Explorer/auf den Desktop speichert Chrome die Datei direkt am Ablageort (DataTransfer "DownloadURL", Chromium-Browser)
Klick zum Herunterladen funktioniert unveraendert; Tooltip erklaert beide Wege
v3.2.0
IMAP-Pool wartet bei Auslastung statt Verbindungs-Burst zu erzeugen
Harte Obergrenze von 3 Verbindungen pro Konto: Ist der Pool belegt, warten Anfragen (max. 30 s) auf einen freien Slot, statt zusaetzliche Verbindungen zu oeffnen - Mailserver drosseln Verbindungs-Bursts pro Nutzer/IP (Dovecot-Limit, fail2ban), was nach dem 3.1.0-Deploy beim ersten Mail-Oeffnen zu haengenden Connects und nginx-504 fuehrte
imapflow-Timeouts gesetzt (connect 20 s, Greeting 10 s): gedrosselte Connects enden als schnelle JSON-Fehlermeldung statt im Proxy-Timeout
Verbindungspool: pro Konto bleiben bis zu 3 IMAP-Verbindungen warm (4 min Idle-Timeout), statt pro Klick TCP+TLS+LOGIN neu zu bezahlen - Ordnerwechsel, Mail oeffnen, Flags, Suche reagieren deutlich schneller
DNS ipv4first (instrumentation.ts): behebt intermittierende Haenger gegen mail.your-server.de (Container ohne IPv6-Route, AAAA-Record)
Echte Pagination: "Mehr laden" holt nur die naechsten 50 Mails (Cursor) statt die ganze Liste neu; Sammelansicht mit per-Konto- Cursorn (k-way merge) statt wachsendem Limit
Thread-Ansicht: Kandidaten-Ordner werden parallel ueber den Pool geladen statt sequenziell
Suche komplett neu: durchsucht alle Konten und alle Ordner (ausser Junk/Papierkorb) parallel, auch aus der Sammelansicht; mehrere Woerter werden UND-verknuepft (Betreff/Absender/Empfaenger je Wort); Inhaltssuche optional per Checkbox (auf Servern ohne Volltext-Index langsam); Treffer zeigen Konto + Ordner und oeffnen die Mail direkt
Alle Mail-Routen liefern bei IMAP-Fehlern jetzt JSON-Fehler mit verstaendlicher Meldung statt leerer 500er
v3.0.0
Link-Ziel beim Mouseover in der Mail anzeigen (Status-Leiste)
Beim Ueberfahren eines Links in der Mail erscheint unten links eine browserartige Status-Leiste mit der echten Ziel-URL - schuetzt vor Links, deren Text eine andere Adresse vortaeuscht
Umsetzung ohne Skripte in der Mail: die App lauscht per Event-Delegation auf dem same-origin iframe-Dokument; die Leiste liegt ausserhalb des iframes und bleibt daher auch bei gezoomten Mails lesbar
v2.9.0
Linke Spalten einklappbar, Mail-Inhalt skaliert auf Passbreite
Seitenleiste und Nachrichtenliste lassen sich am Desktop einzeln zu einer schmalen Leiste einklappen (Zustand wird in localStorage gemerkt)
Mail-HTML mit fester Ueberbreite (starre Newsletter-Tabellen) wird im Lesebereich automatisch auf die Fensterbreite gezoomt - kein horizontales Scrollen mehr, besonders auf dem Handy; bei Rotation wird neu angepasst
v2.8.0
Umschalter-Darstellung, sichtbare Empfaenger im Kopf, Farbe je Postfach
Umschalter (Switch) mit fester Geometrie neu gebaut; kaputte Toggles in Einstellungen (Unterhaltungen, Vorlesen) behoben
Empfaenger/Kopie/Blindkopie/Antwort-an werden beim Oeffnen einer Mail jetzt immer im Kopf angezeigt (nicht mehr eingeklappt)
Farbe je Postfach in den Einstellungen festlegbar (Sammelansichten), sonst deterministischer Standard
v2.7.0
Konversationsansicht, Sammelordner, Compose im rechten Bereich, Marken-Theme
Konversationsansicht (ordneruebergreifendes Threading) mit Umschalter in den Einstellungen
Sammelansichten fuer alle Ordner: Gesendet, Entwuerfe, Archiv, Papierkorb, Spam
Verschieben-Dropdown funktioniert jetzt auch im Sammelmodus (Ordner des jeweiligen Kontos)
Neue Mail und Antworten im rechten Bereich statt als Popup (mobil Vollbild)
Anhaenge per Drag and Drop
Infinite-Scroll ohne Sprung nach oben (keepPreviousData + IntersectionObserver)
Marken-Theme Blau/Orange (hell und dunkel) + Akzent-Auswahl in den Einstellungen
mailto oeffnet neue Mail in der laufenden App (Chromium Launch Handler)
Groessere Aktions-Icons
v2.6.0
Android-Zurueck navigiert in der App statt die PWA zu schliessen
Im Standalone-PWA-Modus poppte der Zurueck-Button die Browser-History und verliess die App, weil Nachricht/Compose/Suche/Drawer nur ueber State geoeffnet werden (kein History-Eintrag). Jetzt haelt useOverlayBack pro offenem Overlay einen History-Guard: ein echter Zurueck-Druck schliesst das oberste Overlay (Prioritaet: Compose > Suche > Drawer > offene Nachricht); Schliessen per UI entfernt den passenden Guard.
v2.5.0
Mail-Ansicht auf Handy — volle Breite, kein Times, kein Querscrollen
E-Mail-HTML wird in ein echtes Dokument mit Viewport-Meta und Basis- Styles gehuellt: moderne Sans-Serif statt Times, Bilder/Tabellen fluid (max-width:100%), Wortumbruch — kein horizontales Scrollen mehr. Inline-Styles designter Mails gewinnen weiterhin (Spezifitaet).
Mail-Inhalt auf Mobile randlos und in voller Breite (Rahmen/Radius nur ab md), Klartext-Mails brechen sauber um.
Aktionsleiste im Mailkopf: Betreff oben, Buttons darunter in voller Breite gleichmaessig verteilt, groessere Touch-Targets, kompaktere Kopfzeile auf Mobile.
v2.4.0
Touch-/Responsive-Refactoring mit Bottom-Tab-Bar
Macht die App grundlegend touchfreundlich und responsive, ohne die schlanke Desktop-UI aufzublaehen (Touch-Vergroesserungen nur via pointer-coarse:-Variante).
Bottom-Tab-Bar als Mobile-Navigation (Posteingang/Ordner/Neue Mail/ Suche/Mehr); oberer Hamburger entfaellt
Primitives (Button/Input/Editor) mit 44px-Touch-Targets + :active-Feedback
Hover-only-Aktionen (Auswahl-Checkbox, Zeilenaktionen, Ordner-Loeschen) jetzt auf Touch dauerhaft sichtbar
Compose und Suche als Vollbild-Sheet auf Mobile, Karte ab sm
Reading-Pane-Kopf entlastet: Sekundaeraktionen im Mehr-Menue auf Mobile
Safe-Area-Insets fuer Bottom-Bar und Compose-Footer
v2.3.0
mailto-Protokoll-Handler — PWA oeffnet neue Mail aus mailto-Links
Manifest registriert die installierte PWA als mailto-Handler (protocol_handlers, /?mailto=%s)
parseMailto() dekodiert die mailto-URI (to/cc/bcc/subject/body), mit Vitest-Tests
Startseite liest den mailto-Parameter beim ersten Render, oeffnet vorausgefuelltes Compose-Fenster und raeumt die URL auf
v2.2.0
Upload-Fortschritt in der Fusszeile + ehrliche 413-Meldung
Fortschritt jetzt ohne Scrollen sichtbar: Prozent + Spinner direkt neben dem Klammer-Symbol, duenner Balken an der Oberkante der Leiste.
413 vom Proxy nennt keine App-Limits mehr (nginx kappt aktuell frueher); App-eigene Limit-Fehler nennen dafuer ihre Grenzen (10/25 MB).
Fix: FileList-Snapshot in addFiles (Live-Liste wurde durch value-Reset geleert, Dateien gingen timing-abhaengig verloren).
v2.0.0
Absender im Compose-Fenster auswaehlbar (From-Adresse + Signatur)
Beim Schreiben einer neuen Mail war der Absender fest auf das beim Oeffnen aktive Konto gesetzt. Wer mehrere Konten hat, musste vorher in der Sidebar das richtige Konto waehlen.
ComposeDialog bekommt die Kontenliste und einen "Von"-Dropdown (nur sichtbar bei mehr als einem Konto); Auswahl steuert die gesendete accountId. Kein Backend-Change noetig — send/route.ts baut den From-Header bereits aus dem Konto.
Beim Absenderwechsel wird die Signatur mitgewechselt: der zuletzt eingefuegte Signaturblock wird im Editor durch den des neuen Kontos ersetzt. Nur bei neuer Mail, nicht bei Antwort/Weiterleitung/Entwurf.
v1.9.0
klare Fehlermeldungen + SMTP-Test beim Anlegen neuer Konten
Beim Anlegen eines Postfachs zeigte jeder Verbindungsfehler nur "Fehler: Command failed" — imapflow wirft einen generischen Error, dessen echte Ursache in err.responseText/err.code steckt.
neuer Helper describeMailError() uebersetzt imapflow-/nodemailer-Fehler in klare deutsche Meldungen (falsches Passwort, Host/Port, Timeout, TLS)
POST /api/accounts gibt detail jetzt ueber describeMailError() zurueck
zusaetzlich SMTP-Verbindung via verifySmtp() vor dem Speichern pruefen, damit SMTP-Fehler nicht erst beim ersten Sendeversuch auffallen
v1.8.0
Versionsschema (1 Commit = 0.1) + Changelog-Generator, Version 1.7.0
Generator (scripts/gen-changelog.mjs) vergibt jetzt eine Version pro Commit (+0.1, Initial = v0.1.0, Merges ausgenommen) statt nach Git-Tags zu gruppieren. pnpm changelog schreibt nun beide Quellen aus derselben Historie: CHANGELOG.md und die In-App-Liste src/lib/changelog.ts (vorher handgepflegt und veraltet), damit sie nicht mehr auseinanderdriften. package.json auf 1.7.0 gesetzt (Stand des neuesten Feature-Commits Vorlesefunktion), wird in der App-Spalte angezeigt.
v1.7.0
Vorlesefunktion (Text-to-Speech) fuer neue Mails und geoeffnete Mail
Liest neu eintreffende Mails automatisch vor (Absender + Betreff) und bietet im Lesebereich einen Vorlesen-Knopf fuer die geoeffnete Mail. Konfigurierbar in den Einstellungen: An/Aus, Stimmenauswahl (Deutsch bevorzugt), Test-Knopf.
Basiert auf der Web Speech API (window.speechSynthesis) - rein clientseitig, keine Server-/DB-Aenderung. Einstellungen liegen in localStorage (mail.readAloud.*).
v1.6.0
remember selected account/view across browser reloads
selectedAccountId was React-only state (initial null), so a reload always fell back to the first account — "Alle Konten" never stuck. Persist the choice in localStorage (like the theme) and restore it via a lazy useState initializer. A stored account that no longer exists is ignored once accounts load, falling back to the first account.
v1.5.0
Sammelposteingang (unified inbox across all accounts)
Adds an opt-in "Alle Konten" view that merges every account's INBOX into one date-sorted list.
New /api/mail/messages-unified route fetches all accounts' INBOX in parallel (Promise.allSettled), merges/sorts by date, isolates per-account failures into errors[] so one dead mailbox never blanks the view.
Message identity is now composite (accountId:uid) so the unified view survives IMAP uid collisions across accounts. Action handlers take a per-message accountId; single-account callers fall back to the page account (behavior unchanged). Optimistic updates patch both the per-account and unified caches (matched on accountId AND uid).
Bulk actions group the selection per account → one request each.
List shows a per-account colour dot + label badge and a banner for failed accounts. Reply/compose derive the owning account from the open message (fixes from-address in unified mode). Folder sidebar, search and move-to-folder are hidden in unified mode; SSE opens one stream per account.
v1.4.0
use next/link instead of <a> on offline page
Resolves the no-html-link-for-pages lint error breaking CI.
v1.3.0
open email links in a new tab instead of inside the message frame
Inbound email anchors had no target, so clicking a link navigated the sandboxed iframe itself and replaced the message content. Sanitizer now adds target=_blank + rel=noopener, and the iframe sandbox allows popups to escape so links open as a normal new tab.
v1.2.0
Auto-Deploy zu Dokku aktualisieren
v1.1.0
PWA (installable + offline shell) + responsive mobile layout
PWA: web manifest (src/app/manifest.ts), icons (src/app/icon.svg + public/icons/*.png generated from the logo SVG), viewport/theme-color + apple-web-app metadata. Privacy-safe service worker (public/sw.js): caches only the static app shell, NEVER /api/* or mail (keeps the live-only model); navigations network-first with an /offline fallback. SW registered in production only; new src/app/offline/page.tsx.
Responsive: sidebar becomes a slide-in drawer on mobile (hamburger + backdrop), static on desktop; message list and reading pane become a master-detail below md (list → full-screen message with a back button) and stay side-by-side on desktop. (app)/layout.tsx uses h-dvh; reading-pane toolbar wraps.
AI/Ollama: per-request timeout + per-chunk inactivity guard, friendly German error mapping (no more raw "fetch failed"), GET /api/ollama/health, shared model allowlist module.
Accounts: edit/delete via /api/accounts/[id]; per-account HTML+text signatures (sanitized at write time), inserted client-side in the composer.
Microsoft 365: OAuth2/XOAUTH2 (Authorization Code + PKCE) for Exchange Online IMAP/SMTP; connectImap + SMTP sender branch on authType (basic | oauth_ms).
Compose: rich-text HTML editor, signature insertion, BCC, attachment upload (multipart), drafts save/resume (IMAP APPEND to Drafts); HTML reply quoting.
app versioning + in-app changelog from git history
Version is sourced from package.json (now 0.2.0) and inlined into the bundle via next.config (NEXT_PUBLIC_APP_VERSION); exposed as APP_VERSION in src/lib/version.ts.
CHANGELOG.md is generated from the commit descriptions, grouped by git tag, via scripts/gen-changelog.mjs (`pnpm changelog`). Tagged v0.1.0 at the pre-rework commit so the 0.1.0 / 0.2.0 split is accurate.
New public /changelog page renders the curated release history (src/lib/changelog.ts) and links to the full CHANGELOG.md on GitHub.
The version number is shown as a link to /changelog in the mail sidebar footer and on the login screen.
typecheck/lint/test/build all green; /changelog prerenders with the version.
v0.8.0
stop tracking local .claude harness artifacts
v0.7.0
align documentation with the live-only rework
Findings from a multi-agent doc audit (README/.env/comments vs. code):
README: DB service is `maildb` (not `postgres`) in the stack line and architecture diagram; add the rationale note.
README: add a compact internal API route table (maps features to method+path), documenting the messages pagination params (limit max 200, before = count of newest messages to skip) and the attachment dl=1 flag.
README: clarify the remote-image toggle (client-side, per message), the AI-output cache semantics, the /api/ollama/pull endpoint, the /api/health endpoint, and the db:generate script.
.env.example: document the bundled-Postgres POSTGRES_* vars.
Dockerfile: drop stale comments referencing a removed `worker` service.
drizzle.config.ts: drop stale pgvector comment.
ollama/pull allowlist: drop the removed embedding model (nomic-embed-text), add the default chat model (llama3.1) — chat-only, matches the feature set.
No behavior change beyond the pull allowlist; typecheck/lint/test/build green.
v0.6.0
Rework to live-only IMAP client (no mail persistence)
Per the owner's requirement, emails must never be downloaded or stored locally - they stay on the IMAP server. This inverts the previous DB-first architecture.
Removed:
Mail persistence layer (sync, persist, messages/folders/threads/attachments tables) and the pg-boss worker + job queue
Tooling: Vitest unit tests (reply, sanitize, crypto), GitHub Actions CI. All green: typecheck, lint, test, build.
v0.5.0
Add auth-gated endpoint to pull small Ollama models
POST /api/ollama/pull streams progress while pulling an allowlisted model (llama3.2:1b/3b, qwen2.5:1.5b/3b, nomic-embed-text) onto the Ollama server. Lets the app provision a CPU-friendly model without server CLI access.
v0.4.0
Make AI lightweight for CPU-only hosts (Hetzner CAX21)
Disable background AI jobs by default (ENABLE_AI_BACKGROUND, default false): the worker no longer runs an LLM inference per synced message, which was saturating the 4-core ARM CPU (categorize on qwen3 also looped via retries).
Disable model "thinking" (think:false) for chat/summarize: qwen3 reasoning multiplies CPU latency and breaks format:json.
Lower job retryLimit 5->1 and concurrency 2->1 so a failing job can't loop. Summarize and search remain available on-demand.
v0.3.0
Rename DB service postgres -> maildb to avoid network name collision
With Coolify's predefined-network connection (required to reach Ollama), a generic "postgres" service name collides with other stacks' postgres on the shared coolify network; DNS resolved ambiguously, causing intermittent "password authentication failed". A unique service name fixes it.
v0.2.0
Fix Docker build for Coolify deploy
Pin pnpm@9.15.9 (packageManager + corepack prepare): corepack otherwise pulls pnpm v11 which needs Node >=22.13 and fails on node:20-alpine.
Add placeholder env in the build stage so `next build` can import env-validating modules (Better Auth, db); real values come at runtime.
v0.1.0
Initial commit: AI mail client (Phases 1-4)
Modern self-hosted webmail for Coolify with local Ollama: