Robin is a mac-first menu bar sidekick built with Electron. V1 focuses on two workflows:
Search: BYOK Perplexity-powered web-grounded answersLocal: Ollama-backed local chat
- Electron
41.0.2 - Electron Forge
7.10.2 - React + TypeScript
- Tailwind CSS
- Install Node.js
22+. If you do not use Homebrew or nvm, you can bootstrap a local copy:
bash ./scripts/bootstrap-node.sh- Install dependencies:
npm install- Start the app:
npm start- Typecheck before packaging:
npm run typecheckmake help
make setup
make dev
make test
make package
make install
make clean-dev
make create-release VERSION=0.1.0If you prefer npm directly:
npm run setup
npm run dev
npm run test
npm run package:mac
npm run make:mac
npm run install:macmake dev now auto-stops stale Robin dev processes and picks free Forge ports, so reruns are safe even after interrupted sessions.
In dev mode, Robin runs as a menu bar app by default: dock icon hidden, tray title fallback enabled, and panel hide-on-blur on.
If the panel disappears after clicking away, open it again from the Robin menu bar item or use CommandOrControl+Shift+Space.
- Keep
make devrunning while editing. - Renderer changes (React/CSS/assets) update live.
- If you change main/preload code, type
rsin the dev terminal to restart the Electron main process.
Replace brand files in assets:
- image.png for the top
Robinbrand button.
UI action icons now come from Hugeicons (@hugeicons/react + @hugeicons/core-free-icons).
Node is pinned in .nvmrc and .node-version. After bootstrap, the repo-level wrappers scripts/nodew and scripts/npmw will use the local runtime automatically.
Robin currently has a strong smoke-test workflow rather than a full automated test suite.
- Run
npm install - Run
npm run test - Run
npm run dev - Verify manually:
- Tray icon appears
CommandOrControl+Shift+Spacetoggles the panelEschides the panel- Onboarding allows Perplexity key entry
- Ollama detection reports the correct state
- Search mode streams a response after you configure a valid Perplexity key
- Local mode streams a response when Ollama is running with a pulled model
- Quit and relaunch the app; conversations should still be there
For a true clean-room retest on macOS:
make clean-dev
make dev- macOS is the primary UX target in v1.
- No hosted inference subsidy is included.
- The app stores conversations in local JSON files under Electron's user data directory.
- API keys are encrypted with Electron
safeStorage.
make makeThis creates distributable artifacts in out/.
Robin now has a SpeakType-style release flow:
- Export Apple signing env vars:
export APPLE_SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"
export APPLE_ID="you@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="TEAMID"You can also copy values from .env.release.example.
- Prepare the release locally:
make create-release VERSION=0.1.0- Publish it to GitHub Releases:
make deploy-release VERSION=0.1.0- Or do both:
make release VERSION=0.1.0For the full checklist, see RELEASING.md.
Robin uses the standard Electron architecture with a main process (Node.js) handling system integration (tray, global shortcuts, IPC) and a renderer process (React) for the UI. Communication is via a typed RobinBridge exposed through contextBridge in the preload script — the renderer never accesses Node APIs directly.
All data is stored as JSON files in Electron's userData directory:
settings.json— app config, provider preferences, model selectionsthreads.json— full conversation history with messages and attachmentstodos.json— todo items with ordering
API keys are encrypted at rest using Electron's safeStorage API (macOS Keychain / Windows DPAPI).
Robin supports multiple AI providers through a uniform streaming interface:
- OpenAI — GPT models with mode support (chat/responses)
- Anthropic — Claude models
- Google — Gemini models with native image support
- Perplexity — Search-grounded answers with citations
- OpenRouter — Proxy to any model via user-provided model IDs
- Ollama — Local models with automatic detection and management
Each provider implements streamReply() with delta callbacks. The ProviderService orchestrates thread management, model resolution, and provider dispatch.
Before sending messages to any provider, Robin optimises the payload:
- Image stripping — only the most recent user message retains image attachments. Older images remain in the thread for display but are excluded from API calls to avoid payload bloat.
- Context truncation — if total message content exceeds ~100K characters (~25K tokens), the oldest messages are dropped while preserving at least the last 4 messages (2 turns).
- Model-agnostic threads — threads store raw messages without model metadata. The model selection is applied at call time, so you can switch providers mid-conversation.
The sidebar uses a 2x2 navigation grid (Chats, Todos, Notes, Calendar) that switches the sidebar content panel. This pattern supports adding new assistant features without changing the core chat architecture. Todos are fully functional with drag-and-drop reordering; Notes and Calendar are placeholder tiles for future development.
- Add real renderer/main-process automated tests
- Add a richer settings surface for provider management
- Add update distribution after the first beta