mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
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:
@@ -115,6 +115,38 @@ pub fn run() {
|
|||||||
let _ = window.set_title("Psysonic (Dev)");
|
let _ = window.set_title("Psysonic (Dev)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Dev: `--theme-watch <theme.css>` live theme reload ─────────
|
||||||
|
// Poll a local theme.css and push it into the running app on save,
|
||||||
|
// so theme authors get a live loop without re-importing a zip. The
|
||||||
|
// frontend (dev only) installs it under the id in its
|
||||||
|
// `[data-theme='<id>']` selector and applies it. Dev-builds only.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
if let Some(i) = args.iter().position(|a| a == "--theme-watch") {
|
||||||
|
match args.get(i + 1).cloned() {
|
||||||
|
Some(path) => {
|
||||||
|
eprintln!("[theme-watch] watching {path}");
|
||||||
|
let handle = app.handle().clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let p = std::path::PathBuf::from(&path);
|
||||||
|
let mut last_css = String::new();
|
||||||
|
loop {
|
||||||
|
if let Ok(css) = std::fs::read_to_string(&p) {
|
||||||
|
if css != last_css {
|
||||||
|
last_css = css.clone();
|
||||||
|
let _ = handle.emit("theme-watch:css", css);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(300));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => eprintln!("[theme-watch] usage: --theme-watch <path/to/theme.css>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Analysis cache (SQLite) ───────────────────────────────────
|
// ── Analysis cache (SQLite) ───────────────────────────────────
|
||||||
{
|
{
|
||||||
let cache = analysis_cache::AnalysisCache::init(app.handle())
|
let cache = analysis_cache::AnalysisCache::init(app.handle())
|
||||||
|
|||||||
+22
@@ -32,6 +32,28 @@ export default function App() {
|
|||||||
syncInjectedThemes(installedThemes);
|
syncInjectedThemes(installedThemes);
|
||||||
}, [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(() => {
|
useEffect(() => {
|
||||||
document.documentElement.setAttribute('data-theme', effectiveTheme);
|
document.documentElement.setAttribute('data-theme', effectiveTheme);
|
||||||
}, [effectiveTheme]);
|
}, [effectiveTheme]);
|
||||||
|
|||||||
Reference in New Issue
Block a user