feat(dev): --theme-watch flag for live theme authoring (#1019)

Debug builds only: `--theme-watch <theme.css>` polls a local theme file
and pushes it into the running app on save (a lightweight Rust watcher
thread emits a Tauri event). The frontend installs the CSS under its
[data-theme='<id>'] selector and applies it, so the existing
syncInjectedThemes effect re-injects live — no zip re-import, no reload.
Double-gated (debug_assertions + import.meta.env.DEV); never wired in
production.
This commit is contained in:
Psychotoxical
2026-06-07 19:54:12 +02:00
committed by GitHub
parent aabd342a64
commit daa6fbbfd7
2 changed files with 54 additions and 0 deletions
+22
View File
@@ -32,6 +32,28 @@ export default function App() {
syncInjectedThemes(installedThemes);
}, [installedThemes]);
// Dev only: `--theme-watch <theme.css>` (debug builds) pushes a local theme's
// CSS in on every save. Install it under the id in its `[data-theme='<id>']`
// selector and apply it — the syncInjectedThemes effect above re-injects, so
// authoring is live without re-importing a zip. Never wired in production.
useEffect(() => {
if (!import.meta.env.DEV) return;
let unlisten: (() => void) | undefined;
void import('@tauri-apps/api/event').then(({ listen }) => {
const sub = listen<string>('theme-watch:css', ({ payload }) => {
const id = payload.match(/\[data-theme=['"]([^'"]+)['"]\]/)?.[1];
if (!id) return;
useInstalledThemesStore.getState().install({
id, name: id, author: 'dev', version: '0.0.0', description: '', mode: 'dark', css: payload, installedAt: Date.now(),
});
useThemeStore.getState().setTheme(id);
});
// Guard the mocked-in-tests case where listen() isn't a promise.
if (sub && typeof sub.then === 'function') sub.then(u => { unlisten = u; });
}).catch(() => {});
return () => unlisten?.();
}, []);
useEffect(() => {
document.documentElement.setAttribute('data-theme', effectiveTheme);
}, [effectiveTheme]);