mirror of
https://github.com/aljazceru/goose.git
synced 2026-02-11 01:24:24 +01:00
feat: settings v2 scaffolding (#1374)
This commit is contained in:
@@ -14,6 +14,7 @@ import { extractExtensionName } from './components/settings/extensions/utils';
|
||||
import WelcomeView from './components/WelcomeView';
|
||||
import ChatView from './components/ChatView';
|
||||
import SettingsView, { type SettingsViewOptions } from './components/settings/SettingsView';
|
||||
import SettingsViewV2 from './components/settings_v2/SettingsView';
|
||||
import MoreModelsView from './components/settings/models/MoreModelsView';
|
||||
import ConfigureProvidersView from './components/settings/providers/ConfigureProvidersView';
|
||||
import ProviderSettings from './components/settings/providers/providers/NewProviderSettingsPage';
|
||||
@@ -28,7 +29,8 @@ export type View =
|
||||
| 'moreModels'
|
||||
| 'configureProviders'
|
||||
| 'configPage'
|
||||
| 'alphaConfigureProviders';
|
||||
| 'alphaConfigureProviders'
|
||||
| 'settingsV2';
|
||||
|
||||
export type ViewConfig = {
|
||||
view: View;
|
||||
@@ -202,15 +204,24 @@ export default function App() {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{view === 'settings' && (
|
||||
<SettingsView
|
||||
onClose={() => {
|
||||
setView('chat');
|
||||
}}
|
||||
setView={setView}
|
||||
viewOptions={viewOptions as SettingsViewOptions}
|
||||
/>
|
||||
)}
|
||||
{view === 'settings' &&
|
||||
(process.env.ALPHA ? (
|
||||
<SettingsViewV2
|
||||
onClose={() => {
|
||||
setView('chat');
|
||||
}}
|
||||
setView={setView}
|
||||
viewOptions={viewOptions as SettingsViewOptions}
|
||||
/>
|
||||
) : (
|
||||
<SettingsView
|
||||
onClose={() => {
|
||||
setView('chat');
|
||||
}}
|
||||
setView={setView}
|
||||
viewOptions={viewOptions as SettingsViewOptions}
|
||||
/>
|
||||
))}
|
||||
{view === 'moreModels' && (
|
||||
<MoreModelsView
|
||||
onClose={() => {
|
||||
|
||||
206
ui/desktop/src/components/settings_v2/SettingsView.tsx
Normal file
206
ui/desktop/src/components/settings_v2/SettingsView.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import React from 'react';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import BackButton from '../ui/BackButton';
|
||||
import type { View } from '../../App';
|
||||
import { Button } from '../ui/button';
|
||||
import { Switch } from '../ui/switch';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Gear } from '../icons/Gear';
|
||||
import { GPSIcon } from '../ui/icons';
|
||||
|
||||
interface ModelOption {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
interface ExtensionItem {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
enabled: boolean;
|
||||
canConfigure?: boolean;
|
||||
}
|
||||
|
||||
// Mock data - replace with actual data source
|
||||
const defaultModelOptions: ModelOption[] = [
|
||||
{
|
||||
id: 'gpt-4',
|
||||
name: 'GPT-4',
|
||||
description: 'Most capable model, best for complex tasks',
|
||||
selected: true,
|
||||
},
|
||||
{
|
||||
id: 'gpt-3.5',
|
||||
name: 'GPT-3.5',
|
||||
description: 'Fast and efficient for most tasks',
|
||||
selected: false,
|
||||
},
|
||||
];
|
||||
|
||||
const extensionItems: ExtensionItem[] = [
|
||||
{
|
||||
id: 'dev',
|
||||
title: 'Developer Tools',
|
||||
subtitle: 'Code editing and shell access',
|
||||
enabled: true,
|
||||
canConfigure: true,
|
||||
},
|
||||
{
|
||||
id: 'browser',
|
||||
title: 'Web Browser',
|
||||
subtitle: 'Internet access and web automation',
|
||||
enabled: false,
|
||||
canConfigure: true,
|
||||
},
|
||||
];
|
||||
|
||||
export type SettingsViewOptions = {
|
||||
extensionId?: string;
|
||||
showEnvVars?: boolean;
|
||||
};
|
||||
|
||||
export default function SettingsView({
|
||||
onClose,
|
||||
setView,
|
||||
viewOptions,
|
||||
}: {
|
||||
onClose: () => void;
|
||||
setView: (view: View) => void;
|
||||
viewOptions: SettingsViewOptions;
|
||||
}) {
|
||||
const [modelOptions, setModelOptions] = React.useState<ModelOption[]>(defaultModelOptions);
|
||||
const [extensions, setExtensions] = React.useState<ExtensionItem[]>(extensionItems);
|
||||
|
||||
const handleModelSelect = (selectedId: string) => {
|
||||
setModelOptions(
|
||||
modelOptions.map((model) => ({
|
||||
...model,
|
||||
selected: model.id === selectedId,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
const handleExtensionToggle = (id: string) => {
|
||||
setExtensions(
|
||||
extensions.map((extension) => ({
|
||||
...extension,
|
||||
enabled: extension.id === id ? !extension.enabled : extension.enabled,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
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="flex flex-col pb-24">
|
||||
<div className="px-8 pt-6 pb-4">
|
||||
<BackButton onClick={() => onClose()} />
|
||||
</div>
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="flex-1 pt-[20px]">
|
||||
<div className="space-y-8">
|
||||
{/* Models Section */}
|
||||
<section id="models">
|
||||
<div className="flex justify-between items-center mb-6 px-8">
|
||||
<h1 className="text-3xl font-medium text-textStandard">Models</h1>
|
||||
</div>
|
||||
<div className="px-8">
|
||||
<div className="space-y-2">
|
||||
{modelOptions.map((model, index) => (
|
||||
<React.Fragment key={model.id}>
|
||||
<div className="flex items-center justify-between py-3">
|
||||
<div className="space-y-1">
|
||||
<h3 className="font-medium text-textStandard">{model.name}</h3>
|
||||
<p className="text-sm text-textSubtle">{model.description}</p>
|
||||
</div>
|
||||
<input
|
||||
type="radio"
|
||||
name="model"
|
||||
checked={model.selected}
|
||||
onChange={() => handleModelSelect(model.id)}
|
||||
className="h-4 w-4 text-white accent-[#393838] bg-[#393838] border-[#393838] checked:bg-[#393838] focus:ring-0 focus:ring-offset-0"
|
||||
/>
|
||||
</div>
|
||||
{index < modelOptions.length - 1 && (
|
||||
<div className="h-px bg-borderSubtle" />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-4 pt-4 w-full">
|
||||
<Button className="flex items-center gap-2 flex-1 justify-center bg-[#393838] hover:bg-subtle">
|
||||
<Plus className="h-4 w-4" />
|
||||
Add Model
|
||||
</Button>
|
||||
<Button className="flex items-center gap-2 flex-1 justify-center text-textSubtle border-standard bg-grey-60 hover:bg-subtle">
|
||||
<Gear className="h-4 w-4" />
|
||||
Configure Providers
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Extensions Section */}
|
||||
<section id="extensions">
|
||||
<div className="flex justify-between items-center mb-6 px-8">
|
||||
<h1 className="text-3xl font-medium text-textStandard">Extensions</h1>
|
||||
</div>
|
||||
<div className="px-8">
|
||||
<p className="text-sm text-textStandard mb-6">
|
||||
These extensions use the Model Context Protocol (MCP). They can expand Goose's
|
||||
capabilities using three main components: Prompts, Resources, and Tools.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{extensions.map((extension, index) => (
|
||||
<React.Fragment key={extension.id}>
|
||||
<div className="flex items-center justify-between py-3">
|
||||
<div className="space-y-1">
|
||||
<h3 className="font-medium text-textStandard">{extension.title}</h3>
|
||||
<p className="text-sm text-textSubtle">{extension.subtitle}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{extension.canConfigure && (
|
||||
<button className="text-textSubtle hover:text-textStandard">
|
||||
<Gear className="h-5 w-5" />
|
||||
</button>
|
||||
)}
|
||||
<Switch
|
||||
checked={extension.enabled}
|
||||
onCheckedChange={() => handleExtensionToggle(extension.id)}
|
||||
className="bg-[#393838] [&_span[data-state]]:bg-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{index < extensions.length - 1 && <div className="h-px bg-borderSubtle" />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-4 pt-4 w-full">
|
||||
<Button className="flex items-center gap-2 flex-1 justify-center bg-[#393838] hover:bg-subtle">
|
||||
<Plus className="h-4 w-4" />
|
||||
Manually Add
|
||||
</Button>
|
||||
<Button
|
||||
className="flex items-center gap-2 flex-1 justify-center text-textSubtle border-standard bg-grey-60 hover:bg-subtle"
|
||||
onClick={() =>
|
||||
window.open('https://block.github.io/goose/v1/extensions/', '_blank')
|
||||
}
|
||||
>
|
||||
<GPSIcon size={18} />
|
||||
Visit Extensions
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ const BackButton: React.FC<BackButtonProps> = ({ onClick, className = '' }) => {
|
||||
className={`flex items-center text-sm text-textSubtle group hover:text-textStandard ${className}`}
|
||||
>
|
||||
<Back className="w-3 h-3 group-hover:-translate-x-1 transition-all mr-1" />
|
||||
<span>Back</span>
|
||||
<span>Exit</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user