6.2 KiB
Changes & Rationale
This document explains how the codebase was improved and why. Each item notes the original behavior, the change, and the reasoning.
1. Three-tier design-token architecture
Before: Tokens were split across streem_theme/colors.rs,
streem_theme/shapes.rs, streem_theme/typography.rs (writing into
mod.widgets.*), while the actual themes lived in streem_theme/mod.rs
(mod.streem_theme.*). The token files were also not declared as Rust
modules anywhere, so they were effectively dead code that never ran.
After: A clean 3-tier system:
streem_design/{palette,shapes,typography}.rs— tier-1 primitives.streem_theme/mod.rs— tier-2 semantic themes that bind roles to primitives.- Widgets — read only the active theme (
s_themes) / language (s_i18n).
Why: This is the standard, scalable token model. Changing one palette entry re-brands everything; widgets never reference raw values.
Registration note: the tier-1 modules use
script_eval!(runtime mutation of themodobject), not thescript_mod!macro form. Thescript_mod!form returns a value to be applied to a component and does not eagerly install a fresh top-level namespace likemod.streem_design.*, which is exactly why the originalcolors.rs/shapes.rs/typography.rs(written withscript_mod!but never wired up) produced "streem_designnot found" errors.script_eval!installs the namespace immediately, matching the workingstreem_theme/streem_i18npattern. Insidescript_eval!there is no preludeuse, so fonts are referenced by their fully-qualified path (mod.theme.font_bold) rather than the baretheme.font_boldalias.
2. App-owned namespace (mod.streem_design.*)
Before: Tokens were written into mod.widgets.*.
After: All app tokens live under mod.streem_design.*.
Why: mod.widgets.* is the framework's widget namespace; writing app
tokens there is confusing and risks collisions. An app-owned namespace makes
ownership explicit.
3. Themes reference palette primitives (single source of truth)
Before: streem_theme/mod.rs inlined hex literals (color_bg: #FFF7ED,
color_primary: #D97706, …), duplicating values and divorcing themes from any
shared palette.
After: Each theme role points at a palette token
(color_primary: mod.streem_design.palette.amber_600).
Why: One palette edit now propagates to every theme that uses it; the palette becomes the single source of truth.
4. Fixed the unused color_text role
Before: Both themes defined color_text, but the UI never used it — body
text relied on widget defaults (white), which is invisible on light surfaces.
After: Semantic roles (color_text, color_text_secondary,
color_text_on_primary) are explicitly applied to the title, subtitle,
status, and section labels.
Why: Per the Splash manual, default text color is white; light themes must set dark text explicitly or it disappears (white-on-white).
5. Live, data-driven status label
Before: The DSL hard-coded text: "Current: light | reapply: 0", a
string that could drift out of sync with actual state.
After: The label starts neutral and is always written from Rust state via
refresh_status (Current: {theme} / {lang} | reapply: {n}), called on
startup, on every toggle, and on live-edit.
Why: A single source of truth prevents the displayed status from lying.
6. Generic theme cycling (no rigid 2-state enum)
Before: A ThemeState { Light, Dark } enum with toggled() hard-coded the
two-theme assumption.
After: The host tracks theme_idx / lang_idx into ordered name lists
(THEMES, LANGS) that mirror theme_names / lang_names in script. Toggling
advances the index modulo the list length.
Why: Adding a third theme (or third language) now requires no host changes to the cycling logic — just extend the lists.
7. Runtime language switching
Before: i18n existed but was never wired to the UI, and it defaulted to
Swahili (s_i18n = sw), which looked like a leftover.
After: A Language button cycles en/sw; the active language is
selected alongside the theme in the single reapply reload path. Default is
now English.
Why: Demonstrates that the same swap-and-reload mechanism localizes the UI, not just re-colors it.
8. Single reload path
Before: apply_theme and reload_ui_from_rust_state overlapped and both
re-applied the theme; the flow was easy to get out of sync.
After: One reapply(cx) selects the active theme+language in the VM,
reloads the script tree, refreshes the status label, and redraws. handle_*
methods call it once when something changes.
Why: Less duplication, clearer control flow, no double application.
9. Hardened Event::LiveEdit
Before: On LiveEdit, the app called apply_theme (which re-evaluated the
theme but could leave i18n/state inconsistent after a hot reload).
After: On LiveEdit, the app re-selects the active theme and language in
the VM and refreshes the status, so a hot reload preserves the user's current
selection instead of reverting to script defaults.
Why: Hot-reloading code shouldn't silently reset the user's theme/language.
10. Expanded, polished UI consuming the tokens
Before: A minimal toggle + two markup cards with magic-number padding and
inline font_size values.
After: A header (title/subtitle), a control bar (themed buttons + live
status), and two bordered surface cards — all driven by the spacing scale,
typography styles, semantic colors, and new_batch: true on text-bearing
backgrounds (per the Splash batching guidance, so text never hides behind its
own background).
Why: Shows the design system actually being consumed end-to-end and follows the documented layout/batching best practices.
11. Better tests
Before: One test that checked the enum toggles back and forth.
After: Tests cover full theme/language cycling and verify the modulo guards never index out of range.
12. Build ergonomics
Cargo.tomldocuments the expected sibling-of-Makepad layout for the path dependency and provides a commented, branch-pinned git fallback.- Added a
markupfeature (default on) for the Markdown/Html widgets. - Added
.gitignorefortarget/.