Compare commits
8 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56070c00c3 | ||
|
|
50e5a63528 | ||
|
|
a6bba2c362 | ||
|
|
006caf0fd5 | ||
|
|
22a6df17b0 | ||
|
|
eaf3fdc93f | ||
|
|
6385a080f1 | ||
|
|
7ece4fd762 |
25 changed files with 366 additions and 1300 deletions
|
|
@ -1,24 +0,0 @@
|
|||
name: check
|
||||
|
||||
# Fast-fail lint/test on short-lived work branches, before it ever reaches
|
||||
# main and triggers a dev-track release build.
|
||||
on:
|
||||
push:
|
||||
branches: ['feature/**', 'fix/**']
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: [self-hosted, hestia]
|
||||
steps:
|
||||
- name: checkout
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf src && mkdir src
|
||||
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
|
||||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: clippy
|
||||
run: cd src && bash ci/build.sh cargo clippy --workspace --all-targets --locked -- -D warnings
|
||||
|
||||
- name: test
|
||||
run: cd src && bash ci/build.sh cargo test --workspace --locked
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
name: dev release
|
||||
|
||||
# Publishes a dev-track build on every push to `main` (the trunk
|
||||
# branch — there is no separate `dev` branch). See bread-ecosystem's
|
||||
# docs/release-channels.md for the release-track policy this is part of.
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted, hestia]
|
||||
steps:
|
||||
- name: checkout
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf src && mkdir src
|
||||
git clone --branch main --depth 1 \
|
||||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: build
|
||||
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||
|
||||
- name: compute dev version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd src
|
||||
# Base the dev version off the latest published stable tag,
|
||||
# not Cargo.toml — Cargo.toml can go stale relative to the last
|
||||
# real release (seen in practice: breadbox/breadpad/breadcrumbs/
|
||||
# breadpaper), which would make a dev build sort as OLDER than
|
||||
# what's already installed and bakery would correctly refuse it.
|
||||
LATEST_TAG="$(git ls-remote --tags --refs \
|
||||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" 'v*' \
|
||||
| awk -F/ '{print $NF}' | sed 's/^v//' | (grep -v -- '-' || true) | sort -V | tail -1)"
|
||||
if [ -n "${LATEST_TAG}" ]; then
|
||||
CUR="${LATEST_TAG}"
|
||||
else
|
||||
CUR="$(grep -m1 '^version' breadbox/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')"
|
||||
fi
|
||||
IFS='.' read -r MA MI PA <<< "${CUR}"
|
||||
SHA="$(git rev-parse --short HEAD)"
|
||||
TS="$(date -u +%Y%m%d%H%M%S)"
|
||||
echo "VERSION=${MA}.${MI}.$((PA + 1))-dev.${TS}+${SHA}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: prepare artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
PKG_DIR="/srv/breadway-dl/dev/breadbox/${VERSION}"
|
||||
mkdir -p "${PKG_DIR}"
|
||||
for bin in breadbox breadbox-sync; do
|
||||
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
|
||||
strip "${PKG_DIR}/${bin}-x86_64"
|
||||
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \
|
||||
> "${PKG_DIR}/${bin}-x86_64.sha256"
|
||||
done
|
||||
cp src/packaging/breadbox-sync.service "${PKG_DIR}/"
|
||||
cp src/config.example.toml "${PKG_DIR}/"
|
||||
cp src/LICENSE "${PKG_DIR}/"
|
||||
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||
ln -sfn "${VERSION}" "/srv/breadway-dl/dev/breadbox/latest"
|
||||
|
||||
# No GitHub Release upload — dev, like the other non-stable track,
|
||||
# is only distributed via dl.breadway.dev/dev/.
|
||||
- name: regenerate dev index.json
|
||||
env:
|
||||
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${MINISIGN_SEC_KEY:-}" ]; then
|
||||
echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate dev index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the dev track)"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true
|
||||
# mktemp: a fixed clone path races when multiple repos' dev/beta
|
||||
# workflows run close together on the same self-hosted runner.
|
||||
ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)"
|
||||
git clone --branch main https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}"
|
||||
TRACK=dev bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh"
|
||||
rm -rf "${ECOSYSTEM_CI_DIR}"
|
||||
19
.forgejo/workflows/mirror.yml
Normal file
19
.forgejo/workflows/mirror.yml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
name: Mirror to GitHub
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['**']
|
||||
tags: ['**']
|
||||
|
||||
jobs:
|
||||
mirror:
|
||||
runs-on: [self-hosted, hestia]
|
||||
steps:
|
||||
- name: Mirror to GitHub
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git clone --mirror "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" repo.git
|
||||
cd repo.git
|
||||
git push --prune \
|
||||
"https://x-access-token:${{ secrets.MIRROR_TOKEN }}@github.com/Breadway/breadbox.git" \
|
||||
'+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*'
|
||||
40
.forgejo/workflows/package.yml
Normal file
40
.forgejo/workflows/package.yml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
name: Build and publish package
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
package:
|
||||
runs-on: [self-hosted, hestia]
|
||||
container:
|
||||
image: archlinux:latest
|
||||
steps:
|
||||
# Note: no actions/checkout — the archlinux image has no Node, which JS
|
||||
# actions require. Everything runs as shell steps and clones manually.
|
||||
- name: Build and publish
|
||||
env:
|
||||
PUBLISH_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${GITHUB_REF_NAME#v}"
|
||||
pacman -Syu --noconfirm base-devel git rust cargo gtk4 gtk4-layer-shell librsvg
|
||||
useradd -m builder
|
||||
git config --global --add safe.directory '*'
|
||||
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
|
||||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" /home/builder/src
|
||||
cd /home/builder/src
|
||||
git archive --format=tar.gz --prefix="breadbox-${VERSION}/" HEAD \
|
||||
> packaging/arch/breadbox-${VERSION}.tar.gz
|
||||
SHA=$(sha256sum packaging/arch/breadbox-${VERSION}.tar.gz | awk '{print $1}')
|
||||
sed -i "s/^pkgver=.*/pkgver=${VERSION}/" packaging/arch/PKGBUILD
|
||||
sed -i "s/^sha256sums=.*/sha256sums=('${SHA}')/" packaging/arch/PKGBUILD
|
||||
chown -R builder:builder /home/builder/src
|
||||
# --nocheck: packaging builds the artifact; tests belong in a CI job.
|
||||
su builder -c "cd /home/builder/src/packaging/arch && makepkg -f --noconfirm --nocheck"
|
||||
PKG=$(find /home/builder/src/packaging/arch -name '*.pkg.tar.zst' | head -1)
|
||||
curl -fsS -X PUT \
|
||||
-H "Authorization: token ${PUBLISH_TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary "@${PKG}" \
|
||||
"https://git.breadway.dev/api/packages/Breadway/arch/os"
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
name: beta (rc) release
|
||||
|
||||
# Publishes a beta-track build for any `vX.Y.Z-rc.N` prerelease tag
|
||||
# pushed to `main` — there is no separate `beta` branch; "freezing" is
|
||||
# just pausing pushes to main while an RC gets tested. See
|
||||
# bread-ecosystem's docs/release-channels.md for the release-track policy.
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ contains(github.ref_name, '-rc.') }}
|
||||
runs-on: [self-hosted, hestia]
|
||||
steps:
|
||||
- name: checkout
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf src && mkdir src
|
||||
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
|
||||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: build
|
||||
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||
|
||||
- name: prepare artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${GITHUB_REF_NAME#v}"
|
||||
PKG_DIR="/srv/breadway-dl/beta/breadbox/${VERSION}"
|
||||
mkdir -p "${PKG_DIR}"
|
||||
for bin in breadbox breadbox-sync; do
|
||||
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
|
||||
strip "${PKG_DIR}/${bin}-x86_64"
|
||||
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \
|
||||
> "${PKG_DIR}/${bin}-x86_64.sha256"
|
||||
done
|
||||
cp src/packaging/breadbox-sync.service "${PKG_DIR}/"
|
||||
cp src/config.example.toml "${PKG_DIR}/"
|
||||
cp src/LICENSE "${PKG_DIR}/"
|
||||
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||
ln -sfn "${VERSION}" "/srv/breadway-dl/beta/breadbox/latest"
|
||||
|
||||
# No GitHub Release upload — beta, like dev, is only distributed via
|
||||
# dl.breadway.dev/beta/.
|
||||
- name: regenerate beta index.json
|
||||
env:
|
||||
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${MINISIGN_SEC_KEY:-}" ]; then
|
||||
echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate beta index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the beta track)"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true
|
||||
# mktemp: a fixed clone path races when multiple repos' dev/beta
|
||||
# workflows run close together on the same self-hosted runner.
|
||||
ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)"
|
||||
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}"
|
||||
TRACK=beta bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh"
|
||||
rm -rf "${ECOSYSTEM_CI_DIR}"
|
||||
|
|
@ -6,7 +6,6 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ !contains(github.ref_name, '-rc.') }}
|
||||
runs-on: [self-hosted, hestia]
|
||||
steps:
|
||||
- name: checkout
|
||||
|
|
@ -17,16 +16,7 @@ jobs:
|
|||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ ! -f src/ci/build.sh ]; then
|
||||
echo "::error::ci/build.sh is missing — bakery release builds must go through the shared CI wrapper"
|
||||
exit 1
|
||||
fi
|
||||
cd src && bash ci/build.sh cargo build --release --locked || {
|
||||
echo "::error::cargo build --release --locked failed. If Cargo.lock drifted, update and commit it; do not drop --locked."
|
||||
exit 1
|
||||
}
|
||||
run: cd src && cargo build --release --locked
|
||||
|
||||
- name: prepare artifacts
|
||||
run: |
|
||||
|
|
@ -42,19 +32,12 @@ jobs:
|
|||
done
|
||||
cp src/packaging/breadbox-sync.service "${PKG_DIR}/"
|
||||
cp src/config.example.toml "${PKG_DIR}/"
|
||||
cp src/LICENSE "${PKG_DIR}/"
|
||||
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||
ln -sfn "${VERSION}" "/srv/breadway-dl/breadbox/latest"
|
||||
|
||||
- name: regenerate index.json
|
||||
env:
|
||||
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${MINISIGN_SEC_KEY:-}" ]; then
|
||||
echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone)"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf /tmp/bread-ecosystem-ci
|
||||
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci
|
||||
bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh
|
||||
|
|
|
|||
1
.github/README.md
vendored
1
.github/README.md
vendored
|
|
@ -1 +0,0 @@
|
|||
Forgejo (`.forgejo/workflows`) is the canonical CI. This `.github` tree is unused.
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -19,9 +19,3 @@ Thumbs.db
|
|||
|
||||
# Claude Code session data
|
||||
.claude/
|
||||
|
||||
# Local hygiene notes (not for commit)
|
||||
CLAUDE.md
|
||||
|
||||
# graphify knowledge-graph output (local tool cache, not for commit)
|
||||
graphify-out/
|
||||
|
|
|
|||
13
AGENTS.md
13
AGENTS.md
|
|
@ -1,13 +0,0 @@
|
|||
# AGENTS.md — Repo hygiene
|
||||
|
||||
Follow [`CONTRIBUTING.md`](CONTRIBUTING.md). Single-trunk: `main` plus short-lived `feature/` / `fix/` branches. `dev`/`beta` are bakery tracks (tags / main), not git branches.
|
||||
|
||||
## Remotes
|
||||
- `origin` — Forgejo (`git.breadway.dev`) — authoritative.
|
||||
- `github` — mirror. Day-to-day push `origin` only.
|
||||
|
||||
## Product
|
||||
GTK4 app launcher + `breadbox-sync` icon cache. Theme via `bread-theme` (pin by tag on `git.breadway.dev`). Toggle uses `bread-utils::singleton`, not a homegrown PID file. `EVENTS.md` is the bread-event contract (app id `box`); emit `bread.box.launched` after a successful launch. `breadbox listen` honors `bread.command.box.open`.
|
||||
|
||||
## Distribution
|
||||
Bakery (`bakery.toml`). Forgejo `.forgejo/workflows/` is canonical; do not re-add a GitHub Actions release workflow.
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
`breadbox` — App launcher for Hyprland / Wayland.
|
||||
|
||||
Part of the bread ecosystem; this repo follows the same branch/release
|
||||
workflow as every other ecosystem product.
|
||||
|
||||
## Branches
|
||||
|
||||
There is one long-lived branch: **`main`**. All day-to-day work lands here.
|
||||
Every push to `main` automatically builds and publishes a **dev-track**
|
||||
build (see Tracks below) — a real install you can test before cutting
|
||||
anything more formal.
|
||||
|
||||
New work — features and bug fixes alike — goes on a short-lived branch:
|
||||
|
||||
```
|
||||
feature/<short-name>
|
||||
fix/<issue-number-or-short-name>
|
||||
```
|
||||
|
||||
Branch off `main`, open a PR/push back into `main` when ready. Short-lived
|
||||
branches get deleted on merge — they never accumulate the kind of drift a
|
||||
second long-lived branch does.
|
||||
|
||||
## The release cycle
|
||||
|
||||
There's no separate `beta` or release branch — "stable" and "beta" are both
|
||||
just **tags** on `main`, not branches that need to be kept in sync:
|
||||
|
||||
1. Work accumulates on `main` via `feature/x` / `fix/x` branches. Each push
|
||||
auto-publishes a dev build — install it with `bakery track set dev` and
|
||||
`bakery update --all`, then fix anything broken with another push.
|
||||
2. When you want to stabilize before a real release, tag a release
|
||||
candidate: `git tag vX.Y.Z-rc.1 && git push origin vX.Y.Z-rc.1` (push to
|
||||
both remotes). That tag alone triggers a beta-track build —
|
||||
"freezing" is just pausing pushes to `main` while you test it, not a
|
||||
branch operation. Cut `-rc.2`, `-rc.3`, etc. for further fixes.
|
||||
3. Once an RC has gone without issues, tag the real release:
|
||||
`git tag vX.Y.Z && git push origin vX.Y.Z` — that's what triggers the
|
||||
signed stable release build.
|
||||
|
||||
## Tracks, from a user's perspective
|
||||
|
||||
```
|
||||
bakery track show # what you're currently on (defaults to stable)
|
||||
bakery track set dev # or beta, or stable
|
||||
bakery update --all # pull the latest build on your current track
|
||||
```
|
||||
|
||||
| Track | What it is | Published from |
|
||||
|--------|-----------|-----------------|
|
||||
| `stable` | The last tagged release | a `vX.Y.Z` tag |
|
||||
| `beta` | Latest release candidate | a `vX.Y.Z-rc.N` tag |
|
||||
| `dev` | Bleeding edge | `main`, on every push |
|
||||
|
||||
Dev versions are auto-computed (`X.Y.Z-dev.<timestamp>+<sha>`) from the
|
||||
latest published stable tag, so they always sort as newer than what you
|
||||
have installed — no manual version bumping needed. Beta versions are just
|
||||
the RC tag itself (already valid semver, already sorts below the real
|
||||
release it's a candidate for).
|
||||
|
||||
## Local development
|
||||
|
||||
```sh
|
||||
cargo build --release --workspace
|
||||
cargo test --release --workspace
|
||||
```
|
||||
|
||||
## CI
|
||||
|
||||
- `dev-release.yml` — triggered on push to `main`.
|
||||
- `rc-release.yml` — triggered on any `vX.Y.Z-rc.N` tag push.
|
||||
- `release.yml` — triggered on any other `v*` tag push, cuts the actual
|
||||
stable release.
|
||||
|
||||
All CI runs on a self-hosted runner; nothing runs automatically on plain
|
||||
commits or PRs beyond the track builds above. See
|
||||
[bread-ecosystem's docs/release-channels.md](https://git.breadway.dev/Breadway/bread-ecosystem/src/branch/main/docs/release-channels.md)
|
||||
for the full policy, including how a new product gets wired onto these tracks.
|
||||
|
||||
## Questions
|
||||
|
||||
Open an issue on this repo's Forgejo tracker.
|
||||
429
Cargo.lock
generated
429
Cargo.lock
generated
|
|
@ -8,62 +8,6 @@ version = "2.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
|
|
@ -82,31 +26,10 @@ version = "2.13.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||
|
||||
[[package]]
|
||||
name = "bread-screenshots"
|
||||
version = "0.7.2"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bread-utils",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-shared"
|
||||
version = "0.7.0"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread?tag=v0.7.0#22e34e2cf2202305d7960759dfccb54dc79f948b"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml 0.8.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-theme"
|
||||
version = "0.7.4"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.4#fcba3760387e2523edb71350f8efea3bc851b21e"
|
||||
version = "0.2.3"
|
||||
source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.10#17d1bb85801b9a8c195b64c02d288cd662c9c780"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"gtk4",
|
||||
|
|
@ -116,10 +39,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bread-utils"
|
||||
version = "0.7.2"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73"
|
||||
version = "0.3.0"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939"
|
||||
dependencies = [
|
||||
"bread-shared",
|
||||
"dirs",
|
||||
"gtk4",
|
||||
"gtk4-layer-shell",
|
||||
|
|
@ -129,14 +51,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "breadbox"
|
||||
version = "0.3.3"
|
||||
version = "0.2.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bread-screenshots",
|
||||
"bread-theme",
|
||||
"bread-utils",
|
||||
"breadbox-shared",
|
||||
"clap",
|
||||
"gtk4",
|
||||
"gtk4-layer-shell",
|
||||
"serde_json",
|
||||
|
|
@ -144,7 +63,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "breadbox-shared"
|
||||
version = "0.3.3"
|
||||
version = "0.2.6"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -153,7 +72,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "breadbox-sync"
|
||||
version = "0.3.3"
|
||||
version = "0.2.6"
|
||||
dependencies = [
|
||||
"breadbox-shared",
|
||||
"serde_json",
|
||||
|
|
@ -180,14 +99,14 @@ checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54"
|
|||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.4.3"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d"
|
||||
checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
|
|
@ -209,52 +128,6 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
|
|
@ -287,13 +160,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.7"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
||||
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -314,9 +187,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.11"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
|
|
@ -339,24 +212,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
|
||||
checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
|
||||
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432"
|
||||
checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
|
|
@ -365,32 +238,32 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed"
|
||||
checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
|
||||
checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
|
||||
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
|
||||
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
|
|
@ -421,7 +294,7 @@ dependencies = [
|
|||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -454,7 +327,7 @@ dependencies = [
|
|||
"libc",
|
||||
"pango-sys",
|
||||
"pkg-config",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -494,7 +367,7 @@ dependencies = [
|
|||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
|
|
@ -548,7 +421,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -558,7 +431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "030967459f9f676851872c6304adea7825c6d462ec9b72554c733cf0c5952233"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -569,7 +442,7 @@ checksum = "22a861859b887a79cf461359c192c97a57d8fb0229dd291232e57aa11f6fa72c"
|
|||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -590,7 +463,7 @@ checksum = "5c7ffdfde88f3570d3705e0d8a2433e036d387a1f2930bbf47eafcb5f569fd04"
|
|||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -621,7 +494,7 @@ dependencies = [
|
|||
"graphene-sys",
|
||||
"libc",
|
||||
"pango-sys",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -647,9 +520,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gtk4-layer-shell"
|
||||
version = "0.8.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17c28ea0f4676fdaaae7ff2413a24d0d35c8657424f84856c1103c73454c9da4"
|
||||
checksum = "a4069987ff4793699511a251028cc336b438e46565b463f111250148d574752a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gdk4",
|
||||
|
|
@ -662,15 +535,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gtk4-layer-shell-sys"
|
||||
version = "0.6.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcf19bb884ef0ef55b9e6b2b369c39b4fcc0c41e3a0c1cbc8c267720338b690b"
|
||||
checksum = "8f566a5ec5bcc454e7fcf2ab76930887ced5365afce12c1e5201bb296b95f1b9"
|
||||
dependencies = [
|
||||
"gdk4-sys",
|
||||
"glib-sys",
|
||||
"gtk4-sys",
|
||||
"libc",
|
||||
"system-deps 8.0.0",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -682,7 +555,7 @@ dependencies = [
|
|||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -701,7 +574,7 @@ dependencies = [
|
|||
"gsk4-sys",
|
||||
"libc",
|
||||
"pango-sys",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -718,9 +591,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
|
||||
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
|
|
@ -732,9 +605,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
|
||||
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
|
|
@ -745,9 +618,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
|
||||
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
|
|
@ -759,17 +632,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
|
||||
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
|
||||
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
|
|
@ -780,15 +652,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
|
||||
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428"
|
||||
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
|
|
@ -830,12 +702,6 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
|
|
@ -850,24 +716,24 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.189"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.20"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a"
|
||||
checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.3"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
|
||||
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
|
@ -906,12 +772,6 @@ version = "1.21.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
|
|
@ -938,7 +798,7 @@ dependencies = [
|
|||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 7.0.8",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -955,15 +815,15 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
|||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.34"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
|
||||
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661"
|
||||
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
|
@ -979,18 +839,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
@ -1031,9 +891,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.43"
|
||||
version = "0.23.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
|
||||
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
|
|
@ -1046,18 +906,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.15.1"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
|
||||
checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046"
|
||||
dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.14"
|
||||
version = "0.103.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a"
|
||||
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
|
|
@ -1072,9 +932,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
|
|
@ -1082,29 +942,29 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
|
@ -1161,12 +1021,6 @@ version = "1.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
|
@ -1184,17 +1038,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
|
|
@ -1203,7 +1046,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1215,20 +1058,7 @@ dependencies = [
|
|||
"cfg-expr",
|
||||
"heck",
|
||||
"pkg-config",
|
||||
"toml 1.1.4+spec-1.1.0",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83779a5c956bcb6ba627a4ecf0a9d7625db47d7537e0892d97f712ac995648a3"
|
||||
dependencies = [
|
||||
"cfg-expr",
|
||||
"heck",
|
||||
"pkg-config",
|
||||
"toml 1.1.4+spec-1.1.0",
|
||||
"toml 1.1.3+spec-1.1.0",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
|
|
@ -1255,14 +1085,14 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.4"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
|
||||
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
|
|
@ -1282,9 +1112,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.4+spec-1.1.0"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5"
|
||||
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
|
|
@ -1341,9 +1171,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow 1.0.4",
|
||||
]
|
||||
|
|
@ -1360,37 +1190,6 @@ version = "1.1.2+spec-1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
|
|
@ -1437,12 +1236,6 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.1"
|
||||
|
|
@ -1647,15 +1440,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.4"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
||||
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.29"
|
||||
version = "0.8.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
|
|
@ -1676,7 +1469,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -1697,7 +1490,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -1709,9 +1502,9 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
|||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.5"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f"
|
||||
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
|
|
@ -1720,9 +1513,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.7"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8"
|
||||
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
|
|
@ -1731,13 +1524,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.4"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47402523226a02bfe5230160dc3ccc089aa6f6f19e7fcbb4e6f824bbb1b4aa62"
|
||||
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
65
EVENTS.md
65
EVENTS.md
|
|
@ -1,65 +0,0 @@
|
|||
# breadbox — bread event integration
|
||||
|
||||
breadbox is a standalone app launcher: it works exactly the same with or
|
||||
without `breadd` running. When breadd *is* present, the GTK launcher
|
||||
publishes a single event into the shared bread automation fabric after a
|
||||
successful launch. See the parent `bread` repo's `Documentation.md` —
|
||||
specifically its "Namespaces" and "Integrating a bread\* app" sections —
|
||||
for the general convention this follows.
|
||||
|
||||
App id: **`box`**. Transport: `bread-utils`'s `bread_client` module
|
||||
(feature `bread-client`) — `breadbox` links it directly. One-shot
|
||||
launcher invocations each `emit` on their own fire-and-forget
|
||||
connection. Command verbs are only received while `breadbox listen` is
|
||||
running — that process holds the `bread.command.box.**` subscription
|
||||
open.
|
||||
|
||||
## Events published (`bread.box.*`)
|
||||
|
||||
| Event | Data | When |
|
||||
|-------|------|------|
|
||||
| `bread.box.launched` | `{ "id": "<desktop id or exec>", "name": "<display name>" }` | The user launched an app (Enter / keypad Enter on the selected row, or activating a row) **and** the spawn succeeded. Not emitted if `Command::spawn` fails (missing terminal, `exec` that cannot start). `id` is the desktop-file id (the `.desktop` filename, e.g. `firefox.desktop`), falling back to the stripped `Exec=` line when that id is empty. `name` is the desktop-entry display name. |
|
||||
| `bread.box.open.done` | `{}` | `bread.command.box.open` was received and `breadbox` was spawned. This is the command confirmation, not proof the overlay mapped — the spawned process is the same toggle as a keybind. |
|
||||
| `bread.box.open.failed` | `{ "error": "<message>" }` | `bread.command.box.open` was received but this binary could not be started. |
|
||||
|
||||
Launch history is local to breadbox (`~/.cache/breadbox/history.json`);
|
||||
the event bus is a notification that a launch happened, not a channel
|
||||
for the exec line's arguments or the resulting process.
|
||||
|
||||
## Commands honored (`bread.command.box.*`)
|
||||
|
||||
These are only received while `breadbox listen` is running. Publishing a
|
||||
command with no subscriber is a silent no-op — that is the documented
|
||||
bread convention, not a breadbox bug.
|
||||
|
||||
| Verb | Data | Effect |
|
||||
|------|------|--------|
|
||||
| `open` | none | Same as running `breadbox` (toggle the launcher overlay via the existing singleton). Emits `bread.box.open.done` / `.failed`. |
|
||||
|
||||
```lua
|
||||
bread.spawn(function()
|
||||
bread.emit("bread.command.box.open")
|
||||
bread.wait("bread.box.open.done", { timeout = 5000 })
|
||||
end)
|
||||
```
|
||||
|
||||
### Not implemented: extra verbs
|
||||
|
||||
There is no `launch` / `close` / `query` command verb. Picking a desktop
|
||||
id from the bus would be a new product surface. If/when that exists, add
|
||||
the corresponding `bread.command.box.*` verb at the same time, not
|
||||
stubbed as a no-op ahead of it.
|
||||
|
||||
## Fail-safe behavior
|
||||
|
||||
- If breadd isn't installed or isn't running, `emit` is a silent no-op
|
||||
(`BreadClient::emit` never blocks or errors the caller) and the
|
||||
command subscription simply never receives anything — launching,
|
||||
history, theming, and the singleton toggle are entirely unaffected.
|
||||
- If breadd restarts, the command subscription reconnects automatically
|
||||
(`BreadClient::subscribe`'s background thread has its own backoff
|
||||
loop); no restart of `breadbox listen` is needed.
|
||||
- If `breadbox listen` is not running, commands are a graceful no-op at
|
||||
the bus (no subscriber). The CLI still works, and one-shot invocations
|
||||
still emit `bread.box.launched` on their own short-lived connection.
|
||||
- Closing the launcher without launching anything emits nothing.
|
||||
12
README.md
12
README.md
|
|
@ -12,9 +12,10 @@ breadbox GTK4 layer-shell launcher
|
|||
|
||||
- Layer-shell window, centered 600 px wide, keyboard-exclusive
|
||||
- Reads the active Hyprland workspace and sorts apps by context priority
|
||||
- Fuzzy filtering as you type; Enter launches, Escape closes
|
||||
- Launch history: non-priority apps sort by most-launched first, then alphabetically
|
||||
- Fuzzy filtering as you type; Enter or click to launch, Escape or click outside to close
|
||||
- App icons loaded from the resolved icon cache (see `breadbox-sync`)
|
||||
- pywal accents from `~/.cache/wal/colors.json`; background/surface/overlay/foreground stay fixed BOS dark
|
||||
- pywal palette auto-detected from `~/.cache/wal/colors.json`, falls back to Catppuccin Mocha
|
||||
- User CSS override at `~/.config/breadbox/style.css`
|
||||
- Toggle/dismiss: running a second instance kills the first
|
||||
|
||||
|
|
@ -88,10 +89,9 @@ breadbox-sync
|
|||
```
|
||||
|
||||
Icon resolution order:
|
||||
1. System icon theme (`~/.local/share/icons`, `/usr/share/icons`, `/usr/share/pixmaps`) — 64 px > 48 px PNG, then SVG
|
||||
2. Flathub media server — for reverse-DNS app IDs (e.g. `org.gnome.Gedit`)
|
||||
3. icon.horse — downloaded and cached
|
||||
4. `application-x-executable` fallback from system theme
|
||||
1. System icon theme (`~/.local/share/icons`, `/usr/share/icons`, `/usr/share/pixmaps`) — 64 px > 48 px > 128 px > 32 px > 256 px PNG, then SVG
|
||||
2. Flathub appstream CDN — for reverse-DNS app IDs (e.g. `org.gnome.Gedit`)
|
||||
3. `application-x-executable` fallback from system theme
|
||||
|
||||
### Systemd service (run on login)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ binaries = ["breadbox", "breadbox-sync"]
|
|||
system_deps = ["gtk4", "gtk4-layer-shell", "librsvg"]
|
||||
optional_system_deps = ["hyprland"]
|
||||
bread_deps = []
|
||||
license_file = "LICENSE"
|
||||
|
||||
[[service]]
|
||||
unit = "breadbox-sync.service"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "breadbox-shared"
|
||||
version = "0.3.3"
|
||||
version = "0.2.6"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
|
|
|||
|
|
@ -54,9 +54,6 @@ pub fn app_dirs() -> Vec<PathBuf> {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DesktopEntry {
|
||||
/// Desktop file id (the `.desktop` filename, e.g. `firefox.desktop`).
|
||||
/// Empty only if the path had no file name; callers fall back to `exec`.
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub exec: String,
|
||||
pub icon_name: String,
|
||||
|
|
@ -158,14 +155,7 @@ pub fn parse_desktop(path: &Path) -> Option<DesktopEntry> {
|
|||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
let id = path
|
||||
.file_name()
|
||||
.map(|n| n.to_string_lossy().into_owned())
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or_default();
|
||||
|
||||
Some(DesktopEntry {
|
||||
id,
|
||||
name,
|
||||
exec,
|
||||
icon_name,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "breadbox-sync"
|
||||
version = "0.3.3"
|
||||
version = "0.2.6"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "breadbox"
|
||||
version = "0.3.3"
|
||||
version = "0.2.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
|
@ -9,13 +9,9 @@ name = "breadbox"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.4", features = ["gtk"] }
|
||||
bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["bread-client", "gtk"] }
|
||||
# Capture primitives for `--screenshot` mode — see src/screenshot.rs.
|
||||
bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2" }
|
||||
bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.10", features = ["gtk"] }
|
||||
bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0", features = ["gtk"] }
|
||||
breadbox-shared = { path = "../breadbox-shared" }
|
||||
gtk4 = { version = "0.11", features = ["v4_12"] }
|
||||
gtk4-layer-shell = "0.8"
|
||||
serde_json = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
anyhow = "1"
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
//! Long-running command subscription for `bread.command.box.*`.
|
||||
//!
|
||||
//! `breadbox` is still a one-shot toggle overlay by default. `breadbox listen`
|
||||
//! is the optional persistent process that can honor bus commands. See
|
||||
//! `EVENTS.md`.
|
||||
|
||||
use bread_utils::bread_client::{BreadClient, BreadEvent};
|
||||
|
||||
/// Sibling-app id in `bread_shared::apps::KNOWN_APPS`.
|
||||
const APP_ID: &str = "box";
|
||||
|
||||
/// Subscribe to `bread.command.box.**` and block until the process is killed.
|
||||
///
|
||||
/// breadd being absent is not an error: [`BreadClient::subscribe`] reconnects
|
||||
/// with backoff, and `on_event` simply isn't called until the daemon is up.
|
||||
pub fn run() {
|
||||
let client = BreadClient::connect(APP_ID);
|
||||
if client.health().is_none() {
|
||||
eprintln!("breadbox: breadd unreachable; command subscription will connect when it comes back");
|
||||
}
|
||||
|
||||
let _commands = client.subscribe("bread.command.box.**", |event| {
|
||||
handle_command(&event);
|
||||
});
|
||||
|
||||
eprintln!("breadbox: listening for bread.command.box.**");
|
||||
loop {
|
||||
std::thread::park();
|
||||
}
|
||||
}
|
||||
|
||||
/// Reacts to `bread.command.box.*` verbs. Only `open` is honored today —
|
||||
/// other verbs are ignored, not stubbed as no-ops that pretend to succeed.
|
||||
fn handle_command(event: &BreadEvent) {
|
||||
let Some(verb) = command_verb(&event.event) else {
|
||||
return;
|
||||
};
|
||||
match verb {
|
||||
"open" => handle_open(),
|
||||
other => {
|
||||
eprintln!("breadbox: ignoring unrecognized bread.command.box.{other}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_open() {
|
||||
// Same as running `breadbox` from a keybind: toggle the overlay via the
|
||||
// existing singleton. Spawn success is the command confirmation — we do
|
||||
// not wait for the GTK window to map.
|
||||
let result = spawn_self();
|
||||
let client = BreadClient::connect(APP_ID);
|
||||
match result {
|
||||
Ok(_) => client.emit("bread.box.open.done", serde_json::json!({})),
|
||||
Err(e) => {
|
||||
eprintln!("breadbox: bread.command.box.open failed: {e}");
|
||||
client.emit(
|
||||
"bread.box.open.failed",
|
||||
serde_json::json!({ "error": e.to_string() }),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_self() -> std::io::Result<std::process::Child> {
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("breadbox"));
|
||||
std::process::Command::new(exe).spawn()
|
||||
}
|
||||
|
||||
fn command_verb(event_name: &str) -> Option<&str> {
|
||||
event_name.strip_prefix("bread.command.box.")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn command_verb_strips_box_prefix() {
|
||||
assert_eq!(command_verb("bread.command.box.open"), Some("open"));
|
||||
assert_eq!(command_verb("bread.command.box.launch"), Some("launch"));
|
||||
assert_eq!(command_verb("bread.command.clip.clear"), None);
|
||||
assert_eq!(command_verb("bread.box.launched"), None);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +1,25 @@
|
|||
use bread_theme::{hex_to_rgba, ink_on, load_palette, Palette};
|
||||
use bread_utils::bread_client::BreadClient;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
env, fs,
|
||||
io::{Read, Write},
|
||||
os::unix::net::UnixStream,
|
||||
env,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
rc::Rc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// This app's id in bread's sibling-app namespace registry
|
||||
/// (`bread_shared::apps::KNOWN_APPS`) — events publish as `bread.box.*`.
|
||||
const APP_ID: &str = "box";
|
||||
|
||||
use breadbox_shared::{
|
||||
config_dir, load_all_desktop_entries, Config, DesktopEntry, IconCache, LaunchHistory,
|
||||
};
|
||||
use gtk4::{
|
||||
glib, pango::EllipsizeMode, prelude::*, Application, Box as GBox, CssProvider, Entry,
|
||||
EventControllerKey, Label, ListBox, Orientation, PolicyType, ScrolledWindow, SelectionMode,
|
||||
glib,
|
||||
pango::EllipsizeMode,
|
||||
prelude::*,
|
||||
Application, Box as GBox, CssProvider, EventControllerKey, Label,
|
||||
ListBox, Orientation, PolicyType, ScrolledWindow, SearchEntry, SelectionMode,
|
||||
};
|
||||
|
||||
mod listen;
|
||||
mod screenshot;
|
||||
|
||||
// ---- Hyprland IPC -----------------------------------------------------------
|
||||
|
||||
fn get_active_workspace() -> Option<String> {
|
||||
let sig = env::var("HYPRLAND_INSTANCE_SIGNATURE").ok()?;
|
||||
let rt = env::var("XDG_RUNTIME_DIR").ok()?;
|
||||
let socket_path = format!("{}/hypr/{}/.socket.sock", rt, sig);
|
||||
|
||||
let mut stream = UnixStream::connect(&socket_path).ok()?;
|
||||
stream.write_all(b"j/activeworkspace").ok()?;
|
||||
stream.shutdown(std::net::Shutdown::Write).ok()?;
|
||||
|
||||
let mut response = String::new();
|
||||
stream.read_to_string(&mut response).ok()?;
|
||||
|
||||
let v: serde_json::Value = serde_json::from_str(&response).ok()?;
|
||||
v["name"].as_str().map(|s| s.to_string())
|
||||
}
|
||||
|
||||
// ---- Manifest ---------------------------------------------------------------
|
||||
|
||||
fn load_manifest() -> HashMap<String, PathBuf> {
|
||||
|
|
@ -86,9 +61,7 @@ fn load_sorted_entries(
|
|||
(None, Some(_)) => std::cmp::Ordering::Greater,
|
||||
(None, None) => {
|
||||
// Most-launched first, then alphabetical
|
||||
history
|
||||
.count(&b.name)
|
||||
.cmp(&history.count(&a.name))
|
||||
history.count(&b.name).cmp(&history.count(&a.name))
|
||||
.then(a.name.to_lowercase().cmp(&b.name.to_lowercase()))
|
||||
}
|
||||
}
|
||||
|
|
@ -136,171 +109,58 @@ fn matches_term(field: &str, term: &str) -> bool {
|
|||
|
||||
// ---- Theming ----------------------------------------------------------------
|
||||
|
||||
const STAGGER_ROWS: usize = 12;
|
||||
|
||||
fn build_css(p: &Palette) -> String {
|
||||
let bg_panel = hex_to_rgba(&p.background, 0.68);
|
||||
let bg_panel = hex_to_rgba(&p.background, 0.60);
|
||||
// breadbox-specific rules only — fonts, palette, and generic widgets come
|
||||
// from the shared ecosystem stylesheet (applied first in connect_activate).
|
||||
// Colour is set on each surface (panel, search, hovered/selected row) so
|
||||
// Colour is set on each surface (panel, search box, hovered/selected row) so
|
||||
// child labels inherit the legible ink for that background. `on_*` are
|
||||
// luminance-picked black/white — the pywal hues are untouched.
|
||||
//
|
||||
// GTK4 ListBox's node is `list`, not `listbox`. These `list row:selected`
|
||||
// rules beat the shared sheet's solid accent fill + on-accent ink so the
|
||||
// glass card keeps a tinted selection and a left inset hairline.
|
||||
let stagger = (0..STAGGER_ROWS)
|
||||
.map(|i| {
|
||||
format!(
|
||||
".launcher-bg.just-opened list row.stagger-{i} {{ animation-delay: {}ms; }}",
|
||||
i * 28
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
// luminance-picked black/white — the pywal hues are untouched. Without this a
|
||||
// light `surface` slot makes the selected row's text vanish.
|
||||
format!(
|
||||
"\
|
||||
window {{ background-color: rgba(0, 0, 0, 0.28); animation: scrim-in 0.28s ease both; }}\
|
||||
@keyframes scrim-in {{\
|
||||
from {{ background-color: rgba(0, 0, 0, 0); }}\
|
||||
to {{ background-color: rgba(0, 0, 0, 0.28); }}\
|
||||
}}\
|
||||
.launcher-bg {{\
|
||||
background-color: {bg_panel}; color: {on_bg}; border-radius: 20px;\
|
||||
border: 1px solid alpha({on_bg}, 0.14);\
|
||||
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.50);\
|
||||
animation: card-in 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;\
|
||||
}}\
|
||||
@keyframes card-in {{\
|
||||
from {{ opacity: 0; margin-top: 112px; }}\
|
||||
to {{ opacity: 1; margin-top: 88px; }}\
|
||||
}}\
|
||||
.launcher-bg entry {{\
|
||||
background-color: transparent; color: {on_bg}; caret-color: {accent};\
|
||||
border: none; outline: none; box-shadow: none;\
|
||||
padding: 20px 22px 14px; border-radius: 20px 20px 0 0;\
|
||||
font-size: 17px; min-height: 28px;\
|
||||
}}\
|
||||
.launcher-bg entry:focus, .launcher-bg entry:focus-within {{\
|
||||
border: none; outline: none; box-shadow: none; background-color: transparent;\
|
||||
}}\
|
||||
entry > text {{ background: transparent; }}\
|
||||
entry image {{ opacity: 0; min-width: 0; margin: 0; padding: 0; }}\
|
||||
.launcher-caret {{\
|
||||
min-height: 2px; max-height: 2px; margin: 0 20px; border-radius: 2px;\
|
||||
background-color: {accent};\
|
||||
background-image: linear-gradient(90deg, {accent}, {accent2});\
|
||||
}}\
|
||||
.launcher-bg.just-opened .launcher-caret {{\
|
||||
animation: caret-draw 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;\
|
||||
}}\
|
||||
@keyframes caret-draw {{\
|
||||
from {{ margin-right: 600px; opacity: 0.25; }}\
|
||||
to {{ margin-right: 20px; opacity: 1; }}\
|
||||
}}\
|
||||
scrolledwindow {{ background: transparent; }}\
|
||||
list {{ background-color: transparent; padding: 8px 8px 4px; }}\
|
||||
list row {{\
|
||||
padding: 6px 8px; color: {on_bg}; background-color: transparent;\
|
||||
border-radius: 14px; margin: 1px 10px; outline: none;\
|
||||
}}\
|
||||
list row:hover {{ background-color: alpha({on_bg}, 0.07); color: {on_bg}; }}\
|
||||
row:selected, list row:selected, list row:selected:focus,\
|
||||
list row:selected:hover, list row:selected:focus:hover {{\
|
||||
background-color: alpha({accent}, 0.22); color: {on_bg};\
|
||||
outline: none; box-shadow: none;\
|
||||
}}\
|
||||
list row:selected label, list row:selected .app-name, list row:selected .app-muted {{\
|
||||
color: {on_bg};\
|
||||
}}\
|
||||
.app-row {{ min-height: 48px; }}\
|
||||
.app-icon-well {{\
|
||||
min-width: 38px; min-height: 38px; margin-right: 12px;\
|
||||
border-radius: 999px; background-color: alpha({on_bg}, 0.08);\
|
||||
}}\
|
||||
.app-icon {{ color: {on_bg}; opacity: 0.88; }}\
|
||||
.app-name {{ font-size: 14px; font-weight: bold; }}\
|
||||
.app-muted {{ opacity: 0.48; font-size: 11px; }}\
|
||||
.launcher-footer {{\
|
||||
padding: 8px 18px 12px; font-size: 11px; opacity: 0.40;\
|
||||
letter-spacing: 0.08em; text-transform: uppercase;\
|
||||
}}\
|
||||
.launcher-bg.just-opened list row {{\
|
||||
animation: row-in 0.32s cubic-bezier(0.22, 1, 0.36, 1) both;\
|
||||
}}\
|
||||
@keyframes row-in {{\
|
||||
from {{ opacity: 0; }}\
|
||||
to {{ opacity: 1; }}\
|
||||
}}\
|
||||
{stagger}\
|
||||
list.reflow row {{ animation: row-fade 0.16s ease both; }}\
|
||||
@keyframes row-fade {{\
|
||||
from {{ opacity: 0.40; }}\
|
||||
to {{ opacity: 1; }}\
|
||||
}}\
|
||||
.no-motion, .no-motion * {{ animation: none; transition: none; }}",
|
||||
bg_panel = bg_panel,
|
||||
accent = p.color4,
|
||||
accent2 = p.color5,
|
||||
on_bg = ink_on(&p.background),
|
||||
stagger = stagger,
|
||||
"window {{ background-color: transparent; }}\
|
||||
.launcher-bg {{ background-color: {bg_panel}; color: {on_bg}; border-radius: 8px;\
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.6); }}\
|
||||
searchentry {{ background-color: {surface}; color: {on_surface}; caret-color: {accent};\
|
||||
border: none; outline: none; box-shadow: none;\
|
||||
padding: 12px 16px; border-radius: 6px 6px 0 0; }}\
|
||||
listbox {{ background-color: transparent; padding: 4px; }}\
|
||||
row {{ padding: 8px 12px; color: {on_bg}; background-color: transparent;\
|
||||
border-radius: 6px; }}\
|
||||
row:hover {{ background-color: {surface}; color: {on_surface}; }}\
|
||||
row:selected {{ background-color: {surface}; color: {on_surface}; }}\
|
||||
.app-name {{ font-size: 14px; }}\
|
||||
.app-muted {{ opacity: 0.6; font-size: 12px; }}\
|
||||
image {{ margin-right: 8px; }}",
|
||||
bg_panel = bg_panel,
|
||||
surface = p.color0,
|
||||
accent = p.color4,
|
||||
on_bg = ink_on(&p.background),
|
||||
on_surface = ink_on(&p.color0),
|
||||
)
|
||||
}
|
||||
|
||||
fn category_label(entry: &DesktopEntry) -> &'static str {
|
||||
let has = |needle: &str| {
|
||||
entry
|
||||
.categories
|
||||
.iter()
|
||||
.any(|c| c.eq_ignore_ascii_case(needle) || c.to_ascii_lowercase().contains(needle))
|
||||
};
|
||||
if entry.terminal || has("terminalemulator") {
|
||||
"Terminal"
|
||||
} else if has("webbrowser") {
|
||||
"Browser"
|
||||
} else if has("game") {
|
||||
"Games"
|
||||
} else if has("instantmessaging") || has("chat") || has("ircclient") {
|
||||
"Chat"
|
||||
} else if has("settings") || has("desktopsettings") || has("system") {
|
||||
"System"
|
||||
} else if has("ide") || has("development") {
|
||||
"IDE"
|
||||
} else if has("office") || has("wordprocessor") || has("texteditor") || has("notes") {
|
||||
"Notes"
|
||||
} else if has("audio") || has("player") || has("audiovideo") {
|
||||
"Music"
|
||||
} else if has("graphics") || has("photography") || has("camera") {
|
||||
"Capture"
|
||||
} else if has("filemanager") {
|
||||
"Files"
|
||||
} else {
|
||||
"App"
|
||||
}
|
||||
}
|
||||
|
||||
fn symbolic_icon(entry: &DesktopEntry) -> &'static str {
|
||||
match category_label(entry) {
|
||||
"Browser" => "web-browser-symbolic",
|
||||
"Terminal" => "utilities-terminal-symbolic",
|
||||
"Notes" => "accessories-text-editor-symbolic",
|
||||
"System" => "emblem-system-symbolic",
|
||||
"Chat" => "user-available-symbolic",
|
||||
"Games" => "applications-games-symbolic",
|
||||
"Music" => "audio-x-generic-symbolic",
|
||||
"Capture" => "camera-photo-symbolic",
|
||||
"IDE" => "applications-engineering-symbolic",
|
||||
"Files" => "folder-symbolic",
|
||||
_ => "application-x-executable-symbolic",
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Icon loading -----------------------------------------------------------
|
||||
|
||||
fn make_icon(entry: &DesktopEntry) -> gtk4::Image {
|
||||
let img = gtk4::Image::from_icon_name(symbolic_icon(entry));
|
||||
img.set_pixel_size(18);
|
||||
img.add_css_class("app-icon");
|
||||
fn make_icon(icon_name: &str, icon_path: Option<&Path>) -> gtk4::Image {
|
||||
// Try loading from resolved cached path via gio::File
|
||||
if let Some(path) = icon_path {
|
||||
let gio_file = gtk4::gio::File::for_path(path);
|
||||
if let Ok(texture) = gtk4::gdk::Texture::from_file(&gio_file) {
|
||||
let img = gtk4::Image::new();
|
||||
img.set_paintable(Some(&texture));
|
||||
img.set_pixel_size(32);
|
||||
return img;
|
||||
}
|
||||
}
|
||||
// Fall back to GTK icon theme lookup by name
|
||||
let name = if icon_name.is_empty() {
|
||||
"application-x-executable"
|
||||
} else {
|
||||
icon_name
|
||||
};
|
||||
let img = gtk4::Image::from_icon_name(name);
|
||||
img.set_pixel_size(32);
|
||||
img
|
||||
}
|
||||
|
||||
|
|
@ -323,42 +183,24 @@ fn pick_terminal() -> String {
|
|||
|
||||
fn do_launch(entry: &DesktopEntry) {
|
||||
let cmd = entry.exec.trim();
|
||||
let spawned = if entry.terminal {
|
||||
if entry.terminal {
|
||||
let term = pick_terminal();
|
||||
Command::new(&term)
|
||||
let _ = Command::new(&term)
|
||||
.args(["-e", "bash", "-c", cmd])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.spawn();
|
||||
} else {
|
||||
Command::new("bash")
|
||||
let _ = Command::new("bash")
|
||||
.args(["-c", cmd])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
};
|
||||
if spawned.is_ok() {
|
||||
emit_launched(entry);
|
||||
.spawn();
|
||||
}
|
||||
}
|
||||
|
||||
/// Publishes `bread.box.launched` after a successful spawn. Fire-and-forget
|
||||
/// and non-fatal (`BreadClient::emit` never blocks or errors this caller) —
|
||||
/// breadd being absent must never affect launching itself.
|
||||
fn emit_launched(entry: &DesktopEntry) {
|
||||
let id = if entry.id.is_empty() {
|
||||
entry.exec.as_str()
|
||||
} else {
|
||||
entry.id.as_str()
|
||||
};
|
||||
BreadClient::connect(APP_ID).emit(
|
||||
"bread.box.launched",
|
||||
serde_json::json!({ "id": id, "name": entry.name }),
|
||||
);
|
||||
}
|
||||
|
||||
// ---- Fuzzy matching ---------------------------------------------------------
|
||||
|
||||
fn fuzzy_matches(pattern: &str, text: &str) -> bool {
|
||||
|
|
@ -379,22 +221,50 @@ fn fuzzy_matches(pattern: &str, text: &str) -> bool {
|
|||
}
|
||||
|
||||
fn fuzzy_score(query: &str, entry: &DesktopEntry) -> u32 {
|
||||
let q = query.to_lowercase();
|
||||
let name = entry.name.to_lowercase();
|
||||
let wm = entry.wm_class.as_deref().unwrap_or("").to_lowercase();
|
||||
if name == q || wm == q { return 0; }
|
||||
if name.starts_with(&q) { return 1; }
|
||||
if name.contains(&q) { return 2; }
|
||||
if wm.starts_with(&q) || wm.contains(&q) { return 3; }
|
||||
4 // subsequence match
|
||||
}
|
||||
|
||||
/// Same tiers as `fuzzy_score`, but `None` when nothing matches at all
|
||||
/// (rather than falling through to a bare-subsequence score), and folds in
|
||||
/// `exec` as a tier-4 (weakest) match too — used for filtering, not sorting.
|
||||
/// Tier 4 is loose enough that e.g. querying "zen" matches "Avahi Zeroconf
|
||||
/// Browser" (z…e…n as a subsequence) alongside the real "Zen Browser" hit;
|
||||
/// the filter hides tier-4-only rows whenever a tier ≤2 (name-based) match
|
||||
/// exists elsewhere in the list, so that kind of noise only shows up when
|
||||
/// it's the best any entry can do.
|
||||
fn match_tier(query: &str, entry: &DesktopEntry) -> Option<u32> {
|
||||
if query.is_empty() {
|
||||
return Some(0);
|
||||
}
|
||||
let q = query.to_lowercase();
|
||||
let name = entry.name.to_lowercase();
|
||||
let wm = entry.wm_class.as_deref().unwrap_or("").to_lowercase();
|
||||
if name == q || wm == q {
|
||||
return 0;
|
||||
return Some(0);
|
||||
}
|
||||
if name.starts_with(&q) {
|
||||
return 1;
|
||||
return Some(1);
|
||||
}
|
||||
if name.contains(&q) {
|
||||
return 2;
|
||||
return Some(2);
|
||||
}
|
||||
if wm.starts_with(&q) || wm.contains(&q) {
|
||||
return 3;
|
||||
return Some(3);
|
||||
}
|
||||
4 // subsequence match
|
||||
if fuzzy_matches(query, &entry.name)
|
||||
|| entry.wm_class.as_deref().is_some_and(|w| fuzzy_matches(query, w))
|
||||
|| fuzzy_matches(query, &entry.exec)
|
||||
{
|
||||
return Some(4);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// ---- UI ---------------------------------------------------------------------
|
||||
|
|
@ -406,44 +276,13 @@ fn get_row_entry(row: >k4::ListBoxRow) -> Option<DesktopEntry> {
|
|||
}
|
||||
}
|
||||
|
||||
fn visible_row_count(list: &ListBox) -> u32 {
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
while let Some(row) = list.row_at_index(i) {
|
||||
if row.is_visible() {
|
||||
n += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
n
|
||||
}
|
||||
|
||||
fn set_footer_count(footer: &Label, n: u32) {
|
||||
match n {
|
||||
0 => footer.set_text("no match"),
|
||||
1 => footer.set_text("1 app"),
|
||||
n => footer.set_text(&format!("{n} apps")),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_ui(
|
||||
entries: Vec<DesktopEntry>,
|
||||
history: LaunchHistory,
|
||||
screenshot_req: Option<screenshot::ScreenshotRequest>,
|
||||
) {
|
||||
let mut builder = Application::builder().application_id("com.breadway.breadbox");
|
||||
if screenshot_req.is_some() {
|
||||
// GApplication is single-instance by default; this machine typically
|
||||
// already has a real breadbox instance, so without this a screenshot
|
||||
// run would just message the *existing* instance instead of
|
||||
// starting a fresh one that ever sees `screenshot_req`.
|
||||
builder = builder.flags(gtk4::gio::ApplicationFlags::NON_UNIQUE);
|
||||
}
|
||||
let app = builder.build();
|
||||
fn run_ui(entries: Vec<DesktopEntry>, history: LaunchHistory) {
|
||||
let app = Application::builder()
|
||||
.application_id("com.breadway.breadbox")
|
||||
.build();
|
||||
|
||||
let history_rc = Rc::new(RefCell::new(history));
|
||||
let query_rc: Rc<RefCell<String>> = Rc::new(RefCell::new(String::new()));
|
||||
let is_screenshot_run = screenshot_req.is_some();
|
||||
|
||||
app.connect_activate(move |app| {
|
||||
// Shared ecosystem base (fonts, palette, generic widgets) first, then
|
||||
|
|
@ -459,9 +298,8 @@ fn run_ui(
|
|||
bread_theme::gtk::apply_user_css(&user_css_path, &user_cell);
|
||||
}
|
||||
|
||||
// Full-screen transparent overlay; panel widget is positioned inside it.
|
||||
// Full-screen transparent window; clicks outside the launcher panel close it.
|
||||
let window = bread_utils::gtk_popup::new_overlay_window(app, "breadbox");
|
||||
bread_theme::gtk::bind_window_auto(&window);
|
||||
|
||||
let close_all: Rc<dyn Fn()> = Rc::new({
|
||||
let w = window.clone();
|
||||
|
|
@ -474,25 +312,13 @@ fn run_ui(
|
|||
vbox.add_css_class("launcher-bg");
|
||||
vbox.set_halign(gtk4::Align::Center);
|
||||
vbox.set_valign(gtk4::Align::Start);
|
||||
vbox.set_margin_top(88);
|
||||
vbox.set_margin_top(120);
|
||||
vbox.set_size_request(600, -1);
|
||||
if is_screenshot_run {
|
||||
window.add_css_class("no-motion");
|
||||
vbox.add_css_class("no-motion");
|
||||
} else {
|
||||
vbox.add_css_class("just-opened");
|
||||
}
|
||||
|
||||
let search = Entry::new();
|
||||
search.set_placeholder_text(Some("Search"));
|
||||
search.set_has_frame(false);
|
||||
let search = SearchEntry::new();
|
||||
search.set_placeholder_text(Some("Search apps…"));
|
||||
vbox.append(&search);
|
||||
|
||||
let caret = GBox::new(Orientation::Horizontal, 0);
|
||||
caret.add_css_class("launcher-caret");
|
||||
caret.set_hexpand(true);
|
||||
vbox.append(&caret);
|
||||
|
||||
let scroll = ScrolledWindow::new();
|
||||
scroll.set_policy(PolicyType::Never, PolicyType::Automatic);
|
||||
scroll.set_max_content_height(480);
|
||||
|
|
@ -503,44 +329,28 @@ fn run_ui(
|
|||
|
||||
for (idx, entry) in entries.iter().enumerate() {
|
||||
let row = gtk4::ListBoxRow::new();
|
||||
if idx < STAGGER_ROWS {
|
||||
row.add_css_class(&format!("stagger-{idx}"));
|
||||
}
|
||||
let hbox = GBox::new(Orientation::Horizontal, 0);
|
||||
hbox.add_css_class("app-row");
|
||||
hbox.set_margin_start(6);
|
||||
hbox.set_margin_end(6);
|
||||
hbox.set_valign(gtk4::Align::Center);
|
||||
|
||||
let well = GBox::new(Orientation::Horizontal, 0);
|
||||
well.add_css_class("app-icon-well");
|
||||
well.set_size_request(38, 38);
|
||||
well.set_halign(gtk4::Align::Center);
|
||||
well.set_valign(gtk4::Align::Center);
|
||||
well.set_hexpand(false);
|
||||
let icon = make_icon(entry);
|
||||
icon.set_halign(gtk4::Align::Center);
|
||||
icon.set_valign(gtk4::Align::Center);
|
||||
icon.set_hexpand(true);
|
||||
well.append(&icon);
|
||||
hbox.append(&well);
|
||||
|
||||
let text = GBox::new(Orientation::Vertical, 1);
|
||||
text.add_css_class("app-text");
|
||||
text.set_hexpand(true);
|
||||
text.set_valign(gtk4::Align::Center);
|
||||
let icon = make_icon(&entry.icon_name, entry.icon_path.as_deref());
|
||||
hbox.append(&icon);
|
||||
|
||||
let name_lbl = Label::new(Some(&entry.name));
|
||||
name_lbl.add_css_class("app-name");
|
||||
name_lbl.set_xalign(0.0);
|
||||
name_lbl.set_hexpand(true);
|
||||
name_lbl.set_ellipsize(EllipsizeMode::End);
|
||||
text.append(&name_lbl);
|
||||
hbox.append(&name_lbl);
|
||||
|
||||
let sub_lbl = Label::new(Some(category_label(entry)));
|
||||
sub_lbl.add_css_class("app-muted");
|
||||
sub_lbl.set_xalign(0.0);
|
||||
sub_lbl.set_ellipsize(EllipsizeMode::End);
|
||||
text.append(&sub_lbl);
|
||||
if let Some(ref wm) = entry.wm_class {
|
||||
let wm_lbl = Label::new(Some(wm));
|
||||
wm_lbl.add_css_class("app-muted");
|
||||
wm_lbl.set_xalign(1.0);
|
||||
hbox.append(&wm_lbl);
|
||||
}
|
||||
|
||||
hbox.append(&text);
|
||||
row.set_child(Some(&hbox));
|
||||
unsafe { row.set_data("entry", entry.clone()) };
|
||||
unsafe { row.set_data("initial_order", idx as u32) };
|
||||
|
|
@ -554,16 +364,8 @@ fn run_ui(
|
|||
list.set_sort_func(move |row_a, row_b| {
|
||||
let query = sort_query.borrow();
|
||||
if query.is_empty() {
|
||||
let oa = unsafe {
|
||||
row_a
|
||||
.data::<u32>("initial_order")
|
||||
.map_or(u32::MAX, |p| *p.as_ref())
|
||||
};
|
||||
let ob = unsafe {
|
||||
row_b
|
||||
.data::<u32>("initial_order")
|
||||
.map_or(u32::MAX, |p| *p.as_ref())
|
||||
};
|
||||
let oa = unsafe { row_a.data::<u32>("initial_order").map_or(u32::MAX, |p| *p.as_ref()) };
|
||||
let ob = unsafe { row_b.data::<u32>("initial_order").map_or(u32::MAX, |p| *p.as_ref()) };
|
||||
return oa.cmp(&ob).into();
|
||||
}
|
||||
let (Some(ea), Some(eb)) = (get_row_entry(row_a), get_row_entry(row_b)) else {
|
||||
|
|
@ -586,66 +388,40 @@ fn run_ui(
|
|||
|
||||
scroll.set_child(Some(&list));
|
||||
vbox.append(&scroll);
|
||||
|
||||
let footer = Label::new(None);
|
||||
footer.add_css_class("launcher-footer");
|
||||
footer.set_xalign(0.0);
|
||||
set_footer_count(&footer, visible_row_count(&list));
|
||||
vbox.append(&footer);
|
||||
|
||||
window.set_child(Some(&vbox));
|
||||
|
||||
if !is_screenshot_run {
|
||||
let vbox_open = vbox.clone();
|
||||
glib::timeout_add_local_once(Duration::from_millis(520), move || {
|
||||
vbox_open.remove_css_class("just-opened");
|
||||
});
|
||||
}
|
||||
|
||||
// Filter on keystroke. ListBox keeps row identity across sort, so the
|
||||
// reorder is already a cheap FLIP analog; a short CSS fade is the extra.
|
||||
// Filter on keystroke
|
||||
let list_f = list.clone();
|
||||
let footer_f = footer.clone();
|
||||
let vbox_f = vbox.clone();
|
||||
let filter_query = Rc::clone(&query_rc);
|
||||
let reflow_gen = Rc::new(Cell::new(0u32));
|
||||
search.connect_changed(move |entry| {
|
||||
let text = entry.text();
|
||||
let query = text.as_str();
|
||||
*filter_query.borrow_mut() = query.to_string();
|
||||
|
||||
// Two passes: first collect each row's match tier, then decide
|
||||
// visibility — a tier-4 (bare subsequence) row only gets hidden
|
||||
// once we know whether some *other* row has a real tier ≤2 hit.
|
||||
let mut rows = Vec::new();
|
||||
let mut i = 0i32;
|
||||
while let Some(row) = list_f.row_at_index(i) {
|
||||
let vis = get_row_entry(&row)
|
||||
.map(|e| {
|
||||
fuzzy_matches(query, &e.name)
|
||||
|| fuzzy_matches(query, category_label(&e))
|
||||
|| e.wm_class
|
||||
.as_deref()
|
||||
.is_some_and(|w| fuzzy_matches(query, w))
|
||||
|| fuzzy_matches(query, &e.exec)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
row.set_visible(vis);
|
||||
let tier = get_row_entry(&row).and_then(|e| match_tier(query, &e));
|
||||
rows.push((row, tier));
|
||||
i += 1;
|
||||
}
|
||||
list_f.invalidate_sort();
|
||||
let first_vis =
|
||||
(0i32..).find_map(|j| list_f.row_at_index(j).filter(|r| r.is_visible()));
|
||||
list_f.select_row(first_vis.as_ref());
|
||||
set_footer_count(&footer_f, visible_row_count(&list_f));
|
||||
if !vbox_f.has_css_class("just-opened") {
|
||||
list_f.remove_css_class("reflow");
|
||||
list_f.add_css_class("reflow");
|
||||
let gen = reflow_gen.get().wrapping_add(1);
|
||||
reflow_gen.set(gen);
|
||||
let list_fade = list_f.clone();
|
||||
let reflow_gen = Rc::clone(&reflow_gen);
|
||||
glib::timeout_add_local_once(Duration::from_millis(180), move || {
|
||||
if reflow_gen.get() == gen {
|
||||
list_fade.remove_css_class("reflow");
|
||||
}
|
||||
});
|
||||
let has_direct_hit = rows.iter().any(|(_, t)| matches!(t, Some(0..=2)));
|
||||
for (row, tier) in &rows {
|
||||
let vis = match tier {
|
||||
None => false,
|
||||
Some(4) if has_direct_hit => false,
|
||||
Some(_) => true,
|
||||
};
|
||||
row.set_visible(vis);
|
||||
}
|
||||
list_f.invalidate_sort();
|
||||
let first_vis = (0i32..).find_map(|j| {
|
||||
list_f.row_at_index(j).filter(|r| r.is_visible())
|
||||
});
|
||||
list_f.select_row(first_vis.as_ref());
|
||||
});
|
||||
|
||||
// Keyboard handling — capture phase on window
|
||||
|
|
@ -698,67 +474,33 @@ fn run_ui(
|
|||
});
|
||||
|
||||
// Click outside launcher panel → close
|
||||
{
|
||||
let close_outside = Rc::clone(&close_all);
|
||||
bread_utils::gtk_popup::close_on_outside_click(&window, &vbox, move || close_outside());
|
||||
}
|
||||
|
||||
if let Some(req) = screenshot_req.clone() {
|
||||
screenshot::dispatch(&window, req);
|
||||
}
|
||||
let close_outside = Rc::clone(&close_all);
|
||||
bread_utils::gtk_popup::close_on_outside_click(&window, &vbox, move || close_outside());
|
||||
|
||||
window.present();
|
||||
search.grab_focus();
|
||||
});
|
||||
|
||||
if is_screenshot_run {
|
||||
// GLib's own option parser otherwise rejects --screenshot/--output
|
||||
// before clap ever sees them (`Cli::parse()` already ran in `main`,
|
||||
// over the real argv).
|
||||
app.run_with_args(&[] as &[&str]);
|
||||
} else {
|
||||
app.run();
|
||||
}
|
||||
app.run();
|
||||
}
|
||||
|
||||
// ---- Main -------------------------------------------------------------------
|
||||
|
||||
fn main() {
|
||||
if std::env::args().nth(1).as_deref() == Some("listen") {
|
||||
listen::run();
|
||||
return;
|
||||
}
|
||||
|
||||
use clap::Parser;
|
||||
let cli = screenshot::Cli::parse();
|
||||
let screenshot_req = cli.screenshot_request();
|
||||
|
||||
// `toggle_or_kill` kills whatever's holding the single-instance lock —
|
||||
// a real, already-running breadbox included. A screenshot run must
|
||||
// never touch it: it's a separate, disposable instance by design (same
|
||||
// reasoning as breadbar's `allow_multiple_instances`), not a toggle of
|
||||
// the operator's real launcher.
|
||||
//
|
||||
// Kept alive for the rest of `main` — dropping it releases the
|
||||
// single-instance lock and removes the pid file, which happens
|
||||
// naturally once `run_ui` returns (after the window closes).
|
||||
let _singleton_guard = if screenshot_req.is_some() {
|
||||
None
|
||||
} else {
|
||||
match bread_utils::singleton::toggle_or_kill("breadbox") {
|
||||
Ok(bread_utils::singleton::Toggle::Started(guard)) => Some(guard),
|
||||
Ok(bread_utils::singleton::Toggle::KilledExisting) => return,
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"breadbox: single-instance lock unavailable ({e}); continuing without it"
|
||||
);
|
||||
None
|
||||
}
|
||||
let _singleton_guard = match bread_utils::singleton::toggle_or_kill("breadbox") {
|
||||
Ok(bread_utils::singleton::Toggle::Started(guard)) => Some(guard),
|
||||
Ok(bread_utils::singleton::Toggle::KilledExisting) => return,
|
||||
Err(e) => {
|
||||
eprintln!("breadbox: single-instance lock unavailable ({e}); continuing without it");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let config = Config::load();
|
||||
let workspace = get_active_workspace().unwrap_or_default();
|
||||
let workspace = bread_utils::hypr::active_workspace_name().unwrap_or_default();
|
||||
let priority = config
|
||||
.context_for(&workspace)
|
||||
.map(|c| c.priority.clone())
|
||||
|
|
@ -768,5 +510,5 @@ fn main() {
|
|||
let manifest = load_manifest();
|
||||
let entries = load_sorted_entries(&manifest, &priority, &history);
|
||||
|
||||
run_ui(entries, history, screenshot_req);
|
||||
run_ui(entries, history);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
//! `--screenshot` CLI mode: render breadbox's launcher panel, capture it via
|
||||
//! `bread-screenshots`, then exit — driven by `bread-ecosystem`'s
|
||||
//! `bread-capture` orchestrator, or run standalone for one-off captures.
|
||||
//!
|
||||
//! breadbox has only one view worth capturing: the launcher panel itself
|
||||
//! (search box + app list). It's a `halign: Center` panel over a full-screen
|
||||
//! transparent overlay window, not its own layer surface, so — same
|
||||
//! reasoning as breadbar's control-panel view — the simplest reliable
|
||||
//! capture is the whole known-size canvas, not a hand-tracked panel
|
||||
//! geometry.
|
||||
|
||||
use bread_utils::screenshot_cli::{validate_pair, DEFAULT_HEIGHT, DEFAULT_WIDTH, SETTLE_DELAY};
|
||||
use clap::Parser;
|
||||
use gtk4::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "breadbox")]
|
||||
pub struct Cli {
|
||||
/// Render the named view, capture it, then exit instead of running
|
||||
/// normally. Known views: "launcher".
|
||||
#[arg(long)]
|
||||
pub screenshot: Option<String>,
|
||||
|
||||
/// PNG path to write the capture to. Required together with --screenshot.
|
||||
#[arg(long)]
|
||||
pub output: Option<PathBuf>,
|
||||
|
||||
/// Capture canvas width — matches the isolated compositor's output width
|
||||
/// (`bread-capture --isolate-width`).
|
||||
#[arg(long, default_value_t = DEFAULT_WIDTH)]
|
||||
pub width: u32,
|
||||
|
||||
/// Capture canvas height — see `width`.
|
||||
#[arg(long, default_value_t = DEFAULT_HEIGHT)]
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScreenshotRequest {
|
||||
pub view: String,
|
||||
pub output: PathBuf,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
/// `None` for a normal run. Exits the process with an error if the
|
||||
/// `--screenshot` / `--output` pair is incomplete, before any GTK setup
|
||||
/// happens.
|
||||
pub fn screenshot_request(&self) -> Option<ScreenshotRequest> {
|
||||
if let Err(e) = validate_pair(self.screenshot.as_deref(), self.output.as_deref()) {
|
||||
eprintln!("breadbox: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
Some(ScreenshotRequest {
|
||||
view: self.screenshot.clone()?,
|
||||
output: self.output.clone()?,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Wire up the given view's screenshot sequence against an already-built,
|
||||
/// not-yet-presented window. Every path here ends by exiting the process —
|
||||
/// it never returns control to the normal launcher UI.
|
||||
pub fn dispatch(window: >k4::ApplicationWindow, req: ScreenshotRequest) {
|
||||
match req.view.as_str() {
|
||||
"launcher" => {
|
||||
let output = req.output;
|
||||
let (width, height) = (req.width as i32, req.height as i32);
|
||||
window.connect_map(move |_| {
|
||||
let output = output.clone();
|
||||
gtk4::glib::timeout_add_local_once(SETTLE_DELAY, move || {
|
||||
finish(bread_screenshots::capture_region(0, 0, width, height, &output));
|
||||
});
|
||||
});
|
||||
}
|
||||
other => {
|
||||
eprintln!("breadbox: unknown screenshot view '{other}' (known: launcher)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(result: anyhow::Result<()>) {
|
||||
match result {
|
||||
Ok(()) => std::process::exit(0),
|
||||
Err(e) => {
|
||||
eprintln!("breadbox: screenshot capture failed: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
147cfbbf96ae4b171027defa1130d2caddb934b1
|
||||
21
ci/build.sh
21
ci/build.sh
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Delegates to bread-ecosystem's shared CI build image/script, pinned to
|
||||
# the commit in ci/bread-ecosystem.rev — not `main`. bread-ecosystem's CI
|
||||
# files now affect every product's release pipeline, so bumping the pin
|
||||
# is a deliberate act instead of silent drift (see the bread-theme test
|
||||
# that broke here for exactly that reason, before it was pinned by rev).
|
||||
#
|
||||
# Usage: ci/build.sh cargo build --release --locked
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
REV="$(cat "${ROOT}/ci/bread-ecosystem.rev")"
|
||||
|
||||
CACHE_DIR="/tmp/bread-ecosystem-ci-${REV}"
|
||||
if [ ! -d "$CACHE_DIR" ]; then
|
||||
rm -rf /tmp/bread-ecosystem-ci-*
|
||||
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "$CACHE_DIR"
|
||||
git -C "$CACHE_DIR" checkout --quiet "$REV"
|
||||
fi
|
||||
|
||||
bash "${CACHE_DIR}/ci/build.sh" breadbox "$ROOT" "$@"
|
||||
39
packaging/arch/PKGBUILD
Normal file
39
packaging/arch/PKGBUILD
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Maintainer: Breadway <plasticbread849@gmail.com>
|
||||
|
||||
pkgname=breadbox
|
||||
pkgver=0.1.0
|
||||
pkgrel=1
|
||||
pkgdesc="App launcher for Hyprland / Wayland"
|
||||
arch=('x86_64')
|
||||
url="https://git.breadway.dev/Breadway/breadbox"
|
||||
license=('MIT')
|
||||
# Some Rust deps (ring/mlua) build vendored C/asm into static archives; makepkg's
|
||||
# default -flto=auto emits GCC LTO bitcode the Rust (lld) link cannot read,
|
||||
# causing undefined-symbol errors. Disable LTO.
|
||||
options=(!lto !debug)
|
||||
depends=('gtk4' 'gtk4-layer-shell' 'librsvg')
|
||||
optdepends=(
|
||||
'hyprland: window and workspace integration'
|
||||
)
|
||||
makedepends=('rust' 'cargo')
|
||||
source=("${pkgname}-${pkgver}.tar.gz")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
cargo build --release --locked
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
cargo test --release --locked --workspace
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
install -Dm755 target/release/breadbox "${pkgdir}/usr/bin/breadbox"
|
||||
install -Dm755 target/release/breadbox-sync "${pkgdir}/usr/bin/breadbox-sync"
|
||||
install -Dm644 packaging/breadbox-sync.service \
|
||||
"${pkgdir}/usr/lib/systemd/user/breadbox-sync.service"
|
||||
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
[Unit]
|
||||
Description=Breadbox icon sync
|
||||
Documentation=https://github.com/breadway/breadbox
|
||||
Documentation=https://git.breadway.dev/Breadway/breadbox
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=%h/.cargo/bin/breadbox-sync
|
||||
ExecStart=/usr/bin/breadbox-sync
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
# Allow up to 2 minutes for slow icon downloads
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue