adrianbonpin 14624d78fe Prevent display sleep by default
`caffeinate -i` blocks system idle sleep but lets the display blank, so the
screen still slept and the extension looked broken. The "Also Prevent Display
Sleep" preference that fixes this defaulted to off.

Flip it to on, and guard it with a manifest test that reads package.json and
asserts the shipped defaults map to `-i -d`. Verified on macOS: `caffeinate -i`
yields PreventUserIdleDisplaySleep 0, `caffeinate -i -d` yields 1.
2026-09-20 21:17:14 +08:00
2026-09-20 21:17:14 +08:00
2026-09-20 21:17:14 +08:00
2026-09-20 21:17:14 +08:00

Caffeinate

Keep your computer awake from Vicinae. One toggle, no terminal.

Caffeinate icon

Why this exists

The Raycast coffee extension does not work on Vicinae. Its index command is a menu-bar command, which Vicinae cannot run at all, and its status command crashes because it gates on LocalStorage.getItem(key) === undefined — Vicinae resolves a missing key to null instead of undefined.

This is a native @vicinae/api extension, so none of that applies.

Commands

Command Mode What it does
Toggle Caffeination no-view Flips sleep prevention on or off, whichever is appropriate. The fast path.
Caffeination Status view Whether sleep is being blocked, since when, and the exact process holding it. Start/stop from here.

How it works

Sleep prevention is an OS-level assertion, so the extension delegates to the platform tool and then gets out of the way:

Platform Mechanism
macOS /usr/bin/caffeinate -i [-d] [-s] (defaults to -i -d)
Linux systemd-inhibit --what=idle:sleep --why=<reason> sleep infinity

Both run until signalled — there is no timer, so nothing needs to be refreshed or babysat.

The process is spawned detached and unref'd. That is required: Vicinae unloads a no-view command as soon as its function returns, so a child process would otherwise be torn down with it.

The process is the state

Nothing is persisted. The running process carries its own identity in its command line:

vicinae-caffeinate:v1:<startedAt>:<flags>        e.g. vicinae-caffeinate:v1:1789895271765:id
  • macOS has no free-form field on caffeinate, so the marker is installed as argv[0] via the shell's exec -a. exec replaces the shell in place, so the pid Node receives is the caffeinate pid.
  • Linux passes it in systemd-inhibit's --why, alongside a human-readable reason.

Status is therefore a ps scan for that marker, and stopping signals every match.

Why this design

The obvious approach — remember the pid in LocalStorage — has a failure mode that is worse than it sounds. Two pieces of state have to stay in sync: the record, and the actual process. If the record is ever lost (extension reinstalled, storage cleared, a crash between spawning and saving) the process keeps holding a sleep assertion that nothing will ever release. A laptop that silently never sleeps, with no way to find the culprit.

Making the process self-describing removes the class of bug rather than guarding against it: there is no record to lose, so the extension cannot lose track of a session. Any status or toggle reconciles against the truth, and start always clears existing sessions first so assertions cannot stack.

Verified by deleting the installed extension while a session was active, restarting Vicinae, then reinstalling: the fresh install saw the leftover as active and stopped it, instead of starting a second one and orphaning the first.

Signals go to the process group (kill -TERM -<pid>). Sessions are spawned detached, so the child is a group leader; on Linux that matters because systemd-inhibit forks a sleep, and signalling only the parent can leave the child behind.

A caffeinate you started yourself is never touched — only processes carrying the marker are ever signalled. If a session ever does need to be cleared by hand:

pkill -f vicinae-caffeinate

Preferences

Preference Default Notes
Prevent Display Sleep on macOS only. On by default: caffeinate -i alone blocks system idle sleep, so the screen still blanks and the extension looks broken. Turn it off to save battery. systemd-inhibit cannot hold the display awake — that is the compositor's job, and the UI says so rather than pretending.
Also Prevent System Sleep off On macOS this assertion only takes effect while on AC power.

Development

npm install
npm run dev        # vici develop — Vicinae must be running

Other scripts: npm run lint (manifest), npm run lint:code (ESLint), npm run typecheck, npm test.

To install a production build into Vicinae:

npm run build      # writes to ~/.local/share/vicinae/extensions/caffeinate

No top bar indicator

Vicinae has no menu-bar command support. This is not a Raycast-compat gap — a native mode: "menu-bar" command is accepted by the manifest and builds cleanly, then is silently dropped at registration, with no warning. @vicinae/api exports a MenuBarExtra component, but there is no host to render it into.

For a real top bar indicator you need a standalone macOS menu-bar app; it cannot be part of a Vicinae extension.

Not implemented

  • Windows. detectPlatform throws with a clear message instead of failing obscurely. powercfg would be the path.
  • Timed windows (caffeinate -t), while-an-app-is-running (caffeinate -w <pid>), and scheduled windows. Deliberately omitted — on/off covers the common case.

License

MIT

S
Description
Keep your computer awake from Vicinae. One toggle, no terminal.
Readme MIT
127 KiB
Languages
TypeScript 97.9%
JavaScript 2.1%