Highlights · Overview · Screenshots · Features · Architecture · Engineering · Tech Stack · Getting Started
A multi-window Electron desktop app with offline on-device AI inference, sub-millisecond SQLite FTS5 search, and a custom mouse-gesture detector — running with zero outbound network requests.
- 🧠 Local AI inference — Quantized Flan-T5 running fully on-device via
@huggingface/transformersand the ONNX runtime. No API keys, no network calls. Generates note titles offline. - ⚡ Sub-millisecond full-text search — SQLite FTS5 virtual table kept in sync with the primary
notestable viaAFTER INSERT/UPDATE/DELETEtriggers; ranked snippets across thousands of notes in < 1 ms. - 🪟 Multi-window IPC architecture — Six independent
BrowserWindowtypes (manager, note, shelf, palette, search, onboarding) behind a single typedcontextBridgesurface withcontextIsolation: true. - 🔒 Zero network at runtime — Production Content Security Policy with
connect-src 'none'; the only network operation in the app's lifetime is a one-time, opt-in AI-model download. - 🎯 ~10,000 lines of strict-mode TypeScript across 71 source files. Zero
anytypes. Built solo.
Drift is a floating developer notepad for Windows. Launch it from the system tray and your last note is already open — no splash screen, no onboarding, no chrome. Pin any note above every other window, switch to a 220×110 micro view that hovers in a corner of your screen, or let mouse events pass through entirely while you keep an editable scratchpad over your IDE.
The engineering challenge: build something that feels lightweight while juggling six different window types, a local full-text search index, an offline AI model, a second clipboard, and a mouse-shake gesture detector — all without ever blocking the editor. Drift solves it with a strict main/renderer split, better-sqlite3 + SQLite FTS5 for sub-millisecond search, Hugging Face Transformers.js running a quantized Flan-T5 model entirely on-device, and a typed window.drift.* IPC bridge that keeps all Node and database access out of the renderer.
Built because every "lightweight notepad" eventually became a heavyweight web app with a login screen.
The home base. Browse projects on the left, edit on the right. Drag any note under any other note.
Ctrl+Shift+Spacefrom anywhere. A floating window opens with a fresh note, ready to type. The sweet spot between Manager and Micro.
Always-on-top with optional click-through. Snaps to a corner; gets out of the way of your IDE.
Fuzzy-search every note, switch views, export, toggle pin or transparency — all keyboard.
Stash files, text snippets, URLs, and images for later. Drag items out to any app.
| Feature | Description |
|---|---|
| 📌 Floating always-on-top notes | Pin any note above every other window. Per-note state, persists across launches |
| 🎚️ Four window modes | Normal (manager), Standard (450×275 floating), Micro (220×110 hover), Fullscreen |
| 👻 Click-through mode | Mouse events pass through the editor to the app below; toolbar stays interactive |
| 🗂️ Projects with nested notes | Group related notes, nest arbitrarily deep, drag any note under any other |
| 🔍 Full-text search | SQLite FTS5 across every note's title and content — sub-millisecond on thousands of notes |
| ⌘ Command Palette | Ctrl+K — fuzzy-find notes, switch views, run commands, all keyboard |
| @ @-mention linking | Type @ in any note to inline-link another note. Click to jump |
| 🕘 Per-note version history | Every save snapshots the previous content. Restore from the side panel |
| 📤 Export to Markdown / PDF / Plain Text | Native save dialog, written straight to disk |
| 📋 Clipboard 2 | Ctrl+Alt+C captures, Ctrl+Alt+V pastes from a second clipboard — original stays untouched |
| 🪄 Shake-to-Shelf | Jiggle the mouse, the Shelf opens. Drop files, text, URLs, or images for later |
| 🧠 Local AI title generation | Auto-titles new notes with a quantized Flan-T5 model running fully offline via ONNX |
| 🖼️ Inline image editing | Paste, crop, and resize images directly in the editor — no external tool |
📄 Open .txt files from disk |
File association lets you edit text files with Drift and save back |
| 🚑 Crash recovery | Every keystroke goes to a draft table. If the app dies, content is restored on next launch |
| 🏠 Last-note-on-launch | Reopening the app jumps you straight back into whatever you were editing |
┌─────────────────────────────────────────────────────────────────────┐
│ Renderer Process (React 18) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Manager │ │ Note │ │ Shelf │ │ Palette │ │ Search │ │
│ │ Window │ │ Windows │ │ Window │ │ Window │ │ Window │ │
│ │ (1 of N) │ │ (M of N) │ │ (0/1) │ │ (0/1) │ │ (0/1) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │ │
│ ┌───────────▼───────────┐ │
│ │ window.drift.* │ contextBridge │
│ │ (typed IPC surface) │ contextIsolation: true │
│ └───────────┬───────────┘ nodeIntegration: false │
└──────────────────────────────│──────────────────────────────────────┘
│ ipcRenderer.invoke
┌──────────────────────────────▼──────────────────────────────────────┐
│ Main Process (Node.js / Electron) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ IPC │ │ Windows │ │ Tray │ │ Shake │ │Clipboard │ │
│ │ Handlers │ │ Manager │ │ Menu │ │ Detector │ │ 2 │ │
│ └────┬─────┘ └──────────┘ └──────────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ │ ┌──────────────────────────┘ │ │
│ │ │ globalShortcut registry │ │
│ │ │ Ctrl+Shift+Space, Ctrl+K, Ctrl+Shift+F │
│ │ │ Ctrl+Alt+C/V │
│ │ └────────────────────────────────────────────────┘
│ │ │
│ ┌────▼──────────────────────────────────────────────────────────┐ │
│ │ better-sqlite3 (WAL, FK ON, prepared statements) │ │
│ │ notes · projects · note_history · preferences · shelves │ │
│ │ · shelf_items · crash_recovery · notes_fts (FTS5 virtual) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Local AI Inference (Hugging Face Transformers.js + ONNX) │ │
│ │ Xenova/flan-t5-small · q8 quantized · cached in userData │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
SQLite FTS5 powers search via a virtual notes_fts table kept in sync with the primary notes table through three triggers — AFTER INSERT, AFTER UPDATE, AFTER DELETE. Queries return ranked snippets in under a millisecond on local benchmarks, scaling effortlessly past thousands of notes. WAL journaling (PRAGMA journal_mode = WAL) lets reads happen during writes without blocking the editor, and PRAGMA synchronous = NORMAL gives durable persistence without the cost of full FULL mode.
New notes get auto-titled by Xenova/flan-t5-small — a q8-quantized text-generation model (~80 MB) running on the ONNX runtime through @huggingface/transformers. The model is downloaded once into app.getPath('userData')/models/ on first use, then runs entirely offline forever after. The renderer subscribes to load-progress events via IPC so the UI can show a download indicator on first launch. No API keys, no rate limits, no per-user inference costs.
Every Node, filesystem, and SQLite operation lives in the main process. The renderer never touches fs, child_process, or the database directly. All cross-boundary calls go through a single typed window.drift.* surface exposed via contextBridge, with contextIsolation: true and nodeIntegration: false on every BrowserWindow. Production builds get a Content Security Policy with connect-src 'none' — the app makes zero outbound HTTP requests at runtime.
The global Ctrl+Shift+Space hotkey creates a new note from anywhere — even when no Drift window is open. Handling the cold-start case meant building a small state machine: persist the target view to the preferences table before creating the manager window, then queue the quick:note-created event for did-finish-load. Warm starts push the event immediately and show the window on the next tick. This eliminates the flash of stale UI between window-open and renderer-mount that plagues most Electron apps.
A 32 ms interval polls screen.getCursorScreenPoint() and keeps the last 400 ms of cursor positions in a sliding window. A shake is detected when ≥ 3 direction reversals occur with ≥ 60 px of total path length within the window. A 700 ms cooldown prevents repeat triggers. The result: a tactile, no-hotkey-needed way to summon the Shelf for stashing files mid-task — built in ~75 lines of TypeScript with no dependencies.
Every keystroke writes the in-memory content to a crash_recovery table keyed by note ID. On launch, the manager queries for any unrecovered drafts and prompts the user to restore. Normal save clears the entry. This catches force-quits, OS crashes, and unplugged batteries — content is never more than one keystroke away from disk.
Drift is a desktop app that handles local files — security mostly means hardening the Electron attack surface and protecting against the most common Electron vulnerabilities.
contextIsolation: trueandnodeIntegration: falseon every BrowserWindow- Preload script is the only bridge — exposes a typed, narrow API; no
remotemodule - Production CSP with
connect-src 'none'— no outbound network at runtime - Singleton-instance lock — second launches focus the existing window instead of spawning a duplicate process
- Foreign keys enforced at the SQLite layer, with
ON DELETE CASCADEfor parent/child notes - WAL journaling with
synchronous = NORMAL— durable without blocking the UI thread - External links open via
shell.openExternal()— never inside a Drift window - No telemetry — Drift makes zero outbound network requests; the only network operation is a one-time AI model fetch, opt-in by usage
| Technology | Purpose |
|---|---|
| Electron 30 | Desktop runtime, multi-window orchestration |
| React 18 | Renderer UI framework |
| TypeScript 5 | Strict mode, zero any |
| Tailwind CSS | Cursor-inspired dark palette, utility-first |
| Vite + electron-vite | Dev server with HMR, production bundling |
| Technology | Purpose |
|---|---|
| CodeMirror 6 | Editor core, markdown rendering, autocomplete |
| better-sqlite3 11 | Synchronous SQLite bindings — fast, simple, durable |
| SQLite FTS5 | Full-text search virtual tables with auto-sync triggers |
| @huggingface/transformers | Local on-device AI inference (ONNX runtime) |
| dnd-kit | Drag-and-drop tree reparenting |
| uuid | Stable note and project identifiers |
| Technology | Purpose |
|---|---|
| electron-builder | Windows NSIS installer + portable .exe |
| @electron-toolkit/utils | Shared Electron helpers |
- Node.js 18+
- Windows 10 or 11
- Visual Studio Build Tools — needed for
better-sqlite3to compile native bindings on first install
git clone https://github.com/riyonp23/Drift.git
cd Drift
npm install
npm run devnpm run dev starts Vite with HMR and launches Electron pointed at the dev server.
npm run build # signed NSIS installer + portable .exe → dist/
npm run build:dir # unpacked app → dist/win-unpacked/ (faster, for local testing)Pre-built Windows installers are published on the Releases page.
Drift/
├── src/
│ ├── main/ Electron main process (Node, SQLite, IPC)
│ │ ├── index.ts App entrypoint, global shortcuts, single-instance lock
│ │ ├── windows.ts Manager / Note / Shelf / Palette / Search / Onboarding window factories
│ │ ├── tray.ts System tray icon and context menu
│ │ ├── ipc.ts Main IPC router — all renderer requests land here
│ │ ├── ipc/
│ │ │ ├── shelfHandlers.ts Shelf CRUD + drag-out file operations
│ │ │ └── paletteHandlers.ts Command palette state sync
│ │ ├── db.ts better-sqlite3 schema, migrations, prepared statements
│ │ ├── dbShelf.ts Shelf and shelf-item queries
│ │ ├── ai.ts Local Hugging Face Transformers.js pipeline
│ │ ├── clipboard2.ts Second-clipboard capture/paste with global hotkeys
│ │ └── shakeDetector.ts Mouse-shake detection over 400 ms sliding window
│ │
│ ├── renderer/ React app (multi-window, shared entry)
│ │ ├── main.tsx Window-type router (manager / note / shelf / etc.)
│ │ ├── ManagerApp.tsx Sidebar + project tree + editor pane
│ │ ├── NoteApp.tsx Standalone floating note window
│ │ ├── PaletteApp.tsx Standalone command palette window
│ │ ├── WelcomeApp.tsx First-launch onboarding
│ │ ├── CommandPalette.tsx Fuzzy search + commands
│ │ ├── ErrorBoundary.tsx React error wall
│ │ ├── icons/ Lucide-style SVG icon set (inline, no font)
│ │ ├── manager/ Manager-window components and hooks
│ │ │ ├── ProjectTree.tsx
│ │ │ ├── NoteTreeItem.tsx
│ │ │ ├── UnifiedEditorPane.tsx
│ │ │ ├── TabBar.tsx
│ │ │ ├── ContextMenu.tsx
│ │ │ ├── TourOverlay.tsx
│ │ │ └── hooks/ useProjectTree, useCommandPalette, useAiStatus, ...
│ │ ├── note/ CodeMirror editor + toolbar + overlays
│ │ │ ├── NoteEditor.tsx CodeMirror setup, extensions, save-on-blur
│ │ │ ├── NoteToolbar.tsx
│ │ │ ├── HistoryPanel.tsx
│ │ │ ├── markdownRenderer.tsx
│ │ │ ├── linkPlugin.ts @-mention + note-link decorations
│ │ │ ├── urlAutolink.ts
│ │ │ ├── cropOverlay.ts
│ │ │ ├── resizeOverlay.ts
│ │ │ └── ...
│ │ ├── shelf/ Shelf window + item rows/cards
│ │ └── search/ Search window
│ │
│ ├── preload/
│ │ ├── index.ts contextBridge — exposes window.drift.*
│ │ └── index.d.ts Type declarations for the renderer
│ │
│ └── shared/
│ └── types.ts Note, Project, Shelf, WindowMode, DriftAPI, ...
│
├── assets/
│ ├── icon.png App icon (256×256)
│ └── tray.png Tray icon (16×16)
├── docs/
│ ├── banner.svg
│ └── screenshots/
├── electron-builder.yml Windows NSIS + portable build config
├── electron.vite.config.ts Vite config for main/preload/renderer
├── tailwind.config.js Cursor-inspired palette tokens
├── CLAUDE.md Developer notes & design system
├── LICENSE MIT
└── README.md
| Shortcut | Action |
|---|---|
Ctrl + Shift + Space |
Quick-capture new note (creates and focuses) |
Ctrl + Shift + F |
Open the search window |
Ctrl + K |
Open the command palette |
Ctrl + Alt + C |
Capture into Clipboard 2 |
Ctrl + Alt + V |
Paste from Clipboard 2 |
| Shortcut | Action |
|---|---|
Ctrl + 1 / 2 / 3 / 4 |
Switch to Normal / Standard / Micro / Fullscreen view |
Ctrl + E |
Toggle markdown source ↔ rendered preview |
Ctrl + S |
Save current note (also auto-saves on blur) |
@ |
Open the @-mention dropdown to link another note inline |
| Right-click on an image | Crop / resize / replace / delete |
| Drag a note in the tree | Reparent under another note or project |
| Gesture | Action |
|---|---|
| Shake the mouse cursor | Open the Shelf |
| Drag-out from Shelf | Drop a stashed file into any app |
| Double-click tray icon | Open Drift to the last note |
- macOS and Linux builds
- Encrypted note option (per-note password)
- Configurable global hotkeys
- Tag system layered over projects
- Plugin API for editor extensions
Riyon Praveen — Computer Science, University of South Florida · Honors College · Class of 2027
Drift is a solo build. Every line — main process, renderer, IPC, SQLite schema, AI integration, gesture detection, build pipeline — is mine.
MIT — do whatever you want with it.




