Add scoped public navigation, reading progress, focus and print controls without loading the editor. Polish responsive settings, graph and media layouts.
Correct share expiry units, preserve math and code during link maintenance, and normalize Markdown destinations for templates and note navigation. Verify browser flows and document the new tools.
Show images in asset directories with preview, rename, replacement, and trash recovery. Update local Markdown references when images move and restore original files if related metadata cannot be committed.
Refresh image previews after replacement and compare actual contents when deduplicating subsequent uploads.
- Drag tabs to reorder; context menu to close, close others, close right
- Resizable sidebar width (synced setting), collapsible with Mod+\
- Outline panel (Mod+Shift+O): right column on desktop, overlay on mobile
- Mobile: title opens quick switcher, swipe left closes the drawer,
44px touch targets
- Markdown format commands shared by Ctrl+B/I/K and a toolbar above the
soft keyboard on touch devices; bold and italic stack correctly
- Fit the app to the visual viewport so the keyboard does not cover the
caret or the toolbar
- Insert images from the photo library or a file picker
- Move notes and folders with a folder picker from the file tree menu or
the command palette
- Open the file tree menu with a long press on touch devices
- Store app settings as one JSON document on the server, merged per key and
pushed to other devices; cache locally to avoid a flash on load
- Settings page groups appearance, account, shortcuts, shares, API tokens and
trash; full screen with a section list on phones
- Font size and content width settings
- Change the login password, signing out other sessions
- Settings writes and password changes only accept a login session
- Share a note as a live read-only page at /s/{token} with optional password and expiry
- Share page renders with the shared parser (TOC, footnotes, math, Mermaid, code highlighting)
and only serves images from the note's own .assets directory
- Shares follow renames made through the API or externally
- Manage shares and API tokens from the command palette and file tree
- Accept read/write API tokens as Bearer credentials; management endpoints require a session
- Serve a hand-written openapi.json, checked against the route table in tests
- WebCrypto keyring (PBKDF2 600k + AES-GCM master key) and per-note/per-image keys
- Encrypt/decrypt commands convert the note and its own images, deleting the originals
- Unlock, setup and change-password dialogs; manual lock; lock on page close
- Encrypted images are decrypted to blob URLs; pasted images are encrypted before upload
- Rewrite image links in the browser when renaming an encrypted note
- Skip local drafts for encrypted notes and confirm before closing with unsaved changes
- Show encrypted notes as locked with an explanation outside secure contexts
- Parser extensions shared by the editor and the HTML renderer: ==highlight==, $inline math$
(Pandoc rules, so amounts like $5 are not math), $$ math blocks, footnote references and
definitions, [TOC], and YAML front matter
- Live preview: highlight, superscript/subscript, emoji names (gemoji), footnote references
with the definition as tooltip, a clickable table of contents, styled front matter
- Math (KaTeX) and Mermaid render in place; while editing a block the source is shown with a
live preview below. KaTeX, Mermaid and the emoji table are loaded only when a note uses them
- KaTeX is pinned to 0.18 so Mermaid shares the same copy
- Move editor focus tracking out of table.ts so block widgets and tables use the same state
- Commands (with optional shortcuts and availability checks) are registered in one place;
global shortcuts are dispatched from it, and plugins can use registerCommand later
- The quick switcher lists commands when the input starts with '>' (VS Code style),
opened with Ctrl+Shift+P or F1 (Firefox reserves Ctrl+Shift+P), plus buttons in the
sidebar footer and the mobile top bar; recently used commands come first
- Built-in commands: quick open, search, new note/folder, save, rename, delete, close tab,
trash, log out
- Mount the trash dialog at the workspace level so it is not positioned inside the
transformed mobile drawer
- Ctrl+P quick switcher with fuzzy matching on note titles and folders; empty input lists
recent notes with the previous note first; creates the note (and folders) when no note
has that name; also reachable from the mobile top bar
- Track recently opened notes, following renames and moves
- Serialize directory refreshes and discard responses that raced with local edits; a stale
response could drop an optimistically added note, and debounced callers could wait
forever, leaving the workspace startup unfinished
- Wait for a pending note creation before renaming it
- Focus the editor after opening a note from the switcher or search
- Index notes in a separate, rebuildable SQLite database (index.db) using FTS5 trigram,
synced from watcher snapshots so external edits are picked up; terms under 3 characters
fall back to LIKE
- GET /api/v1/search?q= returns matching notes with the first matching line and a snippet;
path matches rank first
- Sidebar search panel (Ctrl+Shift+F) with highlighted terms and keyboard navigation;
opening a result jumps to and selects the match
- Restore scroll position on tab switch with scroll snapshots; restoring scrollTop drifted
by a few lines after line heights were re-estimated
- Bind Mod-Shift-z to redo on all platforms (CodeMirror only binds it on Linux/macOS)
- Render tables based on editor focus so a note starting with a table is not shown as source
- Send the save request without waiting for the IndexedDB draft write, and flush on pagehide;
Firefox could drop the last edit when a tab was closed right after typing
- Fall back to clipboardData.items when pasting images with an empty files list
scripts/dev.sh checks toolchains and ports, installs frontend deps when
the lockfile changes, starts the Go server and Vite with prefixed logs,
and stops both on Ctrl+C or when either exits. The Vite proxy reads the
same API_PORT so custom ports keep working.
- The dev build tag reads web/dist from disk instead of embedding it,
so the backend starts without building the frontend first; Vite
serves the UI and proxies /api and WebSocket to the Go server
- Document the two-terminal dev workflow and ignore .dev-data/