mirror of
https://github.com/aaif-goose/goose.git
synced 2026-06-02 06:14:27 +02:00
fix: remove global extension toggles
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
This commit is contained in:
@@ -38,14 +38,3 @@ export async function removeExtension(configKey: string): Promise<void> {
|
||||
const client = await getClient();
|
||||
await client.goose.GooseConfigExtensionsRemove({ configKey });
|
||||
}
|
||||
|
||||
export async function setExtensionEnabled(
|
||||
configKey: string,
|
||||
enabled: boolean,
|
||||
): Promise<void> {
|
||||
const client = await getClient();
|
||||
await client.goose.GooseConfigExtensionsToggle({
|
||||
configKey,
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
addExtension,
|
||||
listExtensions,
|
||||
removeExtension,
|
||||
setExtensionEnabled,
|
||||
} from "../api/extensions";
|
||||
import { nameToKey } from "../lib/extensionKeys";
|
||||
import {
|
||||
getDisplayName,
|
||||
type ExtensionConfig,
|
||||
type ExtensionEntry,
|
||||
} from "../types";
|
||||
import type { ExtensionConfig, ExtensionEntry } from "../types";
|
||||
|
||||
type ExtensionModalMode = "add" | "edit" | null;
|
||||
|
||||
@@ -23,7 +18,6 @@ export function useExtensionsSettings() {
|
||||
const [modalMode, setModalMode] = useState<ExtensionModalMode>(null);
|
||||
const [editingExtension, setEditingExtension] =
|
||||
useState<ExtensionEntry | null>(null);
|
||||
const toggleVersions = useRef<Record<string, number>>({});
|
||||
|
||||
const fetchExtensions = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
@@ -95,42 +89,6 @@ export function useExtensionsSettings() {
|
||||
[fetchExtensions, t],
|
||||
);
|
||||
|
||||
const handleToggleEnabled = useCallback(
|
||||
async (extension: ExtensionEntry, enabled: boolean) => {
|
||||
const configKey = extension.config_key;
|
||||
const version = (toggleVersions.current[configKey] ?? 0) + 1;
|
||||
toggleVersions.current[configKey] = version;
|
||||
|
||||
setExtensions((current) =>
|
||||
current.map((item) =>
|
||||
item.config_key === configKey ? { ...item, enabled } : item,
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
await setExtensionEnabled(configKey, enabled);
|
||||
if (toggleVersions.current[configKey] === version) {
|
||||
await fetchExtensions();
|
||||
}
|
||||
} catch {
|
||||
if (toggleVersions.current[configKey] !== version) return;
|
||||
setExtensions((current) =>
|
||||
current.map((item) =>
|
||||
item.config_key === configKey
|
||||
? { ...item, enabled: extension.enabled }
|
||||
: item,
|
||||
),
|
||||
);
|
||||
toast.error(
|
||||
t("extensions.errors.toggleFailed", {
|
||||
name: getDisplayName(extension),
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
[fetchExtensions, t],
|
||||
);
|
||||
|
||||
const handleModalClose = useCallback(() => {
|
||||
setModalMode(null);
|
||||
setEditingExtension(null);
|
||||
@@ -145,7 +103,6 @@ export function useExtensionsSettings() {
|
||||
handleConfigure,
|
||||
handleSubmit,
|
||||
handleDelete,
|
||||
handleToggleEnabled,
|
||||
handleModalClose,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { IconSettings } from "@tabler/icons-react";
|
||||
import { cn } from "@/shared/lib/cn";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Switch } from "@/shared/ui/switch";
|
||||
import { getDisplayName, type ExtensionEntry } from "../types";
|
||||
|
||||
interface ExtensionItemProps {
|
||||
extension: ExtensionEntry;
|
||||
onConfigure?: (extension: ExtensionEntry) => void;
|
||||
onToggleEnabled?: (extension: ExtensionEntry, enabled: boolean) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -32,12 +30,10 @@ function isEditable(ext: ExtensionEntry): boolean {
|
||||
export function ExtensionItem({
|
||||
extension,
|
||||
onConfigure,
|
||||
onToggleEnabled,
|
||||
className,
|
||||
}: ExtensionItemProps) {
|
||||
const { t } = useTranslation("settings");
|
||||
const editable = isEditable(extension);
|
||||
const toggleable = isUserManagedExtension(extension);
|
||||
const displayName = getDisplayName(extension);
|
||||
|
||||
return (
|
||||
@@ -56,16 +52,6 @@ export function ExtensionItem({
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
{toggleable && onToggleEnabled ? (
|
||||
<Switch
|
||||
checked={extension.enabled}
|
||||
onCheckedChange={(enabled) => onToggleEnabled(extension, enabled)}
|
||||
aria-label={t(
|
||||
extension.enabled ? "extensions.disable" : "extensions.enable",
|
||||
{ name: displayName },
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
{editable && onConfigure && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -48,7 +48,6 @@ export function ExtensionsSettings() {
|
||||
handleConfigure,
|
||||
handleSubmit,
|
||||
handleDelete,
|
||||
handleToggleEnabled,
|
||||
handleModalClose,
|
||||
} = useExtensionsSettings();
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
@@ -105,7 +104,6 @@ export function ExtensionsSettings() {
|
||||
key={ext.config_key}
|
||||
extension={ext}
|
||||
onConfigure={handleConfigure}
|
||||
onToggleEnabled={handleToggleEnabled}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -39,10 +39,7 @@ const extensions: ExtensionEntry[] = [
|
||||
];
|
||||
|
||||
describe("ExtensionsSettings", () => {
|
||||
let handleToggleEnabled: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
handleToggleEnabled = vi.fn();
|
||||
mockUseExtensionsSettings.mockReturnValue({
|
||||
extensions,
|
||||
isLoading: false,
|
||||
@@ -52,7 +49,6 @@ describe("ExtensionsSettings", () => {
|
||||
handleConfigure: vi.fn(),
|
||||
handleSubmit: vi.fn(),
|
||||
handleDelete: vi.fn(),
|
||||
handleToggleEnabled,
|
||||
handleModalClose: vi.fn(),
|
||||
});
|
||||
});
|
||||
@@ -73,10 +69,14 @@ describe("ExtensionsSettings", () => {
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show global enable toggles for Goose capabilities", async () => {
|
||||
it("does not show global enable toggles", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<ExtensionsSettings />);
|
||||
|
||||
expect(
|
||||
screen.queryByRole("switch", { name: /disable github/i }),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
await user.type(screen.getByRole("searchbox"), "summarize");
|
||||
|
||||
expect(
|
||||
|
||||
@@ -82,8 +82,6 @@
|
||||
"editExtension": "Edit Extension",
|
||||
"deleteExtension": "Delete Extension",
|
||||
"configure": "Configure {{name}}",
|
||||
"enable": "Enable {{name}}",
|
||||
"disable": "Disable {{name}}",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"fields": {
|
||||
@@ -126,7 +124,6 @@
|
||||
"errors": {
|
||||
"saveFailed": "Failed to save extension. Please try again.",
|
||||
"deleteFailed": "Failed to delete extension. Please try again.",
|
||||
"toggleFailed": "Failed to update {{name}}. Please try again.",
|
||||
"nameConflict": "An extension named \"{{name}}\" already exists."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -82,8 +82,6 @@
|
||||
"editExtension": "Editar extensión",
|
||||
"deleteExtension": "Eliminar extensión",
|
||||
"configure": "Configurar {{name}}",
|
||||
"enable": "Activar {{name}}",
|
||||
"disable": "Desactivar {{name}}",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"fields": {
|
||||
@@ -126,7 +124,6 @@
|
||||
"errors": {
|
||||
"saveFailed": "Error al guardar la extensión. Inténtalo de nuevo.",
|
||||
"deleteFailed": "Error al eliminar la extensión. Inténtalo de nuevo.",
|
||||
"toggleFailed": "Error al actualizar {{name}}. Inténtalo de nuevo.",
|
||||
"nameConflict": "Ya existe una extensión llamada \"{{name}}\"."
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user