The app's entire look — the scifi cyan palette, the king video background,
the scramble splash, overlay fades — is hard-coded inline across ~14 schema
files (233 set_property_* styling calls, 125 text_color touches) and has
even leaked into the node factories in node.rs (action_fg_color,
url_copy_*, scramble_color, menu role colors). The only variation
mechanism is a compile-time COLOR_SCHEME switch whose PaperLight arm is
dead code. There is no way to change the look at runtime, no defined
boundary between "style" and "structure", and no reset point themes could
start from. This change makes the UI themable at runtime by layering themes
on the property system's existing defaults/vals fallback, turning the
current look into one selectable theme (scifi) over a neutral minimal
baseline.
bin/app/src/prop/): defaults become installable on
live (already-linked) properties via a post-creation set_default_* API
for every type; defaults may hold SExpr; "effective expr" semantics —
is_expr/get_expr fall through vals → defaults, and get_value
never returns an unresolved SExpr (resolves via cache, else type
default). Factories opt in allow_exprs() for themeable props (it is
builder-only today, so live properties can never gain expr support).
Role becomes a bitflag set and properties gain
PropertyPermission (read mask, write mask) enforced on writes
(every mutating API already carries a role) and on attributed reads
(wrap layer, expr evaluation, RPC boundary), failing with
PropertyPermissionDenied — which turns "themes don't override
widget-owned properties" from convention into a hard invariant.PropertyRect has this); when_change handlers
stay invalidation-only./theme token node: a scene-root node holding the shared
themeable vocabulary as token properties whose defaults are the
minimal palette. Schema creation code wires styled properties to
tokens via helper functions ("classes") that install default-exprs
plus add_depend edges — one wiring, every wired widget tracks the
token forever, including nodes created at runtime. Themes can also
mint their own private tokens as tracked child nodes
(/theme/<name>), usable in overrides, gone on unload — themes are
not boxed in by the shared list.bin/app/src/theme/): Theme trait + registry;
apply/unload with nodes as the storage unit — theme-created
properties live on tracked nodes (private tokens under
/theme/<name>), theme tasks are pushed onto those nodes and cancel
with them; every theme modification is stamped with a new
Role::Theme (attribution on the event stream — vals stores no
authorship); unload cancels theme tasks first, then resets only the
recorded touched properties (a deep-walk reset is neither possible
nor safe), restores unbounded-list priors (e.g. nick_colors, which
have no defaults tier), removes theme-added dependency edges, and
unlinks tracked nodes — all as one atomic property batch. Theme
behavior (e.g. the netstatus overlay fade) is implemented as property
watchers, not schema hooks./setting/theme enum (minimal | scifi, default scifi),
persisted through the existing Setting pimpl, applied after schema
load, live-switched on change; the settings screen gains enum
rendering (enums currently display as "unknown").ColorScheme enum, the COLOR_SCHEME const, and
all two-arm style branches (collapsing to the DarkMode values) are
deleted across 14 files, including the dead window-linked bg
vector-art block.prop-defaults: post-creation default installation on live properties
(all types incl. SExpr), effective-expr resolution order for reads, and
f32-array expr evaluation with dependency-triggered re-evaluation.app-theme: the runtime theme system — token node and class wiring,
minimal baseline, theme apply/unload lifecycle (tracked nodes, tasks,
journal), atomic switching, the theme setting, and scifi as first
theme.(none — no existing specs in this repo; the property-system extensions are
new behavior captured in prop-defaults.)
bin/app/src/prop/mod.rs, src/prop/wrap.rs (default APIs, effective
exprs, f32-multi evaluation) + unit tests in the existing test module.bin/app/src/ui/ — color/font expr re-evaluation call sites in
widgets' when_change update paths.bin/app/src/app/node.rs (factory default scrub), src/setting.rs
(theme enum), src/app/schema/settings.rs (enum rendering).bin/app/src/app/schema/** (all files: PaperLight removal, minimal
defaults, token wiring) and src/app/mod.rs (apply theme after
schema::make).bin/app/src/theme/ module.bin/app; no new dependencies. Verified via
bin/app Makefile (make compile-dev, make compile-apk) and the
property unit tests.