git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit4923f2f1cd999fb0fec955b3d9821bab98154c77
parent0a7cf42568
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-28 14:35
refactor: retire the window_manager bevel spellings of the relief keys

`window_manager.bevel_depth` / `bevel_width` were the block the relief
keys were born in before `style.surface.relief` existed; the compositor
never read them, and they survived as compat aliases of `relief.light` /
`.width`. Every other legacy spelling of the relief was retired today,
so these go the same way: not read, reported by path with the other
retired surface keys (`retired_surface_keys` looks under `window_manager`
too), seeded from once by cce-relief and removed by its Save.

Dropping the flatten arms was not enough: the table's fall-through
strips a path's first segment, so `window_manager.bevel_depth` landed on
the live `bevel_depth` registry key by accident, alias or not — the
flatten test caught it. The two spellings now emit no line at all; the
`style.surface.*` retirements need no such guard, since stripped they
become `surface.relief.depth` and the like, which nothing reads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

 CLAUDE.md             |  3 +--
 src/bin/cce-relief.rs | 17 +++++++++++++----
 src/color.rs          | 26 ++++++++++++++++++--------
 src/layout.rs         | 40 +++++++++++++++++++++++++---------------
 4 files changed, 57 insertions(+), 29 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 980026b..9c1800c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -566,9 +566,8 @@ Spellings that are NOT current, and what the loader does with each:
 
 | Spelling | Status |
 |---|---|
-| `window_manager.bevel_depth` / `.bevel_width` | compat aliases of `relief.light` / `.width` — the compositor's old block, a different family |
 | `param.color` (+ top-level `plate_opacity`) | alias of `plate.pane.color`, multiplied by the opacity line |
-| `plate.blur` / `.radius` / `.backdrop_compression` / `.refraction`, `frost.backdrop_compression`, `plate.bevel_width`, `relief.depth`, a material's `finish depth=`, `relief.height` / `.profile` / `.edge_height` / `.edge_profile` | RETIRED: reported by path (`color::retired_surface_keys`), not read; cce-relief seeds from each once and its Save writes the current spelling and removes the old |
+| `plate.blur` / `.radius` / `.backdrop_compression` / `.refraction`, `frost.backdrop_compression`, `plate.bevel_width`, `relief.depth`, a material's `finish depth=`, `relief.height` / `.profile` / `.edge_height` / `.edge_profile`, `window_manager.bevel_depth` / `.bevel_width` | RETIRED: reported by path (`color::retired_surface_keys`), not read; cce-relief seeds from each once and its Save writes the current spelling and removes the old |
 | `relief.wall.knobs` / `edge.knobs`, `profile_knobs` / `edge_knobs` | not style: cce-relief's own state (`~/.config/cce/cce-relief/state.kdl`); read once as a seed, removed on its next Save |
 
 Where each rule is argued, by its lead-in: **Frost is one block** and
diff --git a/src/bin/cce-relief.rs b/src/bin/cce-relief.rs
index 3976f54..676540d 100644
--- a/src/bin/cce-relief.rs
+++ b/src/bin/cce-relief.rs
@@ -1350,7 +1350,10 @@ impl BevelPopup {
             "profile_knobs", "edge_knobs", "wall.knobs", "edge.knobs",
         ]
         .iter()
