mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
remove the double try/catch cases on the frontend side for cleaner code
This commit is contained in:
parent
3eb97f5683
commit
0b905a7f67
22 changed files with 250 additions and 364 deletions
57
src/App.tsx
57
src/App.tsx
|
|
@ -12,7 +12,6 @@ import { StatusBar } from '@/components/StatusBar';
|
|||
import { ErrorBoundary } from '@/components/ErrorBoundary';
|
||||
import { useUpdateChecker } from '@/hooks/useUpdateChecker';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import { STATUSBAR_HEIGHT, TITLEBAR_HEIGHT } from '@/constants';
|
||||
import type { DownloadItem } from '@/types/electron';
|
||||
import type { InterfaceTab, Screen } from '@/types';
|
||||
|
|
@ -40,26 +39,24 @@ export const App = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const checkInstallation = async () => {
|
||||
await safeExecute(async () => {
|
||||
const [currentVersion, hasSeenWelcome] = await Promise.all([
|
||||
window.electronAPI.kobold.getCurrentVersion(),
|
||||
window.electronAPI.config.get('hasSeenWelcome') as Promise<boolean>,
|
||||
]);
|
||||
const [currentVersion, hasSeenWelcome] = await Promise.all([
|
||||
window.electronAPI.kobold.getCurrentVersion(),
|
||||
window.electronAPI.config.get('hasSeenWelcome') as Promise<boolean>,
|
||||
]);
|
||||
|
||||
if (!hasSeenWelcome) {
|
||||
setCurrentScreen('welcome');
|
||||
} else if (currentVersion) {
|
||||
setCurrentScreen('launch');
|
||||
} else {
|
||||
setCurrentScreen('download');
|
||||
}
|
||||
if (!hasSeenWelcome) {
|
||||
setCurrentScreen('welcome');
|
||||
} else if (currentVersion) {
|
||||
setCurrentScreen('launch');
|
||||
} else {
|
||||
setCurrentScreen('download');
|
||||
}
|
||||
|
||||
if (currentVersion) {
|
||||
setTimeout(() => {
|
||||
checkForUpdates();
|
||||
}, 2000);
|
||||
}
|
||||
}, 'Error checking installation:');
|
||||
if (currentVersion) {
|
||||
setTimeout(() => {
|
||||
checkForUpdates();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
setHasInitialized(true);
|
||||
};
|
||||
|
|
@ -69,23 +66,19 @@ export const App = () => {
|
|||
}, []);
|
||||
|
||||
const handleBinaryUpdate = async (download: DownloadItem) => {
|
||||
await safeExecute(async () => {
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: true,
|
||||
wasCurrentBinary: true,
|
||||
});
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: true,
|
||||
wasCurrentBinary: true,
|
||||
});
|
||||
|
||||
if (success) {
|
||||
dismissUpdate();
|
||||
}
|
||||
}, 'Failed to update binary:');
|
||||
if (success) {
|
||||
dismissUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
const handleDownloadComplete = async () => {
|
||||
await safeExecute(async () => {
|
||||
await window.electronAPI.kobold.getCurrentVersion();
|
||||
}, 'Error refreshing versions after download:');
|
||||
await window.electronAPI.kobold.getCurrentVersion();
|
||||
|
||||
setTimeout(() => {
|
||||
setCurrentScreen('launch');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { ReactNode } from 'react';
|
|||
import { Center, Stack, Text, Button, Alert, rem } from '@mantine/core';
|
||||
import { AlertTriangle, FolderOpen } from 'lucide-react';
|
||||
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: ReactNode;
|
||||
|
|
@ -39,12 +38,7 @@ const ErrorFallback = ({
|
|||
leftSection={
|
||||
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
||||
}
|
||||
onClick={async () => {
|
||||
await safeExecute(
|
||||
() => window.electronAPI.app.showLogsFolder(),
|
||||
'Failed to open logs folder'
|
||||
);
|
||||
}}
|
||||
onClick={() => window.electronAPI.app.showLogsFolder()}
|
||||
>
|
||||
Show Logs Folder
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Badge, Tooltip } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
|
||||
interface PerformanceBadgeProps {
|
||||
label: string;
|
||||
|
|
@ -16,12 +15,9 @@ export const PerformanceBadge = ({
|
|||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handlePerformanceClick = async () => {
|
||||
const result = await safeExecute(
|
||||
() => window.electronAPI.app.openPerformanceManager(),
|
||||
'Failed to open performance manager'
|
||||
);
|
||||
const result = await window.electronAPI.app.openPerformanceManager();
|
||||
|
||||
if (result && !result.success) {
|
||||
if (!result.success) {
|
||||
window.electronAPI.logs.logError(
|
||||
`Failed to open performance manager: ${result.error}`
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import { Card, Text, Title, Loader, Stack, Container } from '@mantine/core';
|
||||
import { DownloadCard } from '@/components/DownloadCard';
|
||||
import { getPlatformDisplayName } from '@/utils/platform';
|
||||
import { formatDownloadSize } from '@/utils/format';
|
||||
import { getAssetDescription } from '@/utils/assets';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import type { DownloadItem } from '@/types/electron';
|
||||
|
||||
interface DownloadScreenProps {
|
||||
|
|
@ -32,21 +31,19 @@ export const DownloadScreen = ({ onDownloadComplete }: DownloadScreenProps) => {
|
|||
async (download: DownloadItem) => {
|
||||
setDownloadingAsset(download.name);
|
||||
|
||||
await safeExecute(async () => {
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: false,
|
||||
wasCurrentBinary: false,
|
||||
});
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: false,
|
||||
wasCurrentBinary: false,
|
||||
});
|
||||
|
||||
if (success) {
|
||||
onDownloadComplete();
|
||||
if (success) {
|
||||
onDownloadComplete();
|
||||
|
||||
setTimeout(() => {
|
||||
setDownloadingAsset(null);
|
||||
}, 200);
|
||||
}
|
||||
}, `Failed to download ${download.name}:`);
|
||||
setTimeout(() => {
|
||||
setDownloadingAsset(null);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
setDownloadingAsset(null);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { InfoTooltip } from '@/components/InfoTooltip';
|
|||
import { CheckboxWithTooltip } from '@/components/CheckboxWithTooltip';
|
||||
import { CommandLineArgumentsModal } from '@/components/CommandLineArgumentsModal';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
|
||||
export const AdvancedTab = () => {
|
||||
const {
|
||||
|
|
@ -56,10 +55,7 @@ export const AdvancedTab = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const detectBackendSupport = async () => {
|
||||
const support = await safeExecute(
|
||||
() => window.electronAPI.kobold.detectBackendSupport(),
|
||||
'Failed to detect backend support:'
|
||||
);
|
||||
const support = await window.electronAPI.kobold.detectBackendSupport();
|
||||
|
||||
if (support) {
|
||||
setBackendSupport({
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { InfoTooltip } from '@/components/InfoTooltip';
|
|||
import { BackendSelectItem } from '@/components/screens/Launch/GeneralTab/BackendSelectItem';
|
||||
import { GpuDeviceSelector } from '@/components/screens/Launch/GeneralTab/GpuDeviceSelector';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import type { BackendOption } from '@/types';
|
||||
|
||||
export const BackendSelector = () => {
|
||||
|
|
@ -26,10 +25,8 @@ export const BackendSelector = () => {
|
|||
useEffect(() => {
|
||||
const loadBackends = async () => {
|
||||
setIsLoadingBackends(true);
|
||||
const backends = await safeExecute(
|
||||
() => window.electronAPI.kobold.getAvailableBackends(true),
|
||||
'Failed to detect available backends:'
|
||||
);
|
||||
const backends =
|
||||
await window.electronAPI.kobold.getAvailableBackends(true);
|
||||
setAvailableBackends(backends || []);
|
||||
setIsLoadingBackends(false);
|
||||
hasInitialized.current = true;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Card, Container, Stack, Tabs, Group, Button } from '@mantine/core';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { logError, safeExecute } from '@/utils/logger';
|
||||
import { logError } from '@/utils/logger';
|
||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||
import { useLaunchLogic } from '@/hooks/useLaunchLogic';
|
||||
import { useWarnings } from '@/hooks/useWarnings';
|
||||
|
|
@ -81,10 +81,7 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
|
|||
});
|
||||
|
||||
const setHappyDefaults = useCallback(async () => {
|
||||
const backends = await safeExecute(
|
||||
() => window.electronAPI.kobold.getAvailableBackends(),
|
||||
'Failed to set defaults:'
|
||||
);
|
||||
const backends = await window.electronAPI.kobold.getAvailableBackends();
|
||||
|
||||
if (!backend && backends && backends.length > 0) {
|
||||
handleBackendChange(backends[0].value);
|
||||
|
|
@ -183,24 +180,22 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
|
|||
});
|
||||
|
||||
const handleCreateNewConfig = async (configName: string) => {
|
||||
await safeExecute(async () => {
|
||||
const fullConfigName = `${configName}.json`;
|
||||
const saveSuccess = await window.electronAPI.kobold.saveConfigFile(
|
||||
fullConfigName,
|
||||
buildConfigData()
|
||||
);
|
||||
const fullConfigName = `${configName}.json`;
|
||||
const saveSuccess = await window.electronAPI.kobold.saveConfigFile(
|
||||
fullConfigName,
|
||||
buildConfigData()
|
||||
);
|
||||
|
||||
if (saveSuccess) {
|
||||
await loadConfigFiles();
|
||||
setSelectedFile(fullConfigName);
|
||||
await window.electronAPI.kobold.setSelectedConfig(fullConfigName);
|
||||
} else {
|
||||
logError(
|
||||
'Failed to create new configuration',
|
||||
new Error('Save operation failed')
|
||||
);
|
||||
}
|
||||
}, 'Failed to create new configuration:');
|
||||
if (saveSuccess) {
|
||||
await loadConfigFiles();
|
||||
setSelectedFile(fullConfigName);
|
||||
await window.electronAPI.kobold.setSelectedConfig(fullConfigName);
|
||||
} else {
|
||||
logError(
|
||||
'Failed to create new configuration',
|
||||
new Error('Save operation failed')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveConfig = async () => {
|
||||
|
|
@ -212,24 +207,20 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
|
|||
return false;
|
||||
}
|
||||
|
||||
const success = await safeExecute(async () => {
|
||||
const saveSuccess = await window.electronAPI.kobold.saveConfigFile(
|
||||
selectedFile,
|
||||
buildConfigData()
|
||||
const saveSuccess = await window.electronAPI.kobold.saveConfigFile(
|
||||
selectedFile,
|
||||
buildConfigData()
|
||||
);
|
||||
|
||||
if (!saveSuccess) {
|
||||
logError(
|
||||
'Failed to save configuration',
|
||||
new Error('Save operation failed')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!saveSuccess) {
|
||||
logError(
|
||||
'Failed to save configuration',
|
||||
new Error('Save operation failed')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, 'Failed to save configuration:');
|
||||
|
||||
return success ?? false;
|
||||
return true;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ export const AboutTab = () => {
|
|||
const { handleLogoClick, getLogoStyles } = useLogoClickSounds();
|
||||
useEffect(() => {
|
||||
const loadVersionInfo = async () => {
|
||||
const info = await safeExecute(
|
||||
() => window.electronAPI.app.getVersionInfo(),
|
||||
'Failed to load version info'
|
||||
);
|
||||
const info = await window.electronAPI.app.getVersionInfo();
|
||||
if (info) {
|
||||
setVersionInfo(info);
|
||||
}
|
||||
|
|
@ -48,11 +45,9 @@ export const AboutTab = () => {
|
|||
{ label: 'Electron', value: versionInfo.electronVersion },
|
||||
{
|
||||
label: 'Node.js',
|
||||
value:
|
||||
versionInfo.nodeJsSystemVersion &&
|
||||
versionInfo.nodeJsSystemVersion !== versionInfo.nodeVersion
|
||||
? `${versionInfo.nodeVersion} (System: ${versionInfo.nodeJsSystemVersion})`
|
||||
: versionInfo.nodeVersion,
|
||||
value: versionInfo.nodeJsSystemVersion
|
||||
? `${versionInfo.nodeVersion} (System: ${versionInfo.nodeJsSystemVersion})`
|
||||
: versionInfo.nodeVersion,
|
||||
},
|
||||
{ label: 'Chromium', value: versionInfo.chromeVersion },
|
||||
{ label: 'V8', value: versionInfo.v8Version },
|
||||
|
|
@ -66,21 +61,9 @@ export const AboutTab = () => {
|
|||
];
|
||||
|
||||
const copyVersionInfo = async () => {
|
||||
const nodeJsInfo =
|
||||
versionInfo.nodeJsSystemVersion &&
|
||||
versionInfo.nodeJsSystemVersion !== versionInfo.nodeVersion
|
||||
? `${versionInfo.nodeVersion} (system: ${versionInfo.nodeJsSystemVersion})`
|
||||
: versionInfo.nodeVersion;
|
||||
|
||||
const info = [
|
||||
`${PRODUCT_NAME}: ${versionInfo.appVersion}`,
|
||||
`Electron: ${versionInfo.electronVersion}`,
|
||||
`Node.js: ${nodeJsInfo}`,
|
||||
`Chromium: ${versionInfo.chromeVersion}`,
|
||||
`V8: ${versionInfo.v8Version}`,
|
||||
`OS: ${versionInfo.platform} ${versionInfo.arch} (${versionInfo.osVersion})`,
|
||||
...(versionInfo.uvVersion ? [`uv: ${versionInfo.uvVersion}`] : []),
|
||||
].join('\n');
|
||||
const info = versionItems
|
||||
.map((item) => `${item.label}: ${item.value}`)
|
||||
.join('\n');
|
||||
|
||||
await safeExecute(
|
||||
() => navigator.clipboard.writeText(info),
|
||||
|
|
@ -128,15 +111,11 @@ export const AboutTab = () => {
|
|||
leftSection={
|
||||
<Github style={{ width: rem(16), height: rem(16) }} />
|
||||
}
|
||||
onClick={async () => {
|
||||
await safeExecute(
|
||||
() =>
|
||||
window.electronAPI.app.openExternal(
|
||||
'https://github.com/lone-cloud/gerbil'
|
||||
),
|
||||
'Failed to open GitHub link'
|
||||
);
|
||||
}}
|
||||
onClick={() =>
|
||||
window.electronAPI.app.openExternal(
|
||||
'https://github.com/lone-cloud/gerbil'
|
||||
)
|
||||
}
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
GitHub
|
||||
|
|
@ -148,12 +127,7 @@ export const AboutTab = () => {
|
|||
leftSection={
|
||||
<FolderOpen style={{ width: rem(16), height: rem(16) }} />
|
||||
}
|
||||
onClick={async () => {
|
||||
await safeExecute(
|
||||
() => window.electronAPI.app.showLogsFolder(),
|
||||
'Failed to open logs folder'
|
||||
);
|
||||
}}
|
||||
onClick={() => window.electronAPI.app.showLogsFolder()}
|
||||
>
|
||||
Show Logs
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
} from '@mantine/core';
|
||||
import { Sun, Moon, Monitor } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
|
||||
import {
|
||||
zoomLevelToPercentage,
|
||||
|
|
@ -33,14 +32,8 @@ export const AppearanceTab = () => {
|
|||
useEffect(() => {
|
||||
const loadSettings = async () => {
|
||||
const [currentZoom, savedColorScheme] = await Promise.all([
|
||||
safeExecute(
|
||||
() => window.electronAPI.app.getZoomLevel(),
|
||||
'Failed to load zoom level:'
|
||||
),
|
||||
safeExecute(
|
||||
() => window.electronAPI.app.getColorScheme(),
|
||||
'Failed to load color scheme:'
|
||||
),
|
||||
window.electronAPI.app.getZoomLevel(),
|
||||
window.electronAPI.app.getColorScheme(),
|
||||
]);
|
||||
|
||||
if (typeof currentZoom === 'number') {
|
||||
|
|
@ -56,38 +49,29 @@ export const AppearanceTab = () => {
|
|||
void loadSettings();
|
||||
}, []);
|
||||
|
||||
const handleColorSchemeChange = async (value: string) => {
|
||||
const handleColorSchemeChange = (value: string) => {
|
||||
const newColorScheme = value as MantineColorScheme;
|
||||
setRawColorScheme(newColorScheme);
|
||||
|
||||
await safeExecute(
|
||||
() => window.electronAPI.app.setColorScheme(newColorScheme),
|
||||
'Failed to save color scheme:'
|
||||
);
|
||||
void window.electronAPI.app.setColorScheme(newColorScheme);
|
||||
};
|
||||
|
||||
const handleZoomChange = async (newZoomLevel: number) => {
|
||||
const handleZoomChange = (newZoomLevel: number) => {
|
||||
setZoomLevel(newZoomLevel);
|
||||
const percentage = zoomLevelToPercentage(newZoomLevel);
|
||||
setZoomPercentage(percentage.toString());
|
||||
|
||||
await safeExecute(
|
||||
() => window.electronAPI.app.setZoomLevel(newZoomLevel),
|
||||
'Failed to set zoom level:'
|
||||
);
|
||||
void window.electronAPI.app.setZoomLevel(newZoomLevel);
|
||||
};
|
||||
|
||||
const handleZoomPercentageChange = async (value: string) => {
|
||||
const handleZoomPercentageChange = (value: string) => {
|
||||
setZoomPercentage(value);
|
||||
const numValue = Number(value);
|
||||
if (!isNaN(numValue) && isValidZoomPercentage(numValue)) {
|
||||
const newZoomLevel = percentageToZoomLevel(numValue);
|
||||
setZoomLevel(newZoomLevel);
|
||||
|
||||
await safeExecute(
|
||||
() => window.electronAPI.app.setZoomLevel(newZoomLevel),
|
||||
'Failed to set zoom level:'
|
||||
);
|
||||
void window.electronAPI.app.setZoomLevel(newZoomLevel);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { tryExecute, safeExecute } from '@/utils/logger';
|
||||
import { tryExecute } from '@/utils/logger';
|
||||
import {
|
||||
Stack,
|
||||
Text,
|
||||
|
|
@ -99,11 +99,8 @@ export const GeneralTab = ({
|
|||
|
||||
for (const config of frontendConfigs) {
|
||||
if (config.requirementCheck) {
|
||||
const isAvailable = await safeExecute(
|
||||
config.requirementCheck,
|
||||
`Failed to check requirements for ${config.label}:`
|
||||
);
|
||||
requirementResults.set(config.value, isAvailable ?? false);
|
||||
const isAvailable = await config.requirementCheck();
|
||||
requirementResults.set(config.value, isAvailable);
|
||||
} else {
|
||||
requirementResults.set(config.value, true);
|
||||
}
|
||||
|
|
@ -163,20 +160,15 @@ export const GeneralTab = ({
|
|||
}, [FrontendPreference, checkAllFrontendRequirements]);
|
||||
|
||||
const loadCurrentInstallDir = async () => {
|
||||
const currentDir = await safeExecute(
|
||||
() => window.electronAPI.kobold.getCurrentInstallDir(),
|
||||
'Failed to load install directory:'
|
||||
);
|
||||
const currentDir = await window.electronAPI.kobold.getCurrentInstallDir();
|
||||
if (currentDir) {
|
||||
setInstallDir(currentDir);
|
||||
}
|
||||
};
|
||||
|
||||
const loadFrontendPreference = async () => {
|
||||
const frontendPreference = await safeExecute(
|
||||
() => window.electronAPI.config.get('frontendPreference'),
|
||||
'Failed to load frontend preference:'
|
||||
);
|
||||
const frontendPreference =
|
||||
await window.electronAPI.config.get('frontendPreference');
|
||||
if (frontendPreference) {
|
||||
setFrontendPreference(
|
||||
(frontendPreference as FrontendPreference) || 'koboldcpp'
|
||||
|
|
@ -185,10 +177,8 @@ export const GeneralTab = ({
|
|||
};
|
||||
|
||||
const handleSelectInstallDir = async () => {
|
||||
const selectedDir = await safeExecute(
|
||||
() => window.electronAPI.kobold.selectInstallDirectory(),
|
||||
'Failed to select install directory:'
|
||||
);
|
||||
const selectedDir =
|
||||
await window.electronAPI.kobold.selectInstallDirectory();
|
||||
if (selectedDir) {
|
||||
setInstallDir(selectedDir);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import {
|
|||
stripAssetExtensions,
|
||||
compareVersions,
|
||||
} from '@/utils/version';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import { formatDownloadSize } from '@/utils/format';
|
||||
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
|
|
@ -60,24 +59,19 @@ export const VersionsTab = () => {
|
|||
const loadInstalledVersions = useCallback(async () => {
|
||||
setLoadingInstalled(true);
|
||||
|
||||
await safeExecute(async () => {
|
||||
const [versions, currentVersion] = await Promise.all([
|
||||
window.electronAPI.kobold.getInstalledVersions(),
|
||||
window.electronAPI.kobold.getCurrentVersion(),
|
||||
]);
|
||||
const [versions, currentVersion] = await Promise.all([
|
||||
window.electronAPI.kobold.getInstalledVersions(),
|
||||
window.electronAPI.kobold.getCurrentVersion(),
|
||||
]);
|
||||
|
||||
setInstalledVersions(versions);
|
||||
setCurrentVersion(currentVersion);
|
||||
}, 'Failed to load installed versions:');
|
||||
setInstalledVersions(versions);
|
||||
setCurrentVersion(currentVersion);
|
||||
|
||||
setLoadingInstalled(false);
|
||||
}, []);
|
||||
|
||||
const loadLatestRelease = useCallback(async () => {
|
||||
const release = await safeExecute(
|
||||
() => getLatestReleaseWithDownloadStatus(),
|
||||
'Failed to load latest release:'
|
||||
);
|
||||
const release = await getLatestReleaseWithDownloadStatus();
|
||||
if (release) {
|
||||
setLatestRelease(release);
|
||||
}
|
||||
|
|
@ -174,55 +168,45 @@ export const VersionsTab = () => {
|
|||
}, [downloading]);
|
||||
|
||||
const handleDownload = async (version: VersionInfo) => {
|
||||
await safeExecute(async () => {
|
||||
const download = availableDownloads.find((d) => d.name === version.name);
|
||||
if (!download) {
|
||||
throw new Error('Download not found');
|
||||
}
|
||||
const download = availableDownloads.find((d) => d.name === version.name);
|
||||
if (!download) return;
|
||||
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: false,
|
||||
wasCurrentBinary: false,
|
||||
});
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: false,
|
||||
wasCurrentBinary: false,
|
||||
});
|
||||
|
||||
if (success) {
|
||||
await loadInstalledVersions();
|
||||
}
|
||||
}, 'Failed to download:');
|
||||
if (success) {
|
||||
await loadInstalledVersions();
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdate = async (version: VersionInfo) => {
|
||||
await safeExecute(async () => {
|
||||
const download = availableDownloads.find((d) => d.name === version.name);
|
||||
if (!download) {
|
||||
throw new Error('Download not found');
|
||||
}
|
||||
const download = availableDownloads.find((d) => d.name === version.name);
|
||||
if (!download) return;
|
||||
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: true,
|
||||
wasCurrentBinary: version.isCurrent,
|
||||
});
|
||||
const success = await sharedHandleDownload({
|
||||
item: download,
|
||||
isUpdate: true,
|
||||
wasCurrentBinary: version.isCurrent,
|
||||
});
|
||||
|
||||
if (success) {
|
||||
await loadInstalledVersions();
|
||||
}
|
||||
}, 'Failed to update:');
|
||||
if (success) {
|
||||
await loadInstalledVersions();
|
||||
}
|
||||
};
|
||||
|
||||
const makeCurrent = async (version: VersionInfo) => {
|
||||
if (!version.installedPath) return;
|
||||
|
||||
await safeExecute(async () => {
|
||||
const success = await window.electronAPI.kobold.setCurrentVersion(
|
||||
version.installedPath!
|
||||
);
|
||||
const success = await window.electronAPI.kobold.setCurrentVersion(
|
||||
version.installedPath
|
||||
);
|
||||
|
||||
if (success) {
|
||||
await loadInstalledVersions();
|
||||
}
|
||||
}, 'Failed to set current version:');
|
||||
if (success) {
|
||||
await loadInstalledVersions();
|
||||
}
|
||||
};
|
||||
|
||||
if (loadingInstalled || loadingPlatform || loadingRemote) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { useMantineColorScheme } from '@mantine/core';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
|
||||
type ColorScheme = 'light' | 'dark';
|
||||
|
||||
|
|
@ -10,10 +9,7 @@ export const useAppColorScheme = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const loadColorScheme = async () => {
|
||||
const rawScheme = await safeExecute(
|
||||
() => window.electronAPI.app.getColorScheme(),
|
||||
'Failed to get app color scheme'
|
||||
);
|
||||
const rawScheme = await window.electronAPI.app.getColorScheme();
|
||||
|
||||
if (rawScheme) {
|
||||
let resolvedScheme: ColorScheme;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { safeExecute, tryExecute } from '@/utils/logger';
|
||||
import { tryExecute } from '@/utils/logger';
|
||||
import { compareVersions } from '@/utils/version';
|
||||
import { GITHUB_API } from '@/constants';
|
||||
|
||||
|
|
@ -19,18 +19,12 @@ export const useAppUpdateChecker = () => {
|
|||
useEffect(() => {
|
||||
const initializeUpdater = async () => {
|
||||
const [canUpdate, isDownloaded] = await Promise.all([
|
||||
safeExecute(
|
||||
() => window.electronAPI.updater.canAutoUpdate(),
|
||||
'Failed to check auto-update capability'
|
||||
),
|
||||
safeExecute(
|
||||
() => window.electronAPI.updater.isUpdateDownloaded(),
|
||||
'Failed to check update download status'
|
||||
),
|
||||
window.electronAPI.updater.canAutoUpdate(),
|
||||
window.electronAPI.updater.isUpdateDownloaded(),
|
||||
]);
|
||||
|
||||
if (canUpdate !== null) setCanAutoUpdate(canUpdate);
|
||||
if (isDownloaded !== null) setIsUpdateDownloaded(isDownloaded);
|
||||
setCanAutoUpdate(canUpdate);
|
||||
setIsUpdateDownloaded(isDownloaded);
|
||||
};
|
||||
|
||||
void initializeUpdater();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { tryExecute, safeExecute } from '@/utils/logger';
|
||||
import { tryExecute } from '@/utils/logger';
|
||||
import {
|
||||
getDisplayNameFromPath,
|
||||
compareVersions,
|
||||
|
|
@ -27,13 +27,9 @@ export const useUpdateChecker = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const loadDismissedUpdates = async () => {
|
||||
const dismissed = await safeExecute(
|
||||
async () =>
|
||||
(await window.electronAPI.config.get('dismissedUpdates')) as
|
||||
| string[]
|
||||
| undefined,
|
||||
'Failed to load dismissed updates'
|
||||
);
|
||||
const dismissed = (await window.electronAPI.config.get(
|
||||
'dismissedUpdates'
|
||||
)) as string[] | undefined;
|
||||
|
||||
if (dismissed) {
|
||||
setDismissedUpdates(new Set(dismissed));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import type { BackendOption, BackendSupport } from '@/types';
|
||||
|
||||
export interface Warning {
|
||||
|
|
@ -105,10 +104,7 @@ const checkVramWarnings = async (backend: string): Promise<Warning[]> => {
|
|||
const isGpuBackend = ['cuda', 'rocm', 'vulkan', 'clblast'].includes(backend);
|
||||
|
||||
if (isGpuBackend) {
|
||||
const gpuMemoryInfo = await safeExecute(
|
||||
() => window.electronAPI.kobold.detectGPUMemory(),
|
||||
'Failed to detect GPU memory:'
|
||||
);
|
||||
const gpuMemoryInfo = await window.electronAPI.kobold.detectGPUMemory();
|
||||
|
||||
if (gpuMemoryInfo) {
|
||||
const lowVramThreshold = 8;
|
||||
|
|
@ -189,47 +185,43 @@ const checkBackendWarnings = async (params?: {
|
|||
}): Promise<Warning[]> => {
|
||||
const warnings: Warning[] = [];
|
||||
|
||||
const result = await safeExecute(async () => {
|
||||
const [backendSupport, gpuCapabilities, gpuInfo] = await Promise.all([
|
||||
window.electronAPI.kobold.detectBackendSupport(),
|
||||
window.electronAPI.kobold.detectGPUCapabilities(),
|
||||
window.electronAPI.kobold.detectGPU(),
|
||||
]);
|
||||
|
||||
if (!backendSupport) {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
const gpuWarnings = await checkGpuWarnings(
|
||||
backendSupport,
|
||||
gpuCapabilities,
|
||||
gpuInfo
|
||||
);
|
||||
warnings.push(...gpuWarnings);
|
||||
|
||||
if (params) {
|
||||
const { backend, cpuCapabilities, noavx2, failsafe, availableBackends } =
|
||||
params;
|
||||
|
||||
const vramWarnings = await checkVramWarnings(backend);
|
||||
warnings.push(...vramWarnings);
|
||||
|
||||
if (cpuCapabilities) {
|
||||
const cpuWarnings = checkCpuWarnings(
|
||||
backend,
|
||||
cpuCapabilities,
|
||||
noavx2,
|
||||
failsafe,
|
||||
availableBackends
|
||||
);
|
||||
warnings.push(...cpuWarnings);
|
||||
}
|
||||
}
|
||||
const [backendSupport, gpuCapabilities, gpuInfo] = await Promise.all([
|
||||
window.electronAPI.kobold.detectBackendSupport(),
|
||||
window.electronAPI.kobold.detectGPUCapabilities(),
|
||||
window.electronAPI.kobold.detectGPU(),
|
||||
]);
|
||||
|
||||
if (!backendSupport) {
|
||||
return warnings;
|
||||
}, 'Failed to check backend warnings:');
|
||||
}
|
||||
|
||||
return result || warnings;
|
||||
const gpuWarnings = await checkGpuWarnings(
|
||||
backendSupport,
|
||||
gpuCapabilities,
|
||||
gpuInfo
|
||||
);
|
||||
warnings.push(...gpuWarnings);
|
||||
|
||||
if (params) {
|
||||
const { backend, cpuCapabilities, noavx2, failsafe, availableBackends } =
|
||||
params;
|
||||
|
||||
const vramWarnings = await checkVramWarnings(backend);
|
||||
warnings.push(...vramWarnings);
|
||||
|
||||
if (cpuCapabilities) {
|
||||
const cpuWarnings = checkCpuWarnings(
|
||||
backend,
|
||||
cpuCapabilities,
|
||||
noavx2,
|
||||
failsafe,
|
||||
availableBackends
|
||||
);
|
||||
warnings.push(...cpuWarnings);
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
};
|
||||
|
||||
export const useWarnings = ({
|
||||
|
|
@ -253,27 +245,25 @@ export const useWarnings = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const result = await safeExecute(async () => {
|
||||
const [cpuCapabilitiesResult, availableBackends] = await Promise.all([
|
||||
window.electronAPI.kobold.detectCPU(),
|
||||
window.electronAPI.kobold.getAvailableBackends(),
|
||||
]);
|
||||
const [cpuCapabilitiesResult, availableBackends] = await Promise.all([
|
||||
window.electronAPI.kobold.detectCPU(),
|
||||
window.electronAPI.kobold.getAvailableBackends(),
|
||||
]);
|
||||
|
||||
const cpuCapabilities = {
|
||||
avx: cpuCapabilitiesResult.avx,
|
||||
avx2: cpuCapabilitiesResult.avx2,
|
||||
};
|
||||
const cpuCapabilities = {
|
||||
avx: cpuCapabilitiesResult.avx,
|
||||
avx2: cpuCapabilitiesResult.avx2,
|
||||
};
|
||||
|
||||
return checkBackendWarnings({
|
||||
backend,
|
||||
cpuCapabilities,
|
||||
noavx2,
|
||||
failsafe,
|
||||
availableBackends,
|
||||
});
|
||||
}, 'Failed to check backend warnings:');
|
||||
const result = await checkBackendWarnings({
|
||||
backend,
|
||||
cpuCapabilities,
|
||||
noavx2,
|
||||
failsafe,
|
||||
availableBackends,
|
||||
});
|
||||
|
||||
setBackendWarnings(result || []);
|
||||
setBackendWarnings(result);
|
||||
}, [backend, noavx2, failsafe]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ setupAutoUpdater();
|
|||
|
||||
export const checkForUpdates = async () => {
|
||||
if (isDevelopment) {
|
||||
logError('Auto-updater: Cannot check for updates in development mode');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +50,6 @@ export const checkForUpdates = async () => {
|
|||
|
||||
export const downloadUpdate = async () => {
|
||||
if (isDevelopment) {
|
||||
logError('Auto-updater: Cannot download updates in development mode');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,29 @@
|
|||
import { app } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { createLogger, format, type Logger } from 'winston';
|
||||
import { mkdir } from 'fs/promises';
|
||||
import { createLogger, format } from 'winston';
|
||||
import DailyRotateFile from 'winston-daily-rotate-file';
|
||||
import { isDevelopment } from '@/utils/node/environment';
|
||||
|
||||
let logger: Logger | null = null;
|
||||
let isInitialized = false;
|
||||
|
||||
export const initializeLogger = () => {
|
||||
if (isInitialized) return;
|
||||
const ensureLogsDirectory = async (logsDir: string) => {
|
||||
try {
|
||||
await mkdir(logsDir, { recursive: true });
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const createAppLogger = () => {
|
||||
const logsDir = join(app.getPath('userData'), 'logs');
|
||||
|
||||
logger = createLogger({
|
||||
void ensureLogsDirectory(logsDir);
|
||||
|
||||
return createLogger({
|
||||
level: isDevelopment ? 'debug' : 'info',
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.timestamp({
|
||||
format: 'YYYY-MM-DD HH:mm:ss.SSS',
|
||||
}),
|
||||
format.printf(({ timestamp, level, message, error }) => {
|
||||
let logEntry = `${timestamp} [MAIN] [${level.toUpperCase()}] ${message}`;
|
||||
|
||||
|
|
@ -40,25 +48,17 @@ export const initializeLogger = () => {
|
|||
}),
|
||||
],
|
||||
});
|
||||
|
||||
isInitialized = true;
|
||||
};
|
||||
|
||||
const ensureInitialized = () => {
|
||||
if (!isInitialized) {
|
||||
initializeLogger();
|
||||
}
|
||||
};
|
||||
const logger = createAppLogger();
|
||||
|
||||
export const logError = (message: string, error?: Error) => {
|
||||
ensureInitialized();
|
||||
logger!.error(message, { error });
|
||||
logger.error(message, { error });
|
||||
flushLogs();
|
||||
};
|
||||
|
||||
export const flushLogs = () => {
|
||||
ensureInitialized();
|
||||
const fileTransport = logger!.transports.find(
|
||||
const fileTransport = logger.transports.find(
|
||||
(t) => t.constructor.name === 'DailyRotateFile'
|
||||
);
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { execa } from 'execa';
|
||||
import { spawn } from 'child_process';
|
||||
import { platform } from 'process';
|
||||
import { safeExecute } from '@/utils/node/logger';
|
||||
|
||||
|
|
@ -12,16 +12,37 @@ const LINUX_PERFORMANCE_APPS = [
|
|||
];
|
||||
|
||||
async function tryLaunchCommand(command: string, args: string[] = []) {
|
||||
try {
|
||||
await execa(command, args, {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(command, args, {
|
||||
detached: true,
|
||||
stdio: 'ignore',
|
||||
timeout: 2000,
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
let hasResolved = false;
|
||||
|
||||
child.on('error', () => {
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
child.on('spawn', () => {
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
child.unref();
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
child.kill();
|
||||
resolve(false);
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
export const openPerformanceManager = async () =>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { create } from 'zustand';
|
||||
import { safeExecute } from '@/utils/logger';
|
||||
import type { FrontendPreference } from '@/types';
|
||||
|
||||
interface FrontendPreferenceState {
|
||||
|
|
@ -15,19 +14,15 @@ export const useFrontendPreferenceStore = create<FrontendPreferenceState>()(
|
|||
setFrontendPreference: (preference: FrontendPreference) => {
|
||||
set({ frontendPreference: preference });
|
||||
|
||||
safeExecute(async () => {
|
||||
await window.electronAPI.config.set('frontendPreference', preference);
|
||||
}, 'Error saving frontend preference:');
|
||||
window.electronAPI.config.set('frontendPreference', preference);
|
||||
},
|
||||
|
||||
loadFromConfig: async () => {
|
||||
await safeExecute(async () => {
|
||||
const preference = (await window.electronAPI.config.get(
|
||||
'frontendPreference'
|
||||
)) as FrontendPreference;
|
||||
const preference = (await window.electronAPI.config.get(
|
||||
'frontendPreference'
|
||||
)) as FrontendPreference;
|
||||
|
||||
set({ frontendPreference: preference || 'koboldcpp' });
|
||||
}, 'Error loading frontend preference:');
|
||||
set({ frontendPreference: preference || 'koboldcpp' });
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
createTryExecute,
|
||||
createTryExecuteImmediate,
|
||||
createSafeTryExecute,
|
||||
} from '@/utils/shared/logger-core';
|
||||
} from '@/utils/logger-core';
|
||||
|
||||
export const logError = (message: string, error: Error) => {
|
||||
window.electronAPI.logs.logError(message, error);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
createSafeExecute,
|
||||
createTryExecute,
|
||||
createSafeTryExecute,
|
||||
} from '@/utils/shared/logger-core';
|
||||
} from '@/utils/logger-core';
|
||||
|
||||
export const safeExecute = createSafeExecute(logError);
|
||||
export const tryExecute = createTryExecute(logError);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue