A macOS menu-bar (tray) utility that lets you leave a note for “tomorrow you” and delivers it at the exact moment you start working-either when you open a specific app (e.g. Cursor) or when you unlock your Mac.
This project demonstrates shipping a real desktop utility with OS-level integrations: tray apps, SQLite persistence, IPC boundaries, Accessibility permission handling, and self-healing background loops.
Tonight: write a short note in the Editor.
Tomorrow: the note pops once, then is marked delivered.
Delivery triggers:
- On App Focus — show the reminder when the selected app becomes frontmost
- On Day Start — show the reminder when you unlock your Mac (first unlock of the day)
Guardrails:
- Deliver-after time gate (e.g. “don’t show before 10:00”)
- One-time delivery (no repeat spam after it’s delivered)
- Launch at login toggle for reliability across restarts
- Watchdog that restarts the poller if it stalls
I built Context HandOff to solve a problem I repeatedly experienced while building software:
When I return to a coding project the next day, I often forget exactly where I left off.
That re-orientation cost-reopening files, reloading context, reconstructing intent breaks momentum and wastes deep-work time.
I also realized something more honest:
I don’t have the habit of consistently writing to-do notes and reviewing them at the start of my workday.
Traditional todo apps assume:
- You will write things down.
- You will remember to check them.
I don’t reliably do either.
So instead of trying to “be more disciplined,” I designed a system that:
- Surfaces a short handoff note automatically at the exact moment I resume work
- Removes the need to remember to check a task list
- Reduces the cognitive friction of restarting deep work
- Engineers around a habit gap instead of relying on willpower
- Launch app → it runs in the menu bar (no big window).
- Click tray icon → Editor opens.
- Write note → Save for tomorrow (or Save for today in dev/testing).
- Next day (after deliver-after time):
- If On App Focus: open/focus selected app → overlay appears once.
- If On Day Start: unlock Mac → overlay appears once.
- Note is marked delivered so it won’t show again.
Electron has two worlds:
- Main process: system control (tray, windows, DB, app detection)
- Renderer: UI (React Editor + Overlay)
Renderer never touches OS APIs directly — it calls the main process through IPC.
- Electron
- TypeScript
- macOS menu bar tray + multi-window (Editor + Overlay)
- React
- Vite
- CSS
- SQLite (
better-sqlite3) - DB stored in
app.getPath("userData")
active-win(frontmost app detection on macOS)powerMonitor(unlock-screen detection)app.setLoginItemSettings(launch at login)
- Starts silently (no big window on launch)
- Tray click opens/focuses the Editor window
- Dock hidden on macOS (
app.dock.hide())
Two tables:
- handoff_notes
target_appdeliver_on_datenote_textdelivered_at(NULL until delivered)
- app_settings
- key/value store for:
target_app(bundleId)deliver_after_minutesdelivery_modelaunch_at_loginlast_day_start_deliver_date
- key/value store for:
DB path:
~/Library/Application Support/<YourAppName>/handoff.db
- User picks a time via
<input type="time"> - Reminder only triggers if:
nowMinutes >= deliver_after_minutes
Prevents accidental delivery at night or too early.
- Poll every 500ms
- Read frontmost app bundleId
- If it matches the selected target app → check eligibility → show overlay
Requires macOS Accessibility permission.
- Listen to
powerMonitor.on('unlock-screen') - Deliver only once per day using
last_day_start_deliver_date
No Accessibility required.
- Always on top
- Shows note text
- Marks note as delivered immediately after showing
- Prevents repeat triggers
Background loops can silently stall (promise rejection, thrown error, blocked interval).
Implementation:
- Poller updates
lastPollTickAton every tick - Watchdog checks every 5s
- If
now - lastPollTickAt > 5s→ restart poller
This prevents the “tray icon exists but reminders never fire” failure mode.
All privileged actions live in main process:
- DB reads/writes
- App picker (
dialog.showOpenDialog) - Launch at login
- Permission status checks
- Window resizing / overlay display
Renderer does:
- UI + form state
- calls main via IPC (
ipcMain.handle)
- Frontmost app detection returns bundleId reliably
- App selection via Finder reads
Info.plistfor:CFBundleIdentifier- display name fallback chain (
CFBundleDisplayName→CFBundleName→ folder name)
This avoids “Google Chrome vs Chrome” string mismatch issues.
This app is OS-driven, so “tests” are primarily real interaction checks:
- Tray click → Editor opens
- Save note → persists after restart
- On App Focus mode:
- after deliver-after time, focusing target app triggers overlay once
- On Day Start mode:
- lock screen → unlock → overlay once per day
- Launch at login works in packaged build
npm install
npm run devNote: macOS permissions can behave inconsistently during dev if you rebuild/run from different paths. For stable permission testing, install a packaged build into /Applications.
⸻
npm run distThen:
- Open the generated
.dmgor.zip - Drag the
.appinto/Applications - Run the app from
/Applications
(Important for stable Accessibility permission behavior on macOS)
This app solves a real personal problem: forgetting a planned task when the day starts.
However, it didn’t find users in my immediate network for several reasons:
Most people already rely on:
- Google Calendar
- Reminders / Notes
- Todoist / Notion
- Messaging themselves
Because these tools already solve most of the problem, the incremental benefit of installing a new background utility must be significantly higher to justify adoption.
Many people forget occasionally — but not consistently.
Adoption usually requires:
- Frequent pain
- High cost of forgetting
For most people I spoke to, the problem was not strong enough.
The app requires a new habit:
- Write a note at night
- Trust it will appear the next day
People who already have this habit use other tools.
People who don’t often won’t adopt a new workflow.
Conclusion:
The audience is niche — context-switchers who dislike traditional todo systems but still want contextual, one-time reminders.
- Competing with existing habits is harder than competing with “no solution.”
- Friction (permissions + always-running utility) must be justified by strong recurring pain.
- Narrow tools need either:
- A strong niche with high pain, or
- A broader wedge or integration that removes habit burden.
Desktop apps introduce different failure modes than web apps:
- OS permissions
- Dev vs packaged behavior
- Resource paths (
public/vsprocess.resourcesPath) - Native module packaging constraints
Reliability matters even in small apps:
- Watchdogs and guardrails prevent silent failure.
- Clear IPC boundaries make the app safer and easier to reason about.