more consistent ipcRenderer.on handling, better maximize observer implementation

This commit is contained in:
Egor 2025-09-18 22:57:05 -07:00
parent a07ee2711f
commit dd5a9457e6
6 changed files with 77 additions and 66 deletions

View file

@ -38,17 +38,20 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
setGpuMetrics(metrics); setGpuMetrics(metrics);
}; };
window.electronAPI.monitoring.onCpuMetrics(handleCpuMetrics); const cleanupCpu =
window.electronAPI.monitoring.onMemoryMetrics(handleMemoryMetrics); window.electronAPI.monitoring.onCpuMetrics(handleCpuMetrics);
window.electronAPI.monitoring.onGpuMetrics(handleGpuMetrics); const cleanupMemory =
void window.electronAPI.monitoring.start(); window.electronAPI.monitoring.onMemoryMetrics(handleMemoryMetrics);
const cleanupGpu =
window.electronAPI.monitoring.onGpuMetrics(handleGpuMetrics);
const stopMonitoring = window.electronAPI.monitoring.start();
return () => { return () => {
isMounted = false; isMounted = false;
window.electronAPI.monitoring.removeCpuMetricsListener(); cleanupCpu();
window.electronAPI.monitoring.removeMemoryMetricsListener(); cleanupMemory();
window.electronAPI.monitoring.removeGpuMetricsListener(); cleanupGpu();
window.electronAPI.monitoring.stop(); stopMonitoring();
}; };
}, [maxDataPoints]); }, [maxDataPoints]);

View file

