breadbox's launcher is now a thin host over the `bread-launcher` crate (bread-ecosystem v0.7.5) — the same core breadbar's embedded capsule uses — instead of a private implementation: - `breadbox-shared` becomes a re-export shim over `bread_launcher` (`DesktopEntry`, `LaunchHistory`, `config_dir`, `LAUNCHER_APP`); the local fuzzy-match / ranking / desktop-file parsing is deleted. - `run_ui` builds `bread_launcher::gtk::ResultsList` for the results list and calls `bread_launcher::do_launch` / `load_sorted_entries`; the hand-rolled row/icon/score code is gone. - Launcher geometry and style come from the active shell-theme manifest (`bread_theme::shell`, via the new `src/theme.rs` accessor mirroring breadbar's) — width, radius, panel alpha, top margin, icon size, footer wording. The Daylight / liquid-motion look is the manifest's job now, not hardcoded CSS. - Keeps `main`'s recent improvements on top of the branch work: the overlay window + outside-click come from `bread_utils::gtk_popup` (`new_overlay_window` / `bind_window_auto` / `close_on_outside_click`), replacing the manual layer-shell + GestureClick boilerplate. - Embedded-open redirect: when the active theme embeds the launcher (spotlight capsule), breadbox emits `bread.box.open_requested` on the bus instead of mapping its own window; falls back to its own overlay when breadd is unreachable. - Pins: bread-theme / bread-utils / bread-screenshots / bread-launcher → v0.7.5 (bread-launcher and `bread_theme::shell` first ship there). `.cargo/` gitignored — the local sibling-checkout source override (`fa85fc1`) is no longer needed now that v0.7.5 is tagged. Reconciles the long-lived `feature/launcher-core` (13 commits, incl. `feature/launcher-theming`) with `main`'s independent launcher redesign (`feature/liquid-motion` + `bread_utils::gtk_popup` adoption). Applied as one squashed reconciliation rather than a 13-commit rebase because the two lines rewrote the same ~200-line `run_ui`/`build_css` region in incompatible directions. cargo build --locked / test --locked / clippy --all-targets --locked -D warnings all clean.
7.4 KiB
breadbox — bread event integration
breadbox is a standalone app launcher: it works exactly the same with or
without breadd running. When breadd is present, the GTK launcher
publishes a single event into the shared bread automation fabric after a
successful launch. See the parent bread repo's Documentation.md —
specifically its "Namespaces" and "Integrating a bread* app" sections —
for the general convention this follows.
App id: box. Transport: bread-utils's bread_client module
(feature bread-client) — breadbox links it directly. One-shot
launcher invocations each emit on their own fire-and-forget
connection. Command verbs are only received while breadbox listen is
running — that process holds the bread.command.box.** subscription
open.
Under an [launcher] mode = "embedded" theme (spotlight), breadbar's
own bar-drawer capsule is the launcher UI, and breadbox redirects
instead of mapping its own overlay window — see the "Embedded launcher
theme" section below. Everything else on this page describes the
default (overlay-mode) behavior.
Events published (bread.box.*)
| Event | Data | When |
|---|---|---|
bread.box.launched |
{ "id": "<desktop id or exec>", "name": "<display name>" } |
The user launched an app (Enter / keypad Enter on the selected row, or activating a row) and the spawn succeeded. Not emitted if Command::spawn fails (missing terminal, exec that cannot start). id is the desktop-file id (the .desktop filename, e.g. firefox.desktop), falling back to the stripped Exec= line when that id is empty. name is the desktop-entry display name. |
bread.box.open.done |
{} |
bread.command.box.open was received (via breadbox listen) and this binary was spawned — non-embedded themes only. Not proof the overlay mapped, just that the process was started (same toggle as a keybind). Never emitted under an embedded theme — see below. |
bread.box.open.failed |
{ "error": "<message>" } |
bread.command.box.open was received but this binary could not be started — non-embedded themes only. Unreachable under an embedded theme, since that branch returns before ever attempting to spawn. |
bread.box.open_requested |
{} |
Emitted by the plain breadbox binary itself (not breadbox listen) instead of mapping its own overlay window, only when the active theme's [launcher].mode is embedded. breadbar's capsule (launcher_command.rs) subscribes to this and opens itself. Own-namespace event, not a command — see dispatch_embedded_open's doc comment for why bread.command.box.open would be the wrong shape here. |
bread.box.open.redirected |
{} |
Emitted by breadbox listen's handle_open, only when bread.command.box.open is received while an embedded theme is active. Replaces .done/.failed in that case: this process never spawns anything (breadbar's capsule is the intended handler) and has no way to confirm breadbar actually received or handled the event — pub/sub here is one-way with no ack. This event means "redirect happened, outcome unknown", not "succeeded". |
Launch history is local to breadbox (~/.cache/breadbox/history.json);
the event bus is a notification that a launch happened, not a channel
for the exec line's arguments or the resulting process.
Commands honored (bread.command.box.*)
These are only received while breadbox listen is running. Publishing a
command with no subscriber is a silent no-op — that is the documented
bread convention, not a breadbox bug.
| Verb | Data | Effect |
|---|---|---|
open |
none | Under a non-embedded theme: same as running breadbox (toggle the launcher overlay via the existing singleton). Emits bread.box.open.done / .failed. Under an embedded theme: never spawns; emits bread.box.open.redirected instead — see "Embedded launcher theme" below. |
-- Non-embedded themes: `.done` is a real completion signal.
bread.spawn(function()
bread.emit("bread.command.box.open")
bread.wait("bread.box.open.done", { timeout = 5000 })
end)
Under an embedded theme this .done wait will time out — .done is
never emitted there. A workflow that needs to work under every theme
should wait on bread.box.open.redirected too (or treat a timeout as
"probably fine, embedded themes have no ack" rather than a failure).
Not implemented: extra verbs
There is no launch / close / query command verb. Picking a desktop
id from the bus would be a new product surface. If/when that exists, add
the corresponding bread.command.box.* verb at the same time, not
stubbed as a no-op ahead of it.
Embedded launcher theme (spotlight)
When the active shell theme's [launcher].mode is embedded, running
breadbox directly (e.g. from a keybind) does not map this binary's
overlay window — that would stack a second launcher on top of
breadbar's own capsule. Instead:
breadbox'smainchecksBreadClient::health()— a real, bounded-timeout round trip to breadd, not just "did the socket exist".- If breadd is reachable, it emits
bread.box.open_requestedand returns. breadbar's capsule (subscribed only while its own active theme is alsoembedded) is expected to open itself in response. There is no ack for this — if breadbar isn't running, or is running under a different theme and therefore never subscribed, this is still a silent no-op on the bus with nothing further to fall back to. (BreadClienthas no "is anyone subscribed" query.) - If breadd is not reachable,
breadboxlogs that to stderr and falls back to mapping its own overlay window anyway — a keybind press always opens something, rather than the historical fully silent no-op when the bus was down.
breadbox listen's handle_open (the bread.command.box.open
handler) applies the same "never map a second launcher" rule but from
the command side: under an embedded theme it never spawns, logs to
stderr, and emits bread.box.open.redirected instead of .done — see
the events table above for why .done would be a false claim here.
Fail-safe behavior
- If breadd isn't installed or isn't running,
emitis a silent no-op (BreadClient::emitnever blocks or errors the caller) and the command subscription simply never receives anything — launching, history, theming, and the singleton toggle are entirely unaffected. Under an embedded theme specifically, a directbreadboxinvocation additionally checks reachability up front and falls back to mapping its own overlay window when breadd is down (see above) — so this invariant now holds there too, not just for every other theme. - If breadd restarts, the command subscription reconnects automatically
(
BreadClient::subscribe's background thread has its own backoff loop); no restart ofbreadbox listenis needed. - If
breadbox listenis not running, commands are a graceful no-op at the bus (no subscriber). The CLI still works, and one-shot invocations still emitbread.box.launchedon their own short-lived connection. - Closing the launcher without launching anything emits nothing.
- What is not covered: under an embedded theme, if breadd is reachable but breadbar itself isn't running (or isn't subscribed), neither the direct-keybind path nor the command path can detect that or fall back further — both are fire-and-forget with no ack. This is a known limitation of the current transport, not an oversight.