<h1 align="center">Clipvault</h1>
<p align="center">Clipboard history manager for Wayland, inspired by <a href="https://github.com/sentriz/cliphist">cliphist</a></p>
<p align="center">
<img src="https://img.shields.io/badge/License-AGPL_v3-green.svg" alt="License: AGPL v3" />
<img src="https://img.shields.io/github/v/tag/rolv-apneseth/clipvault?label=version&color=blueviolet" alt="version" />
<a href="https://crates.io/crates/clipvault"><img alt="Crates.io Version" src="https://img.shields.io/crates/v/clipvault"></a>
<a href="https://aur.archlinux.org/packages/clipvault"><img src="https://img.shields.io/aur/version/clipvault" alt="AUR version" /></a>
</p>
<video alt="Clipvault demo" src="https://github.com/user-attachments/assets/8b8ddc27-faa1-443c-a948-840cd4fcc038"></video>
## Features
Like `cliphist`:
- **Save history:** clipboard entries are stored in a local database
- **Recall history:** recall saved entries using any picker you like (e.g. `dmenu`, `rofi`, `wofi`, etc.)
- **Simple:** no builtin picker, only pipes - keep it simple (stupid)
- **Any data:** support for **text**, **images** and **any other** binary data
- **Preservation:** entries are preserved byte-for-byte, including leading/trailing whitespace
In addition:
- **No silent failures:** invalid arguments cause errors, and are also written to log files
- **Relative positions:** support for getting/deleting items by relative position in the saved history
- **Entry size limits**: configurable minimum and maximum size for stored entries
- **Entry age limit:** configurable max age for entries - automatically remove old clipboard entries
- **Ignore entries:** avoid storing certain text data using regex patterns, e.g. `^<meta http-equiv=`
- **Informative previews:** previews for binary data support many more types, e.g. `video/mp4`, `application/pdf`, etc.
## Requirements
- [wl-clipboard](https://github.com/bugaevc/wl-clipboard), or anything with an interface
equivalent to `wl-paste --watch` to keep `clipvault` updated with the latest
clipboard entries.
## Installation
### Cargo
```bash
cargo install clipvault --locked
```
Or, directly from source:
```bash
cargo install --git https://github.com/rolv-apneseth/clipvault --locked
```
### AUR
```bash
paru -S clipvault
```
### Manual
1. Download the tarball for your computer's architecture (probably `x86_64`) from the [releases page](https://github.com/Rolv-Apneseth/clipvault/releases)
2. Unpack the tarball, e.g.:
```sh
tar -xf clipvault-x86_64-unknown-linux-gnu.tar.gz
```
3. Place the `clipvault` binary in your `$PATH`
## Setup
```sh
wl-paste --watch clipvault store
```
This will listen for changes from the Wayland clipboard and write each entry to the history.
Call it once per session - for example:
- **Hyprland**:
```conf
exec-once = wl-paste --watch clipvault store
```
- **Sway:**
```conf
exec wl-paste --watch clipvault store
```
### Filtering
If you wish to narrow down the MIME types copied to `clipvault`, you can run `wl-paste --watch`
once for each type that should actually get forwarded to `clipvault`:
```sh
wl-paste --type text --watch clipvault store # Forward text data
wl-paste --type image --watch clipvault store # Forward raw image data
```
> [!TIP]
> Use `wl-paste --list-types` to find the available MIME types for the currently copied data in the
> Wayland clipboard.
### Image data from browsers
When copying images from browsers, `wl-paste` will usually pass the data to `clipvault` as `text/html`.
This is not ideal for copying images, and you probably want to have the raw image data copied instead.
If so, you can either *only* forward image data, or, more realistically, use the commands below to
copy the image data directly, and ignore the `text/html`:
```sh
# Forward all data, ignoring text that starts with "<meta http-equiv="
wl-paste --watch clipvault store --ignore-pattern '^<meta http-equiv='
# Forward specifically raw image data
wl-paste --type image --watch clipvault store
```
## Usage
#### Select an entry (picker)
```sh
I recommend making a keybind for this one with your favourite picker (see [picker examples](#picker-examples) below).
#### Select an entry (relative index)
```sh
clipvault get --index 0 # Newest entry
clipvault get --index 1 # Entry just before the newest entry
clipvault get --index -1 # Oldest entry
```
#### Delete an entry (picker)
```sh
The `rofi` script in [extras](./extras), which creates a custom mode, uses `clipvault` delete to
remove entries directly from within the `rofi` window.
#### Delete an entry (relative index)
```sh
clipvault delete --index 0 # Delete the newest entry
clipvault delete --index 1 # Delete the entry just before the newest entry
clipvault delete --index -1 # Delete the oldest entry
```
#### Delete all entries
```sh
clipvault clear
```
Alternatively, just delete the database file (default path can be found in `help` output).
#### Additional information
- Logs are written to `$XDG_STATE_HOME/clipvault/logs`
- Log level can be changed using the `RUST_LOG` env var, e.g. `RUST_LOG="trace"`
- ANSI colours used for errors printed to STDOUT can be disabled with `NO_COLOR=1`
## Picker Examples
Examples of basic setups for different picker programs, such as `rofi` and `dmenu`.
Many of the below examples will show only the second column of the output from `clipvault list`
(each line is separated by `\t`). Like
`cliphist`, it's important that a line prefixed with a number is piped into `clipvault get`. This
number is used to look up in the database the exact original selection that was made, with all
leading/trailing/non-printable whitespace preserved, none of which is shown in the preview output of
`clipvault list`.
<details>
<summary>dmenu</summary>
```sh
</details>
<details>
<summary>fzf</summary>
```sh
</details>
<details>
<summary>rofi</summary>
```sh
</details>
<details>
<summary>fuzzel</summary>
```sh
</details>
<details>
<summary>wofi</summary>
```sh
Note also that by default `wofi` may sort entries alphabetically or by its cache. To display entries
in the order they are produced, try adding the following arguments to the `wofi` call above:
```sh
-d -k /dev/null
```
</details>
<details>
<summary>tofi</summary>
```sh
</details>
For more advanced setups, checkout some of the scripts in the [extras](./extras/) directory.
Contributions are welcome.
> [!TIP]
> To avoid proceeding with copying if no entry was selected in the picker, try replacing
> `clipvault get | wl-copy` from any of the above commands with
> `{ read -r output && clipvault get <<< "$output" | wl-copy }`
## Configuration
While there is no support for a dedicated configuration file, `clipvault` *does* support loading additional
CLI arguments from files, thanks to [argfile](https://docs.rs/argfile/latest/argfile/).
To use this functionality, create a file with one argument per line, like [this example](./extras/argfile.txt).
Then, provide its path to the CLI using:
```sh
clipvault @/path/to/argfile
```
> [!TIP]
> Most options can also be set using environment variables - check out the `help` output
> for each command to find the specific variable to set for each.
## Contributing
All contributions are welcome. If you run into any problems, or have any suggestions/feedback, feel free to open an issue.
This project is written in Rust, so for contributing code:
1. Ensure [rustup](https://rustup.rs/) is installed - this project uses the stable toolchain for
most things, but nightly for the formatting.
2. Make your changes and ensure they work as expected - `cargo run -- your_args_here`.
3. Lint + format + run tests:
```rust
cargo clippy --all -- -W clippy::all && cargo +nightly fmt && cargo test
```
4. LGTM 👍
I like [just](https://github.com/casey/just), so I keep some utility commands in the [justfile](./justfile).
Check that out for additional checks which are run in the CI.
## Similar programs
- [cliphist](https://github.com/sentriz/cliphist)
- [cliprust](https://github.com/aulimaru/cliprust)
## Acknowledgements
- [cliphist](https://github.com/sentriz/cliphist) of course
- All the [dependencies](./Cargo.toml) of this project, and all [their dependencies](./Cargo.lock) too
## License
This code is licensed under the [AGPLv3](https://www.gnu.org/licenses/agpl-3.0.en.html#license-text).
See the [LICENSE](./LICENSE) file for more details.