-        .all(|k| cce_ui::config::remove_config_value(&p, &format!("style.surface.relief.{k}")));
+        .all(|k| cce_ui::config::remove_config_value(&p, &format!("style.surface.relief.{k}")))
+            & ["bevel_depth", "bevel_width"]
+                .iter()
+                .all(|k| cce_ui::config::remove_config_value(&p, &format!("window_manager.{k}")));
         let ok = ok & migrated;
         self.status = if ok {
             println!("saved {p}");
@@ -1472,14 +1475,19 @@ impl Application for BevelPopup {
             saved_edge.or_else(|| rel_shape_str("edge", "knobs", "edge_knobs").as_deref().and_then(parse_knobs)),
         );
 
-        // `depth` is retired (the loader does not read it); read HERE as a
-        // seed only, so a file saved before the rename opens on its own
-        // light strength and Save writes it back as `light`.
+        // `depth` and the `window_manager.bevel_*` spellings are retired
+        // (the loader does not read them); read HERE as seeds only, so a
+        // file saved before the renames opens on its own values and Save
+        // writes them back under `relief`.
+        let wm_f32 = |k: &str| {
+            target_json.as_ref().and_then(|v| v.pointer(&format!("/window_manager/{k}"))).and_then(|v| v.as_f64()).map(|f| f as f32)
+        };
         let depth = key_spec
             .as_ref()
             .and_then(|s| s.light)
             .or_else(|| rel_f32("light"))
             .or_else(|| rel_f32("depth"))
+            .or_else(|| wm_f32("bevel_depth"))
             .unwrap_or_else(cce_ui::layout::bevel_depth);
         // The height as the target SPELLS it (value and unit), kept so Save
         // writes the same unit back; the registry fallback carries no unit
@@ -1497,6 +1505,7 @@ impl Application for BevelPopup {
             .as_ref()
             .map(|s| s.width)
             .or_else(|| rel_f32("width"))
+            .or_else(|| wm_f32("bevel_width"))
             .unwrap_or_else(cce_ui::layout::bevel_width);
         // The seeds ARE the material this window previews: install them so
         // the section, the strip and the popup's own plate show the target
diff --git a/src/color.rs b/src/color.rs
index 3fe9f8d..0ba6e9d 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -683,7 +683,7 @@ fn parse_and_set_colors(content: &str) {
     let retired = retired_surface_keys(&val);
     if !retired.is_empty() {
         log::warn!(
-            "retired style.surface keys in config: {} — the frost is `plate {{ frost radius= compression= refraction= }}` (a material's `frost` child spells `compression`), every roll's width is `relief width=`, the light strength is `relief light=` (a material's `finish light=`), and the relief's geometry is `relief {{ wall height= profile= ; edge height= profile= }}`",
+            "retired style.surface keys in config: {} — the frost is `plate {{ frost radius= compression= refraction= }}` (a material's `frost` child spells `compression`), every roll's width is `relief width=`, the light strength is `relief light=` (a material's `finish light=`), and the relief's geometry is `relief {{ wall height= profile= ; edge height= profile= }}` (the window_manager bevel_* spellings are the same relief keys)",
             retired.join(", ")
         );
     }
@@ -777,8 +777,11 @@ fn parse_and_set_colors(content: &str) {
 /// light strength's former name (`light`, since it is not a length); and
 /// the relief's flat geometry keys `height` / `profile` (the wall's, now
 /// `wall.height` / `wall.profile`) and `edge_height` / `edge_profile` (the
-/// edge's, now `edge.height` / `edge.profile`). Each was an alias for part
-/// of 2026-09-28 and is not read now. Empty for a clean config.
+/// edge's, now `edge.height` / `edge.profile`); and `window_manager.bevel_depth`
+/// / `bevel_width`, the block the relief keys were born in before
+/// `style.surface.relief` existed (the compositor never read them). Each
+/// was an alias for part of 2026-09-28 and is not read now. Empty for a
+/// clean config.
 pub fn retired_surface_keys(val: &serde_json::Value) -> Vec<String> {
     let mut found = Vec::new();
     for k in ["blur", "radius", "backdrop_compression", "refraction", "bevel_width"] {
@@ -794,6 +797,11 @@ pub fn retired_surface_keys(val: &serde_json::Value) -> Vec<String> {
             found.push(format!("style.surface.relief.{k}"));
         }
     }
+    for k in ["bevel_depth", "bevel_width"] {
+        if val.pointer(&format!("/window_manager/{k}")).is_some() {
+            found.push(format!("window_manager.{k}"));
+        }
+    }
     if let Some(mats) = val.pointer("/style/surface/material").and_then(|v| v.as_object()) {
         for (name, node) in mats {
             if node.pointer("/frost/backdrop_compression").is_some() {
@@ -2250,23 +2258,23 @@ mod tests {
 
     /// The four flat frost keys, the `backdrop_compression` spelling inside
     /// a `frost` child, `plate.bevel_width`, `depth` (on the relief, or in a
-    /// material's `finish`) and the relief's flat geometry keys are
-    /// reported by path; the blocks themselves and a material's
-    /// `compression` / `light` are not.
+    /// material's `finish`), the relief's flat geometry keys and the
+    /// window_manager bevel spellings are reported by path; the blocks
+    /// themselves and a material's `compression` / `light` are not.
     #[test]
     fn retired_surface_keys_are_named_by_path_and_the_block_is_not() {
         let clean: serde_json::Value = serde_json::json!({ "style": { "surface": {
             "plate": { "frost": { "radius": 5.5, "compression": 0.0, "refraction": 0.0 }, "color": "#6c6c7bf2" },
             "relief": { "light": 0.15, "wall": { "height": 1.0, "profile": "a" }, "edge": { "height": 2.0, "profile": "b" } },
             "material": { "glass": { "frost": { "compression": 0.6 }, "finish": { "light": 0.2 } } }
-        } } });
+        } }, "window_manager": { "corner_shape": 4.5, "control_relief": true } });
         assert!(retired_surface_keys(&clean).is_empty());
         let old: serde_json::Value = serde_json::json!({ "style": { "surface": {
             "plate": { "blur": true, "radius": 1.5, "backdrop_compression": 0.85, "refraction": 0.3,
                        "bevel_width": 12.0, "frost": { "backdrop_compression": 0.2 } },
             "relief": { "depth": 0.15, "height": 1.0, "profile": "a", "edge_height": 2.0, "edge_profile": "b" },
             "material": { "glass": { "frost": { "backdrop_compression": 0.6 }, "finish": { "depth": 0.2 } } }
-        } } });
+        } }, "window_manager": { "bevel_depth": 0.15, "bevel_width": 9.3 } });
         assert_eq!(retired_surface_keys(&old), vec![
             "style.surface.plate.blur",
             "style.surface.plate.radius",
@@ -2279,6 +2287,8 @@ mod tests {
             "style.surface.relief.profile",
             "style.surface.relief.edge_height",
             "style.surface.relief.edge_profile",
+            "window_manager.bevel_depth",
+            "window_manager.bevel_width",
             "style.surface.material.glass.frost.backdrop_compression",
             "style.surface.material.glass.finish.depth",
         ]);
diff --git a/src/layout.rs b/src/layout.rs
index 8d8a807..37f26b0 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -188,6 +188,15 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
             }
         }
         _ => {
+            // A retired `window_manager` spelling of a relief key emits NO
+            // line: the fall-through below strips a path's first segment, so
+            // `window_manager.bevel_depth` would otherwise land on the live
+            // `bevel_depth` registry key by accident, alias or not. The
+            // `style.surface.*` retirements need no guard — stripped, they
+            // become `surface.relief.depth` and the like, which nothing reads.
+            if matches!(prefix, "window_manager.bevel_depth" | "window_manager.bevel_width") {
+                return;
+            }
             let flat_key = match prefix {
                 "style.list.font" | "style.data.list.font" => "list_font",
                 "style.list.font_color" | "style.data.list.font_color" => "list_font_color",
@@ -239,19 +248,19 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.control.toggle.border_color" => "toggle_border_color",
                 "style.control.toggle.corner_radius" => "toggle_corner_radius",
                 "window_manager.light_source_position" => "light_source_position",
-                // The DE's relief material: canonical home style.surface.relief
-                // (these shade every bevel/boss/recess in the toolkit — the
-                // compositor never read them, so the old window_manager
-                // spelling survives only as a compat alias).
-                // `light` is the spelling (since 2026-09-28): it is the
-                // light strength, not a length. `depth` — what every config
-                // said until then — was its alias for the rest of that day
-                // and is RETIRED: not read, reported by path
-                // (`color::retired_surface_keys`), removed by cce-relief's
-                // Save. The `window_manager.bevel_depth` compat spelling is
-                // a different family (the compositor's old block) and stays.
-                "style.surface.relief.light" | "window_manager.bevel_depth" => "bevel_depth",
-                "style.surface.relief.width" | "window_manager.bevel_width" => "bevel_width",
+                // The DE's relief material, home style.surface.relief: these
+                // shade every bevel/boss/recess in the toolkit. `light` is
+                // the spelling (since 2026-09-28): it is the light strength,
+                // not a length. Two older spellings are RETIRED — not read,
+                // reported by path (`color::retired_surface_keys`), removed
+                // by cce-relief's Save: `relief.depth`, what every config
+                // said until that day, and `window_manager.bevel_depth` /
+                // `bevel_width`, the block these keys were born in before
+                // they had a home of their own (the compositor never read
+                // them; the spelling survived as a compat alias until the
+                // evening of 2026-09-28).
+                "style.surface.relief.light" => "bevel_depth",
+                "style.surface.relief.width" => "bevel_width",
                 // The two SHAPES of the relief, each a node under it
                 // (2026-09-28): `wall` is a carve's wall — a recess, boss,
                 // ridge or trough cut into a surface — and `edge` is the
@@ -6970,11 +6979,12 @@ mod tests {
                 "depth": 0.3,
                 "height": 2.0, "edge_height": 3.0, "profile": "a", "edge_profile": "b",
                 "wall": { "knobs": "1,1,1" }, "edge_knobs": "2,2,2"
-            } } }
+            } } },
+            "window_manager": { "bevel_depth": 0.3, "bevel_width": 5.0 }
         });
         let mut flat = String::new();
         flatten_json_to_flat_props(&old, "", &mut flat);
-        for k in ["bevel_depth", "bevel_height", "roll_height", "bevel_profile_spec", "roll_profile_spec", "profile_knobs"] {
+        for k in ["bevel_depth", "bevel_width", "bevel_height", "roll_height", "bevel_profile_spec", "roll_profile_spec", "profile_knobs"] {
             assert!(!flat.lines().any(|l| l.starts_with(&format!("{k} = "))), "{k} landed from a retired spelling: {flat}");
         }
         // And a file carrying BOTH spellings is what its current one says,