diff --git a/README.md b/README.md index 326acec..b591977 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A koboldcpp manager. - download and keep up-to-date your [koboldcpp](https://github.com/LostRuins/koboldcpp/releases) binary - better surface the ROCm-specific builds of koboldcpp from YellowRoseCx and from [koboldai.org](https://koboldai.org/cpplinuxrocm) - manage the koboldcpp binary to prevent it from running in the background indefinitely +- automatically unpack all downloaded koboldcpp binaries for significantly faster operations ### Prerequisites diff --git a/cspell.json b/cspell.json index 620fd76..dd592b6 100644 --- a/cspell.json +++ b/cspell.json @@ -22,10 +22,10 @@ "bundler", "bundling", "can", - "clblast", - "clinfo", "classList", "className", + "clblast", + "clinfo", "cloneNode", "codeinterface", "componentDidCatch", @@ -60,11 +60,11 @@ "filenames", "filepath", "filepaths", + "flashattention", + "Flashattention", "flexbox", "flexdir", "flexwrap", - "flashattention", - "Flashattention", "fontsize", "fontweight", "forwardRef", @@ -118,11 +118,12 @@ "minified", "minify", "moz", - "multiuser", "multiplayer", + "multiuser", "namespace", "namespaces", "newpackagename", + "noavx", "nocertify", "nocuda", "nodeIntegration", @@ -150,6 +151,8 @@ "preload", "prettierrc", "preventDefault", + "PYTHONDONTWRITEBYTECODE", + "PYTHONUNBUFFERED", "querySelector", "querySelectorAll", "radeon", @@ -208,14 +211,14 @@ "url", "urls", "useCallback", + "useclblast", "useContext", + "usecuda", "useEffect", "useMemo", "useReducer", "useRef", "useState", - "useclblast", - "usecuda", "usevulkan", "util", "utils", @@ -231,8 +234,8 @@ "webkit", "webp", "webpack", - "webSecurity", "websearch", + "webSecurity", "whitespace", "wmic", "woff", diff --git a/package-lock.json b/package-lock.json index edc9725..a5884dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "@vitejs/plugin-react": "^5.0.0", "cross-env": "^10.0.0", "cspell": "^9.2.0", - "electron": "^37.2.6", + "electron": "^37.3.0", "electron-builder": "^26.0.12", "electron-vite": "^4.0.0", "eslint": "^9.33.0", @@ -5607,9 +5607,9 @@ } }, "node_modules/electron": { - "version": "37.2.6", - "resolved": "https://registry.npmjs.org/electron/-/electron-37.2.6.tgz", - "integrity": "sha512-Ns6xyxE+hIK5UlujtRlw7w4e2Ju/ImCWXf1Q/PoOhc0N3/6SN6YW7+ujCarsHbxWnolbW+1RlkHtdklUJpjbPA==", + "version": "37.3.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-37.3.0.tgz", + "integrity": "sha512-cPOPUD26DwCh+PZ9q+gMyVBvdBN75SnekI6u5zcOeoLVIXQpzrCm1ewz9BcrkWkVW7oOtfQAEo1G1SffvXrSSw==", "dev": true, "hasInstallScript": true, "license": "MIT", diff --git a/package.json b/package.json index bcd6f42..19666dd 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@vitejs/plugin-react": "^5.0.0", "cross-env": "^10.0.0", "cspell": "^9.2.0", - "electron": "^37.2.6", + "electron": "^37.3.0", "electron-builder": "^26.0.12", "electron-vite": "^4.0.0", "eslint": "^9.33.0", diff --git a/src/App.tsx b/src/App.tsx index f3f651a..294cf0f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,6 +11,7 @@ import { Text, Button, Select, + Badge, useMantineColorScheme, } from '@mantine/core'; import { Settings, ArrowLeft } from 'lucide-react'; @@ -20,7 +21,7 @@ import { InterfaceScreen } from '@/components/screens/InterfaceScreen'; import { UpdateDialog } from '@/components/UpdateDialog'; import { SettingsModal } from '@/components/SettingsModal'; import { ScreenTransition } from '@/components/ScreenTransition'; -import type { UpdateInfo } from '@/types'; +import type { UpdateInfo, InstalledVersion } from '@/types'; type Screen = 'download' | 'launch' | 'interface'; @@ -33,24 +34,64 @@ export const App = () => { const [activeInterfaceTab, setActiveInterfaceTab] = useState( 'terminal' ); + const [currentVersion, setCurrentVersion] = useState( + null + ); + const [installedVersions, setInstalledVersions] = useState< + InstalledVersion[] + >([]); const { colorScheme } = useMantineColorScheme(); + const getDisplayNameFromPath = ( + installedVersion: InstalledVersion + ): string => { + const pathParts = installedVersion.path.split(/[/\\]/); + const launcherIndex = pathParts.findIndex( + (part) => + part === 'koboldcpp-launcher' || + part === 'koboldcpp.exe' || + part === 'koboldcpp' + ); + + if (launcherIndex > 0) { + return pathParts[launcherIndex - 1]; + } + + return installedVersion.filename; + }; + useEffect(() => { const checkInstallation = async () => { try { - const startTime = Date.now(); - const installedVersions = - await window.electronAPI.kobold.getInstalledVersions(false); + const [versions, currentBinaryPath] = await Promise.all([ + window.electronAPI.kobold.getInstalledVersions(), + window.electronAPI.config.get( + 'currentKoboldBinary' + ) as Promise, + ]); - const elapsed = Date.now() - startTime; - const minDelay = 500; - if (elapsed < minDelay) { - await new Promise((resolve) => - setTimeout(resolve, minDelay - elapsed) - ); + setInstalledVersions(versions); + + if (versions.length > 0) { + let current = null; + if (currentBinaryPath) { + current = versions.find((v) => v.path === currentBinaryPath); + } + if (!current) { + current = versions[0]; + if (current) { + await window.electronAPI.config.set( + 'currentKoboldBinary', + current.path + ); + } + } + setCurrentVersion(current); + setCurrentScreen('launch'); + } else { + setCurrentScreen('download'); } - setCurrentScreen(installedVersions.length > 0 ? 'launch' : 'download'); setHasInitialized(true); } catch (error) { console.error('Error checking installation:', error); @@ -70,18 +111,73 @@ export const App = () => { checkInstallation(); }); + const cleanupVersionsListener = window.electronAPI.kobold.onVersionsUpdated( + () => { + checkInstallation(); + } + ); + return () => { window.electronAPI.kobold.removeAllListeners('update-available'); cleanupInstallDirListener(); + cleanupVersionsListener(); }; }, []); - const handleDownloadComplete = () => { + const handleDownloadComplete = async () => { + try { + const [versions, currentBinaryPath] = await Promise.all([ + window.electronAPI.kobold.getInstalledVersions(), + window.electronAPI.config.get('currentKoboldBinary') as Promise, + ]); + + setInstalledVersions(versions); + + if (versions.length > 0) { + let current = null; + if (currentBinaryPath) { + current = versions.find((v) => v.path === currentBinaryPath); + } + if (!current) { + current = versions[0]; + if (current) { + await window.electronAPI.config.set( + 'currentKoboldBinary', + current.path + ); + } + } + setCurrentVersion(current); + } + } catch (error) { + console.error('Error refreshing versions after download:', error); + } + setTimeout(() => { setCurrentScreen('launch'); }, 100); }; + const handleVersionChange = async (versionPath: string | null) => { + if (!versionPath) return; + + try { + const success = + await window.electronAPI.kobold.setCurrentVersion(versionPath); + if (success) { + await window.electronAPI.config.set('currentKoboldBinary', versionPath); + const newCurrent = installedVersions.find( + (v) => v.path === versionPath + ); + if (newCurrent) { + setCurrentVersion(newCurrent); + } + } + } catch (error) { + console.error('Failed to change version:', error); + } + }; + const handleLaunch = () => { setCurrentScreen('interface'); }; @@ -170,6 +266,49 @@ export const App = () => { /> )} + {currentScreen === 'launch' && currentVersion && ( + +