minor improvements

This commit is contained in:
lone-cloud 2025-09-03 20:40:38 -07:00
parent b0c6087e41
commit 1ff27a893b
14 changed files with 107 additions and 78 deletions

View file

@ -12,7 +12,6 @@ import { TitleBar } from '@/components/TitleBar';
import { ErrorBoundary } from '@/components/ErrorBoundary'; import { ErrorBoundary } from '@/components/ErrorBoundary';
import { useUpdateChecker } from '@/hooks/useUpdateChecker'; import { useUpdateChecker } from '@/hooks/useUpdateChecker';
import { useKoboldVersions } from '@/hooks/useKoboldVersions'; import { useKoboldVersions } from '@/hooks/useKoboldVersions';
import { useModalStore } from '@/stores/modal';
import { safeExecute } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
import { TITLEBAR_HEIGHT } from '@/constants'; import { TITLEBAR_HEIGHT } from '@/constants';
import type { DownloadItem } from '@/types/electron'; import type { DownloadItem } from '@/types/electron';
@ -25,8 +24,8 @@ export const App = () => {
useState<InterfaceTab>('terminal'); useState<InterfaceTab>('terminal');
const [frontendPreference, setFrontendPreference] = const [frontendPreference, setFrontendPreference] =
useState<FrontendPreference>('koboldcpp'); useState<FrontendPreference>('koboldcpp');
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
const { modals, setModalOpen } = useModalStore(); const [ejectConfirmModalOpen, setEjectConfirmModalOpen] = useState(false);
const { const {
updateInfo: binaryUpdateInfo, updateInfo: binaryUpdateInfo,
@ -121,7 +120,7 @@ export const App = () => {
if (skipEjectConfirmation) { if (skipEjectConfirmation) {
performEject(); performEject();
} else { } else {
setModalOpen('ejectConfirm', true); setEjectConfirmModalOpen(true);
} }
}; };
@ -158,7 +157,7 @@ export const App = () => {
currentTab={activeInterfaceTab} currentTab={activeInterfaceTab}
onTabChange={setActiveInterfaceTab} onTabChange={setActiveInterfaceTab}
onEject={handleEject} onEject={handleEject}
onOpenSettings={() => setModalOpen('settings', true)} onOpenSettings={() => setSettingsModalOpen(true)}
frontendPreference={frontendPreference} frontendPreference={frontendPreference}
/> />
@ -225,9 +224,9 @@ export const App = () => {
/> />
</AppShell.Main> </AppShell.Main>
<SettingsModal <SettingsModal
opened={modals.settings} opened={settingsModalOpen}
onClose={async () => { onClose={async () => {
setModalOpen('settings', false); setSettingsModalOpen(false);
const preference = await safeExecute( const preference = await safeExecute(
() => () =>
window.electronAPI.config.get( window.electronAPI.config.get(
@ -240,8 +239,8 @@ export const App = () => {
currentScreen={currentScreen || undefined} currentScreen={currentScreen || undefined}
/> />
<EjectConfirmModal <EjectConfirmModal
opened={modals.ejectConfirm} opened={ejectConfirmModalOpen}
onClose={() => setModalOpen('ejectConfirm', false)} onClose={() => setEjectConfirmModalOpen(false)}
onConfirm={handleEjectConfirm} onConfirm={handleEjectConfirm}
/> />
</AppShell> </AppShell>

View file

@ -1,3 +1,4 @@
import { MODAL_STYLES_WITH_TITLEBAR } from '@/constants';
import { import {
Modal, Modal,
Text, Text,
@ -549,7 +550,7 @@ export const CommandLineArgumentsModal = ({
> >
<Group gap="xs" wrap="wrap" justify="space-between"> <Group gap="xs" wrap="wrap" justify="space-between">
<Group gap="xs" wrap="wrap" style={{ flex: 1 }}> <Group gap="xs" wrap="wrap" style={{ flex: 1 }}>
<Code style={{ fontSize: '0.875rem', fontWeight: 600 }}> <Code style={{ fontSize: '0.875em', fontWeight: 600 }}>
{arg.flag} {arg.flag}
</Code> </Code>
{arg.aliases && {arg.aliases &&
@ -609,6 +610,7 @@ export const CommandLineArgumentsModal = ({
title="Available Command Line Arguments" title="Available Command Line Arguments"
size="xl" size="xl"
centered centered
styles={MODAL_STYLES_WITH_TITLEBAR}
> >
<Stack gap="md"> <Stack gap="md">
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">

View file

@ -1,5 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Modal, Text, Group, Button, Checkbox, Stack } from '@mantine/core'; import { Modal, Text, Group, Button, Checkbox, Stack } from '@mantine/core';
import { MODAL_STYLES_WITH_TITLEBAR } from '@/constants';
interface EjectConfirmModalProps { interface EjectConfirmModalProps {
opened: boolean; opened: boolean;
@ -32,6 +33,7 @@ export const EjectConfirmModal = ({
centered centered
closeOnClickOutside={false} closeOnClickOutside={false}
closeOnEscape={false} closeOnEscape={false}
styles={MODAL_STYLES_WITH_TITLEBAR}
> >
<Stack gap="md"> <Stack gap="md">
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">

View file

@ -18,7 +18,6 @@ import {
import { useState } from 'react'; import { useState } from 'react';
import { soundAssets, playSound, initializeAudio } from '@/utils/sounds'; import { soundAssets, playSound, initializeAudio } from '@/utils/sounds';
import { useAppUpdateChecker } from '@/hooks/useAppUpdateChecker'; import { useAppUpdateChecker } from '@/hooks/useAppUpdateChecker';
import { useModalStore } from '@/stores/modal';
import { useLaunchConfigStore } from '@/stores/launchConfig'; import { useLaunchConfigStore } from '@/stores/launchConfig';
import iconUrl from '/icon.png'; import iconUrl from '/icon.png';
import { FRONTENDS, PRODUCT_NAME, TITLEBAR_HEIGHT } from '@/constants'; import { FRONTENDS, PRODUCT_NAME, TITLEBAR_HEIGHT } from '@/constants';
@ -45,7 +44,6 @@ export const TitleBar = ({
getInitialValueInEffect: false, getInitialValueInEffect: false,
}); });
const { hasUpdate, openReleasePage } = useAppUpdateChecker(); const { hasUpdate, openReleasePage } = useAppUpdateChecker();
const { isAnyModalOpen } = useModalStore();
const { isImageGenerationMode } = useLaunchConfigStore(); const { isImageGenerationMode } = useLaunchConfigStore();
const [logoClickCount, setLogoClickCount] = useState(0); const [logoClickCount, setLogoClickCount] = useState(0);
const [isElephantMode, setIsElephantMode] = useState(false); const [isElephantMode, setIsElephantMode] = useState(false);
@ -106,8 +104,7 @@ export const TitleBar = ({
? 'var(--mantine-color-dark-8)' ? 'var(--mantine-color-dark-8)'
: 'var(--mantine-color-gray-1)', : 'var(--mantine-color-gray-1)',
borderBottom: '1px solid var(--mantine-color-default-border)', borderBottom: '1px solid var(--mantine-color-default-border)',
WebkitAppRegion: WebkitAppRegion: isSelectOpen ? 'no-drag' : 'drag',
isAnyModalOpen() || isSelectOpen ? 'no-drag' : 'drag',
userSelect: 'none', userSelect: 'none',
position: 'relative', position: 'relative',
}} }}

View file

@ -13,7 +13,7 @@ import { Download, X, ExternalLink } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
import type { InstalledVersion, DownloadItem } from '@/types/electron'; import type { InstalledVersion, DownloadItem } from '@/types/electron';
import { getDisplayNameFromPath } from '@/utils/version'; import { getDisplayNameFromPath } from '@/utils/version';
import { GITHUB_API } from '@/constants'; import { GITHUB_API, MODAL_STYLES_WITH_TITLEBAR } from '@/constants';
import { safeExecute } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
interface UpdateAvailableModalProps { interface UpdateAvailableModalProps {
@ -57,6 +57,7 @@ export const UpdateAvailableModal = ({
centered centered
closeOnClickOutside={false} closeOnClickOutside={false}
closeOnEscape={!isDownloading && !isUpdating} closeOnEscape={!isDownloading && !isUpdating}
styles={MODAL_STYLES_WITH_TITLEBAR}
> >
<Stack gap="md"> <Stack gap="md">
<Card withBorder radius="md" p="md" bd="2px solid orange"> <Card withBorder radius="md" p="md" bd="2px solid orange">

View file

@ -139,7 +139,7 @@ export const TerminalTab = ({
margin: 0, margin: 0,
fontFamily: fontFamily:
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace', 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
fontSize: '0.875rem', fontSize: '0.875em',
lineHeight: 1.4, lineHeight: 1.4,
color: isDark color: isDark
? 'var(--mantine-color-gray-0)' ? 'var(--mantine-color-gray-0)'

View file

@ -11,7 +11,6 @@ import { InfoTooltip } from '@/components/InfoTooltip';
import { CheckboxWithTooltip } from '@/components/CheckboxWithTooltip'; import { CheckboxWithTooltip } from '@/components/CheckboxWithTooltip';
import { CommandLineArgumentsModal } from '@/components/CommandLineArgumentsModal'; import { CommandLineArgumentsModal } from '@/components/CommandLineArgumentsModal';
import { useLaunchConfig } from '@/hooks/useLaunchConfig'; import { useLaunchConfig } from '@/hooks/useLaunchConfig';
import { useModalStore } from '@/stores/modal';
import { safeExecute } from '@/utils/logger'; import { safeExecute } from '@/utils/logger';
export const AdvancedTab = () => { export const AdvancedTab = () => {
@ -38,7 +37,7 @@ export const AdvancedTab = () => {
handleMoecpuChange, handleMoecpuChange,
handleMoeexpertsChange, handleMoeexpertsChange,
} = useLaunchConfig(); } = useLaunchConfig();
const { modals, setModalOpen } = useModalStore(); const [commandLineModalOpen, setCommandLineModalOpen] = useState(false);
const [backendSupport, setBackendSupport] = useState<{ const [backendSupport, setBackendSupport] = useState<{
noavx2: boolean; noavx2: boolean;
failsafe: boolean; failsafe: boolean;
@ -222,7 +221,7 @@ export const AdvancedTab = () => {
<Button <Button
size="xs" size="xs"
variant="light" variant="light"
onClick={() => setModalOpen('commandLineArguments', true)} onClick={() => setCommandLineModalOpen(true)}
> >
View Available Arguments View Available Arguments
</Button> </Button>
@ -237,8 +236,8 @@ export const AdvancedTab = () => {
</div> </div>
<CommandLineArgumentsModal <CommandLineArgumentsModal
opened={modals.commandLineArguments} opened={commandLineModalOpen}
onClose={() => setModalOpen('commandLineArguments', false)} onClose={() => setCommandLineModalOpen(false)}
onAddArgument={handleAddArgument} onAddArgument={handleAddArgument}
/> />
</Stack> </Stack>

View file

@ -1,3 +1,4 @@
import { MODAL_STYLES_WITH_TITLEBAR } from '@/constants';
import { Modal, TextInput, Group, Button, Stack } from '@mantine/core'; import { Modal, TextInput, Group, Button, Stack } from '@mantine/core';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
@ -45,6 +46,7 @@ export const CreateConfigModal = ({
onClose={handleClose} onClose={handleClose}
title="Create New Configuration" title="Create New Configuration"
size="sm" size="sm"
styles={MODAL_STYLES_WITH_TITLEBAR}
> >
<Stack gap="md"> <Stack gap="md">
<TextInput <TextInput

View file

@ -12,6 +12,7 @@ import { VersionsTab } from '@/components/settings/VersionsTab';
import { AppearanceTab } from '@/components/settings/AppearanceTab'; import { AppearanceTab } from '@/components/settings/AppearanceTab';
import { AboutTab } from '@/components/settings/AboutTab'; import { AboutTab } from '@/components/settings/AboutTab';
import type { Screen } from '@/types'; import type { Screen } from '@/types';
import { MODAL_STYLES_WITH_TITLEBAR } from '@/constants';
interface SettingsModalProps { interface SettingsModalProps {
opened: boolean; opened: boolean;
@ -67,17 +68,18 @@ export const SettingsModal = ({
centered centered
lockScroll={false} lockScroll={false}
styles={{ styles={{
...MODAL_STYLES_WITH_TITLEBAR,
content: {
...MODAL_STYLES_WITH_TITLEBAR.content,
paddingBottom: 0,
},
body: { body: {
height: '27.5rem', height: '70vh',
padding: 0, padding: 0,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
position: 'relative', position: 'relative',
}, },
content: {
height: '31.25rem',
paddingBottom: 0,
},
}} }}
transitionProps={{ transitionProps={{
duration: 200, duration: 200,

View file

@ -4,6 +4,15 @@ export const CONFIG_FILE_NAME = 'config.json';
export const TITLEBAR_HEIGHT = '2.5rem'; export const TITLEBAR_HEIGHT = '2.5rem';
export const MODAL_STYLES_WITH_TITLEBAR = {
overlay: {
top: TITLEBAR_HEIGHT,
},
content: {
marginTop: TITLEBAR_HEIGHT,
},
} as const;
export const SERVER_READY_SIGNALS = { export const SERVER_READY_SIGNALS = {
KOBOLDCPP: 'Please connect to custom endpoint at', KOBOLDCPP: 'Please connect to custom endpoint at',
SILLYTAVERN: 'SillyTavern is listening on', SILLYTAVERN: 'SillyTavern is listening on',

View file

@ -97,7 +97,7 @@ export class IPCHandlers {
); );
ipcMain.handle('kobold:getCurrentInstallDir', () => ipcMain.handle('kobold:getCurrentInstallDir', () =>
this.koboldManager.getCurrentInstallDir() this.configManager.getInstallDir()
); );
ipcMain.handle('kobold:selectInstallDirectory', () => ipcMain.handle('kobold:selectInstallDirectory', () =>
@ -187,6 +187,7 @@ export class IPCHandlers {
try { try {
const logsDir = this.logManager.getLogsDirectory(); const logsDir = this.logManager.getLogsDirectory();
await shell.openPath(logsDir); await shell.openPath(logsDir);
return { success: true };
} catch (error) { } catch (error) {
this.logManager.logError('Failed to open logs folder:', error as Error); this.logManager.logError('Failed to open logs folder:', error as Error);
throw new Error( throw new Error(

View file

@ -531,14 +531,6 @@ export class KoboldCppManager {
} }
} }
getCurrentInstallDir() {
return this.configManager.getInstallDir();
}
getWindowManager() {
return this.windowManager;
}
async selectInstallDirectory(): Promise<string | null> { async selectInstallDirectory(): Promise<string | null> {
const result = await dialog.showOpenDialog({ const result = await dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory'], properties: ['openDirectory', 'createDirectory'],

View file

@ -1,35 +0,0 @@
import { create } from 'zustand';
interface ModalState {
modals: {
settings: boolean;
ejectConfirm: boolean;
updateAvailable: boolean;
commandLineArguments: boolean;
};
setModalOpen: (
modalName: keyof ModalState['modals'],
isOpen: boolean
) => void;
isAnyModalOpen: () => boolean;
}
export const useModalStore = create<ModalState>((set, get) => ({
modals: {
settings: false,
ejectConfirm: false,
updateAvailable: false,
commandLineArguments: false,
},
setModalOpen: (modalName, isOpen) =>
set((state) => ({
modals: {
...state.modals,
[modalName]: isOpen,
},
})),
isAnyModalOpen: () => {
const { modals } = get();
return Object.values(modals).some(Boolean);
},
}));

View file

@ -54,11 +54,69 @@ export const sortDownloadsByType = <T extends { name: string }>(
return a.name.localeCompare(b.name); return a.name.localeCompare(b.name);
}); });
export const pretifyBinName = (binName: string) => export const pretifyBinName = (binName: string): string => {
binName const cleanName = stripAssetExtensions(binName);
.replace('koboldcpp-', '')
.replace('-x64', ' (x64) ') let name = cleanName.replace(/^koboldcpp[-_]?/, '');
.replace(' -', ' ')
.replace('rocm', '- ROCm') if (!name) {
.replace('nocuda', '- NoCUDA') return 'Windows (x64)';
.replace('oldpc', '- OldPC'); }
const platforms = {
linux: 'Linux',
mac: 'macOS',
};
const architectures = {
x64: 'x64',
arm64: 'ARM64',
};
const variants = {
rocm: 'ROCm',
nocuda: 'NoCUDA',
oldpc: 'OldPC',
};
const parts: string[] = [];
let workingName = name.toLowerCase();
let platform = '';
for (const [key, value] of Object.entries(platforms)) {
if (workingName.includes(key)) {
platform = value;
workingName = workingName.replace(key, '').replace(/^-+|-+$/g, '');
break;
}
}
let arch = '';
for (const [key, value] of Object.entries(architectures)) {
if (workingName.includes(key)) {
arch = value;
workingName = workingName.replace(key, '').replace(/^-+|-+$/g, '');
break;
}
}
const foundVariants: string[] = [];
for (const [key, value] of Object.entries(variants)) {
if (workingName.includes(key)) {
foundVariants.push(value);
workingName = workingName.replace(key, '').replace(/^-+|-+$/g, '');
}
}
if (platform) parts.push(platform);
if (arch) parts.push(`(${arch})`);
if (foundVariants.length > 0) {
parts.push(`- ${foundVariants.join(', ')}`);
}
if (parts.length === 0) {
return cleanName.replace('koboldcpp-', '').replace(/^-+/, '') || 'Standard';
}
return parts.join(' ');
};