mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
minor styling updates, workaround recent openweb ui bug (on their side),
This commit is contained in:
parent
068539d20a
commit
02ce36b8ec
17 changed files with 106 additions and 105 deletions
|
|
@ -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": "./",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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 = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
() =>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -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) }} />
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
60
src/stores/preferences.ts
Normal 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();
|
||||
Loading…
Add table
Reference in a new issue