Phase 3b of the shell theme system. A slot list entry can now be
`widget:<key>`, where `<key>` is either a WidgetPlacement alias
(right_of_workspaces, left_of_clock, right_of_clock, left_of_stats, tray)
or a Lua module name. ModuleRegistry::for_each_in_slot creates each
widget container on demand at its slot position; reconcile_widgets routes
each WidgetSpec by module name first, falling back to its placement
alias, and logs+drops (never panics) a spec with no matching container.
bread_shared::widget::WidgetPlacement stays untouched — it's still the
wire type breadd sends, unmodified and unshadowed; only the container
map that placement now resolves through is theme-driven.
Adds ModuleRegistry (src/bar/slots.rs), mapping each [bar.slots] module
name (workspaces/media/clock/volume/wifi/battery/control) to its
already-built widget. main.rs now registers the seven modules once
they're constructed, then walks ShellTheme::slots() to append them
into the left/centre/right containers in theme order instead of a
hardcoded sequence. An unknown module name in a theme manifest is
logged and skipped rather than panicking.
The Lua-declared widget_* containers keep their fixed interleave
(right-of-workspaces, left/right-of-clock, left-of-stats) — that's
Phase 3b, not this change.
breadbar no longer hardcodes BAR_HEIGHT/BAR_MARGIN_TOP/BAR_MARGIN_SIDES/
CHIP_HEIGHT/ICON_PX or the root window's layer-shell setup: it loads
bread_theme:🐚:ShellTheme once (theme::shell_theme(), cached in a
thread-local) and reads bar.window (anchors, margin, exclusive zone,
keyboard mode - now set explicitly instead of relying on the library
default) plus tokens.{icon_px,chip_height} from it everywhere those used
to be literals.
The four satellite surfaces (breadbar-osd, breadbar-notif, breadbar-panel,
breadbar-dismiss) now get their anchor/margin/width/layer from the
manifest's [surfaces.*] table via a small new surface::apply() helper,
narrowly scoped to the three anchor shapes those surfaces actually use.
The pre-existing 8px gap between the panel's top margin and the dismiss
scrim's is preserved exactly (and now commented) rather than "fixed".
theme.rs's load_css() keeps its ~250 lines of hand-written breadbar CSS
(notifications, wifi popover, control panel, media widget) but now reads
its five radius/pad locals and the two easing curves (spring vs
spring_settle - hover/settle transitions were previously miscategorized
as the overshoot curve in the constant audit; the actual code already
used the settle curve there, confirmed against src) from theme tokens
instead of hardcoding them.
Also wires bread_theme:🐚:watch() so editing the active theme's
theme.toml/extra.css hot-reloads CSS tokens without a restart, same as
a pywal palette change already does. Window-spec values still need a
restart per the plan (read once at window-construction time).
Verified pixel-identical: captured all 10 breadbar --screenshot views
(bar, control-panel, connectivity-wifi/bluetooth, media-popover,
notification/-critical, osd-volume/-brightness, wifi-add-dialog) via
bread-capture's isolated headless-Sway harness against both the
pre-change and post-change binaries; every view diffs byte-identical
at the decoded-pixel level.
bread-theme was already pinned to v0.7.4 while bread-utils and
bread-screenshots trailed at v0.7.2, even though bread-ecosystem locks
all workspace packages together as of a9754d9. Bump the two lagging
pins so all three crates from that repo resolve to the same tag/commit.
(cherry picked from commit f0de82aafb547134e0edc9dac8d72c01586db57c)
The control panel lists PipeWire sinks and sets the default, moving
playing streams so the change is immediate. The workspace pill starts
on switch without a layout wait, measures against the Fixed host so it
stays centered on the digit, and ignores row-wide bounds that used to
stretch it across several chips.
One layer-shell window per Hyprland output now loads that output's
bread-theme palette. Notifications, history, and OSD follow the
monitor they appear on. Pin bread-theme to v0.7.4.
Parse FDO action pairs onto popup buttons, emit ActionInvoked on
click (default action on the body), and show an inline-reply field
when senders request it. Closing still emits NotificationClosed.
History persist is unchanged.
Keep a bounded in-memory history (last 50) and a layer-shell window
listing app, summary, truncated body, and time. Toggle with
`breadbar --history` or D-Bus `dev.breadway.Bar.ToggleHistory`.
Pin bread-theme, bread-utils, and bread-screenshots to tag v0.7.2.
bread-shared stays on bread v0.8.0-rc.1.
Delegates dev/rc/release build steps to bread-ecosystem's pinned Arch
container via ci/build.sh, pinned to bread-ecosystem commit
147cfbbf96ae4b171027defa1130d2caddb934b1. Adds a check.yml workflow
for fast clippy/test signal on feature/fix branches, matching the
pattern proven in breadpad. No ci/deps.txt needed — breadbar's
dependencies (zbus, resvg, gtk4-layer-shell, hyprland) are all pure
Rust or already covered by the shared image's package set.
bread-theme moves to git.breadway.dev v0.7.1. bread-utils and
bread-screenshots pin ecosystem origin/main (v0.7.1 has no
BreadClient::request and predates the screenshots crate). bread-shared
pins bread v0.8.0-rc.1 — latest existing tag with widget wire types
(v0.7.0 has none; v0.8.0 was never cut).
bakery.toml sets bread_deps = ["bread"] so the daemon is installed
alongside the bar. CLAUDE.md records the single-trunk workflow and
the bar / notifications / OSD / widgets split.
Replaces the dev/beta branch split with one trunk (main): dev-track
builds still publish on every push, but the beta track now publishes
from a vX.Y.Z-rc.N prerelease tag instead of a separately-maintained
beta branch. Removes the branch nobody reliably kept in sync.
Was a bare gtk4::Window with no titlebar — GTK4's own minimal CSD
fallback for that case is a flat bar with plain system-font title text
and square corners, which is what actually made this look like a stray
window from a different decade next to the rest of the (rounded,
borderless) ecosystem. Now sets a real HeaderBar (which picks up the
window's existing title automatically) and gives the dialog + headerbar
matching rounded corners and dark theming via the existing
.wifi-add-dialog CSS class.
Also switched the Connect button off "suggested-action": GTK4's bundled
theme special-cases that exact class name for a native OS-accent-colour
feature that isn't a normal CSS rule and doesn't yield to any
background-color override this stylesheet adds, confirmed empirically
(a much more specific selector had zero effect). Moved to
"confirm-button", the same accent-button convention breadman/breadpad
already use.
Known residual issue, not resolved here: the Connect button still
doesn't render the accent colour even via "confirm-button" — narrowed
down to *some* button-specific styling quirk in this one dialog (window-
level rules like the rounded corners and dark background apply
correctly; a maximally-obvious magenta test rule on the same selector
also failed to render, ruling out a color-value mistake specifically),
but not root-caused further. Left as a known follow-up rather than
blocking the CSD fix, which was the actual ask.
Also removed two `cursor: pointer` CSS properties (on .clickable and
.media-widget) that GTK4 doesn't support as a CSS property at all — was
logging "Theme parser error: No property named cursor" on every
startup, doing nothing functionally either way.
breadbar is "a bar + the notification daemon + the OSD" — the screenshot
mode only covered the bar and its control-panel popover, missing seven
more distinct surfaces: the WiFi/Bluetooth connectivity popover (both
tabs), the media-controls popover, the standalone notification window
(both normal and critical urgency), the standalone OSD window (volume
and brightness), and the wifi add-network dialog. All ten views are now
--screenshot targets.
Two real refactors needed to make the standalone notification/OSD
windows screenshot-able at all, not just bigger match arms:
- Both windows are built deep inside an async task (`run_osd`/
`popup::run`), only reachable after the real event loop starts — no
window handle ever existed for a caller to hook `connect_map` on before
that. Window construction is now synchronous in `osd::spawn`/
`notifications::spawn`, handed to the async loop as a parameter instead
of created inside it.
- Screenshot mode seeds each with one fixed sample event (SampleKind) via
the same channel the real pactl/backlight/D-Bus sources feed, instead
of waiting for real hardware/dbus activity. For notifications
specifically this also means skipping the real
org.freedesktop.Notifications D-Bus registration entirely in screenshot
mode — claiming that well-known name would just race the real breadbar
(if running) for it, for no benefit, since nothing external needs to
reach a screenshot-only instance.
show_add_network_dialog gained an `on_build` hook (called before
`.present()`, the only point `connect_map` can still catch the map) so
screenshot mode can capture it without changing its one real call site's
behavior — and its `anchor` parameter widened from `&Button` to
`&impl IsA<Widget>` since the screenshot path anchors off a
`ToggleButton`, not a `Button`.
Renders a single view ("bar" or "control-panel"), captures it via
bread-screenshots, then exits — driven by bread-ecosystem's bread-capture
orchestrator, or runnable standalone for one-off captures.
Waits on GTK's map signal (plus a short settle delay) before capturing
rather than guessing a fixed sleep upfront: the control-panel view in
particular needs the popover's autohide disabled (a programmatic popup()
has no input-event serial to grab the Wayland seat with) and a longer
settle window so the CPU/RAM/PWR/GPU/network stats — populated by a
2-second background poller, not the popover's own open — have had at
least one full cycle to fill in before the capture fires.
--width/--height match whatever canvas bread-capture's isolation sizes
the compositor to, so the "bar" view's geometry doesn't depend on
querying anything at capture time.
Needs RelmApp::with_args(vec![]) (GLib's own arg parser otherwise rejects
--screenshot/--output before clap ever sees them) and
allow_multiple_instances(true) for screenshot runs specifically, since
GApplication is single-instance by default and this machine typically
already has a real breadbar running.
bakery already fully covers what the PKGBUILD provided (binary, dependency
declarations) except a LICENSE copy, which bakery.toml's new license_file
field now closes. Removes packaging/arch/ and .forgejo/workflows/package.yml;
adds the LICENSE artifact to each release/dev-release/beta-release
workflow's prepare step. Not pacman-installed inside BOS today (BOS already
consumes these apps exclusively via build-local.sh's skel-staging), so this
only removes the option to `pacman -S` outside of BOS/bakery.
notify-send-based senders (e.g. breadcrumbs) fire a new process per
notification, so replaces_id is always 0 — they instead tag related
notifications with the x-canonical-private-synchronous hint and expect
the daemon to replace whatever's currently showing from that app. We
weren't honoring it, so a persistent (Expire::Never) critical
notification like breadcrumbs' "no Wi-Fi adapter" during a suspend
transition could never be superseded by a later notification, leaving
it stuck until breadbar was restarted. Fixes#2.
Documents the dev/beta/main branch and release-track workflow shared
across the bread ecosystem. See bread-ecosystem's docs/release-channels.md
for the full policy this implements.
Beta is now a real stabilization branch: publishes on every push to
`beta` (mirroring dev's model, auto-versioned X.Y.Z-beta.<ts>+<sha>,
base version from the latest published tag) instead of a manual
beta-v* tag. Fixes made during the freeze land via fix/<issue> branches
merged into `beta` directly. The gen-index.sh clone for beta pulls
bread-ecosystem's default branch (main) rather than pinning to dev,
since beta is the more stable track and main now carries the
TRACK-aware script.
Cargo.toml can drift stale relative to the actual last release (observed
on breadbox/breadpad/breadcrumbs/breadpaper), which made the auto-bumped
dev version sort as OLDER than what's already installed — bakery's semver
check correctly refused those "updates". Deriving the base version from
git ls-remote --tags instead is self-healing regardless of Cargo.toml
drift, with a Cargo.toml fallback only for a repo with no tags yet.
The fixed /tmp/bread-ecosystem-ci path races when multiple repos' dev/beta
workflows run close together on the same self-hosted runner — one job's
rm -rf/clone can stomp another's in-progress checkout, causing the
regenerate-index step to fail intermittently. Switch to mktemp -d.
Adds dev-release.yml (publishes on every push to dev) and beta-release.yml
(publishes on a beta-v* tag), mirroring the pattern landing in
bread-ecosystem/bread. See bread-ecosystem/docs/release-channels.md for
the three-track policy.
Adds real SVG icon assets replacing the icons-needed.txt checklist,
a bluetooth popover module, per-notification urgency (CSS-styled),
and app-name/summary dedup in notification cards. Rounds out the
control panel's stats section with RAM and power draw alongside the
existing CPU/GPU/net rows.
- src/bar/workspaces.rs: the Hyprland EventStream loop exited permanently
on the first Err/end-of-stream (Hyprland restart/reload, IPC hiccup),
freezing every workspace button for the bar's remaining life. Now wrapped
in a reconnect loop with capped exponential backoff, re-syncing workspace
state on every reconnect
- src/notifications/mod.rs + popup.rs: three spec deviations fixed —
expire_timeout=0 now means never-expire instead of being coerced to 5s
(and a critical-urgency notification with no explicit timeout also
persists by default); NotificationClosed is now emitted with the correct
reason code whenever a notification actually goes away (expiry or an
explicit CloseNotification call); replaces_id no longer races its
auto-dismiss timer against the replacement's, via a per-id generation
counter checked before a stale timer is allowed to dismiss anything
- src/main.rs + README.md + assets/icons-needed.txt: lock button (and its
docs) now invoke breadlock instead of hyprlock, the thing breadlock was
built to replace
- src/notifications/mod.rs: added 4 unit tests for the new expire_timeout/
urgency mapping (pulled into a pure compute_expire() for testability) —
this crate had zero test coverage before