git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commit9936eb8d0836ff4fd7bdde64c380de954b78a12f
parent90f0098643
authorLucas Galante <[email protected]>
date2026-08-22 23:56
feat: remove the Fonts page — the DE's families live in config.kdl now

The page edited three foreign files, none of them cce's own config:
fontconfig's fonts.conf (XML), fuzzel.ini and foot.ini. Three of its five
sections had stopped controlling anything:

  Borders           `window-borders` alias — no reader anywhere in the tree.
                    Window borders lost their text when titlebars went away.
  Status Interface  `status-interface` alias — read_status_font_from_config
                    checks `module { font }`, then `/style/status/font`, and
                    only then this. Both KDL keys are set on this host, so the
                    dropdown wrote into a fallback nothing consulted.
  Fuzzel            fuzzel isn't installed; cce-cloud replaced it and reads
                    none of this.

The two live ones — sans/serif/mono, and cce-terminal's family — move to a
`fonts { }` block in the shared config, so no cce setting is left squatting
in fontconfig's family namespace. fonts.conf is still fontconfig's file and
still governs GTK/Electron apps; cce just no longer reads or writes it. That
also ends the page's habit of rewriting that file wholesale, preserving only
its `<dir>` lines — any hand-added hinting or antialias rule was destroyed by
touching a font dropdown.

There is deliberately no settings UI left for fonts, following input.kdl's
precedent. cce-fonts is unaffected: it previews faces, it never assigned them.

Page removal is the 9-site wiring in reverse (mod.rs enum + ALL[15→14] +
label, app.rs field/default/two match arms/AppAction, watchers.rs rx+spawn,
main.rs rx field/init/drain/action arm), plus read_preferred_fonts now coming
from cce_ui rather than the page's own copy.

