Skip to content

Commit

Permalink
ditch router to fix the UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Kvadratni committed Feb 4, 2025
1 parent ff6c2ad commit 78c269e
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 234 deletions.
176 changes: 90 additions & 86 deletions ui/desktop/src/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ import WingToWing, { Working } from './components/WingToWing';
import { askAi } from './utils/askAI';
import { getStoredModel, Provider } from './utils/providerUtils';

Check warning on line 15 in ui/desktop/src/ChatWindow.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'Provider' is defined but never used. Allowed unused vars must match /^_/u
import { ChatLayout } from './components/chat_window/ChatLayout';
import { ChatRoutes } from './components/chat_window/ChatRoutes';
import { WelcomeScreen } from './components/welcome_screen/WelcomeScreen';
import { getStoredProvider, initializeSystem } from './utils/providerUtils';
import { useModel } from './components/settings/models/ModelContext';
import { useRecentModels } from './components/settings/models/RecentModels';
import { createSelectedModel } from './components/settings/models/utils';
import { getDefaultModel } from './components/settings/models/hardcoded_stuff';
import Splash from './components/Splash';
import Settings from './components/settings/Settings';
import MoreModelsSettings from './components/settings/models/MoreModels';
import ConfigureProviders from './components/settings/providers/ConfigureProviders';

export interface Chat {
id: number;
Expand All @@ -33,13 +35,21 @@ export interface Chat {
}>;
}

// A new type for controlling our "view" state.
// Add more if needed, e.g. 'moreModels', 'configureProviders', etc.
export type View = 'welcome' | 'chat' | 'settings' | 'moreModels' | 'configureProviders';

