fix(coding-agent): reload custom themes from disk watcher\n\ncloses #2417\ncloses #2003
This commit is contained in:
@@ -665,6 +665,7 @@ function setGlobalTheme(t: Theme): void {
|
||||
|
||||
let currentThemeName: string | undefined;
|
||||
let themeWatcher: fs.FSWatcher | undefined;
|
||||
let themeReloadTimer: NodeJS.Timeout | undefined;
|
||||
let onThemeChangeCallback: (() => void) | undefined;
|
||||
const registeredThemes = new Map<string, Theme>();
|
||||
|
||||
@@ -730,11 +731,7 @@ export function onThemeChange(callback: () => void): void {
|
||||
}
|
||||
|
||||
function startThemeWatcher(): void {
|
||||
// Stop existing watcher if any
|
||||
if (themeWatcher) {
|
||||
themeWatcher.close();
|
||||
themeWatcher = undefined;
|
||||
}
|
||||
stopThemeWatcher();
|
||||
|
||||
// Only watch if it's a custom theme (not built-in)
|
||||
if (!currentThemeName || currentThemeName === "dark" || currentThemeName === "light") {
|
||||
@@ -742,45 +739,61 @@ function startThemeWatcher(): void {
|
||||
}
|
||||
|
||||
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
|
||||
if (!fs.existsSync(themeFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
themeWatcher = fs.watch(themeFile, (eventType) => {
|
||||
if (eventType === "change") {
|
||||
// Debounce rapid changes
|
||||
setTimeout(() => {
|
||||
try {
|
||||
// Reload the theme
|
||||
setGlobalTheme(loadTheme(currentThemeName!));
|
||||
// Notify callback (to invalidate UI)
|
||||
if (onThemeChangeCallback) {
|
||||
onThemeChangeCallback();
|
||||
}
|
||||
} catch (_error) {
|
||||
// Ignore errors (file might be in invalid state while being edited)
|
||||
}
|
||||
}, 100);
|
||||
} else if (eventType === "rename") {
|
||||
// File was deleted or renamed - fall back to default theme
|
||||
setTimeout(() => {
|
||||
if (!fs.existsSync(themeFile)) {
|
||||
currentThemeName = "dark";
|
||||
setGlobalTheme(loadTheme("dark"));
|
||||
if (themeWatcher) {
|
||||
themeWatcher.close();
|
||||
themeWatcher = undefined;
|
||||
}
|
||||
if (onThemeChangeCallback) {
|
||||
onThemeChangeCallback();
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
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 {
|
||||
// Reload the theme from disk and refresh the registry cache
|
||||
const reloadedTheme = loadThemeFromPath(themeFile);
|
||||
registeredThemes.set(watchedThemeName, reloadedTheme);
|
||||
setGlobalTheme(reloadedTheme);
|
||||
// Notify callback (to invalidate UI)
|
||||
if (onThemeChangeCallback) {
|
||||
onThemeChangeCallback();
|
||||
}
|
||||
} catch (_error) {
|
||||
// Ignore errors (file might be in invalid state while being edited)
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
try {
|
||||
themeWatcher = fs.watch(customThemesDir, (_eventType, filename) => {
|
||||
if (currentThemeName !== watchedThemeName) {
|
||||
return;
|
||||
}
|
||||
if (!filename) {
|
||||
scheduleReload();
|
||||
return;
|
||||
}
|
||||
const changedFile = String(filename);
|
||||
if (changedFile !== watchedFileName) {
|
||||
return;
|
||||
}
|
||||
scheduleReload();
|
||||
});
|
||||
} catch (_error) {
|
||||
// Ignore errors starting watcher
|
||||
@@ -788,6 +801,10 @@ function startThemeWatcher(): void {
|
||||
}
|
||||
|
||||
export function stopThemeWatcher(): void {
|
||||
if (themeReloadTimer) {
|
||||
clearTimeout(themeReloadTimer);
|
||||
themeReloadTimer = undefined;
|
||||
}
|
||||
if (themeWatcher) {
|
||||
themeWatcher.close();
|
||||
themeWatcher = undefined;
|
||||
|
||||
Reference in New Issue
Block a user