The first pass only proved the Tauri capture pipeline against the
default landing view ("About"). All 24 real sidebar sections are wired
up now (every id in lib/sidebar.ts has a real component in
lib/views/registry.ts — no Placeholder fallbacks to skip).
Since there's no URL/route per section (activePage is plain Svelte
$state, switched by the sidebar's own click handler), the frontend now
also listens for a screenshot-set-view Tauri event in +page.svelte's
onMount and sets activePage from its payload — the same effect a real
sidebar click has, just driven from the Rust side. screenshot.rs emits
that event after the initial page-load settle delay, then waits a
second settle delay for the newly-active view's own Tauri-command data
fetch to land before capturing (each section fetches its own state on
mount, same as the About page's system-info fetch needed 2s in the
first pass).
First bread-ecosystem app on the Tauri/webview stack rather than raw
GTK4 — proves the isolated-headless-Sway capture pipeline works against
a plain (non-layer-shell) tao/wry window with no gtk4-rs-specific signal
to hook. No connect_map here: since the window isn't owned by gtk4-rs
directly, this instead waits a fixed settle delay (2s — longer than the
native apps need, since a webview's first paint means a full page load
plus an async Tauri-command round-trip for real content, not just GTK
widget layout — confirmed by an initial 800ms attempt capturing the
sidebar correctly but the About panel's data still blank) on Tauri's own
async runtime, then captures the known-size canvas and exits.
Only one view ("default", the initial landing section) is wired up.
Capturing a specific sidebar section would mean routing the Svelte
frontend to a URL/hash on window creation — real, separate frontend
work, deferred past this first pass.
bread-screenshots is pulled from bread-ecosystem's dev branch, unlike
this repo's existing bread-theme/bread-utils deps which point at main —
it doesn't exist on main yet.
The path dependency on ../../bread-ecosystem/bread-theme (and bread-utils)
assumed a sibling checkout that doesn't exist on the CI runner — this was
the actual cause of release.yml never having succeeded, on top of the
Tauri build-path issue fixed separately. Switched to a git dependency
pinned to branch = "main" rather than a tag, since the functions this
migration needs (css_custom_properties/css_tokens) aren't in a tagged
bread-theme release yet. Verified with a full local build.
bakery.toml gains license_file/desktop_file (closing the same gap found
across the rest of the ecosystem) plus hicolor-icon-theme as an optional
dep. Removes packaging/PKGBUILD and .forgejo/workflows/package.yml.
Also fixes release.yml, which has never actually succeeded since the
GTK4->Tauri migration (93c3b88): Cargo.toml moved to src/src/Cargo.toml
(the checkout dir is conventionally named "src", and this repo's own
Rust backend directory is *also* named "src", not the usual src-tauri),
and the Rust build needs frontend/build to already exist (tauri.conf.json's
frontendDist) — that's normally handled by `cargo tauri build`'s
beforeBuildCommand hook, which a plain `cargo build` never runs, so it's
now a separate explicit "build frontend" step. Adds dev-release.yml/
beta-release.yml for the same three-track policy used ecosystem-wide.
cargo build --release alone doesn't produce a working standalone binary in
Tauri v2 — embedding the frontend vs. hitting devUrl at runtime is gated by
the tauri/custom-protocol Cargo feature, not the debug/release profile.
The scaffold never declared it, so even release builds tried to connect to
localhost:1420. Added the standard default-on custom-protocol feature
(cargo tauri dev strips it via --no-default-features for dev builds); a
plain `cargo build --release --locked` in src/ now produces a real
standalone binary with no dev-server dependency.
Also fixes vite.config.js's file-watch ignore pattern, left stale by the
src-tauri -> src rename: a bare "**/src/**" glob would have also matched
frontend/src itself, so it's now an absolute path scoped to the Rust
project dir specifically.
TagsField now takes an optional suggestions list, rendered as a native
<datalist> on the tag input. Wired the Daemon page's "Disabled modules"
field to breadd's real built-in module names (bread.monitors/devices/
workspaces/binds, from bread/breadd/src/lua/mod.rs's BUILTIN_* registry)
so blocking one is a pick, not free-typed guesswork.
Ports every remaining view (Keybinds, the last one — dual Flat/MultiLayout
schema detection, per-action dedicated fields with a raw-JSON fallback for
anything not modeled) and removes the now-fully-dead GTK4 crate (root
Cargo.toml + src/), leaving src-tauri/ as the single Rust binary crate.
Also folds in the UX pass done alongside the migration: responsive
multi-column layout instead of a single centered column, chip/dropdown
pickers replacing free-typed fields (Wi-Fi networks, Bar style, keyboard
layout, tag lists), consistent pill-switch toggles and boxed-list card
styling across every page, and a sidebar scroll-chaining fix.
bos-settings has had a bakery.toml, a PKGBUILD, and package.yml
(pacman channel) for a while, but never got the release.yml needed to
actually publish to dl.breadway.dev / GitHub releases for the bakery
channel — despite bos/DESIGN.md documenting the intent to make it
bakery-installable standalone. Modeled on breadmon/release.yml (same
shape: single binary, no systemd service). Paired with a new registry
entry in bread-ecosystem-fix-worktree.
Note for whoever runs this for real: bos-settings' Cargo.toml currently
has bread-utils pinned via a path dependency
(../bread-ecosystem-fix-worktree/bread-utils) rather than the tag-pinned
git dependency bread-theme uses — there's already a TODO on that line.
This release.yml (and the existing package.yml) will fail to build in a
clean CI checkout until that's resolved; pre-existing issue, not
something this pass touched.
get_live_monitors used a bare Command::new("hyprctl").output() with no
timeout. Switched to bread_utils::proc::run_json, now that this crate
already depends on bread-utils (added in the config-migration commit).
load_doc/save_doc/atomic_write's bodies now delegate to
bread_utils::tomlcfg and bread_utils::atomic (path dependency for now, see
the TODO in Cargo.toml) instead of owning the temp-then-rename +
.bak-before-overwrite logic locally. Public function names/signatures in
config/mod.rs are unchanged, so none of the ~10 call sites across
ui/views/*.rs needed touching.
This is the other half of tonight's earlier BOS fix pass: that pass gave
breadhelp/src/config.rs its own byte-for-byte copy of this exact logic
(its own doc comment says "same discipline as bos-settings/src/config/
mod.rs::atomic_write") rather than sharing it — breadhelp's migration
follows in the next commit.
Builds clean; all 11 existing tests pass, including the atomic-write
backup/no-leftover-tmp-file test that now exercises the delegated code.
keybinds.rs was built only against a personal multi-layout binds.json schema
(globals/common/layouts). Loading the real BOS-shipped flat schema
(default_mods/bindings, with per-bind label/category/demo_cmd breadhelp
depends on) into that model and saving silently dropped the bindings key --
still valid JSON, so the Lua pcall failsafes never caught it.
- Add SchemaKind (Flat/MultiLayout/Unknown), detected from the file's
top-level keys at load time and pinned for the session so save() always
emits the same shape it read.
- Flat mode hides the layout-switching UI (there's nothing to switch) and
edits a single Bindings list; MultiLayout keeps today's UI unchanged.
- Bind's #[serde(flatten)] extra map already preserved label/category/
demo_cmd/options across a round trip; changed `mods` from Vec<String> to
Option<Vec<String>> so an explicit "mods": [] (media keys pinning "no
modifiers") isn't collapsed into "field omitted -> falls back to
default_mods" -- a real behavior-changing loss the old round trip had.
- Unknown schema refuses to save (visible status-label error) instead of
guessing a shape and overwriting the file.
- New round-trip tests in keybinds.rs load a real BOS-shaped binds.json
fixture through load -> save and assert the bindings key and every
per-bind extra field survive untouched; this is the regression test that
would have caught the original bug.
Also: config writes across the app (TOML via config::save_doc, and the
JSON views -- autostart, appearance/settings.json, hyprland.rs/
monitors.json, breadbar's CSS) now go through a shared
config::atomic_write: write to a temp file in the same directory, rename
over the target, and back up whatever was there first to <path>.bak. A
crash or disk-full mid-write can no longer leave a config truncated or
corrupted with no way back.
Wallpaper: file picker moved off the deprecated FileChooserDialog to
gtk4::FileDialog, plus a "Browse ~/Pictures/Backgrounds" thumbnail
grid (recursive scan, decoded previews) so picking a wallpaper isn't
guessing blind from filenames.
Fixed the app becoming unusable at Hyprland scale > 1.0: the sidebar's
~30-row nav list had no ScrolledWindow, so its unscrollable minimum
height exceeded the logical screen size once scale shrank it below
~1000px, clipping the window (Save buttons included) with no way to
reach the rest. Sidebar now scrolls independently; content column's
minimum width also dropped 760->560 for the same reason at scale 2.0.
Wi-Fi/saved-network password fields had no way to unhide what you
typed. Swapped every masked Entry (network.rs, breadcrumbs.rs, and the
shared password_row widget) to gtk4::PasswordEntry, which has a native
reveal-icon toggle.
Added a Keybinds editor for hypr/binds.json -- the one JSON Hyprland
config (globals/common/per-layout binds) with no GUI anywhere in the
app, despite Display/Appearance/Startup Apps all having one for their
own JSON config. Action-specific fields (command/direction/workspace/
options/...) round-trip through a compact inline-JSON column rather
than hardcoding a widget per action shape.
Adapter power switch, paired devices (connect/disconnect/forget), and
scan-for-new-devices with pairing -- BOS had bluez/bluez-utils packaged
and bread's own daemon already monitors Bluetooth events, but there was
no GUI to actually manage devices anywhere in the settings app.
Shells out to bluetoothctl's non-interactive mode, same "standard CLI,
no new dependency" choice as Network (nmcli) and Snapshots (snapper).
Pairing only covers "just works" SSP (most audio/HID devices) -- a
device requiring a PIN would need this app to register its own
bluetoothd agent, out of scope for now; it just fails with the reason
shown, same honest-failure pattern the Firewall fix added.
Firewall's fetch_status() discarded stderr/exit code on failure, so a
failed pkexec call (e.g. no polkit agent in the session) left the page
silently stuck on "Status not loaded" with no indication anything went
wrong. Now surfaces the actual failure reason, and attaches the
previously-invisible per-action log buffer to a visible TextView
(matching the Packages page's existing pattern).
Snapshots' "No snapshots yet" empty state was shown identically whether
snapper was genuinely unconfigured or the user just lacked permissions
(a "No permissions." stderr) -- now distinguishes the two.
Split out of the bos repo so a bos-settings release no longer requires a
BOS ISO release (and vice versa) — CI here publishes straight to the
[breadway] pacman repo on tag push, independent of bos's own release cadence.
Also adds real GUI editors for the Hyprland JSON config surfaces bos gained
recently (hypr/settings.json, hypr/monitors.json, hypr/autostart.json):
- Appearance panel: gaps, borders (with a real color picker), blur, shadow,
tiling layout, and basic input settings.
- Display panel: a monitor-rule list editor alongside the existing live
connected-monitor readout.
- Startup Apps panel: toggle/edit/add/remove the extra autostart commands.
- --page argv support for deep-linking (breadhelp's guides can now open
bos-settings straight to a specific page).
- Fixed a stale "View keybinds cheat sheet" button pointing at a file
deleted when breadhelp replaced bos-keybinds/bos-welcome.
Continues reducing terminal-reliance for graphical system control. Five
new panels, plus three GUI apps shipped for things better served by an
existing dedicated tool than reimplemented in bos-settings
(gnome-disk-utility, gufw, mission-center).
- Power: battery status/health, brightness, charge-limit thresholds where
the hardware exposes them, TLP profile shown read-only by design (no
Balanced/Performance switcher — TLP auto-selects by power source, and
power-profiles-daemon isn't installed because it conflicts with tlp).
- Firewall: ufw enable/disable, add/remove rules, view active rules.
ufw's own status check requires root (confirmed against the installed
script — not just changes, reads too), so unlike every other panel this
one does NOT query state in build(): every view is constructed eagerly
at app launch, and an unconditional privileged read here would mean a
polkit prompt on every single bos-settings open. Starts blank with a
"Status not loaded" placeholder; state loads only on an explicit Refresh
click, with a guard so refresh's own set_active() doesn't loop back into
triggering ufw enable/disable.
- Users: add/remove accounts, change passwords. All through pkexec on a
background thread. Can't remove the account you're currently running as.
- AUR: search via yay. Installing deliberately opens a terminal instead of
a silent --noconfirm install — yay's interactive PKGBUILD diff review and
sudo prompt are the actual safety mechanism against a malicious AUR
package, not a formality worth automating away.
- Firmware: fwupd device list + updates, same stream-output-then-refresh
pattern as Packages.
packages.x86_64: gnome-disk-utility, gufw, mission-center for disk/firewall/
task-manager GUIs that don't need reinventing inside bos-settings.
Root cause of two panels rendering full-bleed instead of the 760px column
(breadsearch, breadclip): hint()'s long unwrapped text reported an
unbounded natural width, and CONTENT_MAX_WIDTH is a floor (set_size_request),
not a cap — nothing was actually capping width. hint() and empty_state()
now bound their labels with set_max_width_chars, which stabilizes every
panel's column at a consistent width and left edge for the first time.
service_control(): added a second status line (enabled-at-boot vs
just-running), a confirm dialog on Stop for critical units (breadd — the
whole desktop's event backbone was one accidental click from stopping,
styled identically to optional helpers), and a consistent "Save only
writes the file, Restart applies it" hint on panels that have both a
service and a config file (previously only breadsearch said this, in its
own inconsistent wording).
Consistency sweep: Network's Scan button and empty-state treatment now
match the rest of the app (was full-width where every other secondary
action is auto-width; "not scanned" was a hint while "no results" was an
empty_state card — now both empty_state). breadclip gets an actual "Open
history" action instead of reading as an unfinished stub. breadbox's Save
button pulled out of a mixed row into its own row like every other panel.
Packages list rows now get the same card styling as every other row in
the app.
Panel title now shares view_scaffold's width+center treatment with the
content column, so it actually heads the column instead of sitting ~440px
to its left.
Also: switch widgets had a stray box-shadow/outline/border showing as a
faint ring around the knob, and bread-theme's switch slider color
(@on-surface) was being masked by Adwaita's default gloss background-image
— clearing it reveals the correctly-themed near-white knob against the
dark surface.
sidebar.rs and window.rs each independently hardcoded "about" as the
default selected/visible page — harmless while both literals happened to
match, but a real latent bug: changing one without the other silently
desyncs the sidebar highlight from the actually-displayed panel. Found
while capturing screenshots of every panel for a design review, where
exactly that happened. window.rs now owns DEFAULT_PAGE and passes it to
sidebar::build().
New panels: About, Network (Wi-Fi/Ethernet via nmcli), Sound (PipeWire
volume/device via pactl), Date & Time (timedatectl), and Clipboard
(breadclip previously had no bos-settings presence at all despite running
its own daemon).
Layout: every panel now shares one scaffold (6 views were hand-rolling their
own, inconsistently) with a centered, width-capped content column instead of
rows stretching edge-to-edge on a wide window with the control stranded far
from its label. Rows render as individual cards. Sidebar got human labels +
icons + task-based grouping (System/Personalization/Maintenance/About)
instead of raw binary names under "Apps"/"System".
New capability: a shared service_control() widget gives every bread app
backed by a systemd --user daemon (breadd, breadbox-sync, breadcrumbs,
breadmill, breadclipd) live status plus Start/Stop/Restart/View logs —
previously these panels only ever edited a TOML file and hoped the running
process picked it up. breadbar (no systemd unit) gets an equivalent: Save
now actually sends the SIGHUP its own reload mechanism needs, instead of
just claiming to in the hint text.
Fixed two real bugs surfaced while building About: CPU model string kept a
stray leading tab+colon (trim_start_matches was missing '\t'), and GPU
showed "unknown" on hardware whose lspci entry says "Display controller"
instead of "VGA compatible controller".
Local CSS patches (bos-settings/src/theme.rs) for two upstream bread-theme
gaps: suggested/destructive buttons and scale widgets don't clear Adwaita's
default background-image, so flat colour overrides were invisible; and
destructive-action red must not be pywal-derived (it can land on gold
depending on the wallpaper, indistinguishable from a primary action).
Pulls the fix for panels turning brown/off-hue when pywal extracts a light
or muddy background from the wallpaper — background/surface now stay BOS's
fixed dark constants regardless of what the wallpaper looks like.
breadsearch v0.2.1+ ships breadmill built with --features full (npu + rocm
+ cuda in one binary via ort's load-dynamic/dlopen mode), so the earlier
CPU-only restriction here no longer applies. Hint explains what each
backend needs and how to confirm it actually took effect, since a failed
GPU EP registration falls back to CPU silently at the ONNX Runtime level.
Snapshots panel was non-functional (wrong snapper flag); breadgreet picked
the wrong session .desktop and skipped bos-session's PATH fixup; Calamares
aborted offline installs over an unnecessary packages module; snapshot
rollback silently no-op'd on BOS's pinned-subvolume layout (now points at
grub-btrfs instead); breadcrumbs was configurable but had no daemon to run
it. Also: SUPER+I double-bind, breadpaper/packages panels blocking the GTK
main thread, dead polkit rule, and a sweep of smaller drift (default
sidebar view, stale wording, missing .desktop launchers, wallpaper daemon
not recording its own default).
- breadbox panel edited [[contexts]] but breadbox reads [[context]]
(serde rename mismatch) — panel was always empty and Save appended
a dead array.
- Packages panel parsed installed.json as a flat map instead of
unwrapping {"packages": {...}} — always showed one bogus row.
- load_doc silently produced an empty document on any parse error,
so the next Save could clobber a config file with a syntax typo;
now backs it up first.
- Hyprland panel opened hyprland.conf/keybinds.conf (BOS ships
hyprland.lua) via $EDITOR with no terminal (dead click for any TUI
editor) and defaulted to foot (BOS ships kitty). Now opens
hyprland.lua and a keybinds cheat sheet via kitty -e $EDITOR.
- New breadsearch view: power (enabled/run-on-battery), compute
backend (cpu/npu/rocm), index/search settings.
- Move assets to assets/ directory (bread_white.svg, icons 256/512/1024px)
- Update Calamares branding + Plymouth theme logos
- Bake breadpaper (wallpaper manager + pywal) into /etc/skel alongside the
rest of the bread ecosystem — previously missing from the ISO build
- Bump bos-settings to 0.4.0
- .gitignore: ignore the /out/ ISO build dir
- bos-settings: use expect() with messages over unwrap() for piped stdio;
drop a stray blank line
- pacman.conf: reword the SigLevel=Never TODO as a future-improvement note
Pick up the directory-watch fix so bos-settings hot-reloads the shared stylesheet
on `bread-theme reload` like the rest of the desktop (its v0.2.6 build had the
broken file-watch). No code change — only the dependency + version bump.
Replace the hardcoded Nord palette (which ignored pywal and the rest of the
ecosystem entirely) with bread_theme::gtk::apply_shared() — bos-settings now
loads the same generated stylesheet as breadbar/breadbox/breadpad and keeps
only its own layout rules (.view-content padding). It recolours live with the
desktop. Bump gtk4 0.9 -> 0.11 / glib -> 0.22 to match the ecosystem.
Note: bread-theme dep pins tag v0.2.6 (cut at release); Cargo.lock to be
regenerated then.
The bread/breadpad/breadcrumbs/breadbox views wrote invented schemas
(e.g. top-level log_level, [[profile]] name/ssids) that did not match the
apps' real TOML, so they showed empty and — worse — clobbered the real
config on Save, since the old config::save serialized only the keys it
modelled.
Rework the config layer onto toml_edit: parse each file into a
DocumentMut, mutate only the specific keys a view exposes, and write it
back preserving comments and any unmodelled keys (calendar password,
saved-network passwords, model paths). Unit-tested.
Add ui/widgets.rs (switch/entry/password/dropdown/spin/float/csv rows +
view scaffold + save button) bound to the shared document, then rewrite
the four views against the real schemas with far more coverage:
- bread: [daemon], [lua], [modules], all five [adapters.*] with their
sub-options, [events], [notifications]
- breadpad: [settings], [model] + [model.ollama], [reminders], [calendar]
- breadcrumbs: [settings] (7 keys), [[networks]] editor, [profiles.*] editor
- breadbox: fixed to real [[contexts]] name/priority array editor
Goal: configure everything from the GUI rather than hand-editing TOML.
bos-settings was scaffolded but never compiled. Fixes:
- main.rs: import gtk4::prelude (connect_activate/run)
- window.rs: disambiguate WidgetExt::display(); drop unused GBox import
- hyprland.rs: Label has no set_monospace -> use the monospace CSS class
- theme.rs: drop unused prelude import
Also switch package.yml to secrets.REGISTRY_TOKEN (scoped write:package),
since the auto Actions token is not authorized for the owner registry.
Mirrors the build/distribution pattern used by the bread project:
- bakery.toml describes bos-settings as a bakery-managed package
- packaging/arch/PKGBUILD builds and installs the binary via cargo
- packaging/arch/bos-settings.desktop for app launchers
- LICENSE (MIT) required by PKGBUILD
- Fix XDG config dir logic in config/mod.rs (was double-nesting and had /home/user hardcode)
- Replace /home/user hardcodes in breadbar.rs and hyprland.rs with config::config_dir()
- Fix /home/user hardcode in packages.rs (uses /root fallback for .local/state path)
- Remove eprintln! from GTK callback in packages.rs (no stderr at runtime)
- Fix YAML parse error in branding.desc (missing space after sidebarTextHighlight key)
- Add .gitignore (Rust target/, ISO artifacts, editor/OS junk, secrets)
- Delete state.rs (dead code — never mod'd in main.rs)
- Add brightnessctl, grim, slurp to packages.x86_64 (used by keybinds)
- Rename can-you-begin-a-composed-beacon.md → DESIGN.md
ISO structural:
- Move post-install.sh → airootfs/etc/calamares/ (it was never in the squashfs)
- Create airootfs/etc/skel/.config/ with all dotfiles (deploy path now works)
- Add iso/pacman.conf with [breadway] custom repo stub for calamares + bakery
- Add Calamares branding component (bos/branding.desc + show.qml)
- Add missing unpackfs.conf and mount.conf modules
- Add live-session autostart: getty autologin → bash_profile → Hyprland → calamares
- Add polkit rule for wheel-group snapper rollback (pkexec path)
- Remove wlroots from packages (bundled with Hyprland); add bakery to package list
- Fix modules-search path in settings.conf
Dotfiles:
- Rename dotfiles/hyprland/ → dotfiles/hypr/ (Hyprland reads ~/.config/hypr/)
- Fix deprecated shadow options: drop_shadow/shadow_range → shadow { } block
bos-settings Rust:
- Replace glib::MainContext::channel (removed in glib 0.19) with async_channel
- Stream bakery update output line-by-line instead of buffering all at once
- Fix zombie processes: per-package update buttons now wait() in a thread
- Fix sidebar/stack mismatch at startup: select snapshots row to match default view
- Replace deprecated MessageDialog with AlertDialog (GTK 4.10+) throughout
- Use pkexec for snapper rollback so polkit handles privilege escalation
- Add confirmation dialog before delete snapshot (was missing, rollback had one)
- Add refresh button + repopulate after delete in snapshots view
- Add "Saved" / "Error: …" status label to every config view save button
- Add "Remove" buttons to breadbox contexts and breadcrumbs profiles
- Remove hardcoded model string from breadpad defaults
- Drop unused state mod; fix config_dir HOME fallback; fix zombie in editor launches
https://claude.ai/code/session_01WszGHvCmxgcyTwNSkfLF9P