199 lines
8.6 KiB
Markdown
199 lines
8.6 KiB
Markdown
# maCopy
|
||
|
||
macOS clipboard manager in the menu bar. Built with Tauri 2, React, TypeScript, and SQLite.
|
||
|
||

|
||

|
||

|
||
|
||
## Features
|
||
|
||
| Area | Behavior |
|
||
|------|----------|
|
||
| Menu bar | No dock icon; lives in the tray |
|
||
| Global hotkey | `` Ctrl+` `` opens the window (does not steal macOS Cmd+` window-cycle); configurable in Settings |
|
||
| Clipboard monitoring | Watches pasteboard changeCount; captures text, images, and file paths |
|
||
| List performance | Truncated previews in list API; image blobs stay in SQLite until paste |
|
||
| Search | Full-text filtering via SQLite FTS5 |
|
||
| Quick paste | `Cmd+1` through `Cmd+9` paste the Nth item into the previous app |
|
||
| Paste and return | Click copies, hides window, auto-pastes into the previously focused app |
|
||
| Multi-select | `Cmd+Click` toggle; `Shift+Arrow` or `Shift+Click` range; `Cmd+A` all; `Enter` to paste |
|
||
| Pin | Pinned items stay at top and skip auto-delete |
|
||
| Context menu | Right-click: Paste, Pin/Unpin, Delete (multi-select aware) |
|
||
| Window | Resizable; size remembered; position: near cursor, center, or corner |
|
||
| Auto-trim | Up to 50K entries (1K/5K/10K/50K); skips oversized images |
|
||
| Appearance | Dark/light follows macOS |
|
||
| Privacy | Ignores whitespace-only entries; redacts password/card previews; pause monitoring from tray |
|
||
| Transforms | Option-click or right-click: plain text, trim, case, JSON, etc. |
|
||
| Copy only | Right-click Copy puts content on clipboard without auto-paste |
|
||
| Color swatches | Hex/rgb/hsl entries show a color chip in the list |
|
||
| Launch at login | Settings toggle registers a macOS Login Item |
|
||
| Image thumbnails | Small thumbs in list; full image loaded on paste |
|
||
|
||
## Docs
|
||
|
||
| Doc | |
|
||
|---|---|
|
||
| [docs/GUIDE.md](docs/GUIDE.md) | Install and usage guide — start here to use the app |
|
||
| [docs/README.md](docs/README.md) | Docs index |
|
||
| [docs/TESTING.md](docs/TESTING.md) | Automated tests |
|
||
| [docs/LAUNCH-AT-LOGIN.md](docs/LAUNCH-AT-LOGIN.md) | Start when Mac logs in |
|
||
| [docs/PRODUCT.md](docs/PRODUCT.md) | Professional / shipping checklist |
|
||
| [docs/SHARING.md](docs/SHARING.md) | Where the app lives + how to share it with others |
|
||
| [CHANGELOG.md](CHANGELOG.md) | Version history |
|
||
| [ROADMAP.md](ROADMAP.md) | Next features + brainstorm |
|
||
|
||
- **macOS** 10.15+
|
||
- **Rust** 1.77+ — [install via rustup](https://rustup.rs)
|
||
- **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
|
||
|
||
```bash
|
||
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](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, ~72 tests) |
|
||
| `npm run test:rust` | Run Rust backend tests (~67 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](https://v2.tauri.app) |
|
||
| Frontend | React 18, TypeScript, Tailwind CSS v4 |
|
||
| Backend | Rust, rusqlite (bundled SQLite) |
|
||
| Clipboard | [arboard](https://crates.io/crates/arboard) for system clipboard access |
|
||
| Mouse position | [core-graphics](https://crates.io/crates/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 and 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. User queries are tokenized before `MATCH` so path punctuation (`/…`, `.`) does not trip FTS5 syntax. 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](docs/TESTING.md)** for the full map.
|
||
|
||
```bash
|
||
npm test # ~72 frontend tests
|
||
npm run test:rust # ~67 Rust tests
|
||
npm run check # lint + all tests (~139)
|
||
```
|
||
|
||
## Building for Release
|
||
|
||
```bash
|
||
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](docs/LAUNCH-AT-LOGIN.md).
|
||
|
||
## Roadmap & product readiness
|
||
|
||
- Feature backlog: [ROADMAP.md](ROADMAP.md)
|
||
- Professional shipping checklist: [docs/PRODUCT.md](docs/PRODUCT.md)
|
||
|
||
## License
|
||
|
||
MIT
|