feat: UI tweaks including dark mode fixes, FOUC flash fixes, suspenseful loading states, and 7 more (#2079)

This commit is contained in:
Best Codes
2025-04-09 16:54:12 -05:00
committed by GitHub
parent 1d74f538ef
commit 0daff53110
10 changed files with 101 additions and 72 deletions
+5 -2
View File
@@ -19,6 +19,7 @@ import { SharedSessionDetails } from './sharedSessions';
import WelcomeView from './components/WelcomeView'; import WelcomeView from './components/WelcomeView';
import ChatView from './components/ChatView'; import ChatView from './components/ChatView';
import SuspenseLoader from './suspense-loader';
import SettingsView, { type SettingsViewOptions } from './components/settings/SettingsView'; import SettingsView, { type SettingsViewOptions } from './components/settings/SettingsView';
import SettingsViewV2 from './components/settings_v2/SettingsView'; import SettingsViewV2 from './components/settings_v2/SettingsView';
import MoreModelsView from './components/settings/models/MoreModelsView'; import MoreModelsView from './components/settings/models/MoreModelsView';
@@ -44,7 +45,8 @@ export type View =
| 'ConfigureProviders' | 'ConfigureProviders'
| 'settingsV2' | 'settingsV2'
| 'sessions' | 'sessions'
| 'sharedSession'; | 'sharedSession'
| 'loading';
export type ViewConfig = { export type ViewConfig = {
view: View; view: View;
@@ -62,7 +64,7 @@ export default function App() {
const [pendingLink, setPendingLink] = useState<string | null>(null); const [pendingLink, setPendingLink] = useState<string | null>(null);
const [modalMessage, setModalMessage] = useState<string>(''); const [modalMessage, setModalMessage] = useState<string>('');
const [{ view, viewOptions }, setInternalView] = useState<ViewConfig>({ const [{ view, viewOptions }, setInternalView] = useState<ViewConfig>({
view: 'welcome', view: 'loading',
viewOptions: {}, viewOptions: {},
}); });
const { getExtensions, addExtension, read } = useConfig(); const { getExtensions, addExtension, read } = useConfig();
@@ -412,6 +414,7 @@ export default function App() {
<div className="relative w-screen h-screen overflow-hidden bg-bgApp flex flex-col"> <div className="relative w-screen h-screen overflow-hidden bg-bgApp flex flex-col">
<div className="titlebar-drag-region" /> <div className="titlebar-drag-region" />
<div> <div>
{view === 'loading' && <SuspenseLoader />}
{view === 'welcome' && {view === 'welcome' &&
(settingsV2Enabled ? ( (settingsV2Enabled ? (
<ProviderSettings onClose={() => setView('chat')} isOnboarding={true} /> <ProviderSettings onClose={() => setView('chat')} isOnboarding={true} />
+1 -1
View File
@@ -19,7 +19,7 @@ export default function GooseLogo({ className = '', size = 'default', hover = tr
className={`${className} ${sizes[size].frame} ${hover ? 'group/with-hover' : ''} relative overflow-hidden`} className={`${className} ${sizes[size].frame} ${hover ? 'group/with-hover' : ''} relative overflow-hidden`}
> >
<Rain <Rain
className={`${sizes[size].rain} absolute left-0 bottom-0 ${hover ? 'opacity-0 opacity-0 group-hover/with-hover:opacity-100' : ''} transition-all duration-300 z-1`} className={`${sizes[size].rain} absolute left-0 bottom-0 ${hover ? 'opacity-0 group-hover/with-hover:opacity-100' : ''} transition-all duration-300 z-1`}
/> />
<Goose className={`${sizes[size].goose} absolute left-0 bottom-0 z-2`} /> <Goose className={`${sizes[size].goose} absolute left-0 bottom-0 z-2`} />
</div> </div>
@@ -1,5 +1,6 @@
import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react'; import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
import { getActiveProviders } from './utils'; import { getActiveProviders } from './utils';
import SuspenseLoader from '../../../suspense-loader';
// Create a context for active keys // Create a context for active keys
const ActiveKeysContext = createContext< const ActiveKeysContext = createContext<
@@ -33,7 +34,7 @@ export const ActiveKeysProvider = ({ children }: { children: ReactNode }) => {
// Provide active keys and ability to update them // Provide active keys and ability to update them
return ( return (
<ActiveKeysContext.Provider value={{ activeKeys, setActiveKeys }}> <ActiveKeysContext.Provider value={{ activeKeys, setActiveKeys }}>
{!isLoading ? children : <div>Loading...</div>} {/* Conditional rendering */} {!isLoading ? children : <SuspenseLoader />}
</ActiveKeysContext.Provider> </ActiveKeysContext.Provider>
); );
}; };
@@ -49,58 +49,60 @@ export function ConfigureApproveMode({
}; };
return ( return (
<div className="fixed inset-0 bg-black/20 backdrop-blur-sm"> <div className="fixed inset-0 bg-black/20 backdrop-blur-sm z-10">
<Card className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[440px] bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden p-[16px] pt-[24px] pb-0"> <Card className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[440px] bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden p-0">
<div className="px-4 pb-0 space-y-6"> <div className="px-4 pb-0 space-y-6">
{/* Header */} <div className="p-[16px] pt-[24px] pb-0">
<div className="flex"> {/* Header */}
<h2 className="text-2xl font-regular dark:text-white text-gray-900"> <div className="flex">
Configure Approve Mode <h2 className="text-2xl font-regular dark:text-white text-gray-900">
</h2> Configure Approve Mode
</div> </h2>
</div>
<div className="mt-[24px]"> <div className="mt-[24px]">
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6"> <p className="text-sm text-gray-500 dark:text-gray-400 mb-6">
Approve requests can either be given to all tool requests or determine which actions Approve requests can either be given to all tool requests or determine which actions
may need integration may need integration
</p> </p>
<div className="space-y-4"> <div className="space-y-4">
{approveModes.map((mode) => ( {approveModes.map((mode) => (
<ModeSelectionItem <ModeSelectionItem
key={mode.key} key={mode.key}
mode={mode} mode={mode}
showDescription={true} showDescription={true}
currentMode={approveMode} currentMode={approveMode}
isApproveModeConfigure={true} isApproveModeConfigure={true}
handleModeChange={(newMode) => { handleModeChange={(newMode) => {
setApproveMode(newMode); setApproveMode(newMode);
}} }}
/> />
))} ))}
</div>
</div> </div>
</div> </div>
</div>
{/* Actions */} {/* Actions */}
<div className="mt-[8px] ml-[-24px] mr-[-24px] pt-[16px]"> <div className="mt-[8px] ml-[-24px] mr-[-24px] pt-[16px]">
<Button <Button
type="submit" type="submit"
variant="ghost" variant="ghost"
disabled={isSubmitting} disabled={isSubmitting}
onClick={handleModeSubmit} onClick={handleModeSubmit}
className="w-full h-[60px] rounded-none border-t dark:border-gray-600 text-lg hover:bg-gray-50 hover:dark:text-black dark:text-white dark:border-gray-600 font-regular" className="w-full h-[60px] rounded-none border-t text-lg hover:bg-gray-50 dark:hover:text-black dark:text-white dark:border-gray-600 font-regular"
> >
{isSubmitting ? 'Saving...' : 'Save Mode'} {isSubmitting ? 'Saving...' : 'Save Mode'}
</Button> </Button>
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
disabled={isSubmitting} disabled={isSubmitting}
onClick={onClose} onClick={onClose}
className="w-full h-[60px] rounded-none border-t dark:border-gray-600 text-gray-400 hover:bg-gray-50 dark:border-gray-600 text-lg font-regular" className="w-full h-[60px] rounded-none border-t text-gray-400 dark:hover:text-black hover:bg-gray-50 dark:border-gray-600 text-lg font-regular"
> >
Cancel Cancel
</Button> </Button>
</div>
</div> </div>
</Card> </Card>
</div> </div>
@@ -76,7 +76,7 @@ export function AddModelInline() {
<Select <Select
options={providerOptions} options={providerOptions}
value={providerOptions.find((option) => option.value === selectedProvider) || null} value={providerOptions.find((option) => option.value === selectedProvider) || null}
onChange={(option) => { onChange={(option: { value: string | null }) => {
setSelectedProvider(option?.value || null); setSelectedProvider(option?.value || null);
setModelName(''); // Clear model name when provider changes setModelName(''); // Clear model name when provider changes
setFilteredModels([]); setFilteredModels([]);
@@ -110,10 +110,10 @@ export function AddModelInline() {
</div> </div>
<Button <Button
type="button" type="button"
className="bg-black text-white hover:bg-black/90" className="bg-black text-white hover:bg-black/90 dark:bg-white dark:text-black dark:hover:bg-white/90"
onClick={handleSubmit} onClick={handleSubmit}
> >
<Plus className="mr-2 h-4 w-4" /> Add Model <Plus className="h-4 w-4" /> Add Model
</Button> </Button>
</form> </form>
</div> </div>
@@ -74,7 +74,7 @@ export function SearchBar() {
return ( return (
<div className="relative" ref={searchBarRef}> <div className="relative" ref={searchBarRef}>
<Search className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" /> <Search className="absolute left-3 top-[0.8rem] h-4 w-4 text-textSubtle" />
<input <input
type="text" type="text"
placeholder="Search models..." placeholder="Search models..."
@@ -85,17 +85,17 @@ export function SearchBar() {
}} }}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onFocus={() => setShowResults(true)} onFocus={() => setShowResults(true)}
className="w-full pl-12 py-2 bg-background border border-muted-foreground/20 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full pl-9 py-2 text-black dark:text-white bg-bgApp border border-borderSubtle rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
{showResults && search && ( {showResults && search && (
<div className="absolute z-10 w-full mt-2 bg-white dark:bg-gray-800 border border-muted-foreground/20 rounded-md shadow-lg"> <div className="absolute z-10 w-full mt-2 bg-white dark:bg-gray-800 border border-borderSubtle rounded-md shadow-lg">
{filteredModels.length > 0 ? ( {filteredModels.length > 0 ? (
filteredModels.map((model, index) => ( filteredModels.map((model, index) => (
<div <div
key={model.id} key={model.id}
ref={(el) => (resultsRef.current[index] = el)} ref={(el) => (resultsRef.current[index] = el)}
className={`p-2 flex justify-between items-center hover:bg-muted/50 dark:hover:bg-gray-700 cursor-pointer ${ className={`p-2 flex justify-between items-center hover:bg-bgSubtle/50 dark:hover:bg-gray-700 cursor-pointer ${
model.id === currentModel?.id ? 'bg-muted/50 dark:bg-gray-700' : '' model.id === currentModel?.id ? 'bg-bgSubtle/50 dark:bg-gray-700' : ''
}`} }`}
> >
<div> <div>
@@ -112,7 +112,7 @@ export function SearchBar() {
</div> </div>
)) ))
) : ( ) : (
<div className="p-2 text-muted-foreground dark:text-gray-400">No models found</div> <div className="p-2 text-textSubtle dark:text-gray-400">No models found</div>
)} )}
</div> </div>
)} )}
@@ -112,7 +112,7 @@ function BaseProviderCard({
</TooltipProvider> </TooltipProvider>
)} )}
</div> </div>
<p className="text-xs text-textSubtle mt-1.5 mb-3 leading-normal overflow-y-auto max-h-[54px] "> <p className="text-xs text-textSubtle mt-1.5 mb-3 leading-normal scrollbar-thin overflow-y-auto max-h-[54px] ">
{description} {description}
</p> </p>
</div> </div>
+15 -11
View File
@@ -1,24 +1,28 @@
import React from 'react'; import React, { Suspense, lazy } from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import App from './App';
import { ModelProvider } from './components/settings/models/ModelContext'; import { ModelProvider } from './components/settings/models/ModelContext';
import { ConfigProvider } from './components/ConfigContext'; import { ConfigProvider } from './components/ConfigContext';
import { ErrorBoundary } from './components/ErrorBoundary'; import { ErrorBoundary } from './components/ErrorBoundary';
import { ActiveKeysProvider } from './components/settings/api_keys/ActiveKeysContext'; import { ActiveKeysProvider } from './components/settings/api_keys/ActiveKeysContext';
import { patchConsoleLogging } from './utils'; import { patchConsoleLogging } from './utils';
import SuspenseLoader from './suspense-loader';
patchConsoleLogging(); patchConsoleLogging();
const App = lazy(() => import('./App'));
ReactDOM.createRoot(document.getElementById('root')!).render( ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode> <React.StrictMode>
<ConfigProvider> <Suspense fallback={SuspenseLoader()}>
<ModelProvider> <ConfigProvider>
<ActiveKeysProvider> <ModelProvider>
<ErrorBoundary> <ActiveKeysProvider>
<App /> <ErrorBoundary>
</ErrorBoundary> <App />
</ActiveKeysProvider> </ErrorBoundary>
</ModelProvider> </ActiveKeysProvider>
</ConfigProvider> </ModelProvider>
</ConfigProvider>
</Suspense>
</React.StrictMode> </React.StrictMode>
); );
+8
View File
@@ -240,6 +240,10 @@
background: var(--dark-grey-60); background: var(--dark-grey-60);
} }
} }
body {
background-color: var(--background-app);
}
} }
@layer components { @layer components {
@@ -296,4 +300,8 @@
.user-message p { .user-message p {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.scrollbar-thin {
scrollbar-width: thin;
}
} }
+11
View File
@@ -0,0 +1,11 @@
import React from 'react';
import GooseLogo from './components/GooseLogo';
export default function SuspenseLoader() {
return (
<div className="flex flex-col gap-4 items-center justify-center w-screen h-screen overflow-hidden bg-bgApp text-textProminent">
<GooseLogo />
<span className="text-lg">Loading...</span>
</div>
);
}