Thank you for helping. Lucid is a structured-document editor: speech or typing becomes headings, lists, tables and diagrams, by a local model. Its shape is small on purpose, so please open an issue to talk about a larger change before you build it.
You need macOS (Apple Silicon), Windows or Linux (x64), Node 22.12 or newer and pnpm 9.
pnpm install
node scripts/fetch-tectonic.mjs # the PDF engine
pnpm devYou do not need LM Studio for tests: unit, integration and end-to-end tests use a scripted server
(packages/mock-lmstudio). Only pnpm eval and pnpm e2e:real use a real model.
| Command | |
|---|---|
pnpm dev |
the app, with reload |
pnpm test |
unit tests (all packages) |
pnpm e2e |
Electron end-to-end tests with a fake LM Studio, in a maximized window (written and run on a Mac) |
pnpm run ci |
typecheck, lint, formatting and unit tests |
pnpm eval |
74 cases (and their variants) against the real model in LM Studio; see docs/evals.md |
pnpm dist |
builds the installers for the system it runs on into apps/desktop/release/: a .dmg and .zip (macOS), an installer and .zip (Windows), an .AppImage and .deb (Linux) |
pnpm e2e:packaged |
runs the end-to-end tests against the built app, on any of the three systems (after pnpm dist:dir) |
How the code fits together, and what each package does, is in docs/architecture.md.
pnpm run ci # typecheck, lint, formatting, unit tests
pnpm e2e # the Electron tests, in a maximized window (do not touch the keyboard while it runs; written and run on a Mac)- One concern per pull request, in small commits with Conventional Commit messages
(
feat(kit): …,fix(desktop): …). Each commit should pass the checks on its own. - A fix comes with a test that fails without it. A new behaviour comes with tests at the level where it lives.
- End-to-end tests call
maximizeWindow(seeapps/desktop/e2e/helpers.ts): Lucid is made for a full-screen window. - If your change touches the prompts or the pipelines, run
pnpm evalwith the model and say what changed in the pass rates (seedocs/evals.md). - Update the docs (the README,
docs/, the guidedocs/guide.html) and the changelog when something a person sees changes.
The unit tests run on macOS, Linux and Windows in CI, and the packaged app is built and tested on all three before a release. So
code must not assume one system: build file paths with node:path (or posix/win32 when the system is a parameter), take
the system as an argument where behaviour differs, so that a test can cover all three on any machine (see electron/tools.ts
and electron/studio.ts), and never turn a file URL into a path with .pathname (use fileURLToPath). A test that needs a
Unix shell script, a socket file or a Mac-only path says so with skipIf and a comment. Keyboard hints are written with ⌘ ⇧ ⌥ and
shown through keys() (apps/desktop/src/platform.ts), which turns them into Ctrl, Shift and Alt on Windows and Linux.
packages/coreis pure TypeScript: no DOM, no Electron, no network.- Every AI change is exactly one undo step. Never lose what the person said: if the model's answer cannot be used, keep their words as an unprocessed paragraph.
- The model never writes LaTeX or drawing code. It fills typed blocks; the kit's templates do the typography.
- Calls to LM Studio always switch reasoning off (
reasoning_effort: "none"). - No cloud services in the core workflow, and no copied code under a licence that does not fit MIT (GPL in particular).
A format is data: open packages/kit/src/formats/index.ts and add an entry with an id, a
short description, keywords for the suggestion, the sections (each with a hint saying what belongs there and the block
types that prefer it) and the latex options. Then update the lists in packages/kit/src/formats/formats.test.ts. The format
shows up in New from a format… and in the model's prompts.
A block is one folder in packages/kit/src/blocks/: its schema, a short description for the model,
its outline, Markdown and LaTeX output, and fixtures. Register it with one line in blocks/index.ts. The kit's contract test
(packages/kit/src/contract.test.ts) runs for every registered block and fixture, so a new block is tested as soon as it
exists. The editor side lives in apps/desktop/src/editor.
Use the issue templates. Please do not paste notes you want to keep private: in Lucid, Export note… with Include what I said and the change history switched off shares only the page.
Security problems go through SECURITY.md, not a public issue.
By contributing you agree that your contribution is licensed under the MIT licence.