ui: add description field to modal (#1846)

This commit is contained in:
Lily Delalande
2025-03-25 13:28:09 -04:00
committed by GitHub
parent 96b905a501
commit 615788ae62
4 changed files with 56 additions and 30 deletions
@@ -5,6 +5,7 @@ import React, { useState } from 'react';
interface ExtensionInfoFieldsProps { interface ExtensionInfoFieldsProps {
name: string; name: string;
type: 'stdio' | 'sse' | 'builtin'; type: 'stdio' | 'sse' | 'builtin';
description: string;
onChange: (key: string, value: any) => void; onChange: (key: string, value: any) => void;
submitAttempted: boolean; submitAttempted: boolean;
} }
@@ -12,6 +13,7 @@ interface ExtensionInfoFieldsProps {
export default function ExtensionInfoFields({ export default function ExtensionInfoFields({
name, name,
type, type,
description,
onChange, onChange,
submitAttempted, submitAttempted,
}: ExtensionInfoFieldsProps) { }: ExtensionInfoFieldsProps) {
@@ -20,37 +22,54 @@ export default function ExtensionInfoFields({
}; };
return ( return (
<div className="flex justify-between gap-4 mb-6"> <div className="flex flex-col gap-4 mb-6">
<div className="flex-1"> {/* Top row with Name and Type side by side */}
<label className="text-sm font-medium mb-2 block text-textStandard">Extension Name</label> <div className="flex justify-between gap-4">
<div className="relative"> <div className="flex-1">
<Input <label className="text-sm font-medium mb-2 block text-textStandard">Extension Name</label>
value={name} <div className="relative">
onChange={(e) => onChange('name', e.target.value)} <Input
placeholder="Enter extension name..." value={name}
className={`${!submitAttempted || isNameValid() ? 'border-borderSubtle' : 'border-red-500'} text-textStandard focus:border-borderStandard`} onChange={(e) => onChange('name', e.target.value)}
placeholder="Enter extension name..."
className={`${!submitAttempted || isNameValid() ? 'border-borderSubtle' : 'border-red-500'} text-textStandard focus:border-borderStandard`}
/>
{submitAttempted && !isNameValid() && (
<div className="absolute text-xs text-red-500 mt-1">Name is required</div>
)}
</div>
</div>
{/* Type Dropdown */}
<div className="w-[200px]">
<label className="text-sm font-medium mb-2 block text-textStandard">Type</label>
<Select
value={{ value: type, label: type.toUpperCase() }}
onChange={(option: { value: string; label: string } | null) => {
if (option) {
onChange('type', option.value);
}
}}
options={[
{ value: 'stdio', label: 'Standard IO (STDIO)' },
{ value: 'sse', label: 'Security Service Edge (SSE)' },
]}
isSearchable={false}
/> />
{submitAttempted && !isNameValid() && (
<div className="absolute text-xs text-red-500 mt-1">Name is required</div>
)}
</div> </div>
</div> </div>
{/* Type Dropdown */}
<div className="w-[200px]"> {/* Bottom row with Description spanning full width */}
<label className="text-sm font-medium mb-2 block text-textStandard">Type</label> <div className="w-full">
<Select <label className="text-sm font-medium mb-2 block text-textStandard">Description</label>
value={{ value: type, label: type.toUpperCase() }} <div className="relative">
onChange={(option: { value: string; label: string } | null) => { <Input
if (option) { value={description}
onChange('type', option.value); onChange={(e) => onChange('description', e.target.value)}
} placeholder="Optional description..."
}} className={`text-textStandard focus:border-borderStandard`}
options={[ />
{ value: 'stdio', label: 'Standard IO (STDIO)' }, </div>
{ value: 'sse', label: 'Security Service Edge (SSE)' },
]}
isSearchable={false}
/>
</div> </div>
</div> </div>
); );
@@ -177,6 +177,7 @@ export default function ExtensionModal({
<ExtensionInfoFields <ExtensionInfoFields
name={formData.name} name={formData.name}
type={formData.type} type={formData.type}
description={formData.description}
onChange={(key, value) => setFormData({ ...formData, [key]: value })} onChange={(key, value) => setFormData({ ...formData, [key]: value })}
submitAttempted={submitAttempted} submitAttempted={submitAttempted}
/> />
@@ -59,10 +59,10 @@ export function getSubtitle(config: ExtensionConfig): string {
} }
if (config.type === 'stdio') { if (config.type === 'stdio') {
const full_command = combineCmdAndArgs(config.cmd, config.args); const full_command = combineCmdAndArgs(config.cmd, config.args);
return `STDIO extension${full_command ? `\n${full_command}` : ''}`; return `STDIO extension${config.description ? `: ${config.description}` : ''}${full_command ? `\n${full_command}` : ''}`;
} }
if (config.type === 'sse') { if (config.type === 'sse') {
return `SSE extension${config.uri ? ` (${config.uri})` : ''}`; return `SSE extension${config.description ? `: ${config.description}` : ''}${config.uri ? ` (${config.uri})` : ''}`;
} }
return `Unknown type of extension`; return `Unknown type of extension`;
} }
@@ -3,6 +3,7 @@ import { ExtensionConfig } from '../../../api/types.gen';
export interface ExtensionFormData { export interface ExtensionFormData {
name: string; name: string;
description: string;
type: 'stdio' | 'sse' | 'builtin'; type: 'stdio' | 'sse' | 'builtin';
cmd?: string; cmd?: string;
endpoint?: string; endpoint?: string;
@@ -13,6 +14,7 @@ export interface ExtensionFormData {
export function getDefaultFormData(): ExtensionFormData { export function getDefaultFormData(): ExtensionFormData {
return { return {
name: '', name: '',
description: '',
type: 'stdio', type: 'stdio',
cmd: '', cmd: '',
endpoint: '', endpoint: '',
@@ -35,6 +37,8 @@ export function extensionToFormData(extension: FixedExtensionEntry): ExtensionFo
return { return {
name: extension.name, name: extension.name,
description:
extension.type === 'stdio' || extension.type === 'sse' ? extension.description : undefined,
type: extension.type, type: extension.type,
cmd: extension.type === 'stdio' ? combineCmdAndArgs(extension.cmd, extension.args) : undefined, cmd: extension.type === 'stdio' ? combineCmdAndArgs(extension.cmd, extension.args) : undefined,
endpoint: extension.type === 'sse' ? extension.uri : undefined, endpoint: extension.type === 'sse' ? extension.uri : undefined,
@@ -61,6 +65,7 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon
return { return {
type: 'stdio', type: 'stdio',
name: formData.name, name: formData.name,
description: formData.description,
cmd: cmd, cmd: cmd,
args: args, args: args,
...(Object.keys(envs).length > 0 ? { envs } : {}), ...(Object.keys(envs).length > 0 ? { envs } : {}),
@@ -69,6 +74,7 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon
return { return {
type: 'sse', type: 'sse',
name: formData.name, name: formData.name,
description: formData.description,
uri: formData.endpoint, // Assuming endpoint maps to uri for SSE type uri: formData.endpoint, // Assuming endpoint maps to uri for SSE type
...(Object.keys(envs).length > 0 ? { envs } : {}), ...(Object.keys(envs).length > 0 ? { envs } : {}),
}; };