better logger util

This commit is contained in:
lone-cloud 2025-08-31 14:25:27 -07:00
parent 4420a62395
commit 1298debaa8
17 changed files with 130 additions and 167 deletions

View file

@ -11,7 +11,7 @@ import { ScreenTransition } from '@/components/ScreenTransition';
import { TitleBar } from '@/components/TitleBar'; import { TitleBar } from '@/components/TitleBar';
import { useUpdateChecker } from '@/hooks/useUpdateChecker'; import { useUpdateChecker } from '@/hooks/useUpdateChecker';
import { useKoboldVersions } from '@/hooks/useKoboldVersions'; import { useKoboldVersions } from '@/hooks/useKoboldVersions';
import { Logger } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
import { TITLEBAR_HEIGHT } from '@/constants'; import { TITLEBAR_HEIGHT } from '@/constants';
import type { DownloadItem } from '@/types/electron'; import type { DownloadItem } from '@/types/electron';
import type { InterfaceTab, FrontendPreference, Screen } from '@/types'; import type { InterfaceTab, FrontendPreference, Screen } from '@/types';
@ -46,7 +46,7 @@ export const App = () => {
useEffect(() => { useEffect(() => {
const checkInstallation = async () => { const checkInstallation = async () => {
await Logger.safeExecute(async () => { await safeExecute(async () => {
const [currentVersion, hasSeenWelcome, preference] = await Promise.all([ const [currentVersion, hasSeenWelcome, preference] = await Promise.all([
window.electronAPI.kobold.getCurrentVersion(), window.electronAPI.kobold.getCurrentVersion(),
window.electronAPI.config.get('hasSeenWelcome') as Promise<boolean>, window.electronAPI.config.get('hasSeenWelcome') as Promise<boolean>,
@ -80,7 +80,7 @@ export const App = () => {
}, []); }, []);
const handleBinaryUpdate = async (download: DownloadItem) => { const handleBinaryUpdate = async (download: DownloadItem) => {
await Logger.safeExecute(async () => { await safeExecute(async () => {
const success = await sharedHandleDownload({ const success = await sharedHandleDownload({
item: download, item: download,
isUpdate: true, isUpdate: true,
@ -94,7 +94,7 @@ export const App = () => {
}; };
const handleDownloadComplete = async () => { const handleDownloadComplete = async () => {
await Logger.safeExecute(async () => { await safeExecute(async () => {
await window.electronAPI.kobold.getCurrentVersion(); await window.electronAPI.kobold.getCurrentVersion();
}, 'Error refreshing versions after download:'); }, 'Error refreshing versions after download:');
@ -241,7 +241,7 @@ export const App = () => {
opened={settingsOpened} opened={settingsOpened}
onClose={async () => { onClose={async () => {
setSettingsOpened(false); setSettingsOpened(false);
const preference = await Logger.safeExecute( const preference = await safeExecute(
() => () =>
window.electronAPI.config.get( window.electronAPI.config.get(
'frontendPreference' 'frontendPreference'

View file

@ -14,7 +14,7 @@ import { useState } from 'react';
import type { InstalledVersion, DownloadItem } from '@/types/electron'; import type { InstalledVersion, DownloadItem } from '@/types/electron';
import { getDisplayNameFromPath } from '@/utils/version'; import { getDisplayNameFromPath } from '@/utils/version';
import { GITHUB_API } from '@/constants'; import { GITHUB_API } from '@/constants';
import { Logger } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
interface UpdateAvailableModalProps { interface UpdateAvailableModalProps {
opened: boolean; opened: boolean;
@ -39,7 +39,7 @@ export const UpdateAvailableModal = ({
const handleUpdate = async () => { const handleUpdate = async () => {
setIsUpdating(true); setIsUpdating(true);
await Logger.safeExecute(async () => { await safeExecute(async () => {
await onUpdate(availableUpdate); await onUpdate(availableUpdate);
onClose(); onClose();
}, 'Failed to update:'); }, 'Failed to update:');

View file

@ -5,7 +5,7 @@ import { getPlatformDisplayName } from '@/utils/platform';
import { formatDownloadSize } from '@/utils/download'; import { formatDownloadSize } from '@/utils/download';
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets'; import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
import { useKoboldVersions } from '@/hooks/useKoboldVersions'; import { useKoboldVersions } from '@/hooks/useKoboldVersions';
import { Logger } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
import type { DownloadItem } from '@/types/electron'; import type { DownloadItem } from '@/types/electron';
interface DownloadScreenProps { interface DownloadScreenProps {
@ -34,7 +34,7 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
async (download: DownloadItem) => { async (download: DownloadItem) => {
setDownloadingAsset(download.name); setDownloadingAsset(download.name);
await Logger.safeExecute(async () => { await safeExecute(async () => {
const success = await sharedHandleDownload({ const success = await sharedHandleDownload({
item: download, item: download,
isUpdate: false, isUpdate: false,

View file

@ -3,7 +3,7 @@ import { useState, useEffect } from 'react';
import { InfoTooltip } from '@/components/InfoTooltip'; import { InfoTooltip } from '@/components/InfoTooltip';
import { CheckboxWithTooltip } from '@/components/CheckboxWithTooltip'; import { CheckboxWithTooltip } from '@/components/CheckboxWithTooltip';
import { useLaunchConfig } from '@/hooks/useLaunchConfig'; import { useLaunchConfig } from '@/hooks/useLaunchConfig';
import { Logger } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
export const AdvancedTab = () => { export const AdvancedTab = () => {
const { const {
@ -39,7 +39,7 @@ export const AdvancedTab = () => {
useEffect(() => { useEffect(() => {
const detectBackendSupport = async () => { const detectBackendSupport = async () => {
const support = await Logger.safeExecute( const support = await safeExecute(
() => window.electronAPI.kobold.detectBackendSupport(), () => window.electronAPI.kobold.detectBackendSupport(),
'Failed to detect backend support:' 'Failed to detect backend support:'
); );

View file

@ -4,7 +4,7 @@ import { InfoTooltip } from '@/components/InfoTooltip';
import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem'; import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem';
import { GpuDeviceSelector } from '@/components/screens/Launch/GeneralTab/GpuDeviceSelector'; import { GpuDeviceSelector } from '@/components/screens/Launch/GeneralTab/GpuDeviceSelector';
import { useLaunchConfig } from '@/hooks/useLaunchConfig'; import { useLaunchConfig } from '@/hooks/useLaunchConfig';
import { Logger } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
import type { BackendOption } from '@/types'; import type { BackendOption } from '@/types';
export const BackendSelector = () => { export const BackendSelector = () => {
@ -26,7 +26,7 @@ export const BackendSelector = () => {
useEffect(() => { useEffect(() => {
const loadBackends = async () => { const loadBackends = async () => {
setIsLoadingBackends(true); setIsLoadingBackends(true);
const backends = await Logger.safeExecute( const backends = await safeExecute(
() => window.electronAPI.kobold.getAvailableBackends(true), () => window.electronAPI.kobold.getAvailableBackends(true),
'Failed to detect available backends:' 'Failed to detect available backends:'
); );

View file

@ -1,5 +1,6 @@
import { Card, Container, Stack, Tabs, Group, Button } from '@mantine/core'; import { Card, Container, Stack, Tabs, Group, Button } from '@mantine/core';
import { useState, useEffect, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef } from 'react';
import { tryExecute, error, safeExecute } from '@/utils/logger';
import { useLaunchConfig } from '@/hooks/useLaunchConfig'; import { useLaunchConfig } from '@/hooks/useLaunchConfig';
import { useLaunchLogic } from '@/hooks/useLaunchLogic'; import { useLaunchLogic } from '@/hooks/useLaunchLogic';
import { useWarnings } from '@/hooks/useWarnings'; import { useWarnings } from '@/hooks/useWarnings';
@ -10,7 +11,6 @@ import { ImageGenerationTab } from '@/components/screens/Launch/ImageGenerationT
import { WarningDisplay } from '@/components/WarningDisplay'; import { WarningDisplay } from '@/components/WarningDisplay';
import { ConfigFileManager } from '@/components/screens/Launch/ConfigFileManager'; import { ConfigFileManager } from '@/components/screens/Launch/ConfigFileManager';
import { DEFAULT_MODEL_URL } from '@/constants'; import { DEFAULT_MODEL_URL } from '@/constants';
import { Logger } from '@/utils/logger';
import type { ConfigFile } from '@/types'; import type { ConfigFile } from '@/types';
interface LaunchScreenProps { interface LaunchScreenProps {
@ -86,7 +86,7 @@ export const LaunchScreen = ({
}); });
const setHappyDefaults = useCallback(async () => { const setHappyDefaults = useCallback(async () => {
const backends = await Logger.safeExecute( const backends = await safeExecute(
() => window.electronAPI.kobold.getAvailableBackends(), () => window.electronAPI.kobold.getAvailableBackends(),
'Failed to set defaults:' 'Failed to set defaults:'
); );
@ -98,7 +98,7 @@ export const LaunchScreen = ({
const setInitialDefaults = useCallback( const setInitialDefaults = useCallback(
async (currentModelPath: string, currentSdModel: string) => { async (currentModelPath: string, currentSdModel: string) => {
await Logger.tryExecute(async () => { await tryExecute(async () => {
if ( if (
!defaultsSetRef.current && !defaultsSetRef.current &&
!currentModelPath.trim() && !currentModelPath.trim() &&
@ -190,7 +190,7 @@ export const LaunchScreen = ({
}); });
const handleCreateNewConfig = async (configName: string) => { const handleCreateNewConfig = async (configName: string) => {
await Logger.safeExecute(async () => { await safeExecute(async () => {
const fullConfigName = `${configName}.json`; const fullConfigName = `${configName}.json`;
const saveSuccess = await window.electronAPI.kobold.saveConfigFile( const saveSuccess = await window.electronAPI.kobold.saveConfigFile(
fullConfigName, fullConfigName,
@ -202,7 +202,7 @@ export const LaunchScreen = ({
setSelectedFile(fullConfigName); setSelectedFile(fullConfigName);
await window.electronAPI.kobold.setSelectedConfig(fullConfigName); await window.electronAPI.kobold.setSelectedConfig(fullConfigName);
} else { } else {
Logger.error( error(
'Failed to create new configuration', 'Failed to create new configuration',
new Error('Save operation failed') new Error('Save operation failed')
); );
@ -212,21 +212,21 @@ export const LaunchScreen = ({
const handleSaveConfig = async () => { const handleSaveConfig = async () => {
if (!selectedFile) { if (!selectedFile) {
Logger.error( error(
'No configuration file selected for saving', 'No configuration file selected for saving',
new Error('Selected file is null') new Error('Selected file is null')
); );
return false; return false;
} }
const success = await Logger.safeExecute(async () => { const success = await safeExecute(async () => {
const saveSuccess = await window.electronAPI.kobold.saveConfigFile( const saveSuccess = await window.electronAPI.kobold.saveConfigFile(
selectedFile, selectedFile,
buildConfigData() buildConfigData()
); );
if (!saveSuccess) { if (!saveSuccess) {
Logger.error( error(
'Failed to save configuration', 'Failed to save configuration',
new Error('Save operation failed') new Error('Save operation failed')
); );

View file

@ -12,6 +12,7 @@ import {
rem, rem,
} from '@mantine/core'; } from '@mantine/core';
import { Github, FolderOpen } from 'lucide-react'; import { Github, FolderOpen } from 'lucide-react';
import { safeExecute } from '@/utils/logger';
import type { VersionInfo } from '@/types/electron'; import type { VersionInfo } from '@/types/electron';
import { PRODUCT_NAME } from '@/constants'; import { PRODUCT_NAME } from '@/constants';
import iconUrl from '/icon.png'; import iconUrl from '/icon.png';
@ -19,14 +20,12 @@ export const AboutTab = () => {
const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null); const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
useEffect(() => { useEffect(() => {
const loadVersionInfo = async () => { const loadVersionInfo = async () => {
try { const info = await safeExecute(
const info = await window.electronAPI.app.getVersionInfo(); () => window.electronAPI.app.getVersionInfo(),
'Failed to load version info'
);
if (info) {
setVersionInfo(info); setVersionInfo(info);
} catch (error) {
window.electronAPI.logs.logError(
'Failed to load version info',
error as Error
);
} }
}; };
loadVersionInfo(); loadVersionInfo();
@ -106,14 +105,10 @@ export const AboutTab = () => {
<FolderOpen style={{ width: rem(16), height: rem(16) }} /> <FolderOpen style={{ width: rem(16), height: rem(16) }} />
} }
onClick={async () => { onClick={async () => {
try { await safeExecute(
await window.electronAPI.app.showLogsFolder(); () => window.electronAPI.app.showLogsFolder(),
} catch (error) { 'Failed to open logs folder'
window.electronAPI.logs.logError( );
'Failed to open logs folder',
error as Error
);
}
}} }}
> >
Show Logs Show Logs

View file

@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { tryExecute, safeExecute } from '@/utils/logger';
import { import {
Stack, Stack,
Text, Text,
@ -13,7 +14,6 @@ import { Folder, FolderOpen, Monitor } from 'lucide-react';
import styles from '@/styles/layout.module.css'; import styles from '@/styles/layout.module.css';
import type { FrontendPreference } from '@/types'; import type { FrontendPreference } from '@/types';
import { FRONTENDS } from '@/constants'; import { FRONTENDS } from '@/constants';
import { Logger } from '@/utils/logger';
export const GeneralTab = () => { export const GeneralTab = () => {
const [installDir, setInstallDir] = useState<string>(''); const [installDir, setInstallDir] = useState<string>('');
@ -30,7 +30,7 @@ export const GeneralTab = () => {
useEffect(() => { useEffect(() => {
const checkNpxAvailability = async () => { const checkNpxAvailability = async () => {
const available = await Logger.safeExecute( const available = await safeExecute(
() => window.electronAPI.sillytavern.isNpxAvailable(), () => window.electronAPI.sillytavern.isNpxAvailable(),
'Failed to check npx availability:' 'Failed to check npx availability:'
); );
@ -39,7 +39,7 @@ export const GeneralTab = () => {
setIsNpxAvailable(isAvailable); setIsNpxAvailable(isAvailable);
if (!isAvailable && FrontendPreference === 'sillytavern') { if (!isAvailable && FrontendPreference === 'sillytavern') {
await Logger.tryExecute( await tryExecute(
() => () =>
window.electronAPI.config.set('frontendPreference', 'koboldcpp'), window.electronAPI.config.set('frontendPreference', 'koboldcpp'),
'Failed to reset frontend preference:' 'Failed to reset frontend preference:'
@ -54,7 +54,7 @@ export const GeneralTab = () => {
}, [FrontendPreference]); }, [FrontendPreference]);
const loadCurrentInstallDir = async () => { const loadCurrentInstallDir = async () => {
const currentDir = await Logger.safeExecute( const currentDir = await safeExecute(
() => window.electronAPI.kobold.getCurrentInstallDir(), () => window.electronAPI.kobold.getCurrentInstallDir(),
'Failed to load install directory:' 'Failed to load install directory:'
); );
@ -64,7 +64,7 @@ export const GeneralTab = () => {
}; };
const loadFrontendPreference = async () => { const loadFrontendPreference = async () => {
const frontendPreference = await Logger.safeExecute( const frontendPreference = await safeExecute(
() => window.electronAPI.config.get('frontendPreference'), () => window.electronAPI.config.get('frontendPreference'),
'Failed to load frontend preference:' 'Failed to load frontend preference:'
); );
@ -76,7 +76,7 @@ export const GeneralTab = () => {
}; };
const handleSelectInstallDir = async () => { const handleSelectInstallDir = async () => {
const selectedDir = await Logger.safeExecute( const selectedDir = await safeExecute(
() => window.electronAPI.kobold.selectInstallDirectory(), () => window.electronAPI.kobold.selectInstallDirectory(),
'Failed to select install directory:' 'Failed to select install directory:'
); );
@ -88,7 +88,7 @@ export const GeneralTab = () => {
const handleFrontendPreferenceChange = async (value: string | null) => { const handleFrontendPreferenceChange = async (value: string | null) => {
if (!value || (value !== 'koboldcpp' && value !== 'sillytavern')) return; if (!value || (value !== 'koboldcpp' && value !== 'sillytavern')) return;
const success = await Logger.tryExecute( const success = await tryExecute(
() => window.electronAPI.config.set('frontendPreference', value), () => window.electronAPI.config.set('frontendPreference', value),
'Failed to save frontend preference:' 'Failed to save frontend preference:'
); );

View file

@ -18,7 +18,7 @@ import {
stripAssetExtensions, stripAssetExtensions,
compareVersions, compareVersions,
} from '@/utils/version'; } from '@/utils/version';
import { Logger } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
import { formatDownloadSize } from '@/utils/download'; import { formatDownloadSize } from '@/utils/download';
import { useKoboldVersions } from '@/hooks/useKoboldVersions'; import { useKoboldVersions } from '@/hooks/useKoboldVersions';
@ -63,7 +63,7 @@ export const VersionsTab = () => {
const loadInstalledVersions = useCallback(async () => { const loadInstalledVersions = useCallback(async () => {
setLoadingInstalled(true); setLoadingInstalled(true);
await Logger.safeExecute(async () => { await safeExecute(async () => {
const [versions, currentVersion] = await Promise.all([ const [versions, currentVersion] = await Promise.all([
window.electronAPI.kobold.getInstalledVersions(), window.electronAPI.kobold.getInstalledVersions(),
window.electronAPI.kobold.getCurrentVersion(), window.electronAPI.kobold.getCurrentVersion(),
@ -77,7 +77,7 @@ export const VersionsTab = () => {
}, []); }, []);
const loadLatestRelease = useCallback(async () => { const loadLatestRelease = useCallback(async () => {
const release = await Logger.safeExecute( const release = await safeExecute(
() => getLatestReleaseWithDownloadStatus(), () => getLatestReleaseWithDownloadStatus(),
'Failed to load latest release:' 'Failed to load latest release:'
); );
@ -174,7 +174,7 @@ export const VersionsTab = () => {
}, [downloading]); }, [downloading]);
const handleDownload = async (version: VersionInfo) => { const handleDownload = async (version: VersionInfo) => {
await Logger.safeExecute(async () => { await safeExecute(async () => {
const download = availableDownloads.find((d) => d.name === version.name); const download = availableDownloads.find((d) => d.name === version.name);
if (!download) { if (!download) {
throw new Error('Download not found'); throw new Error('Download not found');
@ -193,7 +193,7 @@ export const VersionsTab = () => {
}; };
const handleUpdate = async (version: VersionInfo) => { const handleUpdate = async (version: VersionInfo) => {
await Logger.safeExecute(async () => { await safeExecute(async () => {
const download = availableDownloads.find((d) => d.name === version.name); const download = availableDownloads.find((d) => d.name === version.name);
if (!download) { if (!download) {
throw new Error('Download not found'); throw new Error('Download not found');
@ -214,7 +214,7 @@ export const VersionsTab = () => {
const makeCurrent = async (version: VersionInfo) => { const makeCurrent = async (version: VersionInfo) => {
if (!version.installedPath) return; if (!version.installedPath) return;
await Logger.safeExecute(async () => { await safeExecute(async () => {
const success = await window.electronAPI.kobold.setCurrentVersion( const success = await window.electronAPI.kobold.setCurrentVersion(
version.installedPath! version.installedPath!
); );

View file

@ -1,4 +1,5 @@
import { useState, useCallback, useEffect } from 'react'; import { useState, useCallback, useEffect } from 'react';
import { error } from '@/utils/logger';
import { compareVersions } from '@/utils/version'; import { compareVersions } from '@/utils/version';
import { GITHUB_API } from '@/constants'; import { GITHUB_API } from '@/constants';
@ -48,11 +49,8 @@ export const useAppUpdateChecker = () => {
setLastChecked(new Date()); setLastChecked(new Date());
return updateInfo; return updateInfo;
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to check for app updates:', err as Error);
'Failed to check for app updates:',
error as Error
);
return null; return null;
} finally { } finally {
setIsChecking(false); setIsChecking(false);

View file

@ -1,4 +1,5 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { error } from '@/utils/logger';
import { getROCmDownload } from '@/utils/rocm'; import { getROCmDownload } from '@/utils/rocm';
import { GITHUB_API } from '@/constants'; import { GITHUB_API } from '@/constants';
import { filterAssetsByPlatform } from '@/utils/platform'; import { filterAssetsByPlatform } from '@/utils/platform';
@ -45,11 +46,8 @@ const saveToCache = (releases: DownloadItem[]) => {
timestamp: Date.now(), timestamp: Date.now(),
}; };
localStorage.setItem(CACHE_KEY, JSON.stringify(data)); localStorage.setItem(CACHE_KEY, JSON.stringify(data));
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to save releases to cache', err as Error);
'Failed to save releases to cache',
error as Error
);
} }
}; };
@ -126,11 +124,8 @@ const getLatestReleaseWithDownloadStatus =
release: latestRelease, release: latestRelease,
availableAssets, availableAssets,
}; };
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to fetch latest release with status:', err as Error);
'Failed to fetch latest release with status:',
error as Error
);
return null; return null;
} }
}; };
@ -209,12 +204,8 @@ export const useKoboldVersions = (): UseKoboldVersionsReturn => {
} }
setAvailableDownloads(allDownloads); setAvailableDownloads(allDownloads);
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to load remote versions:', err as Error);
'Failed to load remote versions:',
error as Error
);
const cached = loadFromCache(); const cached = loadFromCache();
if (cached) { if (cached) {
const rocm = await getROCmDownload().catch(() => null); const rocm = await getROCmDownload().catch(() => null);
@ -251,11 +242,8 @@ export const useKoboldVersions = (): UseKoboldVersionsReturn => {
}); });
return result.success !== false; return result.success !== false;
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to download ${item.name}:', err as Error);
`Failed to download ${item.name}:`,
error as Error
);
return false; return false;
} finally { } finally {
setDownloading(null); setDownloading(null);

View file

@ -1,4 +1,5 @@
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { error } from '@/utils/logger';
import type { SdConvDirectMode } from '@/types'; import type { SdConvDirectMode } from '@/types';
interface UseLaunchLogicProps { interface UseLaunchLogicProps {
@ -285,11 +286,8 @@ export const useLaunchLogic = ({
new Error(errorMessage) new Error(errorMessage)
); );
} }
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Error launching KoboldCpp:', err as Error);
'Error launching KoboldCpp:',
error as Error
);
} finally { } finally {
setIsLaunching(false); setIsLaunching(false);
} }

View file

@ -1,4 +1,5 @@
import { useState, useCallback, useEffect } from 'react'; import { useState, useCallback, useEffect } from 'react';
import { error } from '@/utils/logger';
import { import {
getDisplayNameFromPath, getDisplayNameFromPath,
compareVersions, compareVersions,
@ -34,11 +35,8 @@ export const useUpdateChecker = () => {
setDismissedUpdates(new Set(dismissed)); setDismissedUpdates(new Set(dismissed));
} }
setDismissedUpdatesLoaded(true); setDismissedUpdatesLoaded(true);
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to load dismissed updates:', err as Error);
'Failed to load dismissed updates:',
error as Error
);
setDismissedUpdatesLoaded(true); setDismissedUpdatesLoaded(true);
} }
}; };
@ -52,11 +50,8 @@ export const useUpdateChecker = () => {
'dismissedUpdates', 'dismissedUpdates',
Array.from(updates) Array.from(updates)
); );
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to save dismissed updates:', err as Error);
'Failed to save dismissed updates:',
error as Error
);
} }
}, []); }, []);
@ -110,11 +105,8 @@ export const useUpdateChecker = () => {
} }
} }
} }
} catch (error) { } catch (err) {
window.electronAPI.logs.logError( error('Failed to check for updates:', err as Error);
'Failed to check for updates:',
error as Error
);
} finally { } finally {
setIsChecking(false); setIsChecking(false);
} }

View file

@ -1,4 +1,5 @@
import { useMemo, useEffect, useState, useCallback } from 'react'; import { useMemo, useEffect, useState, useCallback } from 'react';
import { safeExecute } from '@/utils/logger';
import type { BackendOption } from '@/types'; import type { BackendOption } from '@/types';
export interface Warning { export interface Warning {
@ -100,8 +101,12 @@ const checkVramWarnings = async (backend: string): Promise<Warning[]> => {
const isGpuBackend = ['cuda', 'rocm', 'vulkan', 'clblast'].includes(backend); const isGpuBackend = ['cuda', 'rocm', 'vulkan', 'clblast'].includes(backend);
if (isGpuBackend) { if (isGpuBackend) {
try { const gpuMemoryInfo = await safeExecute(
const gpuMemoryInfo = await window.electronAPI.kobold.detectGPUMemory(); () => window.electronAPI.kobold.detectGPUMemory(),
'Failed to detect GPU memory:'
);
if (gpuMemoryInfo) {
const lowVramGpus = gpuMemoryInfo.filter( const lowVramGpus = gpuMemoryInfo.filter(
(gpu) => (gpu) =>
typeof gpu.totalMemoryMB === 'number' && gpu.totalMemoryMB < 8192 typeof gpu.totalMemoryMB === 'number' && gpu.totalMemoryMB < 8192
@ -120,11 +125,6 @@ const checkVramWarnings = async (backend: string): Promise<Warning[]> => {
)}). Consider using smaller models, reducing GPU layers, or enabling the "Low VRAM" option on the Advanced tab.`, )}). Consider using smaller models, reducing GPU layers, or enabling the "Low VRAM" option on the Advanced tab.`,
}); });
} }
} catch (error) {
window.electronAPI.logs.logError(
'Failed to detect GPU memory:',
error as Error
);
} }
} }
@ -186,7 +186,7 @@ const checkBackendWarnings = async (params?: {
}): Promise<Warning[]> => { }): Promise<Warning[]> => {
const warnings: Warning[] = []; const warnings: Warning[] = [];
try { const result = await safeExecute(async () => {
const [backendSupport, gpuCapabilities, gpuInfo] = await Promise.all([ const [backendSupport, gpuCapabilities, gpuInfo] = await Promise.all([
window.electronAPI.kobold.detectBackendSupport(), window.electronAPI.kobold.detectBackendSupport(),
window.electronAPI.kobold.detectGPUCapabilities(), window.electronAPI.kobold.detectGPUCapabilities(),
@ -224,13 +224,9 @@ const checkBackendWarnings = async (params?: {
} }
return warnings; return warnings;
} catch (error) { }, 'Failed to check backend warnings:');
window.electronAPI.logs.logError(
'Failed to check backend warnings:', return result || warnings;
error as Error
);
return warnings;
}
}; };
export const useWarnings = ({ export const useWarnings = ({
@ -254,7 +250,7 @@ export const useWarnings = ({
return; return;
} }
try { const result = await safeExecute(async () => {
const [cpuCapabilitiesResult, availableBackends] = await Promise.all([ const [cpuCapabilitiesResult, availableBackends] = await Promise.all([
window.electronAPI.kobold.detectCPU(), window.electronAPI.kobold.detectCPU(),
window.electronAPI.kobold.getAvailableBackends(), window.electronAPI.kobold.getAvailableBackends(),
@ -265,21 +261,16 @@ export const useWarnings = ({
avx2: cpuCapabilitiesResult.avx2, avx2: cpuCapabilitiesResult.avx2,
}; };
const warnings = await checkBackendWarnings({ return checkBackendWarnings({
backend, backend,
cpuCapabilities, cpuCapabilities,
noavx2, noavx2,
failsafe, failsafe,
availableBackends, availableBackends,
}); });
setBackendWarnings(warnings); }, 'Failed to check backend warnings:');
} catch (error) {
window.electronAPI.logs.logError( setBackendWarnings(result || []);
'Failed to check backend warnings:',
error as Error
);
setBackendWarnings([]);
}
}, [backend, noavx2, failsafe]); }, [backend, noavx2, failsafe]);
useEffect(() => { useEffect(() => {

View file

@ -110,6 +110,7 @@ export class WindowManager {
private setupContextMenu() { private setupContextMenu() {
if (!this.mainWindow) return; if (!this.mainWindow) return;
// eslint-disable-next-line sonarjs/cognitive-complexity
this.mainWindow.webContents.on('context-menu', (_, params) => { this.mainWindow.webContents.on('context-menu', (_, params) => {
const hasLinkURL = !!params.linkURL; const hasLinkURL = !!params.linkURL;
const hasSelection = !!params.selectionText; const hasSelection = !!params.selectionText;

View file

@ -1,56 +1,54 @@
export class Logger { const logError = (message: string, error: Error): void => {
private static logError(message: string, error: Error): void { if (window.electronAPI?.logs?.logError) {
if (window.electronAPI?.logs?.logError) { window.electronAPI.logs.logError(message, error);
window.electronAPI.logs.logError(message, error);
}
} }
};
static async safeExecute<T>( export const safeExecute = async <T>(
operation: () => Promise<T>, operation: () => Promise<T>,
errorMessage: string errorMessage: string
): Promise<T | null> { ): Promise<T | null> => {
try { try {
return operation(); return operation();
} catch (error) { } catch (error) {
this.logError(errorMessage, error as Error); logError(errorMessage, error as Error);
return null; return null;
}
} }
};
static safeExecuteSync<T>( export const safeExecuteSync = <T>(
operation: () => T, operation: () => T,
errorMessage: string errorMessage: string
): T | null { ): T | null => {
try { try {
return operation(); return operation();
} catch (error) { } catch (error) {
this.logError(errorMessage, error as Error); logError(errorMessage, error as Error);
return null; return null;
}
} }
};
static async tryExecute( export const tryExecute = async (
operation: () => Promise<void>, operation: () => Promise<void>,
errorMessage: string errorMessage: string
): Promise<boolean> { ): Promise<boolean> => {
try { try {
await operation(); await operation();
return true; return true;
} catch (error) { } catch (error) {
this.logError(errorMessage, error as Error); logError(errorMessage, error as Error);
return false; return false;
}
} }
};
static withErrorHandling<TArgs extends unknown[], TReturn>( export const withErrorHandling =
<TArgs extends unknown[], TReturn>(
fn: (...args: TArgs) => Promise<TReturn>, fn: (...args: TArgs) => Promise<TReturn>,
errorMessage: string errorMessage: string
): (...args: TArgs) => Promise<TReturn | null> { ): ((...args: TArgs) => Promise<TReturn | null>) =>
return async (...args: TArgs) => async (...args: TArgs) =>
this.safeExecute(() => fn(...args), errorMessage); safeExecute(() => fn(...args), errorMessage);
}
static error(message: string, error: Error): void { export const error = (message: string, error: Error): void => {
this.logError(message, error); logError(message, error);
} };
}

View file

@ -1,3 +1,5 @@
import { error } from '@/utils/logger';
export const handleTerminalOutput = ( export const handleTerminalOutput = (
prevContent: string, prevContent: string,
newData: string newData: string
@ -19,8 +21,8 @@ export const handleTerminalOutput = (
} }
return prevContent + newData; return prevContent + newData;
} catch (error) { } catch (err) {
window.electronAPI.logs.logError('Terminal Basic Error', error as Error); error('Terminal Basic Error', err as Error);
return prevContent + newData; return prevContent + newData;
} }
}; };