@ -39,18 +39,12 @@ export const TitleBar = ({
const [settingsModalOpen, setSettingsModalOpen] = useState(false); const [settingsModalOpen, setSettingsModalOpen] = useState(false);
useEffect(() => { useEffect(() => {
const handleMaximize = () => setIsMaximized(true); const cleanup = window.electronAPI.app.onWindowStateToggle(() =>
const handleUnmaximize = () => setIsMaximized(false); setIsMaximized((prev) => !prev)
);
window.electronAPI.on('window-maximized', handleMaximize); return cleanup;
window.electronAPI.on('window-unmaximized', handleUnmaximize);
return () => {
window.electronAPI.removeListener('window-maximized', handleMaximize);
window.electronAPI.removeListener('window-unmaximized', handleUnmaximize);
};
}, []); }, []);
return ( return (
<AppShell.Header <AppShell.Header
style={{ display: 'flex', flexDirection: 'column', border: 'none' }} style={{ display: 'flex', flexDirection: 'column', border: 'none' }}

View file

@ -259,11 +259,10 @@ export const useKoboldVersions = () => {
} }
}; };
window.electronAPI.kobold.onDownloadProgress?.(handleProgress); const cleanup =
window.electronAPI.kobold.onDownloadProgress(handleProgress);
return () => { return cleanup;
window.electronAPI.kobold.removeAllListeners?.('download-progress');
};
}, [downloading]); }, [downloading]);
return { return {

View file

@ -273,14 +273,14 @@ export function setupIPCHandlers() {
ipcMain.handle('dependencies:isUvAvailable', () => isUvAvailable()); ipcMain.handle('dependencies:isUvAvailable', () => isUvAvailable());
ipcMain.handle('monitoring:start', () => { ipcMain.on('monitoring:start', () => {
const mainWindow = getMainWindow(); const mainWindow = getMainWindow();
if (mainWindow) { if (mainWindow) {
startMonitoring(mainWindow); startMonitoring(mainWindow);
} }
}); });
ipcMain.handle('monitoring:stop', () => stopMonitoring()); ipcMain.on('monitoring:stop', () => stopMonitoring());
ipcMain.handle('app:checkForUpdates', () => checkForUpdates()); ipcMain.handle('app:checkForUpdates', () => checkForUpdates());

View file

@ -8,6 +8,11 @@ import type {
MonitoringAPI, MonitoringAPI,
UpdaterAPI, UpdaterAPI,
} from '@/types/electron'; } from '@/types/electron';
import type {
CpuMetrics,
MemoryMetrics,
GpuMetrics,
} from '@/main/modules/monitoring';
const koboldAPI: KoboldAPI = { const koboldAPI: KoboldAPI = {
getInstalledVersions: () => ipcRenderer.invoke('kobold:getInstalledVersions'), getInstalledVersions: () => ipcRenderer.invoke('kobold:getInstalledVersions'),
@ -42,10 +47,13 @@ const koboldAPI: KoboldAPI = {
ipcRenderer.invoke('kobold:selectModelFile', title), ipcRenderer.invoke('kobold:selectModelFile', title),
stopKoboldCpp: () => ipcRenderer.invoke('kobold:stopKoboldCpp'), stopKoboldCpp: () => ipcRenderer.invoke('kobold:stopKoboldCpp'),
onDownloadProgress: (callback) => { onDownloadProgress: (callback) => {
ipcRenderer.on( const handler = (_: IpcRendererEvent, progress: number) =>
'download-progress', callback(progress);
(_: IpcRendererEvent, progress: number) => callback(progress) ipcRenderer.on('download-progress', handler);
);
return () => {
ipcRenderer.removeListener('download-progress', handler);
};
}, },
onInstallDirChanged: (callback) => { onInstallDirChanged: (callback) => {
const handler = (_: IpcRendererEvent, newPath: string) => callback(newPath); const handler = (_: IpcRendererEvent, newPath: string) => callback(newPath);
@ -71,9 +79,6 @@ const koboldAPI: KoboldAPI = {
ipcRenderer.removeListener('kobold-output', handler); ipcRenderer.removeListener('kobold-output', handler);
}; };
}, },
removeAllListeners: (channel) => {
ipcRenderer.removeAllListeners(channel);
},
}; };
const appAPI: AppAPI = { const appAPI: AppAPI = {
@ -95,6 +100,17 @@ const appAPI: AppAPI = {
downloadUpdate: () => ipcRenderer.invoke('app:downloadUpdate'), downloadUpdate: () => ipcRenderer.invoke('app:downloadUpdate'),
quitAndInstall: () => ipcRenderer.invoke('app:quitAndInstall'), quitAndInstall: () => ipcRenderer.invoke('app:quitAndInstall'),
isUpdateDownloaded: () => ipcRenderer.invoke('app:isUpdateDownloaded'), isUpdateDownloaded: () => ipcRenderer.invoke('app:isUpdateDownloaded'),
onWindowStateToggle: (callback) => {
const handler = () => callback();
ipcRenderer.on('window-maximized', handler);
ipcRenderer.on('window-unmaximized', handler);
return () => {
ipcRenderer.removeListener('window-maximized', handler);
ipcRenderer.removeListener('window-unmaximized', handler);
};
},
}; };
const configAPI: ConfigAPI = { const configAPI: ConfigAPI = {
@ -113,25 +129,39 @@ const dependenciesAPI: DependenciesAPI = {
}; };
const monitoringAPI: MonitoringAPI = { const monitoringAPI: MonitoringAPI = {
start: () => ipcRenderer.invoke('monitoring:start'), start: () => {
stop: () => ipcRenderer.invoke('monitoring:stop'), ipcRenderer.send('monitoring:start');
return () => {
ipcRenderer.send('monitoring:stop');
};
},
onCpuMetrics: (callback) => { onCpuMetrics: (callback) => {
ipcRenderer.on('cpu-metrics', (_, metrics) => callback(metrics)); const handler = (_: IpcRendererEvent, metrics: CpuMetrics) =>
callback(metrics);
ipcRenderer.on('cpu-metrics', handler);
return () => {
ipcRenderer.removeListener('cpu-metrics', handler);
};
}, },
onMemoryMetrics: (callback) => { onMemoryMetrics: (callback) => {
ipcRenderer.on('memory-metrics', (_, metrics) => callback(metrics)); const handler = (_: IpcRendererEvent, metrics: MemoryMetrics) =>
callback(metrics);
ipcRenderer.on('memory-metrics', handler);
return () => {
ipcRenderer.removeListener('memory-metrics', handler);
};
}, },
onGpuMetrics: (callback) => { onGpuMetrics: (callback) => {
ipcRenderer.on('gpu-metrics', (_, metrics) => callback(metrics)); const handler = (_: IpcRendererEvent, metrics: GpuMetrics) =>
}, callback(metrics);
removeCpuMetricsListener: () => { ipcRenderer.on('gpu-metrics', handler);
ipcRenderer.removeAllListeners('cpu-metrics');
}, return () => {
removeMemoryMetricsListener: () => { ipcRenderer.removeListener('gpu-metrics', handler);
ipcRenderer.removeAllListeners('memory-metrics'); };
},
removeGpuMetricsListener: () => {
ipcRenderer.removeAllListeners('gpu-metrics');
}, },
}; };
@ -152,10 +182,4 @@ contextBridge.exposeInMainWorld('electronAPI', {
dependencies: dependenciesAPI, dependencies: dependenciesAPI,
monitoring: monitoringAPI, monitoring: monitoringAPI,
updater: updaterAPI, updater: updaterAPI,
on: (channel: string, callback: (...args: unknown[]) => void) => {
ipcRenderer.on(channel, (_, ...args) => callback(...args));
},
removeListener: (channel: string, callback: (...args: unknown[]) => void) => {
ipcRenderer.removeListener(channel, callback);
},
}); });

View file

@ -129,11 +129,10 @@ export interface KoboldAPI {
parseConfigFile: (filePath: string) => Promise<KoboldConfig | null>; parseConfigFile: (filePath: string) => Promise<KoboldConfig | null>;
selectModelFile: (title?: string) => Promise<string | null>; selectModelFile: (title?: string) => Promise<string | null>;
stopKoboldCpp: () => void; stopKoboldCpp: () => void;
onDownloadProgress: (callback: (progress: number) => void) => void; onDownloadProgress: (callback: (progress: number) => void) => () => void;
onInstallDirChanged: (callback: (newPath: string) => void) => () => void; onInstallDirChanged: (callback: (newPath: string) => void) => () => void;
onVersionsUpdated: (callback: () => void) => () => void; onVersionsUpdated: (callback: () => void) => () => void;
onKoboldOutput: (callback: (data: string) => void) => () => void; onKoboldOutput: (callback: (data: string) => void) => () => void;
removeAllListeners: (channel: string) => void;
} }
export interface VersionInfo { export interface VersionInfo {
@ -171,6 +170,7 @@ export interface AppAPI {
downloadUpdate: () => Promise<boolean>; downloadUpdate: () => Promise<boolean>;
quitAndInstall: () => Promise<void>; quitAndInstall: () => Promise<void>;
isUpdateDownloaded: () => Promise<boolean>; isUpdateDownloaded: () => Promise<boolean>;
onWindowStateToggle: (callback: () => void) => () => void;
} }
export interface ConfigAPI { export interface ConfigAPI {
@ -188,14 +188,10 @@ export interface DependenciesAPI {
} }
export interface MonitoringAPI { export interface MonitoringAPI {
start: () => Promise<void>; start: () => () => void;
stop: () => Promise<void>; onCpuMetrics: (callback: (metrics: CpuMetrics) => void) => () => void;
onCpuMetrics: (callback: (metrics: CpuMetrics) => void) => void; onMemoryMetrics: (callback: (metrics: MemoryMetrics) => void) => () => void;
onMemoryMetrics: (callback: (metrics: MemoryMetrics) => void) => void; onGpuMetrics: (callback: (metrics: GpuMetrics) => void) => () => void;
onGpuMetrics: (callback: (metrics: GpuMetrics) => void) => void;
removeCpuMetricsListener: () => void;
removeMemoryMetricsListener: () => void;
removeGpuMetricsListener: () => void;
} }
export interface UpdaterAPI { export interface UpdaterAPI {
@ -217,11 +213,6 @@ declare global {
dependencies: DependenciesAPI; dependencies: DependenciesAPI;
monitoring: MonitoringAPI; monitoring: MonitoringAPI;
updater: UpdaterAPI; updater: UpdaterAPI;
on: (channel: string, callback: (...args: unknown[]) => void) => void;
removeListener: (
channel: string,
callback: (...args: unknown[]) => void
) => void;
}; };
} }
} }