mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
code refactoring
This commit is contained in:
parent
73ec41d61b
commit
54d59c8018
24 changed files with 247 additions and 219 deletions
63
src/components/App/Router.tsx
Normal file
63
src/components/App/Router.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { ScreenTransition } from '@/components/App/ScreenTransition';
|
||||||
|
import { DownloadScreen } from '@/components/screens/Download';
|
||||||
|
import { LaunchScreen } from '@/components/screens/Launch';
|
||||||
|
import { InterfaceScreen } from '@/components/screens/Interface';
|
||||||
|
import { WelcomeScreen } from '@/components/screens/Welcome';
|
||||||
|
import type { InterfaceTab, Screen } from '@/types';
|
||||||
|
|
||||||
|
interface AppRouterProps {
|
||||||
|
currentScreen: Screen | null;
|
||||||
|
hasInitialized: boolean;
|
||||||
|
activeInterfaceTab: InterfaceTab;
|
||||||
|
onWelcomeComplete: () => void;
|
||||||
|
onDownloadComplete: () => void;
|
||||||
|
onLaunch: () => void;
|
||||||
|
onTabChange: (tab: InterfaceTab) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppRouter = ({
|
||||||
|
currentScreen,
|
||||||
|
hasInitialized,
|
||||||
|
activeInterfaceTab,
|
||||||
|
onWelcomeComplete,
|
||||||
|
onDownloadComplete,
|
||||||
|
onLaunch,
|
||||||
|
onTabChange,
|
||||||
|
}: AppRouterProps) => {
|
||||||
|
const isInterfaceScreen = currentScreen === 'interface';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ScreenTransition
|
||||||
|
isActive={currentScreen === 'welcome'}
|
||||||
|
shouldAnimate={hasInitialized}
|
||||||
|
>
|
||||||
|
<WelcomeScreen onGetStarted={onWelcomeComplete} />
|
||||||
|
</ScreenTransition>
|
||||||
|
|
||||||
|
<ScreenTransition
|
||||||
|
isActive={currentScreen === 'download'}
|
||||||
|
shouldAnimate={hasInitialized}
|
||||||
|
>
|
||||||
|
<DownloadScreen onDownloadComplete={onDownloadComplete} />
|
||||||
|
</ScreenTransition>
|
||||||
|
|
||||||
|
<ScreenTransition
|
||||||
|
isActive={currentScreen === 'launch'}
|
||||||
|
shouldAnimate={hasInitialized}
|
||||||
|
>
|
||||||
|
<LaunchScreen onLaunch={onLaunch} />
|
||||||
|
</ScreenTransition>
|
||||||
|
|
||||||
|
<ScreenTransition
|
||||||
|
isActive={isInterfaceScreen}
|
||||||
|
shouldAnimate={hasInitialized}
|
||||||
|
>
|
||||||
|
<InterfaceScreen
|
||||||
|
activeTab={activeInterfaceTab}
|
||||||
|
onTabChange={onTabChange}
|
||||||
|
/>
|
||||||
|
</ScreenTransition>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -9,10 +9,12 @@ import {
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Minus, Square, X, Copy, Settings } from 'lucide-react';
|
import { Minus, Square, X, Copy, Settings } from 'lucide-react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useInterfaceOptions } from '@/hooks/useInterfaceSelection';
|
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
||||||
|
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
|
||||||
|
import { getAvailableInterfaceOptions } from '@/utils/interface';
|
||||||
import { useLogoClickSounds } from '@/hooks/useLogoClickSounds';
|
import { useLogoClickSounds } from '@/hooks/useLogoClickSounds';
|
||||||
import { SettingsModal } from '@/components/settings/SettingsModal';
|
import { SettingsModal } from '@/components/settings/SettingsModal';
|
||||||
import { UpdateButton } from '@/components/UpdateButton';
|
import { UpdateButton } from '@/components/App/UpdateButton';
|
||||||
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
|
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
|
||||||
import icon from '/icon.png';
|
import icon from '/icon.png';
|
||||||
import { PRODUCT_NAME, TITLEBAR_HEIGHT } from '@/constants';
|
import { PRODUCT_NAME, TITLEBAR_HEIGHT } from '@/constants';
|
||||||
|
|
@ -32,12 +34,44 @@ export const TitleBar = ({
|
||||||
onTabChange,
|
onTabChange,
|
||||||
}: TitleBarProps) => {
|
}: TitleBarProps) => {
|
||||||
const colorScheme = useAppColorScheme();
|
const colorScheme = useAppColorScheme();
|
||||||
const interfaceOptions = useInterfaceOptions();
|
|
||||||
const { handleLogoClick, getLogoStyles } = useLogoClickSounds();
|
const { handleLogoClick, getLogoStyles } = useLogoClickSounds();
|
||||||
const [isMaximized, setIsMaximized] = useState(false);
|
const [isMaximized, setIsMaximized] = useState(false);
|
||||||
const [isSelectOpen, setIsSelectOpen] = useState(false);
|
const [isSelectOpen, setIsSelectOpen] = useState(false);
|
||||||
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const { isTextMode, isImageGenerationMode } = useLaunchConfigStore();
|
||||||
|
const { frontendPreference } = useFrontendPreferenceStore();
|
||||||
|
const interfaceOptions = getAvailableInterfaceOptions({
|
||||||
|
frontendPreference,
|
||||||
|
isTextMode,
|
||||||
|
isImageGenerationMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleTabChange = (value: string | null) => {
|
||||||
|
if (value === 'eject') {
|
||||||
|
onEject();
|
||||||
|
} else if (value) {
|
||||||
|
onTabChange(value as InterfaceTab);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderOption = ({
|
||||||
|
option,
|
||||||
|
}: {
|
||||||
|
option: { value: string; label: string };
|
||||||
|
}) => (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
color:
|
||||||
|
option.value === 'eject' ? 'var(--mantine-color-red-6)' : undefined,
|
||||||
|
fontWeight: option.value === 'eject' ? 600 : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const cleanup = window.electronAPI.app.onWindowStateToggle(() =>
|
const cleanup = window.electronAPI.app.onWindowStateToggle(() =>
|
||||||
setIsMaximized((prev) => !prev)
|
setIsMaximized((prev) => !prev)
|
||||||
|
|
@ -93,37 +127,15 @@ export const TitleBar = ({
|
||||||
<Select
|
<Select
|
||||||
placeholder="Interface"
|
placeholder="Interface"
|
||||||
value={currentTab}
|
value={currentTab}
|
||||||
onChange={(value) => {
|
onChange={handleTabChange}
|
||||||
if (value === 'eject') {
|
|
||||||
onEject();
|
|
||||||
} else {
|
|
||||||
onTabChange(value as InterfaceTab);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onDropdownOpen={() => setIsSelectOpen(true)}
|
onDropdownOpen={() => setIsSelectOpen(true)}
|
||||||
onDropdownClose={() => setIsSelectOpen(false)}
|
onDropdownClose={() => setIsSelectOpen(false)}
|
||||||
data={interfaceOptions}
|
data={interfaceOptions}
|
||||||
renderOption={({ option }) => (
|
renderOption={renderOption}
|
||||||
<Box
|
|
||||||
style={{
|
|
||||||
textAlign: 'center',
|
|
||||||
color:
|
|
||||||
option.value === 'eject'
|
|
||||||
? 'var(--mantine-color-red-6)'
|
|
||||||
: undefined,
|
|
||||||
fontWeight: option.value === 'eject' ? 600 : undefined,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{option.label}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
allowDeselect={false}
|
allowDeselect={false}
|
||||||
variant="unstyled"
|
variant="unstyled"
|
||||||
size="sm"
|
size="sm"
|
||||||
style={{
|
style={{ textAlign: 'center', minWidth: '7.5rem' }}
|
||||||
textAlign: 'center',
|
|
||||||
minWidth: '7.5rem',
|
|
||||||
}}
|
|
||||||
styles={{
|
styles={{
|
||||||
input: {
|
input: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
|
@ -11,7 +11,9 @@ import {
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Download, X, ExternalLink } from 'lucide-react';
|
import { Download, X, ExternalLink } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import type { InstalledVersion, DownloadItem } from '@/types/electron';
|
import type { DownloadItem } from '@/types/electron';
|
||||||
|
import type { BinaryUpdateInfo } from '@/hooks/useUpdateChecker';
|
||||||
|
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||||
import { getDisplayNameFromPath } from '@/utils/version';
|
import { getDisplayNameFromPath } from '@/utils/version';
|
||||||
import { GITHUB_API, MODAL_STYLES_WITH_TITLEBAR } from '@/constants';
|
import { GITHUB_API, MODAL_STYLES_WITH_TITLEBAR } from '@/constants';
|
||||||
import { safeExecute } from '@/utils/logger';
|
import { safeExecute } from '@/utils/logger';
|
||||||
|
|
@ -19,24 +21,27 @@ import { safeExecute } from '@/utils/logger';
|
||||||
interface UpdateAvailableModalProps {
|
interface UpdateAvailableModalProps {
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
currentVersion?: InstalledVersion;
|
updateInfo?: BinaryUpdateInfo;
|
||||||
availableUpdate?: DownloadItem;
|
|
||||||
onUpdate: (download: DownloadItem) => Promise<void>;
|
onUpdate: (download: DownloadItem) => Promise<void>;
|
||||||
isDownloading?: boolean;
|
|
||||||
downloadProgress?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UpdateAvailableModal = ({
|
export const UpdateAvailableModal = ({
|
||||||
opened,
|
opened,
|
||||||
onClose,
|
onClose,
|
||||||
currentVersion,
|
updateInfo,
|
||||||
availableUpdate,
|
|
||||||
onUpdate,
|
onUpdate,
|
||||||
isDownloading = false,
|
|
||||||
downloadProgress = 0,
|
|
||||||
}: UpdateAvailableModalProps) => {
|
}: UpdateAvailableModalProps) => {
|
||||||
|
const { downloading, downloadProgress } = useKoboldVersions();
|
||||||
|
const currentVersion = updateInfo?.currentVersion;
|
||||||
|
const availableUpdate = updateInfo?.availableUpdate;
|
||||||
const [isUpdating, setIsUpdating] = useState(false);
|
const [isUpdating, setIsUpdating] = useState(false);
|
||||||
|
|
||||||
|
const isDownloading =
|
||||||
|
!!availableUpdate && downloading === availableUpdate.name;
|
||||||
|
const currentProgress = availableUpdate
|
||||||
|
? downloadProgress[availableUpdate.name] || 0
|
||||||
|
: 0;
|
||||||
|
|
||||||
const handleUpdate = async () => {
|
const handleUpdate = async () => {
|
||||||
if (availableUpdate) {
|
if (availableUpdate) {
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
|
|
@ -113,7 +118,7 @@ export const UpdateAvailableModal = ({
|
||||||
|
|
||||||
<Stack gap="xs" style={{ minHeight: '2.75rem' }}>
|
<Stack gap="xs" style={{ minHeight: '2.75rem' }}>
|
||||||
<Progress
|
<Progress
|
||||||
value={isDownloading ? Math.min(downloadProgress, 100) : 0}
|
value={isDownloading ? Math.min(currentProgress, 100) : 0}
|
||||||
color="orange"
|
color="orange"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -131,7 +136,7 @@ export const UpdateAvailableModal = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isDownloading
|
{isDownloading
|
||||||
? `${Math.min(downloadProgress, 100).toFixed(1)}% complete`
|
? `${Math.min(currentProgress, 100).toFixed(1)}% complete`
|
||||||
: 'Preparing update...'}
|
: 'Preparing update...'}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { AppShell, Loader, Center, Stack, Text } from '@mantine/core';
|
import {
|
||||||
import { DownloadScreen } from '@/components/screens/Download';
|
AppShell,
|
||||||
import { LaunchScreen } from '@/components/screens/Launch';
|
Loader,
|
||||||
import { InterfaceScreen } from '@/components/screens/Interface';
|
Center,
|
||||||
import { WelcomeScreen } from '@/components/screens/Welcome';
|
Stack,
|
||||||
import { UpdateAvailableModal } from '@/components/UpdateAvailableModal';
|
Text,
|
||||||
import { EjectConfirmModal } from '@/components/EjectConfirmModal';
|
useMantineColorScheme,
|
||||||
import { ScreenTransition } from '@/components/ScreenTransition';
|
} from '@mantine/core';
|
||||||
import { TitleBar } from '@/components/TitleBar';
|
import { UpdateAvailableModal } from '@/components/App/UpdateAvailableModal';
|
||||||
import { StatusBar } from '@/components/StatusBar';
|
import { EjectConfirmModal } from '@/components/App/EjectConfirmModal';
|
||||||
import { ErrorBoundary } from '@/components/ErrorBoundary';
|
import { TitleBar } from '@/components/App/TitleBar';
|
||||||
|
import { StatusBar } from '@/components/App/StatusBar';
|
||||||
|
import { ErrorBoundary } from '@/components/App/ErrorBoundary';
|
||||||
|
import { AppRouter } from '@/components/App/Router';
|
||||||
import { useUpdateChecker } from '@/hooks/useUpdateChecker';
|
import { useUpdateChecker } from '@/hooks/useUpdateChecker';
|
||||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||||
|
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
|
||||||
import { STATUSBAR_HEIGHT, TITLEBAR_HEIGHT } from '@/constants';
|
import { STATUSBAR_HEIGHT, TITLEBAR_HEIGHT } from '@/constants';
|
||||||
import type { DownloadItem } from '@/types/electron';
|
import type { DownloadItem } from '@/types/electron';
|
||||||
import type { InterfaceTab, Screen } from '@/types';
|
import type { InterfaceTab, Screen } from '@/types';
|
||||||
|
|
@ -24,6 +28,13 @@ export const App = () => {
|
||||||
const [ejectConfirmModalOpen, setEjectConfirmModalOpen] = useState(false);
|
const [ejectConfirmModalOpen, setEjectConfirmModalOpen] = useState(false);
|
||||||
const isInterfaceScreen = currentScreen === 'interface';
|
const isInterfaceScreen = currentScreen === 'interface';
|
||||||
|
|
||||||
|
const appColorScheme = useAppColorScheme();
|
||||||
|
const { setColorScheme } = useMantineColorScheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setColorScheme(appColorScheme);
|
||||||
|
}, [appColorScheme, setColorScheme]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
updateInfo: binaryUpdateInfo,
|
updateInfo: binaryUpdateInfo,
|
||||||
showUpdateModal,
|
showUpdateModal,
|
||||||
|
|
@ -31,11 +42,7 @@ export const App = () => {
|
||||||
dismissUpdate,
|
dismissUpdate,
|
||||||
} = useUpdateChecker();
|
} = useUpdateChecker();
|
||||||
|
|
||||||
const {
|
const { handleDownload: sharedHandleDownload } = useKoboldVersions();
|
||||||
handleDownload: sharedHandleDownload,
|
|
||||||
downloading,
|
|
||||||
downloadProgress,
|
|
||||||
} = useKoboldVersions();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkInstallation = async () => {
|
const checkInstallation = async () => {
|
||||||
|
|
@ -77,23 +84,6 @@ export const App = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownloadComplete = async () => {
|
|
||||||
await window.electronAPI.kobold.getCurrentVersion();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setCurrentScreen('launch');
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLaunch = () => {
|
|
||||||
setActiveInterfaceTab('terminal');
|
|
||||||
setCurrentScreen('interface');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBackToLaunch = () => {
|
|
||||||
setCurrentScreen('launch');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEject = async () => {
|
const handleEject = async () => {
|
||||||
const skipEjectConfirmation = await window.electronAPI.config.get(
|
const skipEjectConfirmation = await window.electronAPI.config.get(
|
||||||
'skipEjectConfirmation'
|
'skipEjectConfirmation'
|
||||||
|
|
@ -108,7 +98,7 @@ export const App = () => {
|
||||||
|
|
||||||
const performEject = () => {
|
const performEject = () => {
|
||||||
window.electronAPI.kobold.stopKoboldCpp();
|
window.electronAPI.kobold.stopKoboldCpp();
|
||||||
handleBackToLaunch();
|
setCurrentScreen('launch');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEjectConfirm = (skipConfirmation: boolean) => {
|
const handleEjectConfirm = (skipConfirmation: boolean) => {
|
||||||
|
|
@ -118,15 +108,17 @@ export const App = () => {
|
||||||
performEject();
|
performEject();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWelcomeComplete = async () => {
|
const handleWelcomeComplete = () => {
|
||||||
window.electronAPI.config.set('hasSeenWelcome', true);
|
setCurrentScreen('download');
|
||||||
|
};
|
||||||
|
|
||||||
const versions = await window.electronAPI.kobold.getInstalledVersions();
|
const handleDownloadComplete = () => {
|
||||||
if (versions.length > 0) {
|
setTimeout(() => setCurrentScreen('launch'), 500);
|
||||||
setCurrentScreen('launch');
|
};
|
||||||
} else {
|
|
||||||
setCurrentScreen('download');
|
const handleLaunch = () => {
|
||||||
}
|
setActiveInterfaceTab('terminal');
|
||||||
|
setCurrentScreen('interface');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -165,53 +157,23 @@ export const App = () => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<AppRouter
|
||||||
<ScreenTransition
|
currentScreen={currentScreen}
|
||||||
isActive={currentScreen === 'welcome'}
|
hasInitialized={hasInitialized}
|
||||||
shouldAnimate={hasInitialized}
|
activeInterfaceTab={activeInterfaceTab}
|
||||||
>
|
onWelcomeComplete={handleWelcomeComplete}
|
||||||
<WelcomeScreen onGetStarted={handleWelcomeComplete} />
|
onDownloadComplete={handleDownloadComplete}
|
||||||
</ScreenTransition>
|
onLaunch={handleLaunch}
|
||||||
|
onTabChange={setActiveInterfaceTab}
|
||||||
<ScreenTransition
|
/>
|
||||||
isActive={currentScreen === 'download'}
|
|
||||||
shouldAnimate={hasInitialized}
|
|
||||||
>
|
|
||||||
<DownloadScreen onDownloadComplete={handleDownloadComplete} />
|
|
||||||
</ScreenTransition>
|
|
||||||
|
|
||||||
<ScreenTransition
|
|
||||||
isActive={currentScreen === 'launch'}
|
|
||||||
shouldAnimate={hasInitialized}
|
|
||||||
>
|
|
||||||
<LaunchScreen onLaunch={handleLaunch} />
|
|
||||||
</ScreenTransition>
|
|
||||||
|
|
||||||
<ScreenTransition
|
|
||||||
isActive={isInterfaceScreen}
|
|
||||||
shouldAnimate={hasInitialized}
|
|
||||||
>
|
|
||||||
<InterfaceScreen
|
|
||||||
activeTab={activeInterfaceTab}
|
|
||||||
onTabChange={setActiveInterfaceTab}
|
|
||||||
/>
|
|
||||||
</ScreenTransition>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
||||||
<UpdateAvailableModal
|
<UpdateAvailableModal
|
||||||
opened={showUpdateModal && !!binaryUpdateInfo}
|
opened={showUpdateModal && !!binaryUpdateInfo}
|
||||||
onClose={dismissUpdate}
|
onClose={dismissUpdate}
|
||||||
currentVersion={binaryUpdateInfo?.currentVersion}
|
updateInfo={binaryUpdateInfo || undefined}
|
||||||
availableUpdate={binaryUpdateInfo?.availableUpdate}
|
|
||||||
onUpdate={handleBinaryUpdate}
|
onUpdate={handleBinaryUpdate}
|
||||||
isDownloading={downloading === binaryUpdateInfo?.availableUpdate.name}
|
|
||||||
downloadProgress={
|
|
||||||
binaryUpdateInfo
|
|
||||||
? downloadProgress[binaryUpdateInfo.availableUpdate.name] || 0
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</AppShell.Main>
|
</AppShell.Main>
|
||||||
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { Box, Text, Stack } from '@mantine/core';
|
import { Box, Text, Stack } from '@mantine/core';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
||||||
|
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
|
||||||
|
import { getServerInterfaceInfo } from '@/utils/interface';
|
||||||
import { TITLEBAR_HEIGHT, STATUSBAR_HEIGHT } from '@/constants';
|
import { TITLEBAR_HEIGHT, STATUSBAR_HEIGHT } from '@/constants';
|
||||||
import { useServerInterfaceInfo } from '@/hooks/useInterfaceSelection';
|
|
||||||
|
|
||||||
interface ServerTabProps {
|
interface ServerTabProps {
|
||||||
serverUrl?: string;
|
serverUrl?: string;
|
||||||
|
|
@ -13,9 +16,24 @@ export const ServerTab = ({
|
||||||
isServerReady,
|
isServerReady,
|
||||||
activeTab,
|
activeTab,
|
||||||
}: ServerTabProps) => {
|
}: ServerTabProps) => {
|
||||||
const { url: iframeUrl, title } = useServerInterfaceInfo(
|
const { isImageGenerationMode } = useLaunchConfigStore();
|
||||||
serverUrl || '',
|
const { frontendPreference } = useFrontendPreferenceStore();
|
||||||
activeTab
|
|
||||||
|
const effectiveImageMode =
|
||||||
|
activeTab === 'chat-image'
|
||||||
|
? true
|
||||||
|
: activeTab === 'chat-text'
|
||||||
|
? false
|
||||||
|
: isImageGenerationMode;
|
||||||
|
|
||||||
|
const { url: iframeUrl, title } = useMemo(
|
||||||
|
() =>
|
||||||
|
getServerInterfaceInfo({
|
||||||
|
frontendPreference,
|
||||||
|
isImageGenerationMode: effectiveImageMode,
|
||||||
|
serverUrl: serverUrl || '',
|
||||||
|
}),
|
||||||
|
[frontendPreference, effectiveImageMode, serverUrl]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isServerReady || !serverUrl) {
|
if (!isServerReady || !serverUrl) {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import {
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import { handleTerminalOutput, processTerminalContent } from '@/utils/terminal';
|
import { handleTerminalOutput, processTerminalContent } from '@/utils/terminal';
|
||||||
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
||||||
import { useFrontendPreference } from '@/hooks/useInterfaceSelection';
|
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
|
||||||
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
|
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
|
||||||
|
|
||||||
interface TerminalTabProps {
|
interface TerminalTabProps {
|
||||||
|
|
@ -28,7 +28,7 @@ export interface TerminalTabRef {
|
||||||
export const TerminalTab = forwardRef<TerminalTabRef, TerminalTabProps>(
|
export const TerminalTab = forwardRef<TerminalTabRef, TerminalTabProps>(
|
||||||
({ onServerReady }, ref) => {
|
({ onServerReady }, ref) => {
|
||||||
const { host, port, isImageGenerationMode } = useLaunchConfigStore();
|
const { host, port, isImageGenerationMode } = useLaunchConfigStore();
|
||||||
const { frontendPreference } = useFrontendPreference();
|
const { frontendPreference } = useFrontendPreferenceStore();
|
||||||
const colorScheme = useAppColorScheme();
|
const colorScheme = useAppColorScheme();
|
||||||
const [terminalContent, setTerminalContent] = useState('');
|
const [terminalContent, setTerminalContent] = useState('');
|
||||||
const [isUserScrolling, setIsUserScrolling] = useState(false);
|
const [isUserScrolling, setIsUserScrolling] = useState(false);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
||||||
import { ServerTab } from '@/components/screens/Interface/ServerTab';
|
import { ServerTab } from '@/components/screens/Interface/ServerTab';
|
||||||
import {
|
import {
|
||||||
TerminalTab,
|
TerminalTab,
|
||||||
type TerminalTabRef,
|
type TerminalTabRef,
|
||||||
} from '@/components/screens/Interface/TerminalTab';
|
} from '@/components/screens/Interface/TerminalTab';
|
||||||
import { useDefaultInterfaceTab } from '@/hooks/useInterfaceSelection';
|
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
||||||
|
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
|
||||||
|
import { getDefaultInterfaceTab } from '@/utils/interface';
|
||||||
import type { InterfaceTab } from '@/types';
|
import type { InterfaceTab } from '@/types';
|
||||||
|
|
||||||
interface InterfaceScreenProps {
|
interface InterfaceScreenProps {
|
||||||
|
|
@ -19,7 +21,19 @@ export const InterfaceScreen = ({
|
||||||
const [serverUrl, setServerUrl] = useState('');
|
const [serverUrl, setServerUrl] = useState('');
|
||||||
const [isServerReady, setIsServerReady] = useState(false);
|
const [isServerReady, setIsServerReady] = useState(false);
|
||||||
const terminalTabRef = useRef<TerminalTabRef>(null);
|
const terminalTabRef = useRef<TerminalTabRef>(null);
|
||||||
const defaultInterfaceTab = useDefaultInterfaceTab();
|
|
||||||
|
const { isTextMode, isImageGenerationMode } = useLaunchConfigStore();
|
||||||
|
const { frontendPreference } = useFrontendPreferenceStore();
|
||||||
|
|
||||||
|
const defaultInterfaceTab = useMemo(
|
||||||
|
() =>
|
||||||
|
getDefaultInterfaceTab({
|
||||||
|
frontendPreference,
|
||||||
|
isTextMode,
|
||||||
|
isImageGenerationMode,
|
||||||
|
}),
|
||||||
|
[frontendPreference, isTextMode, isImageGenerationMode]
|
||||||
|
);
|
||||||
|
|
||||||
const handleServerReady = useCallback(
|
const handleServerReady = useCallback(
|
||||||
(url: string) => {
|
(url: string) => {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import { useState, useEffect } from 'react';
|
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 { CommandLineArgumentsModal } from '@/components/CommandLineArgumentsModal';
|
import { CommandLineArgumentsModal } from '@/components/screens/Launch/CommandLineArgumentsModal';
|
||||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
|
|
||||||
export const AdvancedTab = () => {
|
export const AdvancedTab = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Stack, Text, Group, TextInput, Slider } from '@mantine/core';
|
import { Stack, Text, Group, TextInput, Slider } from '@mantine/core';
|
||||||
import { InfoTooltip } from '@/components/InfoTooltip';
|
import { InfoTooltip } from '@/components/InfoTooltip';
|
||||||
import { BackendSelector } from '@/components/screens/Launch/GeneralTab/BackendSelector';
|
import { BackendSelector } from '@/components/screens/Launch/GeneralTab/BackendSelector';
|
||||||
import { ModelFileField } from '@/components/ModelFileField';
|
import { ModelFileField } from '@/components/screens/Launch/ModelFileField';
|
||||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
|
|
||||||
export const GeneralTab = () => {
|
export const GeneralTab = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Stack } from '@mantine/core';
|
import { Stack } from '@mantine/core';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ModelFileField } from '@/components/ModelFileField';
|
import { ModelFileField } from '@/components/screens/Launch/ModelFileField';
|
||||||
import { SelectWithTooltip } from '@/components/SelectWithTooltip';
|
import { SelectWithTooltip } from '@/components/SelectWithTooltip';
|
||||||
import { IMAGE_MODEL_PRESETS } from '@/constants/imageModelPresets';
|
import { IMAGE_MODEL_PRESETS } from '@/constants/imageModelPresets';
|
||||||
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { Folder, FolderOpen, Monitor } from 'lucide-react';
|
import { Folder, FolderOpen, Monitor } from 'lucide-react';
|
||||||
import type { FrontendPreference } from '@/types';
|
import type { FrontendPreference } from '@/types';
|
||||||
|
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
|
||||||
import { FRONTENDS } from '@/constants';
|
import { FRONTENDS } from '@/constants';
|
||||||
|
|
||||||
interface FrontendRequirement {
|
interface FrontendRequirement {
|
||||||
|
|
@ -36,8 +37,8 @@ export const GeneralTab = ({
|
||||||
isOnInterfaceScreen = false,
|
isOnInterfaceScreen = false,
|
||||||
}: GeneralTabProps) => {
|
}: GeneralTabProps) => {
|
||||||
const [installDir, setInstallDir] = useState('');
|
const [installDir, setInstallDir] = useState('');
|
||||||
const [FrontendPreference, setFrontendPreference] =
|
const { frontendPreference, setFrontendPreference, loadFrontendPreference } =
|
||||||
useState<FrontendPreference>('koboldcpp');
|
useFrontendPreferenceStore();
|
||||||
const [frontendRequirements, setFrontendRequirements] = useState<
|
const [frontendRequirements, setFrontendRequirements] = useState<
|
||||||
Map<string, boolean>
|
Map<string, boolean>
|
||||||
>(new Map());
|
>(new Map());
|
||||||
|
|
@ -108,13 +109,12 @@ export const GeneralTab = ({
|
||||||
setFrontendRequirements(requirementResults);
|
setFrontendRequirements(requirementResults);
|
||||||
|
|
||||||
const currentFrontendConfig = frontendConfigs.find(
|
const currentFrontendConfig = frontendConfigs.find(
|
||||||
(config) => config.value === FrontendPreference
|
(config) => config.value === frontendPreference
|
||||||
);
|
);
|
||||||
if (currentFrontendConfig && !requirementResults.get(FrontendPreference)) {
|
if (currentFrontendConfig && !requirementResults.get(frontendPreference)) {
|
||||||
window.electronAPI.config.set('frontendPreference', 'koboldcpp');
|
|
||||||
setFrontendPreference('koboldcpp');
|
setFrontendPreference('koboldcpp');
|
||||||
}
|
}
|
||||||
}, [frontendConfigs, FrontendPreference]);
|
}, [frontendConfigs, frontendPreference, setFrontendPreference]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialize = async () => {
|
const initialize = async () => {
|
||||||
|
|
@ -132,10 +132,10 @@ export const GeneralTab = ({
|
||||||
|
|
||||||
window.addEventListener('focus', handleFocus);
|
window.addEventListener('focus', handleFocus);
|
||||||
return () => window.removeEventListener('focus', handleFocus);
|
return () => window.removeEventListener('focus', handleFocus);
|
||||||
}, [checkAllFrontendRequirements]);
|
}, [checkAllFrontendRequirements, loadFrontendPreference]);
|
||||||
|
|
||||||
const getSelectedFrontendConfig = () =>
|
const getSelectedFrontendConfig = () =>
|
||||||
frontendConfigs.find((config) => config.value === FrontendPreference);
|
frontendConfigs.find((config) => config.value === frontendPreference);
|
||||||
|
|
||||||
const getUnmetRequirements = () => {
|
const getUnmetRequirements = () => {
|
||||||
const selectedConfig = getSelectedFrontendConfig();
|
const selectedConfig = getSelectedFrontendConfig();
|
||||||
|
|
@ -157,10 +157,10 @@ export const GeneralTab = ({
|
||||||
frontendRequirements.get(frontendValue) ?? true;
|
frontendRequirements.get(frontendValue) ?? true;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (FrontendPreference) {
|
if (frontendPreference) {
|
||||||
checkAllFrontendRequirements();
|
checkAllFrontendRequirements();
|
||||||
}
|
}
|
||||||
}, [FrontendPreference, checkAllFrontendRequirements]);
|
}, [frontendPreference, checkAllFrontendRequirements]);
|
||||||
|
|
||||||
const loadCurrentInstallDir = async () => {
|
const loadCurrentInstallDir = async () => {
|
||||||
const currentDir = await window.electronAPI.kobold.getCurrentInstallDir();
|
const currentDir = await window.electronAPI.kobold.getCurrentInstallDir();
|
||||||
|
|
@ -169,16 +169,6 @@ export const GeneralTab = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadFrontendPreference = async () => {
|
|
||||||
const frontendPreference =
|
|
||||||
await window.electronAPI.config.get('frontendPreference');
|
|
||||||
if (frontendPreference) {
|
|
||||||
setFrontendPreference(
|
|
||||||
(frontendPreference as FrontendPreference) || 'koboldcpp'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelectInstallDir = async () => {
|
const handleSelectInstallDir = async () => {
|
||||||
const selectedDir =
|
const selectedDir =
|
||||||
await window.electronAPI.kobold.selectInstallDirectory();
|
await window.electronAPI.kobold.selectInstallDirectory();
|
||||||
|
|
@ -195,8 +185,6 @@ export const GeneralTab = ({
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await checkAllFrontendRequirements();
|
await checkAllFrontendRequirements();
|
||||||
|
|
||||||
window.electronAPI.config.set('frontendPreference', value);
|
|
||||||
setFrontendPreference(value as FrontendPreference);
|
setFrontendPreference(value as FrontendPreference);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -263,7 +251,7 @@ export const GeneralTab = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={FrontendPreference}
|
value={frontendPreference}
|
||||||
onChange={handleFrontendPreferenceChange}
|
onChange={handleFrontendPreferenceChange}
|
||||||
disabled={isOnInterfaceScreen}
|
disabled={isOnInterfaceScreen}
|
||||||
onClick={() => checkAllFrontendRequirements()}
|
onClick={() => checkAllFrontendRequirements()}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useMantineColorScheme } from '@mantine/core';
|
|
||||||
|
|
||||||
type ColorScheme = 'light' | 'dark';
|
type ColorScheme = 'light' | 'dark';
|
||||||
|
|
||||||
export const useAppColorScheme = () => {
|
export const useAppColorScheme = () => {
|
||||||
const [colorScheme, setColorScheme] = useState<ColorScheme>('light');
|
const [colorScheme, setColorScheme] = useState<ColorScheme>('light');
|
||||||
const { setColorScheme: setMantineColorScheme } = useMantineColorScheme();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadColorScheme = async () => {
|
const loadColorScheme = async () => {
|
||||||
|
|
@ -24,12 +22,11 @@ export const useAppColorScheme = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setColorScheme(resolvedScheme);
|
setColorScheme(resolvedScheme);
|
||||||
setMantineColorScheme(resolvedScheme);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void loadColorScheme();
|
void loadColorScheme();
|
||||||
}, [setMantineColorScheme]);
|
}, []);
|
||||||
|
|
||||||
return colorScheme;
|
return colorScheme;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,32 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
import type { FrontendPreference } from '@/types';
|
||||||
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
|
|
||||||
import {
|
|
||||||
getAvailableInterfaceOptions,
|
|
||||||
getDefaultInterfaceTab,
|
|
||||||
getServerInterfaceInfo,
|
|
||||||
} from '@/utils/interface';
|
|
||||||
|
|
||||||
export function useFrontendPreference() {
|
export function useFrontendPreference() {
|
||||||
const { frontendPreference, setFrontendPreference, loadFromConfig } =
|
const [frontendPreference, setFrontendPreferenceState] =
|
||||||
useFrontendPreferenceStore();
|
useState<FrontendPreference>('koboldcpp');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const loadFromConfig = async () => {
|
||||||
|
const preference = (await window.electronAPI.config.get(
|
||||||
|
'frontendPreference'
|
||||||
|
)) as FrontendPreference;
|
||||||
|
|
||||||
|
setFrontendPreferenceState(preference || 'koboldcpp');
|
||||||
|
};
|
||||||
|
|
||||||
loadFromConfig();
|
loadFromConfig();
|
||||||
}, [loadFromConfig]);
|
}, []);
|
||||||
|
|
||||||
|
const setFrontendPreference = useCallback(
|
||||||
|
(preference: FrontendPreference) => {
|
||||||
|
setFrontendPreferenceState(preference);
|
||||||
|
window.electronAPI.config.set('frontendPreference', preference);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
frontendPreference,
|
frontendPreference,
|
||||||
setFrontendPreference,
|
setFrontendPreference,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useInterfaceOptions() {
|
|
||||||
const { isTextMode, isImageGenerationMode } = useLaunchConfigStore();
|
|
||||||
const { frontendPreference } = useFrontendPreference();
|
|
||||||
|
|
||||||
return getAvailableInterfaceOptions({
|
|
||||||
frontendPreference,
|
|
||||||
isTextMode,
|
|
||||||
isImageGenerationMode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDefaultInterfaceTab() {
|
|
||||||
const { isTextMode, isImageGenerationMode } = useLaunchConfigStore();
|
|
||||||
const { frontendPreference } = useFrontendPreference();
|
|
||||||
|
|
||||||
return getDefaultInterfaceTab({
|
|
||||||
frontendPreference,
|
|
||||||
isTextMode,
|
|
||||||
isImageGenerationMode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useServerInterfaceInfo(serverUrl: string, activeTab?: string) {
|
|
||||||
const { isImageGenerationMode } = useLaunchConfigStore();
|
|
||||||
const { frontendPreference } = useFrontendPreference();
|
|
||||||
|
|
||||||
const effectiveImageMode =
|
|
||||||
activeTab === 'chat-image'
|
|
||||||
? true
|
|
||||||
: activeTab === 'chat-text'
|
|
||||||
? false
|
|
||||||
: isImageGenerationMode;
|
|
||||||
|
|
||||||
return getServerInterfaceInfo({
|
|
||||||
frontendPreference,
|
|
||||||
isImageGenerationMode: effectiveImageMode,
|
|
||||||
serverUrl,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||||
import { getROCmDownload } from '@/utils/rocm';
|
import { getROCmDownload } from '@/utils/rocm';
|
||||||
import type { InstalledVersion, DownloadItem } from '@/types/electron';
|
import type { InstalledVersion, DownloadItem } from '@/types/electron';
|
||||||
|
|
||||||
interface UpdateInfo {
|
export interface BinaryUpdateInfo {
|
||||||
currentVersion: InstalledVersion;
|
currentVersion: InstalledVersion;
|
||||||
availableUpdate: DownloadItem;
|
availableUpdate: DownloadItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUpdateChecker = () => {
|
export const useUpdateChecker = () => {
|
||||||
const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
|
const [updateInfo, setUpdateInfo] = useState<BinaryUpdateInfo | null>(null);
|
||||||
const [isChecking, setIsChecking] = useState(false);
|
const [isChecking, setIsChecking] = useState(false);
|
||||||
const [showUpdateModal, setShowUpdateModal] = useState(false);
|
const [showUpdateModal, setShowUpdateModal] = useState(false);
|
||||||
const [dismissedUpdates, setDismissedUpdates] = useState<Set<string>>(
|
const [dismissedUpdates, setDismissedUpdates] = useState<Set<string>>(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { StrictMode } from 'react';
|
import { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { MantineProvider, createTheme } from '@mantine/core';
|
import { MantineProvider, createTheme } from '@mantine/core';
|
||||||
import { App } from '@/App.tsx';
|
import { App } from '@/components/App';
|
||||||
import '@/styles/index.css';
|
import '@/styles/index.css';
|
||||||
|
|
||||||
const theme = createTheme({
|
const theme = createTheme({
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,22 @@
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { FrontendPreference } from '@/types';
|
import type { FrontendPreference } from '@/types';
|
||||||
|
|
||||||
interface FrontendPreferenceState {
|
interface FrontendPreferenceStore {
|
||||||
frontendPreference: FrontendPreference;
|
frontendPreference: FrontendPreference;
|
||||||
setFrontendPreference: (preference: FrontendPreference) => void;
|
setFrontendPreference: (preference: FrontendPreference) => void;
|
||||||
loadFromConfig: () => Promise<void>;
|
loadFrontendPreference: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFrontendPreferenceStore = create<FrontendPreferenceState>()(
|
export const useFrontendPreferenceStore = create<FrontendPreferenceStore>(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
frontendPreference: 'koboldcpp',
|
frontendPreference: 'koboldcpp',
|
||||||
|
|
||||||
setFrontendPreference: (preference: FrontendPreference) => {
|
setFrontendPreference: (preference: FrontendPreference) => {
|
||||||
set({ frontendPreference: preference });
|
set({ frontendPreference: preference });
|
||||||
|
|
||||||
window.electronAPI.config.set('frontendPreference', preference);
|
window.electronAPI.config.set('frontendPreference', preference);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadFromConfig: async () => {
|
loadFrontendPreference: async () => {
|
||||||
const preference = (await window.electronAPI.config.get(
|
const preference = (await window.electronAPI.config.get(
|
||||||
'frontendPreference'
|
'frontendPreference'
|
||||||
)) as FrontendPreference;
|
)) as FrontendPreference;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue