diff --git a/Cargo.toml b/Cargo.toml index eaf8db9..61bb665 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bos-settings" -version = "0.5.0" +version = "0.6.0" edition = "2021" [dependencies] diff --git a/src/ui/sidebar.rs b/src/ui/sidebar.rs index bbc4fb1..5b2ef8e 100644 --- a/src/ui/sidebar.rs +++ b/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/src/ui/views/aur.rs b/src/ui/views/aur.rs new file mode 100644 index 0000000..b526224 --- /dev/null +++ b/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