breadsearch: emit bread.search.opened and bread.search.opened_result
All checks were successful
dev release / build (push) Successful in 2m22s

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.
This commit is contained in:
Breadway 2026-08-15 22:14:47 +08:00
parent 95a97d63d3
commit 0e3c8a1668
5 changed files with 81 additions and 0 deletions

13
Cargo.lock generated
View file

@ -204,6 +204,17 @@ dependencies = [
"tracing", "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]] [[package]]
name = "bread-theme" name = "bread-theme"
version = "0.3.1" version = "0.3.1"
@ -220,6 +231,7 @@ name = "bread-utils"
version = "0.3.1" version = "0.3.1"
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886"
dependencies = [ dependencies = [
"bread-shared",
"dirs", "dirs",
"serde", "serde",
"serde_json", "serde_json",
@ -265,6 +277,7 @@ dependencies = [
"anyhow", "anyhow",
"bread-screenshots", "bread-screenshots",
"bread-theme", "bread-theme",
"bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)",
"breadsearch-shared", "breadsearch-shared",
"clap", "clap",
"gtk4", "gtk4",

36
EVENTS.md Normal file
View file

@ -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": "<hit 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.

View file

@ -11,6 +11,8 @@ path = "src/main.rs"
[dependencies] [dependencies]
breadsearch-shared = { path = "../breadsearch-shared" } 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.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. # Capture primitives for `--screenshot` mode — see src/screenshot.rs.
# Not on tag v0.7.1 (crate landed after that tag). Rev, not branch=main. # 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. # v0.7.1 predates this crate; same rev siblings use.

View file

@ -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 }),
);
}

View file

@ -18,6 +18,7 @@ use gtk4::{
}; };
use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell}; use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
mod bread_events;
mod screenshot; mod screenshot;
// ---- Theming ---------------------------------------------------------------- // ---- Theming ----------------------------------------------------------------
@ -193,6 +194,7 @@ fn open_file(path: &str) {
.stdout(std::process::Stdio::null()) .stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null()) .stderr(std::process::Stdio::null())
.spawn(); .spawn();
bread_events::emit_opened_result(path);
} }
fn open_folder(path: &str) { fn open_folder(path: &str) {
@ -206,6 +208,7 @@ fn open_folder(path: &str) {
.stdout(std::process::Stdio::null()) .stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null()) .stderr(std::process::Stdio::null())
.spawn(); .spawn();
bread_events::emit_opened_result(path);
} }
// ---- UI --------------------------------------------------------------------- // ---- UI ---------------------------------------------------------------------
@ -433,6 +436,7 @@ fn run_ui(screenshot_req: Option<screenshot::ScreenshotRequest>) {
screenshot::dispatch(&window, req); screenshot::dispatch(&window, req);
} }
window.connect_map(|_| bread_events::emit_opened());
window.present(); window.present();
search.grab_focus(); search.grab_focus();
}); });