Shadow-verified: the page dropdown lists 14 entries with no Fonts, and the
settings app still renders. 51 tests pass (53 minus the two that tested the
page's own gating).

Co-Authored-By: Claude <[email protected]>

 src/app.rs         |   6 -
 src/main.rs        |  24 +-
 src/pages/fonts.rs | 725 -----------------------------------------------------
 src/pages/mod.rs   |   6 +-
 src/watchers.rs    |   5 +-
 5 files changed, 4 insertions(+), 762 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 955498d..44bb171 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -10,7 +10,6 @@ use crate::pages::services;
 use crate::pages::system_info;
 use crate::pages::timers;
 use crate::pages::storage;
-use crate::pages::fonts;
 use crate::pages::accounts;
 use crate::pages::packages;
 use crate::pages::notifications;
@@ -30,7 +29,6 @@ pub struct AppState {
     pub system_info: system_info::SystemState,
     pub timers: timers::TimersState,
     pub storage: storage::StorageState,
-    pub fonts: fonts::FontsState,
     pub accounts: accounts::AccountsState,
     pub packages: packages::PackagesState,
     pub notifications: notifications::NotificationsState,
@@ -51,7 +49,6 @@ impl Default for AppState {
             system_info: system_info::SystemState::default(),
             timers: timers::TimersState::default(),
             storage: storage::StorageState::default(),
-            fonts: fonts::FontsState::default(),
             accounts: accounts::AccountsState::default_mock(),
             packages: packages::PackagesState::default(),
             notifications: notifications::NotificationsState::default(),
@@ -75,7 +72,6 @@ impl AppState {
             Page::Storage => &self.storage,
             Page::System => &self.system_info,
             Page::Timers => &self.timers,
-            Page::Fonts => &self.fonts,
             Page::Notifications => &self.notifications,
         }
     }
@@ -95,7 +91,6 @@ impl AppState {
             Page::Storage => &mut self.storage,
             Page::System => &mut self.system_info,
             Page::Timers => &mut self.timers,
-            Page::Fonts => &mut self.fonts,
             Page::Notifications => &mut self.notifications,
         }
     }
@@ -123,7 +118,6 @@ pub enum AppAction {
     SystemInfo(system_info::SystemMessage),
     Timers(timers::TimersMessage),
     Storage(storage::StorageMessage),
-    Fonts(fonts::FontsMessage),
     Accounts(accounts::AccountsMessage),
     Packages(packages::PackagesMessage),
     Notifications(notifications::NotificationsMessage),
diff --git a/src/main.rs b/src/main.rs
index a5cf8d3..7cb2b69 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -68,7 +68,6 @@ struct SystemInterface {
     rx_services: std::sync::mpsc::Receiver<Vec<pages::services::ServiceInfo>>,
     rx_default_apps: std::sync::mpsc::Receiver<pages::default_apps::DefaultAppsInfo>,
     rx_timers: std::sync::mpsc::Receiver<Vec<pages::timers::TimerInfo>>,
-    rx_fonts: std::sync::mpsc::Receiver<pages::fonts::FontsState>,
     rx_accounts: std::sync::mpsc::Receiver<Vec<pages::accounts::AccountInfo>>,
     tx_backup: std::sync::mpsc::Sender<pages::storage::StorageMessage>,
     rx_backup: std::sync::mpsc::Receiver<pages::storage::StorageMessage>,
@@ -130,10 +129,7 @@ impl cce_ui::engine::Application for SystemInterface {
 
     fn new(_qh: &wayland_client::QueueHandle<cce_ui::engine::EngineState<Self>>, sender: calloop::channel::Sender<Self::Message>) -> Self {
         cce_ui::scale::set_scale_factor(1.0);
-        let app = AppState {
-            fonts: pages::fonts::read_typeface_config(),
-            ..Default::default()
-        };
+        let app = AppState::default();
 
         // ── Background refresh channels ──
         let initial_page_idx = INITIAL_PAGE_INDEX.load(std::sync::atomic::Ordering::SeqCst);
@@ -142,7 +138,7 @@ impl cce_ui::engine::Application for SystemInterface {
         let (watchers, tx_backup, rx_backup, tx_update, rx_update) =
             cce_settings::watchers::spawn_all(current_page_shared.clone());
 
-        let (sans_family, serif_family, monospace_family, _, _, _, _) = pages::fonts::read_preferred_fonts();
+        let (sans_family, serif_family, monospace_family, _) = cce_ui::layout::read_preferred_fonts();
 
         let pages_names = Page::ALL.iter().map(|p| p.label().to_string()).collect::<Vec<_>>();
         let page_dropdown = cce_ui::widget::input::Dropdown::new(pages_names, initial_page_idx)
@@ -181,7 +177,6 @@ impl cce_ui::engine::Application for SystemInterface {
             rx_notifications: watchers.rx_notifications,
             rx_browser: watchers.rx_browser,
             rx_services: watchers.rx_services,
-            rx_fonts: watchers.rx_fonts,
             rx_accounts: watchers.rx_accounts,
             tx_backup,
             rx_backup,
@@ -598,15 +593,6 @@ impl SystemInterface {
                 self.needs_rebuild = true;
             }
         }
-        while let Ok(s) = self.rx_fonts.try_recv() {
-            self.sans_serif_family = s.sans_serif.clone();
-            self.serif_family = s.serif.clone();
-            self.monospace_family = s.monospace.clone();
-            fonts::update(&mut self.app.fonts, fonts::FontsMessage::TypefaceRefreshed(s));
-            if self.app.current_page == Page::Fonts {
-                self.needs_rebuild = true;
-            }
-        }
         while let Ok(s) = self.rx_default_apps.try_recv() {
             pages::default_apps::update(&mut self.app.default_apps, pages::default_apps::DefaultAppsMessage::Refreshed(s));
             if self.app.current_page == Page::DefaultApps {
@@ -674,12 +660,6 @@ impl SystemInterface {
             },
 
 
-            AppAction::Fonts(m) => {
-                fonts::update(&mut self.app.fonts, m.clone());
-                self.sans_serif_family = self.app.fonts.sans_serif.clone();
-                self.serif_family = self.app.fonts.serif.clone();
-                self.monospace_family = self.app.fonts.monospace.clone();
-            }
             AppAction::Accounts(m) => match m {
                 pages::accounts::AccountsMessage::GoogleLoginInit => {
                     // One listener at a time: port 36137 is fixed, so a second
diff --git a/src/pages/fonts.rs b/src/pages/fonts.rs
deleted file mode 100644
index 11466f2..0000000
--- a/src/pages/fonts.rs
+++ /dev/null
@@ -1,725 +0,0 @@
-use std::fs;
-use cce_ui::layout::LayoutStrategy;
-use cce_ui::widget::{TextBox, Dropdown, Spinbox};
-use crate::app::{PageContent, AppAction};
-use crate::pages::AppPage;
-
-fn fonts_conf_path() -> String {
-    cce_ui::config::config_home().join("fontconfig").join("fonts.conf").to_string_lossy().into_owned()
-}
-fn fuzzel_ini_path() -> String {
-    cce_ui::config::config_home().join("fuzzel").join("fuzzel.ini").to_string_lossy().into_owned()
-}
-fn foot_ini_path() -> String {
-    cce_ui::config::config_home().join("foot").join("foot.ini").to_string_lossy().into_owned()
-}
-
-#[derive(Debug, Clone)]
-pub enum FontsMessage {
-    TypefaceRefreshed(FontsState),
-    Refreshed(FontsState),
-    SetSans(String),
-    SetSerif(String),
-    SetMono(String),
-    SetBorders(String),
-    SetStatus(String),
-    SetFuzzel(String),
-    SetTerminal(String),
-    SetBordersIdx(usize),
-    SetStatusIdx(usize),
-    SetFuzzelIdx(usize),
-    SetTerminalIdx(usize),
-    SetFuzzelSize(u16),
-    SetTerminalSize(u16),
-}
-
-/// The "Other" entry in each font dropdown — the one selection whose section
-/// also paints a free-text box for a font name the presets don't cover.
-///
-/// Named because the gate now has to be stated in three places (the view, the
-/// dispatch-root report, and the manual registration) and a bare `== 3` drifting
-/// in one of them is exactly the bug this replaced. The boxes used to be
-/// registered by pointer unconditionally: an unpainted box kept its last-drawn
-/// rect, kept hit-testing there, and — because the registration *succeeded* —
-/// never tripped the `unregistered/stale root` warning that catches the same
-/// mistake on every other page.
-const FONT_OTHER: usize = 3;
-
-#[derive(Clone)]
-pub struct FontsState {
-    pub typeface_loaded: bool,
-    pub sans_serif: String,
-    pub serif: String,
-    pub monospace: String,
-    pub window_borders: String,
-    pub status_interface: String,
-    pub fuzzel: String,
-    pub terminal: String,
-    pub all_fonts: Vec<String>,
-    pub mono_fonts: Vec<String>,
-    pub sans_box: cce_ui::widget::Adapted<TextBox>,
-    pub serif_box: cce_ui::widget::Adapted<TextBox>,
-    pub mono_box: cce_ui::widget::Adapted<TextBox>,
-    pub borders_box: cce_ui::widget::Adapted<TextBox>,
-    pub status_box: cce_ui::widget::Adapted<TextBox>,
-    pub fuzzel_box: cce_ui::widget::Adapted<TextBox>,
-    pub terminal_box: cce_ui::widget::Adapted<TextBox>,
-    pub borders_menu: cce_ui::widget::Adapted<Dropdown>,
-    pub status_menu: cce_ui::widget::Adapted<Dropdown>,
-    pub fuzzel_menu: cce_ui::widget::Adapted<Dropdown>,
-    pub terminal_menu: cce_ui::widget::Adapted<Dropdown>,
-    pub fuzzel_size_box: cce_ui::widget::Adapted<cce_ui::widget::Spinbox>,
-    pub terminal_size_box: cce_ui::widget::Adapted<cce_ui::widget::Spinbox>,
-}
-
-impl std::fmt::Debug for FontsState {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("FontsState").finish()
-    }
-}
-
-impl Default for FontsState {
-    fn default() -> Self {
-        Self {
-            typeface_loaded: false,
-            sans_serif: "Noto Sans".to_string(),
-            serif: "Noto Serif".to_string(),
-            monospace: "Noto Sans Mono".to_string(),
-            window_borders: "Noto Sans".to_string(),
-            status_interface: "Noto Sans".to_string(),
-            fuzzel: "Noto Sans".to_string(),
-            terminal: "Noto Sans Mono".to_string(),
-            all_fonts: Vec::new(),
-            mono_fonts: Vec::new(),
-            sans_box: TextBox::new(String::new()).with_label("Sans-Serif").with_config(&fonts_conf_path() , "sans-serif"),
-            serif_box: TextBox::new(String::new()).with_label("Serif").with_config(&fonts_conf_path() , "serif"),
-            mono_box: TextBox::new(String::new()).with_label("Monospace").with_config(&fonts_conf_path() , "monospace"),
-            borders_box: TextBox::new(String::new()).with_label("Window Borders").with_config(&fonts_conf_path() , "window_borders"),
-            status_box: TextBox::new(String::new()).with_label("Status Interface").with_config(&fonts_conf_path() , "status_interface"),
-            fuzzel_box: TextBox::new(String::new()).with_label("Fuzzel").with_config(&fonts_conf_path() , "fuzzel"),
-            terminal_box: TextBox::new(String::new()).with_label("Terminal").with_config(&fonts_conf_path() , "terminal"),
-            borders_menu: Dropdown::new(Vec::new(), 0).with_config(&fonts_conf_path() , "window_borders"),
-            status_menu: Dropdown::new(Vec::new(), 0).with_config(&fonts_conf_path() , "status_interface"),
-            fuzzel_menu: Dropdown::new(Vec::new(), 0).with_config(&fonts_conf_path() , "fuzzel"),
-            terminal_menu: Dropdown::new(Vec::new(), 0).with_config(&fonts_conf_path() , "terminal"),
-            fuzzel_size_box: Spinbox::new(14, 6, 72, 1).with_config(&fuzzel_ini_path() , "size"),
-            terminal_size_box: Spinbox::new(12, 6, 72, 1).with_config(&foot_ini_path() , "size"),
-        }
-    }
-}
-
-impl AppPage for FontsState {
-    // Sections: [Preferred Fonts, Borders, Status Interface, Fuzzel, Terminal]
-    // Each section's free-text box is reported only when its menu is on "Other",
-    // mirroring the view below — the boxes are the only conditionally-painted
-    // widgets here, and the groups stay in the view's paint order either way.
-    fn section_widgets(&mut self) -> Vec<Vec<cce_ui::widget::WidgetId>> {
-        let mut borders = vec![self.borders_menu.id()];
-        if self.borders_menu.selected == FONT_OTHER {
-            borders.push(self.borders_box.id());
-        }
-        let mut status = vec![self.status_menu.id()];
-        if self.status_menu.selected == FONT_OTHER {
-            status.push(self.status_box.id());
-        }
-        let mut fuzzel = vec![self.fuzzel_menu.id()];
-        if self.fuzzel_menu.selected == FONT_OTHER {
-            fuzzel.push(self.fuzzel_box.id());
-        }
-        fuzzel.push(self.fuzzel_size_box.id());
-        let mut terminal = vec![self.terminal_menu.id()];
-        if self.terminal_menu.selected == FONT_OTHER {
-            terminal.push(self.terminal_box.id());
-        }
-        terminal.push(self.terminal_size_box.id());
-        vec![
-            vec![self.sans_box.id(), self.serif_box.id(), self.mono_box.id()],
-            borders,
-            status,
-            fuzzel,
-            terminal,
-        ]
-    }
-
-    // Not every section widget passes through `render_widget`'s registration side
-    // effect (the menus draw custom); the id-rooted router needs them all resolvable.
-    //
-    // The four "Other" boxes carry the same gate as `section_widgets` above and
-    // the view below. Registering one the view did not paint does not merely
-    // waste a slot — it resurrects a widget at its last-drawn rect, ahead of the
-    // live widgets that now occupy that space in the dispatch order.
-    fn register_extra_dispatch_roots(&mut self, ctx: &mut cce_ui::context::UiContext) {
-        let (id, ptr) = (self.sans_box.id(), self.sans_box.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        let (id, ptr) = (self.serif_box.id(), self.serif_box.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        let (id, ptr) = (self.mono_box.id(), self.mono_box.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        let (id, ptr) = (self.borders_menu.id(), self.borders_menu.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        if self.borders_menu.selected == FONT_OTHER {
-            let (id, ptr) = (self.borders_box.id(), self.borders_box.as_ptr_mut());
-            ctx.register_widget(id, ptr);
-        }
-        let (id, ptr) = (self.status_menu.id(), self.status_menu.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        if self.status_menu.selected == FONT_OTHER {
-            let (id, ptr) = (self.status_box.id(), self.status_box.as_ptr_mut());
-            ctx.register_widget(id, ptr);
-        }
-        let (id, ptr) = (self.fuzzel_menu.id(), self.fuzzel_menu.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        if self.fuzzel_menu.selected == FONT_OTHER {
-            let (id, ptr) = (self.fuzzel_box.id(), self.fuzzel_box.as_ptr_mut());
-            ctx.register_widget(id, ptr);
-        }
-        let (id, ptr) = (self.fuzzel_size_box.id(), self.fuzzel_size_box.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        let (id, ptr) = (self.terminal_menu.id(), self.terminal_menu.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-        if self.terminal_menu.selected == FONT_OTHER {
-            let (id, ptr) = (self.terminal_box.id(), self.terminal_box.as_ptr_mut());
-            ctx.register_widget(id, ptr);
-        }
-        let (id, ptr) = (self.terminal_size_box.id(), self.terminal_size_box.as_ptr_mut());
-        ctx.register_widget(id, ptr);
-    }
-
-    fn view(
-        &mut self,
-        cx: f32,
-        cy: f32,
-        cw: f32,
-        ch: f32,
-        _root_focused: bool,
-        sec_focused: &[bool],
-        layout: &mut dyn LayoutStrategy,
-        ctx: &mut cce_ui::context::UiContext,
-    ) -> PageContent {
-        let mut final_pc = PageContent::new();
-        let sec_w = 260.0f32;
-        let mut builder = cce_ui::layout::PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(5);
-
-        builder.add_section(&mut final_pc, "Preferred Fonts", sec_focused.get(0).copied().unwrap_or(false), |sec| {
-            let mut stack = sec.vstack(8.0);
-            let sec_w = stack.context.cw;
-            stack.add_widget(&mut self.sans_box, sec_w - 28.0, 44.0, ctx);
-            stack.add_widget(&mut self.serif_box, sec_w - 28.0, 44.0, ctx);
-            stack.add_widget(&mut self.mono_box, sec_w - 28.0, 44.0, ctx);
-        });
-
-        builder.add_section(&mut final_pc, "Borders", sec_focused.get(1).copied().unwrap_or(false), |sec| {
-            let mut stack = sec.vstack(8.0);
-            let sec_w = stack.context.cw;
-            stack.add_widget(&mut self.borders_menu, sec_w - 28.0, 44.0, ctx);
-            if self.borders_menu.selected == FONT_OTHER {
-                stack.add_widget(&mut self.borders_box, sec_w - 28.0, 44.0, ctx);
-            }
-        });
-
-        builder.add_section(&mut final_pc, "Status Interface", sec_focused.get(2).copied().unwrap_or(false), |sec| {
-            let mut stack = sec.vstack(8.0);
-            let sec_w = stack.context.cw;
-            stack.add_widget(&mut self.status_menu, sec_w - 28.0, 44.0, ctx);
-            if self.status_menu.selected == FONT_OTHER {
-                stack.add_widget(&mut self.status_box, sec_w - 28.0, 44.0, ctx);
-            }
-        });
-
-        builder.add_section(&mut final_pc, "Fuzzel", sec_focused.get(3).copied().unwrap_or(false), |sec| {
-            let mut stack = sec.vstack(8.0);
-            let sec_w = stack.context.cw;
-            stack.add_widget(&mut self.fuzzel_menu, sec_w - 28.0, 44.0, ctx);
-            if self.fuzzel_menu.selected == FONT_OTHER {
-                stack.add_widget(&mut self.fuzzel_box, sec_w - 28.0, 44.0, ctx);
-            }
-            stack.add_widget(&mut self.fuzzel_size_box, sec_w - 28.0, 44.0, ctx);
-        });
-
-        builder.add_section(&mut final_pc, "Terminal", sec_focused.get(4).copied().unwrap_or(false), |sec| {
-            let mut stack = sec.vstack(8.0);
-            let sec_w = stack.context.cw;
-            stack.add_widget(&mut self.terminal_menu, sec_w - 28.0, 44.0, ctx);
-            if self.terminal_menu.selected == FONT_OTHER {
-                stack.add_widget(&mut self.terminal_box, sec_w - 28.0, 44.0, ctx);
-            }
-            stack.add_widget(&mut self.terminal_size_box, sec_w - 28.0, 44.0, ctx);
-        });
-
-        final_pc
-    }
-
-    fn propagate_widget_changes(&mut self, actions: &mut Vec<AppAction>) {
-        if self.sans_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetSans(self.sans_box.text.clone())));
-        }
-        if self.serif_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetSerif(self.serif_box.text.clone())));
-        }
-        if self.mono_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetMono(self.mono_box.text.clone())));
-        }
-        if self.borders_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetBorders(self.borders_box.text.clone())));
-        }
-        if self.status_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetStatus(self.status_box.text.clone())));
-        }
-        if self.fuzzel_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetFuzzel(self.fuzzel_box.text.clone())));
-        }
-        if self.terminal_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetTerminal(self.terminal_box.text.clone())));
-        }
-
-        if self.borders_menu.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetBordersIdx(self.borders_menu.selected)));
-        }
-        if self.status_menu.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetStatusIdx(self.status_menu.selected)));
-        }
-        if self.fuzzel_menu.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetFuzzelIdx(self.fuzzel_menu.selected)));
-        }
-        if self.terminal_menu.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetTerminalIdx(self.terminal_menu.selected)));
-        }
-
-        if self.fuzzel_size_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetFuzzelSize(self.fuzzel_size_box.value as u16)));
-        }
-        if self.terminal_size_box.take_change() {
-            actions.push(AppAction::Fonts(FontsMessage::SetTerminalSize(self.terminal_size_box.value as u16)));
-        }
-    }
-}
-
-fn parse_font_for_alias(content: &str, alias: &str) -> Option<String> {
-    let lines: Vec<&str> = content.lines().collect();
-    for i in 0..lines.len() {
-        let line = lines[i].trim();
-        if line.contains("<test") && line.contains("name=\"family\"") && line.contains(&format!("<string>{}</string>", alias)) {
-            for j in (i + 1)..(i + 6).min(lines.len()) {
-                let next_line = lines[j].trim();
-                if next_line.contains("<edit") {
-                    for k in (j + 1)..(j + 6).min(lines.len()) {
-                        let str_line = lines[k].trim();
-                        if str_line.contains("<string>") && str_line.contains("</string>") {
-                            if let Some(start) = str_line.find("<string>") {
-                                if let Some(end) = str_line.find("</string>") {
-                                    let font = &str_line[start + 8..end];
-                                    return Some(font.to_string());
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-    None
-}
-
-pub fn read_preferred_fonts() -> (String, String, String, String, String, String, String) {
-    let content = fs::read_to_string(&fonts_conf_path() ).unwrap_or_default();
-    
-    let sans = parse_font_for_alias(&content, "sans-serif").unwrap_or_else(|| "Noto Sans".to_string());
-    let serif = parse_font_for_alias(&content, "serif").unwrap_or_else(|| "Noto Serif".to_string());
-    let mono = parse_font_for_alias(&content, "monospace").unwrap_or_else(|| "Noto Sans Mono".to_string());
-    let borders = parse_font_for_alias(&content, "window-borders").unwrap_or_else(|| "Noto Sans".to_string());
-    let status = parse_font_for_alias(&content, "status-interface").unwrap_or_else(|| "Noto Sans".to_string());
-    let fuzzel_font = parse_font_for_alias(&content, "fuzzel").unwrap_or_else(|| "Noto Sans".to_string());
-    let term = parse_font_for_alias(&content, "terminal").unwrap_or_else(|| "Noto Sans Mono".to_string());
-    
-    (sans, serif, mono, borders, status, fuzzel_font, term)
-}
-
-pub fn save_preferred_fonts(
-    sans: &str,
-    serif: &str,
-    mono: &str,
-    borders: &str,
-    status: &str,
-    fuzzel: &str,
-    terminal: &str,
-) {
-    let content = fs::read_to_string(&fonts_conf_path() ).unwrap_or_default();
-    let mut dirs = Vec::new();
-    for line in content.lines() {
-        let trimmed = line.trim();
-        if trimmed.starts_with("<dir>") && trimmed.ends_with("</dir>") {
-            dirs.push(trimmed.to_string());
-        }
-    }
-    if dirs.is_empty() {
-        dirs.push("<dir>~/Dropbox/Fonts</dir>".to_string());
-    }
-    
-    let mut new_content = String::new();
-    new_content.push_str("<?xml version=\"1.0\"?>\n");
-    new_content.push_str("<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n");
-    new_content.push_str("<fontconfig>\n");
-    
-    for dir in dirs {
-        new_content.push_str(&format!("    {}\n", dir));
-    }
-    
-    // Sans-Serif
-    new_content.push_str("    <match target=\"pattern\">\n");
-    new_content.push_str("        <test qual=\"any\" name=\"family\"><string>sans-serif</string></test>\n");
-    new_content.push_str("        <edit name=\"family\" mode=\"assign\" binding=\"same\">\n");
-    new_content.push_str(&format!("            <string>{}</string>\n", sans));
-    new_content.push_str("        </edit>\n");
-    new_content.push_str("    </match>\n");
-    
-    // Serif
-    new_content.push_str("    <match target=\"pattern\">\n");
-    new_content.push_str("        <test qual=\"any\" name=\"family\"><string>serif</string></test>\n");
-    new_content.push_str("        <edit name=\"family\" mode=\"assign\" binding=\"same\">\n");
-    new_content.push_str(&format!("            <string>{}</string>\n", serif));
-    new_content.push_str("        </edit>\n");
-    new_content.push_str("    </match>\n");
-    
-    // Monospace
-    new_content.push_str("    <match target=\"pattern\">\n");
-    new_content.push_str("        <test qual=\"any\" name=\"family\"><string>monospace</string></test>\n");
-    new_content.push_str("        <edit name=\"family\" mode=\"assign\" binding=\"same\">\n");
-    new_content.push_str(&format!("            <string>{}</string>\n", mono));
-    new_content.push_str("        </edit>\n");
-    new_content.push_str("    </match>\n");
-    
-    // Window Borders
-    new_content.push_str("    <match target=\"pattern\">\n");
-    new_content.push_str("        <test qual=\"any\" name=\"family\"><string>window-borders</string></test>\n");
-    new_content.push_str("        <edit name=\"family\" mode=\"assign\" binding=\"same\">\n");
-    new_content.push_str(&format!("            <string>{}</string>\n", borders));
-    new_content.push_str("        </edit>\n");
-    new_content.push_str("    </match>\n");
-    
-    // Status Interface
-    new_content.push_str("    <match target=\"pattern\">\n");
-    new_content.push_str("        <test qual=\"any\" name=\"family\"><string>status-interface</string></test>\n");
-    new_content.push_str("        <edit name=\"family\" mode=\"assign\" binding=\"same\">\n");
-    new_content.push_str(&format!("            <string>{}</string>\n", status));
-    new_content.push_str("        </edit>\n");
-    new_content.push_str("    </match>\n");
-    
-    // Fuzzel
-    new_content.push_str("    <match target=\"pattern\">\n");
-    new_content.push_str("        <test qual=\"any\" name=\"family\"><string>fuzzel</string></test>\n");
-    new_content.push_str("        <edit name=\"family\" mode=\"assign\" binding=\"same\">\n");
-    new_content.push_str(&format!("            <string>{}</string>\n", fuzzel));
-    new_content.push_str("        </edit>\n");
-    new_content.push_str("    </match>\n");
-    
-    // Terminal
-    new_content.push_str("    <match target=\"pattern\">\n");
-    new_content.push_str("        <test qual=\"any\" name=\"family\"><string>terminal</string></test>\n");
-    new_content.push_str("        <edit name=\"family\" mode=\"assign\" binding=\"same\">\n");
-    new_content.push_str(&format!("            <string>{}</string>\n", terminal));
-    new_content.push_str("        </edit>\n");
-    new_content.push_str("    </match>\n");
-    
-    new_content.push_str("</fontconfig>\n");
-    let _ = fs::write(&fonts_conf_path() , new_content);
-}
-
-fn read_fuzzel_size() -> Option<u16> {
-    let ini = fs::read_to_string(&fuzzel_ini_path() ).ok()?;
-    for line in ini.lines() {
-        let trimmed = line.trim();
-        if trimmed.starts_with("font") {
-            if let Some(pos) = trimmed.find("size=") {
-                let size_str = &trimmed[pos + 5..];
-                let end_pos = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
-                return size_str[..end_pos].parse::<u16>().ok();
-            }
-        }
-    }
-    None
-}
-
-fn write_fuzzel_size(size: u16) {
-    let path = &fuzzel_ini_path() ;
-    let ini = fs::read_to_string(path).unwrap_or_default();
-    let mut new_lines = Vec::new();
-    for line in ini.lines() {
-        let trimmed = line.trim();
-        if trimmed.starts_with("font") {
-            if let Some(pos) = line.find("size=") {
-                let mut new_line = line[..pos + 5].to_string();
-                new_line.push_str(&size.to_string());
-                let size_str = &line[pos + 5..];
-                let skip = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
-                new_line.push_str(&size_str[skip..]);
-                new_lines.push(new_line);
-            } else {
-                new_lines.push(line.to_string());
-            }
-        } else {
-            new_lines.push(line.to_string());
-        }
-    }
-    let _ = fs::write(path, new_lines.join("\n"));
-}
-
-fn read_terminal_size() -> Option<u16> {
-    let ini = fs::read_to_string(&foot_ini_path() ).ok()?;
-    for line in ini.lines() {
-        let trimmed = line.trim();
-        if trimmed.starts_with("font") {
-            if let Some(pos) = trimmed.find("terminal:size=") {
-                let size_str = &trimmed[pos + 14..];
-                let end_pos = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
-                return size_str[..end_pos].parse::<u16>().ok();
-            }
-        }
-    }
-    None
-}
-
-fn write_terminal_size(size: u16) {
-    let path = &foot_ini_path() ;
-    let ini = fs::read_to_string(path).unwrap_or_default();
-    let mut new_lines = Vec::new();
-    for line in ini.lines() {
-        let trimmed = line.trim();
-        if trimmed.starts_with("font") {
-            if let Some(pos) = line.find("terminal:size=") {
-                let mut new_line = line[..pos + 14].to_string();
-                new_line.push_str(&size.to_string());
-                let size_str = &line[pos + 14..];
-                let skip = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
-                new_line.push_str(&size_str[skip..]);
-                new_lines.push(new_line);
-            } else {
-                new_lines.push(line.to_string());
-            }
-        } else {
-            new_lines.push(line.to_string());
-        }
-    }
-    let _ = fs::write(path, new_lines.join("\n"));
-}
-
-pub async fn fetch_typeface_state() -> FontsState {
-    read_typeface_config()
-}
-
-fn determine_dropdown_index(val: &str, sans: &str, serif: &str, mono: &str) -> usize {
-    if val == sans { 0 }
-    else if val == serif { 1 }
-    else if val == mono { 2 }
-    else { 3 }
-}
-
-fn determine_font_from_index(idx: usize, state: &FontsState) -> String {
-    match idx {
-        0 => state.sans_serif.clone(),
-        1 => state.serif.clone(),
-        2 => state.monospace.clone(),
-        _ => "Noto Sans".to_string(),
-    }
-}
-
-pub fn read_typeface_config() -> FontsState {
-    let (sans, serif, mono, borders, status, fuzzel_font, term) = read_preferred_fonts();
-    
-    let font_dirs = vec![
-        "/usr/share/fonts".to_string(),
-        cce_ui::fonts_dir(),
-    ];
-    let mut all_fonts = Vec::new();
-    let mut mono_fonts = Vec::new();
-    for d in font_dirs {
-        if let Ok(entries) = fs::read_dir(d) {
-            for entry in entries.flatten() {
-                if let Some(name) = entry.file_name().to_str() {
-                    if name.ends_with(".ttf") || name.ends_with(".otf") {
-                        let name_clean = name.trim_end_matches(".ttf").trim_end_matches(".otf").replace("-", " ");
-                        all_fonts.push(name_clean.clone());
-                        if name_clean.to_lowercase().contains("mono") {
-                            mono_fonts.push(name_clean);
-                        }
-                    }
-                }
-            }
-        }
-    }
-    all_fonts.sort();
-    all_fonts.dedup();
-    mono_fonts.sort();
-    mono_fonts.dedup();
-
-    let borders_idx = determine_dropdown_index(&borders, &sans, &serif, &mono);
-    let status_idx = determine_dropdown_index(&status, &sans, &serif, &mono);
-    let fuzzel_idx = determine_dropdown_index(&fuzzel_font, &sans, &serif, &mono);
-    let terminal_idx = determine_dropdown_index(&term, &sans, &serif, &mono);
-    let menu_options = vec![
-        "Sans-Serif".to_string(),
-        "Serif".to_string(),
-        "Monospace".to_string(),
-        "Other".to_string(),
-    ];
-
-    let mut borders_box = TextBox::new(borders.clone()).with_label("Window Borders").with_config(&fonts_conf_path() , "window_borders");
-    borders_box.disabled = borders_idx != 3;
-
-    let mut status_box = TextBox::new(status.clone()).with_label("Status Interface").with_config(&fonts_conf_path() , "status_interface");
-    status_box.disabled = status_idx != 3;
-
-    let mut fuzzel_box = TextBox::new(fuzzel_font.clone()).with_label("Fuzzel").with_config(&fonts_conf_path() , "fuzzel");
-    fuzzel_box.disabled = fuzzel_idx != 3;
-
-    let mut terminal_box = TextBox::new(term.clone()).with_label("Terminal").with_config(&fonts_conf_path() , "terminal");
-    terminal_box.disabled = terminal_idx != 3;
-
-    let fuzzel_size = read_fuzzel_size().unwrap_or(14);
-    let terminal_size = read_terminal_size().unwrap_or(12);
-
-    let mut state = FontsState::default();
-    state.typeface_loaded = true;
-    state.sans_serif = sans.clone();
-    state.serif = serif.clone();
-    state.monospace = mono.clone();
-    state.window_borders = borders;
-    state.status_interface = status;
-    state.fuzzel = fuzzel_font;
-    state.terminal = term;
-    state.all_fonts = all_fonts;
-    state.mono_fonts = mono_fonts;
-    state.sans_box = TextBox::new(sans).with_label("Sans-Serif").with_config(&fonts_conf_path() , "sans-serif");
-    state.serif_box = TextBox::new(serif).with_label("Serif").with_config(&fonts_conf_path() , "serif");
-    state.mono_box = TextBox::new(mono).with_label("Monospace").with_config(&fonts_conf_path() , "monospace");
-    state.borders_box = borders_box;
-    state.status_box = status_box;
-    state.fuzzel_box = fuzzel_box;
-    state.terminal_box = terminal_box;
-    state.borders_menu = Dropdown::new(menu_options.clone(), borders_idx).with_config(&fonts_conf_path() , "window_borders");
-    state.status_menu = Dropdown::new(menu_options.clone(), status_idx).with_config(&fonts_conf_path() , "status_interface");
-    state.fuzzel_menu = Dropdown::new(menu_options.clone(), fuzzel_idx).with_config(&fonts_conf_path() , "fuzzel");
-    state.terminal_menu = Dropdown::new(menu_options, terminal_idx).with_config(&fonts_conf_path() , "terminal");
-    state.fuzzel_size_box = Spinbox::new(fuzzel_size as i32, 6, 72, 1).with_config(&fuzzel_ini_path() , "size");
-    state.terminal_size_box = Spinbox::new(terminal_size as i32, 6, 72, 1).with_config(&foot_ini_path() , "size");
-    state
-}
-
-pub fn update(state: &mut FontsState, msg: FontsMessage) {
-    match msg {
-        FontsMessage::TypefaceRefreshed(refreshed) | FontsMessage::Refreshed(refreshed) => {
-            state.typeface_loaded = refreshed.typeface_loaded;
-            state.sans_serif = refreshed.sans_serif;
-            state.serif = refreshed.serif;
-            state.monospace = refreshed.monospace;
-            state.window_borders = refreshed.window_borders;
-            state.status_interface = refreshed.status_interface;
-            state.fuzzel = refreshed.fuzzel;
-            state.terminal = refreshed.terminal;
-            state.all_fonts = refreshed.all_fonts;
-            state.mono_fonts = refreshed.mono_fonts;
-        }
-        FontsMessage::SetSans(font) => {
-            state.sans_serif = font;
-            save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-        }
-        FontsMessage::SetSerif(font) => {
-            state.serif = font;
-            save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-        }
-        FontsMessage::SetMono(font) => {
-            state.monospace = font;
-            save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-        }
-        FontsMessage::SetBorders(font) => {
-            state.window_borders = font;
-            save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-        }
-        FontsMessage::SetStatus(font) => {
-            state.status_interface = font;
-            save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-        }
-        FontsMessage::SetFuzzel(font) => {
-            state.fuzzel = font;
-            save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-        }
-        FontsMessage::SetTerminal(font) => {
-            state.terminal = font;
-            save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-        }
-        FontsMessage::SetBordersIdx(idx) => {
-            state.borders_menu.selected = idx;
-            state.borders_box.disabled = idx != 3;
-            if idx < 3 {
-                state.window_borders = determine_font_from_index(idx, state);
-                state.borders_box.set_value_string(&state.window_borders);
-                save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-            }
-        }
-        FontsMessage::SetStatusIdx(idx) => {
-            state.status_menu.selected = idx;
-            state.status_box.disabled = idx != 3;
-            if idx < 3 {
-                state.status_interface = determine_font_from_index(idx, state);
-                state.status_box.set_value_string(&state.status_interface);
-                save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-            }
-        }
-        FontsMessage::SetFuzzelIdx(idx) => {
-            state.fuzzel_menu.selected = idx;
-            state.fuzzel_box.disabled = idx != 3;
-            if idx < 3 {
-                state.fuzzel = determine_font_from_index(idx, state);
-                state.fuzzel_box.set_value_string(&state.fuzzel);
-                save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-            }
-        }
-        FontsMessage::SetTerminalIdx(idx) => {
-            state.terminal_menu.selected = idx;
-            state.terminal_box.disabled = idx != 3;
-            if idx < 3 {
-                state.terminal = determine_font_from_index(idx, state);
-                state.terminal_box.set_value_string(&state.terminal);
-                save_preferred_fonts(&state.sans_serif, &state.serif, &state.monospace, &state.window_borders, &state.status_interface, &state.fuzzel, &state.terminal);
-            }
-        }
-        FontsMessage::SetFuzzelSize(size) => {
-            write_fuzzel_size(size);
-        }
-        FontsMessage::SetTerminalSize(size) => {
-            write_terminal_size(size);
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn section_widgets_gate_other_boxes() {
-        let mut st = FontsState::default();
-        // Presets selected: the four free-text boxes are not painted, so the
-        // menu is the section's only reported root (plus the size spinbox where
-        // the section has one).
-        let g = st.section_widgets();
-        assert_eq!(g[1].len(), 1, "borders: menu only");
-        assert_eq!(g[2].len(), 1, "status: menu only");
-        assert_eq!(g[3].len(), 2, "fuzzel: menu + size");
-        assert_eq!(g[4].len(), 2, "terminal: menu + size");
-
-        st.borders_menu.selected = FONT_OTHER;
-        st.fuzzel_menu.selected = FONT_OTHER;
-        let g = st.section_widgets();
-        assert_eq!(g[1].len(), 2, "borders: menu + box");
-        assert_eq!(g[2].len(), 1, "status untouched");
-        assert_eq!(g[3].len(), 3, "fuzzel: menu + box + size");
-        assert_eq!(g[4].len(), 2, "terminal untouched");
-    }
-
-    #[test]
-    fn section_group_count_matches_the_view() {
-        // The outer length picks each section's `sec_focused` index, so it has
-        // to track the five `add_section` calls in the view.
-        assert_eq!(FontsState::default().section_widgets().len(), 5);
-    }
-}
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index d633337..d7bb0bd 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -8,7 +8,6 @@ pub mod system_info;
 pub mod timers;
 pub mod processes;
 pub mod services;
-pub mod fonts;
 pub mod accounts;
 pub mod packages;
 pub mod notifications;
@@ -29,18 +28,16 @@ pub enum Page {
     Timers,
     Processes,
     Services,
-    Fonts,
     Packages,
 }
 
 impl Page {
-    pub const ALL: [Page; 15] = [
+    pub const ALL: [Page; 14] = [
         Page::Accounts,
         Page::Audio,
         Page::Bluetooth,
         Page::Browser,
         Page::DefaultApps,
-        Page::Fonts,
         Page::Network,
         Page::Notifications,
         Page::Packages,
@@ -67,7 +64,6 @@ impl Page {
             Page::Timers => "Timers",
             Page::Processes => "Processes",
             Page::Services => "Services",
-            Page::Fonts => "Fonts",
             Page::Packages => "Packages",
         }
     }
diff --git a/src/watchers.rs b/src/watchers.rs
index 0299fc2..b62dc5a 100644
--- a/src/watchers.rs
+++ b/src/watchers.rs
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 use std::sync::atomic::{AtomicU8, Ordering};
 use std::sync::mpsc::{channel, Receiver, Sender};
-use crate::pages::{Page, audio, bluetooth, browser, default_apps, network, fonts, processes, services, system_info, storage, packages, accounts, notifications, timers, power};
+use crate::pages::{Page, audio, bluetooth, browser, default_apps, network, processes, services, system_info, storage, packages, accounts, notifications, timers, power};
 
 pub struct Watchers {
     pub rx_audio: Receiver<audio::AudioState>,
@@ -16,7 +16,6 @@ pub struct Watchers {
     pub rx_services: Receiver<Vec<services::ServiceInfo>>,
     pub rx_default_apps: Receiver<default_apps::DefaultAppsInfo>,
     pub rx_timers: Receiver<Vec<timers::TimerInfo>>,
-    pub rx_fonts: Receiver<fonts::FontsState>,
     pub rx_accounts: Receiver<Vec<accounts::AccountInfo>>,
     pub rx_packages: Receiver<packages::PackagesState>,
 }
@@ -125,7 +124,6 @@ pub fn spawn_all(
         rx
     };
 
-    let rx_fonts = spawn_bg_active(current_page_shared.clone(), Page::Fonts.index() as u8, 30, || fonts::fetch_typeface_state());
     let rx_services = spawn_bg_active(current_page_shared.clone(), Page::Services.index() as u8, 3, || services::fetch_services());
     let rx_default_apps = spawn_bg_active(current_page_shared.clone(), Page::DefaultApps.index() as u8, 10, || default_apps::fetch_default_apps());
     let rx_timers = spawn_bg_active(current_page_shared.clone(), Page::Timers.index() as u8, 5, || timers::fetch_timers());
@@ -149,7 +147,6 @@ pub fn spawn_all(
             rx_services,
             rx_default_apps,
             rx_timers,
-            rx_fonts,
             rx_accounts,
             rx_packages,
         },