Skip to content

Latest commit

 

History

15 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SideNote Desktop

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.

Install

npm install
npm start

npm 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.

NVIDIA API key setup

  1. Get a key at build.nvidia.com (NVIDIA NIM / integrate.api.nvidia.com).
  2. Open the tray icon → Settings, or click Add your NVIDIA API key inside the overlay.
  3. 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.
  4. Click Test key any time to fire a 1-token request and confirm it's valid.
  5. The key is stored locally via Electron's safeStorage when the OS supports it (Windows DPAPI, macOS Keychain, or the Linux Secret Service). If safeStorage is unavailable, the key falls back to electron-store's encryptionKey option, 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.
  6. 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.

Settings

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.

Selection capture reliability

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+C is 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.

macOS: Accessibility permission

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.

Linux / Wayland limitation

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.

What's NOT included (v2.0, see PRD §12)

  • 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 build produces 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.

Building an installer

npm run build

Produces 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/.

Manual test checklist (maps to PRD §10 Acceptance Criteria)

  1. Clean launch — npm start; no window is visible, the tray icon appears, and the terminal shows no errors.
  2. 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.
  3. 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.
  4. First token latency — with meta/llama-3.1-8b-instruct selected, the first streamed token should appear within roughly 2 seconds of pressing Enter.
  5. Esc / focus return — Esc hides the overlay and keyboard focus returns to whatever app was active before; pressing the hotkey again reopens the same conversation (not a blank one).
  6. 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.
  7. 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.
  8. 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.
  9. Missing key UX — with no API key configured, opening the overlay shows the inline "Add your NVIDIA API key" card, not a stack trace.
  10. Persistence — change a setting and have a conversation, fully quit the app (tray → Quit), relaunch, and confirm settings and history are unchanged.
  11. Shortcut unregisters on quit — quit from the tray, then try the hotkey; nothing should happen until the app is relaunched.
  12. Build — npm run build completes and produces an installer for your OS in dist/.

Architecture notes

  • 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 whitelisted contextBridge API in src/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.
  • marked and DOMPurify are vendored as plain <script> files in src/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 a prefers-color-scheme fallback for "system"). If you add new top-level UI, give it color: var(--text) explicitly — body itself sits outside #app's themed scope, so anything that only relies on inheriting from body will render in the light-mode color regardless of theme.

About

SideNote — a universal ask-anywhere Electron overlay. Select text in any app, hit a global hotkey, and get a streamed answer from NVIDIA NIM without losing focus.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages