maCopy/README.md
ilia b643f50d76 Initial commit: macOS clipboard manager menu bar app
Tauri 2 + React 18 + TypeScript + Tailwind CSS v4 + SQLite (rusqlite)

Features:
- Menu bar app with tray icon (no dock icon)
- Global hotkey Cmd+Shift+V
- Clipboard polling every 500ms (text, images, file paths)
- SQLite FTS5 full-text search
- Pin/unpin entries, auto-trim, context menu
- Settings panel (launch at login, show images, max history, clear all)
- Dark/light mode following macOS system preference
- Frameless floating window, closes on blur

Testing:
- 27 Rust unit tests (db, clipboard, FTS5, trim, settings)
- 31 TypeScript component tests (vitest + @testing-library/react)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 12:55:56 -04:00

155 lines
5.2 KiB
Markdown

# maCopy
A lightweight macOS clipboard manager that lives in your menu bar. Built with Tauri 2, React, TypeScript, and SQLite.
![macOS](https://img.shields.io/badge/macOS-10.15+-black?logo=apple)
![Tauri](https://img.shields.io/badge/Tauri-2-blue?logo=tauri)
![License](https://img.shields.io/badge/license-MIT-green)
## Features
- **Menu bar app** — no dock icon, stays out of your way
- **Global hotkey** — `Cmd+Shift+V` opens the window from anywhere
- **Clipboard monitoring** — polls every 500ms for text, images, and file paths
- **Full-text search** — instant filtering via SQLite FTS5
- **Quick paste** — `Cmd+1` through `Cmd+9` to paste the Nth item
- **Pin entries** — pinned items stay at the top and are never auto-deleted
- **Context menu** — right-click for Copy, Pin/Unpin, Delete
- **Auto-trim** — keeps the last 500 entries by default (configurable: 100/500/1000)
- **Dark/light mode** — follows macOS system appearance
- **Privacy** — whitespace-only entries are ignored; pause monitoring from the tray
## Prerequisites
- **macOS** 10.15+
- **Rust** 1.77+ — [install via rustup](https://rustup.rs)
- **Node.js** 18+ and npm
- **Xcode Command Line Tools** — `xcode-select --install`
## Quick Start
```bash
git clone <your-repo-url> maCopy
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.
## Development
### Project Structure
```
maCopy/
├── src/ # React + TypeScript frontend
│ ├── main.tsx # Entry point
│ ├── App.tsx # Main app: routing, keyboard nav, polling
│ ├── index.css # Tailwind v4 + custom theme tokens
│ ├── types.ts # Shared TypeScript types
│ ├── test/ # Test setup and factories
│ │ ├── setup.ts
│ │ └── factories.ts
│ └── components/
│ ├── SearchBar.tsx # Auto-focused search input
│ ├── ClipboardList.tsx # Entry list with time-ago, pin badges
│ ├── ContextMenu.tsx # Right-click: Copy / Pin / Delete
│ └── SettingsPanel.tsx # Toggles + max history + clear all
├── 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 behavior
│ ├── clipboard.rs # Background polling thread (500ms)
│ ├── db.rs # SQLite CRUD + FTS5 + auto-trim
│ └── commands.rs # Tauri IPC commands
├── package.json
├── vite.config.ts
├── vitest.config.ts
└── tsconfig.json
```
### Scripts
| Command | Description |
|---|---|
| `npm run tauri dev` | Run the app in development mode with hot reload |
| `npm run tauri build` | Build a release `.app` bundle |
| `npm test` | Run frontend tests (vitest) |
| `npm run test:watch` | Run frontend tests in watch mode |
| `npm run test:rust` | Run Rust backend tests |
| `npm run test:all` | Run all tests (frontend + backend) |
### Tech Stack
| Layer | Technology |
|---|---|
| Framework | [Tauri 2](https://v2.tauri.app) |
| Frontend | React 18, TypeScript, Tailwind CSS v4 |
| Backend | Rust, rusqlite (bundled SQLite) |
| Clipboard | [arboard](https://crates.io/crates/arboard) for cross-platform access |
| 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 Polling
A dedicated Rust thread polls the system clipboard every 500ms using the `arboard` crate. Each new clipboard value is hashed (SHA-256) and compared against the last known hash to avoid duplicates. Text, images (stored as base64 PNG data URIs), and file paths are all captured.
### SQLite + FTS5
The database uses a content-synced FTS5 virtual table with triggers that automatically keep the full-text index in sync with the main `clipboard_entries` table. This enables instant prefix search as you type.
### Window Behavior
The window is frameless, always-on-top, and hides when it loses focus — behaving like a native macOS popover. Clicking the tray icon or pressing `Cmd+Shift+V` toggles visibility.
## Data Storage
The SQLite database is stored at:
```
~/Library/Application Support/maCopy/clipboard.db
```
## Testing
### Frontend (31 tests)
```bash
npm test
```
Tests cover all four UI components: SearchBar, ClipboardList, ContextMenu, and SettingsPanel. Tauri APIs are mocked so tests run in jsdom without the native runtime.
### Backend (27 tests)
```bash
npm run test:rust
```
Tests use in-memory SQLite databases and cover: CRUD operations, FTS5 search, auto-trim with pin preservation, settings persistence, SHA-256 hashing, and PNG encoding.
### All tests
```bash
npm run test:all
```
## Building for Release
```bash
npm run tauri build
```
The built `.app` bundle will be in `src-tauri/target/release/bundle/macos/`.
## License
MIT