mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-04 12:13:28 -07:00
447 lines
12 KiB
TypeScript
447 lines
12 KiB
TypeScript
import { Button, Card, Container, Group, Stack, Tabs } from '@mantine/core';
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
import { AdvancedTab } from '@/components/screens/Launch/AdvancedTab';
|
|
import { ConfigFileManager } from '@/components/screens/Launch/ConfigFileManager';
|
|
import { GeneralTab } from '@/components/screens/Launch/GeneralTab/index';
|
|
import { ImageGenerationTab } from '@/components/screens/Launch/ImageGenerationTab';
|
|
import { NetworkTab } from '@/components/screens/Launch/NetworkTab';
|
|
import { PerformanceTab } from '@/components/screens/Launch/PerformanceTab';
|
|
import { WarningDisplay } from '@/components/WarningDisplay';
|
|
import { DEFAULT_MODEL_URL } from '@/constants';
|
|
import { useLaunchLogic } from '@/hooks/useLaunchLogic';
|
|
import { useWarnings } from '@/hooks/useWarnings';
|
|
import { useLaunchConfigStore } from '@/stores/launchConfig';
|
|
import type { Acceleration, ConfigFile } from '@/types';
|
|
import { logError } from '@/utils/logger';
|
|
|
|
interface LaunchScreenProps {
|
|
onLaunch: () => void;
|
|
}
|
|
|
|
export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
|
|
const [configFiles, setConfigFiles] = useState<ConfigFile[]>([]);
|
|
const [selectedFile, setSelectedFile] = useState<string | null>(null);
|
|
const [, setInstallDir] = useState('');
|
|
const [activeTab, setActiveTab] = useState<string | null>('general');
|
|
const [configLoaded, setConfigLoaded] = useState(false);
|
|
const defaultsSetRef = useRef(false);
|
|
|
|
const {
|
|
gpuLayers,
|
|
autoGpuLayers,
|
|
contextSize,
|
|
model,
|
|
additionalArguments,
|
|
preLaunchCommands,
|
|
port,
|
|
host,
|
|
multiuser,
|
|
multiplayer,
|
|
remotetunnel,
|
|
nocertify,
|
|
websearch,
|
|
noshift,
|
|
flashattention,
|
|
noavx2,
|
|
failsafe,
|
|
lowvram,
|
|
quantmatmul,
|
|
usemmap,
|
|
debugmode,
|
|
acceleration,
|
|
gpuDeviceSelection,
|
|
gpuPlatform,
|
|
tensorSplit,
|
|
sdmodel,
|
|
sdt5xxl,
|
|
sdclipl,
|
|
sdclipg,
|
|
sdphotomaker,
|
|
sdvae,
|
|
sdlora,
|
|
sdconvdirect,
|
|
sdvaecpu,
|
|
sdclipgpu,
|
|
moecpu,
|
|
moeexperts,
|
|
smartcache,
|
|
pipelineparallel,
|
|
quantkv,
|
|
jinja,
|
|
jinjatools,
|
|
jinjakwargs,
|
|
parseAndApplyConfigFile,
|
|
loadConfigFromFile,
|
|
setModel,
|
|
setAcceleration,
|
|
} = useLaunchConfigStore();
|
|
|
|
const { isLaunching, handleLaunch } = useLaunchLogic({
|
|
model,
|
|
onLaunch,
|
|
sdmodel,
|
|
});
|
|
|
|
const { warnings: combinedWarnings } = useWarnings({
|
|
acceleration,
|
|
configLoaded,
|
|
model,
|
|
sdmodel,
|
|
});
|
|
|
|
const setHappyDefaults = useCallback(async () => {
|
|
const accelerations = await window.electronAPI.kobold.getAvailableAccelerations();
|
|
|
|
if (!acceleration && accelerations && accelerations.length > 0) {
|
|
setAcceleration(accelerations[0].value as Acceleration);
|
|
}
|
|
}, [acceleration, setAcceleration]);
|
|
|
|
const setInitialDefaults = useCallback(
|
|
(currentModel: string, currentSdModel: string) => {
|
|
if (!defaultsSetRef.current && !currentModel.trim() && !currentSdModel.trim()) {
|
|
setModel(DEFAULT_MODEL_URL);
|
|
defaultsSetRef.current = true;
|
|
}
|
|
},
|
|
[setModel],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (configLoaded && !defaultsSetRef.current) {
|
|
void setHappyDefaults();
|
|
if (!model.trim() && !sdmodel.trim()) {
|
|
setInitialDefaults(model, sdmodel);
|
|
}
|
|
}
|
|
}, [configLoaded, setHappyDefaults, model, sdmodel, setInitialDefaults]);
|
|
|
|
const loadConfigFiles = useCallback(async () => {
|
|
const [files, currentDir, savedConfig] = await Promise.all([
|
|
window.electronAPI.kobold.getConfigFiles(),
|
|
window.electronAPI.kobold.getCurrentInstallDir(),
|
|
window.electronAPI.kobold.getSelectedConfig(),
|
|
]);
|
|
|
|
setConfigFiles(files);
|
|
setInstallDir(currentDir);
|
|
|
|
if (savedConfig && files.some((f) => f.name === savedConfig)) {
|
|
setSelectedFile(savedConfig);
|
|
} else if (files.length > 0 && !selectedFile) {
|
|
setSelectedFile(files[0].name);
|
|
}
|
|
|
|
const loadedConfigFileName = await loadConfigFromFile(files, savedConfig);
|
|
if (loadedConfigFileName && !selectedFile) {
|
|
setSelectedFile(loadedConfigFileName);
|
|
}
|
|
|
|
setConfigLoaded(true);
|
|
}, [selectedFile, loadConfigFromFile]);
|
|
|
|
const handleFileSelection = async (fileName: string) => {
|
|
setSelectedFile(fileName);
|
|
await window.electronAPI.kobold.setSelectedConfig(fileName);
|
|
|
|
const selectedConfig = configFiles.find((f) => f.name === fileName);
|
|
if (selectedConfig) {
|
|
await parseAndApplyConfigFile(selectedConfig.path);
|
|
}
|
|
};
|
|
|
|
const buildConfigData = () => ({
|
|
additionalArguments,
|
|
autoGpuLayers,
|
|
contextsize: contextSize,
|
|
debugmode,
|
|
failsafe,
|
|
flashattention,
|
|
gpuDeviceSelection,
|
|
gpulayers: gpuLayers,
|
|
host,
|
|
model,
|
|
moecpu,
|
|
moeexperts,
|
|
multiplayer,
|
|
multiuser: multiuser ? 1 : 0,
|
|
noavx2,
|
|
nocertify,
|
|
noshift,
|
|
pipelineparallel,
|
|
port,
|
|
preLaunchCommands: preLaunchCommands.filter((cmd) => cmd.trim() !== ''),
|
|
remotetunnel,
|
|
sdclipg,
|
|
sdclipgpu,
|
|
sdclipl,
|
|
sdconvdirect,
|
|
sdlora,
|
|
sdmodel,
|
|
sdphotomaker,
|
|
sdt5xxl,
|
|
sdvae,
|
|
sdvaecpu,
|
|
smartcache,
|
|
tensorSplit,
|
|
usecuda: acceleration === 'cuda' || acceleration === 'rocm',
|
|
usemmap,
|
|
usevulkan: acceleration === 'vulkan',
|
|
websearch,
|
|
quantkv,
|
|
jinja,
|
|
jinjatools,
|
|
jinjakwargs,
|
|
});
|
|
|
|
const handleCreateNewConfig = async (configName: string) => {
|
|
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'));
|
|
}
|
|
};
|
|
|
|
const handleSaveConfig = async () => {
|
|
if (!selectedFile) {
|
|
logError('No configuration file selected for saving', new Error('Selected file is null'));
|
|
return false;
|
|
}
|
|
|
|
const saveSuccess = await window.electronAPI.kobold.saveConfigFile(
|
|
selectedFile,
|
|
buildConfigData(),
|
|
);
|
|
|
|
if (!saveSuccess) {
|
|
logError('Failed to save configuration', new Error('Save operation failed'));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const handleDeleteConfig = async (fileName: string) => {
|
|
const deleteSuccess = await window.electronAPI.kobold.deleteConfigFile(fileName);
|
|
|
|
if (deleteSuccess) {
|
|
await loadConfigFiles();
|
|
|
|
const updatedFiles = await window.electronAPI.kobold.getConfigFiles();
|
|
if (updatedFiles.length > 0) {
|
|
const firstConfig = updatedFiles[0].name;
|
|
setSelectedFile(firstConfig);
|
|
await window.electronAPI.kobold.setSelectedConfig(firstConfig);
|
|
|
|
const selectedConfig = updatedFiles.find((f) => f.name === firstConfig);
|
|
if (selectedConfig) {
|
|
await parseAndApplyConfigFile(selectedConfig.path);
|
|
}
|
|
} else {
|
|
setSelectedFile(null);
|
|
await window.electronAPI.kobold.setSelectedConfig('');
|
|
}
|
|
}
|
|
|
|
return deleteSuccess;
|
|
};
|
|
|
|
useEffect(() => {
|
|
void loadConfigFiles();
|
|
|
|
const handleInstallDirChange = () => {
|
|
void loadConfigFiles();
|
|
};
|
|
|
|
const cleanup = window.electronAPI.kobold.onInstallDirChanged(handleInstallDirChange);
|
|
|
|
return cleanup;
|
|
}, [loadConfigFiles]);
|
|
|
|
const handleLaunchClick = useCallback(() => {
|
|
void handleLaunch({
|
|
acceleration,
|
|
additionalArguments,
|
|
autoGpuLayers,
|
|
contextSize,
|
|
debugmode,
|
|
failsafe,
|
|
flashattention,
|
|
gpuDeviceSelection,
|
|
gpuLayers,
|
|
gpuPlatform,
|
|
host,
|
|
lowvram,
|
|
moecpu,
|
|
moeexperts,
|
|
multiplayer,
|
|
multiuser,
|
|
noavx2,
|
|
nocertify,
|
|
noshift,
|
|
pipelineparallel,
|
|
port: port ?? 5001,
|
|
preLaunchCommands,
|
|
quantmatmul,
|
|
remotetunnel,
|
|
sdclipg,
|
|
sdclipgpu,
|
|
sdclipl,
|
|
sdconvdirect,
|
|
sdlora,
|
|
sdphotomaker,
|
|
sdt5xxl,
|
|
sdvae,
|
|
sdvaecpu,
|
|
smartcache,
|
|
tensorSplit,
|
|
usemmap,
|
|
websearch,
|
|
quantkv,
|
|
jinja,
|
|
jinjatools,
|
|
jinjakwargs,
|
|
});
|
|
}, [
|
|
handleLaunch,
|
|
autoGpuLayers,
|
|
gpuLayers,
|
|
contextSize,
|
|
port,
|
|
host,
|
|
multiuser,
|
|
multiplayer,
|
|
remotetunnel,
|
|
nocertify,
|
|
websearch,
|
|
noshift,
|
|
flashattention,
|
|
noavx2,
|
|
failsafe,
|
|
acceleration,
|
|
lowvram,
|
|
gpuDeviceSelection,
|
|
gpuPlatform,
|
|
tensorSplit,
|
|
quantmatmul,
|
|
usemmap,
|
|
debugmode,
|
|
additionalArguments,
|
|
preLaunchCommands,
|
|
sdt5xxl,
|
|
sdclipl,
|
|
sdclipg,
|
|
sdphotomaker,
|
|
sdvae,
|
|
sdlora,
|
|
sdconvdirect,
|
|
sdvaecpu,
|
|
sdclipgpu,
|
|
moecpu,
|
|
moeexperts,
|
|
smartcache,
|
|
pipelineparallel,
|
|
quantkv,
|
|
jinja,
|
|
jinjatools,
|
|
jinjakwargs,
|
|
]);
|
|
|
|
return (
|
|
<Container size="sm" mt="md">
|
|
<Stack gap="md">
|
|
<Card withBorder radius="md" shadow="sm" p="lg" style={{ position: 'relative' }}>
|
|
<Stack gap="lg">
|
|
<ConfigFileManager
|
|
configFiles={configFiles}
|
|
selectedFile={selectedFile}
|
|
onFileSelection={handleFileSelection}
|
|
onCreateNewConfig={handleCreateNewConfig}
|
|
onSaveConfig={handleSaveConfig}
|
|
onDeleteConfig={handleDeleteConfig}
|
|
onLoadConfigFiles={loadConfigFiles}
|
|
/>
|
|
|
|
<Tabs
|
|
value={activeTab}
|
|
onChange={setActiveTab}
|
|
styles={{
|
|
panel: {
|
|
flex: 1,
|
|
overflow: 'auto',
|
|
paddingTop: '1rem',
|
|
paddingRight: '0.5rem',
|
|
},
|
|
root: {
|
|
maxHeight: '51vh',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
},
|
|
}}
|
|
>
|
|
<Tabs.List>
|
|
<Tabs.Tab value="general">General</Tabs.Tab>
|
|
<Tabs.Tab value="image">Image Generation</Tabs.Tab>
|
|
<Tabs.Tab value="performance">Performance</Tabs.Tab>
|
|
<Tabs.Tab value="network">Network</Tabs.Tab>
|
|
<Tabs.Tab value="advanced">Advanced</Tabs.Tab>
|
|
</Tabs.List>
|
|
|
|
<Tabs.Panel value="general">
|
|
<GeneralTab configLoaded={configLoaded} />
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="image">
|
|
<ImageGenerationTab />
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="performance">
|
|
<PerformanceTab />
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="network">
|
|
<NetworkTab />
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="advanced">
|
|
<AdvancedTab />
|
|
</Tabs.Panel>
|
|
</Tabs>
|
|
|
|
<Group justify="flex-end">
|
|
<WarningDisplay warnings={combinedWarnings}>
|
|
<Button
|
|
radius="md"
|
|
disabled={(!model && !sdmodel) || isLaunching}
|
|
onClick={handleLaunchClick}
|
|
size="lg"
|
|
variant="filled"
|
|
color="blue"
|
|
style={{
|
|
fontSize: '1em',
|
|
fontWeight: 600,
|
|
letterSpacing: '0.03125rem',
|
|
minWidth: '7.5rem',
|
|
padding: '0.75rem 1.75rem',
|
|
textTransform: 'uppercase',
|
|
}}
|
|
>
|
|
Launch
|
|
</Button>
|
|
</WarningDisplay>
|
|
</Group>
|
|
</Stack>
|
|
</Card>
|
|
</Stack>
|
|
</Container>
|
|
);
|
|
};
|