diff --git a/package.json b/package.json index 1e0551f..9dad989 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "gerbil", "productName": "Gerbil", - "version": "0.9.1", + "version": "0.9.2", "description": "Run Large Language Models locally", "main": "out/main/index.js", "homepage": "./", @@ -80,8 +80,8 @@ "vite": "^7.1.3" }, "dependencies": { - "@mantine/core": "^8.2.7", - "@mantine/hooks": "^8.2.7", + "@mantine/core": "^8.2.8", + "@mantine/hooks": "^8.2.8", "axios": "^1.11.0", "execa": "^9.6.0", "lucide-react": "^0.542.0", diff --git a/src/App.tsx b/src/App.tsx index f2b7184..01d4f2a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,11 +8,11 @@ import { UpdateAvailableModal } from '@/components/UpdateAvailableModal'; import { SettingsModal } from '@/components/settings/SettingsModal'; import { EjectConfirmModal } from '@/components/EjectConfirmModal'; import { ScreenTransition } from '@/components/ScreenTransition'; -import { AppHeader } from '@/components/AppHeader'; +import { TitleBar } from '@/components/TitleBar'; import { useUpdateChecker } from '@/hooks/useUpdateChecker'; import { useKoboldVersions } from '@/hooks/useKoboldVersions'; -import { UI } from '@/constants'; import { Logger } from '@/utils/logger'; +import { TITLEBAR_HEIGHT } from '@/constants'; import type { DownloadItem } from '@/types/electron'; import type { InterfaceTab, FrontendPreference, Screen } from '@/types'; @@ -149,28 +149,24 @@ export const App = () => { return ( - {currentScreen !== 'welcome' && ( - + setSettingsOpened(true)} + onOpenSettings={() => setSettingsOpened(true)} /> - )} + {currentScreen === null ? ( diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx deleted file mode 100644 index 89cda31..0000000 --- a/src/components/AppHeader.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { useState } from 'react'; -import { - AppShell, - Group, - ActionIcon, - rem, - Button, - Select, - Title, - Image, - Tooltip, -} from '@mantine/core'; -import { Settings, ArrowLeft, CircleFadingArrowUp } from 'lucide-react'; -import { soundAssets, playSound, initializeAudio } from '@/utils/sounds'; -import { useAppUpdateChecker } from '@/hooks/useAppUpdateChecker'; -import iconUrl from '/icon.png'; -import { FRONTENDS } from '@/constants'; -import type { InterfaceTab, FrontendPreference, Screen } from '@/types'; - -interface AppHeaderProps { - currentScreen: Screen | null; - activeInterfaceTab: InterfaceTab; - setActiveInterfaceTab: (tab: InterfaceTab) => void; - isImageGenerationMode: boolean; - frontendPreference: FrontendPreference; - onEject: () => void; - onSettingsOpen: () => void; -} - -export const AppHeader = ({ - currentScreen, - activeInterfaceTab, - setActiveInterfaceTab, - isImageGenerationMode, - frontendPreference, - onEject, - onSettingsOpen, -}: AppHeaderProps) => { - const [logoClickCount, setLogoClickCount] = useState(0); - const [isElephantMode, setIsElephantMode] = useState(false); - const [isMouseSqueaking, setIsMouseSqueaking] = useState(false); - const { hasUpdate, openReleasePage } = useAppUpdateChecker(); - - const handleLogoClick = async () => { - await initializeAudio(); - setLogoClickCount((prev) => prev + 1); - - try { - if (logoClickCount >= 10 && Math.random() < 0.1) { - setIsElephantMode(true); - await playSound(soundAssets.elephant, 0.6); - - setTimeout(() => { - setIsElephantMode(false); - }, 1500); - } else { - setIsMouseSqueaking(true); - const squeakNumber = Math.floor(Math.random() * 5); - await playSound(soundAssets.mouseSqueaks[squeakNumber], 0.4); - - setTimeout(() => { - setIsMouseSqueaking(false); - }, 300); - } - } catch { - void 0; - } - }; - - return ( - - -
- {currentScreen === 'interface' ? ( - - ) : ( - - Gerbil - - Gerbil - - - )} -
- - {currentScreen === 'interface' && ( - { + if (value === 'eject') { + onEject?.(); + } else { + onTabChange?.(value as InterfaceTab); + } + }} + data={[ + { + value: 'chat', + label: 'Chat', + }, + { value: 'terminal', label: 'Terminal' }, + { value: 'eject', label: 'Eject' }, + ]} + allowDeselect={false} + variant="unstyled" + size="sm" + style={{ + textAlign: 'center', + minWidth: '120px', + }} + styles={{ + input: { + textAlign: 'center', + backgroundColor: 'transparent', + border: 'none', + fontWeight: 500, + userSelect: 'none', + cursor: 'pointer', + }, + }} + /> + )} + + + + {hasUpdate && ( + + + + )} + + + + + + + + {[ + { + icon: , + onClick: handleMinimize, + color: undefined, + label: 'Minimize window', + }, + { + icon: isMaximized ? : , + onClick: handleMaximize, + color: undefined, + label: isMaximized ? 'Restore window' : 'Maximize window', + }, + { + icon: , + onClick: handleClose, + color: 'red' as const, + label: 'Close window', + }, + ].map((button, index) => ( + + {button.icon} + + ))} + + + ); +}; diff --git a/src/components/screens/Interface/ServerTab.tsx b/src/components/screens/Interface/ServerTab.tsx index 9730e6a..56df6ea 100644 --- a/src/components/screens/Interface/ServerTab.tsx +++ b/src/components/screens/Interface/ServerTab.tsx @@ -1,6 +1,6 @@ import { useRef } from 'react'; import { Box, Text, Stack } from '@mantine/core'; -import { UI, SILLYTAVERN, FRONTENDS } from '@/constants'; +import { SILLYTAVERN, FRONTENDS } from '@/constants'; import type { ServerTabMode, FrontendPreference } from '@/types'; interface ServerTabProps { @@ -60,7 +60,7 @@ export const ServerTab = ({ diff --git a/src/components/screens/Interface/TerminalTab.tsx b/src/components/screens/Interface/TerminalTab.tsx index 6e33b03..8ad9b3c 100644 --- a/src/components/screens/Interface/TerminalTab.tsx +++ b/src/components/screens/Interface/TerminalTab.tsx @@ -8,7 +8,7 @@ import { } from '@mantine/core'; import { ChevronDown } from 'lucide-react'; import styles from '@/styles/layout.module.css'; -import { UI, SERVER_READY_SIGNALS } from '@/constants'; +import { SERVER_READY_SIGNALS } from '@/constants'; import { handleTerminalOutput, processTerminalContent } from '@/utils/terminal'; import { useLaunchConfigStore } from '@/stores/launchConfigStore'; import type { FrontendPreference } from '@/types'; @@ -104,7 +104,7 @@ export const TerminalTab = ({ return ( { disabled={!isGpuBackend} />
+ + + + + + @@ -177,41 +203,6 @@ export const AdvancedTab = () => { -
- - - Hardware Compatibility - - - - - - - - - -
-
diff --git a/src/components/screens/Launch/GeneralTab/index.tsx b/src/components/screens/Launch/GeneralTab/index.tsx index 8b9d6f7..30314b4 100644 --- a/src/components/screens/Launch/GeneralTab/index.tsx +++ b/src/components/screens/Launch/GeneralTab/index.tsx @@ -55,6 +55,7 @@ export const GeneralTab = () => { max={131072} step={1} onChange={handleContextSizeChangeWithStep} + style={{ marginBottom: '0.5rem' }} />
diff --git a/src/components/screens/Launch/index.tsx b/src/components/screens/Launch/index.tsx index fa70297..c506d5f 100644 --- a/src/components/screens/Launch/index.tsx +++ b/src/components/screens/Launch/index.tsx @@ -314,7 +314,7 @@ export const LaunchScreen = ({ onChange={setActiveTab} styles={{ root: { - maxHeight: '22rem', + maxHeight: '24rem', display: 'flex', flexDirection: 'column', }, @@ -350,7 +350,7 @@ export const LaunchScreen = ({ - + - - - - - - diff --git a/src/preload/index.ts b/src/preload/index.ts index 1d8723e..285b39c 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -78,6 +78,10 @@ const koboldAPI: KoboldAPI = { const appAPI: AppAPI = { openExternal: (url) => ipcRenderer.invoke('app:openExternal', url), getVersion: () => ipcRenderer.invoke('app:getVersion'), + getVersionInfo: () => ipcRenderer.invoke('app:getVersionInfo'), + minimizeWindow: () => ipcRenderer.invoke('app:minimizeWindow'), + maximizeWindow: () => ipcRenderer.invoke('app:maximizeWindow'), + closeWindow: () => ipcRenderer.invoke('app:closeWindow'), }; const configAPI: ConfigAPI = { diff --git a/src/styles/index.css b/src/styles/index.css index fe06612..ac1eec1 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -45,38 +45,38 @@ body { } } -/* AppHeader animations */ +/* TitleBar gerbil icon animations */ @keyframes elephantShake { 0%, 100% { - transform: scale(1.3) rotate(5deg) translateX(0); + transform: scale(1.1) rotate(5deg) translateX(0); } 10% { - transform: scale(1.4) rotate(-3deg) translateX(-2px); + transform: scale(1.2) rotate(-3deg) translateX(-2px); } 20% { - transform: scale(1.5) rotate(8deg) translateX(2px); + transform: scale(1.3) rotate(8deg) translateX(2px); } 30% { - transform: scale(1.4) rotate(-5deg) translateX(-1px); + transform: scale(1.2) rotate(-5deg) translateX(-1px); } 40% { - transform: scale(1.6) rotate(10deg) translateX(3px); + transform: scale(1.4) rotate(10deg) translateX(3px); } 50% { - transform: scale(1.3) rotate(-8deg) translateX(-2px); + transform: scale(1.2) rotate(-8deg) translateX(-2px); } 60% { - transform: scale(1.5) rotate(6deg) translateX(1px); + transform: scale(1.3) rotate(6deg) translateX(1px); } 70% { - transform: scale(1.2) rotate(-4deg) translateX(-1px); + transform: scale(1) rotate(-4deg) translateX(-1px); } 80% { - transform: scale(1.4) rotate(3deg) translateX(2px); + transform: scale(1.2) rotate(3deg) translateX(2px); } 90% { - transform: scale(1.1) rotate(-2deg) translateX(-1px); + transform: scale(1) rotate(-2deg) translateX(-1px); } } diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index 5c31dc3..c7affc0 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -131,9 +131,24 @@ export interface KoboldAPI { removeAllListeners: (channel: string) => void; } +export interface VersionInfo { + appVersion: string; + electronVersion: string; + nodeVersion: string; + chromeVersion: string; + v8Version: string; + osVersion: string; + platform: string; + arch: string; +} + export interface AppAPI { openExternal: (url: string) => Promise; getVersion: () => Promise; + getVersionInfo: () => Promise; + minimizeWindow: () => void; + maximizeWindow: () => void; + closeWindow: () => void; } export interface ConfigAPI { diff --git a/yarn.lock b/yarn.lock index e647f7e..07cb924 100644 --- a/yarn.lock +++ b/yarn.lock @@ -871,9 +871,9 @@ __metadata: languageName: node linkType: hard -"@mantine/core@npm:^8.2.7": - version: 8.2.7 - resolution: "@mantine/core@npm:8.2.7" +"@mantine/core@npm:^8.2.8": + version: 8.2.8 + resolution: "@mantine/core@npm:8.2.8" dependencies: "@floating-ui/react": "npm:^0.26.28" clsx: "npm:^2.1.1" @@ -882,19 +882,19 @@ __metadata: react-textarea-autosize: "npm:8.5.9" type-fest: "npm:^4.27.0" peerDependencies: - "@mantine/hooks": 8.2.7 + "@mantine/hooks": 8.2.8 react: ^18.x || ^19.x react-dom: ^18.x || ^19.x - checksum: 10c0/d10ba6ef7ac552b6bd5638b9a16c4d2405391102330d94c8df7a7d87756cdf4ec341a350eab252575eb5990f43dc4122ea13244753a9169338a43d2b996f7594 + checksum: 10c0/9933ae22ab2faa852d8a941bdbb427c98d160b253f2977f10aad7024cd725fbca56d36a22560f3c75f8716b3d6651a599fa6d996cb131315c997242206cc4ebb languageName: node linkType: hard -"@mantine/hooks@npm:^8.2.7": - version: 8.2.7 - resolution: "@mantine/hooks@npm:8.2.7" +"@mantine/hooks@npm:^8.2.8": + version: 8.2.8 + resolution: "@mantine/hooks@npm:8.2.8" peerDependencies: react: ^18.x || ^19.x - checksum: 10c0/54dfbc36acad9dbb5e3a29d0944041f7ff31e61416cbd26ecf97c37c3a779a1b0028e05a0e6daa44cf3c3c2406c50b8f2107eb6fee3cfb6647b7524965556686 + checksum: 10c0/0097d8f05ee684c0e78f6160aa23d431e27ef15d32c66010f369d35e86497571a52dc56824fb659ca1c66ff3dd9e6513fe4c8c9bb3e1d98118ba176b4b9feeeb languageName: node linkType: hard @@ -3706,8 +3706,8 @@ __metadata: resolution: "gerbil@workspace:." dependencies: "@eslint/js": "npm:^9.34.0" - "@mantine/core": "npm:^8.2.7" - "@mantine/hooks": "npm:^8.2.7" + "@mantine/core": "npm:^8.2.8" + "@mantine/hooks": "npm:^8.2.8" "@types/node": "npm:^24.3.0" "@types/react": "npm:^19.1.12" "@types/react-dom": "npm:^19.1.9"