feat: wip on ConfigProvider and integration in settings_v2 (#1395)
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { Config } from '../api/config';
|
||||
|
||||
interface ConfigContextType {
|
||||
config: Record<string, any>;
|
||||
upsert: (key: string, value: any, isSecret?: boolean) => Promise<void>;
|
||||
read: (key: string) => Promise<any>;
|
||||
remove: (key: string) => Promise<void>;
|
||||
addExtension: (name: string, config: any) => Promise<void>;
|
||||
removeExtension: (name: string) => Promise<void>;
|
||||
}
|
||||
|
||||
interface ConfigProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ConfigContext = createContext<ConfigContextType | undefined>(undefined);
|
||||
|
||||
export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
|
||||
const [config, setConfig] = useState<Record<string, any>>({});
|
||||
|
||||
useEffect(() => {
|
||||
// Load all configuration data on mount
|
||||
(async () => {
|
||||
const initialConfig = await Config.readAll();
|
||||
setConfig(initialConfig || {});
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const reloadConfig = async () => {
|
||||
const newConfig = await Config.readAll();
|
||||
setConfig(newConfig || {});
|
||||
};
|
||||
|
||||
const upsert = async (key: string, value: any, isSecret?: boolean) => {
|
||||
await Config.upsert(key, value, isSecret);
|
||||
await reloadConfig();
|
||||
};
|
||||
|
||||
const read = async (key: string) => {
|
||||
return Config.read(key);
|
||||
};
|
||||
|
||||
const remove = async (key: string) => {
|
||||
await Config.remove(key);
|
||||
await reloadConfig();
|
||||
};
|
||||
|
||||
const addExtension = async (name: string, config: any) => {
|
||||
await Config.addExtension(name, config);
|
||||
await reloadConfig();
|
||||
};
|
||||
|
||||
const removeExtension = async (name: string) => {
|
||||
await Config.removeExtension(name);
|
||||
await reloadConfig();
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfigContext.Provider value={{ config, upsert, read, remove, addExtension, removeExtension }}>
|
||||
{children}
|
||||
</ConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useConfig = () => {
|
||||
const context = useContext(ConfigContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useConfig must be used within a ConfigProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
import React, { useState } from 'react';
|
||||
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 ExtensionItem {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
enabled: boolean;
|
||||
canConfigure?: boolean;
|
||||
}
|
||||
|
||||
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 default function ExtensionsSection() {
|
||||
const [extensions, setExtensions] = useState<ExtensionItem[]>(extensionItems);
|
||||
|
||||
const handleExtensionToggle = (id: string) => {
|
||||
setExtensions(
|
||||
extensions.map((extension) => ({
|
||||
...extension,
|
||||
enabled: extension.id === id ? !extension.enabled : extension.enabled,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
@@ -2,11 +2,11 @@ import React from 'react';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import BackButton from '../ui/BackButton';
|
||||
import type { View } from '../../App';
|
||||
import { useConfig } from '../ConfigContext';
|
||||
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';
|
||||
import ExtensionsSection from './ExtensionsSection';
|
||||
|
||||
interface ModelOption {
|
||||
id: string;
|
||||
@@ -15,14 +15,6 @@ interface ModelOption {
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
interface ExtensionItem {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
enabled: boolean;
|
||||
canConfigure?: boolean;
|
||||
}
|
||||
|
||||
// Mock data - replace with actual data source
|
||||
const defaultModelOptions: ModelOption[] = [
|
||||
{
|
||||
@@ -39,23 +31,6 @@ const defaultModelOptions: ModelOption[] = [
|
||||
},
|
||||
];
|
||||
|
||||
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;
|
||||
@@ -71,7 +46,10 @@ export default function SettingsView({
|
||||
viewOptions: SettingsViewOptions;
|
||||
}) {
|
||||
const [modelOptions, setModelOptions] = React.useState<ModelOption[]>(defaultModelOptions);
|
||||
const [extensions, setExtensions] = React.useState<ExtensionItem[]>(extensionItems);
|
||||
|
||||
const { config } = useConfig();
|
||||
|
||||
console.log(config);
|
||||
|
||||
const handleModelSelect = (selectedId: string) => {
|
||||
setModelOptions(
|
||||
@@ -82,15 +60,6 @@ export default function SettingsView({
|
||||
);
|
||||
};
|
||||
|
||||
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>
|
||||
@@ -146,57 +115,7 @@ export default function SettingsView({
|
||||
</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>
|
||||
<ExtensionsSection />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user