All checks were successful
check / check (push) Successful in 2m9s
- EVENTS.md: `bread.command.clip.pin` and `bread.clip.pinned` /
`.pin.failed` are now implemented; `select` stays explicitly not
implemented with the reason. AGENTS.md follows.
- README: Configuration section, Ctrl+P bind, updated privacy notes
(CLIPBOARD_STATE + ignore rules), JPEG image entries, primary badge.
- check.yml also runs on pushes to `main` — a push to main triggers a
dev-track release build, so it should be linted/tested first.
- release.yml uses `${GITHUB_REPOSITORY}` instead of a hard-coded
`Breadway/breadclip` for the GitHub mirror release upload.
133 lines
4.9 KiB
Markdown
133 lines
4.9 KiB
Markdown
# breadclip
|
|
|
|
A Wayland clipboard history manager for Hyprland. It consists of two binaries:
|
|
|
|
- **`breadclipd`** — a background daemon that watches the clipboard and persists entries to a local SQLite database.
|
|
- **`breadclip`** — a GTK4 Layer Shell popup for browsing and recalling clipboard history.
|
|
|
|
## Requirements
|
|
|
|
- Rust toolchain (edition 2021)
|
|
- GTK 4.12+
|
|
- `gtk4-layer-shell`
|
|
- `wl-clipboard` (`wl-paste` and `wl-copy` must be on `$PATH`)
|
|
- Hyprland (or any Wayland compositor with Layer Shell support, though the panel-positioning logic is Hyprland-specific)
|
|
|
|
## Build
|
|
|
|
```sh
|
|
git clone https://git.breadway.dev/breadway/breadclip
|
|
cd breadclip
|
|
cargo build --release
|
|
```
|
|
|
|
The compiled binaries are at `target/release/breadclip` and `target/release/breadclipd`.
|
|
|
|
## Install
|
|
|
|
Copy the binaries to somewhere on your `$PATH`, e.g.:
|
|
|
|
```sh
|
|
cp target/release/breadclip target/release/breadclipd ~/.local/bin/
|
|
```
|
|
|
|
### systemd user service
|
|
|
|
A unit file is provided in `contrib/`:
|
|
|
|
```sh
|
|
cp contrib/breadclipd.service ~/.config/systemd/user/
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable --now breadclipd
|
|
```
|
|
|
|
### Hyprland keybind
|
|
|
|
Add the contents of `contrib/hyprland.conf` to your `hyprland.conf`:
|
|
|
|
```
|
|
layerrule = blur, breadclip
|
|
layerrule = ignorezero, breadclip
|
|
|
|
bind = $mainMod, V, exec, breadclip
|
|
```
|
|
|
|
The `layerrule` lines enable the frosted-glass blur effect behind the panel.
|
|
|
|
## Usage
|
|
|
|
Start the daemon (or let the systemd unit handle it):
|
|
|
|
```sh
|
|
breadclipd
|
|
```
|
|
|
|
Open the clipboard history popup:
|
|
|
|
```sh
|
|
breadclip
|
|
```
|
|
|
|
Running `breadclip` a second time while it is open closes it (toggle behaviour).
|
|
|
|
### Keyboard shortcuts
|
|
|
|
| Key | Action |
|
|
|-----|--------|
|
|
| `Up` / `Down` | Move selection |
|
|
| `Enter` | Copy selected entry to clipboard and close |
|
|
| `Delete` | Remove selected entry from history |
|
|
| `Ctrl+P` | Pin/unpin selected entry (pinned entries survive trimming and sort to the top) |
|
|
| `Escape` | Close without copying |
|
|
|
|
Clicking an entry copies it and closes the popup. Clicking outside the panel closes it.
|
|
|
|
### Filtering and search
|
|
|
|
The popup has three filter chips — **All**, **Text**, **Images** — and a search box. The search box filters text entries by content; image entries only appear under the **Images** filter.
|
|
|
|
## Configuration
|
|
|
|
Optional TOML config at `$XDG_CONFIG_HOME/breadclip/config.toml` (typically
|
|
`~/.config/breadclip/config.toml`). Every key has a sensible default, so the
|
|
file can be omitted entirely — a copy of the full example lives in
|
|
`contrib/config.toml.example`:
|
|
|
|
```toml
|
|
[retention]
|
|
text = 200 # max non-pinned text entries (0 = keep none)
|
|
images = 50 # max non-pinned image entries (0 = keep none)
|
|
|
|
[panel]
|
|
width = 520 # popup panel width, px
|
|
|
|
[capture]
|
|
primary = false # also watch the middle-click primary selection
|
|
```
|
|
|
|
## Data storage
|
|
|
|
History is stored under `$XDG_DATA_HOME/breadclip/` (typically `~/.local/share/breadclip/`):
|
|
|
|
| Path | Contents |
|
|
|------|----------|
|
|
| `history.db` | SQLite database of all entries |
|
|
| `images/` | PNG/JPEG files for image entries |
|
|
|
|
The daemon trims the oldest non-pinned entries automatically, keeping at
|
|
most `retention.text` text entries and `retention.images` image entries
|
|
(defaults 200 and 50; configurable). **Pinned entries are exempt from
|
|
trimming** and sort to the top of the popup. Entries captured from the
|
|
primary (middle-click) selection — when `capture.primary = true` — are
|
|
stored alongside regular clipboard entries with a `primary` badge.
|
|
|
|
### Privacy
|
|
|
|
- `history.db` and every file under `images/` are created with `0600` permissions (owner read/write only), regardless of your umask.
|
|
- breadclipd **never persists clipboard content flagged as sensitive**. `wl-paste --watch` reports copies made with `wl-copy --sensitive` — which also covers offers advertising the `x-kde-passwordManagerHint` MIME type, the convention used by KeePassXC, Bitwarden, and other password managers to mark content they own — via `CLIPBOARD_STATE=sensitive`, and those copies are skipped entirely and never reach the database.
|
|
- On top of that, breadclipd runs **best-effort ignore rules** that skip copies that *look* like secrets even when the app didn't flag them: one-time codes, Luhn-valid credit card numbers, private key blocks, `password:`-style credential lines, and well-known API token prefixes (see `breadclipd/src/ignore_rules.rs`). These are deliberately conservative and are a convenience, not a security boundary — the 0600/0700 permissions are the real protection.
|
|
- That said, this is still a plaintext SQLite database of everything else you copy. Anything copied by an app that doesn't set the hint (e.g. copying a password from a terminal or a non-integrated app) will be stored like any other text entry. Treat `history.db` as sensitive, and don't rely on it as your only safeguard.
|
|
|
|
## Theming
|
|
|
|
`breadclip` inherits its colour palette from `bread-theme`. The panel renders with an 80% opaque background so Hyprland's `layerrule = blur` can show a frosted-glass effect behind it.
|