Solo is a privacy‑focused note‑taking app with a strong emphasis on typography and reading/writing experience. No cloud. No accounts. No AI. Your notes stay on your disk as plain HTML.
- Local-first: notes are files you own
- Typography-first: themes + fine-grained layout controls
- Timeline + notebooks + tags
- Integrations: Onyx Boox PDF annotations, Digikam tag-based image carousels
DEMO - see and test the app and it's visual themes.
Solo is a modern, privacy-focused note-taking application with an emphasis on typography and user experience. There's no AI, cloud storage and stuff. Just your thoughts.
I believe that your data is only yours and should not belong to any corporation or even single app. That's why it stays in your file system in plain HTML so that you could have access to it any time without additional instruments.
Most note apps lock you into a database, a cloud, or a proprietary format. Solo stores notes as HTML in your filesystem, so you can read, backup, sync, and migrate them anytime.
- Download the latest release: Releases → Solo-x.y.z (recommended)
- Or build from source (see below)
- Download the latest release: Releases → Solo-x.y.z (recommended)
- Or build from source (see below)
Contact me if you have any questions.
- Rich text editor (TipTap)
- Links, images, tasks, headings
- Zen mode, keyboard shortcuts, word/paragraph count
- Notebooks (nested), tags (hierarchical)
- Timeline view + date picker navigation
- Themes: Air, Typewriter, Narrow, FBI, Alighieri, Terminal
- Controls: font, size, line height, margins, paragraph spacing, drop caps, content width
- Onyx Boox: import / work with PDF annotations
- Digikam: insert image carousels based on tags
- React + TypeScript
- MobX for state management
- Vite for development and building
- TipTap for rich text editing
- Electron for desktop builds
- Kotlin for mobile
Feature flags live in feature-flags.json at the project root and are read at compile time (inlined into the bundle via Vite define).
The top-level keys describe the run mode:
PACKAGED— embedded/plugin build (__IS_PACKAGED__ === true)DESKTOP— native Electron clientMOBILE— native Android client
{
"PACKAGED": { "extended-search": false },
"DESKTOP": { "extended-search": true },
"MOBILE": { "extended-search": false }
}The build mode is resolved entirely at compile time via the PLATFORM
environment variable (each platform gets its own bundle):
| Command | PLATFORM | Constants |
|---|---|---|
npm run build:desktop |
desktop |
__IS_DESKTOP__ = true |
npm run build:android |
android |
__IS_ANDROID__ = true |
npm run build:packaged |
packaged |
__IS_PACKAGED__ = true |
npm run build |
— | builds desktop + android |
Each flag is expanded into a compile-time boolean constant __FF_<NAME>__
(e.g. extended-search → __FF_EXTENDED_SEARCH__), so unused branches are
tree-shaken out of the per-platform bundle.
Use the static flags object in code (best for tree-shaking):
import { flags } from './utils/featureFlags';
if (flags.extendedSearch) {
// compile-time removed from bundles where the flag is false
}A dynamic helper is also available (no tree-shaking, uses the inlined active flag set):
import { isFeatureEnabled } from './utils/featureFlags';
if (isFeatureEnabled('extended-search')) { /* ... */ }# Install dependencies
npm install
# Development
npm run electron:dev # Desktop version
# Production builds
npm run electron:build # Desktop versionNO LICENSE