Compare commits

...

2 commits

Author SHA1 Message Date
Breadway
c97029f654 Document bread init: api-schema, README CLI reference, hooks_shell note
Some checks failed
dev release / build (push) Failing after 1m47s
Registers 'init' as a cli_command in api-schema.toml and adds its
lines to README.md's CLI reference, keeping xtask check-docs clean.
Also adds a note to hooks_shell.rs's module header explaining why the
compositor case earns a consent-gated config edit that the shell-hook
module deliberately avoids, so the two don't read as contradicting
each other.
2026-08-26 19:31:58 +08:00
Breadway
1fce1979c3 Add bread init: Hyprland compositor layer-rule integration
Installs the layerrule blur/ignore_alpha/animation integration that
breadbar's translucency depends on into a user's hyprland.lua, with a
consent-gated single marked block, timestamped backup, dry-run/--yes,
and --undo. Generates a self-contained ~/.config/hypr/bread.lua that
reads layerrules.json (bread-theme layerrules) and emits hl.layer_rule
calls, appearance fields only. Falls back to printing a snippet
(translated to hyprlang .conf syntax when relevant) rather than
writing when the config isn't a Lua hyprland.lua bread recognizes, and
refuses to install when an existing breadbar layer rule (e.g. BOS's
own dotfiles) is already found under ~/.config/hypr.

bread doctor now reports compositor integration status via a pure
filesystem check, independent of the daemon; it never installs
anything itself.

Wired as a top-level Commands::Init (not a HooksCommand variant) since
it needs diff/consent/backup/undo machinery the hooks family has no
use for.
2026-08-26 19:31:18 +08:00
5 changed files with 1341 additions and 0 deletions

View file

@ -237,6 +237,12 @@ bread modules audit <name> # Scan a module's Lua source and suggest a
# Hooks # Hooks
bread hooks install-shell [shell] # Install precmd/preexec/chpwd shell hooks (auto-detects $SHELL) bread hooks install-shell [shell] # Install precmd/preexec/chpwd shell hooks (auto-detects $SHELL)
bread hooks install-git # Install git hooks (post-commit/checkout/merge) in the current repo bread hooks install-git # Install git hooks (post-commit/checkout/merge) in the current repo
# Compositor integration
bread init # Install breadbar's Hyprland layer-rule integration (shows a diff, asks to confirm)
bread init --dry-run # Print the proposed diff only, change nothing
bread init --yes # Skip the confirmation prompt (scripted/image builds)
bread init --undo # Remove the previously installed integration
``` ```
--- ---

View file

@ -608,3 +608,8 @@ since = "0.7"
name = "doctor" name = "doctor"
kind = "cli_command" kind = "cli_command"
since = "0.7" since = "0.7"
[[entry]]
name = "init"
kind = "cli_command"
since = "0.8"

View file

@ -13,6 +13,20 @@
//! user later doesn't want it, an rc-file edit is much harder to notice and //! user later doesn't want it, an rc-file edit is much harder to notice and
//! undo than a printed snippet they chose to paste in. //! undo than a printed snippet they chose to paste in.
//! //!
//! `bread init` (`init.rs`) looks like it breaks this rule — it does
//! propose an edit to a user's own `hyprland.lua` — but it's a narrower,
//! consent-gated exception, not a reversal of it. The difference is what's
//! silent about the failure mode: a missing shell hook is invisible
//! enrichment (no telemetry, nothing looks wrong), so leaving it to the
//! user to paste in at their own pace is the right default. A missing
//! compositor layer rule is not invisible — breadbar renders unblurred and
//! with the wrong opacity, which reads as "this app is broken," not
//! "optional feature not enabled," and there's no snippet a user could be
//! expected to reverse-engineer for that. `init.rs` earns the edit by
//! showing the exact diff, requiring explicit consent (prompt, `--yes`, or
//! nothing at all without a TTY), backing up the file first, and shipping
//! a real `--undo`. See `init.rs`'s module docs for the full rationale.
//!
//! # Why bread-emit, not `bread emit` //! # Why bread-emit, not `bread emit`
//! //!
//! The generated hooks shell out to the separate `bread-emit` binary //! The generated hooks shell out to the separate `bread-emit` binary

1281
bread-cli/src/init.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
mod init;
mod hooks_git; mod hooks_git;
mod hooks_shell; mod hooks_shell;
mod modules_mgmt; mod modules_mgmt;
@ -99,6 +100,22 @@ enum Commands {
#[arg(long)] #[arg(long)]
json: bool, json: bool,
}, },
/// Install bread's Hyprland compositor layer-rule integration
/// (breadbar/breadbox blur, ignore_alpha, animation) into
/// ~/.config/hypr/hyprland.lua, with consent, a backup, and full undo.
/// See bread-cli/src/init.rs for the design rationale.
Init {
/// Print the proposed diff and change nothing
#[arg(long)]
dry_run: bool,
/// Skip the confirmation prompt (for scripted/image builds)
#[arg(long)]
yes: bool,
/// Remove the previously installed marked block instead of
/// installing it
#[arg(long)]
undo: bool,
},
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
@ -224,6 +241,9 @@ async fn main() -> Result<()> {
print_doctor(&socket).await?; print_doctor(&socket).await?;
} }
} }
Commands::Init { dry_run, yes, undo } => {
init::run(dry_run, yes, undo)?;
}
} }
Ok(()) Ok(())
@ -691,6 +711,7 @@ async fn print_doctor(socket: &Path) -> Result<()> {
println!(); println!();
println!(" start the daemon: systemctl --user start breadd"); println!(" start the daemon: systemctl --user start breadd");
println!(" view logs: journalctl --user -u breadd -f"); println!(" view logs: journalctl --user -u breadd -f");
print_compositor_doctor_section();
return Ok(()); return Ok(());
} }
@ -775,6 +796,20 @@ fn render_doctor(health: &Value) {
} }
} }
} }
print_compositor_doctor_section();
}
/// Compositor integration status — pure filesystem check, independent of
/// the daemon, so it prints the same whether breadd is up or not. Reports
/// only; `bread doctor` never installs anything (that's `bread init`'s
/// job).
fn print_compositor_doctor_section() {
println!();
println!("compositor");
for line in init::doctor_report(&init::hypr_dir()) {
println!("{line}");
}
} }
fn config_directory() -> PathBuf { fn config_directory() -> PathBuf {