A periodic spotlight popup for Windows and macOS that asks you "what are you doing?" and "is it bullshit?". Answers are written to an append-only NDJSON journal.
- Nudge sits in the system tray, the timer ticks.
- Timer expires → a spotlight popup appears in the center of the screen (always on top, like Spotlight / Claude quick chat).
- The user answers → Enter → the popup hides, the timer restarts.
- Answers are appended to
journal-rust.ndjson.
Minimalist window in the center of the screen, on top of all other windows. Opens focused on the first field.
| # | Field (Russian product copy — the actual questions) | Type | Description |
|---|---|---|---|
| 1 | Что я делаю? | text | Free-form input, focused on open |
| 2 | Не хуйню ли я делаю? | text | Free-form input, reflection |
| 3 | Следующий nudge через | number (min) | Pre-filled with the current interval (default 10 min) |
- Enter — save and close
- Esc — close without saving (whether the timer restarts depends on how the popup was opened — see below)
Everything the popup does is one state machine: three states, four events, no rules outside the transition table. It is specified in docs/design-spec.md §4 and there is a clickable model of it in docs/state-machine.html.
NDJSON file (journal-rust.ndjson), append-only, one JSON record per line:
{"schema_version":1,"event_type":"submitted","entry_id":"01JS1S8R5W4Y4S4M8Q6A8X7R2V","captured_at":"2026-04-08T14:30:00.000+03:00","implementation":"rust","trigger_source":"timer","doing":"writing requirements","bullshit":"no","next_interval_minutes":10}
{"schema_version":1,"event_type":"submitted","entry_id":"01JS1S9FDRW4K4M7R4F5R9A5A2","captured_at":"2026-04-08T14:40:00.000+03:00","implementation":"rust","trigger_source":"timer","doing":"got sucked into YouTube","bullshit":"yes","next_interval_minutes":5}Full contract: docs/journal-spec.md
- Tray icon — a daisy that drops petals as the next nudge approaches (this
is the time-remaining indicator, see
docs/design-spec.md§5). - Tooltip:
~N min(rounded up, refreshed once per minute);nowafter the timer expires. - Left click: open the popup (does not affect the timer).
- Right click: context menu —
Show Nudge,Settings,Quit.
- Rust (native, no web engines).
- GUI: eframe / egui (glow renderer, transparent window).
- Goal: minimal footprint (~10-20 MB RAM), instant startup, single binary.
- Interval: 10 minutes — field
default_interval_minutesinconfig.json, any positive number (integer or decimal). - Journal:
<Documents>/Nudge/journal-rust.ndjson—%USERPROFILE%\Documentson Windows,~/Documentson macOS. - Config:
%USERPROFILE%\Documents\Nudge\config.jsonon Windows,~/Library/Application Support/Nudge/config.jsonon macOS. Not next to the journal there: Documents is TCC-gated and iCloud-synced, and a menu-bar agent should not have to ask for consent to read its own settings. A file left in~/Documents/Nudge/by an earlier version is moved on first launch. - Hotkey: Ctrl+Shift+Space (
⌃⇧Space) — opens the popup manually from any focused window. Fieldhotkeyinconfig.json: any modifiers plus one key.Ctrl/⌃,Alt/Option/⌥,Shift/⇧,Cmd/Win/⌘, plus a letter, digit,F1–F24,Space,Enter,Tab,Esc,Backspace, an arrow,Home/End/PageUp/PageDown/Insert/Delete, or a symbol cap (- = [ ] \ ; ' , . / ``) — e.g.⌥⌘J,Alt+J,Ctrl+F12,⌃⇧←. Case, spacing and the choice of spelling do not matter. A symbol cap is named by its unshifted legend, so⇧/and?are one combination. At least one modifier is required, except forF1–F24.fncannot be part of a hotkey on macOS; seedocs/design-spec.md` §5 for why. - Launch at login: field
autostart(bool) — kept in sync withHKCU\…\Run\Nudgeon Windows and with a LaunchAgent plist at~/Library/LaunchAgents/com.wizard1209.nudge.pliston macOS; seedocs/design-spec.md§9.
Download the DMG from the Releases page, drag Nudge.app to Applications,
then clear the download quarantine:
xattr -dr com.apple.quarantine /Applications/Nudge.appThat step is needed because the app carries an ad-hoc signature rather than a Developer ID, and macOS 15 removed the Control-click override that used to get past Gatekeeper. The alternative is one trip through System Settings › Privacy & Security › Open Anyway after the first launch is refused.
Nudge runs as a menu-bar agent: no Dock icon, no Cmd-Tab entry, and the daisy in the menu bar is where Settings and Quit live.
The settings window opens from the tray menu (Settings) and edits the same
config.json (hotkey, interval, autostart). Edits are picked up by the
running app immediately via a file watcher.
The hotkey is typed in, in any of the spellings above — ⌥⌘J, Alt+Cmd+J
and alt+cmd+j are one combination. Full grammar: docs/design-spec.md §5.
Save refuses a combination the OS cannot bind rather than writing it.
On macOS it can also be pressed instead of typed: Record, then the
combination. The keys are read from the OS rather than from the window
toolkit, so ⌘C records as ⌘C instead of copying, the keypad is its own key
rather than a fold onto the digit row, and a shortcut follows the physical cap
— with a Russian layout active, the cap that types о still records ⌥⌘J.
Esc, Cancel and clicking away all put the saved value back. Windows has no
Record button: its platform facts are unmeasured, and
docs/TASK-hotkey-native.md is where that work is specified. Behaviour in
full: docs/design-spec.md §9.
If another application or macOS itself already holds the combination, Save
says so and saves it anyway — it is the shortcut the user asked for, and a
taken one binds normally, it just never fires.
This repo holds two implementations of the same spec. Alongside the Rust app
lives electron/ — the prototyping harness where new
features and design changes are tried first, because Electron is fast to build
and easy to inspect visually. The outcome is captured in the shared spec under
docs/, the contract both implementations follow, and then ported to
Rust. The released product is the Rust binary only; the Electron app is a
workbench, fully rewritable from the spec.
