Settings Component¶
Purpose¶
The Settings component provides the central UI for runtime configuration and operational settings changes.
Main locations¶
app/core/components/settings/ui/routes.pyapp/core/components/settings/ui/settings_ui.py
Responsibilities¶
- register the settings route
- render platform configuration forms
- expose runtime-editable settings to operators
- provide a UI entry point for configuration workflows such as auth settings updates
Events¶
The Settings route layer does not currently act as a major direct event publisher or subscriber on the global bus.
UI endpoint¶
/settings
Runtime-editable settings¶
The System tab of the Settings UI edits a curated set of fields (EDITABLE_SETTINGS in app/config.py), grouped into Application, Localization, Theming, and Plugins categories. Values saved here are persisted to Vault (lyndrix/core/settings) and re-applied on boot via Settings.hydrate_from_vault() — unless the matching environment variable is set, in which case the env var always wins. This is what lets UI-saved settings survive a restart while keeping env vars authoritative.
Theming¶
Lyndrix Core ships a dynamic theme engine (app/core/theming/, exported through core.api). Themes are data, not code.
Two layers¶
Each theme lives under app/assets/themes/<id>/ and has two files:
- Token layer —
tokens.json: semantic colour tokens withlight/darkvariants (primary,secondary,accent,bg_body,text_muted, …). Since 1.0 the glass & border surfaces are colour tokens too (surface_glass,elevated_glass,glass_border,border_accent,border_soft) — rgba values with alpha, so a theme can tone the glass effect down or off; the engine emits them as--lx-surface-glass/--lx-elevated-glass/--lx-glass-border/--lx-border/--lx-border-soft, consumed by both the React and NiceGUI UIs. - Component layer —
components.json: maps everyUIStylesclass-attribute name to a Tailwind class string.
The theme validator (core/theming/schema.py) backfills any missing keys with UIStyles defaults, so partial themes are valid.
Runtime model¶
get_theme_engine()returns the singletonThemeEngine.resolve_component_styles(theme_id=None, plugin_id=None)returns the merged style map (active theme + optional plugin overrides).resolve_runtime_palette(dark)returns the Quasar/NiceGUI colour palette for the active theme.list_available_themes()enumerates theme IDs on disk.ThemePack,ThemeTokens,ThemeComponents, andload_theme_packare the data models, also exported fromcore.api.
Configuration¶
| Variable | Default | Purpose |
|---|---|---|
THEME_ENGINE_ENABLED |
True |
Enable the dynamic theme engine. |
THEME_DB_OVERRIDES_ENABLED |
False |
Allow theme overrides stored in the DB to take effect. |
DEFAULT_THEME_ID |
default |
Theme applied when no per-user selection exists. |
All three are also editable from the Settings UI (Theming category).
Using theme styles in plugins¶
Always go through core.api — never import from ui.theme directly:
from core.api import UIStyles
ui.card().classes(UIStyles.CARD_BASE)
ui.label("Title").classes(UIStyles.TITLE_H2)
ui.label("Hint").classes(UIStyles.TEXT_MUTED)
A plugin can override specific style keys for its own pages without touching the global theme:
def setup(ctx):
ctx.register_theme_overrides({
"CARD_BASE": "p-4 rounded-xl border border-zinc-700 bg-zinc-900",
})
Overrides are in-memory and apply only when that plugin's pages resolve styles with their own plugin_id.
Creating a custom theme¶
- Copy
app/assets/themes/default/tokens.jsontoapp/assets/themes/<your-id>/tokens.jsonand change the colours. - Copy
components.jsonand adjust Tailwind strings (omit any keys you want defaulted — they are backfilled). - Select the theme in Settings → Appearance, or set
DEFAULT_THEME_ID=<your-id>.
Alternatively, upload a ZIP containing both JSON files via Settings → Appearance → Upload Theme.
Typography and font override¶
The Settings UI (and global app shell) typography is configured in app/ui/theme.py via CSS variables inside :root:
--lx-font-sans--lx-font-mono
Current strategy:
- prefer Ubuntu system sans for UI text
- use JetBrains Mono for code/mono text
If a browser appears to replace fonts unexpectedly, verify both the browser-computed font and Linux font fallback.
Browser check (DevTools Console)¶
(() => {
const picks = ['body', 'h1', 'h2', 'p', 'button', 'input', '.q-btn', '.q-card'];
for (const sel of picks) {
const el = document.querySelector(sel);
if (!el) continue;
console.log(`${sel}: ${getComputedStyle(el).fontFamily}`);
}
for (const f of document.fonts) {
console.log(`${f.family} | status=${f.status} | weight=${f.weight} | style=${f.style}`);
}
})();
Ubuntu fontconfig fallback check¶
fc-match -s "Ubuntu" | head -n 10
fc-match -s "JetBrains Mono" | head -n 10
fc-match -s "sans-serif" | head -n 10
Notes:
- run the JavaScript snippet in browser DevTools, not in bash
- if fonts still look stale after changes, hard reload (
Ctrl+Shift+R)