diff --git a/package.json b/package.json index ceac4e8..a2a0718 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "gerbil", "productName": "Gerbil", - "version": "1.7.6", + "version": "1.7.7", "description": "Run Large Language Models locally", "main": "out/main/index.js", "homepage": "./", @@ -39,7 +39,7 @@ "license": "AGPL-3.0-or-later", "devDependencies": { "@eslint/js": "^9.38.0", - "@types/node": "^24.9.1", + "@types/node": "^24.9.2", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@types/yauzl": "^2.10.3", @@ -47,7 +47,7 @@ "@typescript-eslint/parser": "^8.46.2", "@vitejs/plugin-react": "^5.1.0", "cross-env": "^10.1.0", - "electron": "^38.4.0", + "electron": "^39.0.0", "electron-builder": "^26.0.12", "electron-vite": "^4.0.1", "eslint": "^9.38.0", diff --git a/src/components/App/TitleBar.tsx b/src/components/App/TitleBar.tsx index 20a4c31..5e2c64d 100644 --- a/src/components/App/TitleBar.tsx +++ b/src/components/App/TitleBar.tsx @@ -32,19 +32,17 @@ export const TitleBar = ({ onEject, onTabChange, }: TitleBarProps) => { - const { resolvedColorScheme: colorScheme, frontendPreference } = - usePreferencesStore(); + const { + resolvedColorScheme: colorScheme, + frontendPreference, + imageGenerationFrontendPreference, + } = 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 interfaceOptions = getAvailableInterfaceOptions({ - frontendPreference, - isTextMode, - isImageGenerationMode, - }); const handleTabChange = (value: string | null) => { if (value === 'eject') { @@ -133,7 +131,12 @@ export const TitleBar = ({ onChange={handleTabChange} onDropdownOpen={() => setIsSelectOpen(true)} onDropdownClose={() => setIsSelectOpen(false)} - data={interfaceOptions} + data={getAvailableInterfaceOptions({ + frontendPreference, + imageGenerationFrontendPreference, + isTextMode, + isImageGenerationMode, + })} renderOption={renderOption} variant="unstyled" style={{ textAlign: 'center', minWidth: '7.5rem' }} diff --git a/src/components/screens/Interface/ServerTab.tsx b/src/components/screens/Interface/ServerTab.tsx index c0ecfe4..aee4a04 100644 --- a/src/components/screens/Interface/ServerTab.tsx +++ b/src/components/screens/Interface/ServerTab.tsx @@ -17,7 +17,8 @@ export const ServerTab = ({ activeTab, }: ServerTabProps) => { const { isImageGenerationMode } = useLaunchConfigStore(); - const { frontendPreference } = usePreferencesStore(); + const { frontendPreference, imageGenerationFrontendPreference } = + usePreferencesStore(); const effectiveImageMode = activeTab === 'chat-image' @@ -30,10 +31,16 @@ export const ServerTab = ({ () => getServerInterfaceInfo({ frontendPreference, + imageGenerationFrontendPreference, isImageGenerationMode: effectiveImageMode, serverUrl: serverUrl || '', }), - [frontendPreference, effectiveImageMode, serverUrl] + [ + frontendPreference, + imageGenerationFrontendPreference, + effectiveImageMode, + serverUrl, + ] ); if (!isServerReady || !serverUrl) { diff --git a/src/components/screens/Interface/TerminalTab.tsx b/src/components/screens/Interface/TerminalTab.tsx index ab850c6..cdca48d 100644 --- a/src/components/screens/Interface/TerminalTab.tsx +++ b/src/components/screens/Interface/TerminalTab.tsx @@ -26,9 +26,13 @@ export interface TerminalTabRef { export const TerminalTab = forwardRef( ({ onServerReady }, ref) => { - const { host, port, isImageGenerationMode } = useLaunchConfigStore(); - const { frontendPreference, resolvedColorScheme: colorScheme } = - usePreferencesStore(); + const { host, port, isImageGenerationMode, isTextMode } = + useLaunchConfigStore(); + const { + frontendPreference, + imageGenerationFrontendPreference, + resolvedColorScheme: colorScheme, + } = usePreferencesStore(); const [terminalContent, setTerminalContent] = useState(''); const [isUserScrolling, setIsUserScrolling] = useState(false); const [shouldAutoScroll, setShouldAutoScroll] = useState(true); @@ -79,16 +83,19 @@ export const TerminalTab = forwardRef( const serverHost = host || 'localhost'; const serverPort = port || 5001; + const effectiveFrontend = isTextMode + ? frontendPreference + : imageGenerationFrontendPreference === 'builtin' + ? 'koboldcpp' + : frontendPreference; + let signalToCheck: string = SERVER_READY_SIGNALS.KOBOLDCPP; - if (frontendPreference === 'sillytavern') { + if (effectiveFrontend === 'sillytavern') { signalToCheck = SERVER_READY_SIGNALS.SILLYTAVERN; - } else if (frontendPreference === 'openwebui') { + } else if (effectiveFrontend === 'openwebui') { signalToCheck = SERVER_READY_SIGNALS.OPENWEBUI; - } else if ( - frontendPreference === 'comfyui' && - isImageGenerationMode - ) { + } else if (effectiveFrontend === 'comfyui') { signalToCheck = SERVER_READY_SIGNALS.COMFYUI; } @@ -106,7 +113,15 @@ export const TerminalTab = forwardRef( ); return cleanup; - }, [onServerReady, host, port, frontendPreference, isImageGenerationMode]); + }, [ + onServerReady, + host, + port, + frontendPreference, + imageGenerationFrontendPreference, + isImageGenerationMode, + isTextMode, + ]); const scrollToBottom = () => { if (viewportRef.current) { diff --git a/src/components/screens/Interface/index.tsx b/src/components/screens/Interface/index.tsx index ed53a16..80ea72b 100644 --- a/src/components/screens/Interface/index.tsx +++ b/src/components/screens/Interface/index.tsx @@ -23,16 +23,23 @@ export const InterfaceScreen = ({ const terminalTabRef = useRef(null); const { isTextMode, isImageGenerationMode } = useLaunchConfigStore(); - const { frontendPreference } = usePreferencesStore(); + const { frontendPreference, imageGenerationFrontendPreference } = + usePreferencesStore(); const defaultInterfaceTab = useMemo( () => getDefaultInterfaceTab({ frontendPreference, + imageGenerationFrontendPreference, isTextMode, isImageGenerationMode, }), - [frontendPreference, isTextMode, isImageGenerationMode] + [ + frontendPreference, + imageGenerationFrontendPreference, + isTextMode, + isImageGenerationMode, + ] ); const handleServerReady = useCallback( diff --git a/src/components/settings/FrontendInterfaceSelector.tsx b/src/components/settings/FrontendInterfaceSelector.tsx index 21bc633..7300024 100644 --- a/src/components/settings/FrontendInterfaceSelector.tsx +++ b/src/components/settings/FrontendInterfaceSelector.tsx @@ -1,8 +1,11 @@ import { useState, useEffect, useCallback, useMemo } from 'react'; import { Text, Box, Anchor, rem } from '@mantine/core'; -import { Monitor } from 'lucide-react'; +import { Monitor, Image } from 'lucide-react'; import { usePreferencesStore } from '@/stores/preferences'; -import type { FrontendPreference } from '@/types'; +import type { + FrontendPreference, + ImageGenerationFrontendPreference, +} from '@/types'; import { FRONTENDS } from '@/constants'; import { Select } from '@/components/Select'; @@ -27,7 +30,12 @@ interface FrontendInterfaceSelectorProps { export const FrontendInterfaceSelector = ({ isOnInterfaceScreen = false, }: FrontendInterfaceSelectorProps) => { - const { frontendPreference, setFrontendPreference } = usePreferencesStore(); + const { + frontendPreference, + setFrontendPreference, + imageGenerationFrontendPreference, + setImageGenerationFrontendPreference, + } = usePreferencesStore(); const [frontendRequirements, setFrontendRequirements] = useState< Map @@ -132,6 +140,12 @@ export const FrontendInterfaceSelector = ({ setFrontendPreference(value as FrontendPreference); }; + const handleImageGenerationFrontendChange = (value: string | null) => { + setImageGenerationFrontendPreference( + value as ImageGenerationFrontendPreference + ); + }; + const renderDisabledFrontendWarnings = () => { const disabledFrontends = frontendConfigs.filter( (config) => !isFrontendAvailable(config.value) @@ -266,6 +280,25 @@ export const FrontendInterfaceSelector = ({ /> {renderDisabledFrontendWarnings()} + + + Image Generation Frontend + + + + Choose which frontend to use for image generation specifically + + +