// This component is our main chat content.
// We'll move the majority of chat logic here, minus the 'view' state.
export function ChatContent({
chats,
setChats,
selectedChatId,
setSelectedChatId,

Check warning on line 48 in ui/desktop/src/ChatWindow.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'setSelectedChatId' is defined but never used. Allowed unused args must match /^_/u
initialQuery,
setProgressMessage,
setWorking,
setView,
}: {
chats: Chat[];
setChats: React.Dispatch<React.SetStateAction<Chat[]>>;
Expand All @@ -48,6 +58,7 @@ export function ChatContent({
initialQuery: string | null;
setProgressMessage: React.Dispatch<React.SetStateAction<string>>;
setWorking: React.Dispatch<React.SetStateAction<Working>>;
setView: (view: View) => void;
}) {
const chat = chats.find((c: Chat) => c.id === selectedChatId);
const [messageMetadata, setMessageMetadata] = useState<Record<string, string[]>>({});
Expand Down Expand Up @@ -95,7 +106,6 @@ export function ChatContent({
window.electron.logInfo('last interaction:' + lastInteractionTime);
if (timeSinceLastInteraction > 60000) {
// 60000ms = 1 minute

window.electron.showNotification({
title: 'Goose finished the task.',
body: 'Click here to expand.',
Expand Down Expand Up @@ -133,7 +143,7 @@ export function ChatContent({
setLastInteractionTime(Date.now());
append({
role: 'user',
content: content,
content,
});
if (scrollRef.current?.scrollToBottom) {
scrollRef.current.scrollToBottom();
Expand Down Expand Up @@ -194,7 +204,8 @@ export function ChatContent({
return (
<div className="flex flex-col w-full h-screen items-center justify-center">
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle border-b border-borderSubtle">
<MoreMenu />
{/* Pass setView to MoreMenu so it can switch to settings or other views */}
<MoreMenu setView={setView} />
</div>
<Card className="flex flex-col flex-1 rounded-none h-[calc(100vh-95px)] w-full bg-bgApp mt-0 border-none relative">
{messages.length === 0 ? (
Expand All @@ -215,12 +226,6 @@ export function ChatContent({
)}
</div>
))}
{/* {isLoading && (
<div className="flex items-center justify-center p-4">
<div onClick={() => setShowGame(true)} style={{ cursor: 'pointer' }}>
</div>
</div>
)} */}
{error && (
<div className="flex flex-col items-center justify-center p-4">
<div className="text-red-700 dark:text-red-300 bg-red-400/50 p-3 rounded-lg mb-2">
Expand Down Expand Up @@ -258,7 +263,7 @@ export function ChatContent({
isLoading={isLoading}
onStop={onStopGoose}
/>
<BottomMenu hasMessages={hasMessages} />
<BottomMenu hasMessages={hasMessages} setView={setView} />
</div>
</Card>

Expand All @@ -268,98 +273,63 @@ export function ChatContent({
}

export default function ChatWindow() {
// We'll add a state controlling which "view" is active.
const [view, setView] = useState<View>('welcome');

// Shared function to create a chat window
const openNewChatWindow = () => {
window.electron.createChatWindow();
};
const { switchModel, currentModel } = useModel(); // Access switchModel via useModel

Check warning on line 283 in ui/desktop/src/ChatWindow.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'currentModel' is assigned a value but never used. Allowed unused vars must match /^_/u
const { addRecentModel } = useRecentModels(); // Access addRecentModel from useRecentModels

// Add keyboard shortcut handler
// This will store chat data for the "chat" view.
const [chats, setChats] = useState<Chat[]>(() => {
return [
{
id: 1,
title: 'Chat 1',
messages: [],
},
];
});
const [selectedChatId, setSelectedChatId] = useState(1);

// Additional states
const [mode, setMode] = useState<'expanded' | 'compact'>('expanded');

Check warning on line 299 in ui/desktop/src/ChatWindow.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'setMode' is assigned a value but never used. Allowed unused vars must match /^_/u
const [working, setWorking] = useState<Working>(Working.Idle);

Check warning on line 300 in ui/desktop/src/ChatWindow.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'working' is assigned a value but never used. Allowed unused vars must match /^_/u
const [progressMessage, setProgressMessage] = useState<string>('');
const [initialQuery, setInitialQuery] = useState<string | null>(null);

// Keyboard shortcut handler
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
// Check for Command+N (Mac) or Control+N (Windows/Linux)
if ((event.metaKey || event.ctrlKey) && event.key === 'n') {
event.preventDefault(); // Prevent default browser behavior
event.preventDefault();
openNewChatWindow();
}
};

// Add event listener
window.addEventListener('keydown', handleKeyDown);

// Cleanup
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, []);

// Get initial query and history from URL parameters
const searchParams = new URLSearchParams(window.location.search);
const initialQuery = searchParams.get('initialQuery');
const historyParam = searchParams.get('history');
const initialHistory = historyParam ? JSON.parse(decodeURIComponent(historyParam)) : [];

const [chats, setChats] = useState<Chat[]>(() => {
const firstChat = {
id: 1,
title: initialQuery || 'Chat 1',
messages: initialHistory.length > 0 ? initialHistory : [],
};
return [firstChat];
});

const [selectedChatId, setSelectedChatId] = useState(1);
const [mode, setMode] = useState<'expanded' | 'compact'>(initialQuery ? 'compact' : 'expanded');
const [working, setWorking] = useState<Working>(Working.Idle);
const [progressMessage, setProgressMessage] = useState<string>('');
const [selectedProvider, setSelectedProvider] = useState<string | Provider | null>(null);
const [showWelcomeModal, setShowWelcomeModal] = useState(true);

// Add this useEffect to track changes and update welcome state
const toggleMode = () => {
const newMode = mode === 'expanded' ? 'compact' : 'expanded';
console.log(`Toggle to ${newMode}`);
setMode(newMode);
};

window.electron.logInfo('ChatWindow loaded');

// Fix the handleSubmit function syntax
const handleSubmit = () => {
setShowWelcomeModal(false);
};

// Attempt to detect config for a stored provider
useEffect(() => {
// Check if we already have a provider set
const config = window.electron.getConfig();
const storedProvider = getStoredProvider(config);

if (storedProvider) {
setShowWelcomeModal(false);
// If there's a stored provider, skip welcome
setView('chat');
} else {
setShowWelcomeModal(true);
setView('welcome');
}
}, []);

const storeSecret = async (key: string, value: string) => {
const response = await fetch(getApiUrl('/configs/store'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': getSecretKey(),
},
body: JSON.stringify({ key, value }),
});

if (!response.ok) {
throw new Error(`Failed to store secret: ${response.statusText}`);
}

return response;
};

// Initialize system on load if we have a stored provider
// Initialize system if we have a stored provider
useEffect(() => {
const setupStoredProvider = async () => {
const config = window.electron.getConfig();
Expand All @@ -380,17 +350,10 @@ export default function ChatWindow() {
if (!storedModel) {
// get the default model
const modelName = getDefaultModel(storedProvider.toLowerCase());

// create model object
const model = createSelectedModel(storedProvider.toLowerCase(), modelName);

// Call the context's switchModel to track the set model state in the front end
switchModel(model);

// Keep track of the recently used models
addRecentModel(model);

console.log('set up provider with default model', storedProvider, modelName);
}
} catch (error) {
console.error('Failed to initialize with stored provider:', error);
Expand All @@ -401,22 +364,63 @@ export default function ChatWindow() {
setupStoredProvider();
}, []);

// Render WelcomeScreen at root level if showing
if (showWelcomeModal) {
return <WelcomeScreen onSubmit={handleSubmit} />;
// Here we conditionally render based on "view"
if (view === 'welcome') {
return (
<WelcomeScreen
onSubmit={() => {
// Once user finishes config, switch to chat
setView('chat');
}}
/>
);
}

if (view === 'settings') {
return (
<Settings
onClose={() => {
setView('chat');
}}
setView={setView}
/>
);
}

if (view === 'moreModels') {
return (
<MoreModelsSettings
onClose={() => {
setView('settings');
}}
setView={setView}
/>
);
}

if (view === 'configureProviders') {
return (
<ConfigureProviders
onClose={() => {
setView('settings');
}}
/>
);
}

// Only render ChatLayout if not showing welcome screen
// Default: show the chat layout
return (
<div>
<ChatLayout mode={mode}>
<ChatRoutes
<ChatContent
chats={chats}
setChats={setChats}
selectedChatId={selectedChatId}
setSelectedChatId={setSelectedChatId}
initialQuery={initialQuery}
setProgressMessage={setProgressMessage}
setWorking={setWorking}
setView={setView}
/>
</ChatLayout>
</div>
Expand Down
16 changes: 12 additions & 4 deletions ui/desktop/src/components/BottomMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ import { useModel } from './settings/models/ModelContext';
import { useRecentModels } from './settings/models/RecentModels'; // Hook for recent models
import { Sliders } from 'lucide-react';
import { ModelRadioList } from './settings/models/ModelRadioList';
import { useNavigate } from 'react-router-dom';
// Remove react-router-dom usage
// import { useNavigate } from 'react-router-dom';
import { Document, ChevronUp, ChevronDown } from './icons';
import type { View } from '../ChatWindow';

export default function BottomMenu({ hasMessages }) {
export default function BottomMenu({
hasMessages,
setView,
}: {
hasMessages: boolean;
setView?: (view: View) => void;
}) {
const [isModelMenuOpen, setIsModelMenuOpen] = useState(false);
const { currentModel } = useModel();
const { recentModels } = useRecentModels(); // Get recent models
const navigate = useNavigate();
const dropdownRef = useRef<HTMLDivElement>(null);

// Add effect to handle clicks outside
Expand Down Expand Up @@ -126,7 +133,8 @@ export default function BottomMenu({ hasMessages }) {
border-t border-borderSubtle mt-2"
onClick={() => {
setIsModelMenuOpen(false);
navigate('/settings');
// Instead of navigate('/settings'), call setView('settings').
setView?.('settings');
}}
>
<span className="text-sm">Tools and Settings</span>
Expand Down
Loading

0 comments on commit 78c269e

Please sign in to comment.