Durations were the only thing keeping a lot of machinery alive, so drop them and let the dead code go with them. Removed: - The "Caffeinate for a Duration" command. - lib/duration.ts entirely (parseDuration / formatDuration / formatRemaining / normalizeDuration / DURATION_PRESETS) plus its test file. Every export was reachable only from duration paths. - The 30s setInterval re-render in the status view. It existed only to keep a "27m left" countdown honest, and with no timed sessions there is nothing to count down. - The duration-preset actions in the status view. - describeOptions(), which only the duration form used to explain itself. - durationSeconds / expiresAt from the options, state and command builder, along with the `-t <seconds>` and `sleep <n>` branches. Both platforms now hold the assertion until signalled, which is what "on" means here. - The unused "restarted" member of ToggleResult. The status view now reports "since <time>" instead of a countdown, and keeps a Refresh action. Also fixes a type error the narrower helper surfaced: the status view's run() helper was typed () => Promise<void> while startCaffeination returns Promise<CaffeinateState> and stopCaffeination returns Promise<boolean>. 407 deletions against 57 insertions. Verified: vici lint, tsc --noEmit, eslint, 8/8 vitest, vici build, and live in Vicinae — toggle on spawns `caffeinate -i`, toggle off stops it, and a pre-existing user-owned `caffeinate -u -dmi` is left untouched throughout.
3.4 KiB
Caffeinate
Keep your computer awake from Vicinae. One toggle, no terminal.
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 command 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] |
| 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.
State is recorded in LocalStorage as { pid, startedAt, command, args, processName, … }.
Stopping signals the recorded PID, not killall caffeinate, so a caffeinate you started yourself in a terminal is left alone. Before signalling, the PID's process name is re-checked via ps -o comm= — PIDs get recycled, and without that check a stale entry could kill an unrelated process.
Status is self-healing: if the recorded process is gone (killed elsewhere, or a reboot), the stale state is cleared on the next read.
Preferences
| Preference | Default | Notes |
|---|---|---|
| Also Prevent Display Sleep | off | macOS only. 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.
detectPlatformthrows with a clear message instead of failing obscurely.powercfgwould 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
