git.lucas.co / cce-status-interface
status bar
git clone https://git.lucas.co/cce-status-interface.git

commit739e30931eb895a7e06e32f87cbbbacd6ff6dc9d
parent32b481cc41
authorLucas Galante <[email protected]>
date2026-08-24 14:56
feat: text_halo — full white outline around module text

Four diagonal white text_faded copies (±0.75px) around each non-boxed
run: a true outline readable over any backdrop, for when the single
letterpress edge isn't enough. Takes precedence over text_relief.

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

 CLAUDE.md     |  4 +++-
 src/config.rs |  8 ++++++++
 src/main.rs   | 19 +++++++++++++++----
 3 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index a3a6069..a8b0c80 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -150,7 +150,9 @@ and `module { text_raise }` (lifts module text above vertical center, logical
 px, bar-side only — every module funnels through `centered_text_y`) and
 `module { text_relief }` (letterpress underlay strength 0-1: a translucent
 white copy 0.75px down-right beneath each non-boxed text run — engraved text,
-guaranteed contrast on dark backdrops). Everything is read through
+guaranteed contrast on dark backdrops) and `module { text_halo }` (full white
+outline 0-1: FOUR diagonal white copies around each run; beats text_relief
+when set). Everything is read through
 `cce_ui::config::cached_config()`; KDL is converted to JSON
 (`cce_ui::config::parse_kdl_to_json`) and looked up by **explicit JSON
 pointer only**: every key names its canonical nesting
diff --git a/src/config.rs b/src/config.rs
index 6a12e74..3bbafc3 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -227,6 +227,14 @@ pub(crate) fn read_text_relief_from_config() -> f32 {
     cfg_f32("/module/text_relief").unwrap_or(0.0).clamp(0.0, 1.0)
 }
 
+/// `module { text_halo }` — full white outline strength (0 = off, 1 = opaque):
+/// four translucent white copies at the diagonal offsets ±0.75px around each
+/// module text run, a true halo readable over any backdrop. When set it
+/// replaces the single-offset `text_relief` underlay.
+pub(crate) fn read_text_halo_from_config() -> f32 {
+    cfg_f32("/module/text_halo").unwrap_or(0.0).clamp(0.0, 1.0)
+}
+
 pub(crate) fn read_status_box_corner_radius_from_config() -> f32 {
     // App-native ONLY (~/.config/cce/cce-status-interface/config.kdl,
     // merged over the shared config by cce-ui): `module { corner_radius }`.
diff --git a/src/main.rs b/src/main.rs
index 6db6fd0..c148bc5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -263,6 +263,9 @@ struct StatusApp {
     /// Letterpress underlay strength for module text (0 = off) — see
     /// `read_text_relief_from_config`.
     text_relief: f32,
+    /// Full white outline strength (0 = off); beats `text_relief` when set —
+    /// see `read_text_halo_from_config`.
+    text_halo: f32,
     text_prims: Vec<TextPrim>,
 
     scale_factor: f64,
@@ -371,6 +374,7 @@ impl StatusApp {
         self.droplet = read_droplet_from_config();
         self.droplet_boxes.clear();
         self.text_relief = read_text_relief_from_config();
+        self.text_halo = read_text_halo_from_config();
 
         self.status_bar.set_rect(0.0, 0.0, self.width as f32, self.height as f32);
         // The surface itself is transparent: every StatusApp is a single
@@ -1121,6 +1125,7 @@ impl cce_ui::engine::Application for StatusApp {
             droplet_boxes: Vec::new(),
             droplet: None,
             text_relief: 0.0,
+            text_halo: 0.0,
             text_prims: Vec::new(),
             scale_factor: 1.0,
             width: if selected_module.is_some() { 120 } else { 1920 },
@@ -1418,10 +1423,16 @@ impl cce_ui::engine::Application for StatusApp {
             match layout {
                 Some(l) => pc.text_boxed(text.clone(), *x, *y, *tsize, *color, font.clone(), *bounds, cce_ui::scene::paint::TextAttrs::default(), *l),
                 None => {
-                    // Letterpress underlay: a translucent white copy offset
-                    // down-right BENEATH the glyphs — dark text keeps a lit
-                    // edge on dark backdrops (the engraved-text treatment).
-                    if self.text_relief > 0.0 {
+                    if self.text_halo > 0.0 {
+                        // Full halo: four diagonal white copies — a true
+                        // outline, readable over any backdrop.
+                        for (dx, dy) in [(-0.75, -0.75), (0.75, -0.75), (-0.75, 0.75), (0.75, 0.75)] {
+                            pc.text_faded(text.clone(), *x + dx, *y + dy, *tsize, [255, 255, 255], self.text_halo, font.clone(), *bounds);
+                        }
+                    } else if self.text_relief > 0.0 {
+                        // Letterpress underlay: a translucent white copy offset
+                        // down-right BENEATH the glyphs — dark text keeps a lit
+                        // edge on dark backdrops (the engraved-text treatment).
                         pc.text_faded(text.clone(), *x + 0.75, *y + 0.75, *tsize, [255, 255, 255], self.text_relief, font.clone(), *bounds);
                     }
                     pc.text_with(text.clone(), *x, *y, *tsize, *color, font.clone(), *bounds)