maCopy
A fast, native macOS clipboard manager that lives in your menu bar. Built with Tauri 2, React, TypeScript, and SQLite.
Features
- Menu bar app — no dock icon, stays out of your way
- Global hotkey —
Ctrl+`opens the window (doesn’t steal macOS Cmd+` window-cycle); configurable in Settings - Clipboard monitoring — watches the macOS pasteboard changeCount (no busy hashing while idle); captures text, images, and file paths
- Fast picker — list API returns truncated previews only (image blobs stay in SQLite until paste); refreshes on copy / window focus instead of reloading everything every second
- Full-text search — instant filtering via SQLite FTS5
- Quick paste —
Cmd+1throughCmd+9to paste the Nth item directly into the previous app - Paste & return — clicking an entry copies it to clipboard, hides the window, and auto-pastes into the previously-focused app
- Multi-select —
Cmd+Clickto toggle,Shift+ArroworShift+Clickto range-select,Cmd+Afor all,Enterto paste selected - Pin entries — pinned items stay at the top and are never auto-deleted
- Context menu — right-click for Paste, Pin/Unpin, Delete (with multi-select support)
- Resizable window — drag edges to resize; size is remembered between sessions
- Window positioning — choose where the window appears: near cursor, center, or any corner (configurable in Settings)
- Auto-trim — keeps up to 50K entries (configurable: 1K/5K/10K/50K); oversized images are skipped
- Dark/light mode — follows macOS system appearance
- Privacy — whitespace-only entries are ignored; password/card/banking previews are redacted (full value still pastes); pause monitoring from the tray
- Paste plain / transforms — ⌥-click or right-click for plain text and transforms (trim, case, JSON, …)
- Copy only — right-click → Copy puts content on the clipboard without auto-pasting or hiding the window
- Color swatches — hex/rgb/hsl entries (e.g.
#3366ff) show a small color chip in the list - Launch at login — Settings toggle registers a macOS Login Item (use the built
.app) - Image thumbnails — list shows small thumbs; full images stay in the DB until paste
Docs
| Doc | |
|---|---|
| docs/GUIDE.md | Install + usage guide (the living doc — start here if you just want to use the app) |
| docs/README.md | Docs index |
| docs/TESTING.md | Automated tests |
| docs/LAUNCH-AT-LOGIN.md | Start when Mac logs in |
| docs/PRODUCT.md | Professional / shipping checklist |
| docs/SHARING.md | Where the app lives + how to share it with others |
| CHANGELOG.md | Version history |
| ROADMAP.md | Next features + brainstorm |
- macOS 10.15+
- Rust 1.77+ — install via rustup
- Node.js 18+ and npm
- Xcode Command Line Tools —
xcode-select --install - Accessibility permission — required for auto-paste (System Settings → Privacy & Security → Accessibility → add maCopy)
Quick Start
git clone gitea@git.levkin.ca:ilia/maCopy.git
cd maCopy
npm install
npm run tauri dev
The app will compile the Rust backend, start the Vite dev server, and launch the menu bar app.
Usage
| Action | How |
|---|---|
| Open/close window | Click tray icon or press Ctrl+` (configurable in Settings) |
| Paste an entry | Click it, or press Enter |
| Paste plain (no formatting) | ⌥/Alt+click, or right-click → Paste plain |
| Copy without pasting | Right-click → Copy |
| Transforms | Right-click → Transform (trim, case, JSON, …) |
| Quick paste | Cmd+1 through Cmd+9 |
| Search | Just start typing |
| Select multiple | Cmd+Click or Shift+Arrow |
| Select all | Cmd+A |
| Delete | Backspace or Delete (on selected items) |
| Pin/unpin | Right-click → Pin/Unpin |
| Settings | Tray icon → Settings… |
| Change hotkey | Settings → Global hotkey → press new shortcut |
| Launch at login | Settings → Launch at login (see docs/LAUNCH-AT-LOGIN.md) |
| Dismiss | Escape or click outside |
Development
Project Structure
maCopy/
├── src/ # React + TypeScript frontend
│ ├── main.tsx # Entry point
│ ├── App.tsx # Main app: state, keyboard nav, multi-select
│ ├── App.test.tsx # App integration tests
│ ├── index.css # Tailwind v4 + custom theme tokens
│ ├── types.ts # Shared TypeScript interfaces
│ ├── test/ # Test setup and factories
│ │ ├── setup.ts # Tauri API mocks for jsdom
│ │ └── factories.ts # makeEntry(), makeSettings()
│ └── components/
│ ├── SearchBar.tsx
│ ├── ClipboardList.tsx # Entry list with multi-select, type badges
│ ├── ContextMenu.tsx # Right-click menu with multi-select labels
│ └── SettingsPanel.tsx # Toggles, max history, window position
├── src-tauri/ # Rust backend
│ ├── Cargo.toml
│ ├── tauri.conf.json # Tauri config, permissions, window
│ └── src/
│ ├── main.rs # Binary entry point
│ ├── lib.rs # App setup: tray, hotkey, window management
│ ├── clipboard.rs # Background polling thread (500ms, SHA-256 dedup)
│ ├── db.rs # SQLite CRUD + FTS5 + settings + auto-trim
│ └── commands.rs # Tauri IPC commands
├── .cursor/rules/ # Cursor AI rules for this project
├── package.json
├── vite.config.ts
├── vitest.config.ts
└── tsconfig.json
Scripts
| Command | Description |
|---|---|
npm run tauri dev |
Run in development mode with hot reload |
npm run tauri build |
Build a release .app bundle |
npm test |
Run frontend tests (Vitest, ~60 tests) |
npm run test:rust |
Run Rust backend tests (~52 tests) |
npm run test:all |
Run all tests (frontend + backend) |
npm run lint |
TypeScript type-check |
npm run check |
Lint + all tests |
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Tauri 2 |
| Frontend | React 18, TypeScript, Tailwind CSS v4 |
| Backend | Rust, rusqlite (bundled SQLite) |
| Clipboard | arboard for system clipboard access |
| Mouse position | core-graphics for global cursor coordinates |
| Search | SQLite FTS5 with content-sync triggers |
| Hotkey | tauri-plugin-global-shortcut |
| Autostart | tauri-plugin-autostart |
| Testing | Vitest + @testing-library/react (frontend), cargo test (backend) |
Architecture
Clipboard monitoring
A background Rust thread watches the macOS pasteboard changeCount and only reads the clipboard when it changes. Text, images (full PNG + thumbnail), and file paths are captured. List IPC returns truncated / redacted previews and image thumbnails — not multi‑MB blobs.
Paste & Return
When you select an entry, maCopy writes it to the system clipboard (text or image), hides its window, waits briefly for macOS to refocus the previous app, then simulates Cmd+V via AppleScript. This requires Accessibility permission. Optional transforms (plain, trim, JSON, …) run before the write.
SQLite + FTS5
The database uses a content-synced FTS5 virtual table with triggers that automatically keep the full-text index in sync with the clipboard_entries table. This enables instant prefix search as you type. Opened with journal_mode=WAL + synchronous=NORMAL so the UI can read while the background poller writes, without blocking on full fsyncs.
Window Behavior
The window uses titleBarStyle: "overlay" for native resize handles while keeping the frameless aesthetic. It's always-on-top and hides on blur (with a short delay so clicks register). Position is determined by the user's setting (near cursor via CoreGraphics, center, or a screen corner).
Data Storage
The SQLite database is stored at:
~/Library/Application Support/maCopy/clipboard.db
Testing
See docs/TESTING.md for the full map.
npm test # ~60 frontend tests
npm run test:rust # ~52 Rust tests
npm run check # lint + all tests (~112)
Building for Release
npm run tauri build
The built .app bundle will be in src-tauri/target/release/bundle/macos/.
Copy it to /Applications, then enable Launch at login in Settings — details in docs/LAUNCH-AT-LOGIN.md.
Roadmap & product readiness
- Feature backlog: ROADMAP.md
- Professional shipping checklist: docs/PRODUCT.md
License
MIT