Emit bread.help.opened when the main window is shown
All checks were successful
dev release / build (push) Successful in 2m4s

Fail-silent via bread-utils BreadClient (ecosystem v0.7.1, bread-client).
No command verbs. Silent autostart and screenshot mode do not emit.
This commit is contained in:
Breadway 2026-08-15 22:16:30 +08:00
parent 3634edef0d
commit c9445fadcc
6 changed files with 90 additions and 6 deletions

12
Cargo.lock generated
View file

@ -42,6 +42,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"
@ -58,6 +69,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",

View file

@ -17,7 +17,7 @@ toml = "0.8"
# Non-destructive state editing (mirrors bos-settings/src/config/mod.rs). # Non-destructive state editing (mirrors bos-settings/src/config/mod.rs).
toml_edit = "0.22" toml_edit = "0.22"
async-channel = "2" 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. # Capture primitives for `--screenshot` mode — see src/screenshot.rs.
# The crate is not in tag v0.7.1 (landed after that tag; no later # 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. # ecosystem tag includes it). Rev-pinned so this is not branch=main.

42
EVENTS.md Normal file
View file

@ -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.

View file

@ -13,3 +13,8 @@ cargo build --release
## Packaging / releasing ## 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`). 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).

View file

@ -1,6 +1,23 @@
//! Maps a `--suggest <id>` payload (sent by a breadd Lua module, e.g. //! Bread bus integration. `--suggest` banners stay local; `bread.help.*`
//! `breadhelp-suggest.lua` reacting to `bread.monitor.connected`) to Home //! emits are optional and fail-silent. See `EVENTS.md`.
//! tab banner text. //!
//! `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 struct Suggestion {
pub text: String, pub text: String,

View file

@ -63,7 +63,7 @@ pub fn present(app: &Application, action: Action) {
// (i.e. breadhelp wasn't already running) — a background daemon // (i.e. breadhelp wasn't already running) — a background daemon
// event shouldn't steal focus from whatever the user is doing. // event shouldn't steal focus from whatever the user is doing.
if just_built { if just_built {
handle.window.present(); show_window(&handle.window, action.autostart);
} }
return; return;
} }
@ -77,11 +77,19 @@ pub fn present(app: &Application, action: Action) {
} }
let silent_autostart = action.autostart && State::load().onboarding_completed(); let silent_autostart = action.autostart && State::load().onboarding_completed();
if !silent_autostart { 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 { fn build(app: &Application) -> Handle {
// First thing on every cold start: revert any keybind a previous, // First thing on every cold start: revert any keybind a previous,
// crashed run may have left temporarily rebound mid tour-step (see // crashed run may have left temporarily rebound mid tour-step (see