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

commit5c134bca73527cc1a1f1182946afb19898f2b518
parent3ed99e89c2
authorLucas Galante <[email protected]>
date2026-05-26 20:18
Refactor Processors settings page to Hardware and optimize typeface layout

 src/app.rs                               |  12 +-
 src/main.rs                              | 435 +++++++++++++++++++++-----
 src/pages/{processors.rs => hardware.rs} |  22 +-
 src/pages/mod.rs                         |   8 +-
 src/pages/services.rs                    | 230 +-------------
 src/pages/typeface.rs                    | 519 +++++++++++++++----------------
 6 files changed, 643 insertions(+), 583 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index c3e0548..0647030 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -7,7 +7,7 @@ use crate::pages::layout;
 use crate::pages::network;
 use crate::pages::notifications;
 use crate::pages::power;
-use crate::pages::processors;
+use crate::pages::hardware;
 use crate::pages::status;
 use crate::pages::storage;
 use crate::pages::system_info;
@@ -25,7 +25,7 @@ pub struct AppState {
     pub network: network::NetworkState,
     pub layout: layout::LayoutState,
     pub input: input::InputState,
-    pub processors: processors::ProcessorsState,
+    pub hardware: hardware::HardwareState,
     pub system_info: system_info::SystemState,
     pub status: status::StatusState,
     pub storage: storage::StorageState,
@@ -46,7 +46,7 @@ impl Default for AppState {
             network: network::NetworkState::default(),
             layout: layout::LayoutState::default(),
             input: input::InputState::default(),
-            processors: processors::ProcessorsState::default(),
+            hardware: hardware::HardwareState::default(),
             system_info: system_info::SystemState::default(),
             status: status::StatusState::default(),
             storage: storage::StorageState::default(),
@@ -67,7 +67,7 @@ pub enum AppAction {
     Radios(network::NetworkMessage),
     Layout(layout::LayoutMessage),
     Input(input::InputMessage),
-    Processors(processors::ProcessorsMessage),
+    Hardware(hardware::HardwareMessage),
     SystemInfo(system_info::SystemMessage),
     Status(status::StatusMessage),
     Storage(storage::StorageMessage),
@@ -144,4 +144,8 @@ impl RenderTarget for PageContent {
     fn text(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4]) {
         self.texts.push((content.to_string(), size, x, y, color, None));
     }
+
+    fn text_with_font(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4], font: &str) {
+        self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string())));
+    }
 }
diff --git a/src/main.rs b/src/main.rs
index 3cb4510..c457c19 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -83,12 +83,72 @@ fn make_text_buffer(fs: &mut FontSystem, text: &str, size: f32) -> Buffer {
     buf
 }
 
