Universal ask-anywhere overlay. Summon it with a global hotkey from any application, and it reads your current text selection, opens a small always-on-top chat, and streams an answer from NVIDIA NIM.
npm install
npm startnpm start launches the app with no window visible and a tray icon. Press
Ctrl+Alt+K (Cmd+Alt+K on macOS) anywhere on your system to summon the overlay.
If that combo is already claimed by another app or browser extension, SideNote
silently falls back through a short list of alternates (Ctrl+Shift+K,
Alt+Shift+K, Ctrl+Shift+Space) and shows a one-time notification telling you
which one it landed on. You can always set a custom shortcut in Settings. The
very first time the overlay opens, a one-time notification confirms it worked
and reminds you Esc hides it again.
- Get a key at build.nvidia.com (NVIDIA NIM /
integrate.api.nvidia.com). - Open the tray icon → Settings, or click Add your NVIDIA API key inside the overlay.
- Paste the key into the API key field, then tab/click away (or press Enter) — it saves immediately on that field losing focus, the field clears, and the placeholder flips to "saved" with a confirmation next to it. You don't need to touch any other setting for it to persist.
- Click Test key any time to fire a 1-token request and confirm it's valid.
- The key is stored locally via Electron's
safeStoragewhen the OS supports it (Windows DPAPI, macOS Keychain, or the Linux Secret Service). IfsafeStorageis unavailable, the key falls back toelectron-store'sencryptionKeyoption, which is obfuscation, not real encryption — anyone with access to your OS user account can recover it. Treat this the same as any other locally-stored secret. - The key is only ever written, never read back into the field — reopening Settings always shows an empty field with a "saved" placeholder, not your actual key. Nothing overwrites a saved key unless you explicitly type and commit a new one.
Tray icon → Settings (or the gear icon in the overlay) covers:
- NVIDIA NIM — API key, model (dropdown or a custom model id), max tokens (256–4096), temperature (0–1).
- Overlay behavior — the global shortcut (click the field, then press a combo to rebind it), follow-cursor vs. remembered position, hide-on-blur, launch at login.
- Appearance — System / Light / Dark theme.
- Data — clear all saved history.
The copy-keystroke mechanic (see Appendix B in the PRD) is the single most failure-prone part of an app like this, so a couple of things are tuned deliberately:
- The synthetic
Ctrl+C/Cmd+Cis sent as separate press/release steps with a small delay between each (not a zero-delay chord) — Chromium-based browsers can silently miss a chord delivered with no gap, especially right on the heels of the hotkey's own key-up, which shows up as an empty Context block with no error anywhere. - There's a short settle delay before the copy keystroke fires at all, and the clipboard poll runs for up to 600ms.
- If capture ever comes back empty in a specific app, it's worth first confirming the hotkey itself fired (a one-time notification confirms this on first use — see Install above) before assuming it's a capture-timing issue.
Selection capture works by synthesizing a copy keystroke (Cmd+C) behind the scenes.
macOS requires Accessibility permission to let an app send synthetic keystrokes to
other applications.
- On first run, if permission hasn't been granted, the overlay shows a one-time screen
explaining this with a button that opens the system prompt
(
System Settings → Privacy & Security → Accessibility). - Without this permission, SideNote still works — it falls back to reading whatever is currently on the clipboard, with a note in the context block.
Wayland's security model blocks synthetic input from arbitrary apps. On a Wayland
session (XDG_SESSION_TYPE=wayland), SideNote automatically disables the copy-keystroke
capture and runs in clipboard-only mode: press the hotkey after manually copying
(Ctrl+C) the text you want to ask about. This is noted in Settings.
- No inline chip next to your selection.
- No accessibility-API text scraping (macOS
AXUIElement/ Windows UIAutomation) to read the selection without a copy keystroke. This would remove the clipboard dance entirely and is the "correct" long-term fix, but it's a real per-platform native-module project on its own — deferred, same as the PRD's v3 plan. - No screenshot/OCR capture.
- No auto-update, code signing, or notarization —
npm run buildproduces an unsigned installer for your current OS. The tray menu's Check for updates… just opens the GitHub releases page; it doesn't check or install anything automatically.
npm run buildProduces an NSIS installer on Windows, a .dmg on macOS, or an .AppImage on Linux
(unsigned, per the non-goals above), using the icons in assets/.
- Clean launch —
npm start; no window is visible, the tray icon appears, and the terminal shows no errors. - Cross-app capture — select text in Claude Desktop, a Chromium browser (Chrome/ Brave/Edge), VS Code, and Notepad/TextEdit; press the hotkey in each; confirm the exact selected text appears in the overlay's Context block every time. Browsers are the most failure-prone target here — see "Selection capture reliability" above if it comes back empty in one.
- Clipboard is restored (hard gate) — copy something memorable, press the hotkey,
dismiss the overlay (
Esc), then paste (Ctrl+V) somewhere — the original clipboard content must still be there. - First token latency — with
meta/llama-3.1-8b-instructselected, the first streamed token should appear within roughly 2 seconds of pressing Enter. - Esc / focus return —
Eschides the overlay and keyboard focus returns to whatever app was active before; pressing the hotkey again reopens the same conversation (not a blank one). - Always-on-top depth — open the overlay while another window is maximized, and while a video is full-screen; the overlay should stay above both.
- Drag / resize / multi-monitor — drag the titlebar and resize from an edge; try this on a multi-monitor setup with mixed DPI scaling and confirm the window never opens partially off-screen.
- XSS hardening — ask a question that causes the model to echo back
<script>alert(1)</script>or<img src=x onerror=alert(1)>(or paste such text as context) — it must render as inert text, never execute. - Missing key UX — with no API key configured, opening the overlay shows the inline "Add your NVIDIA API key" card, not a stack trace.
- Persistence — change a setting and have a conversation, fully quit the app (tray → Quit), relaunch, and confirm settings and history are unchanged.
- Shortcut unregisters on quit — quit from the tray, then try the hotkey; nothing should happen until the app is relaunched.
- Build —
npm run buildcompletes and produces an installer for your OS indist/.
- All network calls to NVIDIA NIM happen in the main process only. The renderer runs
with
contextIsolation: true,nodeIntegration: false,sandbox: true, and only talks to main through the whitelistedcontextBridgeAPI insrc/preload/index.js— the API key never reaches the renderer or the page's JavaScript context. - The overlay window is created once, hidden, and shown/hidden thereafter — it is never destroyed and recreated, which keeps idle CPU near zero and summon time fast.
markedandDOMPurifyare vendored as plain<script>files insrc/renderer/vendor/(no bundler, no npm renderer dependencies) — all model output is parsed to HTML and sanitized before it touches the DOM.- Every icon in the UI is an inlined SVG (
src/renderer/icons.js, Lucide-derived) — nothing relies on emoji or an icon font, so rendering is consistent across platforms. - Light/dark theme is driven entirely by CSS custom properties scoped to
#app(data-theme="light|dark|system", with aprefers-color-schemefallback for"system"). If you add new top-level UI, give itcolor: var(--text)explicitly —bodyitself sits outside#app's themed scope, so anything that only relies on inheriting frombodywill render in the light-mode color regardless of theme.