Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
1b843df
feat(app): the clipboard is watched, kept and pasted, with the panel …
rgdevment Sep 24, 2026
19f84c3
fix(app): what four lenses found, from a paste that never pasted
rgdevment Sep 24, 2026
8cc7991
feat(app): where each copy came from, what failed, and where the logs…
rgdevment Sep 24, 2026
a8d216c
fix(app): the tools link to their own pages, and no link can outlive …
rgdevment Sep 24, 2026
a9abea8
feat(app): retention, quota, emptying and the shortcut stop being dec…
rgdevment Sep 24, 2026
435c5b4
fix(app): the system theme follows the system, and nothing lies in si…
rgdevment Sep 24, 2026
28f2aec
feat(panel): the panel wears the theme you chose, and the system's wh…
rgdevment Sep 24, 2026
b879cbe
fix(ci): what macOS and the linter caught, and this machine could not
rgdevment Sep 24, 2026
69b7b4b
fix(ci): the shell keeps its entry point on both platforms
rgdevment Sep 24, 2026
47a8f3a
fix(app): three ways the history could vanish, and a panel you could …
rgdevment Sep 25, 2026
465dbea
feat(app): the panel can answer back, so a broken history is not a si…
rgdevment Sep 25, 2026
a95d783
feat(installer): binaries out of the data folder, and an offer to ret…
rgdevment Sep 25, 2026
e7e7734
ci: the pieces the release will stand on, and a bundle that checks wh…
rgdevment Sep 25, 2026
07e444a
feat(app): the updater and the channel it will read
rgdevment Sep 25, 2026
fc9ae97
ci: the release and the feed, as LinkUnbound already proved them
rgdevment Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ exclude_re = [
"replace < with <= in rgb_of_hsl",
"replace < with <= in migrate",
"replace Rows::fetch_ahead with \\(\\)",
"replace Engine::ours with",
"replace kept -> Option<i64> with",
]

exclude_globs = [
Expand Down
43 changes: 43 additions & 0 deletions .github/actions/prepare/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Prepare the build
description: Rust toolchain, cache, and the release version stamped into the manifest.

inputs:
version:
description: Version to stamp into Cargo.toml.
required: true
targets:
description: Rust targets to install, comma separated.
required: true
cache-key:
description: Added to the cache key when one job id builds more than one target.
required: false
default: ""

runs:
using: composite
steps:
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
targets: ${{ inputs.targets }}

- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
key: ${{ inputs.cache-key }}
save-if: false

- name: Stamp the version
shell: bash
run: |
version="${{ inputs.version }}"

cargo fetch --locked --offline 2>/dev/null || cargo fetch --locked

sed -i.bak -e "s/^version = .*/version = \"$version\"/" Cargo.toml
rm -f Cargo.toml.bak

if ! grep -q "^version = \"$version\"\$" Cargo.toml; then
echo "::error::could not stamp $version into Cargo.toml"
exit 1
fi

cargo update --workspace --offline
33 changes: 33 additions & 0 deletions .github/actions/sidecar/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: The panel the app carries
description: >-
`externalBin` in tauri.conf.json makes the binary a build-time requirement of
the app, so anything that compiles the workspace — a test, a clippy run —
needs it there first, not only a bundle.

inputs:
profile:
description: debug or release.
required: false
default: debug
target:
description: Rust target the panel is built for, empty for the host.
required: false
default: ""

runs:
using: composite
steps:
- shell: bash
env:
PY: ${{ runner.os == 'Windows' && 'python' || 'python3' }}
PROFILE: ${{ inputs.profile }}
TARGET: ${{ inputs.target }}
run: |
said=""
if [ "$PROFILE" = "debug" ]; then
said="--debug"
fi
if [ -n "$TARGET" ]; then
said="$said --target $TARGET"
fi
"$PY" scripts/sidecar.py $said
129 changes: 129 additions & 0 deletions .github/workflows/bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
name: Bundle

on:
push:
branches: [main]
paths:
- "app/src-tauri/installer.nsi"
- "app/src-tauri/tauri.conf.json"
- "app/src-tauri/Cargo.toml"
- "app/src-tauri/build.rs"
- "app/package.json"
- "scripts/sidecar.py"
- "Cargo.toml"
- ".github/actions/sidecar/action.yml"
- ".github/workflows/bundle.yml"
- ".github/workflows/release.yml"
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
CARGO_TERM_COLOR: always

jobs:
# `paths` on the trigger leaves a required check unreported for good; a skipped job counts as met.
changes:
name: what the branch touched
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
packaging: ${{ steps.look.outputs.packaging }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0

- id: look
env:
BASE: ${{ github.event.pull_request.base.sha }}
run: |
if [ -z "$BASE" ]; then
echo "packaging=true" >> "$GITHUB_OUTPUT"
exit 0
fi
watched='^(app/src-tauri/(installer\.nsi|tauri\.conf\.json|Cargo\.toml|build\.rs)'
watched+='|app/package\.json|scripts/sidecar\.py|Cargo\.toml$'
watched+='|\.github/actions/sidecar/action\.yml$'
watched+='|\.github/workflows/(bundle|release)\.yml$)'
from=$(git merge-base "$BASE" HEAD)
if git diff --name-only --diff-filter=d "$from" HEAD | grep -qE "$watched"; then
echo "packaging=true" >> "$GITHUB_OUTPUT"
else
echo "packaging=false" >> "$GITHUB_OUTPUT"
fi

nsis:
name: the installer builds
needs: changes
if: needs.changes.outputs.packaging == 'true'
runs-on: windows-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
shared-key: bundle
save-if: ${{ github.ref == 'refs/heads/main' }}

- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache-dependency-path: app/package-lock.json

- name: Frontend
working-directory: app
run: npm ci

- uses: ./.github/actions/sidecar
with:
profile: release

- name: Bundle
working-directory: app
shell: bash
run: npm run tauri build -- --bundles nsis --no-sign

- name: What the installer was told to do
shell: bash
run: |
setup=$(ls target/release/bundle/nsis/*-setup.exe 2>/dev/null | head -1) || true
if [ -z "$setup" ]; then
echo "::error::the bundler produced no installer"
exit 1
fi

script=$(ls target/release/nsis/*/installer.nsi 2>/dev/null | head -1) || true
if [ -z "$script" ]; then
echo "::error::makensis ran but left no script behind to read"
exit 1
fi

status=0
want() {
if ! grep -q "$1" "$script"; then
echo "::error::the generated installer does not $2"
status=1
fi
}
want 'oname=cp-panel.exe' \
"lay down the panel; the sidecar was not bundled"
want 'StrCpy .INSTDIR ".LOCALAPPDATA.Programs' \
"keep the binaries out of the data folder"
want '_is1" "UninstallString"' \
"look for CopyPaste 2 before copying anything"
want 'RmDir /r ".LOCALAPPDATA.${PRODUCTNAME}"' \
"point its delete-my-data box at the folder the data is actually in"
exit $status

- name: How big it came out
shell: bash
run: ls -lh target/release/bundle/nsis/*-setup.exe
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: taiki-e/install-action@nextest

- uses: ./.github/actions/sidecar

- run: cargo clippy --workspace --all-targets
- run: cargo nextest run --workspace --no-tests=pass
- if: runner.os == 'Windows'
Expand Down
Loading
Loading