code cleanup continues, adding MMAP support, extra polish

This commit is contained in:
Egor 2025-08-21 13:25:16 -07:00
parent d5323d8b3e
commit ded8acb436
22 changed files with 170 additions and 115 deletions

View file

@ -74,9 +74,11 @@ trycloudflare
unquantized unquantized
useclblast useclblast
usecuda usecuda
usemmap
usevulkan usevulkan
vram vram
vulkan vulkan
vulkaninfo vulkaninfo
wayland wayland
websearch websearch
MMAP

View file

@ -1,7 +1,7 @@
{ {
"name": "friendly-kobold", "name": "friendly-kobold",
"productName": "Friendly Kobold", "productName": "Friendly Kobold",
"version": "0.4.0", "version": "0.4.1",
"description": "A modern Electron shell for KoboldCpp", "description": "A modern Electron shell for KoboldCpp",
"main": "out/main/index.js", "main": "out/main/index.js",
"homepage": "./", "homepage": "./",

View file

@ -6,7 +6,7 @@ import { InterfaceScreen } from '@/components/screens/Interface';
import { WelcomeScreen } from '@/components/screens/Welcome'; import { WelcomeScreen } from '@/components/screens/Welcome';
import { UpdateAvailableModal } from '@/components/UpdateAvailableModal'; import { UpdateAvailableModal } from '@/components/UpdateAvailableModal';
import { SettingsModal } from '@/components/settings/SettingsModal'; import { SettingsModal } from '@/components/settings/SettingsModal';
import { EjectConfirmDialog } from '@/components/EjectConfirmDialog'; import { EjectConfirmModal } from '@/components/EjectConfirmModal';
import { ScreenTransition } from '@/components/ScreenTransition'; import { ScreenTransition } from '@/components/ScreenTransition';
import { AppHeader } from '@/components/AppHeader'; import { AppHeader } from '@/components/AppHeader';
import { useUpdateChecker } from '@/hooks/useUpdateChecker'; import { useUpdateChecker } from '@/hooks/useUpdateChecker';
@ -20,7 +20,7 @@ export const App = () => {
const [currentScreen, setCurrentScreen] = useState<Screen | null>(null); const [currentScreen, setCurrentScreen] = useState<Screen | null>(null);
const [settingsOpened, setSettingsOpened] = useState(false); const [settingsOpened, setSettingsOpened] = useState(false);
const [hasInitialized, setHasInitialized] = useState(false); const [hasInitialized, setHasInitialized] = useState(false);
const [showEjectDialog, setShowEjectDialog] = useState(false); const [showEjectModal, setShowEjectModal] = useState(false);
const [activeInterfaceTab, setActiveInterfaceTab] = useState<string | null>( const [activeInterfaceTab, setActiveInterfaceTab] = useState<string | null>(
'terminal' 'terminal'
); );
@ -179,7 +179,7 @@ export const App = () => {
if (skipEjectConfirmation) { if (skipEjectConfirmation) {
performEject(); performEject();
} else { } else {
setShowEjectDialog(true); setShowEjectModal(true);
} }
}; };
@ -309,9 +309,9 @@ export const App = () => {
onClose={() => setSettingsOpened(false)} onClose={() => setSettingsOpened(false)}
currentScreen={currentScreen || undefined} currentScreen={currentScreen || undefined}
/> />
<EjectConfirmDialog <EjectConfirmModal
opened={showEjectDialog} opened={showEjectModal}
onClose={() => setShowEjectDialog(false)} onClose={() => setShowEjectModal(false)}
onConfirm={handleEjectConfirm} onConfirm={handleEjectConfirm}
/> />
</AppShell> </AppShell>

View file

@ -14,7 +14,6 @@ import { Settings, ArrowLeft } from 'lucide-react';
import { StyledTooltip } from '@/components/StyledTooltip'; import { StyledTooltip } from '@/components/StyledTooltip';
import { soundAssets, playSound, initializeAudio } from '@/utils'; import { soundAssets, playSound, initializeAudio } from '@/utils';
import iconUrl from '/icon.png'; import iconUrl from '/icon.png';
import '@/styles/AppHeader.css';
type Screen = 'welcome' | 'download' | 'launch' | 'interface'; type Screen = 'welcome' | 'download' | 'launch' | 'interface';

View file

@ -1,17 +1,17 @@
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';
interface EjectConfirmDialogProps { interface EjectConfirmModalProps {
opened: boolean; opened: boolean;
onClose: () => void; onClose: () => void;
onConfirm: (skipConfirmation: boolean) => void; onConfirm: (skipConfirmation: boolean) => void;
} }
export const EjectConfirmDialog = ({ export const EjectConfirmModal = ({
opened, opened,
onClose, onClose,
onConfirm, onConfirm,
}: EjectConfirmDialogProps) => { }: EjectConfirmModalProps) => {
const [skipConfirmation, setSkipConfirmation] = useState(false); const [skipConfirmation, setSkipConfirmation] = useState(false);
const handleConfirm = () => { const handleConfirm = () => {

View file

@ -15,7 +15,7 @@ export const ScreenTransition = ({
<Transition <Transition
mounted={isActive} mounted={isActive}
transition="fade" transition="fade"
duration={shouldAnimate ? 150 : 0} duration={shouldAnimate ? 100 : 0}
timingFunction="ease-out" timingFunction="ease-out"
> >
{(styles) => ( {(styles) => (

View file

@ -13,6 +13,7 @@ export const AdvancedTab = () => {
failsafe, failsafe,
lowvram, lowvram,
quantmatmul, quantmatmul,
usemmap,
backend, backend,
handleAdditionalArgumentsChange, handleAdditionalArgumentsChange,
handleNoshiftChange, handleNoshiftChange,
@ -21,6 +22,7 @@ export const AdvancedTab = () => {
handleFailsafeChange, handleFailsafeChange,
handleLowvramChange, handleLowvramChange,
handleQuantmatmulChange, handleQuantmatmulChange,
handleUsemmapChange,
} = useLaunchConfig(); } = useLaunchConfig();
const [backendSupport, setBackendSupport] = useState<{ const [backendSupport, setBackendSupport] = useState<{
noavx2: boolean; noavx2: boolean;
@ -148,6 +150,19 @@ export const AdvancedTab = () => {
/> />
</Group> </Group>
</div> </div>
<div className={styles.minWidth200}>
<Group gap="xs" align="center">
<Checkbox
checked={usemmap}
onChange={(event) =>
handleUsemmapChange(event.currentTarget.checked)
}
label="MMAP"
/>
<InfoTooltip label="Use MMAP to load models when enabled." />
</Group>
</div>
</Group> </Group>
</Stack> </Stack>
</div> </div>

View file

@ -49,6 +49,7 @@ export const LaunchScreen = ({
failsafe, failsafe,
lowvram, lowvram,
quantmatmul, quantmatmul,
usemmap,
backend, backend,
gpuDevice, gpuDevice,
gpuPlatform, gpuPlatform,
@ -148,6 +149,7 @@ export const LaunchScreen = ({
flashattention, flashattention,
noavx2, noavx2,
failsafe, failsafe,
usemmap,
usecuda: backend === 'cuda' || backend === 'rocm', usecuda: backend === 'cuda' || backend === 'rocm',
usevulkan: backend === 'vulkan', usevulkan: backend === 'vulkan',
useclblast: backend === 'clblast', useclblast: backend === 'clblast',
@ -248,10 +250,13 @@ export const LaunchScreen = ({
websearch, websearch,
noshift, noshift,
flashattention, flashattention,
noavx2,
failsafe,
backend, backend,
lowvram, lowvram,
gpuDevice, gpuDevice,
quantmatmul, quantmatmul,
usemmap,
additionalArguments, additionalArguments,
sdt5xxl, sdt5xxl,
sdclipl, sdclipl,

View file

@ -2,5 +2,3 @@ export const DEFAULT_CONTEXT_SIZE = 4096;
export const DEFAULT_MODEL_URL = export const DEFAULT_MODEL_URL =
'https://huggingface.co/MaziyarPanahi/gemma-3-4b-it-GGUF/resolve/main/gemma-3-4b-it.Q8_0.gguf?download=true'; 'https://huggingface.co/MaziyarPanahi/gemma-3-4b-it-GGUF/resolve/main/gemma-3-4b-it.Q8_0.gguf?download=true';
export const DEFAULT_HOST = '';

View file

@ -26,6 +26,7 @@ export const useLaunchConfig = () => {
failsafe: state.failsafe, failsafe: state.failsafe,
lowvram: state.lowvram, lowvram: state.lowvram,
quantmatmul: state.quantmatmul, quantmatmul: state.quantmatmul,
usemmap: state.usemmap,
backend: state.backend, backend: state.backend,
gpuDevice: state.gpuDevice, gpuDevice: state.gpuDevice,
gpuPlatform: state.gpuPlatform, gpuPlatform: state.gpuPlatform,
@ -55,6 +56,7 @@ export const useLaunchConfig = () => {
handleFailsafeChange: state.setFailsafe, handleFailsafeChange: state.setFailsafe,
handleLowvramChange: state.setLowvram, handleLowvramChange: state.setLowvram,
handleQuantmatmulChange: state.setQuantmatmul, handleQuantmatmulChange: state.setQuantmatmul,
handleUsemmapChange: state.setUsemmap,
handleBackendChange: state.setBackend, handleBackendChange: state.setBackend,
handleGpuDeviceChange: state.setGpuDevice, handleGpuDeviceChange: state.setGpuDevice,
handleGpuPlatformChange: state.setGpuPlatform, handleGpuPlatformChange: state.setGpuPlatform,

View file

@ -21,10 +21,13 @@ interface LaunchArgs {
websearch: boolean; websearch: boolean;
noshift: boolean; noshift: boolean;
flashattention: boolean; flashattention: boolean;
noavx2: boolean;
failsafe: boolean;
backend: string; backend: string;
lowvram: boolean; lowvram: boolean;
gpuDevice: number | string; gpuDevice: number | string;
quantmatmul: boolean; quantmatmul: boolean;
usemmap: boolean;
additionalArguments: string; additionalArguments: string;
sdt5xxl: string; sdt5xxl: string;
sdclipl: string; sdclipl: string;
@ -98,6 +101,9 @@ const buildConfigArgs = (launchArgs: LaunchArgs): string[] => {
[launchArgs.websearch, '--websearch'], [launchArgs.websearch, '--websearch'],
[launchArgs.noshift, '--noshift'], [launchArgs.noshift, '--noshift'],
[launchArgs.flashattention, '--flashattention'], [launchArgs.flashattention, '--flashattention'],
[launchArgs.noavx2, '--noavx2'],
[launchArgs.failsafe, '--failsafe'],
[launchArgs.usemmap, '--usemmap'],
]; ];
flagMappings.forEach(([condition, flag, value]) => { flagMappings.forEach(([condition, flag, value]) => {

View file

@ -384,6 +384,7 @@ export class KoboldCppManager {
flashattention?: boolean; flashattention?: boolean;
noavx2?: boolean; noavx2?: boolean;
failsafe?: boolean; failsafe?: boolean;
usemmap?: boolean;
usecuda?: boolean; usecuda?: boolean;
usevulkan?: boolean; usevulkan?: boolean;
useclblast?: [number, number] | boolean; useclblast?: [number, number] | boolean;

View file

@ -1,11 +1,4 @@
import { import { BrowserWindow, app, Menu, shell, nativeImage } from 'electron';
BrowserWindow,
app,
Menu,
shell,
ipcMain,
nativeImage,
} from 'electron';
import * as os from 'os'; import * as os from 'os';
import { join } from 'path'; import { join } from 'path';
import { GITHUB_API } from '../../constants'; import { GITHUB_API } from '../../constants';
@ -311,8 +304,9 @@ OS: ${osInfo}`;
maximizable: false, maximizable: false,
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: false,
contextIsolation: false, contextIsolation: true,
preload: join(__dirname, '../preload/index.js'),
}, },
}); });
@ -328,21 +322,6 @@ OS: ${osInfo}`;
aboutWindow.once('ready-to-show', () => { aboutWindow.once('ready-to-show', () => {
aboutWindow.show(); aboutWindow.show();
}); });
ipcMain.once('open-github', () => {
shell.openExternal(
`https://github.com/${GITHUB_API.FRIENDLY_KOBOLD_REPO}`
);
});
ipcMain.once('close-about-dialog', () => {
aboutWindow.close();
});
aboutWindow.on('closed', () => {
ipcMain.removeAllListeners('open-github');
ipcMain.removeAllListeners('close-about-dialog');
});
} }
private getTemplatePath(filename: string): string { private getTemplatePath(filename: string): string {

View file

@ -131,8 +131,6 @@
</div> </div>
<script> <script>
const { ipcRenderer } = require('electron');
function setVersionInfo(info) { function setVersionInfo(info) {
document.getElementById('version-info').innerHTML = info.replace( document.getElementById('version-info').innerHTML = info.replace(
/\n/g, /\n/g,
@ -141,11 +139,16 @@
} }
function openGitHub() { function openGitHub() {
ipcRenderer.send('open-github'); // Send IPC message directly since this is a simple modal
if (window.electronAPI && window.electronAPI.app) {
window.electronAPI.app.openExternal(
'https://github.com/lone-cloud/friendly-kobold'
);
}
} }
function closeDialog() { function closeDialog() {
ipcRenderer.send('close-about-dialog'); window.close();
} }
// Handle keyboard shortcuts // Handle keyboard shortcuts

View file

@ -2,14 +2,10 @@ import { contextBridge, ipcRenderer, type IpcRendererEvent } from 'electron';
import type { KoboldAPI, AppAPI, ConfigAPI, LogsAPI } from '@/types/electron'; import type { KoboldAPI, AppAPI, ConfigAPI, LogsAPI } from '@/types/electron';
const koboldAPI: KoboldAPI = { const koboldAPI: KoboldAPI = {
getInstalledVersion: () => ipcRenderer.invoke('kobold:getInstalledVersion'),
getInstalledVersions: () => ipcRenderer.invoke('kobold:getInstalledVersions'), getInstalledVersions: () => ipcRenderer.invoke('kobold:getInstalledVersions'),
getCurrentVersion: () => ipcRenderer.invoke('kobold:getCurrentVersion'),
getCurrentBinaryInfo: () => ipcRenderer.invoke('kobold:getCurrentBinaryInfo'), getCurrentBinaryInfo: () => ipcRenderer.invoke('kobold:getCurrentBinaryInfo'),
setCurrentVersion: (version: string) => setCurrentVersion: (version: string) =>
ipcRenderer.invoke('kobold:setCurrentVersion', version), ipcRenderer.invoke('kobold:setCurrentVersion', version),
getVersionFromBinary: (binaryPath: string) =>
ipcRenderer.invoke('kobold:getVersionFromBinary', binaryPath),
getLatestReleaseWithStatus: () => getLatestReleaseWithStatus: () =>
ipcRenderer.invoke('kobold:getLatestReleaseWithStatus'), ipcRenderer.invoke('kobold:getLatestReleaseWithStatus'),
getROCmDownload: () => ipcRenderer.invoke('kobold:getROCmDownload'), getROCmDownload: () => ipcRenderer.invoke('kobold:getROCmDownload'),
@ -18,11 +14,7 @@ const koboldAPI: KoboldAPI = {
getPlatform: () => ipcRenderer.invoke('kobold:getPlatform'), getPlatform: () => ipcRenderer.invoke('kobold:getPlatform'),
detectGPU: () => ipcRenderer.invoke('kobold:detectGPU'), detectGPU: () => ipcRenderer.invoke('kobold:detectGPU'),
detectCPU: () => ipcRenderer.invoke('kobold:detectCPU'), detectCPU: () => ipcRenderer.invoke('kobold:detectCPU'),
detectGPUCapabilities: () =>
ipcRenderer.invoke('kobold:detectGPUCapabilities'),
detectROCm: () => ipcRenderer.invoke('kobold:detectROCm'), detectROCm: () => ipcRenderer.invoke('kobold:detectROCm'),
detectAllCapabilities: () =>
ipcRenderer.invoke('kobold:detectAllCapabilities'),
detectBackendSupport: (binaryPath: string) => detectBackendSupport: (binaryPath: string) =>
ipcRenderer.invoke('kobold:detectBackendSupport', binaryPath), ipcRenderer.invoke('kobold:detectBackendSupport', binaryPath),
getAvailableBackends: () => ipcRenderer.invoke('kobold:getAvailableBackends'), getAvailableBackends: () => ipcRenderer.invoke('kobold:getAvailableBackends'),
@ -33,7 +25,6 @@ const koboldAPI: KoboldAPI = {
ipcRenderer.invoke('kobold:downloadRelease', asset), ipcRenderer.invoke('kobold:downloadRelease', asset),
launchKoboldCpp: (args?: string[], configFilePath?: string) => launchKoboldCpp: (args?: string[], configFilePath?: string) =>
ipcRenderer.invoke('kobold:launchKoboldCpp', args, configFilePath), ipcRenderer.invoke('kobold:launchKoboldCpp', args, configFilePath),
checkForUpdates: () => ipcRenderer.invoke('kobold:checkForUpdates'),
getConfigFiles: () => ipcRenderer.invoke('kobold:getConfigFiles'), getConfigFiles: () => ipcRenderer.invoke('kobold:getConfigFiles'),
saveConfigFile: ( saveConfigFile: (
configName: string, configName: string,
@ -52,6 +43,7 @@ const koboldAPI: KoboldAPI = {
flashattention?: boolean; flashattention?: boolean;
noavx2?: boolean; noavx2?: boolean;
failsafe?: boolean; failsafe?: boolean;
usemmap?: boolean;
usecuda?: boolean; usecuda?: boolean;
usevulkan?: boolean; usevulkan?: boolean;
useclblast?: boolean; useclblast?: boolean;
@ -107,7 +99,6 @@ const koboldAPI: KoboldAPI = {
}; };
const appAPI: AppAPI = { const appAPI: AppAPI = {
getVersion: () => ipcRenderer.invoke('app:getVersion'),
openExternal: (url) => ipcRenderer.invoke('app:openExternal', url), openExternal: (url) => ipcRenderer.invoke('app:openExternal', url),
}; };

View file

@ -1,7 +1,7 @@
import { create } from 'zustand'; import { create } from 'zustand';
import type { ConfigFile } from '@/types'; import type { ConfigFile } from '@/types';
import type { ImageModelPreset } from '@/utils/imageModelPresets'; import type { ImageModelPreset } from '@/utils/imageModelPresets';
import { DEFAULT_CONTEXT_SIZE, DEFAULT_HOST } from '@/constants'; import { DEFAULT_CONTEXT_SIZE } from '@/constants';
interface LaunchConfigState { interface LaunchConfigState {
gpuLayers: number; gpuLayers: number;
@ -22,6 +22,7 @@ interface LaunchConfigState {
failsafe: boolean; failsafe: boolean;
lowvram: boolean; lowvram: boolean;
quantmatmul: boolean; quantmatmul: boolean;
usemmap: boolean;
backend: string; backend: string;
gpuDevice: number; gpuDevice: number;
gpuPlatform: number; gpuPlatform: number;
@ -51,6 +52,7 @@ interface LaunchConfigState {
setFailsafe: (failsafe: boolean) => void; setFailsafe: (failsafe: boolean) => void;
setLowvram: (lowvram: boolean) => void; setLowvram: (lowvram: boolean) => void;
setQuantmatmul: (quantmatmul: boolean) => void; setQuantmatmul: (quantmatmul: boolean) => void;
setUsemmap: (usemmap: boolean) => void;
setBackend: (backend: string) => void; setBackend: (backend: string) => void;
setGpuDevice: (device: number) => void; setGpuDevice: (device: number) => void;
setGpuPlatform: (platform: number) => void; setGpuPlatform: (platform: number) => void;
@ -86,7 +88,7 @@ export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
modelPath: '', modelPath: '',
additionalArguments: '', additionalArguments: '',
port: undefined, port: undefined,
host: DEFAULT_HOST, host: '',
multiuser: false, multiuser: false,
multiplayer: false, multiplayer: false,
remotetunnel: false, remotetunnel: false,
@ -98,6 +100,7 @@ export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
failsafe: false, failsafe: false,
lowvram: false, lowvram: false,
quantmatmul: true, quantmatmul: true,
usemmap: true,
backend: '', backend: '',
gpuDevice: 0, gpuDevice: 0,
gpuPlatform: 0, gpuPlatform: 0,
@ -127,6 +130,7 @@ export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
setFailsafe: (failsafe) => set({ failsafe }), setFailsafe: (failsafe) => set({ failsafe }),
setLowvram: (lowvram) => set({ lowvram }), setLowvram: (lowvram) => set({ lowvram }),
setQuantmatmul: (quantmatmul) => set({ quantmatmul }), setQuantmatmul: (quantmatmul) => set({ quantmatmul }),
setUsemmap: (usemmap) => set({ usemmap }),
setBackend: (backend) => set({ backend }), setBackend: (backend) => set({ backend }),
setGpuDevice: (device) => set({ gpuDevice: device }), setGpuDevice: (device) => set({ gpuDevice: device }),
setGpuPlatform: (platform) => set({ gpuPlatform: platform }), setGpuPlatform: (platform) => set({ gpuPlatform: platform }),
@ -171,7 +175,7 @@ export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
if (typeof configData.host === 'string') { if (typeof configData.host === 'string') {
updates.host = configData.host; updates.host = configData.host;
} else { } else {
updates.host = DEFAULT_HOST; updates.host = '';
} }
if (typeof configData.multiuser === 'number') { if (typeof configData.multiuser === 'number') {
@ -236,6 +240,12 @@ export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
updates.quantmatmul = configData.quantmatmul; updates.quantmatmul = configData.quantmatmul;
} }
if (typeof configData.usemmap === 'boolean') {
updates.usemmap = configData.usemmap;
} else {
updates.usemmap = true;
}
if (configData.usecuda === true) { if (configData.usecuda === true) {
const gpuInfo = await window.electronAPI.kobold.detectGPU(); const gpuInfo = await window.electronAPI.kobold.detectGPU();
updates.backend = gpuInfo.hasNVIDIA ? 'cuda' : 'rocm'; updates.backend = gpuInfo.hasNVIDIA ? 'cuda' : 'rocm';

View file

@ -1,45 +0,0 @@
@keyframes elephantShake {
0%,
100% {
transform: scale(1.3) rotate(5deg) translateX(0px);
}
10% {
transform: scale(1.4) rotate(-3deg) translateX(-2px);
}
20% {
transform: scale(1.5) rotate(8deg) translateX(2px);
}
30% {
transform: scale(1.4) rotate(-5deg) translateX(-1px);
}
40% {
transform: scale(1.6) rotate(10deg) translateX(3px);
}
50% {
transform: scale(1.3) rotate(-8deg) translateX(-2px);
}
60% {
transform: scale(1.5) rotate(6deg) translateX(1px);
}
70% {
transform: scale(1.2) rotate(-4deg) translateX(-1px);
}
80% {
transform: scale(1.4) rotate(3deg) translateX(2px);
}
90% {
transform: scale(1.1) rotate(-2deg) translateX(-1px);
}
}
@keyframes mouseSqueak {
0% {
transform: scale(1) rotate(0deg);
}
50% {
transform: scale(1.15) rotate(2deg);
}
100% {
transform: scale(1) rotate(0deg);
}
}

View file

@ -1 +1,97 @@
@import '@mantine/core/styles.css'; @import '@mantine/core/styles.css';
/* Global smooth scrolling */
* {
scroll-behavior: smooth;
}
html {
scroll-behavior: smooth;
}
/* Custom scrollbars */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(134, 142, 150, 0.5);
border-radius: 4px;
transition: all 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(134, 142, 150, 0.8);
}
::-webkit-scrollbar-thumb:active {
background: rgba(73, 80, 87, 0.9);
}
/* Dark mode scrollbar support */
@media (prefers-color-scheme: dark) {
::-webkit-scrollbar-thumb {
background: rgba(173, 181, 189, 0.5);
}
::-webkit-scrollbar-thumb:hover {
background: rgba(173, 181, 189, 0.8);
}
::-webkit-scrollbar-thumb:active {
background: rgba(206, 212, 218, 0.9);
}
}
/* AppHeader animations */
@keyframes elephantShake {
0%,
100% {
transform: scale(1.3) rotate(5deg) translateX(0px);
}
10% {
transform: scale(1.4) rotate(-3deg) translateX(-2px);
}
20% {
transform: scale(1.5) rotate(8deg) translateX(2px);
}
30% {
transform: scale(1.4) rotate(-5deg) translateX(-1px);
}
40% {
transform: scale(1.6) rotate(10deg) translateX(3px);
}
50% {
transform: scale(1.3) rotate(-8deg) translateX(-2px);
}
60% {
transform: scale(1.5) rotate(6deg) translateX(1px);
}
70% {
transform: scale(1.2) rotate(-4deg) translateX(-1px);
}
80% {
transform: scale(1.4) rotate(3deg) translateX(2px);
}
90% {
transform: scale(1.1) rotate(-2deg) translateX(-1px);
}
}
@keyframes mouseSqueak {
0% {
transform: scale(1) rotate(0deg);
}
50% {
transform: scale(1.15) rotate(2deg);
}
100% {
transform: scale(1) rotate(0deg);
}
}

View file

@ -57,22 +57,17 @@ export interface DownloadItem {
} }
export interface KoboldAPI { export interface KoboldAPI {
getInstalledVersion: () => Promise<string | undefined>;
getInstalledVersions: () => Promise<InstalledVersion[]>; getInstalledVersions: () => Promise<InstalledVersion[]>;
getCurrentVersion: () => Promise<InstalledVersion | null>;
getCurrentBinaryInfo: () => Promise<{ getCurrentBinaryInfo: () => Promise<{
path: string; path: string;
filename: string; filename: string;
} | null>; } | null>;
setCurrentVersion: (version: string) => Promise<boolean>; setCurrentVersion: (version: string) => Promise<boolean>;
getVersionFromBinary: (binaryPath: string) => Promise<string | null>;
getLatestRelease: () => Promise<DownloadItem[]>; getLatestRelease: () => Promise<DownloadItem[]>;
getPlatform: () => Promise<PlatformInfo>; getPlatform: () => Promise<PlatformInfo>;
detectGPU: () => Promise<BasicGPUInfo>; detectGPU: () => Promise<BasicGPUInfo>;
detectCPU: () => Promise<CPUCapabilities>; detectCPU: () => Promise<CPUCapabilities>;
detectGPUCapabilities: () => Promise<GPUCapabilities>;
detectROCm: () => Promise<{ supported: boolean; devices: string[] }>; detectROCm: () => Promise<{ supported: boolean; devices: string[] }>;
detectAllCapabilities: () => Promise<HardwareInfo>;
detectBackendSupport: (binaryPath: string) => Promise<{ detectBackendSupport: (binaryPath: string) => Promise<{
rocm: boolean; rocm: boolean;
vulkan: boolean; vulkan: boolean;
@ -95,7 +90,6 @@ export interface KoboldAPI {
error?: string; error?: string;
}>; }>;
getROCmDownload: () => Promise<DownloadItem | null>; getROCmDownload: () => Promise<DownloadItem | null>;
checkForUpdates: () => Promise<UpdateInfo | null>;
getLatestReleaseWithStatus: () => Promise<ReleaseWithStatus | null>; getLatestReleaseWithStatus: () => Promise<ReleaseWithStatus | null>;
launchKoboldCpp: ( launchKoboldCpp: (
args?: string[], args?: string[],
@ -121,6 +115,7 @@ export interface KoboldAPI {
flashattention?: boolean; flashattention?: boolean;
noavx2?: boolean; noavx2?: boolean;
failsafe?: boolean; failsafe?: boolean;
usemmap?: boolean;
usecuda?: boolean; usecuda?: boolean;
usevulkan?: boolean; usevulkan?: boolean;
useclblast?: boolean; useclblast?: boolean;
@ -152,7 +147,6 @@ export interface KoboldAPI {
} }
export interface AppAPI { export interface AppAPI {
getVersion: () => Promise<string>;
openExternal: (url: string) => Promise<void>; openExternal: (url: string) => Promise<void>;
} }

View file

@ -1,4 +1,3 @@
import { formatFileSizeInMB } from '@/utils/fileSize';
import { KOBOLDAI_URLS } from '@/constants'; import { KOBOLDAI_URLS } from '@/constants';
export const formatDownloadSize = (size: number, url?: string): string => { export const formatDownloadSize = (size: number, url?: string): string => {
@ -35,3 +34,9 @@ export const compareVersions = (versionA: string, versionB: string): number => {
return 0; return 0;
}; };
const formatFileSizeInMB = (bytes: number) => {
if (bytes === 0) return '0 MB';
const mb = bytes / (1024 * 1024);
return parseFloat(mb.toFixed(1)) + ' MB';
};

View file

@ -1,5 +0,0 @@
export const formatFileSizeInMB = (bytes: number) => {
if (bytes === 0) return '0 MB';
const mb = bytes / (1024 * 1024);
return parseFloat(mb.toFixed(1)) + ' MB';
};

View file

@ -1,7 +1,6 @@
export * from './assets'; export * from './assets';
export * from './clblast'; export * from './clblast';
export * from './downloadUtils'; export * from './downloadUtils';
export * from './fileSize';
export * from './hardware'; export * from './hardware';
export * from './imageModelPresets'; export * from './imageModelPresets';
export * from './platform'; export * from './platform';