A zero-click, hover-activated clipboard shelf and native OS file-transfer hub for the desktop.
Lives invisibly on the left edge of your screen. Approach it, and it opens. Drag anything out — into Photoshop, Word, Slack, Explorer, anywhere.
Quick Start · Demos · How It Works · Architecture · Security · Roadmap · Contributing
Built with Electron · React · TypeScript · Framer Motion · Zustand
License: Apache-2.0 · Status: Public Beta
Every clipboard manager on the market breaks your flow. You copy something, switch apps, paste, then hunt through Win+V history with arrow keys or dig into a tray menu. Multi-step. Modal. Slow.
Edge-Drop removes the friction. It anchors to the leftmost pixel of your monitor as a transparent, always-on-top, click-through surface. When your cursor approaches the edge, the shelf springs open. Drag images, file stacks, rich text, and HTML bundles out of it — directly into whatever desktop app you're already using. No shortcuts. No window switching. No modal dialogs.
It is built for the developer and creative workflow where you constantly juggle screenshots, code snippets, file paths, design assets, and reference links between many windows at once.
All demos are silent autoplay loops. Hover to scrub, right-click → open in new tab for full size.
1. Welcome to Edge-Dropplaceholder_welcome.mp4 |
2. Collect Anythingplaceholder_copy.1.mp4 |
3. Drag & Drop Anywhereplaceholder_drag.mp4 |
4. Explore File Stacksplaceholder_stacks.mp4 |
5. Ungroup & Split Stacksplaceholder_ungroup.mp4 |
6. Combine & Merge Itemsplaceholder_merge.mp4 |
- Node.js v18 or higher
- OS: Windows 10/11 (uses Win32 OLE drag pipelines and transparent-window cursor polling)
git clone https://github.com/Deepender25/Edge-Drop.git
cd Edge-Drop
npm install
npm run dev # launches Electron + Vite HMRnpm run typecheck # runs tsc --noEmit against both node and web configsnpm run package # outputs an NSIS .exe to /distNote
On Windows, if packaging fails with EBUSY: resource busy or locked, close any running Edge-Drop instances first: taskkill /F /IM electron.exe /T.
Edge-Drop is an Electron app split into three strictly isolated processes — Main (Node.js, OS access), Preload (typed sandbox bridge), and Renderer (React UI). They communicate over a fully typed IPC contract. No string channel names, no any payloads.
The shelf stays hidden as a frameless, transparent, click-through BrowserWindow anchored at x=0. When collapsed, all mouse events pass through to the apps beneath it — your desktop is 100% usable. Detection happens in the Main process via a 16ms screen.getCursorScreenPoint() poll, because Windows transparent windows silently drop pointermove forwarding.
A dead-band hysteresis state machine prevents the shelf from flickering open/closed when your cursor hovers near its boundary:
| Threshold | Value | Meaning |
|---|---|---|
| Trigger Zone | x ≤ 3px |
A 3-pixel strip on the left edge starts a 120ms dwell timer |
| Keep-Open | x ≤ 255px |
Cursor clearly inside the blade → cancel any close timer |
| Dead Band | 255px < x ≤ 290px |
Micro-tremors here are ignored — no action |
| Start-Close | x > 290px |
Cursor clearly outside → 250ms grace timer begins |
This is the kind of detail that separates a "looks nice" demo from a tool you can actually live with.
The ClipboardWatcher polls the OS clipboard every 600ms. To detect change without re-encoding images on every tick, it computes a cheap content signature:
- Files → joined path list
- Text → the text itself
- Images → an FNV-1a hash over ~400 sampled bytes of the raw BGRA bitmap (dimensions + hash)
The previous naive approach of comparing toPNG().length was both expensive (re-encoded the whole image each tick) and broken (two different 1920×1080 screenshots of similar complexity produced identical byte counts → the second was silently dropped). The FNV-1a sampler is O(400) regardless of image size and has astronomically low collision probability.
It also respects privacy flags. Clipboard formats from password managers and dictation tools — ExcludeClipboardContentFromMonitorProcessing, ClipboardViewerIgnore, CanIncludeInClipboardHistory=0, KeePassClipFormat, com.bitwarden.concealed, etc. — are matched case-insensitively and skipped entirely.
Standard HTML5 drag events cannot hand file handles to external desktop software. Edge-Drop intercepts the renderer's dragstart, sends a fire-and-forget IPC (item:start-drag) to the Main process, which stages the item's content as a temp file and calls webContents.startDrag({ file, icon }). The OS then renders a native drag ghost and handles the drop into Photoshop, Word, Explorer, or any other app — exactly as if you had dragged the file from Explorer itself.
Custom drag icons are generated on the fly: stacked card PNGs for file bundles (with a count badge), glassmorphic quote cards for text, real image thumbnails for images. Rendered via @resvg/resvg-js, cached, and pre-warmed on startup so the first drag is instant.
When you re-copy existing content, Edge-Drop doesn't add a duplicate — it bumps the item to position 0, increments its hitCount badge, and refreshes its timestamp. Multi-file drag-ins and multi-image copies auto-group into expandable 3D card stacks (max 10 per stack). Drag any item card over another to merge them into a bundle; double-click to expand and drag a sub-item to the left edge to split it back out.
graph TD
subgraph OS ["Operating System"]
WinClip["Win32 System Clipboard"]
WinFS["Local Filesystem / AppData"]
OLE["OS OLE Drag & Drop Pipeline"]
end
subgraph Main ["Electron Main Process (Node.js)"]
ClipWatch["ClipboardWatcher — 600ms poll, FNV-1a signatures"]
Store["ItemStore — atomic JSON + per-image PNG files"]
Tray["System Tray & Native Menu"]
WinMgr["BrowserWindow + 16ms Cursor Edge Poller"]
end
subgraph Bridge ["Typed Preload Bridge (contextIsolation)"]
API["contextBridge → window.edge"]
end
subgraph Renderer ["React Renderer"]
Zustand["Zustand store"]
Hooks["useEdgeHover · useDragOut"]
UI["Panel · ItemList · Fluid Bundles"]
end
WinClip <-->|"polls every 600ms"| ClipWatch
ClipWatch -->|"state:items IPC"| API
Store <-->|"atomic JSON read/write"| WinFS
WinMgr <-->|"window:cursor-edge · window:set-interactive"| API
UI -->|"item:start-drag IPC"| API
API -->|"OLE native handle"| OLE
API <-->|"typed handlers & events"| Zustand
Zustand <--> UI
stateDiagram-v2
[*] --> Closed: startup, x=0, click-through
state Closed {
[*] --> PollingEdge
PollingEdge --> TriggerZone: cursor x ≤ 3px in hot zone
TriggerZone --> DwellTimer: linger ≥ 120ms
}
state Open {
[*] --> Interactive: setInteractive(true)
Interactive --> HysteresisCheck: 16ms main poll
HysteresisCheck --> Interactive: x ≤ 255px (inside blade)
HysteresisCheck --> GraceTimer: x > 290px OR left y-bounds
GraceTimer --> Interactive: cursor returns within 250ms
}
Closed --> Open: dwell timer expires
Open --> Closed: grace timer expires
sequenceDiagram
participant OS as System Clipboard
participant CW as ClipboardWatcher
participant IS as ItemStore (disk)
participant R as React UI
loop Every 600ms
CW->>OS: read available formats
alt Files (Win32 HDROP / FileNameW)
CW->>OS: PowerShell GetFileDropList (bypasses single-file limit)
else Image
CW->>OS: clipboard.readImage → raw BGRA
CW->>CW: FNV-1a hash over ~400 sampled bytes
else Text / URL / HTML
CW->>OS: clipboard.readText / readHTML
end
CW->>IS: lookup signature
alt Duplicate
IS->>IS: bump hitCount, move to front, update timestamp
else New
IS->>IS: prepend, enforce historyLimit, evict oldest unpinned
end
IS-->>R: broadcast state:items
end
sequenceDiagram
participant U as User
participant R as React Card
participant M as Electron Main
participant OS as OS / External App
Note over U,OS: Drag OUT — native OLE
U->>R: dragstart on item tile
R->>R: preventDefault (kill HTML5 ghost)
R->>M: send item:start-drag (fire & forget)
M->>M: stage content → temp file (.png / .txt / real paths)
M->>OS: webContents.startDrag({ file, icon })
OS->>U: native drag ghost → drop anywhere
Note over U,OS: Drag IN — file drop
U->>R: drag files onto panel edge
R->>M: webUtils.getPathForFile
M->>M: validate paths, create bundle, broadcast
Zero-click edge hover
- Frameless, transparent, always-on-top
BrowserWindowanchored atx=0 - 100% click-through when collapsed — desktop stays fully usable
- 16ms Main-process cursor poll (bypasses broken Windows transparent-window
pointermoveforwarding) - Configurable hot-zone height (25% / 40% / 60% of screen) and blade height (40% – 100%)
Multi-format clipboard engine
- Captures plain text, URLs, rich HTML, raw images, and multi-file selections
- Win32
FileNameW/ HDROP parsing via PowerShell to bypass Electron's single-file limit - Respects password-manager and dictation-tool privacy flags (case-insensitive matching)
- Smart deduplication — re-copies bump
hitCountand move the item to the top - Incognito mode — one click suspends polling for sensitive data
Native OS drag & drop
webContents.startDrag()hands real file handles to external apps- Custom drag icons: stacked card PNGs with count badges, glassmorphic text cards, real image thumbnails
- Drag-in: drop files onto the shelf to add them; drag-out: drop anywhere — Photoshop, Word, Explorer, Slack
- Pre-warmed icon cache so the first drag is instant
Fluid collections & stacks
- Auto-group multi-file drag-ins and multi-image copies into 3D card stacks (max 10)
- Drag a card over another card to merge them into a bundle
- Double-click to expand, drag a sub-item to the left edge to split it back out
- Type-safe merge rules: images only merge with images, files with files (text never groups)
UI / UX
- Frosted-glass macOS aesthetic — deep black,
backdrop-filter: blur(20px), hairline borders - Framer Motion spring physics with synchronized elastic overshoot on open
- Custom SVG connection flares that scale with the blade
- Scroll gradient masks top & bottom to fade items into black
- Monochrome pin / multiplier badges for maximum legibility
- Reduce-motion setting for accessibility
Edge-Drop touches the OS clipboard, the filesystem, and the Win32 OLE drag pipeline — so the security posture is intentional, not optional.
| Control | Implementation |
|---|---|
| Process isolation | contextIsolation: true · nodeIntegration: false · sandbox: true on both windows |
| Typed IPC | shared/ipc.ts defines InvokeMap, EventMap, SendMap — channel names and payload types are statically checked on both sides |
| Privacy-aware clipboard | Honors ExcludeClipboardContentFromMonitorProcessing, ClipboardViewerIgnore, CanIncludeInClipboardHistory, CanUploadToCloudClipboard, plus 1Password / Bitwarden / KeePass concealed formats |
| Atomic persistence | JSON index written via temp-file + rename; image bytes stored as per-id PNG files |
| Dev-safe startup | app.setLoginItemSettings is gated by app.isPackaged — dev builds never touch the Windows Registry |
| External links | setWindowOpenHandler forces all window-open requests to shell.openExternal — no in-app navigation |
| Layer | Choice | Why |
|---|---|---|
| Desktop runtime | Electron 30+ | Only way to access Win32 OLE drag pipelines and native clipboard formats from JS |
| Build tooling | electron-vite | Separate Main / Preload / Renderer builds with Vite HMR |
| UI | React 18 + TypeScript | Strongly typed component hierarchy |
| Animation | Framer Motion | Spring physics, layout transitions, gesture animations |
| State | Zustand | Selector-optimized, zero cascading re-renders during drags |
| Drag icons | @resvg/resvg-js | Server-side SVG → PNG rendering for custom drag ghosts |
Edge-Drop/
├─ shared/ Typed IPC contracts & domain models
│ ├─ types.ts ClipboardItem, Bundle, Settings, DragRequest DTOs
│ └─ ipc.ts InvokeMap / EventMap / SendMap channel definitions
├─ electron/ Node.js backend & OS integrations
│ ├─ main/
│ │ ├─ index.ts Single-instance lock, IPC registration, startup
│ │ ├─ window.ts Frameless window, setIgnoreMouseEvents, cursor poll
│ │ ├─ tray.ts System tray icon & context menus
│ │ └─ drag.ts OLE startDrag, temp-file staging, icon generation
│ ├─ preload/ Sandbox bridge exposing window.edge
│ ├─ clipboard/
│ │ ├─ ClipboardWatcher.ts 600ms poll loop, transient-copy rejection
│ │ └─ formats.ts FNV-1a signatures, Win32 HDROP, privacy-flag detection
│ └─ store/
│ ├─ ItemStore.ts Atomic JSON persistence, dedup, merge/split logic
│ ├─ settings.ts User config & startup registration
│ └─ paths.ts AppData + temp directory resolution
├─ src/ React renderer
│ ├─ components/ Panel, ItemList, ClipboardItem, SearchBar, Settings, Icons
│ ├─ hooks/ useEdgeHover (hysteresis), useDragOut, useFilteredItems
│ ├─ store/ Zustand appStore
│ ├─ lib/ Theme tokens, format helpers, file-type detection
│ └─ styles/ tokens.css, panel.css, settings.css, item.css, global.css
Edge-Drop is in public beta. The following are planned, in rough priority order:
- AI semantic self-organization — embed text/URL/HTML items, auto-cluster into named groups, replace manual pinning
- AI summarization — condense multi-file bundles and long HTML copies into one-line summaries + tags
- Multi-monitor support — anchor to any display edge, not just primary
- Linux port — replace Win32-specific paths with cross-platform equivalents
- Plugin SDK — let users write custom format readers and drag-out targets
- Cloud sync (opt-in, E2E encrypted) — sync pinned items across machines
- Search across full history — currently capped at
historyLimit(default 500)
The AI features are the headline roadmap items and the reason this project is applying to OpenAI's Codex for Open Source program.
Edge-Drop is Apache-2.0 licensed and open to contributions. As a solo-maintained project in active beta, the best ways to help right now are:
- File issues for bugs, crashes, or privacy-edge-cases you hit (especially around clipboard format detection on different apps)
- macOS porting — Currently Edge-Drop only supports Windows; contributions for a macOS port are welcome
- Suggest format readers — if you copy from an app whose content Edge-Drop mis-categorizes, open an issue with the available formats list (
clipboard.availableFormats()output) - Pick up a roadmap item — open an issue first to discuss scope, then send a PR against a feature branch
npm install
npm run dev # Electron + Vite HMR
npm run typecheck # tsc --noEmit (node + web configs)
npm run package # build Windows NSIS installer to /distApache License 2.0 — see LICENSE. Commercial and non-commercial use, modification, and distribution all permitted with attribution.
.png)