host: site themes are the host's (AppConfig::with_themes, commands::THEME)

theme_commands serves theme_get / theme_set / theme_preview / theme_scopes_get / theme_scopes_set
(shell-only, each checks its caller first; testkit::assert_theme_guards) over the theme state the
host now keeps: the owner's theme, account and pane themes, the Theme Builder preview. The app
only names its built-in themes and the two files (ThemeConfig). apply() themes every site tab
(script, cached Google font bytes, native background); page_scripts()/tab_background() serve a new
document; set_theme/set_scopes/set_preview are the app's own writers (backup import, dev mode).
A font install or removal re-themes the tabs in the host itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CRTkafa 2026-10-01 14:10:43 +07:00
parent 260646bbc0
commit 82382293a2
6 changed files with 276 additions and 7 deletions

View file

@ -96,6 +96,9 @@ pub struct AppConfig {
/// [`commands::DEV`] and runs the watcher while it is on; the theme file is the app's
/// ([`SiteHooks::dev_theme_apply`]).
pub dev: bool,
/// Site themes (the owner's theme, account and pane themes, the live preview): serves
/// [`commands::THEME`]; the app names its built-in themes and the files they are saved in.
pub themes: Option<&'static crate::theme_commands::ThemeConfig>,
/// Native updates and the hotfix channel: serves [`commands::UPDATE`] and runs the update loop.
/// Needs `updater` (the plugin) and the `net` feature.
#[cfg(feature = "net")]
@ -126,6 +129,7 @@ impl AppConfig {
fonts: false,
sync: false,
dev: false,
themes: None,
#[cfg(feature = "net")]
updates: None,
}
@ -144,6 +148,12 @@ impl AppConfig {
self
}
/// Serves the theme commands ([`commands::THEME`]) with these built-in themes.
pub const fn with_themes(mut self, themes: &'static crate::theme_commands::ThemeConfig) -> Self {
self.themes = Some(themes);
self
}
/// Serves the sync commands ([`commands::SYNC`]).
pub const fn with_sync(mut self) -> Self {
self.sync = true;
@ -246,8 +256,8 @@ pub trait SiteHooks: Send + Sync + 'static {
/// A tab webview was created.
fn tab_created(&self, _app: &AppHandle) {}
/// A Google font was installed or removed ([`commands::GFONTS`]): give the site tabs their
/// theme, and the font bytes, again.
/// A Google font was installed or removed ([`commands::GFONTS`]); the host has already given the
/// site tabs their theme, and the font bytes, again ([`crate::theme_commands::apply`]).
fn fonts_changed(&self, _app: &AppHandle) {}
/// The app's native settings as backup sections (themes, plugins, tweaks, ... keyed by section
@ -1246,6 +1256,11 @@ fn dev_handler() -> impl Fn(tauri::ipc::Invoke<Wry>) -> bool + Send + Sync + 'st
crate::dev_commands::handler()
}
/// The theme commands ([`commands::THEME`]).
fn theme_handler() -> impl Fn(tauri::ipc::Invoke<Wry>) -> bool + Send + Sync + 'static {
crate::theme_commands::handler()
}
/// The workspaces and sync commands ([`commands::WORKSPACES`], [`commands::SYNC`]).
fn sync_handler() -> impl Fn(tauri::ipc::Invoke<Wry>) -> bool + Send + Sync + 'static {
crate::sync_commands::handler()
@ -1314,6 +1329,7 @@ impl App {
let update_handler = update_handler();
let sync_handler = sync_handler();
let dev_handler = dev_handler();
let theme_handler = theme_handler();
let backup_handler = backup_handler();
let builder = tauri::Builder::<Wry>::default()
// Must be first: a second launch focuses the running window instead of fighting over
@ -1397,6 +1413,7 @@ impl App {
crate::media_commands::init(&handle);
crate::sync_commands::init(&handle);
crate::dev_commands::init(&handle);
crate::theme_commands::init(&handle);
#[cfg(feature = "net")]
crate::fonts_commands::init(&handle);
#[cfg(feature = "net")]
@ -1429,6 +1446,8 @@ impl App {
dev_handler(invoke)
} else if commands::BACKUP.contains(&invoke.message.command()) {
backup_handler(invoke)
} else if commands::THEME.contains(&invoke.message.command()) {
theme_handler(invoke)
} else if commands::is_host(invoke.message.command()) {
host_handler(invoke)
} else {