A local-first desktop app for project notes and day planning. Built with Electron, React, TypeScript, and SQLite.
Juggle less, accomplish more. Capture notes, plan your day, and keep track of what matters.
Capture and organize project-related information:
- Text Captures: Quick text snippets with optional details
- Image Captures: Drag & drop or paste images from clipboard
- Audio Captures: Record audio directly in the app with automatic transcription
- Open Loop Tracking: Flag captures as "open loops" to track unfinished work
- AI-Powered Summaries: Generate project summaries based on recent captures and tasks
Plan your day with smart time blocking:
- Calendar Integration: Import
.icsfiles or paste ICS content - Task Management: Create and manage tasks with priorities and time estimates
- Automatic Planning: Generate time blocks based on calendar events and open tasks
- Manual Editing: Adjust generated plans to fit your needs
- Persistent Plans: Save and retrieve plans for any date
- Electron: Desktop app framework
- Vite: Fast build tool and dev server
- React + TypeScript: UI framework with type safety
- SQLite + Drizzle ORM: Local database with typed queries
- Worker Threads: Background processing for transcription and AI tasks
- ical.js: Calendar parsing
- Node.js 18+ and npm
- For local transcription: whisper.cpp binary and model file
npm installnpm run devThis starts:
- Vite dev server on
http://localhost:5173 - Electron app with hot reload
npm run buildCreates distributable packages in dist/ directory.
npm testnpm run lintLoops supports pluggable AI providers for transcription and summaries. Configure in Settings:
Remote (OpenAI Whisper API)
- Requires API key
- Supports OpenAI-compatible endpoints
- Configuration:
- API Key (stored securely)
- Base URL (default:
https://api.openai.com/v1)
Local (whisper.cpp)
- Runs transcription locally
- No API costs
- Configuration:
- Path to whisper.cpp binary
- Path to model file (e.g.,
ggml-base.en.bin)
Remote (OpenAI-compatible)
- Uses chat completion API for AI-generated summaries
- Works with OpenAI, Ollama, and other OpenAI-compatible endpoints
- Configuration:
- API Key (stored securely) - use any string for Ollama
- Base URL (default:
https://api.openai.com/v1)- For Ollama:
http://localhost:11434/v1
- For Ollama:
- Model (default:
gpt-4o-mini)- For Ollama: your model name (e.g.,
llama2,mistral)
- For Ollama: your model name (e.g.,
Disabled (Local Fallback)
- Generates simple local summaries without AI
- No API required
- Shows basic counts and recent items
Using Ollama (Local LLM) See OLLAMA_SETUP.md for complete setup guide with local transcription and summaries.
Configure day planning behavior:
- Working Hours: Start and end times (default: 9:00 - 17:00)
- Minimum Block Size: Smallest time block in minutes (default: 30)
- Summary Lookback: Days of history to include in summaries (default: 14)
All data is stored locally in your user data directory:
- Windows:
%APPDATA%\open-loops\app-data - macOS:
~/Library/Application Support/open-loops/app-data - Linux:
~/.config/open-loops/app-data
app-data/
βββ loops.db # SQLite database
βββ images/ # Captured images by project
β βββ {projectId}/
βββ audio/ # Audio recordings by project
βββ {projectId}/
projects: Project metadatacaptures: Text, image, and audio capturescapture_flags: Open loop flags for capturestranscriptions: Audio transcription resultstasks: Task list with priorities and estimatesproject_summaries: Cached AI summariescalendar_events: Imported calendar eventsday_plans: Saved day plansday_plan_blocks: Time blocks within plansjob_queue: Background job processingsettings: App configuration
- Click the "+" button in the sidebar
- Enter a project name
- Click "Create"
Text Capture:
- Open a project
- Click "+ Text"
- Enter title and optional details
- Click "Create"
Image Capture:
- Drag and drop image files onto the project view
- Or copy an image and paste (Ctrl/Cmd + V) in the project view
Audio Capture:
- Click "π€ Record"
- Speak into your microphone
- Click "βΉ Stop" when finished
- Transcription will start automatically (if configured)
- Open a project
- Click "Regenerate" in the Project Summary panel
- Wait for the summary to generate (appears in 3-5 seconds)
Note: Summaries require an LLM provider to be configured. In "Disabled" mode, you'll get a basic local summary.
Import Calendar:
- Go to "Today Planner" in sidebar
- Click "Import .ics File" to select a file
- Or click "Paste ICS Text" to paste calendar data
Create Tasks:
- Click "+ Task"
- Enter title, estimate (minutes), and priority (1-5)
- Click "Create"
Generate Plan:
- Select a date
- Click "Generate Plan"
- Review the generated time blocks
- Edit blocks as needed (click "Edit" on any block)
- Click "Commit Plan" to save
Access Settings from the sidebar to configure:
- Transcription provider (remote or local)
- LLM provider for summaries
- API keys (stored securely)
- Working hours and planning preferences
- Main Process (
src/main/main.ts): Core app logic, IPC handlers, database access - Renderer Process (
src/renderer/): React UI, no direct Node.js access - Preload Script (
src/main/preload.ts): Secure IPC bridge with contextIsolation
- Context isolation enabled
- Sandbox mode enabled
- No Node.js in renderer
- Strict IPC boundaries
- Secrets stored separately from database (note: MVP stores in settings table; production should use keytar)
- Worker threads handle transcription and summary jobs
- Job queue in SQLite coordinates work
- Main thread polls for pending jobs
- Results written back to database
The planner uses a greedy allocation algorithm:
- Parse calendar events for the selected date
- Find free windows between events within working hours
- Filter windows smaller than minimum block size
- Sort tasks by priority (desc), due date (asc), estimate (desc)
- Allocate tasks to windows:
- Fit entire task if possible
- Split task across windows if needed
- Fill remaining time with buffer blocks
- No Calendar OAuth: Must import .ics files manually. Future: OAuth for Google/Microsoft.
- No Multi-Device Sync: Data is local only. Future: Optional cloud sync.
- No Keytar Integration: Secrets stored in settings table. Future: OS keychain via keytar.
- Basic Planning: Heuristic algorithm, not optimization. Future: User feedback and learning.
- No Export: Can't export plans back to .ics. Future: Export functionality.
- No Drag-Drop Editing: Blocks must be edited via modal. Future: Timeline UI with drag/drop.
- Audio transcription requires either API key or local whisper.cpp setup
- Local whisper.cpp path must be configured manually
- Image preview not implemented (shows full size)
- No undo/redo functionality
- No search across captures
- Summary generation is fire-and-forget (no progress indication beyond initial message)
Remote provider:
- Check API key is set correctly in Settings
- Verify base URL is correct (include
/v1for OpenAI) - Check network connectivity
Local provider:
- Ensure whisper.cpp binary path is correct
- Verify model file exists at specified path
- Test whisper.cpp from command line first
- Check that LLM provider is configured (or use "Disabled" mode)
- Verify API key and base URL
- Try "Disabled" mode for basic local summary
- Ensure .ics file is valid (test with another calendar app)
- Check file uses UTF-8 encoding
- Try pasting ICS content directly instead of file import
- Database file is at
{userData}/app-data/loops.db - Backup database before manual edits
- Delete database file to reset (loses all data)
loops/
βββ src/
β βββ main/ # Electron main process
β β βββ main.ts # Entry point, IPC setup
β β βββ preload.ts # IPC bridge
β β βββ db/ # Database layer
β β βββ ai/ # AI provider interfaces
β β βββ workers/ # Background job system
β β βββ planning/ # Calendar and planning logic
β βββ renderer/ # React UI
β β βββ App.tsx # Main app component
β β βββ components/ # Reusable components
β β βββ views/ # Page views
β βββ types/ # Shared TypeScript types
βββ drizzle/ # Database migrations
βββ dist/ # Built renderer
βββ dist-electron/ # Built electron processes
βββ package.json
-
Add handler in
src/main/main.ts:ipcMain.handle('namespace:action', async (_, arg) => { // implementation })
-
Add to preload API in
src/main/preload.ts:namespace: { action: (arg: Type) => ipcRenderer.invoke('namespace:action', arg) }
-
Use in renderer:
const result = await window.electron.namespace.action(arg)
Tests use Vitest with in-memory SQLite databases. Run tests with:
npm testAdd tests in *.test.ts files alongside the code they test.
MIT
This is an MVP. Contributions welcome for:
- OAuth calendar integration
- Keytar secret storage
- Better planning algorithms
- Timeline UI for plan editing
- Search functionality
- Export features
- OAuth integration (Google Calendar, Microsoft Outlook)
- Cloud sync with end-to-end encryption
- Mobile companion app (view-only)
- Advanced planning with ML-based time estimates
- Team collaboration features
- Integration with task management tools (Todoist, etc.)
- Voice-to-text for text captures
- OCR for image captures
- Markdown support in captures
- Tags and advanced filtering
- Weekly/monthly planning views
- Time tracking integration
Built with β€οΈ for focused work and closed loops.