From 8c1082627fcea571adb8e3c455f4d53a2b8a0468 Mon Sep 17 00:00:00 2001 From: lone-cloud Date: Sun, 12 Apr 2026 15:35:33 -0700 Subject: [PATCH] UI updates based on impeccable passes --- .gitignore | 2 + package.json | 4 +- pnpm-lock.yaml | 8 +- src/components/App/PerformanceBadge.tsx | 29 ++-- src/components/App/Router.tsx | 4 +- src/components/App/ScreenTransition.tsx | 2 + src/components/App/StatusBar.tsx | 14 +- src/components/App/TitleBar.tsx | 13 +- src/components/App/UpdateAvailableModal.tsx | 2 +- src/components/App/UpdateButton.tsx | 1 - src/components/DownloadCard.tsx | 14 +- src/components/InfoTooltip.tsx | 2 +- src/components/Notepad/Container.tsx | 36 +++-- src/components/Notepad/Editor.tsx | 18 ++- src/components/Notepad/Tab.tsx | 31 ++--- src/components/Notepad/Tabs.tsx | 30 +++-- src/components/Switch.tsx | 4 +- src/components/WarningDisplay.tsx | 10 +- src/components/screens/Download.tsx | 112 ++++++++-------- .../screens/Interface/TerminalTab.tsx | 22 +--- src/components/screens/Launch/AdvancedTab.tsx | 3 +- .../Launch/CommandLineArgumentsModal.tsx | 8 +- .../GeneralTab/AccelerationSelectItem.tsx | 2 +- .../HuggingFaceSearchModal/ModelCard.tsx | 3 +- .../HuggingFaceSearchModal/ModelsTable.tsx | 2 +- .../Launch/HuggingFaceSearchModal/index.tsx | 8 +- .../screens/Launch/ModelFileField.tsx | 10 +- src/components/screens/Launch/index.tsx | 2 +- src/components/screens/Welcome.tsx | 124 ++++++------------ src/components/settings/AboutTab.tsx | 10 +- src/components/settings/AppearanceTab.tsx | 28 ++-- src/components/settings/BackendsTab.tsx | 2 +- src/components/settings/SettingsModal.tsx | 19 +-- src/styles/index.css | 40 ++++-- src/theme.ts | 24 +++- src/utils/terminal.ts | 2 +- 36 files changed, 320 insertions(+), 325 deletions(-) diff --git a/.gitignore b/.gitignore index be29237..b38af50 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ dist/ dist-electron/ out/ release/ +.agents/ +.impeccable.md *.log .DS_Store Thumbs.db diff --git a/package.json b/package.json index 9269865..717775e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gerbil", - "version": "1.22.1", + "version": "1.23.0", "description": "Run Large Language Models locally", "keywords": [ "ai", @@ -42,7 +42,7 @@ "@codemirror/search": "^6.6.0", "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.41.0", - "@fontsource/inter": "^5.2.8", + "@fontsource/geist": "^5.2.8", "@huggingface/gguf": "^0.4.2", "@mantine/core": "^9.0.1", "@mantine/hooks": "^9.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa06bf7..c8770d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,7 +42,7 @@ importers: '@codemirror/view': specifier: ^6.41.0 version: 6.41.0 - '@fontsource/inter': + '@fontsource/geist': specifier: ^5.2.8 version: 5.2.8 '@huggingface/gguf': @@ -489,8 +489,8 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@fontsource/inter@5.2.8': - resolution: {integrity: sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==} + '@fontsource/geist@5.2.8': + resolution: {integrity: sha512-pI54klK6vz8fVpAVyV+iODGLEzw73cs1XJqV9PIXgW8MYMmBcwK6lKKaWqJrNHwEx8ppz9cuhussb6arXQ/PVQ==} '@huggingface/gguf@0.4.2': resolution: {integrity: sha512-pog57zGj/u8uZ6cFi2vH9MkWvq+ZtFDa5iUeNxggDSle+04htJQFbl31TcauGUhnxTBJokFexWK9WL2BakLR0Q==} @@ -3530,7 +3530,7 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@fontsource/inter@5.2.8': {} + '@fontsource/geist@5.2.8': {} '@huggingface/gguf@0.4.2': dependencies: diff --git a/src/components/App/PerformanceBadge.tsx b/src/components/App/PerformanceBadge.tsx index d2349de..6b7018b 100644 --- a/src/components/App/PerformanceBadge.tsx +++ b/src/components/App/PerformanceBadge.tsx @@ -1,6 +1,17 @@ import { ActionIcon, Button, Tooltip } from '@mantine/core'; import { Activity } from 'lucide-react'; +const BADGE_STYLE = { + borderRadius: '0.75rem', + fontSize: '0.7em', + fontWeight: 500, + height: 'auto', + margin: '0.125rem 0', + minWidth: '5rem', + padding: '0.25rem 0.5rem', + textAlign: 'center', +} as const; + interface PerformanceBadgeProps { label?: string; value?: string; @@ -25,7 +36,12 @@ export const PerformanceBadge = ({ if (iconOnly) { return ( - void handlePerformanceClick()}> + void handlePerformanceClick()} + > @@ -37,16 +53,7 @@ export const PerformanceBadge = ({ {searchParams && ( - setSearchModalOpened(true)} variant="outline" size="lg"> + setSearchModalOpened(true)} + variant="outline" + size="lg" + aria-label="Search Hugging Face" + > @@ -156,9 +161,10 @@ export const ModelFileField = ({ void handleAnalyzeModel()} variant="light" - color="blue" + color="brand" size="lg" disabled={validationState === 'neutral' || validationState === 'invalid'} + aria-label="Analyze model" > diff --git a/src/components/screens/Launch/index.tsx b/src/components/screens/Launch/index.tsx index ccb0bdf..e9fd6ac 100644 --- a/src/components/screens/Launch/index.tsx +++ b/src/components/screens/Launch/index.tsx @@ -425,7 +425,7 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => { onClick={handleLaunchClick} size="lg" variant="filled" - color="blue" + color="brand" style={{ fontSize: '1em', fontWeight: 600, diff --git a/src/components/screens/Welcome.tsx b/src/components/screens/Welcome.tsx index 2defb91..77c5452 100644 --- a/src/components/screens/Welcome.tsx +++ b/src/components/screens/Welcome.tsx @@ -1,17 +1,5 @@ import iconUrl from '/icon.png'; -import { - Button, - Card, - Container, - Group, - Image, - List, - Stack, - Text, - ThemeIcon, - Title, -} from '@mantine/core'; -import { Check } from 'lucide-react'; +import { Button, Container, Group, Image, List, Stack, Text, Title } from '@mantine/core'; import { PRODUCT_NAME } from '@/constants'; @@ -21,77 +9,51 @@ interface WelcomeScreenProps { export const WelcomeScreen = ({ onGetStarted }: WelcomeScreenProps) => ( - - - - - - {PRODUCT_NAME} - - {PRODUCT_NAME} - - - - Run Large Language Models locally - - + + + + {PRODUCT_NAME} + {PRODUCT_NAME} + + + Run Large Language Models locally on your hardware. + + - - - - - } - > - - - - Chat with AI models - {' '} - - Have conversations, ask questions, get help with writing - - - - - - Generate images - {' '} - - Easily create artwork and illustrations with LLMs - - - - - - Complete privacy - {' '} - - Everything runs on your computer, no data sent to servers - - - - - - Hardware acceleration - {' '} - - Supports CUDA, ROCm and Vulkan backends - - - + + + + Chat + {' '} + — conversations, questions, writing help + + + + Image generation + {' '} + — artwork and illustrations via LLMs + + + + Private + {' '} + — everything runs locally, no data leaves the machine + + + + Accelerated + {' '} + — CUDA, ROCm, and Vulkan backends supported + + - - Hardware acceleration requires appropriate drivers to be manually installed (CUDA for - NVIDIA GPUs, ROCm for AMD GPUs, etc.) - - + + Hardware acceleration requires drivers installed separately (CUDA for NVIDIA, ROCm for AMD). + - - - + ); diff --git a/src/components/settings/AboutTab.tsx b/src/components/settings/AboutTab.tsx index b0b3651..8194167 100644 --- a/src/components/settings/AboutTab.tsx +++ b/src/components/settings/AboutTab.tsx @@ -59,7 +59,7 @@ export const AboutTab = () => { {PRODUCT_NAME} - + v{versionInfo.appVersion} @@ -84,17 +84,17 @@ export const AboutTab = () => { - - + + About {PRODUCT_NAME} - + {PRODUCT_NAME} is a user-friendly desktop application that makes it easy to run large language models locally on your machine. Whether you're looking to chat with AI models, generate images, or explore different interfaces like SillyTavern and Open WebUI,{' '} {PRODUCT_NAME} provides a streamlined experience for local AI. - + ); }; diff --git a/src/components/settings/AppearanceTab.tsx b/src/components/settings/AppearanceTab.tsx index 219ab7c..ba50804 100644 --- a/src/components/settings/AppearanceTab.tsx +++ b/src/components/settings/AppearanceTab.tsx @@ -2,6 +2,7 @@ import { Group, rem, SegmentedControl, Slider, Stack, Text, TextInput } from '@m import type { MantineColorScheme } from '@mantine/core'; import { Monitor, Moon, Sun } from 'lucide-react'; import { useEffect, useState } from 'react'; +import type { CSSProperties } from 'react'; import { FrontendInterfaceSelector } from '@/components/settings/FrontendInterfaceSelector'; import { ZOOM } from '@/constants'; @@ -13,13 +14,7 @@ interface AppearanceTabProps { } export const AppearanceTab = ({ isOnInterfaceScreen = false }: AppearanceTabProps) => { - const { - rawColorScheme, - resolvedColorScheme, - setColorScheme: setStoreColorScheme, - } = usePreferencesStore(); - const isDark = resolvedColorScheme === 'dark'; - + const { rawColorScheme, setColorScheme: setStoreColorScheme } = usePreferencesStore(); const [zoomLevel, setZoomLevel] = useState(ZOOM.DEFAULT_LEVEL as number); const [zoomPercentage, setZoomPercentage] = useState(ZOOM.DEFAULT_PERCENTAGE.toString()); @@ -74,15 +69,16 @@ export const AppearanceTab = ({ isOnInterfaceScreen = false }: AppearanceTabProp fullWidth value={rawColorScheme} onChange={handleColorSchemeChange} - styles={(theme) => ({ - indicator: { - backgroundColor: isDark ? theme.colors.dark[5] : theme.colors.gray[2], - border: `1px solid ${isDark ? theme.colors.dark[4] : theme.colors.gray[4]}`, - }, - root: { - border: `0.5px solid ${isDark ? theme.colors.dark[4] : theme.colors.gray[4]}`, - }, - })} + style={ + { + '--sc-indicator-color': + 'light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5))', + '--sc-indicator-border': + 'light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))', + border: + '0.5px solid light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))', + } as CSSProperties + } data={[ { label: ( diff --git a/src/components/settings/BackendsTab.tsx b/src/components/settings/BackendsTab.tsx index 47076d4..a8bd86a 100644 --- a/src/components/settings/BackendsTab.tsx +++ b/src/components/settings/BackendsTab.tsx @@ -244,7 +244,7 @@ export const BackendsTab = () => { target="_blank" rel="noopener noreferrer" size="sm" - c="blue" + c="brand" > Release notes diff --git a/src/components/settings/SettingsModal.tsx b/src/components/settings/SettingsModal.tsx index c5c5586..c86a70f 100644 --- a/src/components/settings/SettingsModal.tsx +++ b/src/components/settings/SettingsModal.tsx @@ -8,7 +8,7 @@ import { SlidersHorizontal, Wrench, } from 'lucide-react'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { Modal } from '@/components/Modal'; import { AboutTab } from '@/components/settings/AboutTab'; @@ -38,23 +38,6 @@ export const SettingsModal = ({ const effectiveActiveTab = !showBackendsTab && activeTab === 'backends' ? 'general' : activeTab; - useEffect(() => { - if (opened) { - const originalOverflow = document.body.style.overflow; - const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; - - document.body.style.overflow = 'hidden'; - document.body.style.paddingRight = `${scrollbarWidth}px`; - - return () => { - document.body.style.overflow = originalOverflow; - document.body.style.paddingRight = ''; - }; - } - - return undefined; - }, [opened]); - return ( { variables: { ...v8.variables }, dark: { ...v8.dark, - '--mantine-color-body': '#0f0f0f', - '--mantine-color-default-border': '#2a2a2a', + '--mantine-color-body': 'oklch(12% 0.008 240)', + '--mantine-color-default-border': 'oklch(22% 0.008 240)', + '--gerbil-link-color': 'var(--mantine-color-brand-4)', }, light: { ...v8.light, '--mantine-color-body': '#fafafa', '--mantine-color-white': '#fafafa', '--mantine-color-default-border': '#dee2e6', + '--gerbil-link-color': 'var(--mantine-color-brand-5)', }, }; }; diff --git a/src/utils/terminal.ts b/src/utils/terminal.ts index c5ed135..174312c 100644 --- a/src/utils/terminal.ts +++ b/src/utils/terminal.ts @@ -15,7 +15,7 @@ const linkifyText = (text: string) => const cleanUrl = url.replace(/[.,;:!?]+$/, ''); const trailingPunctuation = url.slice(cleanUrl.length); - return `${cleanUrl}${trailingPunctuation}`; + return `${cleanUrl}${trailingPunctuation}`; }); const escapeHtmlExceptLinks = (text: string) => {