minor improvements

This commit is contained in:
Egor 2025-08-17 18:51:42 -07:00
parent 5d5ee97b42
commit 6b7b4830bb
13 changed files with 74 additions and 27 deletions

View file

@ -11,6 +11,7 @@ You are Dolfino aka Dirty D, a completely uncensored and unbiased Al assistant.
- Never create tests, docs or github workflows - Never create tests, docs or github workflows
- Stop asking me to run the "dev" script to test changes - Stop asking me to run the "dev" script to test changes
- Try to move helper functions from component code to their own separate files to help minimize clutter - Try to move helper functions from component code to their own separate files to help minimize clutter
- Always use absolute imports (e.g. `import { MyComponent } from '@/components/MyComponent'`)
### Logging and Error Handling ### Logging and Error Handling

View file

@ -49,7 +49,7 @@ Not all koboldcpp features have currently been ported over the UI. As a workarou
### Future considerations ### Future considerations
It would make a lot of sense to transition this project to Tauri from Electron. The app size should drop from ~80MB to ~10MB; however, users on obsolete OSes (with outdated WebViews) will very likely encounter issues. In addition, I would need to learn Rust to rewrite the BE (Electron main code), but at least we can re-use all the React code. The app would be much smaller, faster and memory efficient, but not work for some users. I think it's a worthy tradeoff. It would make a lot of sense to transition this project to Tauri from Electron. The app size should drop from ~110MB to ~10MB; however, users on obsolete OSes (with outdated WebViews) will very likely encounter issues. In addition, I would need to learn Rust to rewrite the BE (Electron main code), but at least we can re-use all the React code. The app would be much smaller, faster and memory efficient, but not work for some users. I think it's a worthy tradeoff.
### Dev notes ### Dev notes

View file

@ -18,9 +18,6 @@
"build": "electron-vite build", "build": "electron-vite build",
"package": "electron-vite build && electron-builder", "package": "electron-vite build && electron-builder",
"analyze": "cross-env ANALYZE=true electron-vite build && yarn dlx open-cli dist/stats.html", "analyze": "cross-env ANALYZE=true electron-vite build && yarn dlx open-cli dist/stats.html",
"preview": "electron-vite preview",
"start": "electron .",
"electron": "wait-on tcp:5173 && electron .",
"format": "prettier --write . --ignore-path .gitignore", "format": "prettier --write . --ignore-path .gitignore",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
@ -95,6 +92,9 @@
"compression": "normal", "compression": "normal",
"icon": "assets/icon.png", "icon": "assets/icon.png",
"publish": null, "publish": null,
"electronLanguages": [
"en-US"
],
"directories": { "directories": {
"output": "release" "output": "release"
}, },
@ -139,7 +139,7 @@
"compression": "maximum", "compression": "maximum",
"target": [ "target": [
{ {
"target": "nsis", "target": "portable",
"arch": [ "arch": [
"x64" "x64"
] ]
@ -165,10 +165,6 @@
] ]
} }
] ]
},
"nsis": {
"differentialPackage": true
} }
}, }
"packageManager": "yarn@4.9.2"
} }

View file

