Files
maCopy/README.md
T
ilia f57ac62c3a
CI / skip-ci-check (pull_request) Successful in 37s
CI / secret-scan (pull_request) Successful in 36s
CI / node-ci (pull_request) Successful in 1m9s
Docs: align README test count table with current suite.
2026-08-11 09:51:50 -04:00

199 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# maCopy
macOS clipboard manager in the 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
| 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 multiMB 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