feat: goose windows (#880)
Co-authored-by: Ryan Versaw <ryan@versaw.com>
This commit is contained in:
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -2,18 +2,20 @@ import { Popover, PopoverContent, PopoverTrigger, PopoverPortal } from '@radix-u
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FaMoon, FaSun } from 'react-icons/fa';
|
||||
import VertDots from './ui/VertDots';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
// Removed react-router-dom import
|
||||
// import { useNavigate } from 'react-router-dom';
|
||||
import { More } from './icons';
|
||||
import { Settings, Grid, MessageSquare } from 'lucide-react';
|
||||
import { Button } from './ui/button';
|
||||
import type { View } from '../../ChatWindow';
|
||||
|
||||
interface VersionInfo {
|
||||
current_version: string;
|
||||
available_versions: string[];
|
||||
}
|
||||
|
||||
export default function MoreMenu() {
|
||||
const navigate = useNavigate();
|
||||
// Accept setView as a prop from the parent (e.g. ChatContent)
|
||||
export default function MoreMenu({ setView }: { setView?: (view: View) => void }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [versions, setVersions] = useState<VersionInfo | null>(null);
|
||||
const [showVersions, setShowVersions] = useState(false);
|
||||
@@ -229,7 +231,8 @@ export default function MoreMenu() {
|
||||
<button
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
navigate('/settings');
|
||||
// Instead of navigate('/settings'), call setView to switch.
|
||||
setView?.('settings');
|
||||
}}
|
||||
className="w-full text-left p-2 text-sm hover:bg-bgSubtle transition-colors"
|
||||
>
|
||||
@@ -264,12 +267,16 @@ export default function MoreMenu() {
|
||||
>
|
||||
Reset Provider
|
||||
</button>
|
||||
|
||||
{/* Provider keys settings */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<button
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
navigate('/keys');
|
||||
// Instead of navigate('/keys'), we might do setView('someKeysView') or open new window.
|
||||
// For now, just do nothing or set to some placeholder.
|
||||
// setView?.('keys');
|
||||
window.electron.createChatWindow();
|
||||
}}
|
||||
className="w-full text-left p-2 text-sm hover:bg-bgSubtle transition-colors"
|
||||
>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { ChatContent } from '../../ChatWindow';
|
||||
import Settings from '../settings/Settings';
|
||||
import MoreModelsSettings from '../settings/models/MoreModels';
|
||||
import ConfigureProviders from '../settings/providers/ConfigureProviders';
|
||||
import { WelcomeScreen } from '../welcome_screen/WelcomeScreen';
|
||||
|
||||
export const ChatRoutes = ({
|
||||
chats,
|
||||
setChats,
|
||||
selectedChatId,
|
||||
setSelectedChatId,
|
||||
setProgressMessage,
|
||||
setWorking,
|
||||
}) => (
|
||||
<Routes>
|
||||
<Route
|
||||
path="/chat/:id"
|
||||
element={
|
||||
<ChatContent
|
||||
chats={chats}
|
||||
setChats={setChats}
|
||||
selectedChatId={selectedChatId}
|
||||
setSelectedChatId={setSelectedChatId}
|
||||
initialQuery={null}
|
||||
setProgressMessage={setProgressMessage}
|
||||
setWorking={setWorking}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
<Route path="/settings/more-models" element={<MoreModelsSettings />} />
|
||||
<Route path="/settings/configure-providers" element={<ConfigureProviders />} />
|
||||
<Route path="/welcome" element={<WelcomeScreen />} />
|
||||
<Route path="*" element={<Navigate to="/chat/1" replace />} />
|
||||
</Routes>
|
||||
);
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Settings as SettingsType } from './types';
|
||||
import {
|
||||
@@ -15,6 +14,7 @@ import { ConfigureBuiltInExtensionModal } from './extensions/ConfigureBuiltInExt
|
||||
import BackButton from '../ui/BackButton';
|
||||
import { RecentModelsRadio } from './models/RecentModels';
|
||||
import { ExtensionItem } from './extensions/ExtensionItem';
|
||||
import type { View } from '../../ChatWindow';
|
||||
|
||||
const EXTENSIONS_DESCRIPTION =
|
||||
'The Model Context Protocol (MCP) is a system that allows AI models to securely connect with local or remote resources using standard server setups. It works like a client-server setup and expands AI capabilities using three main components: Prompts, Resources, and Tools.';
|
||||
@@ -46,9 +46,18 @@ const DEFAULT_SETTINGS: SettingsType = {
|
||||
extensions: BUILT_IN_EXTENSIONS,
|
||||
};
|
||||
|
||||
export default function Settings() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
// We'll accept two props:
|
||||
// onClose: to go back to chat
|
||||
// setView: to switch to moreModels, configureProviders, etc.
|
||||
export default function Settings({
|
||||
onClose,
|
||||
setView,
|
||||
}: {
|
||||
onClose: () => void;
|
||||
setView: (view: View) => void;
|
||||
}) {
|
||||
// We'll read query params from window.location instead of react-router's useLocation
|
||||
const [searchParams] = useState(() => new URLSearchParams(window.location.search));
|
||||
|
||||
const [settings, setSettings] = React.useState<SettingsType>(() => {
|
||||
const saved = localStorage.getItem('user_settings');
|
||||
@@ -96,9 +105,8 @@ export default function Settings() {
|
||||
|
||||
// Handle URL parameters for auto-opening extension configuration
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const extensionId = params.get('extensionId');
|
||||
const showEnvVars = params.get('showEnvVars');
|
||||
const extensionId = searchParams.get('extensionId');
|
||||
const showEnvVars = searchParams.get('showEnvVars');
|
||||
|
||||
if (extensionId && showEnvVars === 'true') {
|
||||
// Find the extension in settings
|
||||
@@ -113,7 +121,9 @@ export default function Settings() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [location.search, settings.extensions]);
|
||||
// We only run this once on load
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [settings.extensions]);
|
||||
|
||||
const handleExtensionToggle = async (extensionId: string) => {
|
||||
// Find the extension to get its current state
|
||||
@@ -160,28 +170,11 @@ export default function Settings() {
|
||||
extensions: prev.extensions.filter((ext) => ext.id !== extensionBeingConfigured.id),
|
||||
}));
|
||||
setExtensionBeingConfigured(null);
|
||||
navigate('/settings', { replace: true });
|
||||
}
|
||||
};
|
||||
|
||||
const handleNavClick = (section: string, e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
const scrollArea = document.querySelector('[data-radix-scroll-area-viewport]');
|
||||
const element = document.getElementById(section.toLowerCase());
|
||||
|
||||
if (scrollArea && element) {
|
||||
const topPos = element.offsetTop;
|
||||
scrollArea.scrollTo({
|
||||
top: topPos,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleExtensionConfigSubmit = () => {
|
||||
setExtensionBeingConfigured(null);
|
||||
// Clear the URL parameters after configuration
|
||||
navigate('/settings', { replace: true });
|
||||
};
|
||||
|
||||
const isBuiltIn = (extensionId: string) => {
|
||||
@@ -197,7 +190,8 @@ export default function Settings() {
|
||||
<div className="px-8 pt-6 pb-4">
|
||||
<BackButton
|
||||
onClick={() => {
|
||||
navigate('/chat/1', { replace: true });
|
||||
// Instead of navigate('/chat/1', { replace: true });
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
<h1 className="text-3xl font-medium text-textStandard mt-1">Settings</h1>
|
||||
@@ -210,7 +204,10 @@ export default function Settings() {
|
||||
<div className="flex justify-between items-center mb-6 border-b border-borderSubtle px-8">
|
||||
<h2 className="text-xl font-medium text-textStandard">Models</h2>
|
||||
<button
|
||||
onClick={() => navigate('/settings/more-models')}
|
||||
onClick={() => {
|
||||
// Instead of navigate('/settings/more-models'):
|
||||
setView('moreModels');
|
||||
}}
|
||||
className="text-indigo-500 hover:text-indigo-600 text-sm"
|
||||
>
|
||||
Browse
|
||||
@@ -230,7 +227,6 @@ export default function Settings() {
|
||||
className="text-indigo-500 hover:text-indigo-600 text-sm"
|
||||
title="Add Manually"
|
||||
>
|
||||
{/* <Plus className="h-4 w-4" /> */}
|
||||
Add
|
||||
</button>
|
||||
|
||||
@@ -255,7 +251,7 @@ export default function Settings() {
|
||||
<ExtensionItem
|
||||
key={ext.id}
|
||||
{...ext}
|
||||
canConfigure={true} // Ensure gear icon always appears
|
||||
canConfigure={true}
|
||||
onToggle={handleExtensionToggle}
|
||||
onConfigure={(extension) => setExtensionBeingConfigured(extension)}
|
||||
/>
|
||||
@@ -273,7 +269,6 @@ export default function Settings() {
|
||||
isOpen={!!extensionBeingConfigured && isBuiltIn(extensionBeingConfigured.id)}
|
||||
onClose={() => {
|
||||
setExtensionBeingConfigured(null);
|
||||
navigate('/settings', { replace: true });
|
||||
}}
|
||||
extension={extensionBeingConfigured}
|
||||
onSubmit={handleExtensionConfigSubmit}
|
||||
@@ -283,8 +278,6 @@ export default function Settings() {
|
||||
isOpen={!!extensionBeingConfigured}
|
||||
onClose={() => {
|
||||
setExtensionBeingConfigured(null);
|
||||
// Clear URL parameters when closing manually
|
||||
navigate('/settings', { replace: true });
|
||||
}}
|
||||
extension={extensionBeingConfigured}
|
||||
onSubmit={handleExtensionConfigSubmit}
|
||||
|
||||
@@ -6,25 +6,27 @@ import BackButton from '../../ui/BackButton';
|
||||
import { SearchBar } from './Search';
|
||||
import { useModel } from './ModelContext';
|
||||
import { AddModelInline } from './AddModelInline';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
// Removed react-router-dom usage
|
||||
// import { useNavigate } from 'react-router-dom';
|
||||
import { ScrollArea } from '../../ui/scroll-area';
|
||||
import type { View } from '../../../ChatWindow';
|
||||
|
||||
export default function MoreModelsPage() {
|
||||
export default function MoreModelsPage({
|
||||
onClose,
|
||||
setView,
|
||||
}: {
|
||||
onClose: () => void;
|
||||
setView: (view: View) => void;
|
||||
}) {
|
||||
const { currentModel } = useModel();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
{/*
|
||||
Instead of forcing one row, allow the layout
|
||||
to stack vertically on small screens:
|
||||
*/}
|
||||
|
||||
<div className="px-8 pt-6 pb-4">
|
||||
<BackButton />
|
||||
<BackButton onClick={onClose} />
|
||||
<h1 className="text-3xl font-medium text-textStandard mt-1">Browse models</h1>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +36,7 @@ export default function MoreModelsPage() {
|
||||
<div className="flex justify-between items-center mb-6 border-b border-borderSubtle px-8">
|
||||
<h2 className="text-xl font-medium text-textStandard">Models</h2>
|
||||
<button
|
||||
onClick={() => navigate('/settings/configure-providers')}
|
||||
onClick={() => setView('configureProviders')}
|
||||
className="text-indigo-500 hover:text-indigo-600 text-sm"
|
||||
>
|
||||
Configure
|
||||
|
||||
@@ -2,15 +2,22 @@ import React from 'react';
|
||||
import { ScrollArea } from '../../ui/scroll-area';
|
||||
import BackButton from '../../ui/BackButton';
|
||||
import { ConfigureProvidersGrid } from './ConfigureProvidersGrid';
|
||||
import type { View } from '../../../ChatWindow';
|
||||
|
||||
export default function ConfigureProviders() {
|
||||
export default function ConfigureProviders({
|
||||
onClose,
|
||||
setView,
|
||||
}: {
|
||||
onClose: () => void;
|
||||
setView?: (view: View) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="h-screen w-full">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="px-8 pt-6 pb-4">
|
||||
<BackButton />
|
||||
<BackButton onClick={onClose} />
|
||||
<h1 className="text-3xl font-medium text-textStandard mt-1">Configure</h1>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user