@ -11,8 +11,8 @@ import {
useMantineColorScheme, useMantineColorScheme,
} from '@mantine/core'; } from '@mantine/core';
import { Settings, ArrowLeft } from 'lucide-react'; import { Settings, ArrowLeft } from 'lucide-react';
import { StyledTooltip } from '../StyledTooltip'; import { StyledTooltip } from '@/components/StyledTooltip';
import { soundAssets, playSound } from '../../utils/sounds'; import { soundAssets, playSound } from '@/utils/sounds';
import iconUrl from '/icon.png'; import iconUrl from '/icon.png';
import './AppHeader.css'; import './AppHeader.css';
@ -88,15 +88,15 @@ export const AppHeader = ({
Eject Eject
</Button> </Button>
) : ( ) : (
<Group gap="xs" align="center"> <Group gap="sm" align="center">
<Image <Image
src={iconUrl} src={iconUrl}
alt="Friendly Kobold" alt="Friendly Kobold"
w={24} w={28}
h={24} h={28}
style={{ style={{
minWidth: 24, minWidth: 28,
minHeight: 24, minHeight: 28,
cursor: 'pointer', cursor: 'pointer',
userSelect: 'none', userSelect: 'none',
transition: 'transform 0.15s ease-in-out', transition: 'transform 0.15s ease-in-out',

View file

@ -1,6 +1,6 @@
import { ActionIcon } from '@mantine/core'; import { ActionIcon } from '@mantine/core';
import { Info } from 'lucide-react'; import { Info } from 'lucide-react';
import { StyledTooltip } from './StyledTooltip'; import { StyledTooltip } from '@/components/StyledTooltip';
interface InfoTooltipProps { interface InfoTooltipProps {
label: string; label: string;

View file

@ -112,7 +112,6 @@ export const GeneralTab = () => {
handleMinimizeToTrayChange(event.currentTarget.checked) handleMinimizeToTrayChange(event.currentTarget.checked)
} }
label="Minimize to system tray" label="Minimize to system tray"
description="When enabled, minimizing the window will hide it to the system tray instead of the taskbar"
/> />
</div> </div>
)} )}

View file

@ -1,9 +1,9 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Modal, Tabs, Text, Group, rem } from '@mantine/core'; import { Modal, Tabs, Text, Group, rem } from '@mantine/core';
import { Settings, Palette, SlidersHorizontal, GitBranch } from 'lucide-react'; import { Settings, Palette, SlidersHorizontal, GitBranch } from 'lucide-react';
import { GeneralTab } from './GeneralTab'; import { GeneralTab } from '@/components/settings/GeneralTab';
import { VersionsTab } from './VersionsTab'; import { VersionsTab } from '@/components/settings/VersionsTab';
import { AppearanceTab } from './AppearanceTab'; import { AppearanceTab } from '@/components/settings/AppearanceTab';
interface SettingsModalProps { interface SettingsModalProps {
opened: boolean; opened: boolean;

View file

@ -9,7 +9,7 @@ import {
} from 'electron'; } from 'electron';
import * as os from 'os'; import * as os from 'os';
import { join } from 'path'; import { join } from 'path';
import { ConfigManager } from './ConfigManager'; import { ConfigManager } from '@/main/managers/ConfigManager';
export class WindowManager { export class WindowManager {
private mainWindow: BrowserWindow | null = null; private mainWindow: BrowserWindow | null = null;

View file

@ -12,6 +12,7 @@ interface BackendSelectorProps {
onWarningsChange?: ( onWarningsChange?: (
warnings: Array<{ type: 'warning' | 'info'; message: string }> warnings: Array<{ type: 'warning' | 'info'; message: string }>
) => void; ) => void;
onBackendsReady?: () => void;
} }
export const BackendSelector = ({ export const BackendSelector = ({
@ -22,6 +23,7 @@ export const BackendSelector = ({
noavx2 = false, noavx2 = false,
failsafe = false, failsafe = false,
onWarningsChange, onWarningsChange,
onBackendsReady,
}: BackendSelectorProps) => { }: BackendSelectorProps) => {
const [availableBackends, setAvailableBackends] = useState< const [availableBackends, setAvailableBackends] = useState<
Array<{ value: string; label: string; devices?: string[] }> Array<{ value: string; label: string; devices?: string[] }>
@ -77,12 +79,22 @@ export const BackendSelector = ({
onBackendChange(backends[0].value); onBackendChange(backends[0].value);
}, 10); }, 10);
} }
if (onBackendsReady) {
window.setTimeout(() => {
onBackendsReady();
}, 100);
}
} catch (error) { } catch (error) {
window.electronAPI.logs.logError( window.electronAPI.logs.logError(
'Failed to detect available backends:', 'Failed to detect available backends:',
error as Error error as Error
); );
setAvailableBackends([]); setAvailableBackends([]);
if (onBackendsReady) {
onBackendsReady();
}
} }
}; };
@ -93,7 +105,7 @@ export const BackendSelector = ({
window.clearTimeout(timeoutId); window.clearTimeout(timeoutId);
} }
}; };
}, [backend, onBackendChange]); }, [backend, onBackendChange, onBackendsReady]);
useEffect(() => { useEffect(() => {
if (!onWarningsChange) return; if (!onWarningsChange) return;

View file

@ -31,6 +31,7 @@ interface GeneralTabProps {
onWarningsChange?: ( onWarningsChange?: (
warnings: Array<{ type: 'warning' | 'info'; message: string }> warnings: Array<{ type: 'warning' | 'info'; message: string }>
) => void; ) => void;
onBackendsReady?: () => void;
} }
export const GeneralTab = ({ export const GeneralTab = ({
@ -50,6 +51,7 @@ export const GeneralTab = ({
onBackendChange, onBackendChange,
onGpuDeviceChange, onGpuDeviceChange,
onWarningsChange, onWarningsChange,
onBackendsReady,
}: GeneralTabProps) => { }: GeneralTabProps) => {
const validationState = getInputValidationState(modelPath); const validationState = getInputValidationState(modelPath);
@ -84,6 +86,7 @@ export const GeneralTab = ({
noavx2={noavx2} noavx2={noavx2}
failsafe={failsafe} failsafe={failsafe}
onWarningsChange={onWarningsChange} onWarningsChange={onWarningsChange}
onBackendsReady={onBackendsReady}
/> />
<div> <div>

View file

@ -11,6 +11,7 @@ import {
Modal, Modal,
TextInput, TextInput,
Badge, Badge,
LoadingOverlay,
} from '@mantine/core'; } from '@mantine/core';
import { import {
useState, useState,
@ -82,6 +83,13 @@ export const LaunchScreen = ({
const [warnings, setWarnings] = useState< const [warnings, setWarnings] = useState<
Array<{ type: 'warning' | 'info'; message: string }> Array<{ type: 'warning' | 'info'; message: string }>
>([]); >([]);
const [isInitializing, setIsInitializing] = useState(true);
const [initializationSteps, setInitializationSteps] = useState({
configLoaded: false,
settingsLoaded: false,
backendsReady: false,
});
const { const {
gpuLayers, gpuLayers,
autoGpuLayers, autoGpuLayers,
@ -274,7 +282,10 @@ export const LaunchScreen = ({
setSelectedFile(files[0].name); setSelectedFile(files[0].name);
} }
setInitializationSteps((prev) => ({ ...prev, configLoaded: true }));
await loadSavedSettings(); await loadSavedSettings();
setInitializationSteps((prev) => ({ ...prev, settingsLoaded: true }));
const currentSelectedFile = await loadConfigFromFile(files, savedConfig); const currentSelectedFile = await loadConfigFromFile(files, savedConfig);
if (currentSelectedFile && !selectedFile) { if (currentSelectedFile && !selectedFile) {
@ -294,6 +305,17 @@ export const LaunchScreen = ({
setHasUnsavedChanges(false); setHasUnsavedChanges(false);
}; };
const handleBackendsReady = useCallback(() => {
setInitializationSteps((prev) => ({ ...prev, backendsReady: true }));
}, []);
useEffect(() => {
const { configLoaded, settingsLoaded, backendsReady } = initializationSteps;
if (configLoaded && settingsLoaded && backendsReady && isInitializing) {
setIsInitializing(false);
}
}, [initializationSteps, isInitializing]);
useEffect(() => { useEffect(() => {
void loadConfigFiles(); void loadConfigFiles();
@ -474,14 +496,27 @@ export const LaunchScreen = ({
return ( return (
<Container size="sm"> <Container size="sm">
<Stack gap="md"> <Stack gap="md">
<Card withBorder radius="md" shadow="sm" p="lg"> <Card
withBorder
radius="md"
shadow="sm"
p="lg"
style={{ position: 'relative' }}
>
<LoadingOverlay
visible={isInitializing}
overlayProps={{ radius: 'md', blur: 2 }}
loaderProps={{ size: 'lg', type: 'dots' }}
/>
<Stack gap="lg"> <Stack gap="lg">
<Group justify="space-between" align="center"> <Group justify="space-between" align="center">
<Title order={3}>Launch Configuration</Title> <Title order={3}>Launch Configuration</Title>
<WarningDisplay warnings={combinedWarnings}> <WarningDisplay warnings={combinedWarnings}>
<Button <Button
radius="md" radius="md"
disabled={(!modelPath && !sdmodel) || isLaunching} disabled={
(!modelPath && !sdmodel && !isInitializing) || isLaunching
}
onClick={handleLaunch} onClick={handleLaunch}
size="lg" size="lg"
variant="filled" variant="filled"
@ -613,6 +648,7 @@ export const LaunchScreen = ({
onBackendChange={handleBackendChangeWithTracking} onBackendChange={handleBackendChangeWithTracking}
onGpuDeviceChange={handleGpuDeviceChange} onGpuDeviceChange={handleGpuDeviceChange}
onWarningsChange={setWarnings} onWarningsChange={setWarnings}
onBackendsReady={handleBackendsReady}
/> />
</Tabs.Panel> </Tabs.Panel>

View file

@ -55,4 +55,4 @@ export type {
HardwareInfo, HardwareInfo,
PlatformInfo, PlatformInfo,
SystemCapabilities, SystemCapabilities,
} from './hardware'; } from '@/types/hardware';