From c9445fadccf4bc523a6fd3fed6da104f3588fc2d Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:16:30 +0800 Subject: [PATCH] Emit bread.help.opened when the main window is shown Fail-silent via bread-utils BreadClient (ecosystem v0.7.1, bread-client). No command verbs. Silent autostart and screenshot mode do not emit. --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 2 +- EVENTS.md | 42 ++++++++++++++++++++++++++++++++++++++++++ README.md | 5 +++++ src/services/breadd.rs | 23 ++++++++++++++++++++--- src/ui/window.rs | 12 ++++++++++-- 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 EVENTS.md diff --git a/Cargo.lock b/Cargo.lock index e376161..454a88a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,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" @@ -58,6 +69,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", diff --git a/Cargo.toml b/Cargo.toml index 4e7ddf7..d94305b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ toml = "0.8" # Non-destructive state editing (mirrors bos-settings/src/config/mod.rs). toml_edit = "0.22" async-channel = "2" -bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["toml"] } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["toml", "bread-client"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. # The crate is not in tag v0.7.1 (landed after that tag; no later # ecosystem tag includes it). Rev-pinned so this is not branch=main. diff --git a/EVENTS.md b/EVENTS.md new file mode 100644 index 0000000..6bdd1c8 --- /dev/null +++ b/EVENTS.md @@ -0,0 +1,42 @@ +# breadhelp — bread event integration + +breadhelp is a standalone GTK help center: it works exactly the same with +or without `breadd` running. When breadd *is* present, it publishes one +event after the main window is actually shown. 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: **`help`**. Transport: `bread-utils`'s `bread_client` module +(feature `bread-client`) — breadhelp links it directly. Each `emit` is +its own short-lived connection. There is no long-running command +subscription. + +## Events published (`bread.help.*`) + +| Event | Data | When | +|-------|------|------| +| `bread.help.opened` | `{ "autostart": bool }` | The main help window is presented (`ApplicationWindow::present`). `autostart` is `true` when that invocation was launched with `--autostart`. | + +Not emitted when: + +- every-login `--autostart` builds a hidden window because onboarding is + already done (silent autostart) +- first-run `--autostart` starts the tour overlay without presenting the + main window +- `--onboard` / `--tour-event` (tour only) +- `--screenshot` (capture, not a user-visible open) + +## Commands honored (`bread.command.help.*`) + +None. Opening the help center, starting the tour, and applying one-click +fixes already exist as local CLI / UI paths. There is no command +subscription, and no verb is stubbed as a no-op. If breadhelp later grows +a bus verb that maps to real behavior, add it then. + +## 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) — the help + center, tour, and screenshots are entirely unaffected. +- There is no command subscription to reconnect. diff --git a/README.md b/README.md index 2e54855..dd4a036 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,8 @@ cargo build --release ## Packaging / releasing See `CONTRIBUTING.md` for the single-trunk + tag model. `main` publishes a bakery **dev** build; a `vX.Y.Z-rc.N` tag publishes **beta**; a `vX.Y.Z` tag publishes **stable**. The leftover `packaging/` tree is not a pacman source — bakery is canonical (see `packaging/README.md`). + +## bread event integration + +Works the same with or without `breadd`. When the main window is shown, +breadhelp publishes `bread.help.opened`. See [EVENTS.md](EVENTS.md). diff --git a/src/services/breadd.rs b/src/services/breadd.rs index a7c06d0..d6f51df 100644 --- a/src/services/breadd.rs +++ b/src/services/breadd.rs @@ -1,6 +1,23 @@ -//! Maps a `--suggest ` payload (sent by a breadd Lua module, e.g. -//! `breadhelp-suggest.lua` reacting to `bread.monitor.connected`) to Home -//! tab banner text. +//! Bread bus integration. `--suggest` banners stay local; `bread.help.*` +//! emits are optional and fail-silent. See `EVENTS.md`. +//! +//! `BreadClient::emit` never blocks or errors this process — a missing or +//! restarting breadd must not affect the help center itself. + +use bread_utils::bread_client::BreadClient; + +/// Sibling-app id in `bread_shared::apps::KNOWN_APPS`. Events publish as +/// `bread.help.*`. There is no command subscription. +pub const APP_ID: &str = "help"; + +/// Fire-and-forget `bread.help.opened` after the main window is actually +/// shown. Silent autostart (window built, not presented) does not call this. +pub fn emit_opened(autostart: bool) { + BreadClient::connect(APP_ID).emit( + "bread.help.opened", + serde_json::json!({ "autostart": autostart }), + ); +} pub struct Suggestion { pub text: String, diff --git a/src/ui/window.rs b/src/ui/window.rs index dc6fb12..10a7839 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -63,7 +63,7 @@ pub fn present(app: &Application, action: Action) { // (i.e. breadhelp wasn't already running) — a background daemon // event shouldn't steal focus from whatever the user is doing. if just_built { - handle.window.present(); + show_window(&handle.window, action.autostart); } return; } @@ -77,11 +77,19 @@ pub fn present(app: &Application, action: Action) { } let silent_autostart = action.autostart && State::load().onboarding_completed(); if !silent_autostart { - handle.window.present(); + show_window(&handle.window, action.autostart); } }); } +/// Present the help window and publish `bread.help.opened`. Screenshot +/// mode calls `window.present()` itself and must not go through here — +/// that is capture, not a user-visible open. +fn show_window(window: &ApplicationWindow, autostart: bool) { + window.present(); + crate::services::breadd::emit_opened(autostart); +} + fn build(app: &Application) -> Handle { // First thing on every cold start: revert any keybind a previous, // crashed run may have left temporarily rebound mid tour-step (see