diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index 15a54a7314..e4670c4b6d 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -1219,14 +1219,7 @@ export default function ChatInput({
- - -
- -
-
- Current Goose mode configuration -
+ {messages.length > 0 && ( void; -} - -export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProps) => { +export const BottomMenuModeSelection = () => { const [gooseMode, setGooseMode] = useState('auto'); const { read, upsert } = useConfig(); @@ -74,9 +69,6 @@ export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProp currentMode={gooseMode} showDescription={false} isApproveModeConfigure={false} - parentView="chat" - parentViewOptions={undefined} - setView={setView} handleModeChange={handleModeChange} /> diff --git a/ui/desktop/src/components/settings/SettingsView.tsx b/ui/desktop/src/components/settings/SettingsView.tsx index 7d7c4d0d31..dad9ab65aa 100644 --- a/ui/desktop/src/components/settings/SettingsView.tsx +++ b/ui/desktop/src/components/settings/SettingsView.tsx @@ -110,7 +110,7 @@ export default function SettingsView({ value="chat" className="mt-0 focus-visible:outline-none focus-visible:ring-0" > - + void; -} - -export default function ChatSettingsSection({ setView }: ChatSettingsSectionProps) { +export default function ChatSettingsSection() { return (
@@ -19,7 +14,7 @@ export default function ChatSettingsSection({ setView }: ChatSettingsSectionProp Configure how Goose interacts with tools and extensions - + @@ -64,7 +59,7 @@ export default function ChatSettingsSection({ setView }: ChatSettingsSectionProp - +
diff --git a/ui/desktop/src/components/settings/mode/ConfigureApproveMode.tsx b/ui/desktop/src/components/settings/mode/ConfigureApproveMode.tsx index be4cbcf627..e462901bf9 100644 --- a/ui/desktop/src/components/settings/mode/ConfigureApproveMode.tsx +++ b/ui/desktop/src/components/settings/mode/ConfigureApproveMode.tsx @@ -70,9 +70,6 @@ export function ConfigureApproveMode({ showDescription={true} currentMode={approveMode || ''} isApproveModeConfigure={true} - parentView={'settings' as const} - parentViewOptions={undefined} - setView={() => {}} // No-op since we're in configure mode handleModeChange={(newMode) => { setApproveMode(newMode); }} diff --git a/ui/desktop/src/components/settings/mode/ModeSection.tsx b/ui/desktop/src/components/settings/mode/ModeSection.tsx index 70b5ce82e4..a53b86272b 100644 --- a/ui/desktop/src/components/settings/mode/ModeSection.tsx +++ b/ui/desktop/src/components/settings/mode/ModeSection.tsx @@ -1,14 +1,9 @@ import { useEffect, useState, useCallback } from 'react'; import { all_goose_modes, ModeSelectionItem } from './ModeSelectionItem'; -import { View, ViewOptions } from '../../../App'; import { useConfig } from '../../ConfigContext'; import { Input } from '../../ui/input'; -interface ModeSectionProps { - setView: (view: View, viewOptions?: ViewOptions) => void; -} - -export const ModeSection = ({ setView }: ModeSectionProps) => { +export const ModeSection = () => { const [currentMode, setCurrentMode] = useState('auto'); const [maxTurns, setMaxTurns] = useState(1000); const { read, upsert } = useConfig(); @@ -68,9 +63,6 @@ export const ModeSection = ({ setView }: ModeSectionProps) => { currentMode={currentMode} showDescription={true} isApproveModeConfigure={false} - parentView="settings" - parentViewOptions={{ section: 'chat' }} - setView={setView} handleModeChange={handleModeChange} /> ))} diff --git a/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx b/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx index 4cc52f0316..515b5b298d 100644 --- a/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx +++ b/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, forwardRef } from 'react'; import { Gear } from '../../icons'; import { ConfigureApproveMode } from './ConfigureApproveMode'; -import { View, ViewOptions } from '../../../App'; +import PermissionRulesModal from '../permission/PermissionRulesModal'; export interface GooseMode { key: string; @@ -37,28 +37,14 @@ interface ModeSelectionItemProps { mode: GooseMode; showDescription: boolean; isApproveModeConfigure: boolean; - parentView: View; - parentViewOptions?: ViewOptions; - setView: (view: View, viewOptions?: ViewOptions) => void; handleModeChange: (newMode: string) => void; } export const ModeSelectionItem = forwardRef( - ( - { - currentMode, - mode, - showDescription, - isApproveModeConfigure, - parentView, - parentViewOptions, - setView, - handleModeChange, - }, - ref - ) => { + ({ currentMode, mode, showDescription, isApproveModeConfigure, handleModeChange }, ref) => { const [checked, setChecked] = useState(currentMode == mode.key); const [isDialogOpen, setIsDialogOpen] = useState(false); + const [isPermissionModalOpen, setIsPermissionModalOpen] = useState(false); useEffect(() => { setChecked(currentMode === mode.key); @@ -80,11 +66,9 @@ export const ModeSelectionItem = forwardRef {!isApproveModeConfigure && (mode.key == 'approve' || mode.key == 'smart_approve') && (
+ + {/* Permission Rules Modal */} + setIsPermissionModalOpen(false)} + /> ); } diff --git a/ui/desktop/src/components/settings/permission/PermissionRulesModal.tsx b/ui/desktop/src/components/settings/permission/PermissionRulesModal.tsx new file mode 100644 index 0000000000..38b51ba19e --- /dev/null +++ b/ui/desktop/src/components/settings/permission/PermissionRulesModal.tsx @@ -0,0 +1,142 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/dialog'; +import { ScrollArea } from '../../ui/scroll-area'; +import { FixedExtensionEntry, useConfig } from '../../ConfigContext'; +import { ChevronRight } from 'lucide-react'; +import PermissionModal from './PermissionModal'; +import { Button } from '../../ui/button'; + +function RuleItem({ title, description }: { title: string; description: string }) { + const [isModalOpen, setIsModalOpen] = useState(false); + + const handleModalClose = () => { + setIsModalOpen(false); + }; + + return ( + <> + + {isModalOpen && } + + ); +} + +function RulesSection({ title, rules }: { title: string; rules: React.ReactNode }) { + return ( +
+

{title}

+ {rules} +
+ ); +} + +interface PermissionRulesModalProps { + isOpen: boolean; + onClose: () => void; +} + +export default function PermissionRulesModal({ isOpen, onClose }: PermissionRulesModalProps) { + const { getExtensions } = useConfig(); + const [extensions, setExtensions] = useState([]); + + const fetchExtensions = useCallback(async () => { + const extensionsList = await getExtensions(true); // Force refresh + // Filter out disabled extensions + const enabledExtensions = extensionsList.filter((extension) => extension.enabled); + enabledExtensions.push({ + name: 'platform', + type: 'builtin', + enabled: true, + }); + // Sort extensions by name to maintain consistent order + const sortedExtensions = [...enabledExtensions].sort((a, b) => { + // First sort by builtin + if (a.type === 'builtin' && b.type !== 'builtin') return -1; + if (a.type !== 'builtin' && b.type === 'builtin') return 1; + + // Then sort by bundled (handle null/undefined cases) + const aBundled = a.bundled === true; + const bBundled = b.bundled === true; + if (aBundled && !bBundled) return -1; + if (!aBundled && bBundled) return 1; + + // Finally sort alphabetically within each group + return a.name.localeCompare(b.name); + }); + setExtensions(sortedExtensions); + }, [getExtensions]); + + useEffect(() => { + if (isOpen) { + fetchExtensions(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOpen]); + + return ( + + + +
+
+ + + + + +
+
+ + Permission Rules + +

+ Configure tool permissions for extensions to control how they interact with your + system. +

+
+
+
+ + +
+ {/* Extension Rules Section */} + + {extensions.map((extension) => ( + + ))} + + } + /> +
+
+
+
+ ); +} diff --git a/ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx b/ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx index 82125bf7a8..e7b0ac9941 100644 --- a/ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx +++ b/ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx @@ -1,12 +1,7 @@ import { useEffect, useState, useCallback } from 'react'; -import { View, ViewOptions } from '../../../App'; import { useConfig } from '../../ConfigContext'; import { getApiUrl, getSecretKey } from '../../../config'; -interface ToolSelectionStrategySectionProps { - setView: (view: View, viewOptions?: ViewOptions) => void; -} - interface ToolSelectionStrategy { key: string; label: string; @@ -32,9 +27,7 @@ export const all_tool_selection_strategies: ToolSelectionStrategy[] = [ }, ]; -export const ToolSelectionStrategySection = ({ - setView: _setView, -}: ToolSelectionStrategySectionProps) => { +export const ToolSelectionStrategySection = () => { const [currentStrategy, setCurrentStrategy] = useState('default'); const [_error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false);