-fn make_text_buffer_with_font(fs: &mut FontSystem, text: &str, size: f32, font: Option<&str>) -> Buffer {
+fn find_cased_family(fs: &FontSystem, name: &str) -> Option<String> {
+    let lower_name = name.to_lowercase();
+    for face in fs.db().faces() {
+        for (family, _) in &face.families {
+            if family.to_lowercase() == lower_name {
+                return Some(family.clone());
+            }
+        }
+    }
+    None
+}
+
+fn make_text_buffer_with_font(
+    fs: &mut FontSystem,
+    text: &str,
+    size: f32,
+    font: Option<&str>,
+    sans_fallback: &str,
+    serif_fallback: &str,
+    mono_fallback: &str,
+) -> Buffer {
     let metrics = Metrics::new(size, size * 1.4);
     let mut buf = Buffer::new(fs, metrics);
     let mut attrs = Attrs::new();
+    let mut resolved_storage = None;
     if let Some(font_name) = font {
-        attrs = attrs.family(glyphon::Family::Name(font_name));
+        let family = match font_name {
+            "monospace" => {
+                if !mono_fallback.is_empty() {
+                    resolved_storage = find_cased_family(fs, mono_fallback);
+                    if let Some(ref cased) = resolved_storage {
+                        glyphon::Family::Name(cased)
+                    } else {
+                        glyphon::Family::Name(mono_fallback)
+                    }
+                } else {
+                    glyphon::Family::Monospace
+                }
+            }
+            "sans-serif" => {
+                if !sans_fallback.is_empty() {
+                    resolved_storage = find_cased_family(fs, sans_fallback);
+                    if let Some(ref cased) = resolved_storage {
+                        glyphon::Family::Name(cased)
+                    } else {
+                        glyphon::Family::Name(sans_fallback)
+                    }
+                } else {
+                    glyphon::Family::SansSerif
+                }
+            }
+            "serif" => {
+                if !serif_fallback.is_empty() {
+                    resolved_storage = find_cased_family(fs, serif_fallback);
+                    if let Some(ref cased) = resolved_storage {
+                        glyphon::Family::Name(cased)
+                    } else {
+                        glyphon::Family::Name(serif_fallback)
+                    }
+                } else {
+                    glyphon::Family::Serif
+                }
+            }
+            _ => glyphon::Family::Name(font_name),
+        };
+        attrs = attrs.family(family);
     }
     buf.set_text(fs, text, attrs, glyphon::Shaping::Advanced);
     buf.shape_until_scroll(fs, true);
@@ -158,7 +218,7 @@ struct SystemInterface {
     rx_layout: std::sync::mpsc::Receiver<pages::layout::LayoutState>,
     rx_input: std::sync::mpsc::Receiver<pages::input::InputState>,
     rx_fingers: std::sync::mpsc::Receiver<Vec<pages::input::Finger>>,
-    rx_processors: std::sync::mpsc::Receiver<pages::processors::ProcessorsState>,
+    rx_hardware: std::sync::mpsc::Receiver<pages::hardware::HardwareState>,
     rx_system: std::sync::mpsc::Receiver<pages::system_info::SystemState>,
     rx_status: std::sync::mpsc::Receiver<pages::status::StatusState>,
     rx_storage: std::sync::mpsc::Receiver<pages::storage::StorageState>,
@@ -180,6 +240,9 @@ struct SystemInterface {
     max_scroll_y: f32,
     page_root_container: clear_ui::widget::Container,
     page_sec_containers: Vec<clear_ui::widget::Container>,
+    sans_serif_family: String,
+    serif_family: String,
+    monospace_family: String,
 }
 
 impl SystemInterface {
@@ -355,7 +418,7 @@ impl SystemInterface {
             rx
         };
         let rx_system = spawn_bg(5, || pages::system_info::fetch_system_state());
-        let rx_processors = spawn_bg(3, || pages::processors::fetch_processors_state());
+        let rx_hardware = spawn_bg(3, || pages::hardware::fetch_hardware_state());
         let rx_status = spawn_bg(10, || pages::status::fetch_status_state());
         let rx_storage = spawn_bg(10, || pages::storage::fetch_storage_state());
         let rx_notifications = {
@@ -388,6 +451,8 @@ impl SystemInterface {
         let (tx_color_selector, rx_color_selector) = std::sync::mpsc::channel();
         let scale_factor = scale;
 
+        let (sans_family, serif_family, monospace_family, _, _, _, _) = pages::typeface::read_preferred_fonts();
+
         let mut this = Self {
             window, surface, wgpu_surface, device, queue, config, render_pipeline,
             vertex_buffer, vertex_count: 0,
@@ -398,7 +463,7 @@ impl SystemInterface {
             cursor_x: 0.0, cursor_y: 0.0,
             scale_factor,
             rx_power, rx_audio, rx_display, rx_network, rx_layout, rx_input, rx_fingers,
-            rx_processors, rx_system, rx_status, rx_storage, rx_notifications,
+            rx_hardware, rx_system, rx_status, rx_storage, rx_notifications,
             rx_backup_state, rx_typeface, rx_services, rx_colors, tx_backup, rx_backup,
             tx_color_selector, rx_color_selector,
             width, height,
@@ -407,6 +472,9 @@ impl SystemInterface {
             max_scroll_y: 0.0,
             page_root_container: clear_ui::widget::Container::new(),
             page_sec_containers: Vec::new(),
+            sans_serif_family: sans_family,
+            serif_family,
+            monospace_family,
         };
         this.rebuild_layout(width as f32, height as f32);
         this
@@ -498,7 +566,15 @@ impl SystemInterface {
         }
         for (t, size, x, y, tc, font_opt) in &pc.texts {
             text_items.push(TextItem {
-                buffer: make_text_buffer_with_font(&mut self.font_system, t, *size * s, font_opt.as_deref()),
+                buffer: make_text_buffer_with_font(
+                    &mut self.font_system,
+                    t,
+                    *size * s,
+                    font_opt.as_deref(),
+                    &self.sans_serif_family,
+                    &self.serif_family,
+                    &self.monospace_family,
+                ),
                 x: *x * s, y: (*y - scroll_offset_y) * s,
                 color: glyphon::Color::rgb(
                     (tc[0] * 255.0) as u8, (tc[1] * 255.0) as u8, (tc[2] * 255.0) as u8,
@@ -526,8 +602,8 @@ impl SystemInterface {
                        && btn.y >= sb_y - 1.0 && btn.y + btn.h <= sb_y + sb_h + 1.0 {
                         left_align = true;
                     }
-                } else if self.app.current_page == Page::Processors {
-                    let sb = &self.app.processors.cpu_list_box;
+                } else if self.app.current_page == Page::Hardware {
+                    let sb = &self.app.hardware.cpu_list_box;
                     let (sb_x, sb_y, sb_w, sb_h) = sb.rect();
                     if btn.x >= sb_x - 1.0 && btn.x + btn.w <= sb_x + sb_w + 1.0
                        && btn.y >= sb_y - 1.0 && btn.y + btn.h <= sb_y + sb_h + 1.0 {
@@ -587,7 +663,7 @@ impl SystemInterface {
 
         self.app.services.list_box.scroll_box.clear_children(); self.app.services.list_box.scroll_box.set_parent(None);
 
-        self.app.processors.cpu_list_box.scroll_box.clear_children(); self.app.processors.cpu_list_box.scroll_box.set_parent(None);
+        self.app.hardware.cpu_list_box.scroll_box.clear_children(); self.app.hardware.cpu_list_box.scroll_box.set_parent(None);
 
         self.app.network.wifi_list_box.scroll_box.clear_children(); self.app.network.wifi_list_box.scroll_box.set_parent(None);
 
@@ -652,8 +728,8 @@ impl SystemInterface {
                 link_parent_child(&mut self.page_root_container, &mut self.app.services.search_box);
                 link_parent_child(&mut self.page_root_container, &mut self.app.services.list_box.scroll_box);
             }
-            Page::Processors => {
-                link_parent_child(&mut self.page_root_container, &mut self.app.processors.cpu_list_box.scroll_box);
+            Page::Hardware => {
+                link_parent_child(&mut self.page_root_container, &mut self.app.hardware.cpu_list_box.scroll_box);
             }
             Page::Radios => {
                 link_parent_child(&mut self.page_root_container, &mut self.app.network.wifi_list_box.scroll_box);
@@ -726,6 +802,52 @@ impl SystemInterface {
             _ => {}
         }
 
+        // Render context menu overlay if visible
+        if clear_ui::widget::context_menu::is_visible() {
+            let cx = clear_ui::widget::context_menu::x();
+            let cy = clear_ui::widget::context_menu::y();
+            let cw = clear_ui::widget::context_menu::w();
+            let ch = clear_ui::widget::context_menu::h();
+            
+            // Border
+            widgets.push(AppWidget {
+                x: cx * s, y: cy * s, w: cw * s, h: ch * s,
+                color: [0.22, 0.22, 0.28, 1.0], hover_color: [0.22, 0.22, 0.28, 1.0],
+                hovering: false, kind: WidgetKind::Static,
+            });
+            // Bg
+            widgets.push(AppWidget {
+                x: (cx + 1.0) * s, y: (cy + 1.0) * s, w: (cw - 2.0) * s, h: (ch - 2.0) * s,
+                color: [0.06, 0.06, 0.09, 1.0], hover_color: [0.06, 0.06, 0.09, 1.0],
+                hovering: false, kind: WidgetKind::Static,
+            });
+            
+            // Hover highlight
+            if let Some(h_idx) = clear_ui::widget::context_menu::hovered_item() {
+                let iy = cy + h_idx as f32 * 24.0;
+                widgets.push(AppWidget {
+                    x: (cx + 2.0) * s, y: (iy + 2.0) * s, w: (cw - 4.0) * s, h: 20.0 * s,
+                    color: [0.20, 0.40, 0.65, 0.6], hover_color: [0.20, 0.40, 0.65, 0.6],
+                    hovering: false, kind: WidgetKind::Static,
+                });
+            }
+            
+            // Texts
+            for (idx, opt) in clear_ui::widget::context_menu::options().iter().enumerate() {
+                let iy = cy + idx as f32 * 24.0 + (24.0 - 12.0) / 2.0;
+                let text_color = if clear_ui::widget::context_menu::hovered_item() == Some(idx) {
+                    glyphon::Color::rgb(0xff, 0xff, 0xff)
+                } else {
+                    glyphon::Color::rgb(0xcc, 0xcc, 0xd4)
+                };
+                text_items.push(TextItem {
+                    buffer: make_text_buffer(&mut self.font_system, opt, 12.0 * s),
+                    x: (cx + 8.0) * s, y: iy * s,
+                    color: text_color,
+                });
+            }
+        }
+
         self.widgets = widgets;
         self.text_items = text_items;
         self.page_buttons = page_buttons;
@@ -744,7 +866,7 @@ impl SystemInterface {
             Page::Display => display::view(&mut self.app.display, cx, cy, cw, ch),
             Page::Radios => network::view(&mut self.app.network, cx, cy, cw, ch, root_focused),
             Page::Layout => layout::view(&mut self.app.layout, cx, cy, cw, ch, &sec_focused),
-            Page::Processors => processors::view(&mut self.app.processors, cx, cy, cw, ch, root_focused),
+            Page::Hardware => hardware::view(&mut self.app.hardware, cx, cy, cw, ch, root_focused),
             Page::Input => input::view(&mut self.app.input, cx, cy, cw, ch, &sec_focused),
             Page::System => system_info::view(&self.app.system_info, cx, cy, cw, ch),
             Page::Status => status::view(&mut self.app.status, cx, cy, cw, ch),
@@ -852,8 +974,8 @@ impl SystemInterface {
             system_info::update(&mut self.app.system_info, system_info::SystemMessage::Refreshed(s));
             self.needs_rebuild = true;
         }
-        while let Ok(s) = self.rx_processors.try_recv() {
-            processors::update(&mut self.app.processors, processors::ProcessorsMessage::Refreshed(s));
+        while let Ok(s) = self.rx_hardware.try_recv() {
+            hardware::update(&mut self.app.hardware, hardware::HardwareMessage::Refreshed(s));
             self.needs_rebuild = true;
         }
         while let Ok(s) = self.rx_status.try_recv() {
@@ -873,6 +995,9 @@ impl SystemInterface {
             self.needs_rebuild = true;
         }
         while let Ok(s) = self.rx_typeface.try_recv() {
+            self.sans_serif_family = s.sans_serif.clone();
+            self.serif_family = s.serif.clone();
+            self.monospace_family = s.monospace.clone();
             typeface::update(&mut self.app.typeface, typeface::TypefaceMessage::Refreshed(s));
             self.needs_rebuild = true;
         }
@@ -911,11 +1036,16 @@ impl SystemInterface {
             AppAction::Layout(m) => layout::update(&mut self.app.layout, m.clone()),
             AppAction::Input(m) => input::update(&mut self.app.input, m.clone()),
             AppAction::SystemInfo(m) => system_info::update(&mut self.app.system_info, m.clone()),
-            AppAction::Processors(m) => processors::update(&mut self.app.processors, m.clone()),
+            AppAction::Hardware(m) => hardware::update(&mut self.app.hardware, m.clone()),
             AppAction::Status(m) => status::update(&mut self.app.status, m.clone()),
             AppAction::Storage(m) => storage::update(&mut self.app.storage, m.clone()),
             AppAction::Notifications(m) => notifications::update(&mut self.app.notifications, m.clone()),
-            AppAction::Typeface(m) => typeface::update(&mut self.app.typeface, m.clone()),
+            AppAction::Typeface(m) => {
+                typeface::update(&mut self.app.typeface, m.clone());
+                self.sans_serif_family = self.app.typeface.sans_serif.clone();
+                self.serif_family = self.app.typeface.serif.clone();
+                self.monospace_family = self.app.typeface.monospace.clone();
+            }
             AppAction::Services(m) => services::update(&mut self.app.services, m.clone()),
             AppAction::Colors(m) => colors::update(&mut self.app.colors, m.clone()),
             AppAction::Backup(m) => match m {
@@ -936,6 +1066,17 @@ impl SystemInterface {
         self.cursor_x = x;
         self.cursor_y = y;
         let s = self.scale_factor as f32;
+        
+        if clear_ui::widget::context_menu::is_visible() {
+            let lx_no_scroll = x / s;
+            let ly_no_scroll = y / s;
+            if clear_ui::widget::context_menu::cursor_moved(lx_no_scroll, ly_no_scroll) {
+                self.needs_rebuild = true;
+                return true;
+            }
+            return false;
+        }
+
         let lx = self.cursor_x / s;
         let ly = self.cursor_y / s + self.scroll_y;
         let mut changed = false;
@@ -1044,16 +1185,16 @@ impl SystemInterface {
                 changed = true;
             }
         }
-        if self.app.current_page == Page::Processors {
-            if self.app.processors.cpu_label.cursor_moved(lx, ly) {
+        if self.app.current_page == Page::Hardware {
+            if self.app.hardware.cpu_label.cursor_moved(lx, ly) {
                 changed = true;
             }
-            for gpu_lbl in &mut self.app.processors.gpu_labels {
+            for gpu_lbl in &mut self.app.hardware.gpu_labels {
                 if gpu_lbl.cursor_moved(lx, ly) {
                     changed = true;
                 }
             }
-            if self.app.processors.cpu_list_box.cursor_moved(lx, ly) {
+            if self.app.hardware.cpu_list_box.cursor_moved(lx, ly) {
                 changed = true;
             }
         }
@@ -1105,6 +1246,18 @@ impl SystemInterface {
             if self.app.typeface.list_box.cursor_moved(lx, ly) {
                 changed = true;
             }
+            if self.app.typeface.borders_size_box.cursor_moved(lx, ly) {
+                changed = true;
+            }
+            if self.app.typeface.status_size_box.cursor_moved(lx, ly) {
+                changed = true;
+            }
+            if self.app.typeface.fuzzel_size_box.cursor_moved(lx, ly) {
+                changed = true;
+            }
+            if self.app.typeface.terminal_size_box.cursor_moved(lx, ly) {
+                changed = true;
+            }
         }
         if self.app.current_page == Page::Services {
             if self.app.services.search_box.cursor_moved(lx, ly) {
@@ -1119,9 +1272,19 @@ impl SystemInterface {
     }
 
     fn handle_mouse_input(&mut self, button: clear_ui::widget::MouseButton, state: clear_ui::widget::ElementState) -> bool {
-        if button != clear_ui::widget::MouseButton::Left { return false; }
         let s = self.scale_factor as f32;
-        if state == clear_ui::widget::ElementState::Released {
+        let lx_no_scroll = self.cursor_x / s;
+        let ly_no_scroll = self.cursor_y / s;
+
+        if clear_ui::widget::context_menu::is_visible() {
+            if clear_ui::widget::context_menu::mouse_input(button, state, lx_no_scroll, ly_no_scroll) {
+                self.needs_rebuild = true;
+                return true;
+            }
+        }
+
+        if button != clear_ui::widget::MouseButton::Left && button != clear_ui::widget::MouseButton::Right { return false; }
+        if button == clear_ui::widget::MouseButton::Left && state == clear_ui::widget::ElementState::Released {
             let (px, py) = (self.cursor_x, self.cursor_y);
             for btn in &self.page_buttons.clone() {
                 if px >= btn.x && px <= btn.x + btn.w && py >= btn.y && py <= btn.y + btn.h {
@@ -1218,9 +1381,9 @@ impl SystemInterface {
                     if srv.search_box.hit_test(lx, ly) { clicked_any_focusable = true; }
                     if srv.list_box.hit_test(lx, ly) { clicked_any_focusable = true; }
                 }
-                Page::Processors => {
-                    let proc = &mut self.app.processors;
-                    if proc.cpu_list_box.hit_test(lx, ly) { clicked_any_focusable = true; }
+                Page::Hardware => {
+                    let hw = &mut self.app.hardware;
+                    if hw.cpu_list_box.hit_test(lx, ly) { clicked_any_focusable = true; }
                 }
                 Page::Radios => {
                     let net = &mut self.app.network;
@@ -1420,123 +1583,163 @@ impl SystemInterface {
             if !lbl2.hit_test(lx, ly) { lbl2.unfocus(); }
             lbl2.mouse_input(button, state, lx, ly);
         }
-        if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Typefaces {
+        if self.app.current_page == Page::Typefaces {
             let tb = &mut self.app.typeface.sans_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetSans(tb.text.clone())));
             }
 
             let tb = &mut self.app.typeface.serif_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetSerif(tb.text.clone())));
             }
 
             let tb = &mut self.app.typeface.mono_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetMono(tb.text.clone())));
             }
 
             let menu = &mut self.app.typeface.borders_menu;
-            if !menu.hit_test(lx, ly) { menu.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
             if menu.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if menu.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBordersMenu(menu.selected)));
             }
 
             let tb = &mut self.app.typeface.borders_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBorders(tb.text.clone())));
             }
 
             let menu = &mut self.app.typeface.status_menu;
-            if !menu.hit_test(lx, ly) { menu.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
             if menu.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if menu.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatusMenu(menu.selected)));
             }
 
             let tb = &mut self.app.typeface.status_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatus(tb.text.clone())));
             }
 
             let menu = &mut self.app.typeface.fuzzel_menu;
-            if !menu.hit_test(lx, ly) { menu.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
             if menu.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if menu.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzelMenu(menu.selected)));
             }
 
             let tb = &mut self.app.typeface.fuzzel_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzel(tb.text.clone())));
             }
 
             let menu = &mut self.app.typeface.terminal_menu;
-            if !menu.hit_test(lx, ly) { menu.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
             if menu.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if menu.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminalMenu(menu.selected)));
             }
 
             let tb = &mut self.app.typeface.terminal_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminal(tb.text.clone())));
             }
 
             let tb = &mut self.app.typeface.search_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
-            if tb.take_change() {
+            if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
                 actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetSearch(tb.text.clone())));
             }
 
+            let sb = &mut self.app.typeface.borders_size_box;
+            if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old_val = sb.value;
+            if sb.mouse_input(button, state, lx, ly) {
+                self.needs_rebuild = true;
+            }
+            if sb.value != old_val {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBordersSize(sb.value)));
+            }
+
+            let sb = &mut self.app.typeface.status_size_box;
+            if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old_val = sb.value;
+            if sb.mouse_input(button, state, lx, ly) {
+                self.needs_rebuild = true;
+            }
+            if sb.value != old_val {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatusSize(sb.value)));
+            }
+
+            let sb = &mut self.app.typeface.fuzzel_size_box;
+            if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old_val = sb.value;
+            if sb.mouse_input(button, state, lx, ly) {
+                self.needs_rebuild = true;
+            }
+            if sb.value != old_val {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzelSize(sb.value)));
+            }
+
+            let sb = &mut self.app.typeface.terminal_size_box;
+            if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+            let old_val = sb.value;
+            if sb.mouse_input(button, state, lx, ly) {
+                self.needs_rebuild = true;
+            }
+            if sb.value != old_val {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminalSize(sb.value)));
+            }
+
             let tf = &mut self.app.typeface;
             if tf.list_box.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
         }
