Key-value notes in your menu bar.
One shortcut to open. Keyboard-first. Masked values for secrets.
- 🔑 Key-value notes stored locally in a plain JSON file — no cloud, no accounts
- ⚡ Global shortcut (
⌘⇧K/Ctrl+Shift+K) — open from anywhere - ⌨️ Keyboard-first navigation — arrow-keys to move, Enter to copy, Escape to hide
- 🙈 Value masking for secrets — toggle on/off per session
- 🌓 Themes — system / light / dark
- 📍 Configurable data file location — keep your notes anywhere
- 🎹 Customizable shortcuts — global toggle, new note, focus search
- 🔒 Secure-by-default Electron —
contextIsolation: true,nodeIntegration: false
- Node.js 24.15.0 — pinned via
.nvmrc(read by nvm/fnm/asdf/volta) and enforced softly byengines.nodeinpackage.json - Electron 40 — desktop shell with secure IPC (
contextIsolation: true,nodeIntegration: false) - TypeScript — across main, preload, and renderer
- electron-vite — dev server with HMR and production build
- Vitest — unit tests with V8 coverage (100% threshold on main process)
- Playwright — E2E tests driving a packaged Electron build
- electron-builder — packaging and installers
- ESLint + Prettier — linting and formatting
- JSON file storage — no native dependencies; notes persisted to a plain JSON file
nvm use # or `fnm use` — activates the Node version pinned in .nvmrc
npm install
npm run devThe window starts hidden — look for the Keycache icon in the menu bar / system tray, or press the default global shortcut ⌘⇧K (macOS) / Ctrl+Shift+K (Windows, Linux) to toggle it.
| Command | Description |
|---|---|
npm run dev |
Launch dev server with HMR |
npm run start |
Alias for dev |
npm run build |
Production build to out/ |
npm run preview |
Preview the production build (electron-vite preview) |
npm run test |
Run unit tests |
npm run test:watch |
Run unit tests in watch mode |
npm run test:coverage |
Run unit tests with coverage report |
npm run test:e2e |
Build + run Playwright E2E tests |
npm run lint |
Lint with ESLint |
npm run format |
Format with Prettier |
npm run package |
Build + package unpacked app to dist/ |
npm run dist |
Build + create distributable installer to dist/ |
npm run version:preview |
Show the bump level picked from commits since the last tag (dry run) |
npm run version:auto |
Bump package.json + tag based on commits since the last tag |
npm run release |
Full release flow: sync, checks, bump, push (one command) |
npm run release:dry-run |
Run all checks and the bump preview, stop before mutating anything |
Releases are built and published via GitHub Actions (.github/workflows/release.yml). The workflow produces unsigned artifacts for macOS (.dmg + .zip), Windows (.exe), and Linux (.AppImage).
The fast path — one command that does everything:
npm run releaseIt runs these checks in order, aborting on the first failure:
- On the
mainbranch - Working tree is clean (no uncommitted changes)
- Fast-forward with
origin/main(pulls if behind; aborts if diverged or ahead) - Local Node matches
.nvmrc npm cinpm run lint && npm run test && npm run build
Then it previews the bump (same rules as npm run version:preview) and asks for a single [y/N] confirmation before running npm version <level> -m "chore: release v%s", git push origin main, and git push --tags.
Pushing the v* tag triggers the workflow. Each of the three OS runners builds its native artifacts in parallel on macos-latest / windows-latest / ubuntu-latest; a final job collects them and publishes a GitHub Release with auto-generated notes, visible to the public immediately.
Run npm run release:dry-run to exercise every guard and see the bump preview without mutating anything.
The bump level comes from Conventional Commit prefixes since the last tag, mapped to semver:
- patch — bugfix, no behavior change (
fix:,chore:,docs:,refactor:,test:,ci:,build:,perf:,style:,revert:) - minor — new feature, backwards-compatible (
feat:) - major — breaking change (any type with
!likefeat!:/fix!:, or aBREAKING CHANGE:footer)
The highest-severity match across all commits since the last tag wins.
If you'd rather run the steps by hand (to pick a different bump level, skip npm ci, etc.):
git switch main
git pull
nvm use # or `fnm use`
npm ci
npm run lint && npm run test && npm run build
npm version <patch|minor|major> -m "chore: release v%s"
git push origin main
git push --tagsUse the Actions tab's Run workflow button to trigger the matrix without publishing — artifacts are uploaded to the workflow run for inspection only.
- macOS users see a Gatekeeper warning on first launch; right-click the app → Open to bypass.
- Windows users see a SmartScreen warning; click More info → Run anyway.
- macOS default target is the runner's arch only (arm64 on
macos-latest). For x64 or universal builds, extendmac.target/archinelectron-builder.yml.
Code signing (Apple Developer cert, Windows EV cert) is not set up; enabling it requires the certs plus additional GitHub secrets.
A properly formed Git commit subject line should always be able to complete the following sentence:
If applied, this commit will your subject line here
[type](optional scope): [subject]
Must be one of the following:
- build - build related changes
- ci - CI related changes
- chore - build process or auxiliary tool changes
- docs - documentation only changes
- feat - a new feature
- fix - a bug fix
- perf - a code change that improves performance
- refactor - a code change that neither fixes a bug nor adds a feature
- revert - reverting things
- style - markup, white-space, formatting, missing semicolons, etc
- test - adding missing tests
The subject contains a succinct description of the change:
- Use the imperative, present tense: "change" not "changed" nor "changes"
- No dot (.) at the end
Just as in the subject, use the imperative, present tense: "change", not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
The 7 rules of a great commit message:
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Summary in present tense. Not capitalized
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
Copyright (c) 2026 Eugênio Moreira
Licensed under the GNU General Public License v3.0 or later. See LICENSE for the full text.
