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
162 lines
4.7 KiB
Rust
162 lines
4.7 KiB
Rust
use gtk4::prelude::*;
|
|
use gtk4::{Box as GBox, Button, Entry, Label, ListBox, ListBoxRow, Orientation, ScrolledWindow};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
use crate::config;
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Default)]
|
|
pub struct BreadboxConfig {
|
|
#[serde(default)]
|
|
pub context: Vec<Context>,
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize, Clone)]
|
|
pub struct Context {
|
|
pub name: String,
|
|
#[serde(default)]
|
|
pub apps: Vec<String>,
|
|
}
|
|
|
|
fn config_path() -> std::path::PathBuf {
|
|
config::config_dir().join("breadbox/config.toml")
|
|
}
|
|
|
|
fn rebuild_list(list: &ListBox, cfg: &Rc<RefCell<BreadboxConfig>>) {
|
|
while let Some(child) = list.first_child() {
|
|
list.remove(&child);
|
|
}
|
|
for (i, ctx) in cfg.borrow().context.iter().enumerate() {
|
|
let row = ListBoxRow::new();
|
|
row.set_selectable(false);
|
|
|
|
let hbox = GBox::new(Orientation::Horizontal, 8);
|
|
hbox.set_margin_top(6);
|
|
hbox.set_margin_bottom(6);
|
|
hbox.set_margin_start(8);
|
|
hbox.set_margin_end(8);
|
|
|
|
let name_entry = Entry::new();
|
|
name_entry.set_text(&ctx.name);
|
|
name_entry.set_width_chars(14);
|
|
name_entry.set_placeholder_text(Some("name"));
|
|
|
|
let apps_entry = Entry::new();
|
|
apps_entry.set_text(&ctx.apps.join(", "));
|
|
apps_entry.set_hexpand(true);
|
|
apps_entry.set_placeholder_text(Some("app1, app2, ..."));
|
|
|
|
let remove_btn = Button::with_label("Remove");
|
|
remove_btn.add_css_class("destructive-action");
|
|
|
|
{
|
|
let cfg = cfg.clone();
|
|
name_entry.connect_changed(move |e| {
|
|
if let Some(c) = cfg.borrow_mut().context.get_mut(i) {
|
|
c.name = e.text().to_string();
|
|
}
|
|
});
|
|
}
|
|
{
|
|
let cfg = cfg.clone();
|
|
apps_entry.connect_changed(move |e| {
|
|
if let Some(c) = cfg.borrow_mut().context.get_mut(i) {
|
|
c.apps = e.text()
|
|
.split(',')
|
|
.map(|s| s.trim().to_string())
|
|
.filter(|s| !s.is_empty())
|
|
.collect();
|
|
}
|
|
});
|
|
}
|
|
{
|
|
let cfg = cfg.clone();
|
|
let list = list.clone();
|
|
remove_btn.connect_clicked(move |_| {
|
|
cfg.borrow_mut().context.remove(i);
|
|
rebuild_list(&list, &cfg);
|
|
});
|
|
}
|
|
|
|
hbox.append(&name_entry);
|
|
hbox.append(&apps_entry);
|
|
hbox.append(&remove_btn);
|
|
row.set_child(Some(&hbox));
|
|
list.append(&row);
|
|
}
|
|
}
|
|
|
|
pub fn build() -> GBox {
|
|
let path = config_path();
|
|
let cfg: BreadboxConfig = config::load(&path).unwrap_or_default();
|
|
let cfg = Rc::new(RefCell::new(cfg));
|
|
|
|
let vbox = GBox::new(Orientation::Vertical, 12);
|
|
vbox.add_css_class("view-content");
|
|
|
|
let title = Label::new(Some("breadbox"));
|
|
title.add_css_class("title");
|
|
title.set_xalign(0.0);
|
|
vbox.append(&title);
|
|
|
|
let subtitle = Label::new(Some("Context priority lists — apps shown in each context."));
|
|
subtitle.set_xalign(0.0);
|
|
subtitle.set_margin_bottom(8);
|
|
vbox.append(&subtitle);
|
|
|
|
let list = ListBox::new();
|
|
list.set_selection_mode(gtk4::SelectionMode::None);
|
|
rebuild_list(&list, &cfg);
|
|
|
|
let scroll = ScrolledWindow::new();
|
|
scroll.set_vexpand(true);
|
|
scroll.set_child(Some(&list));
|
|
vbox.append(&scroll);
|
|
|
|
let btn_row = GBox::new(Orientation::Horizontal, 8);
|
|
btn_row.set_margin_top(8);
|
|
|
|
let add_btn = Button::with_label("Add context");
|
|
{
|
|
let cfg = cfg.clone();
|
|
let list = list.clone();
|
|
add_btn.connect_clicked(move |_| {
|
|
cfg.borrow_mut().context.push(Context {
|
|
name: "new".to_string(),
|
|
apps: Vec::new(),
|
|
});
|
|
rebuild_list(&list, &cfg);
|
|
});
|
|
}
|
|
|
|
let save_btn = Button::with_label("Save");
|
|
let status_lbl = Label::new(None);
|
|
status_lbl.add_css_class("dim-label");
|
|
|
|
{
|
|
let cfg = cfg.clone();
|
|
let path = path.clone();
|
|
let status_lbl = status_lbl.clone();
|
|
save_btn.connect_clicked(move |_| {
|
|
match config::save(&path, &*cfg.borrow()) {
|
|
Ok(()) => {
|
|
status_lbl.set_text("Saved");
|
|
let lbl = status_lbl.clone();
|
|
glib::timeout_add_seconds_local(3, move || {
|
|
lbl.set_text("");
|
|
glib::ControlFlow::Break
|
|
});
|
|
}
|
|
Err(e) => status_lbl.set_text(&format!("Error: {e}")),
|
|
}
|
|
});
|
|
}
|
|
|
|
btn_row.append(&add_btn);
|
|
btn_row.append(&save_btn);
|
|
btn_row.append(&status_lbl);
|
|
vbox.append(&btn_row);
|
|
|
|
vbox
|
|
}
|