-        if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Services {
+        if self.app.current_page == Page::Services {
             let tb = &mut self.app.services.search_box;
-            if !tb.hit_test(lx, ly) { tb.unfocus(); }
+            if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
             if tb.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
@@ -1545,9 +1748,9 @@ impl SystemInterface {
                 self.needs_rebuild = true;
             }
         }
-        if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Processors {
-            let proc = &mut self.app.processors;
-            if proc.cpu_list_box.mouse_input(button, state, lx, ly) {
+        if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Hardware {
+            let hw = &mut self.app.hardware;
+            if hw.cpu_list_box.mouse_input(button, state, lx, ly) {
                 self.needs_rebuild = true;
             }
         }
@@ -1588,9 +1791,9 @@ impl SystemInterface {
                     return true;
                 }
             }
-            if self.app.current_page == Page::Processors {
-                let proc = &mut self.app.processors;
-                if proc.cpu_list_box.mouse_wheel(delta, lx, ly) {
+            if self.app.current_page == Page::Hardware {
+                let hw = &mut self.app.hardware;
+                if hw.cpu_list_box.mouse_wheel(delta, lx, ly) {
                     self.needs_rebuild = true;
                     return true;
                 }
@@ -1620,7 +1823,7 @@ impl SystemInterface {
 
     fn get_page_root_widget(&mut self) -> Option<*mut (dyn clear_ui::widget::Widget + 'static)> {
         match self.app.current_page {
-            Page::Typefaces | Page::Services | Page::Processors | Page::Radios |
+            Page::Typefaces | Page::Services | Page::Hardware | Page::Radios |
             Page::Layout | Page::Colors | Page::Notifications | Page::Input |
             Page::Audio | Page::Display => {
                 let ptr = &mut self.page_root_container as &mut dyn clear_ui::widget::Widget as *mut dyn clear_ui::widget::Widget;
@@ -1831,10 +2034,7 @@ impl SystemInterface {
                     (clear_ui::widget::Key::Named(clear_ui::widget::NamedKey::ArrowUp), false) => true,
                     _ => false,
                 };
-                if is_down {
-                    if !clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
-                        return false;
-                    }
+                if is_down && clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
                     let next_idx_font_scroll = {
                         let tf = &self.app.typeface;
                         let query = tf.search_box.text.to_lowercase();
@@ -1875,10 +2075,7 @@ impl SystemInterface {
                         self.needs_rebuild = true;
                         return true;
                     }
-                } else if is_up {
-                    if !clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
-                        return false;
-                    }
+                } else if is_up && clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
                     let next_idx_font_scroll = {
                         let tf = &self.app.typeface;
                         let query = tf.search_box.text.to_lowercase();
@@ -1989,6 +2186,30 @@ impl SystemInterface {
                 }
                 consumed = true;
             }
+
+            let sb = &mut self.app.typeface.borders_size_box;
+            if sb.keyboard_input(event) {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBordersSize(sb.value)));
+                consumed = true;
+            }
+
+            let sb = &mut self.app.typeface.status_size_box;
+            if sb.keyboard_input(event) {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatusSize(sb.value)));
+                consumed = true;
+            }
+
+            let sb = &mut self.app.typeface.fuzzel_size_box;
+            if sb.keyboard_input(event) {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzelSize(sb.value)));
+                consumed = true;
+            }
+
+            let sb = &mut self.app.typeface.terminal_size_box;
+            if sb.keyboard_input(event) {
+                actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminalSize(sb.value)));
+                consumed = true;
+            }
             
             for a in &actions {
                 self.handle_action(a);
@@ -2011,9 +2232,9 @@ impl SystemInterface {
                 return true;
             }
         }
