add extensions settings page and context panel widget (#8540)

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
This commit is contained in:
morgmart
2026-04-15 09:55:46 -07:00
committed by GitHub
parent a7396a7bd0
commit c9507ba672
18 changed files with 1208 additions and 32 deletions
@@ -46,7 +46,8 @@
"folderNotSet": "Folder not set",
"noChanges": "No uncommitted changes",
"noProjectAssigned": "No project assigned.",
"noServersConfigured": "No servers configured"
"noExtensions": "No extensions enabled",
"noMatchingExtensions": "No matching extensions"
},
"errors": {
"gitRead": "Unable to read git status."
@@ -89,7 +90,8 @@
"artifacts": "Artifacts",
"changes": "Changes",
"changesOnBranch": "on",
"mcpServers": "MCP Servers",
"extensions": "Extensions",
"searchExtensions": "Search extensions...",
"workspace": "Workspace"
}
},
@@ -55,6 +55,54 @@
"description": "Are you sure you want to permanently delete \"{{name}}\"? This cannot be undone.",
"title": "Delete project permanently?"
},
"extensions": {
"title": "Extensions",
"description": "Manage MCP extensions that add tools and capabilities to Goose.",
"search": "Search extensions...",
"enabled": "Enabled",
"enabledCount": "Enabled ({{count}})",
"available": "Available",
"availableCount": "Available ({{count}})",
"empty": "No extensions configured.",
"noResults": "No extensions match your search.",
"addExtension": "Add Extension",
"editExtension": "Edit Extension",
"deleteExtension": "Delete Extension",
"toggle": "Toggle {{name}}",
"configure": "Configure {{name}}",
"save": "Save",
"cancel": "Cancel",
"fields": {
"name": "Name",
"namePlaceholder": "my-extension",
"type": "Type",
"command": "Command",
"commandPlaceholder": "npx",
"arguments": "Arguments",
"argumentsPlaceholder": "One argument per line",
"url": "URL",
"urlPlaceholder": "http://localhost:3000/mcp",
"description": "Description",
"descriptionPlaceholder": "What does this extension do?",
"timeout": "Timeout (seconds)",
"envVars": "Environment Variables",
"envKeyPlaceholder": "KEY",
"envValuePlaceholder": "value",
"addEnvVar": "Add variable",
"removeEnvVar": "Remove variable"
},
"types": {
"stdio": "Stdio",
"streamable_http": "HTTP",
"builtin": "Built-in"
},
"errors": {
"toggleFailed": "Failed to toggle extension. Please try again.",
"saveFailed": "Failed to save extension. Please try again.",
"deleteFailed": "Failed to delete extension. Please try again.",
"nameConflict": "An extension named \"{{name}}\" already exists."
}
},
"doctor": {
"agents": "Agents",
"copied": "Copied",
@@ -85,6 +133,7 @@
"doctor": "Doctor",
"general": "General",
"projects": "Projects",
"extensions": "Extensions",
"providers": "Providers"
},
"projects": {
@@ -46,7 +46,8 @@
"folderNotSet": "Carpeta no configurada",
"noChanges": "No hay cambios sin confirmar",
"noProjectAssigned": "No hay proyecto asignado.",
"noServersConfigured": "No hay servidores configurados"
"noExtensions": "No hay extensiones habilitadas",
"noMatchingExtensions": "No hay extensiones que coincidan"
},
"errors": {
"gitRead": "No se pudo leer el estado de git."
@@ -89,7 +90,8 @@
"artifacts": "Artefactos",
"changes": "Cambios",
"changesOnBranch": "en",
"mcpServers": "Servidores MCP",
"extensions": "Extensiones",
"searchExtensions": "Buscar extensiones...",
"workspace": "Espacio de trabajo"
}
},
@@ -55,6 +55,54 @@
"description": "¿Seguro que quieres eliminar permanentemente \"{{name}}\"? Esta acción no se puede deshacer.",
"title": "¿Eliminar el proyecto de forma permanente?"
},
"extensions": {
"title": "Extensiones",
"description": "Administra las extensiones MCP que agregan herramientas y capacidades a Goose.",
"search": "Buscar extensiones...",
"enabled": "Habilitadas",
"enabledCount": "Habilitadas ({{count}})",
"available": "Disponibles",
"availableCount": "Disponibles ({{count}})",
"empty": "No hay extensiones configuradas.",
"noResults": "Ninguna extensión coincide con tu búsqueda.",
"addExtension": "Agregar extensión",
"editExtension": "Editar extensión",
"deleteExtension": "Eliminar extensión",
"toggle": "Activar/desactivar {{name}}",
"configure": "Configurar {{name}}",
"save": "Guardar",
"cancel": "Cancelar",
"fields": {
"name": "Nombre",
"namePlaceholder": "mi-extension",
"type": "Tipo",
"command": "Comando",
"commandPlaceholder": "npx",
"arguments": "Argumentos",
"argumentsPlaceholder": "Un argumento por línea",
"url": "URL",
"urlPlaceholder": "http://localhost:3000/mcp",
"description": "Descripción",
"descriptionPlaceholder": "¿Qué hace esta extensión?",
"timeout": "Tiempo de espera (segundos)",
"envVars": "Variables de entorno",
"envKeyPlaceholder": "CLAVE",
"envValuePlaceholder": "valor",
"addEnvVar": "Agregar variable",
"removeEnvVar": "Eliminar variable"
},
"types": {
"stdio": "Stdio",
"streamable_http": "HTTP",
"builtin": "Integrada"
},
"errors": {
"toggleFailed": "Error al cambiar la extensión. Inténtalo de nuevo.",
"saveFailed": "Error al guardar la extensión. Inténtalo de nuevo.",
"deleteFailed": "Error al eliminar la extensión. Inténtalo de nuevo.",
"nameConflict": "Ya existe una extensión llamada \"{{name}}\"."
}
},
"doctor": {
"agents": "Agentes",
"copied": "Copiado",
@@ -85,6 +133,7 @@
"doctor": "Diagnóstico",
"general": "General",
"projects": "Proyectos",
"extensions": "Extensiones",
"providers": "Proveedores"
},
"projects": {
+16 -7
View File
@@ -2,17 +2,26 @@ import type * as React from "react";
import { cn } from "@/shared/lib/cn";
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
const variantStyles = {
default: [
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input hover:border-border-input-hover focus-visible:border-ring focus-visible:ring-0 focus-visible:ring-offset-0 flex h-9 w-full min-w-0 rounded-input border bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
],
ghost: [
"w-full border-none bg-transparent text-sm text-foreground shadow-none outline-none placeholder:text-muted-foreground focus:outline-none focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50",
],
} as const;
interface InputProps extends React.ComponentProps<"input"> {
variant?: keyof typeof variantStyles;
}
function Input({ className, type, variant = "default", ...props }: InputProps) {
return (
<input
type={type}
data-slot="input"
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input hover:border-border-input-hover focus-visible:border-ring focus-visible:ring-0 focus-visible:ring-offset-0 flex h-9 w-full min-w-0 rounded-input border bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className,
)}
className={cn(...variantStyles[variant], className)}
{...props}
/>
);