- 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/
- Login and first-run setup screens
- File tree with inline create/rename, context menu, drag-and-drop
move and trash with undo; all operations update the UI first and roll
back on failure
- Tabs keep a CodeMirror state each; edits autosave after 500 ms with
If-Match revisions, unsent edits persist in IndexedDB, and conflicts
or remote deletions surface as banners with explicit choices
- WebSocket events sync external edits, moves and deletions into open
tabs; reconnects trigger a full resync
- Pasted or dropped images show instantly and upload to note.assets/
- Responsive layout with a swipe-in sidebar drawer on phones
- Switch to TypeScript 6: vue-tsc does not support TypeScript 7 yet
- vault: path validation (no traversal, hidden segments or symlink
escape), atomic writes with If-Match/If-None-Match revisions, moves
that carry the note's .assets folder and rewrite its image links,
trash with restore, content-addressed asset uploads
- auth: single-user Argon2id password, sliding sessions in SQLite,
login rate limiting, first-run setup gated by a one-time code written
to the data directory
- watch: fsnotify plus periodic rescans diffed against a snapshot, so
both RawMark and external edits are broadcast as tree/changed/moved/
deleted events over WebSocket
- api: stdlib router, SameSite=Strict cookies plus Origin checks,
sandboxed raw file serving, embedded SPA with gzip and CSP
- Copy button in the top-right of fenced code blocks, with execCommand
fallback when the Clipboard API is unavailable over plain HTTP
- Mod-A inside a code block selects the block content first; pressing
it again selects the whole document
- Paint code block backgrounds on a ::before layer below CodeMirror's
selection layer so selections inside code blocks are visible
- Table toolbar sits in reserved space above the table instead of
overlapping it, with grouped icon buttons
- Top-level GFM tables render as an editable widget: click a cell to
edit its Markdown source, Tab/Enter/arrows to navigate, toolbar to
insert/delete rows and columns or switch to source
- Structural table edits are isolated undo steps; focus is restored
after the widget is rebuilt
- Clicking an image moves the cursor to its syntax to reveal the source
- Renderer escapes raw HTML and filters dangerous URL schemes by default
- CodeMirror 6 editor with Obsidian/Typora-style live preview: block
markup revealed per line, inline markup revealed per element
- Fenced code highlighting via Shiki (github-dark, JS regex engine) in a
Web Worker, languages loaded on demand
- Lezer syntax tree to HTML renderer for share pages, checked against
the CommonMark 0.31.2 spec (640/652; 12 known parser deviations)
- Use markdownLanguage directly to avoid bundling lang-html/css/js