feat: goose windows (#880)

Co-authored-by: Ryan Versaw <ryan@versaw.com>
This commit is contained in:
Max Novich
2025-02-10 15:05:13 -08:00
committed by GitHub
parent 98aecbef23
commit cfd3ee8fd9
43 changed files with 1327 additions and 456 deletions
+25 -32
View File
@@ -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>