fix(coding-agent): reload custom themes from disk watcher\n\ncloses #2417\ncloses #2003

This commit is contained in:
Mario Zechner
2026-03-19 21:53:05 +01:00
parent e0f85a3b5e
commit 3620adb3e2

View File

@@ -665,6 +665,7 @@ function setGlobalTheme(t: Theme): void {
let currentThemeName: string | undefined; let currentThemeName: string | undefined;
let themeWatcher: fs.FSWatcher | undefined; let themeWatcher: fs.FSWatcher | undefined;
let themeReloadTimer: NodeJS.Timeout | undefined;
let onThemeChangeCallback: (() => void) | undefined; let onThemeChangeCallback: (() => void) | undefined;
const registeredThemes = new Map<string, Theme>(); const registeredThemes = new Map<string, Theme>();
@@ -730,11 +731,7 @@ export function onThemeChange(callback: () => void): void {
} }
function startThemeWatcher(): void { function startThemeWatcher(): void {
// Stop existing watcher if any stopThemeWatcher();
if (themeWatcher) {
themeWatcher.close();
themeWatcher = undefined;
}
// Only watch if it's a custom theme (not built-in) // Only watch if it's a custom theme (not built-in)
if (!currentThemeName || currentThemeName === "dark" || currentThemeName === "light") { if (!currentThemeName || currentThemeName === "dark" || currentThemeName === "light") {
@@ -742,21 +739,37 @@ function startThemeWatcher(): void {
} }
const customThemesDir = getCustomThemesDir(); const customThemesDir = getCustomThemesDir();
const themeFile = path.join(customThemesDir, `${currentThemeName}.json`); const watchedThemeName = currentThemeName;
const watchedFileName = `${watchedThemeName}.json`;
const themeFile = path.join(customThemesDir, watchedFileName);
// Only watch if the file exists // Only watch if the file exists
if (!fs.existsSync(themeFile)) { if (!fs.existsSync(themeFile)) {
return; return;
} }
const scheduleReload = () => {
if (themeReloadTimer) {
clearTimeout(themeReloadTimer);
}
themeReloadTimer = setTimeout(() => {
themeReloadTimer = undefined;
// Ignore stale timers after switching themes or stopping the watcher
if (currentThemeName !== watchedThemeName) {
return;
}
// Keep the last successfully loaded theme active if the file is temporarily missing
if (!fs.existsSync(themeFile)) {
return;
}
try { try {
themeWatcher = fs.watch(themeFile, (eventType) => { // Reload the theme from disk and refresh the registry cache
if (eventType === "change") { const reloadedTheme = loadThemeFromPath(themeFile);
// Debounce rapid changes registeredThemes.set(watchedThemeName, reloadedTheme);
setTimeout(() => { setGlobalTheme(reloadedTheme);
try {
// Reload the theme
setGlobalTheme(loadTheme(currentThemeName!));
// Notify callback (to invalidate UI) // Notify callback (to invalidate UI)
if (onThemeChangeCallback) { if (onThemeChangeCallback) {
onThemeChangeCallback(); onThemeChangeCallback();
@@ -765,22 +778,22 @@ function startThemeWatcher(): void {
// Ignore errors (file might be in invalid state while being edited) // Ignore errors (file might be in invalid state while being edited)
} }
}, 100); }, 100);
} else if (eventType === "rename") { };
// File was deleted or renamed - fall back to default theme
setTimeout(() => { try {
if (!fs.existsSync(themeFile)) { themeWatcher = fs.watch(customThemesDir, (_eventType, filename) => {
currentThemeName = "dark"; if (currentThemeName !== watchedThemeName) {
setGlobalTheme(loadTheme("dark")); return;
if (themeWatcher) {
themeWatcher.close();
themeWatcher = undefined;
} }
if (onThemeChangeCallback) { if (!filename) {
onThemeChangeCallback(); scheduleReload();
return;
} }
const changedFile = String(filename);
if (changedFile !== watchedFileName) {
return;
} }
}, 100); scheduleReload();
}
}); });
} catch (_error) { } catch (_error) {
// Ignore errors starting watcher // Ignore errors starting watcher
@@ -788,6 +801,10 @@ function startThemeWatcher(): void {
} }
export function stopThemeWatcher(): void { export function stopThemeWatcher(): void {
if (themeReloadTimer) {
clearTimeout(themeReloadTimer);
themeReloadTimer = undefined;
}
if (themeWatcher) { if (themeWatcher) {
themeWatcher.close(); themeWatcher.close();
themeWatcher = undefined; themeWatcher = undefined;