Desktop  ·  2026

Quill

A quiet desktop writing app for Markdown and prose, with local model assistance.

2 Themes
0 Cloud calls
439 Tests

2 Themes

Desktop

2026

Key decisions

  • AI requests go only to LM Studio, and the client rejects any base URL that is not localhost, 127.0.0.1 or ::1, because unfinished writing is the last thing anyone wants leaving their machine.
  • The local-model tradeoff is stated in the app rather than discovered as a failure — inline suggestions report named suppression reasons such as missing-model and local-ai-unavailable instead of failing silently.
  • Autosave is on by default and writes to the real file rather than a shadow copy, because it is the feature that decides whether a writing tool is trusted.
  • AI-written and pasted ranges are marked in a CodeMirror StateField that maps provenance through every later edit, because a drafting tool should never blur what the writer wrote and what the model supplied.

Quiet is a design requirement

Most Markdown editors are configuration surfaces that happen to contain a text field. Quill inverts that: the workspace is quiet by default and precision is available when asked for.

Focus mode dims everything but the active block. Typewriter scrolling holds the caret at a fixed vertical position so your eyes stop tracking down the screen. Syntax controls let you turn markup rendering up or down without leaving the document. The Paper and Ink themes are warm rather than clinical, because prose is not code even when it is stored as text.

All of these are CodeMirror 6 extensions held in a Compartment, so preference changes reconfigure the live editor instead of remounting it. Word counts and document stats run in a Web Worker (documentStatsWorker) so they never tax typing, and a rule-based style check flags clichés, filler, wordy phrasing and redundancy, lazily loading the compromise tagger only when the check is on.

Local assistance only

AI features run against LM Studio on localhost:1234. Nothing is sent anywhere — the client in services/lmStudio refuses any base URL that is not loopback, so even a misconfigured endpoint fails closed. The tradeoff is explicit — you must have a model loaded and the local server running before the assistance features do anything — and that is stated in the app rather than discovered as a failure.

Assistance comes in three shapes, each with its own request policy — versioned prompt, token cap, temperature cap and timeout (aiRequestPolicy). Inline suggestions stream from the OpenAI-compatible chat endpoint as ghost text and report named suppression reasons (missing-model, local-ai-unavailable, not-useful-context) instead of failing silently. An ask panel answers questions about the document under an explicit context budget, and selection rewrites apply edits in place. A CodeMirror StateField marks AI-written and pasted ranges and maps those marks through every subsequent edit, so the record of what the model supplied survives the drafting session.

For a drafting tool this is not a privacy posture so much as a working condition: unfinished writing is the last thing anyone wants leaving their machine.

The Electron boundary

Native capability is exposed through a secure preload and contextBridge, with the renderer holding no direct filesystem access. Every IPC handler in the main process validates its sender against the trusted renderer URL before touching anything, navigation is denied by default, and external links are handed to the OS browser. File open, save, autosave and export all cross that boundary explicitly — DOCX export runs in the main process because html-to-docx is CommonJS and is require()d lazily rather than bundled into the ESM build.

Beyond single documents, a library subsystem watches a folder of Markdown files in the main process and indexes its links, backlinks, tags and transclusions. Autosave is the feature that decides whether a writing tool is trusted, so it is on by default and writes to the real file rather than a shadow copy — and a draft-recovery snapshot sits beneath it as the crash net. Updates follow the same quiet rule: electron-updater stays inert unless explicitly armed with an environment variable, and then only shows a single restart notice when one is ready.