Skip to content

Commit

Permalink
feat: wip on ConfigProvider and integration in settings_v2
Browse files Browse the repository at this point in the history
  • Loading branch information
alexhancock committed Feb 26, 2025
1 parent 26348a6 commit 3ac8a15
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 411 deletions.
72 changes: 72 additions & 0 deletions ui/desktop/src/components/ConfigContext.tsx
Original file line number Diff line number Diff line change
@@ -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;
};
96 changes: 96 additions & 0 deletions ui/desktop/src/components/settings_v2/ExtensionsSection.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
}
95 changes: 7 additions & 88 deletions ui/desktop/src/components/settings_v2/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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[] = [
{
Expand All @@ -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;
Expand All @@ -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(
Expand All @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
Loading

0 comments on commit 3ac8a15

Please sign in to comment.