Skip to content

riyonp23/Drift

Repository files navigation


⭐ Highlights

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/transformers and 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 notes table via AFTER INSERT/UPDATE/DELETE triggers; ranked snippets across thousands of notes in < 1 ms.
  • 🪟 Multi-window IPC architecture — Six independent BrowserWindow types (manager, note, shelf, palette, search, onboarding) behind a single typed contextBridge surface with contextIsolation: 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 any types. Built solo.

📌 Overview

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.


📸 Screenshots

Manager — projects, nested notes, drag-to-reparent

The home base. Browse projects on the left, edit on the right. Drag any note under any other note.

Manager

Standard view — 450×275 quick-capture

Ctrl+Shift+Space from anywhere. A floating window opens with a fresh note, ready to type. The sweet spot between Manager and Micro.

Standard view

Micro view — 220×110 floating editor

Always-on-top with optional click-through. Snaps to a corner; gets out of the way of your IDE.

Micro view

Command Palette — Ctrl+K, jump anywhere

Fuzzy-search every note, switch views, export, toggle pin or transparency — all keyboard.

Command Palette

Shelf — shake the mouse to summon it

Stash files, text snippets, URLs, and images for later. Drag items out to any app.

Shelf


✨ Features

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

🏗️ Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                       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     │  │
│  └───────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘

🔬 Key Engineering Decisions

Sub-millisecond search on thousands of notes

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.

On-device AI inference, zero API costs

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.

Strict main/renderer split with typed IPC

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.

Multi-window IPC with cold-start race handling

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.

Custom mouse-shake gesture detection

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.

Crash-recovery drafts table

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.


🔒 Security & Hardening

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: true and nodeIntegration: false on every BrowserWindow
  • Preload script is the only bridge — exposes a typed, narrow API; no remote module
  • 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 CASCADE for 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

🛠️ Tech Stack

Application

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

Editor & Data

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

Build & Distribution

Technology Purpose
electron-builder Windows NSIS installer + portable .exe
@electron-toolkit/utils Shared Electron helpers

🚀 Getting Started

Prerequisites

  • Node.js 18+
  • Windows 10 or 11
  • Visual Studio Build Tools — needed for better-sqlite3 to compile native bindings on first install

Build from source

git clone https://github.com/riyonp23/Drift.git
cd Drift
npm install
npm run dev

npm run dev starts Vite with HMR and launches Electron pointed at the dev server.

Build a Windows installer

npm run build      # signed NSIS installer + portable .exe → dist/
npm run build:dir  # unpacked app → dist/win-unpacked/ (faster, for local testing)

Download a release build

Pre-built Windows installers are published on the Releases page.


📂 Project Structure

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

⌨️ Keyboard Shortcuts

Global (work from any app)

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

In-app

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

Mouse

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

🗺️ Roadmap

  • macOS and Linux builds
  • Encrypted note option (per-note password)
  • Configurable global hotkeys
  • Tag system layered over projects
  • Plugin API for editor extensions

👤 Author

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.

LinkedIn GitHub


📄 License

MIT — do whatever you want with it.


Built because every "lightweight notepad" eventually became a heavyweight web app with a login screen.

About

A floating developer notepad for Windows — built in Electron + React + TypeScript with offline on-device AI inference, sub-millisecond SQLite FTS5 search, and a multi-window IPC architecture. Zero network requests at runtime.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages