mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
503 lines
16 KiB
TypeScript
503 lines
16 KiB
TypeScript
import { Card, Container, Stack, Tabs, Group, Button } from '@mantine/core';
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { useLaunchConfig } from '@/hooks/useLaunchConfig';
|
|
import { useTrackedConfigHandlers } from '@/hooks/useTrackedConfigHandlers';
|
|
import { useLaunchLogic } from '@/hooks/useLaunchLogic';
|
|
import { useWarnings } from '@/hooks/useWarnings';
|
|
import { GeneralTab } from '@/components/screens/Launch/GeneralTab';
|
|
import { AdvancedTab } from '@/components/screens/Launch/AdvancedTab';
|
|
import { NetworkTab } from '@/components/screens/Launch/NetworkTab';
|
|
import { ImageGenerationTab } from '@/components/screens/Launch/ImageGenerationTab';
|
|
import { WarningDisplay } from '@/components/WarningDisplay';
|
|
import { ConfigFileManager } from '@/components/ConfigFileManager';
|
|
import type { ConfigFile } from '@/types';
|
|
|
|
interface LaunchScreenProps {
|
|
onLaunch: () => void;
|
|
onLaunchModeChange?: (isImageMode: boolean) => void;
|
|
}
|
|
|
|
export const LaunchScreen = ({
|
|
onLaunch,
|
|
onLaunchModeChange,
|
|
}: LaunchScreenProps) => {
|
|
const [configFiles, setConfigFiles] = useState<ConfigFile[]>([]);
|
|
const [selectedFile, setSelectedFile] = useState<string | null>(null);
|
|
const [, setInstallDir] = useState<string>('');
|
|
const [activeTab, setActiveTab] = useState<string | null>('general');
|
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
const [warnings, setWarnings] = useState<
|
|
Array<{ type: 'warning' | 'info'; message: string }>
|
|
>([]);
|
|
|
|
const {
|
|
gpuLayers,
|
|
autoGpuLayers,
|
|
contextSize,
|
|
modelPath,
|
|
additionalArguments,
|
|
port,
|
|
host,
|
|
multiuser,
|
|
multiplayer,
|
|
remotetunnel,
|
|
nocertify,
|
|
websearch,
|
|
noshift,
|
|
flashattention,
|
|
noavx2,
|
|
failsafe,
|
|
lowvram,
|
|
quantmatmul,
|
|
backend,
|
|
gpuDevice,
|
|
sdmodel,
|
|
sdt5xxl,
|
|
sdclipl,
|
|
sdclipg,
|
|
sdphotomaker,
|
|
sdvae,
|
|
sdlora,
|
|
parseAndApplyConfigFile,
|
|
loadSavedSettings,
|
|
loadConfigFromFile,
|
|
handleSelectModelFile,
|
|
handleGpuDeviceChange,
|
|
handleSelectSdmodelFile,
|
|
handleSelectSdt5xxlFile,
|
|
handleSelectSdcliplFile,
|
|
handleSelectSdclipgFile,
|
|
handleSelectSdphotomakerFile,
|
|
handleSelectSdvaeFile,
|
|
handleSelectSdloraFile,
|
|
handleApplyPreset,
|
|
handleModelPathChange,
|
|
handleGpuLayersChange,
|
|
handleAutoGpuLayersChange,
|
|
handleContextSizeChangeWithStep,
|
|
handleAdditionalArgumentsChange,
|
|
handlePortChange,
|
|
handleHostChange,
|
|
handleMultiuserChange,
|
|
handleMultiplayerChange,
|
|
handleRemotetunnelChange,
|
|
handleNocertifyChange,
|
|
handleWebsearchChange,
|
|
handleNoshiftChange,
|
|
handleFlashattentionChange,
|
|
handleNoavx2Change,
|
|
handleFailsafeChange,
|
|
handleLowvramChange,
|
|
handleQuantmatmulChange,
|
|
handleBackendChange,
|
|
handleSdmodelChange,
|
|
handleSdt5xxlChange,
|
|
handleSdcliplChange,
|
|
handleSdclipgChange,
|
|
handleSdphotomakerChange,
|
|
handleSdvaeChange,
|
|
handleSdloraChange,
|
|
} = useLaunchConfig();
|
|
|
|
const trackedHandlers = useTrackedConfigHandlers({
|
|
setHasUnsavedChanges,
|
|
handlers: {
|
|
handleModelPathChange,
|
|
handleGpuLayersChange,
|
|
handleAutoGpuLayersChange,
|
|
handleContextSizeChangeWithStep,
|
|
handleAdditionalArgumentsChange,
|
|
handlePortChange,
|
|
handleHostChange,
|
|
handleMultiuserChange,
|
|
handleMultiplayerChange,
|
|
handleRemotetunnelChange,
|
|
handleNocertifyChange,
|
|
handleWebsearchChange,
|
|
handleNoshiftChange,
|
|
handleFlashattentionChange,
|
|
handleNoavx2Change,
|
|
handleFailsafeChange,
|
|
handleLowvramChange,
|
|
handleQuantmatmulChange,
|
|
handleBackendChange,
|
|
handleSdmodelChange,
|
|
handleSdt5xxlChange,
|
|
handleSdcliplChange,
|
|
handleSdclipgChange,
|
|
handleSdphotomakerChange,
|
|
handleSdvaeChange,
|
|
handleSdloraChange,
|
|
},
|
|
});
|
|
|
|
const { isLaunching, handleLaunch } = useLaunchLogic({
|
|
configFiles,
|
|
selectedFile,
|
|
modelPath,
|
|
sdmodel,
|
|
onLaunch,
|
|
onLaunchModeChange,
|
|
});
|
|
|
|
const combinedWarnings = useWarnings({ modelPath, sdmodel, warnings });
|
|
|
|
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);
|
|
}
|
|
|
|
await loadSavedSettings();
|
|
|
|
const currentSelectedFile = await loadConfigFromFile(files, savedConfig);
|
|
if (currentSelectedFile && !selectedFile) {
|
|
setSelectedFile(currentSelectedFile);
|
|
}
|
|
}, [selectedFile, loadSavedSettings, 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);
|
|
}
|
|
|
|
setHasUnsavedChanges(false);
|
|
};
|
|
|
|
const buildConfigData = () => ({
|
|
gpulayers: gpuLayers,
|
|
contextsize: contextSize,
|
|
model_param: modelPath,
|
|
port,
|
|
host,
|
|
multiuser: multiuser ? 1 : 0,
|
|
multiplayer,
|
|
remotetunnel,
|
|
nocertify,
|
|
websearch,
|
|
noshift,
|
|
flashattention,
|
|
noavx2,
|
|
failsafe,
|
|
usecuda: backend === 'cuda' || backend === 'rocm',
|
|
usevulkan: backend === 'vulkan',
|
|
useclblast: backend === 'clblast',
|
|
sdmodel,
|
|
sdt5xxl,
|
|
sdclipl,
|
|
sdclipg,
|
|
sdphotomaker,
|
|
sdvae,
|
|
});
|
|
|
|
const handleCreateNewConfig = async (configName: string) => {
|
|
try {
|
|
const success = await window.electronAPI.kobold.saveConfigFile(
|
|
configName,
|
|
buildConfigData()
|
|
);
|
|
|
|
if (success) {
|
|
await loadConfigFiles();
|
|
const newFileName = `${configName}.kcpps`;
|
|
setSelectedFile(newFileName);
|
|
await window.electronAPI.kobold.setSelectedConfig(newFileName);
|
|
setHasUnsavedChanges(false);
|
|
} else {
|
|
window.electronAPI.logs.logError(
|
|
'Failed to create new configuration',
|
|
new Error('Save operation failed')
|
|
);
|
|
}
|
|
} catch (error) {
|
|
window.electronAPI.logs.logError(
|
|
'Failed to create new configuration:',
|
|
error as Error
|
|
);
|
|
}
|
|
};
|
|
|
|
const handleSaveConfig = async () => {
|
|
if (!selectedFile) {
|
|
window.electronAPI.logs.logError(
|
|
'No configuration file selected for saving',
|
|
new Error('Selected file is null')
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const configName = selectedFile.replace('.kcpps', '');
|
|
|
|
const success = await window.electronAPI.kobold.saveConfigFile(
|
|
configName,
|
|
buildConfigData()
|
|
);
|
|
|
|
if (success) {
|
|
setHasUnsavedChanges(false);
|
|
} else {
|
|
window.electronAPI.logs.logError(
|
|
'Failed to save configuration',
|
|
new Error('Save operation failed')
|
|
);
|
|
}
|
|
} catch (error) {
|
|
window.electronAPI.logs.logError(
|
|
'Failed to save configuration:',
|
|
error as Error
|
|
);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
void loadConfigFiles();
|
|
|
|
const handleInstallDirChange = () => {
|
|
void loadConfigFiles();
|
|
};
|
|
|
|
const cleanup = window.electronAPI.kobold.onInstallDirChanged(
|
|
handleInstallDirChange
|
|
);
|
|
|
|
return cleanup;
|
|
}, [loadConfigFiles]);
|
|
|
|
const handleLaunchClick = () => {
|
|
handleLaunch({
|
|
autoGpuLayers,
|
|
gpuLayers,
|
|
contextSize,
|
|
port: port ?? 5001,
|
|
host,
|
|
multiuser,
|
|
multiplayer,
|
|
remotetunnel,
|
|
nocertify,
|
|
websearch,
|
|
noshift,
|
|
flashattention,
|
|
backend,
|
|
lowvram,
|
|
gpuDevice,
|
|
quantmatmul,
|
|
additionalArguments,
|
|
sdt5xxl,
|
|
sdclipl,
|
|
sdclipg,
|
|
sdphotomaker,
|
|
sdvae,
|
|
sdlora,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Container size="sm">
|
|
<Stack gap="md">
|
|
<Card
|
|
withBorder
|
|
radius="md"
|
|
shadow="sm"
|
|
p="lg"
|
|
style={{ position: 'relative' }}
|
|
>
|
|
<Stack gap="lg">
|
|
<ConfigFileManager
|
|
configFiles={configFiles}
|
|
selectedFile={selectedFile}
|
|
hasUnsavedChanges={hasUnsavedChanges}
|
|
onFileSelection={handleFileSelection}
|
|
onCreateNewConfig={handleCreateNewConfig}
|
|
onSaveConfig={handleSaveConfig}
|
|
onLoadConfigFiles={loadConfigFiles}
|
|
/>
|
|
|
|
<Tabs value={activeTab} onChange={setActiveTab}>
|
|
<Tabs.List>
|
|
<Tabs.Tab value="general">General</Tabs.Tab>
|
|
<Tabs.Tab value="image">Image Generation</Tabs.Tab>
|
|
<Tabs.Tab value="network">Network</Tabs.Tab>
|
|
<Tabs.Tab value="advanced">Advanced</Tabs.Tab>
|
|
</Tabs.List>
|
|
|
|
<div>
|
|
<Tabs.Panel value="general" pt="md">
|
|
<GeneralTab
|
|
modelPath={modelPath}
|
|
gpuLayers={gpuLayers}
|
|
autoGpuLayers={autoGpuLayers}
|
|
contextSize={contextSize}
|
|
backend={backend}
|
|
gpuDevice={gpuDevice}
|
|
noavx2={noavx2}
|
|
failsafe={failsafe}
|
|
onModelPathChange={
|
|
trackedHandlers.handleModelPathChangeWithTracking
|
|
}
|
|
onSelectModelFile={handleSelectModelFile}
|
|
onGpuLayersChange={
|
|
trackedHandlers.handleGpuLayersChangeWithTracking
|
|
}
|
|
onAutoGpuLayersChange={
|
|
trackedHandlers.handleAutoGpuLayersChangeWithTracking
|
|
}
|
|
onContextSizeChange={
|
|
trackedHandlers.handleContextSizeChangeWithTracking
|
|
}
|
|
onBackendChange={
|
|
trackedHandlers.handleBackendChangeWithTracking
|
|
}
|
|
onGpuDeviceChange={handleGpuDeviceChange}
|
|
onWarningsChange={setWarnings}
|
|
/>
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="advanced" pt="md">
|
|
<AdvancedTab
|
|
additionalArguments={additionalArguments}
|
|
noshift={noshift}
|
|
flashattention={flashattention}
|
|
noavx2={noavx2}
|
|
failsafe={failsafe}
|
|
lowvram={lowvram}
|
|
quantmatmul={quantmatmul}
|
|
backend={backend}
|
|
onAdditionalArgumentsChange={
|
|
trackedHandlers.handleAdditionalArgumentsChangeWithTracking
|
|
}
|
|
onNoshiftChange={
|
|
trackedHandlers.handleNoshiftChangeWithTracking
|
|
}
|
|
onFlashattentionChange={
|
|
trackedHandlers.handleFlashattentionChangeWithTracking
|
|
}
|
|
onNoavx2Change={
|
|
trackedHandlers.handleNoavx2ChangeWithTracking
|
|
}
|
|
onFailsafeChange={
|
|
trackedHandlers.handleFailsafeChangeWithTracking
|
|
}
|
|
onLowvramChange={
|
|
trackedHandlers.handleLowvramChangeWithTracking
|
|
}
|
|
onQuantmatmulChange={
|
|
trackedHandlers.handleQuantmatmulChangeWithTracking
|
|
}
|
|
/>
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="network" pt="md">
|
|
<NetworkTab
|
|
port={port}
|
|
host={host}
|
|
multiuser={multiuser}
|
|
multiplayer={multiplayer}
|
|
remotetunnel={remotetunnel}
|
|
nocertify={nocertify}
|
|
websearch={websearch}
|
|
onPortChange={trackedHandlers.handlePortChangeWithTracking}
|
|
onHostChange={trackedHandlers.handleHostChangeWithTracking}
|
|
onMultiuserChange={
|
|
trackedHandlers.handleMultiuserChangeWithTracking
|
|
}
|
|
onMultiplayerChange={
|
|
trackedHandlers.handleMultiplayerChangeWithTracking
|
|
}
|
|
onRemotetunnelChange={
|
|
trackedHandlers.handleRemotetunnelChangeWithTracking
|
|
}
|
|
onNocertifyChange={
|
|
trackedHandlers.handleNocertifyChangeWithTracking
|
|
}
|
|
onWebsearchChange={
|
|
trackedHandlers.handleWebsearchChangeWithTracking
|
|
}
|
|
/>
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel value="image" pt="md">
|
|
<ImageGenerationTab
|
|
sdmodel={sdmodel}
|
|
sdt5xxl={sdt5xxl}
|
|
sdclipl={sdclipl}
|
|
sdclipg={sdclipg}
|
|
sdphotomaker={sdphotomaker}
|
|
sdvae={sdvae}
|
|
sdlora={sdlora}
|
|
onSdmodelChange={
|
|
trackedHandlers.handleSdmodelChangeWithTracking
|
|
}
|
|
onSelectSdmodelFile={handleSelectSdmodelFile}
|
|
onSdt5xxlChange={
|
|
trackedHandlers.handleSdt5xxlChangeWithTracking
|
|
}
|
|
onSelectSdt5xxlFile={handleSelectSdt5xxlFile}
|
|
onSdcliplChange={
|
|
trackedHandlers.handleSdcliplChangeWithTracking
|
|
}
|
|
onSelectSdcliplFile={handleSelectSdcliplFile}
|
|
onSdclipgChange={
|
|
trackedHandlers.handleSdclipgChangeWithTracking
|
|
}
|
|
onSelectSdclipgFile={handleSelectSdclipgFile}
|
|
onSdphotomakerChange={
|
|
trackedHandlers.handleSdphotomakerChangeWithTracking
|
|
}
|
|
onSelectSdphotomakerFile={handleSelectSdphotomakerFile}
|
|
onSdvaeChange={
|
|
trackedHandlers.handleSdvaeChangeWithTracking
|
|
}
|
|
onSelectSdvaeFile={handleSelectSdvaeFile}
|
|
onSdloraChange={
|
|
trackedHandlers.handleSdloraChangeWithTracking
|
|
}
|
|
onSelectSdloraFile={handleSelectSdloraFile}
|
|
onApplyPreset={handleApplyPreset}
|
|
/>
|
|
</Tabs.Panel>
|
|
</div>
|
|
</Tabs>
|
|
|
|
<Group justify="flex-end" pt="md">
|
|
<WarningDisplay warnings={combinedWarnings}>
|
|
<Button
|
|
radius="md"
|
|
disabled={(!modelPath && !sdmodel) || isLaunching}
|
|
onClick={handleLaunchClick}
|
|
size="lg"
|
|
variant="filled"
|
|
color="blue"
|
|
style={{
|
|
fontWeight: 600,
|
|
fontSize: '16px',
|
|
padding: '12px 28px',
|
|
minWidth: '120px',
|
|
textTransform: 'uppercase',
|
|
letterSpacing: '0.5px',
|
|
}}
|
|
>
|
|
Launch
|
|
</Button>
|
|
</WarningDisplay>
|
|
</Group>
|
|
</Stack>
|
|
</Card>
|
|
</Stack>
|
|
</Container>
|
|
);
|
|
};
|