From e5922e9c905a880112769a0f53e04ebb4e96a2df Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 3 Jul 2026 22:38:41 +0800 Subject: [PATCH 01/32] Make ORT_MIGRAPHX_MODEL_CACHE_PATH unconditional in packaged systemd unit Without a valid MIGraphX cache dir, ONNX Runtime's MIGraphX EP reads an uninitialized cache path and crashes on the first query rather than just recompiling on every restart -- confirmed by hand while verifying the rocm backend. Previously commented out as an opt-in perf tweak; it's actually required for the rocm backend to work at all, so bakery-installed systems need it set by default. No-op for cpu/npu/cuda backends. Version bump: breadmill 0.2.1 -> 0.2.2. --- Cargo.lock | 2 +- breadmill/Cargo.toml | 2 +- packaging/breadmill.service | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e7fa2b..5918b71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "breadmill" -version = "0.2.1" +version = "0.2.2" dependencies = [ "breadsearch-shared", "hex", diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index 5e3822b..dcff332 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadmill" -version = "0.2.1" +version = "0.2.2" edition = "2021" license = "MIT" diff --git a/packaging/breadmill.service b/packaging/breadmill.service index 70f81e6..a1da0b6 100644 --- a/packaging/breadmill.service +++ b/packaging/breadmill.service @@ -5,10 +5,11 @@ After=default.target [Service] Type=simple -# Uncomment if built with --features rocm: persists MIGraphX's compiled-kernel -# cache across restarts (each new sequence length otherwise costs a ~60-120s -# recompile). See README.md#gpu-backend-notes. -#Environment=ORT_MIGRAPHX_MODEL_CACHE_PATH=%h/.cache/breadsearch/migraphx-cache +# Required (not just a perf tweak) when backend = "rocm": without a valid +# cache dir, ONNX Runtime's MIGraphX EP reads an uninitialized cache path and +# crashes on the first query instead of just recompiling every restart. Inert +# for cpu/npu/cuda backends. See README.md#gpu-backend-notes. +Environment=ORT_MIGRAPHX_MODEL_CACHE_PATH=%h/.cache/breadsearch/migraphx-cache ExecStart=%h/.cargo/bin/breadmill Restart=on-failure RestartSec=5 From c6ed6a41d818aab32dc6268f7a9664068df5f635 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 3 Jul 2026 22:58:27 +0800 Subject: [PATCH 02/32] Add OpenVINO backend for Intel iGPU/dGPU (Arc) Mirrors the rocm/cuda pattern: ort::ep::OpenVINO with device_type "GPU" (covers both Intel integrated graphics and Arc discrete GPUs through the same EP), load-dynamic/dlopen so no OpenVINO toolkit is needed at build time, and its own --openvino flag / backend = "openvino" config value. Folded into the `full` feature alongside npu/rocm/cuda. OpenVINO's provider options go through a generic key/value FFI interface rather than a fixed C struct (unlike MIGraphX's OrtMIGraphXProviderOptions), so it should be less exposed to the ABI-version-skew crash MIGraphX hit -- but that's inference from the EP's design, not verified against real hardware. Like CUDA, this is compile-checked only: no Intel GPU in this dev environment to runtime-verify against. Version bump: breadmill 0.2.2 -> 0.2.3. --- Cargo.lock | 2 +- DESIGN.md | 6 ++-- README.md | 57 +++++++++++++++++++++++------------ breadmill/Cargo.toml | 16 ++++++---- breadmill/src/embed.rs | 36 ++++++++++++++++++++++ breadmill/src/main.rs | 24 ++++++++++++--- breadsearch-shared/src/lib.rs | 3 +- packaging/breadmill.service | 3 +- 8 files changed, 112 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5918b71..be95ae6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "breadmill" -version = "0.2.2" +version = "0.2.3" dependencies = [ "breadsearch-shared", "hex", diff --git a/DESIGN.md b/DESIGN.md index 5ffa28e..85d7e6b 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -80,7 +80,9 @@ Start from `breadbox/breadbox/src/main.rs`. **Reuse verbatim:** the gtk4-layer-s - nomic prefixes + mean-pool + normalize must match between index and query or recall collapses. - `ort` linking: prefer the crate's downloaded/bundled ONNX Runtime to avoid version skew with Arch's `onnxruntime`. - Office formats (docx/odt) are best-effort in v1; md/txt/org/pdf are the reliable path. -- GPU EPs (ROCm/CUDA) fail to register silently at the ONNX Runtime level and fall back to CPU — always check - startup logs for `Successfully registered` before trusting a GPU build is actually accelerating. See +- GPU EPs (ROCm/CUDA/OpenVINO) fail to register silently at the ONNX Runtime level and fall back to CPU — always + check startup logs for `Successfully registered` before trusting a GPU build is actually accelerating. See [README: GPU backend notes](README.md#gpu-backend-notes) for the MIGraphX-vs-ROCMExecutionProvider distinction and the per-shape JIT-compile-and-cache behavior that matters for interactive query latency. +- CUDA and OpenVINO are compile-checked only — no NVIDIA or Intel GPU hardware in this dev environment (AMD-only) + to runtime-verify against, unlike ROCm which was confirmed end-to-end on real hardware. diff --git a/README.md b/README.md index 446787d..af25de7 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,11 @@ Optional features: | Feature | What it adds | |---------|-------------| -| `npu` | AMD XDNA NPU via VitisAI ONNX Runtime EP (requires Ryzen AI SDK) | -| `rocm` | AMD iGPU via the MIGraphX ONNX Runtime EP (ROCm-backed) | -| `cuda` | NVIDIA GPU via the CUDA ONNX Runtime EP | -| `full` | All three of the above in one binary | +| `npu` | AMD XDNA NPU via VitisAI ONNX Runtime EP (requires Ryzen AI SDK) | +| `rocm` | AMD iGPU via the MIGraphX ONNX Runtime EP (ROCm-backed) | +| `cuda` | NVIDIA GPU via the CUDA ONNX Runtime EP | +| `openvino` | Intel iGPU/dGPU (Arc) via the OpenVINO ONNX Runtime EP | +| `full` | All four of the above in one binary | ``` # NPU build @@ -36,25 +37,29 @@ cargo build --release -p breadmill --features rocm # CUDA (NVIDIA GPU) build cargo build --release -p breadmill --features cuda +# OpenVINO (Intel iGPU/dGPU) build +cargo build --release -p breadmill --features openvino + # All backends in one binary (what the release build ships) cargo build --release -p breadmill --features full ``` -`rocm`/`cuda`/`npu` all use `ort`'s `load-dynamic` mode: at runtime, breadmill -dlopens whatever `libonnxruntime.so` the dynamic linker resolves (or +`rocm`/`cuda`/`npu`/`openvino` all use `ort`'s `load-dynamic` mode: at runtime, +breadmill dlopens whatever `libonnxruntime.so` the dynamic linker resolves (or `ORT_DYLIB_PATH` if set). GPU acceleration only works if that ONNX Runtime build actually has the matching execution provider compiled in — breadmill logs a clear `Successfully registered` / `not enabled in this build` line for this at startup (see [GPU backend notes](#gpu-backend-notes) below). -Because all three are dlopen-based, `full` doesn't require the NPU/ROCm/CUDA -toolkits to be installed at build time — only at run time, and only for -whichever single backend you actually select via `--npu`/`--rocm`/`--cuda` -or `backend` in config.toml. The **released binaries are built with -`full`**: same binary works CPU-only out of the box, and picks up NPU/ROCm/CUDA -acceleration on a machine that has the matching ONNX Runtime available, -without needing a different download. An explicit `--npu`/`--rocm`/`--cuda` -flag always overrides `backend` in config.toml, not the other way around. +Because all four are dlopen-based, `full` doesn't require the NPU/ROCm/CUDA/ +OpenVINO toolkits to be installed at build time — only at run time, and only +for whichever single backend you actually select via +`--npu`/`--rocm`/`--cuda`/`--openvino` or `backend` in config.toml. The +**released binaries are built with `full`**: same binary works CPU-only out +of the box, and picks up NPU/ROCm/CUDA/OpenVINO acceleration on a machine +that has the matching ONNX Runtime available, without needing a different +download. An explicit `--npu`/`--rocm`/`--cuda`/`--openvino` flag always +overrides `backend` in config.toml, not the other way around. ## Setup @@ -117,6 +122,7 @@ breadmill status breadmill --npu breadmill --rocm breadmill --cuda +breadmill --openvino ``` ## Config @@ -137,7 +143,7 @@ snippet_len = 200 # max characters in result snippet [model] name = "nomic-embed-text-v1.5" dim = 768 -backend = "cpu" # "cpu", "npu", "rocm", or "cuda" +backend = "cpu" # "cpu", "npu", "rocm", "cuda", or "openvino" ``` `roots` and `excludes` support `~/` expansion. The index respects `.gitignore` files found during the walk. @@ -154,10 +160,11 @@ Set `backend = "npu"` in config (or pass `--npu`) when running a build compiled ### GPU backend notes -Both `rocm` and `cuda` need a system ONNX Runtime that was actually built with -the matching execution provider — the crate's own downloaded binary is CPU-only. -Point `ORT_DYLIB_PATH` at one, or install a distro package that provides -`libonnxruntime.so` with the EP baked in and let the dynamic linker find it. +`rocm`, `cuda`, and `openvino` all need a system ONNX Runtime that was +actually built with the matching execution provider — the crate's own +downloaded binary is CPU-only. Point `ORT_DYLIB_PATH` at one, or install a +distro package that provides `libonnxruntime.so` with the EP baked in and +let the dynamic linker find it. **ROCm (`--rocm` / `backend = "rocm"`)** targets ONNX Runtime's **MIGraphX** execution provider, not the classic `ROCMExecutionProvider`. Distro @@ -183,6 +190,18 @@ noticeable for interactive query embedding. CUDA/cuDNN install. Unverified on real NVIDIA hardware in this repo — only compile-checked, since development happened on an AMD-only machine. +**OpenVINO (`--openvino` / `backend = "openvino"`)** targets +`OpenVINOExecutionProvider` with `device_type = "GPU"`, covering both Intel +iGPUs and Arc dGPUs through the same EP (OpenVINO abstracts Intel's whole +hardware line — CPU/GPU/NPU — behind one provider and a device-type string). +Needs an OpenVINO-enabled ONNX Runtime and the OpenVINO runtime itself +installed. Its options go through a generic key/value FFI interface rather +than a fixed C struct (unlike MIGraphX's `OrtMIGraphXProviderOptions`), so it +should be less exposed to the kind of ABI-version-skew crash MIGraphX hit — +but that's inference from reading the EP's design, not something verified +against real Intel GPU hardware. Also unverified on real hardware in this +repo — only compile-checked, for the same reason as CUDA. + ## Runtime paths | Purpose | Path | diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index dcff332..a76d28d 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadmill" -version = "0.2.2" +version = "0.2.3" edition = "2021" license = "MIT" @@ -14,13 +14,17 @@ npu = ["ort/vitis", "ort/load-dynamic"] # libonnxruntime_providers_rocm.so, which most distros don't package. rocm = ["ort/migraphx", "ort/load-dynamic"] cuda = ["ort/cuda", "ort/load-dynamic"] +# Intel iGPU/dGPU (Arc) + CPU via OpenVINO. One EP covers Intel's whole +# hardware line by device_type string ("CPU"/"GPU"/"GPU.0"/"NPU"/"HETERO:..."); +# we always request "GPU" since CPU is already covered by the cpu backend. +openvino = ["ort/openvino", "ort/load-dynamic"] # All backends in one binary. Safe to combine: every backend here uses # ort's load-dynamic (dlopen) mode, so none of this links against an actual -# NPU/ROCm/CUDA toolkit at build time — which ONNX Runtime actually gets -# loaded (and thus which EPs are really available) is decided at runtime by -# ORT_DYLIB_PATH / the dynamic linker, per the --npu/--rocm/--cuda flag or -# `backend` config value in use for that run. -full = ["npu", "rocm", "cuda"] +# NPU/ROCm/CUDA/OpenVINO toolkit at build time — which ONNX Runtime actually +# gets loaded (and thus which EPs are really available) is decided at +# runtime by ORT_DYLIB_PATH / the dynamic linker, per the --npu/--rocm/ +# --cuda/--openvino flag or `backend` config value in use for that run. +full = ["npu", "rocm", "cuda", "openvino"] [[bin]] name = "breadmill" diff --git a/breadmill/src/embed.rs b/breadmill/src/embed.rs index 9700d6c..3020e58 100644 --- a/breadmill/src/embed.rs +++ b/breadmill/src/embed.rs @@ -27,6 +27,11 @@ pub enum Backend { Rocm, /// NVIDIA GPU via the CUDA ONNX Runtime execution provider. Cuda, + /// Intel iGPU/dGPU (Arc) via the OpenVINO ONNX Runtime execution + /// provider, requesting device_type "GPU". `cache_dir` stores OpenVINO's + /// compiled-model blobs between runs (its own `with_cache_dir`, not an + /// env var — unlike MIGraphX this doesn't need a workaround). + OpenVino { cache_dir: PathBuf }, } pub struct OrtEmbedder { @@ -165,6 +170,7 @@ fn configure_eps(builder: SessionBuilder, backend: &Backend) -> Result npu_session(builder, cache_dir), Backend::Rocm => rocm_session(builder), Backend::Cuda => cuda_session(builder), + Backend::OpenVino { cache_dir } => openvino_session(builder, cache_dir), } } @@ -253,6 +259,36 @@ fn cuda_session(builder: SessionBuilder) -> Result { Ok(builder) } +// ---- OpenVINO EP (Intel iGPU/dGPU) ------------------------------------------- + +#[cfg(feature = "openvino")] +fn openvino_session(builder: SessionBuilder, cache_dir: &Path) -> Result { + let ov_cache = cache_dir.join("openvino"); + std::fs::create_dir_all(&ov_cache).map_err(|e| e.to_string())?; + eprintln!("breadmill: using OpenVINO execution provider (device_type GPU)"); + eprintln!("breadmill: OpenVINO model cache: {}", ov_cache.display()); + eprintln!( + "breadmill: note — check the log line above/below for \"Successfully registered \ + `OpenVINOExecutionProvider`\"; if it's missing, the ONNX Runtime in use wasn't built \ + with OpenVINO support and inference silently fell back to CPU" + ); + builder + .with_execution_providers([ + ort::ep::OpenVINO::default() + .with_device_type("GPU") + .with_cache_dir(ov_cache.to_string_lossy()) + .build(), + ort::ep::CPU::default().build(), + ]) + .map_err(|e| e.to_string()) +} + +#[cfg(not(feature = "openvino"))] +fn openvino_session(builder: SessionBuilder, _cache_dir: &Path) -> Result { + eprintln!("breadmill: OpenVINO backend requested but not compiled in (rebuild with --features openvino); using CPU"); + Ok(builder) +} + /// Locate the VitisAI EP config file required by the AMD Ryzen AI SDK. /// /// Search order: diff --git a/breadmill/src/main.rs b/breadmill/src/main.rs index 8d2e9bd..12f0f99 100644 --- a/breadmill/src/main.rs +++ b/breadmill/src/main.rs @@ -39,12 +39,14 @@ fn main() { let use_npu = raw_args.iter().any(|a| a == "--npu"); let use_rocm = raw_args.iter().any(|a| a == "--rocm"); let use_cuda = raw_args.iter().any(|a| a == "--cuda"); + let use_openvino = raw_args.iter().any(|a| a == "--openvino"); // Build a view of argv without backend flags for command matching. + let backend_flags = ["--npu", "--rocm", "--cuda", "--openvino"]; let args: Vec<&str> = raw_args .iter() .skip(1) - .filter(|a| a.as_str() != "--npu" && a.as_str() != "--rocm" && a.as_str() != "--cuda") + .filter(|a| !backend_flags.contains(&a.as_str())) .map(|s| s.as_str()) .collect(); @@ -59,7 +61,7 @@ fn main() { } } Some("--reindex") | Some("reindex") => { - if let Err(e) = run_daemon(true, use_npu, use_rocm, use_cuda) { + if let Err(e) = run_daemon(true, use_npu, use_rocm, use_cuda, use_openvino) { eprintln!("breadmill: {}", e); std::process::exit(1); } @@ -76,7 +78,7 @@ fn main() { cli_status(); } None | Some("serve") | Some("--serve") => { - if let Err(e) = run_daemon(false, use_npu, use_rocm, use_cuda) { + if let Err(e) = run_daemon(false, use_npu, use_rocm, use_cuda, use_openvino) { eprintln!("breadmill: {}", e); std::process::exit(1); } @@ -84,7 +86,7 @@ fn main() { Some(cmd) => { eprintln!("breadmill: unknown command: {}", cmd); eprintln!( - "usage: breadmill [serve|reindex|fetch-model|query |status] [--npu|--rocm|--cuda] [--version]" + "usage: breadmill [serve|reindex|fetch-model|query |status] [--npu|--rocm|--cuda|--openvino] [--version]" ); std::process::exit(1); } @@ -93,7 +95,13 @@ fn main() { // ---- Daemon ----------------------------------------------------------------- -fn run_daemon(force_reindex: bool, use_npu: bool, use_rocm: bool, use_cuda: bool) -> Result<(), String> { +fn run_daemon( + force_reindex: bool, + use_npu: bool, + use_rocm: bool, + use_cuda: bool, + use_openvino: bool, +) -> Result<(), String> { let config = breadsearch_shared::Config::load(); let state_dir = breadsearch_shared::state_dir(); let cache_dir = breadsearch_shared::cache_dir(); @@ -115,6 +123,8 @@ fn run_daemon(force_reindex: bool, use_npu: bool, use_rocm: bool, use_cuda: bool "rocm" } else if use_cuda { "cuda" + } else if use_openvino { + "openvino" } else { config.model.backend.as_str() }; @@ -132,6 +142,10 @@ fn run_daemon(force_reindex: bool, use_npu: bool, use_rocm: bool, use_cuda: bool eprintln!("breadmill: CUDA backend selected"); Backend::Cuda } + "openvino" => { + eprintln!("breadmill: OpenVINO backend selected"); + Backend::OpenVino { cache_dir: cache_dir.clone() } + } _ => Backend::Cpu, }; diff --git a/breadsearch-shared/src/lib.rs b/breadsearch-shared/src/lib.rs index 7b8b625..fdea6f5 100644 --- a/breadsearch-shared/src/lib.rs +++ b/breadsearch-shared/src/lib.rs @@ -129,7 +129,8 @@ pub struct ModelConfig { pub name: String, #[serde(default = "default_dim")] pub dim: usize, - /// Compute backend: "cpu", "npu" (VitisAI/XDNA), "rocm" (MIGraphX/AMD GPU), or "cuda" (NVIDIA GPU). + /// Compute backend: "cpu", "npu" (VitisAI/XDNA), "rocm" (MIGraphX/AMD GPU), + /// "cuda" (NVIDIA GPU), or "openvino" (Intel iGPU/dGPU). #[serde(default = "default_backend")] pub backend: String, } diff --git a/packaging/breadmill.service b/packaging/breadmill.service index a1da0b6..69c928f 100644 --- a/packaging/breadmill.service +++ b/packaging/breadmill.service @@ -8,7 +8,8 @@ Type=simple # Required (not just a perf tweak) when backend = "rocm": without a valid # cache dir, ONNX Runtime's MIGraphX EP reads an uninitialized cache path and # crashes on the first query instead of just recompiling every restart. Inert -# for cpu/npu/cuda backends. See README.md#gpu-backend-notes. +# for cpu/npu/cuda/openvino backends (openvino's cache dir is set in code, +# not via env var). See README.md#gpu-backend-notes. Environment=ORT_MIGRAPHX_MODEL_CACHE_PATH=%h/.cache/breadsearch/migraphx-cache ExecStart=%h/.cargo/bin/breadmill Restart=on-failure From 3d83bd747e6fcd4d2f00f1d0b6cacac0d462c450 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 5 Jul 2026 09:13:54 +0800 Subject: [PATCH 03/32] breadmill 0.2.4: bind socket before loading the embedding model The model load ran synchronously on the main thread before serve::run() bound the daemon's socket. A slow or stuck EP compile (OpenVINO in particular) meant the socket didn't exist for as long as that took, so every client -- including the breadsearch GUI -- saw a bare connection refused with no way to tell "still loading" from "actually broken." The load now happens on a background thread; the socket binds immediately, and serve.rs's existing model_ready check answers "model not ready" for any request that arrives before the load finishes. --- Cargo.lock | 2 +- breadmill/Cargo.toml | 2 +- breadmill/src/main.rs | 28 +++++++++++++++++++--------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be95ae6..768ecb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "breadmill" -version = "0.2.3" +version = "0.2.4" dependencies = [ "breadsearch-shared", "hex", diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index a76d28d..768735d 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadmill" -version = "0.2.3" +version = "0.2.4" edition = "2021" license = "MIT" diff --git a/breadmill/src/main.rs b/breadmill/src/main.rs index 12f0f99..da5814d 100644 --- a/breadmill/src/main.rs +++ b/breadmill/src/main.rs @@ -152,21 +152,31 @@ fn run_daemon( let store = Store::open(&state_dir, dim)?; let state = Arc::new(SharedState::new(store)); - // Load embedder if model files present + // Load the embedder on a background thread — an OpenVINO/CUDA/etc EP + // compile can take minutes or hang outright, and doing this inline used + // to block the socket bind below until it finished. That turned "model + // still loading" into an indistinguishable "connection refused" for + // every client, including the GUI, for as long as the load took. The + // socket now opens immediately; serve.rs already answers "model not + // ready" (via `model_ready`) for any request that arrives before the + // background load finishes. let model_dir = model_dir(&cache_dir); let model_path = model_dir.join("model.onnx"); let tokenizer_path = model_dir.join("tokenizer.json"); if model_path.exists() && tokenizer_path.exists() { - eprintln!("breadmill: loading model..."); - match OrtEmbedder::load(&model_path, &tokenizer_path, dim, backend) { - Ok(embedder) => { - *state.embedder.lock().unwrap() = Some(embedder); - state.model_ready.store(true, Ordering::Relaxed); - eprintln!("breadmill: model loaded"); + let state_clone = Arc::clone(&state); + std::thread::spawn(move || { + eprintln!("breadmill: loading model..."); + match OrtEmbedder::load(&model_path, &tokenizer_path, dim, backend) { + Ok(embedder) => { + *state_clone.embedder.lock().unwrap() = Some(embedder); + state_clone.model_ready.store(true, Ordering::Relaxed); + eprintln!("breadmill: model loaded"); + } + Err(e) => eprintln!("breadmill: model load failed: {} — run --fetch-model", e), } - Err(e) => eprintln!("breadmill: model load failed: {} — run --fetch-model", e), - } + }); } else { eprintln!( "breadmill: model files not found in {} — run: breadmill --fetch-model", From d01a3841d967ffd272000f201d13495ec3963829 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 17 Jul 2026 08:16:25 +0800 Subject: [PATCH 04/32] breadmill/breadsearch: narrow embedder lock scope, recover from poisoned mutexes, atomic index save + corrupt rebuild, fix GUI busy-poll, bump theme pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - indexer.rs: embedder mutex is now locked only around each chunk's embed_document() call instead of the whole file's chunk loop, so a slow MIGraphX JIT compile on one file no longer blocks every query for minutes - sync_ext.rs (new)/indexer.rs/main.rs/serve.rs: added MutexExt::lock_recover(), replacing every .lock().unwrap() so a panic on one thread (poisoning the mutex) logs a warning and recovers instead of cascading into every future lock().unwrap() call - store.rs: save_index now writes to a .tmp sibling and renames atomically into place; Store::open recovers from an index that fails to load by wiping it and the SQLite files/chunks tables so the next scan rebuilds from scratch, instead of refusing to start at all. Note: this recovery only catches corruption usearch's loader reports as Err — verified experimentally that sufficiently garbled input segfaults the process instead, which no Rust-side handling can catch; the atomic save is what actually prevents the realistic mid-crash corruption case from arising - breadsearch/src/main.rs: GUI query-result wait switched from glib::idle_add_local (re-invoked every main-loop tick, pegging a core for the whole wait) to a 15ms glib::timeout_add_local poll - breadsearch/Cargo.toml: bread-theme pin bumped v0.2.8 -> v0.2.10 to match the rest of the family --- Cargo.lock | 2 +- breadmill/src/indexer.rs | 45 +++++++++++++-------- breadmill/src/main.rs | 4 +- breadmill/src/serve.rs | 5 ++- breadmill/src/store.rs | 82 ++++++++++++++++++++++++++++++++++++--- breadmill/src/sync_ext.rs | 41 ++++++++++++++++++++ breadsearch/Cargo.toml | 2 +- breadsearch/src/main.rs | 13 +++++-- 8 files changed, 164 insertions(+), 30 deletions(-) create mode 100644 breadmill/src/sync_ext.rs diff --git a/Cargo.lock b/Cargo.lock index 768ecb2..a5395bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,7 +123,7 @@ dependencies = [ [[package]] name = "bread-theme" version = "0.2.3" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.8#5e58558dd36031433d4a8d8e70c71206c3f1f8f4" +source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.10#17d1bb85801b9a8c195b64c02d288cd662c9c780" dependencies = [ "dirs", "gtk4", diff --git a/breadmill/src/indexer.rs b/breadmill/src/indexer.rs index 3ad20b5..539b58c 100644 --- a/breadmill/src/indexer.rs +++ b/breadmill/src/indexer.rs @@ -10,7 +10,7 @@ use ignore::WalkBuilder; use notify::{RecommendedWatcher, RecursiveMode, Watcher, EventKind}; use sha2::{Digest, Sha256}; -use crate::{embed::OrtEmbedder, extract, chunk, power, store::Store}; +use crate::{embed::OrtEmbedder, extract, chunk, power, store::Store, sync_ext::MutexExt}; pub struct SharedState { pub store: Mutex, @@ -64,7 +64,7 @@ impl Indexer { pub fn full_reindex(&self) { eprintln!("breadmill: full reindex triggered"); { - let mut store = self.state.store.lock().unwrap(); + let mut store = self.state.store.lock_recover(); // Clear all state let _ = store.conn.execute_batch("DELETE FROM chunks; DELETE FROM files;"); let _ = store.index.reserve(4096); @@ -87,7 +87,7 @@ impl Indexer { // Snapshot existing indexed files let known: HashMap = { - let store = self.state.store.lock().unwrap(); + let store = self.state.store.lock_recover(); store.all_files() .unwrap_or_default() .into_iter() @@ -155,7 +155,7 @@ impl Indexer { .collect(); if !to_delete.is_empty() { - let mut store = self.state.store.lock().unwrap(); + let mut store = self.state.store.lock_recover(); for path in to_delete { eprintln!("breadmill: removing deleted file: {}", path); let _ = store.delete_file(&path); @@ -163,7 +163,7 @@ impl Indexer { } let count = { - let store = self.state.store.lock().unwrap(); + let store = self.state.store.lock_recover(); let n = store.chunk_count(); let _ = store.save_index(&self.state_dir); n @@ -238,7 +238,7 @@ impl Indexer { self.handle_fs_event(&path); } let count = { - let store = self.state.store.lock().unwrap(); + let store = self.state.store.lock_recover(); let n = store.chunk_count(); let _ = store.save_index(&self.state_dir); n @@ -261,7 +261,7 @@ impl Indexer { if !path.is_file() { let path_str = path.to_string_lossy().into_owned(); // File deleted — remove from index - let mut store = self.state.store.lock().unwrap(); + let mut store = self.state.store.lock_recover(); let _ = store.delete_file(&path_str); return; } @@ -309,7 +309,7 @@ impl Indexer { // Check if hash changed (catches content changes without mtime change) { - let store = self.state.store.lock().unwrap(); + let store = self.state.store.lock_recover(); if let Ok(files) = store.all_files() { if files.iter().any(|f| f.path == path_str && f.hash == hash) { return; @@ -322,20 +322,22 @@ impl Indexer { // for natural-language files. let chunks = chunk::chunk_text(&text, 400, 80, 2_000); eprintln!("breadmill: embedding {} ({} chars, {} chunks)", path_str, text.len(), chunks.len()); - let mut embedder_guard = self.state.embedder.lock().unwrap(); if !self.state.model_ready.load(Ordering::Relaxed) { eprintln!("breadmill: model not ready, skipping embed for {}", path_str); return; } - let embedder = match embedder_guard.as_mut() { - Some(e) => e, - None => return, - }; + // Confirm the embedder is actually present before committing to + // clearing this file's old chunks below — same check as before, + // just without holding the embedder lock past this one glance (see + // the per-chunk locking in the loop for why). + if self.state.embedder.lock_recover().is_none() { + return; + } { - let mut store = self.state.store.lock().unwrap(); + let mut store = self.state.store.lock_recover(); let _ = store.delete_file(path_str); // remove old chunks/vectors first } @@ -344,9 +346,18 @@ impl Indexer { for (i, chunk) in chunks.iter().enumerate() { eprintln!("breadmill: embed chunk {}/{} ({} chars) for {}", i + 1, chunks.len(), chunk.text.len(), path_str); - match embedder.embed_document(&chunk.text) { + // Lock the embedder only around this single chunk's embed call — + // this used to be held for the whole file's chunk loop, so one + // large file needing a fresh MIGraphX JIT compile (60-120s for a + // new sequence length) could block every query (serve.rs locks + // this same mutex) for the entire file, not just one chunk. + let embed_result = match self.state.embedder.lock_recover().as_mut() { + Some(embedder) => embedder.embed_document(&chunk.text), + None => break, // model was unloaded mid-scan; stop here + }; + match embed_result { Ok(embedding) => { - let mut store = self.state.store.lock().unwrap(); + let mut store = self.state.store.lock_recover(); // Ensure file row exists before inserting chunks (FK constraint) let _ = store.upsert_file(path_str, mtime, &hash); let _ = store.insert_chunk( @@ -367,7 +378,7 @@ impl Indexer { eprintln!("breadmill: no chunks embedded for {}", path_str); // Record the file so the mtime+hash check skips it on the next startup // rather than re-entering the same embed-fail loop. - let store = self.state.store.lock().unwrap(); + let store = self.state.store.lock_recover(); let _ = store.upsert_file(path_str, mtime, &hash); } else { // Increment live so `status` reflects progress before the full scan ends. diff --git a/breadmill/src/main.rs b/breadmill/src/main.rs index da5814d..08452e8 100644 --- a/breadmill/src/main.rs +++ b/breadmill/src/main.rs @@ -13,10 +13,12 @@ mod indexer; mod power; mod serve; mod store; +mod sync_ext; use embed::{Backend, OrtEmbedder}; use indexer::{Indexer, SharedState}; use store::Store; +use sync_ext::MutexExt; const MODEL_URL: &str = "https://huggingface.co/nomic-ai/nomic-embed-text-v1.5/resolve/main/onnx/model.onnx"; @@ -170,7 +172,7 @@ fn run_daemon( eprintln!("breadmill: loading model..."); match OrtEmbedder::load(&model_path, &tokenizer_path, dim, backend) { Ok(embedder) => { - *state_clone.embedder.lock().unwrap() = Some(embedder); + *state_clone.embedder.lock_recover() = Some(embedder); state_clone.model_ready.store(true, Ordering::Relaxed); eprintln!("breadmill: model loaded"); } diff --git a/breadmill/src/serve.rs b/breadmill/src/serve.rs index cd67e67..a147235 100644 --- a/breadmill/src/serve.rs +++ b/breadmill/src/serve.rs @@ -8,6 +8,7 @@ use std::{ use breadsearch_shared::{Request, Response, StatusInfo}; use crate::indexer::SharedState; +use crate::sync_ext::MutexExt; pub fn run(socket_path: &Path, state: Arc, snippet_len: usize, search_limit: usize) { let _ = std::fs::remove_file(socket_path); @@ -86,7 +87,7 @@ fn dispatch( } let embedding = { - let mut embedder = state.embedder.lock().unwrap(); + let mut embedder = state.embedder.lock_recover(); match embedder.as_mut() { Some(e) => match e.embed_query(&query) { Ok(v) => v, @@ -101,7 +102,7 @@ fn dispatch( }; let limit = limit.min(search_limit).max(1); - let store = state.store.lock().unwrap(); + let store = state.store.lock_recover(); match store.search(&embedding, limit, snippet_len) { Ok(hits) => Response::Hits { hits }, diff --git a/breadmill/src/store.rs b/breadmill/src/store.rs index 077736d..ed1392a 100644 --- a/breadmill/src/store.rs +++ b/breadmill/src/store.rs @@ -57,9 +57,32 @@ impl Store { let index = new_index(&options).map_err(|e| e.to_string())?; if idx_path.exists() { - index - .load(idx_path.to_str().unwrap()) - .map_err(|e| e.to_string())?; + // NOTE: this only catches corruption that usearch's loader + // itself detects and reports as an `Err` (e.g. a recognizable + // but wrong/incompatible header). Verified experimentally: a + // file that doesn't even look like a usearch index at all (pure + // garbage bytes) crashes the *process* with a SIGSEGV inside the + // native loader rather than returning an `Err` — no amount of + // Rust-side `Result`/`catch_unwind` handling can intercept that, + // it's a native-code robustness gap in the usearch library + // itself. This recovery path is still worth having (it's the + // difference between "won't start" and "rebuilds and starts" + // for the errors it *does* catch), but it is not a complete + // guarantee against every possible corrupt file. The atomic + // save below is the real fix for the common case this was + // written for (a crash mid-save) — it now can't produce a + // half-written `vectors.usearch` in the first place. + if let Err(e) = index.load(idx_path.to_str().unwrap()) { + eprintln!( + "breadmill: WARNING: {} failed to load ({}) — treating it as corrupt, \ + discarding it, and rebuilding the index from scratch on the next scan", + idx_path.display(), + e + ); + conn.execute_batch("DELETE FROM chunks; DELETE FROM files;") + .map_err(|e| e.to_string())?; + index.reserve(4096).map_err(|e| e.to_string())?; + } } else { index.reserve(4096).map_err(|e| e.to_string())?; } @@ -218,11 +241,18 @@ impl Store { // ---- persistence -------------------------------------------------------- + /// Saves to a `.tmp` sibling and renames it into place — a same- + /// filesystem rename is atomic, so a crash mid-save leaves only an + /// orphaned `.tmp` file rather than a truncated/corrupt + /// `vectors.usearch` that would otherwise fail to load on next start + /// (see the recovery path in `open`). pub fn save_index(&self, state_dir: &Path) -> Result<(), String> { let idx_path = state_dir.join("vectors.usearch"); + let tmp_path = state_dir.join("vectors.usearch.tmp"); self.index - .save(idx_path.to_str().unwrap()) - .map_err(|e| e.to_string()) + .save(tmp_path.to_str().unwrap()) + .map_err(|e| e.to_string())?; + std::fs::rename(&tmp_path, &idx_path).map_err(|e| e.to_string()) } } @@ -233,3 +263,45 @@ fn truncate_to_chars(s: &str, max_chars: usize) -> String { let truncated: String = s.chars().take(max_chars).collect(); format!("{}…", truncated.trim_end()) } + +#[cfg(test)] +mod tests { + use super::*; + + fn test_dir(name: &str) -> std::path::PathBuf { + let dir = std::env::temp_dir().join(format!("breadmill-store-test-{name}-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + dir + } + + #[test] + fn save_index_leaves_no_tmp_file_behind_and_is_loadable() { + let dir = test_dir("save-atomic"); + let store = Store::open(&dir, 4).unwrap(); + store.save_index(&dir).unwrap(); + + assert!(dir.join("vectors.usearch").exists()); + assert!( + !dir.join("vectors.usearch.tmp").exists(), + "the .tmp staging file should be renamed away, not left behind" + ); + + // A fresh `open` can load what was just saved without error. + Store::open(&dir, 4).unwrap(); + + std::fs::remove_dir_all(&dir).unwrap(); + } + + // No automated test for the corrupt-index recovery branch in `open`: + // the natural way to construct a "corrupt" fixture (writing arbitrary + // garbage to vectors.usearch) was tried and crashes the *test process* + // with a SIGSEGV inside usearch's native loader before our `Result` + // handling ever gets a chance to run — see the comment on that branch + // in `open`. A real usearch file with a deliberately-broken-but-still- + // parseable header could plausibly hit the `Err` path exercised there + // instead, but reverse-engineering that format precisely enough to + // build a safe fixture wasn't worth the risk of another flaky/crashing + // test. The atomic-save test above covers the actual mechanism that + // prevents this scenario from arising in the first place. +} diff --git a/breadmill/src/sync_ext.rs b/breadmill/src/sync_ext.rs new file mode 100644 index 0000000..a891cd7 --- /dev/null +++ b/breadmill/src/sync_ext.rs @@ -0,0 +1,41 @@ +//! Poison-tolerant `Mutex` locking. +//! +//! A panic on any thread while holding `SharedState::store` or +//! `SharedState::embedder` (e.g. an untrapped ONNX Runtime panic — only PDF +//! extraction is `catch_unwind`-guarded, see `extract.rs`) poisons the +//! `Mutex`. Every subsequent plain `.lock().unwrap()` — in the indexer *and* +//! in every query handler in `serve.rs` — would then immediately panic too, +//! silently bricking indexing and search until the daemon is restarted by +//! hand. +//! +//! `Mutex` poisoning exists to flag "the data guarded by this lock might be +//! in an inconsistent state," but every lock scope in this crate is a short, +//! single-step SQLite call or usearch operation — nothing here spans a +//! multi-step invariant across a single `lock()` call — so recovering the +//! guard and logging loudly is a reasonable trade here: continuing to serve +//! (and re-attempting the operation that panicked, on the next file/query) +//! beats a daemon that silently stops answering everything after one panic. + +use std::sync::{Mutex, MutexGuard}; + +pub trait MutexExt { + /// Like `.lock().unwrap()`, but recovers from a poisoned mutex instead + /// of panicking again — logs once per recovery so it's visible in the + /// daemon's own output, not just silently swallowed. + fn lock_recover(&self) -> MutexGuard<'_, T>; +} + +impl MutexExt for Mutex { + fn lock_recover(&self) -> MutexGuard<'_, T> { + match self.lock() { + Ok(guard) => guard, + Err(poisoned) => { + eprintln!( + "breadmill: WARNING: a mutex was poisoned by a panic on another thread; \ + recovering it and continuing instead of cascading the panic" + ); + poisoned.into_inner() + } + } + } +} diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 5569deb..ca78b7a 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -10,7 +10,7 @@ path = "src/main.rs" [dependencies] breadsearch-shared = { path = "../breadsearch-shared" } -bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.8", features = ["gtk"] } +bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.10", features = ["gtk"] } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" diff --git a/breadsearch/src/main.rs b/breadsearch/src/main.rs index 58f4b47..a9812fe 100644 --- a/breadsearch/src/main.rs +++ b/breadsearch/src/main.rs @@ -298,12 +298,19 @@ fn run_ui() { let _ = tx.send(breadsearch_shared::send_request(&req)); }); - // Poll via idle_add_local until the thread delivers its result. - // Unix socket round-trips are sub-millisecond so this fires once. + // Wait for the thread's result on a bounded timer rather than + // an `idle_add_local` — an idle source has no wait condition + // of its own, so GLib re-invokes it on every single main-loop + // iteration, i.e. a busy-spin pinning a full CPU core for as + // long as the daemon takes to answer (normally sub-ms, but + // the daemon's socket has no read timeout of its own, so a + // wedged/slow daemon previously meant an indefinite spin). + // 15ms is imperceptible added latency for a search box and + // caps this at a couple dozen checks per second instead. let rx = Rc::new(rx); let list_t = list_clone.clone(); - glib::idle_add_local(move || { + glib::timeout_add_local(std::time::Duration::from_millis(15), move || { match rx.try_recv() { Ok(result) => { populate_list(&list_t, result); From 099096972246a4ba072d120629d2791c1776a727 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 17 Jul 2026 09:41:58 +0800 Subject: [PATCH 05/32] Migrate embedding pipeline, EP session building, and model download to bread-onnx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit embed.rs's OrtEmbedder now delegates its tokenize -> tensor build -> mean-pool -> L2-normalize pipeline to bread_onnx::embedding::EmbeddingSession (near-byte-identical to breadarrd's own OrtEmbedder — same duplication, now shared, path dependency for now, see the TODO in breadmill/Cargo.toml), and its per-EP session builders (npu_session/rocm_session/cuda_session/ openvino_session) collapse into a single to_provider() mapping onto bread_onnx::Provider, which this crate's own breadmill/src/session.rs counterpart now handles generically. This crate's Backend enum, cargo feature gates (npu/rocm/cuda/openvino/full - unchanged, still control which onnxruntime EPs actually link/load), and NPU vaip_config.json discovery all stay local since they're genuinely breadsearch-specific. main.rs's Backend construction and CLI flag handling are untouched. This is also the reference implementation the MIGraphX-not-ROCm default in bread-onnx's provider module was promoted from (see this machine's own breadsearch-gpu-backends operator notes) — breadpad's ONNX migration, which had the actual silent-fallback bug, follows in a later commit. download_if_missing is replaced with bread_onnx::download::ensure_file (same sync/ureq approach, now shared with breadarrd's downloader). Builds clean with default features AND --features full (npu+rocm+cuda+ openvino all compiling together, matching how this crate already combined them). All existing tests pass across the whole workspace. --- Cargo.lock | 44 ++++++++ breadmill/Cargo.toml | 2 + breadmill/src/embed.rs | 245 +++++++---------------------------------- breadmill/src/main.rs | 28 +---- 4 files changed, 88 insertions(+), 231 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5395bb..9b54221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" +[[package]] +name = "anyhow" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" + [[package]] name = "arbitrary" version = "1.4.2" @@ -120,6 +126,20 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bread-onnx" +version = "0.2.3" +dependencies = [ + "anyhow", + "bread-utils", + "hex", + "ort", + "sha2", + "tokenizers", + "tracing", + "ureq 2.12.1", +] + [[package]] name = "bread-theme" version = "0.2.3" @@ -131,10 +151,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "bread-utils" +version = "0.2.3" +dependencies = [ + "dirs", + "serde", + "serde_json", +] + [[package]] name = "breadmill" version = "0.2.4" dependencies = [ + "bread-onnx", "breadsearch-shared", "hex", "ignore", @@ -2966,9 +2996,21 @@ 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", +] + [[package]] name = "tracing-core" version = "0.1.36" @@ -3107,6 +3149,8 @@ dependencies = [ "once_cell", "rustls", "rustls-pki-types", + "serde", + "serde_json", "url", "webpki-roots 0.26.11", ] diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index 768735d..929fd8d 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -42,6 +42,8 @@ breadsearch-shared = { path = "../breadsearch-shared" } # needed for the plain CPU path even in the npu build. ort = { version = "2.0.0-rc.12", default-features = false, features = ["std", "tracing", "download-binaries", "tls-native", "copy-dylibs", "api-23"] } tokenizers = "0" +# TODO(owner): switch to tag-pinned git dependency once bread-onnx is merged and tagged, matching the bread-theme pattern +bread-onnx = { path = "../../bread-ecosystem-fix-worktree/bread-onnx" } # Surfaces ort's own EP-registration tracing (e.g. a GPU EP silently failing to # register and falling back to CPU) as visible log output instead of nowhere. diff --git a/breadmill/src/embed.rs b/breadmill/src/embed.rs index 3020e58..d507951 100644 --- a/breadmill/src/embed.rs +++ b/breadmill/src/embed.rs @@ -1,10 +1,7 @@ use std::path::{Path, PathBuf}; -use ort::{ - session::{Session, builder::{GraphOptimizationLevel, SessionBuilder}}, - value::Tensor, -}; -use tokenizers::Tokenizer; +use bread_onnx::embedding::EmbeddingSession; +use bread_onnx::Provider; const DOCUMENT_PREFIX: &str = "search_document: "; const QUERY_PREFIX: &str = "search_query: "; @@ -35,23 +32,15 @@ pub enum Backend { } pub struct OrtEmbedder { - session: Session, - tokenizer: Tokenizer, - dim: usize, + inner: EmbeddingSession, } impl OrtEmbedder { pub fn load(model_path: &Path, tokenizer_path: &Path, dim: usize, backend: Backend) -> Result { - let builder = Session::builder() - .map_err(|e| e.to_string())? - .with_optimization_level(GraphOptimizationLevel::All) + let provider = to_provider(backend)?; + let inner = EmbeddingSession::load(model_path, tokenizer_path, dim, MAX_SEQ_LEN, &[provider]) .map_err(|e| e.to_string())?; - - let mut builder = configure_eps(builder, &backend)?; - let session = builder.commit_from_file(model_path).map_err(|e| e.to_string())?; - let tokenizer = Tokenizer::from_file(tokenizer_path).map_err(|e| e.to_string())?; - - Ok(Self { session, tokenizer, dim }) + Ok(Self { inner }) } pub fn embed_document(&mut self, text: &str) -> Result, String> { @@ -64,229 +53,71 @@ impl OrtEmbedder { fn embed_with_prefix(&mut self, text: &str, prefix: &str) -> Result, String> { let input = format!("{}{}", prefix, text); - - let encoding = self - .tokenizer - .encode(input, true) - .map_err(|e| e.to_string())?; - - let mut ids: Vec = encoding.get_ids().iter().map(|&x| x as i64).collect(); - let mut mask: Vec = encoding - .get_attention_mask() - .iter() - .map(|&x| x as i64) - .collect(); - let mut type_ids: Vec = encoding - .get_type_ids() - .iter() - .map(|&x| x as i64) - .collect(); - - if ids.len() > MAX_SEQ_LEN { - eprintln!( - "breadmill: truncating {} tokens to {} (chunk too large)", - ids.len(), - MAX_SEQ_LEN - ); - ids.truncate(MAX_SEQ_LEN); - mask.truncate(MAX_SEQ_LEN); - type_ids.truncate(MAX_SEQ_LEN); - } - - let seq_len = ids.len() as i64; - - let id_tensor = - Tensor::::from_array((vec![1i64, seq_len], ids.clone())).map_err(|e| e.to_string())?; - let mask_tensor = - Tensor::::from_array((vec![1i64, seq_len], mask.clone())).map_err(|e| e.to_string())?; - let type_tensor = - Tensor::::from_array((vec![1i64, seq_len], type_ids)).map_err(|e| e.to_string())?; - - let outputs = self - .session - .run(ort::inputs! { - "input_ids" => id_tensor, - "attention_mask" => mask_tensor, - "token_type_ids" => type_tensor, - }) - .map_err(|e| e.to_string())?; - - // last_hidden_state: shape [1, seq_len, dim] - let (shape, data) = outputs["last_hidden_state"] - .try_extract_tensor::() - .map_err(|e| e.to_string())?; - - let actual_seq = shape[1] as usize; - let actual_dim = shape[2] as usize; - - // Mean-pool over non-padding positions. Some execution providers (e.g. - // MIGraphX) pad the output sequence dimension for kernel efficiency, so - // actual_seq can exceed mask.len() — only positions covered by our own - // attention mask are meaningful, so cap the loop at whichever is shorter. - let mut result = vec![0.0f32; actual_dim]; - let mut count = 0usize; - - for t in 0..actual_seq.min(mask.len()) { - if mask[t] > 0 { - for d in 0..actual_dim { - result[d] += data[t * actual_dim + d]; - } - count += 1; - } - } - - if count > 0 { - for x in &mut result { - *x /= count as f32; - } - } - - l2_normalize(&mut result); - - // Clamp/pad to configured dim - result.truncate(self.dim); - while result.len() < self.dim { - result.push(0.0); - } - - Ok(result) + self.inner.embed(&input).map_err(|e| e.to_string()) } } -fn l2_normalize(v: &mut Vec) { - let norm: f32 = v.iter().map(|x| x * x).sum::().sqrt(); - if norm > 1e-10 { - for x in v.iter_mut() { - *x /= norm; - } - } -} +// ---- Backend -> bread_onnx::Provider ---------------------------------------- +// +// `bread_onnx::session::build_session` (via `EmbeddingSession::load`) is the +// shared session-builder + EP-fallback + loud-logging code every EP branch +// below used to hand-roll separately (`configure_eps`/`npu_session`/ +// `rocm_session`/`cuda_session`/`openvino_session`). What's genuinely +// specific to this crate — its cargo feature gates (npu/rocm/cuda/openvino), +// and NPU's `vaip_config.json` discovery — stays here. -// ---- Execution provider selection ------------------------------------------- - -fn configure_eps(builder: SessionBuilder, backend: &Backend) -> Result { +fn to_provider(backend: Backend) -> Result { match backend { - Backend::Cpu => Ok(builder), - Backend::Npu { cache_dir } => npu_session(builder, cache_dir), - Backend::Rocm => rocm_session(builder), - Backend::Cuda => cuda_session(builder), - Backend::OpenVino { cache_dir } => openvino_session(builder, cache_dir), + Backend::Cpu => Ok(Provider::Cpu), + Backend::Npu { cache_dir } => npu_provider(cache_dir), + Backend::Rocm => rocm_provider(), + Backend::Cuda => cuda_provider(), + Backend::OpenVino { cache_dir } => Ok(Provider::OpenVino { device_type: "GPU".to_string(), cache_dir }), } } #[cfg(feature = "npu")] -fn npu_session(builder: SessionBuilder, cache_dir: &Path) -> Result { - let vitis_ep = build_vitis_ep(cache_dir)?; - eprintln!("breadmill: using NPU (VitisAI) execution provider"); +fn npu_provider(cache_dir: PathBuf) -> Result { + let vaip_config = find_vaip_config()?; if std::env::var("ORT_DYLIB_PATH").is_err() { eprintln!( "breadmill: hint — set ORT_DYLIB_PATH to the Ryzen AI SDK ORT, e.g.:\n \ ORT_DYLIB_PATH=~/.local/share/ryzen-ai-1.7.1/lib/libonnxruntime.so" ); } - builder - .with_execution_providers([vitis_ep, ort::ep::CPU::default().build()]) - .map_err(|e| e.to_string()) + Ok(Provider::Vitis { + config_file: vaip_config, + cache_dir: cache_dir.join("npu"), + cache_key: "nomic-embed-text-v1.5".to_string(), + }) } #[cfg(not(feature = "npu"))] -fn npu_session(builder: SessionBuilder, _cache_dir: &Path) -> Result { +fn npu_provider(_cache_dir: PathBuf) -> Result { eprintln!("breadmill: NPU backend requested but not compiled in (rebuild with --features npu); using CPU"); - Ok(builder) + Ok(Provider::Cpu) } -// ---- VitisAI EP (NPU) ------------------------------------------------------- - -#[cfg(feature = "npu")] -fn build_vitis_ep(cache_dir: &Path) -> Result { - let vaip_config = find_vaip_config()?; - let npu_cache = cache_dir.join("npu"); - std::fs::create_dir_all(&npu_cache).map_err(|e| e.to_string())?; - eprintln!("breadmill: vaip_config: {}", vaip_config.display()); - eprintln!("breadmill: NPU model cache: {}", npu_cache.display()); - Ok(ort::ep::Vitis::default() - .with_config_file(vaip_config.to_string_lossy()) - .with_cache_dir(npu_cache.to_string_lossy()) - .with_cache_key("nomic-embed-text-v1.5") - .build()) -} - -// ---- MIGraphX EP (AMD iGPU, ROCm-backed) ------------------------------------- - #[cfg(feature = "rocm")] -fn rocm_session(builder: SessionBuilder) -> Result { - eprintln!("breadmill: using MIGraphX execution provider (device 0)"); - eprintln!( - "breadmill: note — check the log line above/below for \"Successfully registered \ - `MIGraphXExecutionProvider`\"; if it's missing, the ONNX Runtime in use wasn't built \ - with MIGraphX support and inference silently fell back to CPU" - ); - builder - .with_execution_providers([ - ort::ep::MIGraphX::default().with_device_id(0).build(), - ort::ep::CPU::default().build(), - ]) - .map_err(|e| e.to_string()) +fn rocm_provider() -> Result { + Ok(Provider::MiGraphX { device_id: 0 }) } #[cfg(not(feature = "rocm"))] -fn rocm_session(builder: SessionBuilder) -> Result { +fn rocm_provider() -> Result { eprintln!("breadmill: ROCm backend requested but not compiled in (rebuild with --features rocm); using CPU"); - Ok(builder) + Ok(Provider::Cpu) } -// ---- CUDA EP (NVIDIA GPU) ---------------------------------------------------- - #[cfg(feature = "cuda")] -fn cuda_session(builder: SessionBuilder) -> Result { - eprintln!("breadmill: using CUDA execution provider (device 0)"); - eprintln!( - "breadmill: note — check the log line above/below for \"Successfully registered \ - `CUDAExecutionProvider`\"; if it's missing, the ONNX Runtime in use wasn't built \ - with CUDA support and inference silently fell back to CPU" - ); - builder - .with_execution_providers([ - ort::ep::CUDA::default().with_device_id(0).build(), - ort::ep::CPU::default().build(), - ]) - .map_err(|e| e.to_string()) +fn cuda_provider() -> Result { + Ok(Provider::Cuda { device_id: 0 }) } #[cfg(not(feature = "cuda"))] -fn cuda_session(builder: SessionBuilder) -> Result { +fn cuda_provider() -> Result { eprintln!("breadmill: CUDA backend requested but not compiled in (rebuild with --features cuda); using CPU"); - Ok(builder) -} - -// ---- OpenVINO EP (Intel iGPU/dGPU) ------------------------------------------- - -#[cfg(feature = "openvino")] -fn openvino_session(builder: SessionBuilder, cache_dir: &Path) -> Result { - let ov_cache = cache_dir.join("openvino"); - std::fs::create_dir_all(&ov_cache).map_err(|e| e.to_string())?; - eprintln!("breadmill: using OpenVINO execution provider (device_type GPU)"); - eprintln!("breadmill: OpenVINO model cache: {}", ov_cache.display()); - eprintln!( - "breadmill: note — check the log line above/below for \"Successfully registered \ - `OpenVINOExecutionProvider`\"; if it's missing, the ONNX Runtime in use wasn't built \ - with OpenVINO support and inference silently fell back to CPU" - ); - builder - .with_execution_providers([ - ort::ep::OpenVINO::default() - .with_device_type("GPU") - .with_cache_dir(ov_cache.to_string_lossy()) - .build(), - ort::ep::CPU::default().build(), - ]) - .map_err(|e| e.to_string()) -} - -#[cfg(not(feature = "openvino"))] -fn openvino_session(builder: SessionBuilder, _cache_dir: &Path) -> Result { - eprintln!("breadmill: OpenVINO backend requested but not compiled in (rebuild with --features openvino); using CPU"); - Ok(builder) + Ok(Provider::Cpu) } /// Locate the VitisAI EP config file required by the AMD Ryzen AI SDK. diff --git a/breadmill/src/main.rs b/breadmill/src/main.rs index 08452e8..0b79eef 100644 --- a/breadmill/src/main.rs +++ b/breadmill/src/main.rs @@ -1,5 +1,4 @@ use std::{ - io::Read, path::{Path, PathBuf}, sync::{Arc, atomic::Ordering}, }; @@ -226,29 +225,10 @@ fn download_if_missing(url: &str, dest: &Path) -> Result<(), String> { eprintln!(" already present: {}", dest.display()); return Ok(()); } - - eprintln!(" downloading {} ...", url); - let agent = ureq::AgentBuilder::new() - .timeout(std::time::Duration::from_secs(300)) - .build(); - - let response = agent.get(url).call().map_err(|e| e.to_string())?; - let mut bytes = Vec::new(); - response - .into_reader() - .read_to_end(&mut bytes) - .map_err(|e| e.to_string())?; - - if bytes.is_empty() { - return Err(format!("empty download from {}", url)); - } - - // Write atomically via temp file - let tmp = dest.with_extension("tmp"); - std::fs::write(&tmp, &bytes).map_err(|e| e.to_string())?; - std::fs::rename(&tmp, dest).map_err(|e| e.to_string())?; - - eprintln!(" saved {} ({:.1} MB)", dest.display(), bytes.len() as f64 / 1_048_576.0); + // Shared with breadarrd's own (previously reqwest/async, now also this + // same sync/ureq implementation) model downloader — see + // bread_onnx::download's doc comment. + bread_onnx::download::ensure_file(url, dest, None).map_err(|e| e.to_string())?; Ok(()) } From 99724853a0ef173a8f008b52a0c948dcc2a0d1f6 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 19 Jul 2026 03:53:04 +0800 Subject: [PATCH 06/32] Switch to tag-pinned bread-ecosystem deps; bump version to v0.3.0 --- Cargo.lock | 547 ++++++++++++++++++++-------------- breadmill/Cargo.toml | 5 +- breadsearch-shared/Cargo.toml | 2 +- breadsearch/Cargo.toml | 2 +- packaging/breadmill.service | 2 +- 5 files changed, 325 insertions(+), 233 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b54221..49e20a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,7 +25,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -104,9 +104,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block-buffer" @@ -117,6 +117,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "block-padding" version = "0.3.3" @@ -128,13 +137,14 @@ dependencies = [ [[package]] name = "bread-onnx" -version = "0.2.3" +version = "0.3.0" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939" dependencies = [ "anyhow", "bread-utils", "hex", "ort", - "sha2", + "sha2 0.10.9", "tokenizers", "tracing", "ureq 2.12.1", @@ -153,7 +163,8 @@ dependencies = [ [[package]] name = "bread-utils" -version = "0.2.3" +version = "0.3.0" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939" dependencies = [ "dirs", "serde", @@ -162,7 +173,7 @@ dependencies = [ [[package]] name = "breadmill" -version = "0.2.4" +version = "0.3.0" dependencies = [ "bread-onnx", "breadsearch-shared", @@ -175,7 +186,7 @@ dependencies = [ "rusqlite", "serde", "serde_json", - "sha2", + "sha2 0.11.0", "tokenizers", "tracing-subscriber", "ureq 2.12.1", @@ -185,7 +196,7 @@ dependencies = [ [[package]] name = "breadsearch" -version = "0.2.0" +version = "0.3.0" dependencies = [ "bread-theme", "breadsearch-shared", @@ -196,7 +207,7 @@ dependencies = [ [[package]] name = "breadsearch-shared" -version = "0.2.0" +version = "0.3.0" dependencies = [ "serde", "serde_json", @@ -205,12 +216,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" dependencies = [ "memchr", - "serde", + "serde_core", ] [[package]] @@ -219,12 +230,6 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "bytecount" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" - [[package]] name = "byteorder" version = "1.5.0" @@ -233,9 +238,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "bzip2" @@ -262,7 +267,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cairo-sys-rs", "glib", "libc", @@ -299,9 +304,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" dependencies = [ "find-msvc-tools", "jobserver", @@ -311,9 +316,9 @@ dependencies = [ [[package]] name = "cff-parser" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f5b6e9141c036f3ff4ce7b2f7e432b0f00dee416ddcd4f17741d189ddc2e9d" +checksum = "c5810ca1a2b5870df2aab1c03e11c40c361ba51d6e3e361e56310f1cb3b4e087" [[package]] name = "cfg-expr" @@ -331,30 +336,41 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "inout", ] [[package]] name = "clap" -version = "4.6.1" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" dependencies = [ "anstyle", "clap_lex", @@ -395,9 +411,9 @@ dependencies = [ [[package]] name = "console" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" dependencies = [ "encode_unicode", "libc", @@ -405,6 +421,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -436,6 +458,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -462,18 +493,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -481,18 +512,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crypto-common" @@ -505,10 +536,19 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.194" +name = "crypto-common" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cxx" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00424da159cc5adcb4eeea7e7b5cb1d96df41f5fa695ec596922181bdc36232a" dependencies = [ "cc", "cxx-build", @@ -521,9 +561,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.194" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +checksum = "43e05269dbed4dab7072ae0f04ef31799ad189d52ce2ac12710c2997b754f86a" dependencies = [ "cc", "codespan-reporting", @@ -536,9 +576,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.194" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +checksum = "e2f017b07e0da7f425642339faff0edc9f0de6459a18180183d086d1f3381e89" dependencies = [ "clap", "codespan-reporting", @@ -550,15 +590,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.194" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" +checksum = "293d267f43a5778bf3b89fff2a658f081166e7f152d9640e2ee3d917d065a5fc" [[package]] name = "cxxbridge-macro" -version = "1.0.194" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +checksum = "72dd233dc128223fe85d2afa5c617c79743c0f47fb495b69234b5da680d4986a" dependencies = [ "indexmap", "proc-macro2", @@ -624,9 +664,9 @@ checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" [[package]] name = "der" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ "pem-rfc7468", "zeroize", @@ -686,11 +726,22 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", +] + [[package]] name = "dirs" version = "5.0.1" @@ -888,24 +939,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -914,15 +965,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", @@ -931,15 +982,15 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-core", "futures-macro", @@ -975,9 +1026,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd42fdbbf48612c6e8f47c65fb92d2e8f39c25aecd6af047e83897c1a22d2a4e" +checksum = "d81e2a6c6ecba2aab60633a98df1868b03fa0bfdce8105edc27c1bccf71f0e39" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -991,9 +1042,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d974ac4f15e67472c3a9728daf612590b4a5762a4b33f0edd298df0b80d043c" +checksum = "3d8f608d8d7d229975c4d0d026f5d3071598c4ddab3c5262b0a31840fec78d13" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1036,16 +1087,30 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] [[package]] -name = "gio" -version = "0.22.6" +name = "getrandom" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3848bcba3a35cc0a71df8ba8ecfd799d6bfb862342a53a4a915fb62213aa4e6" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "gio" +version = "0.22.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3e1f669909c326b9413bde5a742097b8c90a7d78f45326db13668984769ded" dependencies = [ "futures-channel", "futures-core", @@ -1060,9 +1125,9 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.22.0" +version = "0.22.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64729ba2772c080448f9f966dba8f4456beeb100d8c28a865ef8a0f2ef4987e1" +checksum = "353fdc7da7cd16da916104b1e0e4e7de380ec9c8aaa20d4d742d66310ab4b0d5" dependencies = [ "glib-sys", "gobject-sys", @@ -1093,11 +1158,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.22.7" +version = "0.22.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a" +checksum = "ddbcf514bd1881fc1b960e4e52b4e82873f4da3bceddbd58d42827b508888100" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "futures-channel", "futures-core", "futures-executor", @@ -1126,9 +1191,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.22.6" +version = "0.22.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7fbac234ed5bc2a28359b7bde8e1b9cdf1441cc2d7f068e4824672d7db9445" +checksum = "030967459f9f676851872c6304adea7825c6d462ec9b72554c733cf0c5952233" dependencies = [ "libc", "system-deps", @@ -1136,9 +1201,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd" dependencies = [ "aho-corasick", "bstr", @@ -1160,32 +1225,30 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.22.0" +version = "0.22.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d1b7881f96869f49808b6adfe906a93a57a34204952253444d68c3208d71f1" +checksum = "eb856b9c558971c3f13ab692358926da710b046932a4e087aedcc35b040d7dff" dependencies = [ "glib", "graphene-sys", - "libc", ] [[package]] name = "graphene-sys" -version = "0.22.0" +version = "0.22.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "517f062f3fd6b7fd3e57a3f038a74b3c23ca32f51199ff028aa704609943f79c" +checksum = "5c7ffdfde88f3570d3705e0d8a2433e036d387a1f2930bbf47eafcb5f569fd04" dependencies = [ "glib-sys", "libc", - "pkg-config", "system-deps", ] [[package]] name = "gsk4" -version = "0.11.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c912dfcbd28acace5fc99c40bb9f25e1dcb73efb1f2608327f66a99acdcb62" +checksum = "b867be1c5f14dcb8f552c0eff6e9a9b1da5f8b43943e8efc3a63c889d84952ff" dependencies = [ "cairo-rs", "gdk4", @@ -1198,9 +1261,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.11.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d54bbc7a9d8b6ffe4f0c95eede15ccfb365c8bf521275abe6bcfb57b18fb8a" +checksum = "5b7c7eb2e681ee896646cfb8872b431f24d09f53ba9283289d9b10caa6707088" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -1214,9 +1277,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7181b837f04cbe93f79441475f7a00560a92cba7a72e38cc1a68b6f8b78eaae2" +checksum = "98a0a0466484f64b07b5b8184d43fa46be78eb0b8e04ae4e179af31d770b76d9" dependencies = [ "cairo-rs", "field-offset", @@ -1239,7 +1302,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4069987ff4793699511a251028cc336b438e46565b463f111250148d574752a" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "gdk4", "glib", "glib-sys", @@ -1263,9 +1326,9 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.11.0" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3581b242ba62fdff122ebb626ea641582ec326031622bd19d60f85029c804a87" +checksum = "5ac7179400a36a04de039c24206bb841c5596992b907b43b23ee8d5bdc40d00e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1275,9 +1338,9 @@ dependencies = [ [[package]] name = "gtk4-sys" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ba8e695e2640455561274e65e45f0a151619e450746007667f4b23ceae4e1b" +checksum = "82b8f954786af0b1984425c4446b77f5ff6594346181316be3f850caab1c6f01" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1312,9 +1375,9 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5081f264ed7adee96ea4b4778b6bb9da0a7228b084587aa3bd3ff05da7c5a3b" +checksum = "32069d97bb81e38fa67eab65e3393bf804bb85969f2bc06bf13f64aef5aba248" dependencies = [ "hashbrown 0.17.1", ] @@ -1337,7 +1400,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -1362,6 +1425,15 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "hybrid-array" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +dependencies = [ + "typenum", +] + [[package]] name = "icu_collections" version = "2.2.0" @@ -1473,9 +1545,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.26" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" +checksum = "7b009b6744c1445efd7244084e25e498636412effb6760b55067553baa925cc7" dependencies = [ "crossbeam-deque", "globset", @@ -1499,9 +1571,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.4" +version = "0.18.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" +checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c" dependencies = [ "console", "portable-atomic", @@ -1523,9 +1595,9 @@ dependencies = [ [[package]] name = "inotify-sys" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" dependencies = [ "libc", ] @@ -1557,19 +1629,19 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -1598,7 +1670,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", ] @@ -1626,9 +1698,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "libc", ] @@ -1673,26 +1745,25 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "lopdf" -version = "0.38.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7184fdea2bc3cd272a1acec4030c321a8f9875e877b3f92a53f2f6033fdc289" +checksum = "25aab26d99567469098e64a02f42679f8965c6401263eefa31d8f2dcc37a221c" dependencies = [ "aes", - "bitflags 2.13.0", + "bitflags 2.13.1", "cbc", "ecb", "encoding_rs", "flate2", - "getrandom 0.3.4", + "getrandom 0.4.3", "indexmap", "itoa", "log", "md-5", "nom 8.0.0", - "nom_locate", - "rand", + "rand 0.10.2", "rangemap", - "sha2", + "sha2 0.10.9", "stringprep", "thiserror 2.0.18", "ttf-parser", @@ -1753,9 +1824,9 @@ dependencies = [ [[package]] name = "matrixmultiply" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +checksum = "3f607c237553f086e7043417a51df26b2eb899d3caff94e6a67592ff992fedc7" dependencies = [ "autocfg", "rawpointer", @@ -1768,14 +1839,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", ] [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memoffset" @@ -1887,24 +1958,13 @@ dependencies = [ "memchr", ] -[[package]] -name = "nom_locate" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d" -dependencies = [ - "bytecount", - "memchr", - "nom 8.0.0", -] - [[package]] name = "notify" version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1980,7 +2040,7 @@ version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "once_cell", "onig_sys", @@ -2002,7 +2062,7 @@ version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "foreign-types", "libc", @@ -2072,13 +2132,12 @@ dependencies = [ [[package]] name = "pango" -version = "0.22.6" +version = "0.22.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251bdc6e6487b811be0e406a21e301e07e45c0aa8fa39e00c0c8e12a91752438" +checksum = "5d800d8d0de2ad5d0fb046f5344dbaba14a003cf3dd27cc21d85893d35ea316c" dependencies = [ "gio", "glib", - "libc", "pango-sys", ] @@ -2106,15 +2165,15 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", + "digest 0.10.7", "hmac", ] [[package]] name = "pdf-extract" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28ba1758a3d3f361459645780e09570b573fc3c82637449e9963174c813a98" +checksum = "417e8fdc940f1d5bc62c5f89864c3a2255f74f69aa353c98509213d67df61e73" dependencies = [ "adobe-cmap-parser", "cff-parser", @@ -2162,9 +2221,9 @@ checksum = "60f6ce597ecdcc9a098e7fddacb1065093a3d66446fa16c675e7e71d1b5c28e6" [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" [[package]] name = "portable-atomic-util" @@ -2211,7 +2270,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -2225,9 +2284,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.40.1" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2474bd2e5029e7ccb6abb2ba48cf2383a333851dedf495901544281590c7da7f" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", "serde", @@ -2249,13 +2308,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand" -version = "0.9.4" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", ] [[package]] @@ -2265,7 +2341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -2277,6 +2353,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rangemap" version = "1.7.1" @@ -2333,9 +2415,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -2345,9 +2427,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -2390,7 +2472,7 @@ version = "0.40.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2414,7 +2496,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys", @@ -2423,9 +2505,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "log", "once_cell", @@ -2438,9 +2520,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "zeroize", ] @@ -2458,9 +2540,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -2498,7 +2580,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation", "core-foundation-sys", "libc", @@ -2584,13 +2666,13 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", ] [[package]] @@ -2600,8 +2682,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -2621,9 +2714,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "slab" @@ -2709,9 +2802,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -2738,7 +2831,7 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.3+spec-1.1.0", "version-compare", ] @@ -2755,7 +2848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2812,18 +2905,18 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] [[package]] name = "time" -version = "0.3.51" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", "num-conv", @@ -2850,9 +2943,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -2883,7 +2976,7 @@ dependencies = [ "monostate", "onig", "paste", - "rand", + "rand 0.9.5", "rayon", "rayon-cond", "regex", @@ -2911,9 +3004,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" dependencies = [ "indexmap", "serde_core", @@ -2921,7 +3014,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -2958,14 +3051,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -2974,7 +3067,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -2985,9 +3078,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tracing" @@ -3199,9 +3292,9 @@ dependencies = [ [[package]] name = "usearch" -version = "2.25.3" +version = "2.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c08f764417012cf6aea6d1380ef9ea8712c5795a938b726fc67b9bf7ea8824b" +checksum = "1cd7f672d20412962c457b11c858c6c5aecb949808a5345a95e1d671112bcf72" dependencies = [ "cxx", "cxx-build", @@ -3271,9 +3364,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -3284,9 +3377,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3294,9 +3387,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", @@ -3307,9 +3400,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] @@ -3326,9 +3419,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" dependencies = [ "rustls-pki-types", ] @@ -3339,14 +3432,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -3553,9 +3646,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -3612,18 +3705,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", @@ -3736,9 +3829,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zopfli" diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index 929fd8d..9faeae0 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadmill" -version = "0.2.4" +version = "0.3.0" edition = "2021" license = "MIT" @@ -42,8 +42,7 @@ breadsearch-shared = { path = "../breadsearch-shared" } # needed for the plain CPU path even in the npu build. ort = { version = "2.0.0-rc.12", default-features = false, features = ["std", "tracing", "download-binaries", "tls-native", "copy-dylibs", "api-23"] } tokenizers = "0" -# TODO(owner): switch to tag-pinned git dependency once bread-onnx is merged and tagged, matching the bread-theme pattern -bread-onnx = { path = "../../bread-ecosystem-fix-worktree/bread-onnx" } +bread-onnx = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0" } # Surfaces ort's own EP-registration tracing (e.g. a GPU EP silently failing to # register and falling back to CPU) as visible log output instead of nowhere. diff --git a/breadsearch-shared/Cargo.toml b/breadsearch-shared/Cargo.toml index c557db6..3ebc491 100644 --- a/breadsearch-shared/Cargo.toml +++ b/breadsearch-shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadsearch-shared" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT" diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index ca78b7a..241457f 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadsearch" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT" diff --git a/packaging/breadmill.service b/packaging/breadmill.service index 69c928f..e6ade17 100644 --- a/packaging/breadmill.service +++ b/packaging/breadmill.service @@ -1,6 +1,6 @@ [Unit] Description=Breadmill semantic search indexer -Documentation=https://github.com/breadway/breadsearch +Documentation=https://git.breadway.dev/Breadway/breadsearch After=default.target [Service] From cfca6161b04e611ee4f6838704f91093f79d28d5 Mon Sep 17 00:00:00 2001 From: Breadway Date: Tue, 21 Jul 2026 19:18:06 +0800 Subject: [PATCH 07/32] ci: remove GitHub push-mirror workflow --- .forgejo/workflows/mirror.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .forgejo/workflows/mirror.yml diff --git a/.forgejo/workflows/mirror.yml b/.forgejo/workflows/mirror.yml deleted file mode 100644 index 37462af..0000000 --- a/.forgejo/workflows/mirror.yml +++ /dev/null @@ -1,19 +0,0 @@ -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/breadsearch.git" \ - '+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*' From 13f5e509e415b6199d39599c03064f24985dbc43 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 09:56:49 +0800 Subject: [PATCH 08/32] ci: add dev/beta build track workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds dev-release.yml (publishes on every push to dev) and beta-release.yml (publishes on a beta-v* tag), mirroring the pattern landing in bread-ecosystem/bread. Also creates the dev branch for this repo, which didn't exist before — see bread-ecosystem/docs/release-channels.md for the three-track policy. --- .forgejo/workflows/beta-release.yml | 60 +++++++++++++++++++++++++ .forgejo/workflows/dev-release.yml | 69 +++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 .forgejo/workflows/beta-release.yml create mode 100644 .forgejo/workflows/dev-release.yml diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/beta-release.yml new file mode 100644 index 0000000..fccc4e0 --- /dev/null +++ b/.forgejo/workflows/beta-release.yml @@ -0,0 +1,60 @@ +name: beta release + +# Publishes a beta-track build when a `beta-v*` tag is pushed — +# separate from release.yml's tag-triggered stable releases. See +# bread-ecosystem's docs/release-channels.md for the three-track policy +# this is part of. +on: + push: + tags: ['beta-v*'] + +jobs: + build: + 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#beta-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 the other non-stable track, + # 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 + # --branch dev: the TRACK-aware gen-index.sh isn't merged to + # bread-ecosystem's main yet. + git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci + TRACK=beta bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml new file mode 100644 index 0000000..ad91274 --- /dev/null +++ b/.forgejo/workflows/dev-release.yml @@ -0,0 +1,69 @@ +name: dev release + +# Publishes a dev-track build on every push to `dev` — +# separate from release.yml's tag-triggered stable releases. See +# bread-ecosystem's docs/release-channels.md for the three-track policy +# this is part of. +on: + push: + branches: ['dev'] + +jobs: + build: + runs-on: [self-hosted, hestia] + steps: + - name: checkout + run: | + set -euo pipefail + rm -rf src && mkdir src + git clone --branch dev --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 + CUR="$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + 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 + # --branch dev: the TRACK-aware gen-index.sh isn't merged to + # bread-ecosystem's main yet. + git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci + TRACK=dev bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh From bf711f8047a9a26c15f65369107539e79c94db42 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 10:10:16 +0800 Subject: [PATCH 09/32] ci: retrigger dev-track build now that BAKERY_MINISIGN_SEC_KEY_PATH is set From 1e3872124b1d27ac412ad1126fb2acacf8092380 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 10:24:44 +0800 Subject: [PATCH 10/32] ci: use a unique temp dir for the bread-ecosystem clone in dev/beta CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fixed /tmp/bread-ecosystem-ci path races when multiple repos' dev/beta workflows run close together on the same self-hosted runner — one job's rm -rf/clone can stomp another's in-progress checkout, causing the regenerate-index step to fail intermittently. Switch to mktemp -d. --- .forgejo/workflows/beta-release.yml | 12 +++++++----- .forgejo/workflows/dev-release.yml | 14 ++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/beta-release.yml index fccc4e0..e0870c2 100644 --- a/.forgejo/workflows/beta-release.yml +++ b/.forgejo/workflows/beta-release.yml @@ -53,8 +53,10 @@ jobs: 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 - # --branch dev: the TRACK-aware gen-index.sh isn't merged to - # bread-ecosystem's main yet. - git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci - TRACK=beta bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh + 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 dev 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}" diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index ad91274..4c143bc 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -29,7 +29,7 @@ jobs: run: | set -euo pipefail cd src - CUR="$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + CUR="$(grep -m1 '^version' breadsearch/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" IFS='.' read -r MA MI PA <<< "${CUR}" SHA="$(git rev-parse --short HEAD)" TS="$(date -u +%Y%m%d%H%M%S)" @@ -62,8 +62,10 @@ jobs: 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 - # --branch dev: the TRACK-aware gen-index.sh isn't merged to - # bread-ecosystem's main yet. - git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci - TRACK=dev bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh + 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 dev 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}" From dabd110f9208a2ae3f95787a4b76e9fe1ec28629 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 11:57:04 +0800 Subject: [PATCH 11/32] random commit message, read it yourself --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 36f7f5b..8d2803a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ logs/ # Runtime files *.sock *.pid + +# Local hygiene notes (not for commit) +CLAUDE.md From 19087b0168105b7a5dff7bdf631b5e06d0b4d692 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 13:52:44 +0800 Subject: [PATCH 12/32] ci: base dev version on the latest published tag, not Cargo.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cargo.toml can drift stale relative to the actual last release (observed on breadbox/breadpad/breadcrumbs/breadpaper), which made the auto-bumped dev version sort as OLDER than what's already installed — bakery's semver check correctly refused those "updates". Deriving the base version from git ls-remote --tags instead is self-healing regardless of Cargo.toml drift, with a Cargo.toml fallback only for a repo with no tags yet. --- .forgejo/workflows/dev-release.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index 4c143bc..7a1ae2c 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -29,7 +29,19 @@ jobs: run: | set -euo pipefail cd src - CUR="$(grep -m1 '^version' breadsearch/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + # 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//' | 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)" From 1f9707f30a146b1989df22fcf05b6c5a4c9ed651 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 18:37:43 +0800 Subject: [PATCH 13/32] ci: make beta a branch-triggered freeze track, not a one-off tag Beta is now a real stabilization branch: publishes on every push to `beta` (mirroring dev's model, auto-versioned X.Y.Z-beta.+, base version from the latest published tag) instead of a manual beta-v* tag. Fixes made during the freeze land via fix/ branches merged into `beta` directly. The gen-index.sh clone for beta pulls bread-ecosystem's default branch (main) rather than pinning to dev, since beta is the more stable track and main now carries the TRACK-aware script. --- .forgejo/workflows/beta-release.yml | 41 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/beta-release.yml index e0870c2..43c2fb4 100644 --- a/.forgejo/workflows/beta-release.yml +++ b/.forgejo/workflows/beta-release.yml @@ -1,12 +1,12 @@ name: beta release -# Publishes a beta-track build when a `beta-v*` tag is pushed — -# separate from release.yml's tag-triggered stable releases. See -# bread-ecosystem's docs/release-channels.md for the three-track policy -# this is part of. +# Publishes a beta-track build on every push to `beta` — a frozen +# stabilization branch cut from `dev` when ready to stabilize; only +# fix/ branches merged into `beta` should land here afterward. +# See bread-ecosystem's docs/release-channels.md for the three-track policy. on: push: - tags: ['beta-v*'] + branches: ['beta'] jobs: build: @@ -16,7 +16,7 @@ jobs: run: | set -euo pipefail rm -rf src && mkdir src - git clone --branch "${GITHUB_REF_NAME}" --depth 1 \ + git clone --branch beta --depth 1 \ "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build @@ -25,10 +25,31 @@ jobs: - name: test run: cd src && cargo test --release --locked --workspace --features full + - name: compute beta version + run: | + set -euo pipefail + cd src + # Base the beta 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 beta 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//' | 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))-beta.${TS}+${SHA}" >> "$GITHUB_ENV" + - name: prepare artifacts run: | set -euo pipefail - VERSION="${GITHUB_REF_NAME#beta-v}" PKG_DIR="/srv/breadway-dl/beta/breadsearch/${VERSION}" mkdir -p "${PKG_DIR}" for bin in breadsearch breadmill; do @@ -42,8 +63,8 @@ jobs: cp src/bakery.toml "${PKG_DIR}/bakery.toml" ln -sfn "${VERSION}" "/srv/breadway-dl/beta/breadsearch/latest" - # No GitHub Release upload — beta, like the other non-stable track, - # is only distributed via dl.breadway.dev/beta/. + # 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 }} @@ -57,6 +78,6 @@ jobs: # 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 dev https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" + 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}" From 08717e51c0dbcfa256ad30a6c30909857083cdf7 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 19:41:42 +0800 Subject: [PATCH 14/32] docs: add CONTRIBUTING.md Documents the dev/beta/main branch and release-track workflow shared across the bread ecosystem. See bread-ecosystem's docs/release-channels.md for the full policy this implements. --- CONTRIBUTING.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..deebcfe --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# 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 + +- **`main`** — release branch, always tag-ready. Nothing is committed to it + directly; it only moves forward via a `beta` merge (see below). +- **`dev`** — integration branch. All day-to-day work lands here first. + Every push to `dev` automatically builds and publishes a **dev-track** + build (see Tracks below) — use this to test your change in a real install + before it goes any further. +- **`beta`** — a frozen stabilization branch, cut from `dev` periodically. + Every push to `beta` automatically builds and publishes a **beta-track** + build. While a freeze is active, only fixes for issues found *in that + freeze* should land on `beta`. + +New work — features and bug fixes alike — goes on a short-lived branch: + +``` +feature/ +fix/ +``` + +Branch off `dev`, open a PR/push back into `dev` when ready. If you're fixing +something reported against an active `beta` freeze, branch off `beta` +instead, merge the fix there to unblock testers, and also forward the same +fix into `dev` so it doesn't quietly reappear next cycle. + +## The release cycle + +1. Work accumulates on `dev` 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 report or fix anything broken with another + push to `dev`. +2. Once `dev` has gone roughly **a week** without new issues, `beta` is cut + fresh from `dev`'s current tip. This freezes it as the stabilization + target — `dev` keeps moving independently starting the next cycle. +3. `beta` is open for anyone to test: `bakery track set beta` and + `bakery update --all`. **File issues against anything you find on this + repo's Forgejo issue tracker.** Fixes land via `fix/` branches + merged into `beta`. +4. Once `beta` has gone roughly **a month** without new issues, it's merged + into `main` and tagged `vX.Y.Z` — that tag is what actually triggers the + stable release build. `beta` is then reset from `dev` to start the next + cycle. + +## 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 | `main`, on a `vX.Y.Z` tag push | +| `beta` | Current stabilization freeze | `beta`, on every push | +| `dev` | Bleeding edge | `dev`, on every push | + +Dev/beta versions are auto-computed (`X.Y.Z-dev.+` / +`-beta.…`) from the latest published stable tag, so they always sort as +newer than what you have installed — no manual version bumping needed when +pushing to `dev` or `beta`. + +## Local development + +```sh +cargo build --release --workspace --features full +cargo test --release --workspace --features full +``` + +## CI + +- `dev-release.yml` — triggered on push to `dev`. +- `beta-release.yml` — triggered on push to `beta`. +- `release.yml` — triggered on a `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. From 5098780f5b7501437ad179905e5700868d3cb72e Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 29 Jul 2026 11:40:23 +0800 Subject: [PATCH 15/32] breadsearch: add --screenshot CLI mode for automated capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same pattern as breadbox/breadclip: one view ("search"), full known-size canvas capture since the search panel isn't its own layer surface. Also fixes the same PID-file-toggle footgun — a screenshot run now skips toggle_or_continue() entirely instead of killing whatever real instance holds breadsearch.pid. --- Cargo.lock | 107 +++++++++++++++++++++++++++++++++- breadsearch/Cargo.toml | 4 ++ breadsearch/src/main.rs | 42 ++++++++++--- breadsearch/src/screenshot.rs | 94 +++++++++++++++++++++++++++++ 4 files changed, 239 insertions(+), 8 deletions(-) create mode 100644 breadsearch/src/screenshot.rs diff --git a/Cargo.lock b/Cargo.lock index 49e20a5..ad06819 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 241457f..c661de3 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -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" diff --git a/breadsearch/src/main.rs b/breadsearch/src/main.rs index a9812fe..146dd46 100644 --- a/breadsearch/src/main.rs +++ b/breadsearch/src/main.rs @@ -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) { + 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>> = 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); } diff --git a/breadsearch/src/screenshot.rs b/breadsearch/src/screenshot.rs new file mode 100644 index 0000000..4373fb3 --- /dev/null +++ b/breadsearch/src/screenshot.rs @@ -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, + + /// PNG path to write the capture to. Required together with --screenshot. + #[arg(long)] + pub output: Option, + + /// 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 { + 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); + } + } +} From 8c11bfd3b8748d1c7f23a76d0292e01a600540e8 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 31 Jul 2026 11:05:29 +0800 Subject: [PATCH 16/32] =?UTF-8?q?CI:=20single-trunk=20model=20=E2=80=94=20?= =?UTF-8?q?dev=20triggers=20on=20main,=20beta=20becomes=20RC-tag-triggered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the dev/beta branch split with one trunk (main): dev-track builds still publish on every push, but the beta track now publishes from a vX.Y.Z-rc.N prerelease tag instead of a separately-maintained beta branch. Removes the branch nobody reliably kept in sync. --- .forgejo/workflows/dev-release.yml | 15 ++++---- .../{beta-release.yml => rc-release.yml} | 38 +++++-------------- .forgejo/workflows/release.yml | 1 + 3 files changed, 17 insertions(+), 37 deletions(-) rename .forgejo/workflows/{beta-release.yml => rc-release.yml} (60%) diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index 7a1ae2c..aa80a71 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -1,12 +1,11 @@ name: dev release -# Publishes a dev-track build on every push to `dev` — -# separate from release.yml's tag-triggered stable releases. See -# bread-ecosystem's docs/release-channels.md for the three-track policy -# this is part of. +# 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: ['dev'] + branches: ['main'] jobs: build: @@ -16,7 +15,7 @@ jobs: run: | set -euo pipefail rm -rf src && mkdir src - git clone --branch dev --depth 1 \ + git clone --branch main --depth 1 \ "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build @@ -36,7 +35,7 @@ jobs: # 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//' | sort -V | tail -1)" + | awk -F/ '{print $NF}' | sed 's/^v//' | (grep -v -- '-' || true) | sort -V | tail -1)" if [ -n "${LATEST_TAG}" ]; then CUR="${LATEST_TAG}" else @@ -78,6 +77,6 @@ jobs: # 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 dev https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" + 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}" diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/rc-release.yml similarity index 60% rename from .forgejo/workflows/beta-release.yml rename to .forgejo/workflows/rc-release.yml index 43c2fb4..1a9385f 100644 --- a/.forgejo/workflows/beta-release.yml +++ b/.forgejo/workflows/rc-release.yml @@ -1,22 +1,23 @@ -name: beta release +name: beta (rc) release -# Publishes a beta-track build on every push to `beta` — a frozen -# stabilization branch cut from `dev` when ready to stabilize; only -# fix/ branches merged into `beta` should land here afterward. -# See bread-ecosystem's docs/release-channels.md for the three-track policy. +# 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: - branches: ['beta'] + 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 beta --depth 1 \ + git clone --branch "${GITHUB_REF_NAME}" --depth 1 \ "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build @@ -25,31 +26,10 @@ jobs: - name: test run: cd src && cargo test --release --locked --workspace --features full - - name: compute beta version - run: | - set -euo pipefail - cd src - # Base the beta 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 beta 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//' | 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))-beta.${TS}+${SHA}" >> "$GITHUB_ENV" - - 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 diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index e53b875..54298b4 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -6,6 +6,7 @@ on: jobs: build: + if: ${{ !contains(github.ref_name, '-rc.') }} runs-on: [self-hosted, hestia] steps: - name: checkout From 4cb254801f07f9018a9f916eb54c5bd725cdae07 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 31 Jul 2026 11:08:41 +0800 Subject: [PATCH 17/32] CONTRIBUTING.md: document single-trunk + RC-tag release model --- CONTRIBUTING.md | 70 ++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index deebcfe..529208f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,16 +7,10 @@ workflow as every other ecosystem product. ## Branches -- **`main`** — release branch, always tag-ready. Nothing is committed to it - directly; it only moves forward via a `beta` merge (see below). -- **`dev`** — integration branch. All day-to-day work lands here first. - Every push to `dev` automatically builds and publishes a **dev-track** - build (see Tracks below) — use this to test your change in a real install - before it goes any further. -- **`beta`** — a frozen stabilization branch, cut from `dev` periodically. - Every push to `beta` automatically builds and publishes a **beta-track** - build. While a freeze is active, only fixes for issues found *in that - freeze* should land on `beta`. +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: @@ -25,28 +19,26 @@ feature/ fix/ ``` -Branch off `dev`, open a PR/push back into `dev` when ready. If you're fixing -something reported against an active `beta` freeze, branch off `beta` -instead, merge the fix there to unblock testers, and also forward the same -fix into `dev` so it doesn't quietly reappear next cycle. +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 -1. Work accumulates on `dev` via `feature/x` / `fix/x` branches. Each push +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 report or fix anything broken with another - push to `dev`. -2. Once `dev` has gone roughly **a week** without new issues, `beta` is cut - fresh from `dev`'s current tip. This freezes it as the stabilization - target — `dev` keeps moving independently starting the next cycle. -3. `beta` is open for anyone to test: `bakery track set beta` and - `bakery update --all`. **File issues against anything you find on this - repo's Forgejo issue tracker.** Fixes land via `fix/` branches - merged into `beta`. -4. Once `beta` has gone roughly **a month** without new issues, it's merged - into `main` and tagged `vX.Y.Z` — that tag is what actually triggers the - stable release build. `beta` is then reset from `dev` to start the next - cycle. + `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 @@ -58,14 +50,15 @@ bakery update --all # pull the latest build on your current track | Track | What it is | Published from | |--------|-----------|-----------------| -| `stable` | The last tagged release | `main`, on a `vX.Y.Z` tag push | -| `beta` | Current stabilization freeze | `beta`, on every push | -| `dev` | Bleeding edge | `dev`, on every push | +| `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/beta versions are auto-computed (`X.Y.Z-dev.+` / -`-beta.…`) from the latest published stable tag, so they always sort as -newer than what you have installed — no manual version bumping needed when -pushing to `dev` or `beta`. +Dev versions are auto-computed (`X.Y.Z-dev.+`) 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 @@ -76,9 +69,10 @@ cargo test --release --workspace --features full ## CI -- `dev-release.yml` — triggered on push to `dev`. -- `beta-release.yml` — triggered on push to `beta`. -- `release.yml` — triggered on a `v*` tag push, cuts the actual stable release. +- `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 From 065f10658441cdf5ef1680e334616ff8999fc34e Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 31 Jul 2026 14:30:01 +0800 Subject: [PATCH 18/32] Repoint bread-ecosystem git deps from dev to main branch --- Cargo.lock | 4 ++-- breadsearch/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad06819..0e1c309 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,7 +197,7 @@ dependencies = [ [[package]] name = "bread-screenshots" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" dependencies = [ "anyhow", "bread-utils 0.3.1", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "bread-utils" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" dependencies = [ "dirs", "serde", diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index c661de3..3f31d22 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -12,7 +12,7 @@ path = "src/main.rs" 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" } +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "main" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" From 572aebd2825cbe475ff6c6562c5a220250355111 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 21:40:06 +0800 Subject: [PATCH 19/32] Pin bread-ecosystem crates to v0.7.1 and document single-trunk + overlay split bread-theme and bread-onnx now come from git.breadway.dev tag v0.7.1 (gtk feature on theme). bread-screenshots is not on that tag, so it is pinned to rev f86e299 instead of branch=main. CLAUDE.md tracks the single-trunk release model; README states breadsearch is document/meaning search and is not breadbox's app-launcher overlay. --- .gitignore | 3 --- CLAUDE.md | 50 ++++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 20 ++++++++--------- README.md | 2 ++ breadmill/Cargo.toml | 2 +- breadsearch/Cargo.toml | 5 +++-- 6 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 8d2803a..36f7f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,3 @@ logs/ # Runtime files *.sock *.pid - -# Local hygiene notes (not for commit) -CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d0dfa26 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,50 @@ +# CLAUDE.md — Repo hygiene + +Scope: this file covers *repo hygiene* — branching, remotes, CI — plus a +short map of the binaries. It is not user-facing project documentation. + +This repo follows the branch/release workflow documented in `CONTRIBUTING.md` +— read and follow it for any git, branch, or release work here (the +single-trunk model, `feature/x`/`fix/x` branch naming, how RC tags work, +etc). Don't improvise a different workflow. The short version: there is one +long-lived branch, `main` — no `dev` or `beta` branch exists. `main` +auto-publishes a dev-track build on every push. "Beta" and "stable" are both +just tags, not branches: push a `vX.Y.Z-rc.N` tag to publish a beta-track +build, push a plain `vX.Y.Z` tag to cut the signed stable release. +"Freezing" for stabilization means pausing pushes to `main`, not moving a +branch. This replaced an earlier three-branch (`dev`/`beta`/`main`) model +after `main` was found to have silently rotted out of sync with `dev`/`beta` +across most repos in this ecosystem. + +When starting work on a new feature, create branch `feature/`. +When working on a bug or issue, create branch `fix/`. + +## Remotes +- `origin` — Forgejo (`git.breadway.dev` via Hestia, SSH) — authoritative. +- `github` — GitHub mirror. Push both when publishing. + +## CI +- `dev-release.yml` triggers on `push: branches: ['main']`. +- `rc-release.yml` triggers on `vX.Y.Z-rc.N` tag pushes (beta track). +- `release.yml` triggers on any other `v*` tag push (stable). + None of these run on plain commits or PRs beyond what's listed. + +## Architecture + +Two binaries + a shared lib: + +| Crate | Role | +|---|---| +| `breadsearch-shared` | XDG paths, config, IPC types, Unix-socket client | +| `breadmill` | Daemon: walk → extract → chunk → embed → index; serves queries | +| `breadsearch` | GTK4 layer-shell overlay: ranked document hits from breadmill | + +breadsearch is document/meaning search. breadbox is the app launcher. They +are not the same overlay. + +`--screenshot` (`breadsearch/src/screenshot.rs`) captures the search panel +through `bread-screenshots`; do not rewrite it just to retarget the crate pin. + +## Don't +- Don't embed credentials in remote URLs — SSH or a credential helper only. +- Don't rewrite `screenshot.rs` as part of pin/docs work. diff --git a/Cargo.lock b/Cargo.lock index 0e1c309..e679363 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,11 +181,11 @@ dependencies = [ [[package]] name = "bread-onnx" -version = "0.3.0" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" dependencies = [ "anyhow", - "bread-utils 0.3.0", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", "hex", "ort", "sha2 0.10.9", @@ -197,17 +197,17 @@ dependencies = [ [[package]] name = "bread-screenshots" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=f86e299f4a0ea73ff485cd84923b986ddcc8242e#f86e299f4a0ea73ff485cd84923b986ddcc8242e" dependencies = [ "anyhow", - "bread-utils 0.3.1", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=f86e299f4a0ea73ff485cd84923b986ddcc8242e)", "tracing", ] [[package]] name = "bread-theme" -version = "0.2.3" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.10#17d1bb85801b9a8c195b64c02d288cd662c9c780" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" dependencies = [ "dirs", "gtk4", @@ -217,8 +217,8 @@ dependencies = [ [[package]] name = "bread-utils" -version = "0.3.0" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" dependencies = [ "dirs", "serde", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "bread-utils" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=f86e299f4a0ea73ff485cd84923b986ddcc8242e#f86e299f4a0ea73ff485cd84923b986ddcc8242e" dependencies = [ "dirs", "serde", diff --git a/README.md b/README.md index af25de7..71663ce 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Semantic document search for Bread OS. Type a concept — not a keyword — and get ranked hits from your documents. +This is **not** [breadbox](https://git.breadway.dev/Breadway/breadbox). breadsearch is document/meaning search over your files; breadbox is the app launcher (fuzzy `.desktop` launch). They are separate overlays with separate binaries, sockets, and keybinds — even though the search panel was originally forked from breadbox's launcher UI. + Two binaries in one Cargo workspace: - **breadmill** — background daemon. Walks configured directories, extracts text, chunks and embeds documents with [nomic-embed-text-v1.5](https://huggingface.co/nomic-ai/nomic-embed-text-v1.5) (768-dim ONNX), stores vectors in an HNSW index (usearch) backed by SQLite metadata, and serves queries over a Unix socket. Watches for filesystem changes and re-indexes incrementally. diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index 9faeae0..bd3a886 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -42,7 +42,7 @@ breadsearch-shared = { path = "../breadsearch-shared" } # needed for the plain CPU path even in the npu build. ort = { version = "2.0.0-rc.12", default-features = false, features = ["std", "tracing", "download-binaries", "tls-native", "copy-dylibs", "api-23"] } tokenizers = "0" -bread-onnx = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0" } +bread-onnx = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" } # Surfaces ort's own EP-registration tracing (e.g. a GPU EP silently failing to # register and falling back to CPU) as visible log output instead of nowhere. diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 3f31d22..1d2cb0b 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -10,9 +10,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"] } +bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["gtk"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. -bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "main" } +# Not on tag v0.7.1 (crate landed after that tag). Rev, not branch=main. +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", rev = "f86e299f4a0ea73ff485cd84923b986ddcc8242e" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" From 6983fd83daf6a491eb06e9f5022f50cb4b6eba8f Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:03:30 +0800 Subject: [PATCH 20/32] Align bread-screenshots rev with siblings; track AGENTS.md --- AGENTS.md | 50 ++++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 6 ++--- breadsearch/Cargo.toml | 3 ++- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3c0ac3f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,50 @@ +# AGENTS.md — Repo hygiene + +Scope: this file covers *repo hygiene* — branching, remotes, CI — plus a +short map of the binaries. It is not user-facing project documentation. + +This repo follows the branch/release workflow documented in `CONTRIBUTING.md` +— read and follow it for any git, branch, or release work here (the +single-trunk model, `feature/x`/`fix/x` branch naming, how RC tags work, +etc). Don't improvise a different workflow. The short version: there is one +long-lived branch, `main` — no `dev` or `beta` branch exists. `main` +auto-publishes a dev-track build on every push. "Beta" and "stable" are both +just tags, not branches: push a `vX.Y.Z-rc.N` tag to publish a beta-track +build, push a plain `vX.Y.Z` tag to cut the signed stable release. +"Freezing" for stabilization means pausing pushes to `main`, not moving a +branch. This replaced an earlier three-branch (`dev`/`beta`/`main`) model +after `main` was found to have silently rotted out of sync with `dev`/`beta` +across most repos in this ecosystem. + +When starting work on a new feature, create branch `feature/`. +When working on a bug or issue, create branch `fix/`. + +## Remotes +- `origin` — Forgejo (`git.breadway.dev` via Hestia, SSH) — authoritative. +- `github` — GitHub mirror. Push both when publishing. + +## CI +- `dev-release.yml` triggers on `push: branches: ['main']`. +- `rc-release.yml` triggers on `vX.Y.Z-rc.N` tag pushes (beta track). +- `release.yml` triggers on any other `v*` tag push (stable). + None of these run on plain commits or PRs beyond what's listed. + +## Architecture + +Two binaries + a shared lib: + +| Crate | Role | +|---|---| +| `breadsearch-shared` | XDG paths, config, IPC types, Unix-socket client | +| `breadmill` | Daemon: walk → extract → chunk → embed → index; serves queries | +| `breadsearch` | GTK4 layer-shell overlay: ranked document hits from breadmill | + +breadsearch is document/meaning search. breadbox is the app launcher. They +are not the same overlay. + +`--screenshot` (`breadsearch/src/screenshot.rs`) captures the search panel +through `bread-screenshots`; do not rewrite it just to retarget the crate pin. + +## Don't +- Don't embed credentials in remote URLs — SSH or a credential helper only. +- Don't rewrite `screenshot.rs` as part of pin/docs work. diff --git a/Cargo.lock b/Cargo.lock index e679363..b911012 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,10 +197,10 @@ dependencies = [ [[package]] name = "bread-screenshots" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=f86e299f4a0ea73ff485cd84923b986ddcc8242e#f86e299f4a0ea73ff485cd84923b986ddcc8242e" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" dependencies = [ "anyhow", - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=f86e299f4a0ea73ff485cd84923b986ddcc8242e)", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d)", "tracing", ] @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "bread-utils" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=f86e299f4a0ea73ff485cd84923b986ddcc8242e#f86e299f4a0ea73ff485cd84923b986ddcc8242e" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" dependencies = [ "dirs", "serde", diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 1d2cb0b..0d2e114 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -13,7 +13,8 @@ breadsearch-shared = { path = "../breadsearch-shared" } bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["gtk"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. # Not on tag v0.7.1 (crate landed after that tag). Rev, not branch=main. -bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", rev = "f86e299f4a0ea73ff485cd84923b986ddcc8242e" } +# v0.7.1 predates this crate; same rev siblings use. +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", rev = "69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" From 95a97d63d3320a9f1be510857ccb69f320c5beeb Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:04:00 +0800 Subject: [PATCH 21/32] Remove CLAUDE.md (renamed to AGENTS.md) --- CLAUDE.md | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index d0dfa26..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,50 +0,0 @@ -# CLAUDE.md — Repo hygiene - -Scope: this file covers *repo hygiene* — branching, remotes, CI — plus a -short map of the binaries. It is not user-facing project documentation. - -This repo follows the branch/release workflow documented in `CONTRIBUTING.md` -— read and follow it for any git, branch, or release work here (the -single-trunk model, `feature/x`/`fix/x` branch naming, how RC tags work, -etc). Don't improvise a different workflow. The short version: there is one -long-lived branch, `main` — no `dev` or `beta` branch exists. `main` -auto-publishes a dev-track build on every push. "Beta" and "stable" are both -just tags, not branches: push a `vX.Y.Z-rc.N` tag to publish a beta-track -build, push a plain `vX.Y.Z` tag to cut the signed stable release. -"Freezing" for stabilization means pausing pushes to `main`, not moving a -branch. This replaced an earlier three-branch (`dev`/`beta`/`main`) model -after `main` was found to have silently rotted out of sync with `dev`/`beta` -across most repos in this ecosystem. - -When starting work on a new feature, create branch `feature/`. -When working on a bug or issue, create branch `fix/`. - -## Remotes -- `origin` — Forgejo (`git.breadway.dev` via Hestia, SSH) — authoritative. -- `github` — GitHub mirror. Push both when publishing. - -## CI -- `dev-release.yml` triggers on `push: branches: ['main']`. -- `rc-release.yml` triggers on `vX.Y.Z-rc.N` tag pushes (beta track). -- `release.yml` triggers on any other `v*` tag push (stable). - None of these run on plain commits or PRs beyond what's listed. - -## Architecture - -Two binaries + a shared lib: - -| Crate | Role | -|---|---| -| `breadsearch-shared` | XDG paths, config, IPC types, Unix-socket client | -| `breadmill` | Daemon: walk → extract → chunk → embed → index; serves queries | -| `breadsearch` | GTK4 layer-shell overlay: ranked document hits from breadmill | - -breadsearch is document/meaning search. breadbox is the app launcher. They -are not the same overlay. - -`--screenshot` (`breadsearch/src/screenshot.rs`) captures the search panel -through `bread-screenshots`; do not rewrite it just to retarget the crate pin. - -## Don't -- Don't embed credentials in remote URLs — SSH or a credential helper only. -- Don't rewrite `screenshot.rs` as part of pin/docs work. From 0e3c8a1668140f332d2b190b38f7f328ec28da84 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:14:47 +0800 Subject: [PATCH 22/32] breadsearch: emit bread.search.opened and bread.search.opened_result MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire the GTK overlay to the bread event fabric via bread-utils BreadClient (app id search). Fail-silent when breadd is down. No command verbs — the overlay has no existing command surface. --- Cargo.lock | 13 ++++++++++++ EVENTS.md | 36 +++++++++++++++++++++++++++++++++ breadsearch/Cargo.toml | 2 ++ breadsearch/src/bread_events.rs | 26 ++++++++++++++++++++++++ breadsearch/src/main.rs | 4 ++++ 5 files changed, 81 insertions(+) create mode 100644 EVENTS.md create mode 100644 breadsearch/src/bread_events.rs diff --git a/Cargo.lock b/Cargo.lock index b911012..118b7d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,6 +204,17 @@ dependencies = [ "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.3.1" @@ -220,6 +231,7 @@ name = "bread-utils" version = "0.3.1" source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" dependencies = [ + "bread-shared", "dirs", "serde", "serde_json", @@ -265,6 +277,7 @@ dependencies = [ "anyhow", "bread-screenshots", "bread-theme", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", "breadsearch-shared", "clap", "gtk4", diff --git a/EVENTS.md b/EVENTS.md new file mode 100644 index 0000000..dbcaa54 --- /dev/null +++ b/EVENTS.md @@ -0,0 +1,36 @@ +# breadsearch — bread event integration + +breadsearch is a standalone document-search overlay: it works exactly the +same with or without `breadd` running. When breadd *is* present, the +`breadsearch` GTK overlay publishes events into the shared bread automation +fabric. 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: **`search`**. Transport: `bread-utils`'s `bread_client` module +(feature `bread-client`) — the overlay links it directly. Each `emit` is +its own short-lived connection (`BreadClient::emit` is fire-and-forget, +the same stance as `bread-emit`). breadmill, the indexing daemon, does +not talk to breadd. + +## Events published (`bread.search.*`) + +| Event | Data | When | +|-------|------|------| +| `bread.search.opened` | `{}` | The overlay window maps (the search panel is shown). | +| `bread.search.opened_result` | `{ "path": "" }` | The user opens a hit — Enter / click opens the file, Ctrl+Enter reveals its folder. `path` is the hit's document path, not the parent folder. | + +## Commands honored (`bread.command.search.*`) + +None. The overlay is a short-lived toggle process with no existing command +surface (no show/hide/query IPC beyond the PID-file toggle and breadmill's +own query socket). Adding verbs would mean inventing a control plane that +does not exist; if/when breadsearch grows one, the corresponding +`bread.command.search.*` verbs should be added at the same time, not stubbed +out 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) — breadsearch's + overlay and breadmill's indexing/query path are entirely unaffected. diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 0d2e114..311cefd 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -11,6 +11,8 @@ path = "src/main.rs" [dependencies] breadsearch-shared = { path = "../breadsearch-shared" } bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["gtk"] } +# Bread event fabric client — emit bread.search.* (fail-silent if breadd is down). +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["bread-client"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. # Not on tag v0.7.1 (crate landed after that tag). Rev, not branch=main. # v0.7.1 predates this crate; same rev siblings use. diff --git a/breadsearch/src/bread_events.rs b/breadsearch/src/bread_events.rs new file mode 100644 index 0000000..d87e595 --- /dev/null +++ b/breadsearch/src/bread_events.rs @@ -0,0 +1,26 @@ +//! `bread.search.*` event integration — optional, non-blocking. See +//! `EVENTS.md` at the repo root for the full contract. breadsearch works +//! identically with or without breadd running; every call here is +//! fire-and-forget (`BreadClient::emit` never blocks or errors this +//! process) so a missing or restarting breadd never affects the overlay. + +use bread_utils::bread_client::BreadClient; + +/// This app's id in bread's sibling-app namespace registry +/// (`bread_shared::apps::KNOWN_APPS`) — events publish as `bread.search.*`. +pub const APP_ID: &str = "search"; + +fn client() -> BreadClient { + BreadClient::connect(APP_ID) +} + +pub fn emit_opened() { + client().emit("bread.search.opened", serde_json::json!({})); +} + +pub fn emit_opened_result(path: &str) { + client().emit( + "bread.search.opened_result", + serde_json::json!({ "path": path }), + ); +} diff --git a/breadsearch/src/main.rs b/breadsearch/src/main.rs index 146dd46..6f55769 100644 --- a/breadsearch/src/main.rs +++ b/breadsearch/src/main.rs @@ -18,6 +18,7 @@ use gtk4::{ }; use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell}; +mod bread_events; mod screenshot; // ---- Theming ---------------------------------------------------------------- @@ -193,6 +194,7 @@ fn open_file(path: &str) { .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) .spawn(); + bread_events::emit_opened_result(path); } fn open_folder(path: &str) { @@ -206,6 +208,7 @@ fn open_folder(path: &str) { .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) .spawn(); + bread_events::emit_opened_result(path); } // ---- UI --------------------------------------------------------------------- @@ -433,6 +436,7 @@ fn run_ui(screenshot_req: Option) { screenshot::dispatch(&window, req); } + window.connect_map(|_| bread_events::emit_opened()); window.present(); search.grab_focus(); }); From 4f9f4de911828df36ff457859d5140a0cfd5ff85 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 5 Aug 2026 19:14:43 +0800 Subject: [PATCH 23/32] CI: build inside bread-ecosystem's shared Arch container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports breadsearch onto the shared, pinned Arch build image already proven in breadpad, so this repo's release CI stops installing its toolchain/GTK4 stack directly on the bare self-hosted runner. - ci/build.sh + ci/bread-ecosystem.rev: thin wrapper cloning bread-ecosystem pinned to a commit sha and delegating to its own ci/build.sh. - dev-release.yml/rc-release.yml/release.yml: route the existing build/test cargo invocations through ci/build.sh, unchanged otherwise. - check.yml: fast clippy/test signal on feature/** and fix/** pushes. No ci/deps.txt: breadmill's npu/rocm/cuda/openvino features (pulled in by --features full) all use ort's load-dynamic (dlopen) mode, so none of them link against a real ONNX Runtime EP at build time — confirmed against breadmill/Cargo.toml and by breadpad already building ort with its own rocm feature against the same base image with no extra packages. The GUI's librsvg system dep is a runtime icon-loader dependency (bakery.toml's system_deps), not something linked at build/link time, so the base image's gtk4/gtk4-layer-shell already cover what compiling breadsearch needs. --- .forgejo/workflows/check.yml | 24 ++++++++++++++++++++++++ .forgejo/workflows/dev-release.yml | 4 ++-- .forgejo/workflows/rc-release.yml | 4 ++-- .forgejo/workflows/release.yml | 4 ++-- ci/bread-ecosystem.rev | 1 + ci/build.sh | 21 +++++++++++++++++++++ 6 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 .forgejo/workflows/check.yml create mode 100644 ci/bread-ecosystem.rev create mode 100755 ci/build.sh diff --git a/.forgejo/workflows/check.yml b/.forgejo/workflows/check.yml new file mode 100644 index 0000000..c9431c8 --- /dev/null +++ b/.forgejo/workflows/check.yml @@ -0,0 +1,24 @@ +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 --features full -- -D warnings + + - name: test + run: cd src && bash ci/build.sh cargo test --workspace --locked --features full diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index aa80a71..85fcaa6 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -19,10 +19,10 @@ jobs: "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build - run: cd src && cargo build --release --locked --workspace --features full + run: cd src && bash ci/build.sh cargo build --release --locked --workspace --features full - name: test - run: cd src && cargo test --release --locked --workspace --features full + run: cd src && bash ci/build.sh cargo test --release --locked --workspace --features full - name: compute dev version run: | diff --git a/.forgejo/workflows/rc-release.yml b/.forgejo/workflows/rc-release.yml index 1a9385f..3bb6832 100644 --- a/.forgejo/workflows/rc-release.yml +++ b/.forgejo/workflows/rc-release.yml @@ -21,10 +21,10 @@ jobs: "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build - run: cd src && cargo build --release --locked --workspace --features full + run: cd src && bash ci/build.sh cargo build --release --locked --workspace --features full - name: test - run: cd src && cargo test --release --locked --workspace --features full + run: cd src && bash ci/build.sh cargo test --release --locked --workspace --features full - name: prepare artifacts run: | diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 54298b4..bd7ff33 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -22,10 +22,10 @@ jobs: # `backend` in config.toml. All three are ort load-dynamic (dlopen) # EPs, so this doesn't require the NPU/ROCm/CUDA toolkits to be # present on the build host — see breadmill/Cargo.toml. - run: cd src && cargo build --release --locked --workspace --features full + run: cd src && bash ci/build.sh cargo build --release --locked --workspace --features full - name: test - run: cd src && cargo test --release --locked --workspace --features full + run: cd src && bash ci/build.sh cargo test --release --locked --workspace --features full - name: prepare artifacts run: | diff --git a/ci/bread-ecosystem.rev b/ci/bread-ecosystem.rev new file mode 100644 index 0000000..34e7aa9 --- /dev/null +++ b/ci/bread-ecosystem.rev @@ -0,0 +1 @@ +147cfbbf96ae4b171027defa1130d2caddb934b1 diff --git a/ci/build.sh b/ci/build.sh new file mode 100755 index 0000000..2c71070 --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,21 @@ +#!/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" breadsearch "$ROOT" "$@" From 93c34e738714ffeea44423a8137bd816901f92d4 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:53:46 +0800 Subject: [PATCH 24/32] Pin bread-ecosystem crates to v0.7.2 Repoint bread-theme, bread-utils, bread-screenshots, and bread-onnx at tag v0.7.2 on git.breadway.dev. Drop the 69ce2d67 rev pin now that screenshots is on a tagged release. --- Cargo.lock | 32 +++++++++++--------------------- breadmill/Cargo.toml | 2 +- breadsearch/Cargo.toml | 8 +++----- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 118b7d4..f0fc8bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,11 +181,11 @@ dependencies = [ [[package]] name = "bread-onnx" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "anyhow", - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", + "bread-utils", "hex", "ort", "sha2 0.10.9", @@ -196,11 +196,11 @@ dependencies = [ [[package]] name = "bread-screenshots" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "anyhow", - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d)", + "bread-utils", "tracing", ] @@ -217,8 +217,8 @@ dependencies = [ [[package]] name = "bread-theme" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "dirs", "gtk4", @@ -228,8 +228,8 @@ dependencies = [ [[package]] name = "bread-utils" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "bread-shared", "dirs", @@ -237,16 +237,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "bread-utils" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" -dependencies = [ - "dirs", - "serde", - "serde_json", -] - [[package]] name = "breadmill" version = "0.3.0" @@ -277,7 +267,7 @@ dependencies = [ "anyhow", "bread-screenshots", "bread-theme", - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", + "bread-utils", "breadsearch-shared", "clap", "gtk4", diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index bd3a886..a651199 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -42,7 +42,7 @@ breadsearch-shared = { path = "../breadsearch-shared" } # needed for the plain CPU path even in the npu build. ort = { version = "2.0.0-rc.12", default-features = false, features = ["std", "tracing", "download-binaries", "tls-native", "copy-dylibs", "api-23"] } tokenizers = "0" -bread-onnx = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" } +bread-onnx = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2" } # Surfaces ort's own EP-registration tracing (e.g. a GPU EP silently failing to # register and falling back to CPU) as visible log output instead of nowhere. diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 311cefd..5b4933c 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -10,13 +10,11 @@ path = "src/main.rs" [dependencies] breadsearch-shared = { path = "../breadsearch-shared" } -bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["gtk"] } +bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["gtk"] } # Bread event fabric client — emit bread.search.* (fail-silent if breadd is down). -bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["bread-client"] } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["bread-client"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. -# Not on tag v0.7.1 (crate landed after that tag). Rev, not branch=main. -# v0.7.1 predates this crate; same rev siblings use. -bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", rev = "69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" } +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" From e20c64fa1a48f732dbb180f53ae0451df461830f Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 23:05:47 +0800 Subject: [PATCH 25/32] Bump version to v0.3.1 --- Cargo.lock | 6 +++--- breadmill/Cargo.toml | 2 +- breadsearch-shared/Cargo.toml | 2 +- breadsearch/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0fc8bf..45c703a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,7 +239,7 @@ dependencies = [ [[package]] name = "breadmill" -version = "0.3.0" +version = "0.3.1" dependencies = [ "bread-onnx", "breadsearch-shared", @@ -262,7 +262,7 @@ dependencies = [ [[package]] name = "breadsearch" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "bread-screenshots", @@ -277,7 +277,7 @@ dependencies = [ [[package]] name = "breadsearch-shared" -version = "0.3.0" +version = "0.3.1" dependencies = [ "serde", "serde_json", diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index a651199..f201c9a 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadmill" -version = "0.3.0" +version = "0.3.1" edition = "2021" license = "MIT" diff --git a/breadsearch-shared/Cargo.toml b/breadsearch-shared/Cargo.toml index 3ebc491..a25ee94 100644 --- a/breadsearch-shared/Cargo.toml +++ b/breadsearch-shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadsearch-shared" -version = "0.3.0" +version = "0.3.1" edition = "2021" license = "MIT" diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 5b4933c..abc0269 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadsearch" -version = "0.3.0" +version = "0.3.1" edition = "2021" license = "MIT" From cdee9da60989b68d9fbf863acefaefca5b20edb4 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 23:11:28 +0800 Subject: [PATCH 26/32] Honor bread.command.search.open via breadsearch listen --- EVENTS.md | 44 +++++++++++++++----- breadsearch/src/listen.rs | 84 +++++++++++++++++++++++++++++++++++++++ breadsearch/src/main.rs | 6 +++ 3 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 breadsearch/src/listen.rs diff --git a/EVENTS.md b/EVENTS.md index dbcaa54..61f5030 100644 --- a/EVENTS.md +++ b/EVENTS.md @@ -10,8 +10,10 @@ convention this follows. App id: **`search`**. Transport: `bread-utils`'s `bread_client` module (feature `bread-client`) — the overlay links it directly. Each `emit` is its own short-lived connection (`BreadClient::emit` is fire-and-forget, -the same stance as `bread-emit`). breadmill, the indexing daemon, does -not talk to breadd. +the same stance as `bread-emit`). Command verbs are only received while +`breadsearch listen` is running — that process holds the +`bread.command.search.**` subscription open. breadmill, the indexing +daemon, does not talk to breadd. ## Events published (`bread.search.*`) @@ -19,18 +21,42 @@ not talk to breadd. |-------|------|------| | `bread.search.opened` | `{}` | The overlay window maps (the search panel is shown). | | `bread.search.opened_result` | `{ "path": "" }` | The user opens a hit — Enter / click opens the file, Ctrl+Enter reveals its folder. `path` is the hit's document path, not the parent folder. | +| `bread.search.open.done` | `{}` | `bread.command.search.open` was received and `breadsearch` was spawned. This is the command confirmation, not proof the overlay mapped — the spawned process is the same PID-file toggle as a keybind. | +| `bread.search.open.failed` | `{ "error": "" }` | `bread.command.search.open` was received but this binary could not be started. | ## Commands honored (`bread.command.search.*`) -None. The overlay is a short-lived toggle process with no existing command -surface (no show/hide/query IPC beyond the PID-file toggle and breadmill's -own query socket). Adding verbs would mean inventing a control plane that -does not exist; if/when breadsearch grows one, the corresponding -`bread.command.search.*` verbs should be added at the same time, not stubbed -out ahead of it. +These are only received while `breadsearch listen` is running. Publishing a +command with no subscriber is a silent no-op — that is the documented +bread convention, not a breadsearch bug. + +| Verb | Data | Effect | +|------|------|--------| +| `open` | none | Same as running `breadsearch` (PID-file toggle: show the overlay, or dismiss it if it is already up). Emits `bread.search.open.done` / `.failed`. | + +```lua +bread.spawn(function() + bread.emit("bread.command.search.open") + bread.wait("bread.search.open.done", { timeout = 5000 }) +end) +``` + +### Not implemented: extra verbs + +There is no `query` / `close` / `reindex` command verb. breadmill already +has its own query socket; inventing a bus query plane would be a new +product surface. If/when that exists, add the corresponding +`bread.command.search.*` 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) — breadsearch's + (`BreadClient::emit` never blocks or errors the caller) and the + command subscription simply never receives anything — breadsearch's overlay and breadmill's indexing/query path are entirely unaffected. +- If breadd restarts, the command subscription reconnects automatically + (`BreadClient::subscribe`'s background thread has its own backoff + loop); no restart of `breadsearch listen` is needed. +- If `breadsearch listen` is not running, commands are a graceful no-op at + the bus (no subscriber). The overlay CLI still works. diff --git a/breadsearch/src/listen.rs b/breadsearch/src/listen.rs new file mode 100644 index 0000000..aef9c84 --- /dev/null +++ b/breadsearch/src/listen.rs @@ -0,0 +1,84 @@ +//! Long-running command subscription for `bread.command.search.*`. +//! +//! `breadsearch` is still a one-shot toggle overlay by default. +//! `breadsearch listen` is the optional persistent process that can honor +//! bus commands. See `EVENTS.md`. + +use bread_utils::bread_client::{BreadClient, BreadEvent}; + +use crate::bread_events::APP_ID; + +/// Subscribe to `bread.command.search.**` 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!( + "breadsearch: breadd unreachable; command subscription will connect when it comes back" + ); + } + + let _commands = client.subscribe("bread.command.search.**", |event| { + handle_command(&event); + }); + + eprintln!("breadsearch: listening for bread.command.search.**"); + loop { + std::thread::park(); + } +} + +/// Reacts to `bread.command.search.*` 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!("breadsearch: ignoring unrecognized bread.command.search.{other}"); + } + } +} + +fn handle_open() { + // Same as running `breadsearch` from a keybind: the PID-file toggle + // shows the overlay (or dismisses it if it is already up). + let result = spawn_self(); + let client = BreadClient::connect(APP_ID); + match result { + Ok(_) => client.emit("bread.search.open.done", serde_json::json!({})), + Err(e) => { + eprintln!("breadsearch: bread.command.search.open failed: {e}"); + client.emit( + "bread.search.open.failed", + serde_json::json!({ "error": e.to_string() }), + ); + } + } +} + +fn spawn_self() -> std::io::Result { + let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("breadsearch")); + std::process::Command::new(exe).spawn() +} + +fn command_verb(event_name: &str) -> Option<&str> { + event_name.strip_prefix("bread.command.search.") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn command_verb_strips_search_prefix() { + assert_eq!(command_verb("bread.command.search.open"), Some("open")); + assert_eq!(command_verb("bread.command.search.query"), Some("query")); + assert_eq!(command_verb("bread.command.box.open"), None); + assert_eq!(command_verb("bread.search.opened"), None); + } +} diff --git a/breadsearch/src/main.rs b/breadsearch/src/main.rs index 6f55769..247ad80 100644 --- a/breadsearch/src/main.rs +++ b/breadsearch/src/main.rs @@ -19,6 +19,7 @@ use gtk4::{ use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell}; mod bread_events; +mod listen; mod screenshot; // ---- Theming ---------------------------------------------------------------- @@ -454,6 +455,11 @@ fn run_ui(screenshot_req: Option) { // ---- 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(); From 7eefe9087a9717bc500c272bd99e2b8581f52d47 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 16 Aug 2026 00:26:06 +0800 Subject: [PATCH 27/32] Adopt bread_utils::screenshot_cli for --screenshot flags Replace the local settle delay, canvas defaults, and pair-validation error path with bread-utils v0.7.2. Clap parsing stays in-tree. --- breadsearch/src/screenshot.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/breadsearch/src/screenshot.rs b/breadsearch/src/screenshot.rs index 4373fb3..ad1f6ad 100644 --- a/breadsearch/src/screenshot.rs +++ b/breadsearch/src/screenshot.rs @@ -7,15 +7,10 @@ //! 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 bread_utils::screenshot_cli::{validate_pair, DEFAULT_HEIGHT, DEFAULT_WIDTH, SETTLE_DELAY}; 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")] @@ -31,11 +26,11 @@ pub struct Cli { /// Capture canvas width — matches the isolated compositor's output width /// (`bread-capture --isolate-width`). - #[arg(long, default_value_t = 1920)] + #[arg(long, default_value_t = DEFAULT_WIDTH)] pub width: u32, /// Capture canvas height — see `width`. - #[arg(long, default_value_t = 1080)] + #[arg(long, default_value_t = DEFAULT_HEIGHT)] pub height: u32, } @@ -48,16 +43,20 @@ pub struct ScreenshotRequest { } impl Cli { - /// `None` for a normal run. Exits the process with an error if - /// `--screenshot` was given without `--output`, before any GTK setup + /// `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 { - let view = self.screenshot.clone()?; - let Some(output) = self.output.clone() else { - eprintln!("breadsearch: --screenshot requires --output"); + if let Err(e) = validate_pair(self.screenshot.as_deref(), self.output.as_deref()) { + eprintln!("breadsearch: {e}"); std::process::exit(1); - }; - Some(ScreenshotRequest { view, output, width: self.width, height: self.height }) + } + Some(ScreenshotRequest { + view: self.screenshot.clone()?, + output: self.output.clone()?, + width: self.width, + height: self.height, + }) } } From e1a2bef109de6b855a52ca934916491c039a072c Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 16 Aug 2026 00:50:22 +0800 Subject: [PATCH 28/32] CI: refuse unsigned bakery index on stable tag releases --- .forgejo/workflows/release.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index bd7ff33..829ff6f 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -22,7 +22,16 @@ jobs: # `backend` in config.toml. All three are ort load-dynamic (dlopen) # EPs, so this doesn't require the NPU/ROCm/CUDA toolkits to be # present on the build host — see breadmill/Cargo.toml. - run: cd src && bash ci/build.sh cargo build --release --locked --workspace --features full + 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 --workspace --features full || { + echo "::error::cargo build --release --locked failed. If Cargo.lock drifted, update and commit it; do not drop --locked." + exit 1 + } - name: test run: cd src && bash ci/build.sh cargo test --release --locked --workspace --features full @@ -45,8 +54,14 @@ jobs: ln -sfn "${VERSION}" "/srv/breadway-dl/breadsearch/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 From 60d1d817779b82f767dc38cf802ea88d47015964 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 16 Aug 2026 13:23:15 +0800 Subject: [PATCH 29/32] Bind the window to the current monitor's bread-theme palette Pin bread-theme to v0.7.4. --- Cargo.lock | 465 ++++++++++++++++++++++------------------ breadsearch/Cargo.toml | 2 +- breadsearch/src/main.rs | 1 + 3 files changed, 253 insertions(+), 215 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45c703a..c615ccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,9 +44,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arbitrary" @@ -134,6 +134,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64ct" version = "1.8.3" @@ -217,8 +223,8 @@ dependencies = [ [[package]] name = "bread-theme" -version = "0.7.2" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" +version = "0.7.4" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.4#fcba3760387e2523edb71350f8efea3bc851b21e" dependencies = [ "dirs", "gtk4", @@ -286,9 +292,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", "serde_core", @@ -351,7 +357,7 @@ checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -374,9 +380,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.3.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -429,9 +435,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -439,9 +445,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -451,14 +457,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -636,9 +642,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00424da159cc5adcb4eeea7e7b5cb1d96df41f5fa695ec596922181bdc36232a" +checksum = "824894a4a85dca76d4c95c2b9098c036f5a29f627b30c12780774f6654e60974" dependencies = [ "cc", "cxx-build", @@ -651,9 +657,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e05269dbed4dab7072ae0f04ef31799ad189d52ce2ac12710c2997b754f86a" +checksum = "f1ae0b651ea5b0000b19513aef5a03f194d7e3486f2d9258b658da8677fe9036" dependencies = [ "cc", "codespan-reporting", @@ -661,39 +667,39 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 3.0.3", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2f017b07e0da7f425642339faff0edc9f0de6459a18180183d086d1f3381e89" +checksum = "fb05f91d3fb8435d9bab6ac5ce6ac1868be774325fb7fb2a91be39393b21388e" dependencies = [ "clap", "codespan-reporting", "indexmap", "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "cxxbridge-flags" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293d267f43a5778bf3b89fff2a658f081166e7f152d9640e2ee3d917d065a5fc" +checksum = "bf293202e0e3e98495785745389e8d0755b217e66f19194a5c695c25e03282ef" [[package]] name = "cxxbridge-macro" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd233dc128223fe85d2afa5c617c79743c0f47fb495b69234b5da680d4986a" +checksum = "ca001d746947c7249ed9d332a10f7a59daedbafeb0ec68c5c18a7db7a93f6ccc" dependencies = [ "indexmap", "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -723,7 +729,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.119", ] [[package]] @@ -734,7 +740,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -776,7 +782,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -797,7 +803,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -807,7 +813,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn", + "syn 2.0.119", ] [[package]] @@ -855,13 +861,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -875,9 +881,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "encode_unicode" @@ -942,9 +948,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "field-offset" @@ -968,9 +974,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "flate2" @@ -1029,24 +1035,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -1055,32 +1061,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-macro", @@ -1111,7 +1117,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -1144,7 +1150,7 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -1222,7 +1228,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.8", "windows-sys 0.61.2", ] @@ -1276,7 +1282,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1286,14 +1292,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "030967459f9f676851872c6304adea7825c6d462ec9b72554c733cf0c5952233" dependencies = [ "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] name = "globset" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", @@ -1310,7 +1316,7 @@ checksum = "22a861859b887a79cf461359c192c97a57d8fb0229dd291232e57aa11f6fa72c" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -1331,7 +1337,7 @@ checksum = "5c7ffdfde88f3570d3705e0d8a2433e036d387a1f2930bbf47eafcb5f569fd04" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -1362,7 +1368,7 @@ dependencies = [ "graphene-sys", "libc", "pango-sys", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -1388,9 +1394,9 @@ dependencies = [ [[package]] name = "gtk4-layer-shell" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4069987ff4793699511a251028cc336b438e46565b463f111250148d574752a" +checksum = "17c28ea0f4676fdaaae7ff2413a24d0d35c8657424f84856c1103c73454c9da4" dependencies = [ "bitflags 2.13.1", "gdk4", @@ -1403,15 +1409,15 @@ dependencies = [ [[package]] name = "gtk4-layer-shell-sys" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f566a5ec5bcc454e7fcf2ab76930887ced5365afce12c1e5201bb296b95f1b9" +checksum = "bcf19bb884ef0ef55b9e6b2b369c39b4fcc0c41e3a0c1cbc8c267720338b690b" dependencies = [ "gdk4-sys", "glib-sys", "gtk4-sys", "libc", - "system-deps", + "system-deps 8.0.0", ] [[package]] @@ -1423,7 +1429,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1442,7 +1448,7 @@ dependencies = [ "gsk4-sys", "libc", "pango-sys", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -1501,9 +1507,9 @@ checksum = "ec9d92d097f4749b64e8cc33d924d9f40a2d4eb91402b458014b781f5733d60f" [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -1517,18 +1523,18 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "typenum", ] [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -1540,9 +1546,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -1553,9 +1559,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1567,16 +1573,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -1587,15 +1594,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" dependencies = [ "displaydoc", "icu_locale_core", @@ -1635,9 +1642,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.30" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b009b6744c1445efd7244084e25e498636412effb6760b55067553baa925cc7" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" dependencies = [ "crossbeam-deque", "globset", @@ -1735,9 +1742,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -1752,9 +1759,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kqueue" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" +checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" dependencies = [ "kqueue-sys", "libc", @@ -1778,9 +1785,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libloading" @@ -1794,18 +1801,18 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" dependencies = [ "libc", ] [[package]] name = "libsqlite3-sys" -version = "0.38.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c19a05435c21ac299d71b6a9c13db3e3f47c520517d58990a462a1397a61db" +checksum = "f1d20bef17f513b9b3004532233187769cd072d790971f4e4da0e346eb6401e8" dependencies = [ "cc", "pkg-config", @@ -1829,9 +1836,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "log" @@ -1861,7 +1868,7 @@ dependencies = [ "rangemap", "sha2 0.10.9", "stringprep", - "thiserror 2.0.18", + "thiserror 2.0.20", "ttf-parser", "weezl", ] @@ -1895,19 +1902,19 @@ dependencies = [ [[package]] name = "macro_rules_attribute" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" +checksum = "b3ae8f6d608c795738406608304d30a2dfbdc8e58e44f7ba43236da5208ded3c" dependencies = [ "macro_rules_attribute-proc_macro", - "paste", + "pastey", ] [[package]] name = "macro_rules_attribute-proc_macro" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" +checksum = "fc04a4c58212d57930a24bf47d3fa87485264a3a054e9c10e042eb373573ad3c" [[package]] name = "matchers" @@ -2000,7 +2007,7 @@ checksum = "e4db6d5580af57bf992f59068d4ea26fd518574ff48d7639b255a36f9de6e7e9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2099,9 +2106,9 @@ checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -2117,9 +2124,9 @@ dependencies = [ [[package]] name = "numkong" -version = "7.7.0" +version = "7.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58d4bb97df102ebdde66a352a20c0bc65c7c407ee9bef12ebe317edc2458a55" +checksum = "81601dc994296baed2968db046b01589be7898837c31f2b073704c6e65ff8f04" dependencies = [ "cc", ] @@ -2180,7 +2187,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2209,27 +2216,27 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ort" -version = "2.0.0-rc.12" +version = "2.0.0-rc.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7de3af33d24a745ffb8fab904b13478438d1cd52868e6f17735ef6e1f8bf133" +checksum = "4336a1e2b38848325241c72889086886004e589b7c74f335e60a8e8db5138a0b" dependencies = [ "libloading", "ndarray", "ort-sys", "smallvec", "tracing", - "ureq 3.3.0", + "ureq 3.4.0", ] [[package]] name = "ort-sys" -version = "2.0.0-rc.12" +version = "2.0.0-rc.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b497d21a8b6fbb4b5a544f8fadb77e801a09ae0add9e411d31c6f89e3c1e90" +checksum = "cf211e3776eea6aec988552fa118dd746d70e1b1e5e244058d1c98015f3e5872" dependencies = [ "hmac-sha256", "lzma-rust2", - "ureq 3.3.0", + "ureq 3.4.0", ] [[package]] @@ -2252,7 +2259,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -2261,6 +2268,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -2311,9 +2324,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "pom" @@ -2323,9 +2336,9 @@ checksum = "60f6ce597ecdcc9a098e7fddacb1065093a3d66446fa16c675e7e71d1b5c28e6" [[package]] name = "portable-atomic" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" @@ -2344,9 +2357,9 @@ checksum = "78451badbdaebaf17f053fd9152b3ffb33b516104eacb45e7864aaa9c712f306" [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -2377,9 +2390,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -2396,9 +2409,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -2463,9 +2476,9 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rangemap" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" +checksum = "a611d15b50743feb4c76b7d03edcb0e64f399c26961e4efe6975bc398be6aa3d" [[package]] name = "rawpointer" @@ -2529,9 +2542,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -2565,14 +2578,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ "hashbrown 0.16.1", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "rusqlite" -version = "0.40.1" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323" +checksum = "23f2a97da3e3873c73cb2a2e71b35c40ff95e0b1eefa8d72d8499a6928c3b5b3" dependencies = [ "bitflags 2.13.1", "fallible-iterator", @@ -2607,9 +2620,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.42" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "log", "once_cell", @@ -2622,18 +2635,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" dependencies = [ "ring", "rustls-pki-types", @@ -2707,9 +2720,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -2717,29 +2730,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -2913,6 +2926,17 @@ 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" @@ -2921,7 +2945,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2933,7 +2957,20 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 1.1.3+spec-1.1.0", + "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", "version-compare", ] @@ -2976,11 +3013,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -2991,18 +3028,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -3016,9 +3053,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "num-conv", @@ -3035,9 +3072,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -3086,7 +3123,7 @@ dependencies = [ "serde", "serde_json", "spm_precompiled", - "thiserror 2.0.18", + "thiserror 2.0.20", "unicode-normalization-alignments", "unicode-segmentation", "unicode_categories", @@ -3106,9 +3143,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.3+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -3165,9 +3202,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow 1.0.4", ] @@ -3203,7 +3240,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3352,11 +3389,11 @@ dependencies = [ [[package]] name = "ureq" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "der", "log", "native-tls", @@ -3370,11 +3407,11 @@ dependencies = [ [[package]] name = "ureq-proto" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "http", "httparse", "log", @@ -3472,9 +3509,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -3485,9 +3522,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3495,22 +3532,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] @@ -3769,15 +3806,15 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" [[package]] name = "xz2" @@ -3807,28 +3844,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3848,7 +3885,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -3869,14 +3906,14 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -3885,9 +3922,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8" dependencies = [ "yoke", "zerofrom", @@ -3896,13 +3933,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "47402523226a02bfe5230160dc3ccc089aa6f6f19e7fcbb4e6f824bbb1b4aa62" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -3927,7 +3964,7 @@ dependencies = [ "memchr", "pbkdf2", "sha1", - "thiserror 2.0.18", + "thiserror 2.0.20", "time", "xz2", "zeroize", diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index abc0269..56fab2b 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -10,7 +10,7 @@ path = "src/main.rs" [dependencies] breadsearch-shared = { path = "../breadsearch-shared" } -bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["gtk"] } +bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.4", features = ["gtk"] } # Bread event fabric client — emit bread.search.* (fail-silent if breadd is down). bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["bread-client"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. diff --git a/breadsearch/src/main.rs b/breadsearch/src/main.rs index 247ad80..d4d4786 100644 --- a/breadsearch/src/main.rs +++ b/breadsearch/src/main.rs @@ -247,6 +247,7 @@ fn run_ui(screenshot_req: Option) { window.set_anchor(edge, true); } window.set_exclusive_zone(0); + bread_theme::gtk::bind_window_auto(&window); let close_all: Rc = Rc::new({ let w = window.clone(); From 1ce5f72819e5a388bc237ca6e715d22531b0642e Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 16 Aug 2026 14:09:28 +0800 Subject: [PATCH 30/32] Bump version to v0.3.3 --- Cargo.lock | 83 ++++++----------------------------- breadmill/Cargo.toml | 2 +- breadsearch-shared/Cargo.toml | 2 +- breadsearch/Cargo.toml | 2 +- 4 files changed, 17 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c615ccc..60a3e79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,15 +167,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", -] - [[package]] name = "block-padding" version = "0.3.3" @@ -194,7 +185,7 @@ dependencies = [ "bread-utils", "hex", "ort", - "sha2 0.10.9", + "sha2", "tokenizers", "tracing", "ureq 2.12.1", @@ -245,7 +236,7 @@ dependencies = [ [[package]] name = "breadmill" -version = "0.3.1" +version = "0.3.3" dependencies = [ "bread-onnx", "breadsearch-shared", @@ -258,7 +249,7 @@ dependencies = [ "rusqlite", "serde", "serde_json", - "sha2 0.11.0", + "sha2", "tokenizers", "tracing-subscriber", "ureq 2.12.1", @@ -268,7 +259,7 @@ dependencies = [ [[package]] name = "breadsearch" -version = "0.3.1" +version = "0.3.3" dependencies = [ "anyhow", "bread-screenshots", @@ -283,7 +274,7 @@ dependencies = [ [[package]] name = "breadsearch-shared" -version = "0.3.1" +version = "0.3.3" dependencies = [ "serde", "serde_json", @@ -429,7 +420,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "inout", ] @@ -517,12 +508,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - [[package]] name = "constant_time_eq" version = "0.3.1" @@ -631,15 +616,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - [[package]] name = "cxx" version = "1.0.199" @@ -822,22 +798,11 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", - "crypto-common 0.1.7", + "block-buffer", + "crypto-common", "subtle", ] -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer 0.12.1", - "const-oid", - "crypto-common 0.2.2", -] - [[package]] name = "dirs" version = "5.0.1" @@ -1496,7 +1461,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1521,15 +1486,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "hybrid-array" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" -dependencies = [ - "typenum", -] - [[package]] name = "icu_collections" version = "2.3.0" @@ -1866,7 +1822,7 @@ dependencies = [ "nom 8.0.0", "rand 0.10.2", "rangemap", - "sha2 0.10.9", + "sha2", "stringprep", "thiserror 2.0.20", "ttf-parser", @@ -1942,7 +1898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest 0.10.7", + "digest", ] [[package]] @@ -2280,7 +2236,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.10.7", + "digest", "hmac", ] @@ -2787,7 +2743,7 @@ checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest 0.10.7", + "digest", ] [[package]] @@ -2798,18 +2754,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "digest 0.11.3", + "digest", ] [[package]] diff --git a/breadmill/Cargo.toml b/breadmill/Cargo.toml index f201c9a..c57023e 100644 --- a/breadmill/Cargo.toml +++ b/breadmill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadmill" -version = "0.3.1" +version = "0.3.3" edition = "2021" license = "MIT" diff --git a/breadsearch-shared/Cargo.toml b/breadsearch-shared/Cargo.toml index a25ee94..6f45d3f 100644 --- a/breadsearch-shared/Cargo.toml +++ b/breadsearch-shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadsearch-shared" -version = "0.3.1" +version = "0.3.3" edition = "2021" license = "MIT" diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 56fab2b..4b200b7 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadsearch" -version = "0.3.1" +version = "0.3.3" edition = "2021" license = "MIT" From 4bfcc694bd1d075c3171f7b421a0c909d437606a Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 23 Aug 2026 14:39:11 +0800 Subject: [PATCH 31/32] Adopt gtk_popup overlay helpers and singleton::toggle_or_kill Replace the TOCTOU pid-file toggle with bread_utils::singleton and the hand-rolled layer-shell overlay/Up-Down/click-outside with gtk_popup, matching breadclip. Screenshot runs still skip the lock. --- Cargo.lock | 2 + breadsearch/Cargo.toml | 2 +- breadsearch/src/main.rs | 157 +++++++++++----------------------------- 3 files changed, 47 insertions(+), 114 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60a3e79..77a2b16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,6 +230,8 @@ source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517 dependencies = [ "bread-shared", "dirs", + "gtk4", + "gtk4-layer-shell", "serde", "serde_json", ] diff --git a/breadsearch/Cargo.toml b/breadsearch/Cargo.toml index 4b200b7..2c11e28 100644 --- a/breadsearch/Cargo.toml +++ b/breadsearch/Cargo.toml @@ -12,7 +12,7 @@ path = "src/main.rs" breadsearch-shared = { path = "../breadsearch-shared" } bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.4", features = ["gtk"] } # Bread event fabric client — emit bread.search.* (fail-silent if breadd is down). -bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["bread-client"] } +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" } gtk4 = { version = "0.11", features = ["v4_12"] } diff --git a/breadsearch/src/main.rs b/breadsearch/src/main.rs index d4d4786..53302ec 100644 --- a/breadsearch/src/main.rs +++ b/breadsearch/src/main.rs @@ -1,22 +1,12 @@ use bread_theme::{hex_to_rgba, ink_on, load_palette, Palette}; use breadsearch_shared::{Hit, Request, Response}; -use std::{ - cell::RefCell, - env, fs, - path::PathBuf, - process::Command, - rc::Rc, - sync::mpsc, -}; +use std::{cell::RefCell, process::Command, rc::Rc, sync::mpsc}; use gtk4::{ - glib, - pango::EllipsizeMode, - prelude::*, - Application, ApplicationWindow, Box as GBox, CssProvider, EventControllerKey, Image, Label, - ListBox, Orientation, PolicyType, ScrolledWindow, SearchEntry, SelectionMode, + glib, pango::EllipsizeMode, prelude::*, Application, Box as GBox, CssProvider, + EventControllerKey, Image, Label, ListBox, Orientation, PolicyType, ScrolledWindow, + SearchEntry, SelectionMode, }; -use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell}; mod bread_events; mod listen; @@ -43,47 +33,14 @@ fn build_css(p: &Palette) -> String { .hit-snippet {{ opacity: 0.75; font-size: 11px; font-style: italic; }}\ .hit-score {{ opacity: 0.5; font-size: 11px; }}\ image {{ margin-right: 8px; }}", - bg_panel = bg_panel, - surface = p.color0, - accent = p.color4, - on_bg = ink_on(&p.background), + bg_panel = bg_panel, + surface = p.color0, + accent = p.color4, + on_bg = ink_on(&p.background), on_surface = ink_on(&p.color0), ) } -// ---- PID file toggle -------------------------------------------------------- - -fn pid_file() -> PathBuf { - env::var("XDG_RUNTIME_DIR") - .map(PathBuf::from) - .unwrap_or_else(|_| PathBuf::from("/tmp")) - .join("breadsearch.pid") -} - -fn is_breadsearch_pid(pid: u32) -> bool { - fs::read_to_string(format!("/proc/{}/comm", pid)) - .map(|s| s.trim() == "breadsearch") - .unwrap_or(false) -} - -fn toggle_or_continue() -> bool { - let pf = pid_file(); - if let Ok(content) = fs::read_to_string(&pf) { - if let Ok(pid) = content.trim().parse::() { - if is_breadsearch_pid(pid) { - let _ = Command::new("kill").arg(pid.to_string()).status(); - return false; - } - } - } - let _ = fs::write(&pf, std::process::id().to_string()); - true -} - -fn cleanup_pid() { - let _ = fs::remove_file(pid_file()); -} - // ---- Row builder ------------------------------------------------------------ fn make_hit_row(hit: &Hit) -> gtk4::ListBoxRow { @@ -238,21 +195,13 @@ fn run_ui(screenshot_req: Option) { bread_theme::gtk::apply_user_css(&user_css_path, &user_cell); } - let window = ApplicationWindow::builder().application(app).build(); - window.init_layer_shell(); - window.set_namespace(Some("breadsearch")); - window.set_layer(Layer::Overlay); - window.set_keyboard_mode(KeyboardMode::Exclusive); - for edge in [Edge::Top, Edge::Bottom, Edge::Left, Edge::Right] { - window.set_anchor(edge, true); - } - window.set_exclusive_zone(0); + // Full-screen transparent overlay; panel widget is positioned inside it. + let window = bread_utils::gtk_popup::new_overlay_window(app, "breadsearch"); bread_theme::gtk::bind_window_auto(&window); let close_all: Rc = Rc::new({ let w = window.clone(); move || { - cleanup_pid(); w.close(); } }); @@ -308,7 +257,10 @@ fn run_ui(screenshot_req: Option) { let (tx, rx) = mpsc::sync_channel::>(1); std::thread::spawn(move || { - let req = Request::Query { query: q, limit: 10 }; + let req = Request::Query { + query: q, + limit: 10, + }; let _ = tx.send(breadsearch_shared::send_request(&req)); }); @@ -368,36 +320,11 @@ fn run_ui(screenshot_req: Option) { glib::Propagation::Stop } Key::Down => { - let cur = list_k.selected_row().map(|r| r.index()).unwrap_or(-1); - let mut i = cur + 1; - loop { - match list_k.row_at_index(i) { - Some(r) if r.is_selectable() => { - list_k.select_row(Some(&r)); - break; - } - Some(_) => i += 1, - None => break, - } - } + bread_utils::gtk_popup::select_next_visible(&list_k); glib::Propagation::Stop } Key::Up => { - let cur = list_k.selected_row().map(|r| r.index()).unwrap_or(0); - let mut i = cur - 1; - loop { - if i < 0 { - break; - } - match list_k.row_at_index(i) { - Some(r) if r.is_selectable() => { - list_k.select_row(Some(&r)); - break; - } - Some(_) => i -= 1, - None => break, - } - } + bread_utils::gtk_popup::select_prev_visible(&list_k); glib::Propagation::Stop } _ => glib::Propagation::Proceed, @@ -415,24 +342,10 @@ fn run_ui(screenshot_req: Option) { }); // Click outside launcher panel → close - let close_outside = Rc::clone(&close_all); - let vbox_ref = vbox.clone(); - let win_ref = window.clone(); - let outside_click = gtk4::GestureClick::new(); - outside_click.connect_pressed(move |_, _, x, y| { - if let Some(b) = vbox_ref.compute_bounds(&win_ref) { - if x < b.x() as f64 - || x > (b.x() + b.width()) as f64 - || y < b.y() as f64 - || y > (b.y() + b.height()) as f64 - { - close_outside(); - } - } - }); - window.add_controller(outside_click); - - window.connect_destroy(|_| cleanup_pid()); + { + 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); @@ -465,11 +378,29 @@ fn main() { 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; - } + // `toggle_or_kill` kills whatever's holding the single-instance lock — + // a real, already-running breadsearch 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 search panel. + // + // 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("breadsearch") { + Ok(bread_utils::singleton::Toggle::Started(guard)) => Some(guard), + Ok(bread_utils::singleton::Toggle::KilledExisting) => return, + Err(e) => { + eprintln!( + "breadsearch: single-instance lock unavailable ({e}); continuing without it" + ); + None + } + } + }; + run_ui(screenshot_req); } From 993dc4a52546d28c5b1b1ee73298eb9a198f677c Mon Sep 17 00:00:00 2001 From: Breadway Date: Thu, 6 Aug 2026 08:52:18 +0800 Subject: [PATCH 32/32] Fix cargo clippy warnings across the workspace - Derive Default for Config instead of manually mirroring each sub-config's Default impl - Use std::io::Error::other instead of Error::new(ErrorKind::Other, ..) - Drop the unused mut on the store lock in full_reindex - Remove Store::dim, a field that was set once and never read - Use char_indices().enumerate() instead of a hand-rolled loop counter in split_by_chars, and is_multiple_of() for the chunk boundary check - Use strip_prefix instead of manual slicing in expand_home (cherry picked from commit b740af38e17d04bd83db93bbb0585ed5744a436e) --- breadmill/src/chunk.rs | 6 ++---- breadmill/src/indexer.rs | 6 +++--- breadmill/src/store.rs | 3 +-- breadsearch-shared/src/lib.rs | 15 ++------------- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/breadmill/src/chunk.rs b/breadmill/src/chunk.rs index dcddca8..7468f89 100644 --- a/breadmill/src/chunk.rs +++ b/breadmill/src/chunk.rs @@ -81,10 +81,9 @@ fn split_by_chars(chunk: Chunk, max_chars: usize) -> Vec { let text = &chunk.text; let mut result = Vec::new(); let mut seg_start = 0usize; - let mut count = 0usize; - for (byte_idx, _) in text.char_indices() { - if count > 0 && count % max_chars == 0 { + for (count, (byte_idx, _)) in text.char_indices().enumerate() { + if count > 0 && count.is_multiple_of(max_chars) { result.push(Chunk { text: text[seg_start..byte_idx].to_string(), start: chunk.start + seg_start, @@ -92,7 +91,6 @@ fn split_by_chars(chunk: Chunk, max_chars: usize) -> Vec { }); seg_start = byte_idx; } - count += 1; } if seg_start < text.len() { result.push(Chunk { diff --git a/breadmill/src/indexer.rs b/breadmill/src/indexer.rs index 539b58c..67c7694 100644 --- a/breadmill/src/indexer.rs +++ b/breadmill/src/indexer.rs @@ -64,7 +64,7 @@ impl Indexer { pub fn full_reindex(&self) { eprintln!("breadmill: full reindex triggered"); { - let mut store = self.state.store.lock_recover(); + let store = self.state.store.lock_recover(); // Clear all state let _ = store.conn.execute_batch("DELETE FROM chunks; DELETE FROM files;"); let _ = store.index.reserve(4096); @@ -416,9 +416,9 @@ fn sha256_str(bytes: &[u8]) -> String { } pub fn expand_home(path: &str) -> PathBuf { - if path.starts_with("~/") { + if let Some(rest) = path.strip_prefix("~/") { let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".into()); - PathBuf::from(home).join(&path[2..]) + PathBuf::from(home).join(rest) } else { PathBuf::from(path) } diff --git a/breadmill/src/store.rs b/breadmill/src/store.rs index ed1392a..106e4c5 100644 --- a/breadmill/src/store.rs +++ b/breadmill/src/store.rs @@ -6,7 +6,6 @@ use usearch::{Index, IndexOptions, MetricKind, ScalarKind, new_index}; pub struct Store { pub conn: Connection, pub index: Index, - pub dim: usize, } // usearch::Index wraps a raw C++ pointer; access is serialized by the Mutex. @@ -87,7 +86,7 @@ impl Store { index.reserve(4096).map_err(|e| e.to_string())?; } - Ok(Self { conn, index, dim }) + Ok(Self { conn, index }) } // ---- file state --------------------------------------------------------- diff --git a/breadsearch-shared/src/lib.rs b/breadsearch-shared/src/lib.rs index fdea6f5..5ed69c4 100644 --- a/breadsearch-shared/src/lib.rs +++ b/breadsearch-shared/src/lib.rs @@ -43,7 +43,7 @@ pub fn socket_path() -> PathBuf { // ---- Config ----------------------------------------------------------------- -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Config { #[serde(default)] pub index: IndexConfig, @@ -157,16 +157,6 @@ impl Default for ModelConfig { } } -impl Default for Config { - fn default() -> Self { - Self { - index: IndexConfig::default(), - search: SearchConfig::default(), - model: ModelConfig::default(), - power: PowerConfig::default(), - } - } -} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PowerConfig { @@ -255,8 +245,7 @@ pub struct StatusInfo { pub fn send_request(req: &Request) -> std::io::Result { let mut stream = UnixStream::connect(socket_path())?; - let mut line = serde_json::to_string(req) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + let mut line = serde_json::to_string(req).map_err(std::io::Error::other)?; line.push('\n'); stream.write_all(line.as_bytes())?; stream.flush()?;