minor styling updates, workaround recent openweb ui bug (on their side),

This commit is contained in:
Egor 2025-09-19 02:34:48 -07:00
parent 068539d20a
commit 02ce36b8ec
17 changed files with 106 additions and 105 deletions

View file

@ -1,7 +1,7 @@
{
"name": "gerbil",
"productName": "Gerbil",
"version": "1.4.0-beta.5",
"version": "1.4.0",
"description": "Run Large Language Models locally",
"main": "out/main/index.js",
"homepage": "./",

View file

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { Group, AppShell } from '@mantine/core';
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
import { usePreferencesStore } from '@/stores/preferences';
import type {
CpuMetrics,
MemoryMetrics,
@ -18,7 +18,7 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
null
);
const [gpuMetrics, setGpuMetrics] = useState<GpuMetrics | null>(null);
const colorScheme = useAppColorScheme();
const { resolvedColorScheme: colorScheme } = usePreferencesStore();
useEffect(() => {
let isMounted = true;

View file

@ -10,12 +10,11 @@ import {
import { Minus, Square, X, Copy, Settings } from 'lucide-react';
import { useState, useEffect } from 'react';
import { useLaunchConfigStore } from '@/stores/launchConfig';
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
import { usePreferencesStore } from '@/stores/preferences';
import { getAvailableInterfaceOptions } from '@/utils/interface';
import { useLogoClickSounds } from '@/hooks/useLogoClickSounds';
import { SettingsModal } from '@/components/settings/SettingsModal';
import { UpdateButton } from '@/components/App/UpdateButton';
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
import icon from '/icon.png';
import { PRODUCT_NAME, TITLEBAR_HEIGHT } from '@/constants';
import type { InterfaceTab, Screen } from '@/types';
@ -33,14 +32,14 @@ export const TitleBar = ({
onEject,
onTabChange,
}: TitleBarProps) => {
const colorScheme = useAppColorScheme();
const { resolvedColorScheme: colorScheme, frontendPreference } =
usePreferencesStore();
const { handleLogoClick, getLogoStyles } = useLogoClickSounds();
const [isMaximized, setIsMaximized] = useState(false);
const [isSelectOpen, setIsSelectOpen] = useState(false);
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
const { isTextMode, isImageGenerationMode } = useLaunchConfigStore();
const { frontendPreference } = useFrontendPreferenceStore();
const interfaceOptions = getAvailableInterfaceOptions({
frontendPreference,
isTextMode,
@ -79,6 +78,7 @@ export const TitleBar = ({
return cleanup;
}, []);
return (
<AppShell.Header
style={{ display: 'flex', flexDirection: 'column', border: 'none' }}
@ -171,7 +171,10 @@ export const TitleBar = ({
style={{
width: '0.0625rem',
height: '1.25rem',
backgroundColor: 'var(--mantine-color-default-border)',
backgroundColor:
colorScheme === 'dark'
? 'var(--mantine-color-dark-3)'
: 'var(--mantine-color-default-border)',
margin: '0 0.25rem',
}}
/>

View file

@ -15,7 +15,7 @@ import { ErrorBoundary } from '@/components/App/ErrorBoundary';
import { AppRouter } from '@/components/App/Router';
import { useUpdateChecker } from '@/hooks/useUpdateChecker';
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
import { usePreferencesStore } from '@/stores/preferences';
import { STATUSBAR_HEIGHT, TITLEBAR_HEIGHT } from '@/constants';
import type { DownloadItem } from '@/types/electron';
import type { InterfaceTab, Screen } from '@/types';
@ -28,7 +28,7 @@ export const App = () => {
const [ejectConfirmModalOpen, setEjectConfirmModalOpen] = useState(false);
const isInterfaceScreen = currentScreen === 'interface';
const appColorScheme = useAppColorScheme();
const { resolvedColorScheme: appColorScheme } = usePreferencesStore();
const { setColorScheme } = useMantineColorScheme();
useEffect(() => {

View file

@ -12,7 +12,7 @@ import {
import { Download } from 'lucide-react';
import { MouseEvent } from 'react';
import { pretifyBinName, isWindowsROCmBuild } from '@/utils/assets';
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
import { usePreferencesStore } from '@/stores/preferences';
interface DownloadCardProps {
name: string;
@ -47,7 +47,7 @@ export const DownloadCard = ({
onMakeCurrent,
onUpdate,
}: DownloadCardProps) => {
const colorScheme = useAppColorScheme();
const { resolvedColorScheme: colorScheme } = usePreferencesStore();
const renderActionButtons = () => {
const buttons = [];

View file

@ -1,7 +1,7 @@
import { Box, Text, Stack } from '@mantine/core';
import { useMemo } from 'react';
import { useLaunchConfigStore } from '@/stores/launchConfig';
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
import { usePreferencesStore } from '@/stores/preferences';
import { getServerInterfaceInfo } from '@/utils/interface';
import { TITLEBAR_HEIGHT, STATUSBAR_HEIGHT } from '@/constants';
@ -17,7 +17,7 @@ export const ServerTab = ({
activeTab,
}: ServerTabProps) => {
const { isImageGenerationMode } = useLaunchConfigStore();
const { frontendPreference } = useFrontendPreferenceStore();
const { frontendPreference } = usePreferencesStore();
const effectiveImageMode =
activeTab === 'chat-image'

View file

@ -14,8 +14,7 @@ import {
} from '@/constants';
import { handleTerminalOutput, processTerminalContent } from '@/utils/terminal';
import { useLaunchConfigStore } from '@/stores/launchConfig';
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
import { usePreferencesStore } from '@/stores/preferences';
interface TerminalTabProps {
onServerReady: (url: string) => void;
@ -28,8 +27,8 @@ export interface TerminalTabRef {
export const TerminalTab = forwardRef<TerminalTabRef, TerminalTabProps>(
({ onServerReady }, ref) => {
const { host, port, isImageGenerationMode } = useLaunchConfigStore();
const { frontendPreference } = useFrontendPreferenceStore();
const colorScheme = useAppColorScheme();
const { frontendPreference, resolvedColorScheme: colorScheme } =
usePreferencesStore();
const [terminalContent, setTerminalContent] = useState('');
const [isUserScrolling, setIsUserScrolling] = useState(false);
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);

View file

@ -5,7 +5,7 @@ import {
type TerminalTabRef,
} from '@/components/screens/Interface/TerminalTab';
import { useLaunchConfigStore } from '@/stores/launchConfig';
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
import { usePreferencesStore } from '@/stores/preferences';
import { getDefaultInterfaceTab } from '@/utils/interface';
import type { InterfaceTab } from '@/types';
@ -23,7 +23,7 @@ export const InterfaceScreen = ({
const terminalTabRef = useRef<TerminalTabRef>(null);
const { isTextMode, isImageGenerationMode } = useLaunchConfigStore();
const { frontendPreference } = useFrontendPreferenceStore();
const { frontendPreference } = usePreferencesStore();
const defaultInterfaceTab = useMemo(
() =>

View file

@ -207,7 +207,7 @@ export const AdvancedTab = () => {
</div>
<div>
<Group mb="md" justify="space-between">
<Group mb="xs" justify="space-between">
<Group>
<Text size="sm" fw={500}>
Additional arguments

View file

@ -298,14 +298,14 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
onChange={setActiveTab}
styles={{
root: {
maxHeight: '59vh',
maxHeight: '54vh',
display: 'flex',
flexDirection: 'column',
},
panel: {
flex: 1,
overflow: 'auto',
paddingTop: '1rem',
paddingTop: '0.75rem',
paddingRight: '0.5rem',
},
}}

View file

@ -111,7 +111,7 @@ export const AboutTab = () => {
</Text>
<Group gap="md" mt="md">
<Button
variant="subtle"
variant="light"
size="compact-sm"
leftSection={
<Github style={{ width: rem(16), height: rem(16) }} />

View file

@ -10,7 +10,7 @@ import {
} from '@mantine/core';
import { Sun, Moon, Monitor } from 'lucide-react';
import { useState, useEffect } from 'react';
import { useAppColorScheme } from '@/hooks/useAppColorScheme';
import { usePreferencesStore } from '@/stores/preferences';
import {
zoomLevelToPercentage,
percentageToZoomLevel,
@ -19,11 +19,13 @@ import {
import { ZOOM } from '@/constants';
export const AppearanceTab = () => {
const appColorScheme = useAppColorScheme();
const isDark = appColorScheme === 'dark';
const {
rawColorScheme,
resolvedColorScheme,
setColorScheme: setStoreColorScheme,
} = usePreferencesStore();
const isDark = resolvedColorScheme === 'dark';
const [rawColorScheme, setRawColorScheme] =
useState<MantineColorScheme>('auto');
const [zoomLevel, setZoomLevel] = useState<number>(ZOOM.DEFAULT_LEVEL);
const [zoomPercentage, setZoomPercentage] = useState(
ZOOM.DEFAULT_PERCENTAGE.toString()
@ -31,19 +33,12 @@ export const AppearanceTab = () => {
useEffect(() => {
const loadSettings = async () => {
const [currentZoom, savedColorScheme] = await Promise.all([
window.electronAPI.app.getZoomLevel(),
window.electronAPI.app.getColorScheme(),
]);
const currentZoom = await window.electronAPI.app.getZoomLevel();
if (typeof currentZoom === 'number') {
setZoomLevel(currentZoom);
setZoomPercentage(zoomLevelToPercentage(currentZoom).toString());
}
if (savedColorScheme) {
setRawColorScheme(savedColorScheme as MantineColorScheme);
}
};
void loadSettings();
@ -51,9 +46,7 @@ export const AppearanceTab = () => {
const handleColorSchemeChange = (value: string) => {
const newColorScheme = value as MantineColorScheme;
setRawColorScheme(newColorScheme);
void window.electronAPI.app.setColorScheme(newColorScheme);
void setStoreColorScheme(newColorScheme);
};
const handleZoomChange = (newZoomLevel: number) => {

View file

@ -12,7 +12,7 @@ import {
} from '@mantine/core';
import { Folder, FolderOpen, Monitor } from 'lucide-react';
import type { FrontendPreference } from '@/types';
import { useFrontendPreferenceStore } from '@/stores/frontendPreference';
import { usePreferencesStore } from '@/stores/preferences';
import { FRONTENDS } from '@/constants';
interface FrontendRequirement {
@ -37,8 +37,7 @@ export const GeneralTab = ({
isOnInterfaceScreen = false,
}: GeneralTabProps) => {
const [installDir, setInstallDir] = useState('');
const { frontendPreference, setFrontendPreference, loadFrontendPreference } =
useFrontendPreferenceStore();
const { frontendPreference, setFrontendPreference } = usePreferencesStore();
const [frontendRequirements, setFrontendRequirements] = useState<
Map<string, boolean>
>(new Map());
@ -120,7 +119,6 @@ export const GeneralTab = ({
const initialize = async () => {
await Promise.all([
loadCurrentInstallDir(),
loadFrontendPreference(),
checkAllFrontendRequirements(),
]);
};
@ -132,7 +130,7 @@ export const GeneralTab = ({
window.addEventListener('focus', handleFocus);
return () => window.removeEventListener('focus', handleFocus);
}, [checkAllFrontendRequirements, loadFrontendPreference]);
}, [checkAllFrontendRequirements]);
const getSelectedFrontendConfig = () =>
frontendConfigs.find((config) => config.value === frontendPreference);

View file

@ -1,32 +0,0 @@
import { useState, useEffect } from 'react';
type ColorScheme = 'light' | 'dark';
export const useAppColorScheme = () => {
const [colorScheme, setColorScheme] = useState<ColorScheme>('light');
useEffect(() => {
const loadColorScheme = async () => {
const rawScheme = await window.electronAPI.app.getColorScheme();
if (rawScheme) {
let resolvedScheme: ColorScheme;
if (rawScheme === 'auto') {
const prefersDark = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches;
resolvedScheme = prefersDark ? 'dark' : 'light';
} else {
resolvedScheme = rawScheme;
}
setColorScheme(resolvedScheme);
}
};
void loadColorScheme();
}, []);
return colorScheme;
};

View file

@ -15,7 +15,14 @@ import { getUvEnvironment } from './dependencies';
let openWebUIProcess: ChildProcess | null = null;
const OPENWEBUI_BASE_ARGS = ['--python', '3.11', 'open-webui@latest', 'serve'];
const OPENWEBUI_BASE_ARGS = [
'--python',
'3.11',
'--with',
'itsdangerous',
'open-webui@latest',
'serve',
];
on('SIGINT', () => {
void cleanup();

View file

@ -1,27 +0,0 @@
import { create } from 'zustand';
import type { FrontendPreference } from '@/types';
interface FrontendPreferenceStore {
frontendPreference: FrontendPreference;
setFrontendPreference: (preference: FrontendPreference) => void;
loadFrontendPreference: () => Promise<void>;
}
export const useFrontendPreferenceStore = create<FrontendPreferenceStore>(
(set) => ({
frontendPreference: 'koboldcpp',
setFrontendPreference: (preference: FrontendPreference) => {
set({ frontendPreference: preference });
window.electronAPI.config.set('frontendPreference', preference);
},
loadFrontendPreference: async () => {
const preference = (await window.electronAPI.config.get(
'frontendPreference'
)) as FrontendPreference;
set({ frontendPreference: preference || 'koboldcpp' });
},
})
);

60
src/stores/preferences.ts Normal file
View file

@ -0,0 +1,60 @@
import { create } from 'zustand';
import type { MantineColorScheme } from '@mantine/core';
import type { FrontendPreference } from '@/types';
type ResolvedColorScheme = 'light' | 'dark';
interface PreferencesStore {
frontendPreference: FrontendPreference;
rawColorScheme: MantineColorScheme;
resolvedColorScheme: ResolvedColorScheme;
setFrontendPreference: (preference: FrontendPreference) => void;
setColorScheme: (scheme: MantineColorScheme) => Promise<void>;
loadPreferences: () => Promise<void>;
}
const resolveColorScheme = (raw: MantineColorScheme): ResolvedColorScheme => {
if (raw === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
return raw;
};
export const usePreferencesStore = create<PreferencesStore>((set) => ({
frontendPreference: 'koboldcpp',
rawColorScheme: 'auto',
resolvedColorScheme: 'light',
setFrontendPreference: (preference: FrontendPreference) => {
set({ frontendPreference: preference });
window.electronAPI.config.set('frontendPreference', preference);
},
setColorScheme: async (scheme: MantineColorScheme) => {
set({
rawColorScheme: scheme,
resolvedColorScheme: resolveColorScheme(scheme),
});
await window.electronAPI.app.setColorScheme(scheme);
},
loadPreferences: async () => {
const [frontendPref, colorScheme] = await Promise.all([
window.electronAPI.config.get(
'frontendPreference'
) as Promise<FrontendPreference>,
window.electronAPI.app.getColorScheme(),
]);
set({
frontendPreference: frontendPref || 'koboldcpp',
rawColorScheme: colorScheme || 'auto',
resolvedColorScheme: resolveColorScheme(colorScheme || 'auto'),
});
},
}));
void usePreferencesStore.getState().loadPreferences();