- Per-user routing (own mail / own Discord webhook) for task alarms
(sent at once), shares, completed shared tasks and watched tasks;
settings as JSON on the user (GET /auth/me, PATCH /auth/settings).
- Everything but alarms is queued and sent as one message per user
after NOTIFY_DIGEST_MINUTES (default 5); mails now multipart with an
HTML version, Discord as one embed. Three failed attempts drop it.
- Own changes never notify the actor, except on watched tasks when
asked for; "paused" stops everything including alarms.
- Task watchers: POST/DELETE /tasks/{id}/watch, isWatching on tasks.
- Daily summary at a chosen time: overdue, today, tomorrow, plus the
count of the next 7 days; nothing due, nothing sent.
- Migration 7; jobs do not run in the demo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server only creates the new note and returns the link; the editor
swaps the branch for it as an ordinary own edit through the sync engine.
No more clash with unsent typing (conflict copies), the link shows at
once also with polling, and Strg+Z restores the branch. Undoing (toast or
Strg+Z) puts the new note into the trash via POST /extract/undo. The
sidebar gets the new note right away and opens its folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Extract: button on the bullet (desktop) or /extract (/notiz). The bullet
becomes the title (hashtags stay at the link), its children the content
of a new note in the same folder with the source's shares (a foreign
source owner gets write access); the branch becomes [[title]] through
the operation log, so open editors follow.
- Merge: drop a note onto another in the sidebar or use "Mit anderer
Notiz zusammenführen…" in the note header. The own source is appended
as an h2 section with its bullets below and goes to the trash; the toast
offers a complete undo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GET /link-preview?url= fetches the page on the server (https only,
SSRF-guarded like webhooks), reads Open Graph/Twitter tags, title,
description and icon from the head, loads picture and icon (no SVG) and
keeps them in link_previews (migration 6) for 7 days; readers' browsers
never contact the page. Off in the demo; 300 fetches per address in
15 minutes.
- Compact card above the bullet (icon, site, title, one line, small
picture); the duplicate link badge is left out.
- ✕ turns the address into [title](url), so hiding syncs with the note;
"Link-Vorschauen anzeigen" under appearance switches all off per device.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Almost no side margin, 14 px indent per level (22 px from tablet width
on), zoom and delete buttons only take width in the active row. Very deep
rows keep about 14rem and the note scrolls sideways instead of squeezing
the text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bare SMTP_FROM address gets the display name in the From header; a name
already in SMTP_FROM wins, the envelope keeps the bare address. Also a
field in the admin mail settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/image replaces /bild (aliases bild/foto keep working), shorter
descriptions for /link, /image, /audio, /diktat; the menu is wider, the
command never wraps and long descriptions are cut off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Backup ZIP (download and automatic per-user server backups) carries the
files of the user's own notes as media/<id><ext>; restoring creates
them with new IDs and rewrites the blocks.
- Markdown archive puts them under attachments/ and embeds them relative
to each note (); re-importing turns them
back into image/audio blocks.
- Obsidian import takes images and audio a note embeds on its own
(![[file.png]], ); unused or invalid files appear as
skipped in the preview instead of vanishing.
- Format detection and file paths moved into storage for this.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/diktat (only in an empty bullet, only with voice input available)
listens right away; the bullet becomes one button with the live wave
across it and a translucent "Stopp" on top. A tap ends it and the
recognised text lands in the bullet as plain text. Uses the configured
speech recognition (own Whisper server or the browser); Esc cancels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /audio (only in an empty bullet) shows a recording bubble: green round
button starts, it turns red with a live wave and timer on its left, a
tap saves the recording as an inline audio player. An audio file can be
uploaded instead; Esc/✕ restores the empty bullet.
- Chrome's WebM recordings get their length measured, so the player shows
the total time and seeking works.
- With the own Whisper server "Transkribieren" turns the recording into
the (searchable) caption.
- Loaded files keep their Blob, as the CSP rightly blocks fetch() on blob:
URLs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /bild (only in an empty bullet) turns the bullet into an inline picker:
file, camera on phones, drag & drop, paste, or an https link the server
copies. Esc/✕ restores the empty bullet; nothing is synced before the
upload succeeded.
- Photos are shrunk to 2048 px and stored as WebP (JPEG fallback); the
re-encoding drops EXIF data such as the location.
- Image block: loaded with the login as blob URL, click opens full
screen, optional caption kept in the block content
().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Table media (migration 5), files in $DATA_DIR/media/<docId>/.
- POST /documents/{id}/media (write access), POST .../media/fetch copies an
external https image (SSRF-guarded, not in the demo), GET /media/{id}
with range requests for readers of a note that shows the file.
- Format detected from the content (no SVG); own sandbox CSP on answers.
- Blocks point to files through their content (media:<id>), so the note
sync needs no change; compaction removes files no block mentions any
more after a day. Demo reset and demo start clear the folder.
- Limits MEDIA_MAX_IMAGE_MB, MEDIA_MAX_AUDIO_MB, MEDIA_QUOTA_MB,
MEDIA_DEMO_MAX_MB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docker-compose.yml only maps variables; every variable is explained in
.env.example (German, with defaults, units and choices).
- The whisper service is a commented block to switch on instead of a
compose profile.
- TZ, TRUSTED_PROXIES, LOGIN_PROTECTION, DEMO_RESET_MINUTES and
LEGAL_*_CONTENT are now passed through (they had no effect from .env).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Converting via /code or ``` swapped the text field for the code editor's
and left the focus nowhere, so further typing triggered app shortcuts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New useVoiceInput hook (lib/voice.ts): "server" records with MediaRecorder
and sends to /voice/transcribe, "browser" keeps the live recognition,
"off" or missing browser support hides the microphone.
- Quick dump bar, mobile capture sheet and hold-to-record use it; live
waves from the real input level (VoiceWave) and "Wird erkannt…".
- Recordings stop after STT_MAX_SECONDS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- STT_URL switches voice input to an OpenAI-compatible Whisper server
(e.g. speaches); without it the browser's recognition stays. Configured
by environment only, with limits for upload size, parallel jobs, hourly
budget per user and timeout. The demo always uses the browser.
- POST /voice/transcribe forwards recordings and returns the text; nothing
is stored. On startup MindSift waits for the server and asks it to
download the model if missing.
- Admin card shows the active mode and server state, with a test recording.
- docker-compose: optional "whisper" service (speaches 0.8.3 CPU, profile
"whisper", models in ./whisper).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several unsent text changes of one block were applied one by one to the
local view, numbering local versions ahead of the server, which folds them
into one change (formBatch). The focused field kept the inflated version as
base, so the next edit after a sync conflicted with the user's own text.
The local view now applies pending ops as they will be sent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Demo: senders for mail, Discord and webhooks refuse, no alarm ticker;
test alarm, SMTP test, server settings, backups, account and session
changes of the shared demo accounts answer 403; the session list only
shows the own device. The UI shows the notices and disables the controls.
- SMTP password is never sent back (smtpPassSet), empty keeps it.
- ?token= on webhook ingest accepts only API keys.
- Request bodies capped at 2 MB (sync 8 MB, imports 101 MB).
- Deleting an account removes its server backups.
- Backup downloads, exports and import uploads get 30 minutes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- NewID uses 96 random bits; invite codes get their own 100 bits in Base32.
- Registration and forgot-password are limited per address via loginGuard.
- The invite is claimed atomically before the account is created and
released again when the account cannot be created.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Every login is a session (device, IP, last use); the token's jti is its ID.
Sessions end on logout, per device from the profile, or after 30 idle days.
- Tokens older than a day are renewed via the X-Session-Token header.
- Changing password or login e-mail needs the current password; a new
password logs out all other devices and mails the old address.
- Demo reset keeps the demo visitors' sessions.
- revoked_tokens is replaced by sessions (migration 4); everyone is logged
out once after the upgrade.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
safeHref lets only http, https and mailto become links (Markdown viewer,
outliner, legal links); javascript: and friends show as text. A
middleware sets a Content-Security-Policy (scripts only from MindSift,
no framing, connections only to this server), X-Frame-Options,
nosniff, Referrer-Policy and a Permissions-Policy. The two inline
scripts of index.html moved to files so script-src stays 'self'.
The second inline script also deleted every cache on each page load,
which undermined the offline start; it is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tags become three-part (v0.6.0), the header shows the full version and
pre-release tags no longer move :stable. CHANGELOG.md (Keep a Changelog)
starts with an Unreleased section; older versions will be added from the
history. The quantity field opens the number keypad on phones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The + button and the menu entry focused the new-item field 60 ms after
the tap; iOS only opens the keyboard for focus() inside the user gesture,
so the field was selected but nothing could be typed. Focus now happens
right in the tap. The list inputs use 16px on phones, so iOS no longer
zooms in and pushes the field under the keyboard. Version 0.5.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hold-to-record opened the sheet on pointerup; the click the browser sends
right after landed on the new sheet's backdrop and closed it at once.
Lists and notes have no such backdrop and kept working. A short tap now
opens on the click itself (as before), and after a hold that click is
ignored. Tests send the real pointerup + click sequence. Version 0.5.1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>