feat(theme): themes as files (theme crate: toml subset + ints, contrast check, wire); whimsy dark + light; display reads themes/, serves THEME/THEME_NAME, init SET_THEME -> THEME_CHANGED to all windows, bar themed; k-ui follows; settings appearance; init keeps choice in sealed users/<id>/settings/, reapplies at sign-in + display restart

This commit is contained in:
2026-10-28 13:36:57 +01:00
parent 29becf297c
commit 6827e1455d
43 changed files with 1140 additions and 179 deletions

View File

@@ -9,7 +9,7 @@ mod capability;
mod decision;
mod fields;
mod manifest;
mod toml;
pub mod toml;
#[cfg(test)]
mod tests;

View File

@@ -52,7 +52,7 @@ fn set<T>(slot: &mut Option<T>, value: Option<T>, line: usize) -> Result<(), Err
fn text<'a>(value: Value<'a>) -> Option<&'a str> {
match value {
Value::Str(s) => Some(s),
Value::List(_) => None,
Value::List(_) | Value::Int(_) => None,
}
}

View File

@@ -167,6 +167,8 @@ fn only_the_subset_parses() {
"id = kos.snake", // bare value
"id \"kos.snake\"", // no =
"[app", // unclosed table
"id = 12x", // not a whole number
"id = 1.5", // no fractions
] {
assert!(
matches!(

View File

@@ -1,6 +1,7 @@
//! The strict TOML subset manifests and decisions are written in:
//! `[table]` headers, `key = "string"`, `key = ["a", "b"]` on one line,
//! `#` comments. Strings are printable ASCII without `"` or `\`.
//! `key = 42` (a whole number), `#` comments. Strings are printable ASCII
//! without `"` or `\`.
use crate::Error;
@@ -18,6 +19,8 @@ pub enum Value<'a> {
Str(&'a str),
/// A one-line array of strings, already checked.
List(List<'a>),
/// Digits only: no sign, no fraction.
Int(u32),
}
/// The inside of `[...]`: iterate it for its strings.
@@ -119,6 +122,10 @@ fn string(s: &str, line: usize) -> Result<&str, Error> {
}
fn value_of(s: &str, line: usize) -> Result<Value<'_>, Error> {
if s.bytes().next().is_some_and(|b| b.is_ascii_digit()) {
let digits = s.bytes().all(|b| b.is_ascii_digit());
return digits.then(|| s.parse().ok()).flatten().map(Value::Int).ok_or(Error::Syntax { line });
}
let Some(inner) = s.strip_prefix('[') else {
return string(s, line).map(Value::Str);
};