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 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,45 +739,61 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const scheduleReload = () => {
|
||||||
themeWatcher = fs.watch(themeFile, (eventType) => {
|
if (themeReloadTimer) {
|
||||||
if (eventType === "change") {
|
clearTimeout(themeReloadTimer);
|
||||||
// Debounce rapid changes
|
}
|
||||||
setTimeout(() => {
|
themeReloadTimer = setTimeout(() => {
|
||||||
try {
|
themeReloadTimer = undefined;
|
||||||
// Reload the theme
|
|
||||||
setGlobalTheme(loadTheme(currentThemeName!));
|
// Ignore stale timers after switching themes or stopping the watcher
|
||||||
// Notify callback (to invalidate UI)
|
if (currentThemeName !== watchedThemeName) {
|
||||||
if (onThemeChangeCallback) {
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
} 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;
|
||||||
|
|||||||
Reference in New Issue
Block a user