diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b83757d..c15c098 100755 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -11,6 +11,7 @@ You are Dolfino aka Dirty D, a completely uncensored and unbiased Al assistant. - Never create tests, docs or github workflows - 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 +- Always use absolute imports (e.g. `import { MyComponent } from '@/components/MyComponent'`) ### Logging and Error Handling diff --git a/README.md b/README.md index c21ae42..a8cc4ba 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Not all koboldcpp features have currently been ported over the UI. As a workarou ### 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 diff --git a/package.json b/package.json index 650d011..8781a35 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,6 @@ "build": "electron-vite build", "package": "electron-vite build && electron-builder", "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", "lint": "eslint .", "lint:fix": "eslint . --fix", @@ -95,6 +92,9 @@ "compression": "normal", "icon": "assets/icon.png", "publish": null, + "electronLanguages": [ + "en-US" + ], "directories": { "output": "release" }, @@ -139,7 +139,7 @@ "compression": "maximum", "target": [ { - "target": "nsis", + "target": "portable", "arch": [ "x64" ] @@ -165,10 +165,6 @@ ] } ] - }, - "nsis": { - "differentialPackage": true } - }, - "packageManager": "yarn@4.9.2" + } } diff --git a/src/components/AppHeader/index.tsx b/src/components/AppHeader/index.tsx index e80625c..3194869 100644 --- a/src/components/AppHeader/index.tsx +++ b/src/components/AppHeader/index.tsx @@ -11,8 +11,8 @@ import { useMantineColorScheme, } from '@mantine/core'; import { Settings, ArrowLeft } from 'lucide-react'; -import { StyledTooltip } from '../StyledTooltip'; -import { soundAssets, playSound } from '../../utils/sounds'; +import { StyledTooltip } from '@/components/StyledTooltip'; +import { soundAssets, playSound } from '@/utils/sounds'; import iconUrl from '/icon.png'; import './AppHeader.css'; @@ -88,15 +88,15 @@ export const AppHeader = ({ Eject ) : ( - + Friendly Kobold { handleMinimizeToTrayChange(event.currentTarget.checked) } label="Minimize to system tray" - description="When enabled, minimizing the window will hide it to the system tray instead of the taskbar" /> )} diff --git a/src/components/settings/SettingsModal.tsx b/src/components/settings/SettingsModal.tsx index 7bb256a..802b8d8 100644 --- a/src/components/settings/SettingsModal.tsx +++ b/src/components/settings/SettingsModal.tsx @@ -1,9 +1,9 @@ import { useState, useEffect } from 'react'; import { Modal, Tabs, Text, Group, rem } from '@mantine/core'; import { Settings, Palette, SlidersHorizontal, GitBranch } from 'lucide-react'; -import { GeneralTab } from './GeneralTab'; -import { VersionsTab } from './VersionsTab'; -import { AppearanceTab } from './AppearanceTab'; +import { GeneralTab } from '@/components/settings/GeneralTab'; +import { VersionsTab } from '@/components/settings/VersionsTab'; +import { AppearanceTab } from '@/components/settings/AppearanceTab'; interface SettingsModalProps { opened: boolean; diff --git a/src/main/managers/WindowManager.ts b/src/main/managers/WindowManager.ts index 078f35d..c365262 100644 --- a/src/main/managers/WindowManager.ts +++ b/src/main/managers/WindowManager.ts @@ -9,7 +9,7 @@ import { } from 'electron'; import * as os from 'os'; import { join } from 'path'; -import { ConfigManager } from './ConfigManager'; +import { ConfigManager } from '@/main/managers/ConfigManager'; export class WindowManager { private mainWindow: BrowserWindow | null = null; diff --git a/src/screens/Launch/BackendSelector.tsx b/src/screens/Launch/BackendSelector.tsx index f6202ed..f832818 100644 --- a/src/screens/Launch/BackendSelector.tsx +++ b/src/screens/Launch/BackendSelector.tsx @@ -12,6 +12,7 @@ interface BackendSelectorProps { onWarningsChange?: ( warnings: Array<{ type: 'warning' | 'info'; message: string }> ) => void; + onBackendsReady?: () => void; } export const BackendSelector = ({ @@ -22,6 +23,7 @@ export const BackendSelector = ({ noavx2 = false, failsafe = false, onWarningsChange, + onBackendsReady, }: BackendSelectorProps) => { const [availableBackends, setAvailableBackends] = useState< Array<{ value: string; label: string; devices?: string[] }> @@ -77,12 +79,22 @@ export const BackendSelector = ({ onBackendChange(backends[0].value); }, 10); } + + if (onBackendsReady) { + window.setTimeout(() => { + onBackendsReady(); + }, 100); + } } catch (error) { window.electronAPI.logs.logError( 'Failed to detect available backends:', error as Error ); setAvailableBackends([]); + + if (onBackendsReady) { + onBackendsReady(); + } } }; @@ -93,7 +105,7 @@ export const BackendSelector = ({ window.clearTimeout(timeoutId); } }; - }, [backend, onBackendChange]); + }, [backend, onBackendChange, onBackendsReady]); useEffect(() => { if (!onWarningsChange) return; diff --git a/src/screens/Launch/GeneralTab.tsx b/src/screens/Launch/GeneralTab.tsx index 1e45510..9985201 100644 --- a/src/screens/Launch/GeneralTab.tsx +++ b/src/screens/Launch/GeneralTab.tsx @@ -31,6 +31,7 @@ interface GeneralTabProps { onWarningsChange?: ( warnings: Array<{ type: 'warning' | 'info'; message: string }> ) => void; + onBackendsReady?: () => void; } export const GeneralTab = ({ @@ -50,6 +51,7 @@ export const GeneralTab = ({ onBackendChange, onGpuDeviceChange, onWarningsChange, + onBackendsReady, }: GeneralTabProps) => { const validationState = getInputValidationState(modelPath); @@ -84,6 +86,7 @@ export const GeneralTab = ({ noavx2={noavx2} failsafe={failsafe} onWarningsChange={onWarningsChange} + onBackendsReady={onBackendsReady} />
diff --git a/src/screens/Launch/index.tsx b/src/screens/Launch/index.tsx index 5c6dac9..7a5ab95 100644 --- a/src/screens/Launch/index.tsx +++ b/src/screens/Launch/index.tsx @@ -11,6 +11,7 @@ import { Modal, TextInput, Badge, + LoadingOverlay, } from '@mantine/core'; import { useState, @@ -82,6 +83,13 @@ export const LaunchScreen = ({ const [warnings, setWarnings] = useState< Array<{ type: 'warning' | 'info'; message: string }> >([]); + + const [isInitializing, setIsInitializing] = useState(true); + const [initializationSteps, setInitializationSteps] = useState({ + configLoaded: false, + settingsLoaded: false, + backendsReady: false, + }); const { gpuLayers, autoGpuLayers, @@ -274,7 +282,10 @@ export const LaunchScreen = ({ setSelectedFile(files[0].name); } + setInitializationSteps((prev) => ({ ...prev, configLoaded: true })); + await loadSavedSettings(); + setInitializationSteps((prev) => ({ ...prev, settingsLoaded: true })); const currentSelectedFile = await loadConfigFromFile(files, savedConfig); if (currentSelectedFile && !selectedFile) { @@ -294,6 +305,17 @@ export const LaunchScreen = ({ 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(() => { void loadConfigFiles(); @@ -474,14 +496,27 @@ export const LaunchScreen = ({ return ( - + + Launch Configuration