-        if self.app.current_page == Page::Processors {
-            let proc = &mut self.app.processors;
-            if proc.cpu_list_box.keyboard_input(event) {
+        if self.app.current_page == Page::Hardware {
+            let hw = &mut self.app.hardware;
+            if hw.cpu_list_box.keyboard_input(event) {
                 self.needs_rebuild = true;
                 return true;
             }
@@ -2084,6 +2305,29 @@ impl SystemInterface {
     }
 }
 
+struct PressedKey {
+    logical_key: clear_ui::widget::Key,
+    text: Option<String>,
+    first_pressed: std::time::Instant,
+    last_repeated: std::time::Instant,
+}
+
+fn is_repeatable_key(key: &clear_ui::widget::Key) -> bool {
+    use clear_ui::widget::{Key, NamedKey};
+    match key {
+        Key::Named(NamedKey::Backspace) |
+        Key::Named(NamedKey::Delete) |
+        Key::Named(NamedKey::ArrowLeft) |
+        Key::Named(NamedKey::ArrowRight) |
+        Key::Named(NamedKey::ArrowUp) |
+        Key::Named(NamedKey::ArrowDown) |
+        Key::Named(NamedKey::Home) |
+        Key::Named(NamedKey::End) |
+        Key::Character(_) => true,
+        _ => false,
+    }
+}
+
 struct App {
     registry_state: RegistryState,
     compositor_state: CompositorState,
@@ -2104,6 +2348,8 @@ struct App {
     exit: bool,
     redraw: bool,
     ctrl_pressed: bool,
+    shift_pressed: bool,
+    pressed_key: Option<PressedKey>,
 }
 
 
@@ -2342,6 +2588,7 @@ impl KeyboardHandler for App {
         _layout: u32,
     ) {
         self.ctrl_pressed = modifiers.ctrl;
+        self.shift_pressed = modifiers.shift;
     }
 }
 
@@ -2380,8 +2627,28 @@ impl App {
             text: event.utf8.clone(),
             repeat: false,
             ctrl: self.ctrl_pressed,
+            shift: self.shift_pressed,
         };
 
+        if state == clear_ui::widget::ElementState::Pressed {
+            if is_repeatable_key(&custom_event.logical_key) {
+                self.pressed_key = Some(PressedKey {
+                    logical_key: custom_event.logical_key.clone(),
+                    text: custom_event.text.clone(),
+                    first_pressed: std::time::Instant::now(),
+                    last_repeated: std::time::Instant::now(),
+                });
+            } else {
+                self.pressed_key = None;
+            }
+        } else if state == clear_ui::widget::ElementState::Released {
+            if let Some(ref pk) = self.pressed_key {
+                if pk.logical_key == custom_event.logical_key {
+                    self.pressed_key = None;
+                }
+            }
+        }
+
         if let Some(st) = &mut self.state {
             if st.handle_key_input(&custom_event) {
                 self.redraw = true;
@@ -2493,6 +2760,8 @@ fn main() {
         exit: false,
         redraw: true,
         ctrl_pressed: false,
+        shift_pressed: false,
+        pressed_key: None,
     };
 
     // Perform a roundtrip to populate output_state with active output scales
@@ -2520,6 +2789,9 @@ fn main() {
     let loop_handle = event_loop.handle();
     WaylandSource::new(conn, event_queue).insert(loop_handle.clone()).unwrap();
 
+    const KEY_REPEAT_DELAY: std::time::Duration = std::time::Duration::from_millis(500);
+    const KEY_REPEAT_INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
+
     loop {
         event_loop
             .dispatch(std::time::Duration::from_millis(16), &mut app)
@@ -2533,6 +2805,29 @@ fn main() {
                 app.redraw = true;
             }
         }
+
+        if let Some(ref mut pk) = app.pressed_key {
+            let now = std::time::Instant::now();
+            if now.duration_since(pk.first_pressed) >= KEY_REPEAT_DELAY {
+                if now.duration_since(pk.last_repeated) >= KEY_REPEAT_INTERVAL {
+                    pk.last_repeated = now;
+                    let custom_event = clear_ui::widget::KeyEvent {
+                        state: clear_ui::widget::ElementState::Pressed,
+                        logical_key: pk.logical_key.clone(),
+                        text: pk.text.clone(),
+                        repeat: true,
+                        ctrl: app.ctrl_pressed,
+                        shift: app.shift_pressed,
+                    };
+                    if let Some(st) = &mut app.state {
+                        if st.handle_key_input(&custom_event) {
+                            app.redraw = true;
+                        }
+                    }
+                }
+            }
+        }
+
         if app.redraw {
             app.redraw = false;
             if let Some(state) = &mut app.state {
diff --git a/src/pages/processors.rs b/src/pages/hardware.rs
similarity index 95%
rename from src/pages/processors.rs
rename to src/pages/hardware.rs
index a225900..7b0d1f4 100644
--- a/src/pages/processors.rs
+++ b/src/pages/hardware.rs
@@ -3,7 +3,7 @@ use clear_ui::layout::Section;
 use clear_ui::widget::{Label, ScrollingList};
 
 #[derive(Debug, Clone)]
-pub struct ProcessorsState {
+pub struct HardwareState {
     pub cpu_model: String,
     pub cpu_usage: f32,
     pub cpu_cores: u32,
@@ -15,7 +15,7 @@ pub struct ProcessorsState {
     pub cpu_list_box: ScrollingList,
 }
 
-impl Default for ProcessorsState {
+impl Default for HardwareState {
     fn default() -> Self {
         Self {
             cpu_model: String::new(),
@@ -32,8 +32,8 @@ impl Default for ProcessorsState {
 }
 
 #[derive(Debug, Clone)]
-pub enum ProcessorsMessage {
-    Refreshed(ProcessorsState),
+pub enum HardwareMessage {
+    Refreshed(HardwareState),
     None,
 }
 
@@ -95,7 +95,7 @@ async fn read_nvidia_gpu_temp() -> Option<f32> {
     val_str.trim().parse::<f32>().ok()
 }
 
-pub async fn fetch_processors_state() -> ProcessorsState {
+pub async fn fetch_hardware_state() -> HardwareState {
     let (cpu_model, cpu_cores) = {
         let lscpu = tokio::process::Command::new("lscpu")
             .output().await.ok()
@@ -188,7 +188,7 @@ pub async fn fetch_processors_state() -> ProcessorsState {
         Label::new(&text).with_font_size(12.0).with_color([212, 212, 212])
     }).collect();
 
-    ProcessorsState {
+    HardwareState {
         cpu_model,
         cpu_usage,
         cpu_cores,
@@ -204,7 +204,7 @@ pub async fn fetch_processors_state() -> ProcessorsState {
 const TEXT_FG: [f32; 4] = [0.83, 0.83, 0.83, 1.0];
 const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
 
-pub fn view(state: &mut ProcessorsState, cx: f32, cy: f32, cw: f32, _ch: f32, root_focused: bool) -> PageContent {
+pub fn view(state: &mut HardwareState, cx: f32, cy: f32, cw: f32, _ch: f32, root_focused: bool) -> PageContent {
     let mut pc = PageContent::new();
     let mut y = cy + 12.0;
 
@@ -255,7 +255,7 @@ pub fn view(state: &mut ProcessorsState, cx: f32, cy: f32, cw: f32, _ch: f32, ro
                     [0.0, 0.0, 0.0, 0.0],
                     [1.0, 1.0, 1.0, 0.06],
                     [0.0, 0.0, 0.0, 0.0],
-                    crate::app::AppAction::Processors(ProcessorsMessage::None),
+                    crate::app::AppAction::Hardware(HardwareMessage::None),
                 );
                 
                 pc.text(pid, list_box_x + 12.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
@@ -288,9 +288,9 @@ pub fn view(state: &mut ProcessorsState, cx: f32, cy: f32, cw: f32, _ch: f32, ro
     pc
 }
 
-pub fn update(state: &mut ProcessorsState, msg: ProcessorsMessage) {
+pub fn update(state: &mut HardwareState, msg: HardwareMessage) {
     match msg {
-        ProcessorsMessage::Refreshed(new) => {
+        HardwareMessage::Refreshed(new) => {
             state.loaded = new.loaded;
             state.cpu_model = new.cpu_model;
             state.cpu_usage = new.cpu_usage;
@@ -303,6 +303,6 @@ pub fn update(state: &mut ProcessorsState, msg: ProcessorsMessage) {
             state.cpu_list_box = new.cpu_list_box;
             state.cpu_list_box.set_scroll_y(old_scroll);
         }
-        ProcessorsMessage::None => {}
+        HardwareMessage::None => {}
     }
 }
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index 56878b4..ce677a1 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -8,7 +8,7 @@ pub mod system_info;
 pub mod keybindings;
 pub mod input;
 pub mod status;
-pub mod processors;
+pub mod hardware;
 pub mod notifications;
 pub mod backup;
 pub mod typeface;
@@ -25,7 +25,7 @@ pub enum Page {
     Display,
     Layout,
     System,
-    Processors,
+    Hardware,
     Input,
     Status,
     Notifications,
@@ -43,7 +43,7 @@ impl Page {
         Page::Input,
         Page::Layout,
         Page::Notifications,
-        Page::Processors,
+        Page::Hardware,
         Page::Power,
         Page::Radios,
         Page::Services,
@@ -63,7 +63,7 @@ impl Page {
             Page::Display => "Display",
             Page::Layout => "Layout",
             Page::System => "System",
-            Page::Processors => "Processors",
+            Page::Hardware => "Hardware",
             Page::Input => "Input",
             Page::Status => "Status",
             Page::Notifications => "Notifications",
diff --git a/src/pages/services.rs b/src/pages/services.rs
index e7fdb0a..47bdcb6 100644
--- a/src/pages/services.rs
+++ b/src/pages/services.rs
@@ -1,236 +1,8 @@
 use crate::app::PageContent;
 use clear_ui::layout::Section;
-use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList};
+use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, TextBox};
 use clear_ui::widget::{ElementState, KeyEvent, MouseButton, Key, NamedKey};
 
-// ── TextBox Widget ──
-
-#[derive(Debug, Clone)]
-pub struct TextBox {
-    x: f32, y: f32, w: f32, h: f32,
-    pub text: String,
-    pub editing: bool,
-    pub edit_buffer: String,
-    hovered: bool,
-    just_changed: bool,
-    label: Option<String>,
-    row_x: f32,
-    row_w: f32,
-    pub parent: Option<*mut (dyn Widget + 'static)>,
-    pub children: Vec<*mut (dyn Widget + 'static)>,
-}
-
-impl TextBox {
-    pub fn new(text: String) -> Self {
-        Self {
-            x: 0.0, y: 0.0, w: 0.0, h: 0.0,
-            text,
-            editing: false,
-            edit_buffer: String::new(),
-            hovered: false,
-            just_changed: false,
-            label: None,
-            row_x: 0.0,
-            row_w: 0.0,
-            parent: None,
-            children: Vec::new(),
-        }
-    }
-
-    pub fn with_label(mut self, label: &str) -> Self {
-        self.label = Some(label.to_string());
-        self
-    }
-
-    pub fn set_label(&mut self, label: &str) {
-        self.label = Some(label.to_string());
-    }
-
-    pub fn take_change(&mut self) -> bool {
-        let changed = self.just_changed;
-        self.just_changed = false;
-        changed
-    }
-}
-
-impl Default for TextBox {
-    fn default() -> Self {
-        Self::new(String::new())
-    }
-}
-
-impl Widget for TextBox {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-    fn set_row_rect(&mut self, x: f32, w: f32) { self.row_x = x; self.row_w = w; }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-
-    fn color(&self) -> [f32; 4] {
-        [0.10, 0.10, 0.16, 1.0]
-    }
-
-    fn hit_test(&self, px: f32, py: f32) -> bool {
-        let (x, y, w, h) = self.rect();
-        let hx = if self.row_w > 0.0 { self.row_x } else { x };
-        let hw = if self.row_w > 0.0 { self.row_w } else { w };
-        let (hy, hh) = if self.label.is_some() {
-            (y - 18.0, h + 18.0)
-        } else {
-            (y, h)
-        };
-        px >= hx && px <= hx + hw && py >= hy && py <= hy + hh
-    }
-
-    fn top_room(&self) -> f32 { if self.label.is_some() { 18.0 } else { 0.0 } }
-
-    fn cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        let was = self.hovered;
-        self.hovered = self.hit_test(px, py);
-        was != self.hovered
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if button != MouseButton::Left { return false; }
-        if state != ElementState::Pressed { return false; }
-        let (x, y, w, h) = self.rect();
-        let (hy, hh) = if self.label.is_some() {
-            (y - 18.0, h + 18.0)
-        } else {
-            (y, h)
-        };
-        if !(px >= x && px <= x + w && py >= hy && py <= hy + hh) {
-            return false;
-        }
-        self.focus();
-        true
-    }
-
-    fn focus(&mut self) {
-        if !self.editing {
-            self.editing = true;
-            self.edit_buffer = self.text.clone();
-            clear_ui::widget::focus::set_focused(self);
-        }
-    }
-
-    fn unfocus(&mut self) {
-        if self.editing {
-            self.editing = false;
-            if self.text != self.edit_buffer {
-                self.text = self.edit_buffer.clone();
-                self.just_changed = true;
-            }
-        }
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
-        if !self.editing { return false; }
-        if event.state != ElementState::Pressed { return false; }
-        match &event.logical_key {
-            Key::Named(NamedKey::Backspace) => {
-                self.edit_buffer.pop();
-                true
-            }
-            Key::Named(NamedKey::Enter) => {
-                self.text = self.edit_buffer.clone();
-                self.editing = false;
-                self.just_changed = true;
-                true
-            }
-            Key::Named(NamedKey::Escape) => {
-                self.editing = false;
-                true
-            }
-            _ => {
-                if let Some(text) = &event.text {
-                    if !event.repeat {
-                        for ch in text.chars() {
-                            if ch.is_alphanumeric() || ch == ' ' || ch == '-' || ch == '_' || ch == '*' || ch == '.' || ch == '@' {
-                                self.edit_buffer.push(ch);
-                            }
-                        }
-                    }
-                }
-                true
-            }
-        }
-    }
-
-    fn hover_highlight(&self) -> Option<[f32; 4]> {
-        None
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        if self.hovered {
-            let (hy, hh) = if self.label.is_some() {
-                (self.y - 18.0, self.h + 18.0)
-            } else {
-                (self.y, self.h)
-            };
-            let hx = if self.row_w > 0.0 { self.row_x } else { self.x };
-            let hw = if self.row_w > 0.0 { self.row_w } else { self.w };
-            quads.push((hx, hy, hw, hh, [1.0, 1.0, 1.0, 0.06]));
-        }
-        let bg_color = if self.editing {
-            [0.12, 0.12, 0.18, 1.0]
-        } else {
-            [0.08, 0.08, 0.12, 1.0]
-        };
-        let border_color = if self.editing {
-            [0.30, 0.50, 0.32, 1.0]
-        } else if self.hovered {
-            [0.25, 0.25, 0.35, 1.0]
-        } else {
-            [0.18, 0.18, 0.24, 1.0]
-        };
-        quads.push((self.x, self.y, self.w, self.h, border_color));
-        quads.push((self.x + 1.0, self.y + 1.0, self.w - 2.0, self.h - 2.0, bg_color));
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        let mut labels = Vec::new();
-        if let Some(ref label) = self.label {
-            labels.push(TextLabel {
-                text: label.clone(),
-                x: self.x + 4.0,
-                y: self.y - 14.0,
-                font_size: 12.0,
-                color: [0x83, 0x83, 0x8a],
-            });
-        }
-        let val_text = if self.editing {
-            format!("{}|", self.edit_buffer)
-        } else {
-            self.text.clone()
-        };
-        labels.push(TextLabel {
-            text: val_text,
-            x: self.x + 8.0,
-            y: self.y + (self.h - 12.0) / 2.0,
-            font_size: 13.0,
-            color: if self.editing { [0xee, 0xee, 0xf5] } else { [0xcc, 0xcc, 0xd4] },
-        });
-        labels
-    }
-
-    fn parent(&self) -> Option<*mut (dyn Widget + 'static)> { self.parent }
-    fn set_parent(&mut self, parent: Option<*mut (dyn Widget + 'static)>) { self.parent = parent; }
-    fn children(&self) -> Vec<*mut (dyn Widget + 'static)> { self.children.clone() }
-    fn add_child(&mut self, child: *mut (dyn Widget + 'static)) { self.children.push(child); }
-    fn clear_children(&mut self) { self.children.clear(); }
-}
-
-impl Drop for TextBox {
-    fn drop(&mut self) {
-        clear_ui::widget::focus::clear_if_matches(self);
-    }
-}
-
-unsafe impl Send for TextBox {}
-unsafe impl Sync for TextBox {}
 
 // ── Service Types and Page State ──
 
diff --git a/src/pages/typeface.rs b/src/pages/typeface.rs
index 9bdab1c..2364a99 100644
--- a/src/pages/typeface.rs
+++ b/src/pages/typeface.rs
@@ -1,255 +1,11 @@
 use std::fs;
 use crate::app::PageContent;
 use clear_ui::layout::Section;
-use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, Dropdown};
+use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, Dropdown, TextBox, Spinbox};
 use clear_ui::widget::{ElementState, KeyEvent, MouseButton, Key, NamedKey};
 
 const FONTS_CONF_PATH: &str = "/home/lsgalante/.config/fontconfig/fonts.conf";
 
-// ── TextBox Widget ──
-
-#[derive(Debug, Clone)]
-pub struct TextBox {
-    x: f32, y: f32, w: f32, h: f32,
-    pub text: String,
-    pub editing: bool,
-    pub edit_buffer: String,
-    hovered: bool,
-    just_changed: bool,
-    label: Option<String>,
-    row_x: f32,
-    row_w: f32,
-    pub disabled: bool,
-    pub parent: Option<*mut (dyn Widget + 'static)>,
-    pub children: Vec<*mut (dyn Widget + 'static)>,
-}
-
-impl TextBox {
-    pub fn new(text: String) -> Self {
-        Self {
-            x: 0.0, y: 0.0, w: 0.0, h: 0.0,
-            text,
-            editing: false,
-            edit_buffer: String::new(),
-            hovered: false,
-            just_changed: false,
-            label: None,
-            row_x: 0.0,
-            row_w: 0.0,
-            disabled: false,
-            parent: None,
-            children: Vec::new(),
-        }
-    }
-
-    pub fn with_label(mut self, label: &str) -> Self {
-        self.label = Some(label.to_string());
-        self
-    }
-
-    pub fn set_label(&mut self, label: &str) {
-        self.label = Some(label.to_string());
-    }
-
-    pub fn take_change(&mut self) -> bool {
-        let changed = self.just_changed;
-        self.just_changed = false;
-        changed
-    }
-}
-
-impl Default for TextBox {
-    fn default() -> Self {
-        Self::new(String::new())
-    }
-}
-
-impl Widget for TextBox {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-    fn set_row_rect(&mut self, x: f32, w: f32) { self.row_x = x; self.row_w = w; }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-
-    fn color(&self) -> [f32; 4] {
-        [0.10, 0.10, 0.16, 1.0]
-    }
-
-    fn hit_test(&self, px: f32, py: f32) -> bool {
-        let (x, y, w, h) = self.rect();
-        let hx = if self.row_w > 0.0 { self.row_x } else { x };
-        let hw = if self.row_w > 0.0 { self.row_w } else { w };
-        let (hy, hh) = if self.label.is_some() {
-            (y - 18.0, h + 18.0)
-        } else {
-            (y, h)
-        };
-        px >= hx && px <= hx + hw && py >= hy && py <= hy + hh
-    }
-
-    fn top_room(&self) -> f32 { if self.label.is_some() { 18.0 } else { 0.0 } }
-
-    fn cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        if self.disabled {
-            let was = self.hovered;
-            self.hovered = false;
-            return was;
-        }
-        let was = self.hovered;
-        self.hovered = self.hit_test(px, py);
-        was != self.hovered
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if self.disabled { return false; }
-        if button != MouseButton::Left { return false; }
-        if state != ElementState::Pressed { return false; }
-        let (x, y, w, h) = self.rect();
-        let (hy, hh) = if self.label.is_some() {
-            (y - 18.0, h + 18.0)
-        } else {
-            (y, h)
-        };
-        if !(px >= x && px <= x + w && py >= hy && py <= hy + hh) {
-            return false;
-        }
-        self.focus();
-        true
-    }
-
-    fn focus(&mut self) {
-        if self.disabled { return; }
-        if !self.editing {
-            self.editing = true;
-            self.edit_buffer = self.text.clone();
-            clear_ui::widget::focus::set_focused(self);
-        }
-    }
-
-    fn unfocus(&mut self) {
-        if self.editing {
-            self.editing = false;
-            if self.text != self.edit_buffer {
-                self.text = self.edit_buffer.clone();
-                self.just_changed = true;
-            }
-        }
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
-        if self.disabled { return false; }
-        if !self.editing { return false; }
-        if event.state != ElementState::Pressed { return false; }
-        match &event.logical_key {
-            Key::Named(NamedKey::Backspace) => {
-                self.edit_buffer.pop();
-                true
-            }
-            Key::Named(NamedKey::Enter) => {
-                self.text = self.edit_buffer.clone();
-                self.editing = false;
-                self.just_changed = true;
-                true
-            }
-            Key::Named(NamedKey::Escape) => {
-                self.editing = false;
-                true
-            }
-            _ => {
-                if let Some(text) = &event.text {
-                    if !event.repeat {
-                        for ch in text.chars() {
-                            if ch.is_alphanumeric() || ch == ' ' || ch == '-' || ch == '_' || ch == '*' {
-                                self.edit_buffer.push(ch);
-                            }
-                        }
-                    }
-                }
-                true
-            }
-        }
-    }
-
-    fn hover_highlight(&self) -> Option<[f32; 4]> {
-        None
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        if self.disabled {
-            quads.push((self.x, self.y, self.w, self.h, [0.12, 0.12, 0.16, 1.0])); // border
-            quads.push((self.x + 1.0, self.y + 1.0, self.w - 2.0, self.h - 2.0, [0.06, 0.06, 0.08, 1.0])); // bg
-            return quads;
-        }
-        if self.hovered {
-            let (hy, hh) = if self.label.is_some() {
-                (self.y - 18.0, self.h + 18.0)
-            } else {
-                (self.y, self.h)
-            };
-            let hx = if self.row_w > 0.0 { self.row_x } else { self.x };
-            let hw = if self.row_w > 0.0 { self.row_w } else { self.w };
-            quads.push((hx, hy, hw, hh, [1.0, 1.0, 1.0, 0.06]));
-        }
-        let bg_color = if self.editing {
-            [0.12, 0.12, 0.18, 1.0]
-        } else {
-            [0.08, 0.08, 0.12, 1.0]
-        };
-        let border_color = if self.editing {
-            [0.30, 0.50, 0.32, 1.0]
-        } else if self.hovered {
-            [0.25, 0.25, 0.35, 1.0]
-        } else {
-            [0.18, 0.18, 0.24, 1.0]
-        };
-        quads.push((self.x, self.y, self.w, self.h, border_color));
-        quads.push((self.x + 1.0, self.y + 1.0, self.w - 2.0, self.h - 2.0, bg_color));
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        let mut labels = Vec::new();
-        if let Some(ref label) = self.label {
-            labels.push(TextLabel {
-                text: label.clone(),
-                x: self.x + 4.0,
-                y: self.y - 14.0,
-                font_size: 12.0,
-                color: [0x83, 0x83, 0x8a],
-            });
-        }
-        let val_text = if self.editing {
-            format!("{}|", self.edit_buffer)
-        } else {
-            self.text.clone()
-        };
-        labels.push(TextLabel {
-            text: val_text,
-            x: self.x + 8.0,
-            y: self.y + (self.h - 12.0) / 2.0,
-            font_size: 13.0,
-            color: if self.disabled { [0x53, 0x53, 0x5a] } else if self.editing { [0xee, 0xee, 0xf5] } else { [0xcc, 0xcc, 0xd4] },
-        });
-        labels
-    }
-
-    fn parent(&self) -> Option<*mut (dyn Widget + 'static)> { self.parent }
-    fn set_parent(&mut self, parent: Option<*mut (dyn Widget + 'static)>) { self.parent = parent; }
-    fn children(&self) -> Vec<*mut (dyn Widget + 'static)> { self.children.clone() }
-    fn add_child(&mut self, child: *mut (dyn Widget + 'static)) { self.children.push(child); }
-    fn clear_children(&mut self) { self.children.clear(); }
-}
-
-impl Drop for TextBox {
-    fn drop(&mut self) {
-        clear_ui::widget::focus::clear_if_matches(self);
-    }
-}
-
-unsafe impl Send for TextBox {}
-unsafe impl Sync for TextBox {}
-
 // ── TypefaceState and TypefaceMessage ──
 
 #[derive(Debug, Clone)]
@@ -278,6 +34,10 @@ pub struct TypefaceState {
     pub status_menu: Dropdown,
     pub fuzzel_menu: Dropdown,
     pub terminal_menu: Dropdown,
+    pub borders_size_box: Spinbox,
+    pub status_size_box: Spinbox,
+    pub fuzzel_size_box: Spinbox,
+    pub terminal_size_box: Spinbox,
 }
 
 impl Default for TypefaceState {
@@ -307,6 +67,10 @@ impl Default for TypefaceState {
             status_menu: Dropdown::default(),
             fuzzel_menu: Dropdown::default(),
             terminal_menu: Dropdown::default(),
+            borders_size_box: Spinbox::new(11, 6, 72, 1),
+            status_size_box: Spinbox::new(13, 6, 72, 1),
+            fuzzel_size_box: Spinbox::new(14, 6, 72, 1),
+            terminal_size_box: Spinbox::new(12, 6, 72, 1),
         }
     }
 }
@@ -328,6 +92,10 @@ pub enum TypefaceMessage {
     SetStatusMenu(usize),
     SetFuzzelMenu(usize),
     SetTerminalMenu(usize),
+    SetBordersSize(i32),
+    SetStatusSize(i32),
+    SetFuzzelSize(i32),
+    SetTerminalSize(i32),
 }
 
 fn parse_font_for_alias(content: &str, alias: &str) -> Option<String> {
@@ -469,6 +237,164 @@ pub fn save_preferred_fonts(
         .spawn();
 }
 
+fn parse_u16_from(content: &str, key: &str, default: u16) -> u16 {
+    for line in content.lines() {
+        let trimmed = line.trim();
+        if let Some(rest) = trimmed.strip_prefix(key) {
+            let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+            return rest.trim_end_matches('"').trim().parse::<u16>().unwrap_or(default);
+        }
+    }
+    default
+}
+
+fn write_config_value(key: &str, value: &str) -> bool {
+    let content = fs::read_to_string("/home/lsgalante/.config/clearwm/config.toml").unwrap_or_default();
+    let new_line = format!("{} = {}", key, value);
+    let mut found = false;
+    let updated: String = content.lines()
+        .map(|line| {
+            if line.trim().starts_with(key) { found = true; new_line.clone() }
+            else { line.to_string() }
+        }).collect::<Vec<_>>().join("\n");
+    if !found {
+        let mut result = String::new();
+        let mut in_layout = false;
+        let mut inserted = false;
+        for line in updated.lines() {
+            if line.trim() == "[layout]" { in_layout = true; }
+            else if line.trim().starts_with('[') && in_layout {
+                if !inserted { result.push_str(&new_line); result.push('\n'); inserted = true; }
+                in_layout = false;
+            }
+            result.push_str(line); result.push('\n');
+        }
+        if in_layout && !inserted { result.push_str(&new_line); result.push('\n'); }
+        fs::write("/home/lsgalante/.config/clearwm/config.toml", result).is_ok()
+    } else { fs::write("/home/lsgalante/.config/clearwm/config.toml", updated).is_ok() }
+}
+
+fn send_ipc_command(cmd: &str) {
+    if let Ok(mut stream) = std::os::unix::net::UnixStream::connect("/tmp/clearwm.sock") {
+        use std::io::Write;
+        let _ = stream.write_all(format!("{}\n", cmd).as_bytes());
+    }
+}
+
+fn read_border_font_size() -> Option<u16> {
+    let content = fs::read_to_string("/home/lsgalante/.config/clearwm/config.toml").ok()?;
+    Some(parse_u16_from(&content, "border_font_size", 11))
+}
+
+fn read_waybar_size() -> Option<u16> {
+    let css = fs::read_to_string("/home/lsgalante/.config/waybar/style.css").ok()?;
+    for line in css.lines() {
+        if line.contains("font-size") {
+            let val = line.split(':').nth(1)?.trim().trim_end_matches(';').trim();
+            if let Some(px) = val.strip_suffix("px") {
+                return px.trim().parse::<u16>().ok();
+            }
+            return val.parse::<u16>().ok();
+        }
+    }
+    None
+}
+
+fn write_waybar_size(size: u16) {
+    let path = "/home/lsgalante/.config/waybar/style.css";
+    let css = fs::read_to_string(path).unwrap_or_default();
+    let mut new_lines = Vec::new();
+    for line in css.lines() {
+        if line.contains("font-size") {
+            new_lines.push(format!("    font-size: {}px;", size));
+        } else {
+            new_lines.push(line.to_string());
+        }
+    }
+    let _ = fs::write(path, new_lines.join("\n"));
+    let _ = std::process::Command::new("pkill")
+        .args(["-x", "waybar", "-SIGUSR2"])
+        .spawn();
+}
+
+fn read_fuzzel_size() -> Option<u16> {
+    let ini = fs::read_to_string("/home/lsgalante/.config/fuzzel/fuzzel.ini").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 = "/home/lsgalante/.config/fuzzel/fuzzel.ini";
+    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("/home/lsgalante/.config/foot/foot.ini").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 = "/home/lsgalante/.config/foot/foot.ini";
+    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"));
+}
+
 fn parse_families(output: Option<std::process::Output>) -> Vec<String> {
     let mut families = Vec::new();
     if let Some(o) = output {
@@ -526,18 +452,23 @@ pub async fn fetch_typeface_state() -> TypefaceState {
         "Other".to_string(),
     ];
 
-    let mut borders_box = TextBox::new(borders.clone()).with_label("Window Borders");
+    let mut borders_box = TextBox::new(borders.clone()).with_label("Window Borders").with_width(300.0);
     borders_box.disabled = borders_idx != 3;
 
-    let mut status_box = TextBox::new(status.clone()).with_label("Status Interface");
+    let mut status_box = TextBox::new(status.clone()).with_label("Status Interface").with_width(300.0);
     status_box.disabled = status_idx != 3;
 
-    let mut fuzzel_box = TextBox::new(fuzzel_font.clone()).with_label("Fuzzel");
+    let mut fuzzel_box = TextBox::new(fuzzel_font.clone()).with_label("Fuzzel").with_width(300.0);
     fuzzel_box.disabled = fuzzel_idx != 3;
 
-    let mut terminal_box = TextBox::new(term.clone()).with_label("Terminal");
+    let mut terminal_box = TextBox::new(term.clone()).with_label("Terminal").with_width(300.0);
     terminal_box.disabled = terminal_idx != 3;
 
+    let borders_size = read_border_font_size().unwrap_or(11);
+    let status_size = read_waybar_size().unwrap_or(13);
+    let fuzzel_size = read_fuzzel_size().unwrap_or(14);
+    let terminal_size = read_terminal_size().unwrap_or(12);
+
     TypefaceState {
         loaded: true,
         sans_serif: sans.clone(),
@@ -563,6 +494,10 @@ pub async fn fetch_typeface_state() -> TypefaceState {
         status_menu: Dropdown::new(menu_options.clone(), status_idx),
         fuzzel_menu: Dropdown::new(menu_options.clone(), fuzzel_idx),
         terminal_menu: Dropdown::new(menu_options.clone(), terminal_idx),
+        borders_size_box: Spinbox::new(borders_size as i32, 6, 72, 1),
+        status_size_box: Spinbox::new(status_size as i32, 6, 72, 1),
+        fuzzel_size_box: Spinbox::new(fuzzel_size as i32, 6, 72, 1),
+        terminal_size_box: Spinbox::new(terminal_size as i32, 6, 72, 1),
     }
 }
 
@@ -607,42 +542,59 @@ pub fn view(state: &mut TypefaceState, cx: f32, cy: f32, cw: f32, _ch: f32, sec_
         sec.spacing(18.0);
     } else {
         let dropdown_w = 120.0;
-        let textbox_w = widget_w - dropdown_w - 12.0;
+        let textbox_w = 300.0;
+        let spinbox_w = 90.0;
+
+        let menu_row_w = dropdown_w + 10.0;
+        let box_row_w = textbox_w + 10.0;
+        let spinbox_row_w = spinbox_w + 10.0;
+
+        let box_row_x = cx + 8.0 + menu_row_w;
+        let spin_row_x = box_row_x + box_row_w;
+        let spin_x = cx + 12.0 + dropdown_w + 12.0 + textbox_w + 12.0;
 
         // Window Borders
         let start_y = sec.ay();
         let top_room = state.borders_box.top_room();
-        state.borders_menu.set_row_rect(cx + 8.0, cw - 16.0);
+        state.borders_menu.set_row_rect(cx + 8.0, menu_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.borders_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.borders_box.set_row_rect(cx + 8.0, cw - 16.0);
+        state.borders_box.set_row_rect(box_row_x, box_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.borders_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+        state.borders_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+        clear_ui::layout::render_widget(&mut pc, &mut state.borders_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
         sec.spacing(widget_h + top_room + 12.0);
 
         // Status Interface
         let start_y = sec.ay();
         let top_room = state.status_box.top_room();
-        state.status_menu.set_row_rect(cx + 8.0, cw - 16.0);
+        state.status_menu.set_row_rect(cx + 8.0, menu_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.status_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.status_box.set_row_rect(cx + 8.0, cw - 16.0);
+        state.status_box.set_row_rect(box_row_x, box_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.status_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+        state.status_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+        clear_ui::layout::render_widget(&mut pc, &mut state.status_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
         sec.spacing(widget_h + top_room + 12.0);
 
         // Fuzzel
         let start_y = sec.ay();
         let top_room = state.fuzzel_box.top_room();
-        state.fuzzel_menu.set_row_rect(cx + 8.0, cw - 16.0);
+        state.fuzzel_menu.set_row_rect(cx + 8.0, menu_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.fuzzel_box.set_row_rect(cx + 8.0, cw - 16.0);
+        state.fuzzel_box.set_row_rect(box_row_x, box_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+        state.fuzzel_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+        clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
         sec.spacing(widget_h + top_room + 12.0);
 
         // Terminal
         let start_y = sec.ay();
         let top_room = state.terminal_box.top_room();
-        state.terminal_menu.set_row_rect(cx + 8.0, cw - 16.0);
+        state.terminal_menu.set_row_rect(cx + 8.0, menu_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.terminal_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
-        state.terminal_box.set_row_rect(cx + 8.0, cw - 16.0);
+        state.terminal_box.set_row_rect(box_row_x, box_row_w);
         clear_ui::layout::render_widget(&mut pc, &mut state.terminal_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+        state.terminal_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+        clear_ui::layout::render_widget(&mut pc, &mut state.terminal_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
         sec.spacing(widget_h + top_room + 8.0);
     }
     let prog_focused = sec_focused.get(1).copied().unwrap_or(false);
@@ -940,6 +892,10 @@ pub fn update(state: &mut TypefaceState, msg: TypefaceMessage) {
             if !state.search_box.editing {
                 state.search_box = new.search_box;
             }
+            state.borders_size_box = new.borders_size_box;
+            state.status_size_box = new.status_size_box;
+            state.fuzzel_size_box = new.fuzzel_size_box;
+            state.terminal_size_box = new.terminal_size_box;
             let old_scroll = state.list_box.scroll_y();
             state.list_box = new.list_box;
             state.list_box.set_scroll_y(old_scroll);
@@ -1095,16 +1051,32 @@ pub fn update(state: &mut TypefaceState, msg: TypefaceMessage) {
                 let text = font.clone();
                 move || {
                     let mut copied = false;
-                    if let Ok(mut child) = std::process::Command::new("wl-copy")
+                    let child = std::process::Command::new("wl-copy")
                         .stdin(std::process::Stdio::piped())
-                        .spawn()
-                    {
-                        if let Some(mut stdin) = child.stdin.take() {
-                            if stdin.write_all(text.as_bytes()).is_ok() {
-                                copied = true;
+                        .stderr(std::process::Stdio::piped())
+                        .spawn();
+                    match child {
+                        Ok(mut child) => {
+                            if let Some(mut stdin) = child.stdin.take() {
+                                let _ = stdin.write_all(text.as_bytes());
                             }
+                            match child.wait_with_output() {
+                                Ok(output) => {
+                                    if output.status.success() {
+                                        copied = true;
+                                    } else {
+                                        let err_msg = String::from_utf8_lossy(&output.stderr);
+                                        eprintln!("wl-copy exited with error status: {:?}, stderr: {}", output.status, err_msg);
+                                    }
+                                }
+                                Err(e) => {
+                                    eprintln!("wl-copy wait failed: {:?}", e);
+                                }
+                            }
+                        }
+                        Err(e) => {
+                            eprintln!("wl-copy spawn failed: {:?}", e);
                         }
-                        let _ = child.wait();
                     }
                     if !copied {
                         if let Ok(mut child) = std::process::Command::new("xclip")
@@ -1214,6 +1186,23 @@ pub fn update(state: &mut TypefaceState, msg: TypefaceMessage) {
                 &state.terminal,
             );
         }
+        TypefaceMessage::SetBordersSize(val) => {
+            state.borders_size_box.value = val;
+            write_config_value("border_font_size", &val.to_string());
+            send_ipc_command(&format!("layout border_font_size {}", val));
+        }
+        TypefaceMessage::SetStatusSize(val) => {
+            state.status_size_box.value = val;
+            write_waybar_size(val as u16);
+        }
+        TypefaceMessage::SetFuzzelSize(val) => {
+            state.fuzzel_size_box.value = val;
+            write_fuzzel_size(val as u16);
+        }
+        TypefaceMessage::SetTerminalSize(val) => {
+            state.terminal_size_box.value = val;
+            write_terminal_size(val as u16);
+        }
     }
 }