From ad4d71db34532ac83d849d5524537f601f2a53c1 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 4 Jul 2026 11:04:16 +0800 Subject: [PATCH] bos-settings 0.6.0: Power, Firewall, Users, AUR, Firmware panels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- Cargo.lock | 2 +- README.md | 47 +++-- bos-settings/Cargo.toml | 2 +- bos-settings/src/ui/sidebar.rs | 5 + bos-settings/src/ui/views/aur.rs | 169 +++++++++++++++ bos-settings/src/ui/views/firewall.rs | 246 +++++++++++++++++++++ bos-settings/src/ui/views/firmware.rs | 159 ++++++++++++++ bos-settings/src/ui/views/mod.rs | 5 + bos-settings/src/ui/views/power.rs | 193 +++++++++++++++++ bos-settings/src/ui/views/users.rs | 293 ++++++++++++++++++++++++++ bos-settings/src/ui/window.rs | 5 + iso/packages.x86_64 | 9 + 12 files changed, 1119 insertions(+), 16 deletions(-) create mode 100644 bos-settings/src/ui/views/aur.rs create mode 100644 bos-settings/src/ui/views/firewall.rs create mode 100644 bos-settings/src/ui/views/firmware.rs create mode 100644 bos-settings/src/ui/views/power.rs create mode 100644 bos-settings/src/ui/views/users.rs diff --git a/Cargo.lock b/Cargo.lock index b126d42..bc9c049 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,7 +28,7 @@ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bos-settings" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel", "bread-theme", diff --git a/README.md b/README.md index 39e1fd8..b736e5d 100644 --- a/README.md +++ b/README.md @@ -120,21 +120,40 @@ avoid memory pressure. ## bos-settings -`bos-settings` edits each bread\* app's TOML **non-destructively**: it parses -the file with `toml_edit`, changes only the keys a view exposes, and writes it -back — preserving comments and any keys the UI doesn't model (calendar -passwords, saved-network passwords, model paths). Views: +A GTK4 settings app aiming for GNOME-Settings-style parity: not just editing +config files, but live system state and control, so day-to-day machine +administration doesn't require a terminal. -| View | Config | -|------|--------| -| bread | `bread/breadd.toml` — daemon, lua, modules, all adapters, events, notifications | -| breadbar | `breadbar/style.css` override | -| breadbox | `breadbox/config.toml` — launcher contexts | -| breadcrumbs | `breadcrumbs/breadcrumbs.toml` — settings, saved networks, profiles | -| breadpad | `breadpad/breadpad.toml` — settings, model + ollama, reminders, calendar | -| Snapshots | `snapper` list / rollback / delete | -| Packages | `bakery` installed list + updates | -| Hyprland | open config in editor + monitor list | +Bread-ecosystem TOML configs are edited **non-destructively**: `toml_edit` +parses the file, changes only the keys a view exposes, and writes it back — +preserving comments and any keys the UI doesn't model (calendar passwords, +saved-network passwords, model paths). Panels with a daemon behind them +(bread, breadbox, breadcrumbs, breadsearch, breadclip) also get live +systemd status + Start/Stop/Restart/Logs via a shared `service_control` +widget, not just the config file. + +| Panel | What it does | +|-------|--------------| +| About | System info (OS/kernel/CPU/GPU/memory/disk/uptime) + hostname | +| Network | Wi-Fi scan/connect, Ethernet status, radio toggle | +| Wi-Fi Profiles (breadcrumbs) | `breadcrumbs.toml` — settings, saved networks, profiles | +| Firewall | ufw rules: enable/disable, add/remove, view active rules | +| Sound | PipeWire output/input device + volume via `pactl` | +| Power | Battery status/health, brightness, charge limits (hardware-dependent), TLP profile (read-only) | +| Date & Time | Timezone, NTP sync toggle | +| Display (Hyprland) | Connected monitors + open `hyprland.lua` in editor | +| Users | Add/remove accounts, change passwords | +| Wallpaper (breadpaper) | Set wallpaper, drives the pywal-derived accent palette | +| Bar (breadbar) | `breadbar/style.css` override, live-reloads on save | +| Launcher (breadbox) | `breadbox/config.toml` — launcher contexts | +| Clipboard (breadclip) | breadclipd service control + "open history" | +| Notes (breadpad) | `breadpad/breadpad.toml` — settings, model + ollama, reminders, calendar | +| File Search (breadsearch) | `breadsearch/config.toml` — index/search/model + breadmill service | +| Daemon (bread) | `breadd.toml` — daemon, lua, modules, adapters, events, notifications | +| Packages | `bakery` installed list + updates, pacman system update | +| AUR | Search via `yay`; installing opens a terminal (AUR build scripts need review) | +| Firmware | `fwupd` device list + updates | +| Snapshots | `snapper` list / boot-into (grub-btrfs) / delete | Build standalone: diff --git a/bos-settings/Cargo.toml b/bos-settings/Cargo.toml index eaf8db9..61bb665 100644 --- a/bos-settings/Cargo.toml +++ b/bos-settings/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bos-settings" -version = "0.5.0" +version = "0.6.0" edition = "2021" [dependencies] diff --git a/bos-settings/src/ui/sidebar.rs b/bos-settings/src/ui/sidebar.rs index bbc4fb1..5b2ef8e 100644 --- a/bos-settings/src/ui/sidebar.rs +++ b/bos-settings/src/ui/sidebar.rs @@ -33,9 +33,12 @@ const fn item_sub( pub const SYSTEM_ITEMS: &[SidebarItem] = &[ item("network", "Network", "network-wireless-symbolic"), item_sub("breadcrumbs", "Wi-Fi Profiles", "breadcrumbs", "network-workgroup-symbolic"), + item("firewall", "Firewall", "security-high-symbolic"), item("sound", "Sound", "audio-volume-high-symbolic"), + item("power", "Power", "battery-good-symbolic"), item("datetime", "Date & Time", "preferences-system-time-symbolic"), item_sub("hyprland", "Display", "hyprland.lua", "video-display-symbolic"), + item("users", "Users", "system-users-symbolic"), ]; pub const PERSONALIZATION_ITEMS: &[SidebarItem] = &[ @@ -50,6 +53,8 @@ pub const PERSONALIZATION_ITEMS: &[SidebarItem] = &[ pub const MAINTENANCE_ITEMS: &[SidebarItem] = &[ item("packages", "Packages", "package-x-generic-symbolic"), + item("aur", "AUR", "system-search-symbolic"), + item("firmware", "Firmware", "software-update-available-symbolic"), item("snapshots", "Snapshots", "document-open-recent-symbolic"), ]; diff --git a/bos-settings/src/ui/views/aur.rs b/bos-settings/src/ui/views/aur.rs new file mode 100644 index 0000000..b526224 --- /dev/null +++ b/bos-settings/src/ui/views/aur.rs @@ -0,0 +1,169 @@ +//! AUR search via yay — graphical discovery for the wider AUR beyond +//! bakery's bread ecosystem and [breadway]'s own republished packages. +//! +//! Search and browsing are fully graphical; actually installing a package +//! opens a terminal running `yay -S ` instead of a silent `--noconfirm` +//! install. That's deliberate, not a shortcut we didn't get around to: +//! AUR packages run arbitrary maintainer-supplied build scripts, and yay's +//! interactive PKGBUILD diff review (plus the sudo password prompt) is the +//! actual safety mechanism against a malicious/compromised AUR package — +//! automating it away in the name of "no terminal" would remove the one +//! step that exists to catch that. + +use gtk4::prelude::*; +use gtk4::{Box as GBox, Button, Entry, Label, ListBox, ListBoxRow, Orientation, ScrolledWindow}; +use std::process::Command; + +use crate::ui::widgets as w; + +#[derive(Clone)] +struct AurResult { + name: String, + version: String, + description: String, +} + +fn search(query: &str) -> Vec { + let Ok(output) = Command::new("yay").args(["-Ss", "--aur", query]).output() else { + return Vec::new(); + }; + let text = String::from_utf8_lossy(&output.stdout); + let mut results = Vec::new(); + let mut lines = text.lines().peekable(); + while let Some(header) = lines.next() { + // "aur/name version (+votes score) [Orphaned]" — name/version are + // always the first two whitespace-separated fields after the repo/. + let Some(rest) = header.strip_prefix("aur/") else { continue }; + let mut parts = rest.split_whitespace(); + let Some(name) = parts.next() else { continue }; + let version = parts.next().unwrap_or("").to_string(); + let description = lines.next().unwrap_or("").trim().to_string(); + results.push(AurResult { name: name.to_string(), version, description }); + } + results +} + +fn install_in_terminal(pkg: &str) { + let _ = Command::new("kitty").args(["-e", "yay", "-S", pkg]).spawn(); +} + +pub fn build() -> GBox { + let (outer, content) = w::view_scaffold("AUR"); + content.append(&w::hint( + "Search the Arch User Repository via yay. Installing opens a \ + terminal — AUR packages run arbitrary build scripts, and reviewing \ + what yay is about to do (and entering your password) is a real \ + safety step, not just a formality.", + )); + + let search_row = GBox::new(Orientation::Horizontal, 8); + let search_entry = Entry::new(); + search_entry.set_hexpand(true); + search_entry.set_placeholder_text(Some("Search the AUR…")); + let search_btn = Button::with_label("Search"); + search_btn.add_css_class("suggested-action"); + search_row.append(&search_entry); + search_row.append(&search_btn); + content.append(&search_row); + + let status = w::hint("Search for a package to see results here."); + content.append(&status); + + let list = ListBox::new(); + list.set_selection_mode(gtk4::SelectionMode::None); + let scroll = ScrolledWindow::new(); + scroll.set_vexpand(true); + scroll.set_min_content_height(320); + scroll.set_child(Some(&list)); + content.append(&scroll); + + let run_search = { + let list = list.clone(); + let status = status.clone(); + let search_entry = search_entry.clone(); + move |btn: Option