GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
refactor: retire relief.depth — light is the one spelling of the strength
`depth` was every config's name for the relief's light strength until
this afternoon's rename made `light` the spelling and `depth` an alias
that lost to it. An alias that keeps working is a second spelling
forever, so the loader no longer reads it: the flatten arm goes, the
`depth`/`light` pair leaves `prefer_relief_spellings` (a retired key
never reaches a registry key, so there is nothing to prefer), a
material's `finish depth=` alias goes the same way, and a config still
carrying either is reported by path with the other retired surface keys.
cce-relief reads `depth` as a seed only — a file saved before the rename
opens on its own strength — and its Save writes `light` and removes
`depth`, as before. The `window_manager.bevel_depth` compat spelling is
the compositor's old block, a different family, and stays.
Fixtures and the KDL writer tests move onto `light`; the retired-keys
test expects both `depth` spellings; the precedence test asserts that a
lone `depth` flattens to no registry key at all.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CLAUDE.md | 11 ++++++-----
src/bin/cce-relief.rs | 3 +++
src/color.rs | 33 +++++++++++++++++++++++----------
src/config.rs | 14 +++++++-------
src/layout.rs | 51 +++++++++++++++++++++++++--------------------------
src/scene/material.rs | 6 +++---
6 files changed, 67 insertions(+), 51 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index daf069a..83e0934 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -566,11 +566,11 @@ Spellings that are NOT current, and what the loader does with each:
| Spelling | Status |
|---|---|
-| `relief.depth`, `window_manager.bevel_depth`, a material's `finish depth=` | alias of `relief.light` / `finish light=`; `light` wins when both are present |
+| `window_manager.bevel_depth` / `.bevel_width` | compat aliases of `relief.light` / `.width` — the compositor's old block, a different family |
| `relief.height` / `.profile`, `relief.edge_height` / `.edge_profile` | aliases of `wall.*` / `edge.*`; the node wins when both are present |
| `window_manager.bevel_width` | alias of `relief.width` |
| `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` | RETIRED: reported by path (`color::retired_surface_keys`), not read |
+| `plate.blur` / `.radius` / `.backdrop_compression` / `.refraction`, `frost.backdrop_compression`, `plate.bevel_width`, `relief.depth`, a material's `finish depth=` | RETIRED: reported by path (`color::retired_surface_keys`), not read; cce-relief seeds from `depth` once and writes `light` |
| `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
@@ -756,9 +756,10 @@ relief light=0.15 width=9.3 {
`width` (the run of both, one number — see the roll-width note below) and
`light` (how hard the light falls across either shape — NOT a length, it is
`bevel_depth` → `Finish.strength`; `depth`, what every config said until
-2026-09-28, is its alias, losing to `light` when a file carries both, and
-cce-relief's Save writes `light` and takes `depth` off) stay on the node
-itself, since both shapes share them. `height` is a length — the wall's drop, the edge's rise —
+2026-09-28, was its alias for the rest of that day and is retired — reported
+by path, not read, seeded from once by cce-relief whose Save writes `light`
+and takes `depth` off) stay on the node itself, since both shapes share
+them. `height` is a length — the wall's drop, the edge's rise —
and `height=(mm)0.3` is honest geometry resolved through the metric; unset, a
carve drops `relief_shade::RECESS_DEPTH` (0.6) of its wall (saturating at the
DE roll width) and the roll is a quarter-round of radius width, the look every
diff --git a/src/bin/cce-relief.rs b/src/bin/cce-relief.rs
index 7bb39a2..043c4b5 100644
--- a/src/bin/cce-relief.rs
+++ b/src/bin/cce-relief.rs
@@ -1471,6 +1471,9 @@ 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`.
let depth = key_spec
.as_ref()
.and_then(|s| s.light)
diff --git a/src/color.rs b/src/color.rs
index 7e4aea2..43cf279 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`), and every roll's width is `relief width=`",
+ "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=`, and the light strength is `relief light=` (a material's `finish light=`)",
retired.join(", ")
);
}
@@ -748,7 +748,7 @@ fn parse_and_set_colors(content: &str) {
MaterialDef {
tint: node.get("color").and_then(|v| v.as_str()).and_then(parse_hex),
frost,
- light: fk("light").or(fk("depth")),
+ light: fk("light"),
spec: fk("spec"),
shininess: fk("shininess"),
curvature: fk("curvature"),
@@ -771,10 +771,12 @@ fn parse_and_set_colors(content: &str) {
/// the dotted paths a user would grep for: the four flat frost keys under
/// `plate` (`blur`, `radius`, `backdrop_compression`, `refraction`),
/// `backdrop_compression` inside any `frost` child (the plate's, or a named
-/// material's), whose one name is `compression`, and `plate.bevel_width`,
-/// the pane roll's former width of its own (an override for the rest of
-/// 2026-09-28, retired that evening: every roll is `relief.width`). Empty
-/// for a clean config.
+/// material's), whose one name is `compression`; `plate.bevel_width`, the
+/// pane roll's former width of its own (every roll is `relief.width`); and
+/// `relief.depth` with `depth` inside any material's `finish` child, the
+/// light strength's former name (`light`, since it is not a length). 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"] {
@@ -785,11 +787,17 @@ pub fn retired_surface_keys(val: &serde_json::Value) -> Vec<String> {
if val.pointer("/style/surface/plate/frost/backdrop_compression").is_some() {
found.push("style.surface.plate.frost.backdrop_compression".to_string());
}
+ if val.pointer("/style/surface/relief/depth").is_some() {
+ found.push("style.surface.relief.depth".to_string());
+ }
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() {
found.push(format!("style.surface.material.{name}.frost.backdrop_compression"));
}
+ if node.pointer("/finish/depth").is_some() {
+ found.push(format!("style.surface.material.{name}.finish.depth"));
+ }
}
}
found
@@ -2237,19 +2245,22 @@ mod tests {
}
/// The four flat frost keys, the `backdrop_compression` spelling inside
- /// a `frost` child and `plate.bevel_width` are reported by path; the
- /// block itself and a material's `compression` are not.
+ /// a `frost` child, `plate.bevel_width` and `depth` (on the relief, or
+ /// in a material's `finish`) are reported by path; the block itself 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" },
- "material": { "glass": { "frost": { "compression": 0.6 } } }
+ "relief": { "light": 0.15 },
+ "material": { "glass": { "frost": { "compression": 0.6 }, "finish": { "light": 0.2 } } }
} } });
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 } },
- "material": { "glass": { "frost": { "backdrop_compression": 0.6 } } }
+ "relief": { "depth": 0.15 },
+ "material": { "glass": { "frost": { "backdrop_compression": 0.6 }, "finish": { "depth": 0.2 } } }
} } });
assert_eq!(retired_surface_keys(&old), vec![
"style.surface.plate.blur",
@@ -2258,7 +2269,9 @@ mod tests {
"style.surface.plate.refraction",
"style.surface.plate.bevel_width",
"style.surface.plate.frost.backdrop_compression",
+ "style.surface.relief.depth",
"style.surface.material.glass.frost.backdrop_compression",
+ "style.surface.material.glass.finish.depth",
]);
assert!(retired_surface_keys(&serde_json::json!({})).is_empty());
}
diff --git a/src/config.rs b/src/config.rs
index 68d0144..87afdb5 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -999,7 +999,7 @@ mod tests {
/// which is how cce-relief's Save migrates a file in place.
#[test]
fn relief_wall_and_edge_keys_write_as_child_nodes_and_the_flat_ones_come_off() {
- let content = "style {\n surface {\n relief depth=(f64)0.15 width=(f64)9.3 profile=\"old\" edge_knobs=(bevel)\"0.500,0.500,0.500\"\n }\n}\n";
+ let content = "style {\n surface {\n relief light=(f64)0.15 width=(f64)9.3 profile=\"old\" edge_knobs=(bevel)\"0.500,0.500,0.500\"\n }\n}\n";
let mut doc = content.parse::<kdl::KdlDocument>().unwrap();
let spec = "smooth;0.000:0.500,0.400:1.000,1.000:0.000";
assert!(update_kdl_in_memory(&mut doc, "style.surface.relief.wall.profile", spec, "style"));
@@ -1014,13 +1014,13 @@ mod tests {
assert!(!remove_kdl_in_memory(&mut doc, "style.surface.nothing.here"), "missing node: nothing to remove");
let out = doc.to_string();
assert_eq!(out.matches("relief").count(), 1, "one relief node: {out}");
- assert!(out.contains("depth=(f64)0.15"), "the node keeps its properties: {out}");
+ assert!(out.contains("light=(f64)0.15"), "the node keeps its properties: {out}");
assert!(!out.contains("profile=\"old\""), "flat profile migrated: {out}");
assert!(!out.contains("edge_knobs"), "flat knobs migrated: {out}");
let val = parse_kdl_to_json(&out);
let relief = val.pointer("/style/surface/relief").unwrap();
- assert_eq!(relief.get("depth").and_then(|v| v.as_f64()), Some(0.15));
+ assert_eq!(relief.get("light").and_then(|v| v.as_f64()), Some(0.15));
assert_eq!(relief.pointer("/wall/profile").and_then(|v| v.as_str()), Some(spec));
assert_eq!(relief.pointer("/wall/height").and_then(|v| v.as_str()), Some("0.3mm"), "{relief}");
assert_eq!(relief.pointer("/edge/profile").and_then(|v| v.as_str()), Some(spec));
@@ -1037,12 +1037,12 @@ mod tests {
fn relief_keys_write_as_properties_and_round_trip() {
// `relief` is a PROP_NODES member: style.surface.relief.* must land as
// properties on the existing relief node (the config.kdl shape), not
- // as duplicate child nodes shadowing the depth=/width= properties.
- let content = "style {\n surface {\n relief depth=(f64)0.15 width=(f64)9.3\n }\n}\n";
+ // as duplicate child nodes shadowing the light=/width= properties.
+ let content = "style {\n surface {\n relief light=(f64)0.15 width=(f64)9.3\n }\n}\n";
let mut doc = content.parse::<kdl::KdlDocument>().unwrap();
let spec = "smooth;0.000:0.500,0.400:1.000,1.000:0.000";
assert!(update_kdl_in_memory(&mut doc, "style.surface.relief.profile", spec, "style"));
- assert!(update_kdl_in_memory(&mut doc, "style.surface.relief.depth", "0.3", "style"));
+ assert!(update_kdl_in_memory(&mut doc, "style.surface.relief.light", "0.3", "style"));
let out = doc.to_string();
// Still one relief node, no child block grown under it.
assert_eq!(out.matches("relief").count(), 1, "out: {out}");
@@ -1053,7 +1053,7 @@ mod tests {
let val = parse_kdl_to_json(&out);
let relief = val.get("style").unwrap().get("surface").unwrap().get("relief").unwrap();
assert_eq!(relief.get("profile").unwrap().as_str().unwrap(), spec);
- assert_eq!(relief.get("depth").unwrap().as_f64().unwrap(), 0.3);
+ assert_eq!(relief.get("light").unwrap().as_f64().unwrap(), 0.3);
assert_eq!(relief.get("width").unwrap().as_f64().unwrap(), 9.3);
}
diff --git a/src/layout.rs b/src/layout.rs
index 35d46c8..59ef75b 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -245,9 +245,12 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
// 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 — is the alias, and `prefer_relief_spellings`
- // lets `light` win when a file carries both.
- "style.surface.relief.light" | "style.surface.relief.depth" | "window_manager.bevel_depth" => "bevel_depth",
+ // 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 two SHAPES of the relief, each a node under it
// (2026-09-28): `wall` is a carve's wall — a recess, boss,
@@ -396,15 +399,15 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
}
/// The relief's current spellings win over its legacy ones: the `wall` /
-/// `edge` node spellings over the flat geometry keys, and `light` over
-/// `depth`. Each pair flattens to ONE registry key (`relief.wall.height`
-/// and `relief.height` are both `bevel_height`; `light` and `depth` are
-/// both `bevel_depth`), and `reload_config` loads the flat lines in the
-/// order the JSON hands them out, so a config carrying both spellings — a
-/// file cce-relief has saved once under the new names while an older line
+/// `edge` node spellings over the flat geometry keys. Each pair flattens to
+/// ONE registry key (`relief.wall.height` and `relief.height` are both
+/// `bevel_height`), and `reload_config` loads the flat lines in the order
+/// the JSON hands them out, so a config carrying both spellings — a file
+/// cce-relief has saved once under the new names while an older line
/// survives, or a per-app override written in the other spelling — would
/// otherwise be decided by which line came first. Run on the parsed config
-/// before it is flattened.
+/// before it is flattened. (`depth` versus `light` was a pair here for the
+/// rest of 2026-09-28; `depth` is retired now and never flattens at all.)
pub(crate) fn prefer_relief_spellings(val: &mut serde_json::Value) {
const NODE_PAIRS: &[(&str, &str, &str)] = &[
("height", "wall", "height"),
@@ -412,7 +415,6 @@ pub(crate) fn prefer_relief_spellings(val: &mut serde_json::Value) {
("edge_height", "edge", "height"),
("edge_profile", "edge", "profile"),
];
- const FLAT_PAIRS: &[(&str, &str)] = &[("depth", "light")];
let Some(relief) = val.pointer_mut("/style/surface/relief").and_then(|v| v.as_object_mut()) else {
return;
};
@@ -421,11 +423,6 @@ pub(crate) fn prefer_relief_spellings(val: &mut serde_json::Value) {
relief.remove(*flat);
}
}
- for (legacy, current) in FLAT_PAIRS {
- if relief.get(*current).is_some() {
- relief.remove(*legacy);
- }
- }
}
fn read_config() -> Option<String> {
@@ -7005,20 +7002,22 @@ mod tests {
}
/// A config carrying both spellings of one relief key is decided by the
- /// current spelling — the node one for the geometry, `light` over
- /// `depth` — whichever line the file wrote first: the flat pass loads
- /// in JSON order, and `prefer_relief_spellings` is what makes the order
- /// irrelevant. A key with no current spelling beside it is left alone.
+ /// current spelling — the node one for the geometry — whichever line
+ /// the file wrote first: the flat pass loads in JSON order, and
+ /// `prefer_relief_spellings` is what makes the order irrelevant. A key
+ /// with no current spelling beside it is left alone. The retired
+ /// `depth` is not a pair any more: it never reaches a registry key.
#[test]
fn the_current_spelling_of_a_relief_key_wins_over_the_legacy_one() {
- let mut both: serde_json::Value = serde_json::json!({
- "style": { "surface": { "relief": { "depth": 0.3, "light": 0.1 } } }
+ let retired: serde_json::Value = serde_json::json!({
+ "style": { "surface": { "relief": { "depth": 0.3 } } }
});
- prefer_relief_spellings(&mut both);
- assert_eq!(both.pointer("/style/surface/relief"), Some(&serde_json::json!({ "light": 0.1 })), "light wins over depth");
+ let mut flat = String::new();
+ flatten_json_to_flat_props(&retired, "", &mut flat);
+ assert!(!flat.contains("bevel_depth"), "a retired `depth` flattens to no registry key: {flat}");
let mut val: serde_json::Value = serde_json::json!({
"style": { "surface": { "relief": {
- "depth": 0.15,
+ "light": 0.15,
"height": 2.0, "wall": { "height": 5.0 },
"edge_profile": "old", "edge": { "profile": "new" },
"profile": "flat-only"
@@ -7031,7 +7030,7 @@ mod tests {
assert_eq!(relief.pointer("/wall/height").and_then(|v| v.as_f64()), Some(5.0));
assert_eq!(relief.pointer("/edge/profile").and_then(|v| v.as_str()), Some("new"));
assert_eq!(relief.get("profile").and_then(|v| v.as_str()), Some("flat-only"), "no node spelling: kept");
- assert_eq!(relief.get("depth").and_then(|v| v.as_f64()), Some(0.15), "no `light` beside it: the alias is kept");
+ assert_eq!(relief.get("light").and_then(|v| v.as_f64()), Some(0.15));
let mut flat = String::new();
flatten_json_to_flat_props(&val, "", &mut flat);
assert_eq!(flat.lines().filter(|l| l.starts_with("bevel_height = ")).count(), 1, "{flat}");
diff --git a/src/scene/material.rs b/src/scene/material.rs
index 1604e26..f71ee70 100644
--- a/src/scene/material.rs
+++ b/src/scene/material.rs
@@ -632,7 +632,7 @@ mod tests {
frost compression=(f64)0.6 refraction=(f64)0.3
root corner_radius=(i64)24
}
- relief depth=(f64)0.08
+ relief light=(f64)0.08
}
}
"##;
@@ -649,7 +649,7 @@ mod tests {
plate material="glass" {
root corner_radius=(i64)24
}
- relief depth=(f64)0.08
+ relief light=(f64)0.08
}
}
"##;
@@ -773,7 +773,7 @@ mod tests {
}
}
plate blur=(bool)true material="plastic"
- relief depth=(f64)0.15 spec=(f64)0.5 shininess=(f64)20.0 curvature=(f64)0.1
+ relief light=(f64)0.15 spec=(f64)0.5 shininess=(f64)20.0 curvature=(f64)0.1
}
control material="ghost"
}