Compare commits
11 commits
cfca6161b0
...
4cb254801f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cb254801f | ||
|
|
8c11bfd3b8 | ||
|
|
aefe07009b | ||
|
|
5098780f5b | ||
|
|
08717e51c0 | ||
|
|
1f9707f30a | ||
|
|
19087b0168 | ||
|
|
dabd110f92 | ||
|
|
1e3872124b | ||
|
|
bf711f8047 | ||
|
|
13f5e509e4 |
9 changed files with 472 additions and 8 deletions
82
.forgejo/workflows/dev-release.yml
Normal file
82
.forgejo/workflows/dev-release.yml
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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 && cargo build --release --locked --workspace --features full
|
||||
|
||||
- name: test
|
||||
run: cd src && cargo test --release --locked --workspace --features full
|
||||
|
||||
- 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' breadsearch/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/breadsearch/${VERSION}"
|
||||
mkdir -p "${PKG_DIR}"
|
||||
for bin in breadsearch breadmill; 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/breadmill.service "${PKG_DIR}/"
|
||||
cp src/config.example.toml "${PKG_DIR}/"
|
||||
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||
ln -sfn "${VERSION}" "/srv/breadway-dl/dev/breadsearch/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}"
|
||||
63
.forgejo/workflows/rc-release.yml
Normal file
63
.forgejo/workflows/rc-release.yml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
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 && cargo build --release --locked --workspace --features full
|
||||
|
||||
- name: test
|
||||
run: cd src && cargo test --release --locked --workspace --features full
|
||||
|
||||
- name: prepare artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${GITHUB_REF_NAME#v}"
|
||||
PKG_DIR="/srv/breadway-dl/beta/breadsearch/${VERSION}"
|
||||
mkdir -p "${PKG_DIR}"
|
||||
for bin in breadsearch breadmill; 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/breadmill.service "${PKG_DIR}/"
|
||||
cp src/config.example.toml "${PKG_DIR}/"
|
||||
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||
ln -sfn "${VERSION}" "/srv/breadway-dl/beta/breadsearch/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,6 +6,7 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ !contains(github.ref_name, '-rc.') }}
|
||||
runs-on: [self-hosted, hestia]
|
||||
steps:
|
||||
- name: checkout
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -29,3 +29,6 @@ logs/
|
|||
# Runtime files
|
||||
*.sock
|
||||
*.pid
|
||||
|
||||
# Local hygiene notes (not for commit)
|
||||
CLAUDE.md
|
||||
|
|
|
|||
84
CONTRIBUTING.md
Normal file
84
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Contributing
|
||||
|
||||
`breadsearch` — Semantic system-wide search for BOS.
|
||||
|
||||
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 --features full
|
||||
cargo test --release --workspace --features full
|
||||
```
|
||||
|
||||
## 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.
|
||||
107
Cargo.lock
generated
107
Cargo.lock
generated
|
|
@ -51,12 +51,56 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[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.103"
|
||||
|
|
@ -141,7 +185,7 @@ version = "0.3.0"
|
|||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bread-utils",
|
||||
"bread-utils 0.3.0",
|
||||
"hex",
|
||||
"ort",
|
||||
"sha2 0.10.9",
|
||||
|
|
@ -150,6 +194,16 @@ dependencies = [
|
|||
"ureq 2.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-screenshots"
|
||||
version = "0.3.1"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bread-utils 0.3.1",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-theme"
|
||||
version = "0.2.3"
|
||||
|
|
@ -171,6 +225,16 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-utils"
|
||||
version = "0.3.1"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "breadmill"
|
||||
version = "0.3.0"
|
||||
|
|
@ -198,8 +262,11 @@ dependencies = [
|
|||
name = "breadsearch"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bread-screenshots",
|
||||
"bread-theme",
|
||||
"breadsearch-shared",
|
||||
"clap",
|
||||
"gtk4",
|
||||
"gtk4-layer-shell",
|
||||
"serde_json",
|
||||
|
|
@ -364,6 +431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -372,11 +440,24 @@ version = "4.6.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.0"
|
||||
|
|
@ -394,6 +475,12 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.9.1"
|
||||
|
|
@ -1612,6 +1699,12 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
|
|
@ -2034,6 +2127,12 @@ 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 = "onig"
|
||||
version = "6.5.3"
|
||||
|
|
@ -3313,6 +3412,12 @@ 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 = "valuable"
|
||||
version = "0.1.1"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ path = "src/main.rs"
|
|||
[dependencies]
|
||||
breadsearch-shared = { path = "../breadsearch-shared" }
|
||||
bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.10", features = ["gtk"] }
|
||||
# Capture primitives for `--screenshot` mode — see src/screenshot.rs.
|
||||
bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "dev" }
|
||||
gtk4 = { version = "0.11", features = ["v4_12"] }
|
||||
gtk4-layer-shell = "0.8"
|
||||
serde_json = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
anyhow = "1"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ use gtk4::{
|
|||
};
|
||||
use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
|
||||
|
||||
mod screenshot;
|
||||
|
||||
// ---- Theming ----------------------------------------------------------------
|
||||
|
||||
fn build_css(p: &Palette) -> String {
|
||||
|
|
@ -208,10 +210,17 @@ fn open_folder(path: &str) {
|
|||
|
||||
// ---- UI ---------------------------------------------------------------------
|
||||
|
||||
fn run_ui() {
|
||||
let app = Application::builder()
|
||||
.application_id("com.breadway.breadsearch")
|
||||
.build();
|
||||
fn run_ui(screenshot_req: Option<screenshot::ScreenshotRequest>) {
|
||||
let mut builder = Application::builder().application_id("com.breadway.breadsearch");
|
||||
if screenshot_req.is_some() {
|
||||
// GApplication is single-instance by default; this machine typically
|
||||
// already has a real breadsearch 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();
|
||||
let is_screenshot_run = screenshot_req.is_some();
|
||||
|
||||
let debounce_id: Rc<RefCell<Option<glib::SourceId>>> = Rc::new(RefCell::new(None));
|
||||
|
||||
|
|
@ -419,18 +428,37 @@ fn run_ui() {
|
|||
window.add_controller(outside_click);
|
||||
|
||||
window.connect_destroy(|_| cleanup_pid());
|
||||
|
||||
if let Some(req) = screenshot_req.clone() {
|
||||
screenshot::dispatch(&window, req);
|
||||
}
|
||||
|
||||
window.present();
|
||||
search.grab_focus();
|
||||
});
|
||||
|
||||
app.run();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Main -------------------------------------------------------------------
|
||||
|
||||
fn main() {
|
||||
if !toggle_or_continue() {
|
||||
use clap::Parser;
|
||||
let cli = screenshot::Cli::parse();
|
||||
let screenshot_req = cli.screenshot_request();
|
||||
|
||||
// The PID-file toggle kills whatever's holding the file — a real,
|
||||
// already-running breadsearch instance included. A screenshot run must
|
||||
// never touch it: it's a separate, disposable instance by design.
|
||||
if screenshot_req.is_none() && !toggle_or_continue() {
|
||||
return;
|
||||
}
|
||||
run_ui();
|
||||
run_ui(screenshot_req);
|
||||
}
|
||||
|
|
|
|||
94
breadsearch/src/screenshot.rs
Normal file
94
breadsearch/src/screenshot.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//! `--screenshot` CLI mode: render breadsearch's search panel, capture it
|
||||
//! via `bread-screenshots`, then exit — driven by `bread-ecosystem`'s
|
||||
//! `bread-capture` orchestrator, or run standalone for one-off captures.
|
||||
//!
|
||||
//! Same reasoning as breadbox: one view ("search"), full known-size canvas
|
||||
//! capture since the panel isn't its own layer surface. Captures whatever
|
||||
//! the panel shows at settle time — normally the "Type to search…" empty
|
||||
//! state, since there's no query to type in an automated run.
|
||||
|
||||
use clap::Parser;
|
||||
use gtk4::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Extra settle time after `map` for the first frame to actually paint
|
||||
/// before grim runs — `map` fires once the surface exists, not once
|
||||
/// anything has been drawn into it.
|
||||
const SETTLE_DELAY: Duration = Duration::from_millis(300);
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "breadsearch")]
|
||||
pub struct Cli {
|
||||
/// Render the named view, capture it, then exit instead of running
|
||||
/// normally. Known views: "search".
|
||||
#[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 = 1920)]
|
||||
pub width: u32,
|
||||
|
||||
/// Capture canvas height — see `width`.
|
||||
#[arg(long, default_value_t = 1080)]
|
||||
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
|
||||
/// `--screenshot` was given without `--output`, before any GTK setup
|
||||
/// happens.
|
||||
pub fn screenshot_request(&self) -> Option<ScreenshotRequest> {
|
||||
let view = self.screenshot.clone()?;
|
||||
let Some(output) = self.output.clone() else {
|
||||
eprintln!("breadsearch: --screenshot requires --output");
|
||||
std::process::exit(1);
|
||||
};
|
||||
Some(ScreenshotRequest { view, output, 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 search UI.
|
||||
pub fn dispatch(window: >k4::ApplicationWindow, req: ScreenshotRequest) {
|
||||
match req.view.as_str() {
|
||||
"search" => {
|
||||
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!("breadsearch: unknown screenshot view '{other}' (known: search)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(result: anyhow::Result<()>) {
|
||||
match result {
|
||||
Ok(()) => std::process::exit(0),
|
||||
Err(e) => {
|
||||
eprintln!("breadsearch: screenshot capture failed: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue