diff --git a/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx b/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx index f68d4ceabc48..d13adcdfeb83 100644 --- a/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx +++ b/ui/desktop/src/components/GooseSidebar/AppSidebar.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { FileText, Clock, Home, Puzzle, History, FolderKanban } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; -import { motion, AnimatePresence } from 'framer-motion'; +import { motion } from 'framer-motion'; import { SidebarContent, SidebarFooter, @@ -10,7 +10,6 @@ import { SidebarMenuButton, SidebarGroup, SidebarGroupContent, - useSidebar, SidebarSeparator, } from '../ui/sidebar'; import { ChatSmart, Gear } from '../icons'; @@ -18,7 +17,6 @@ import { ViewOptions, View } from '../../App'; import { Recipe } from '../../recipe'; import { saveRecipe } from '../../recipe/recipeStorage'; import { useChatContext } from '../../contexts/ChatContext'; -import { Goose } from '../icons/Goose'; interface SidebarProps { onSelectSession: (sessionId: string) => void; @@ -38,7 +36,6 @@ const AppSidebar: React.FC = ({ currentPath }) => { const [saveGlobal, setSaveGlobal] = useState(true); const [saving, setSaving] = useState(false); const { resetChat } = useChatContext(); - const { state } = useSidebar(); useEffect(() => { // Trigger animation after a small delay @@ -282,33 +279,7 @@ const AppSidebar: React.FC = ({ currentPath }) => { - - -
- - - {state === 'expanded' && ( - - codename -
goose -
- )} -
-
-
-
+ {/* Save Recipe Dialog */} {showSaveDialog && ( diff --git a/ui/desktop/src/components/Splash.tsx b/ui/desktop/src/components/Splash.tsx index 92c0e10a5483..ebe11000cde0 100644 --- a/ui/desktop/src/components/Splash.tsx +++ b/ui/desktop/src/components/Splash.tsx @@ -40,7 +40,6 @@ export default function Splash({ append, activities, title }: SplashProps) {
-
{messagePill && (
diff --git a/ui/desktop/src/components/UserMessage.tsx b/ui/desktop/src/components/UserMessage.tsx index 1fbeb67cef46..9415e484e239 100644 --- a/ui/desktop/src/components/UserMessage.tsx +++ b/ui/desktop/src/components/UserMessage.tsx @@ -31,7 +31,7 @@ export default function UserMessage({ message }: UserMessageProps) { const urls = extractUrls(displayText, []); return ( -
+
@@ -52,11 +52,11 @@ export default function UserMessage({ message }: UserMessageProps) {
)} -
-
+
+
{timestamp}
-
+
diff --git a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx index 8917addac94c..72e20517e919 100644 --- a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx +++ b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx @@ -58,9 +58,9 @@ export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProp {getValueByKey(gooseMode).toLowerCase()} - + {all_goose_modes.map((mode) => ( - + -

- {/* {greeting.prefix} */} - {greeting.message} -

-
+

+ {/* {greeting.prefix} */} + {greeting.message} +

); } diff --git a/ui/desktop/src/components/sessions/SessionsInsights.tsx b/ui/desktop/src/components/sessions/SessionsInsights.tsx index 1beb57057ded..f0e022b1eb6a 100644 --- a/ui/desktop/src/components/sessions/SessionsInsights.tsx +++ b/ui/desktop/src/components/sessions/SessionsInsights.tsx @@ -9,6 +9,7 @@ import { useNavigate } from 'react-router-dom'; import { Button } from '../ui/button'; import { ChatSmart } from '../icons/'; import { motion, AnimatePresence } from 'framer-motion'; +import { Goose } from '../icons/Goose'; interface SessionInsightsType { totalSessions: number; @@ -106,7 +107,17 @@ export function SessionInsights() { return (
- +
+ + + + +
{/* Top row with three equal columns */} diff --git a/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx b/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx index c6dec41e5f6b..eefa9a1b5880 100644 --- a/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx +++ b/ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx @@ -59,17 +59,15 @@ export function ModeSelectionItem({ }, [currentMode, mode.key]); return ( -
+
handleModeChange(mode.key)} >
-

{mode.label}

- {showDescription && ( -

{mode.description}

- )} +

{mode.label}

+ {showDescription &&

{mode.description}

}
diff --git a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx index a58c0380c79c..12c1875344a7 100644 --- a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx +++ b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx @@ -1,11 +1,17 @@ import { Sliders } from 'lucide-react'; -import React, { useEffect, useState, useRef } from 'react'; +import React, { useEffect, useState, useRef, useCallback } from 'react'; import { useModelAndProvider } from '../../../ModelAndProviderContext'; import { AddModelModal } from '../subcomponents/AddModelModal'; import { LeadWorkerSettings } from '../subcomponents/LeadWorkerSettings'; import { View } from '../../../../App'; -import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '../../../ui/Tooltip'; +import { Tooltip, TooltipTrigger, TooltipContent } from '../../../ui/Tooltip'; import { Dialog, DialogContent } from '../../../ui/dialog'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '../../../ui/dropdown-menu'; import { useCurrentModelInfo } from '../../../ChatView'; import { useConfig } from '../../../ConfigContext'; @@ -17,15 +23,14 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa const { currentModel } = useModelAndProvider(); const currentModelInfo = useCurrentModelInfo(); const { read } = useConfig(); - const [isModelMenuOpen, setIsModelMenuOpen] = useState(false); const [isAddModelModalOpen, setIsAddModelModalOpen] = useState(false); const [isLeadWorkerModalOpen, setIsLeadWorkerModalOpen] = useState(false); const [isLeadWorkerActive, setIsLeadWorkerActive] = useState(false); - const menuRef = useRef(null); const [isModelTruncated, setIsModelTruncated] = useState(false); // eslint-disable-next-line no-undef const modelRef = useRef(null); const [isTooltipOpen, setIsTooltipOpen] = useState(false); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); // Check if lead/worker mode is active useEffect(() => { @@ -62,35 +67,31 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa setIsTooltipOpen(false); }, [isModelTruncated]); - // Add click outside handler - useEffect(() => { - function handleClickOutside(event: MouseEvent) { - if (menuRef.current && !menuRef.current.contains(event.target as Node)) { - setIsModelMenuOpen(false); + const handleTooltipOpenChange = useCallback( + (open: boolean) => { + // Only allow tooltip to open if dropdown is closed + if (!isDropdownOpen) { + setIsTooltipOpen(open); } - } + }, + [isDropdownOpen] + ); - // Add the event listener when the menu is open - if (isModelMenuOpen) { - document.addEventListener('mousedown', handleClickOutside); + const handleDropdownOpenChange = useCallback((open: boolean) => { + setIsDropdownOpen(open); + // Close tooltip when dropdown opens + if (open) { + setIsTooltipOpen(false); } - - // Clean up the event listener - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [isModelMenuOpen]); + }, []); return (
-
-
setIsModelMenuOpen(!isModelMenuOpen)} - > - - - + + + + +
({modelMode}) )} - - {isModelTruncated && ( - - {displayModel} - {isLeadWorkerActive && modelMode && ( - ({modelMode}) - )} - - )} - - -
- - {/* Dropdown Menu */} - {isModelMenuOpen && ( -
-
-
{ - setIsModelMenuOpen(false); - setIsAddModelModalOpen(true); - }} - > - Change Model - -
-
{ - setIsModelMenuOpen(false); - setIsLeadWorkerModalOpen(true); - }} - > - Lead/Worker Settings -
-
-
+
+ + setIsAddModelModalOpen(true)}> + Change Model + + + setIsLeadWorkerModalOpen(true)}> + Lead/Worker Settings + + + +
+
+ {isModelTruncated && ( + + {displayModel} + {isLeadWorkerActive && modelMode && ( + ({modelMode}) + )} + )} -
+ {isAddModelModalOpen ? ( setIsAddModelModalOpen(false)} /> diff --git a/ui/desktop/src/components/settings/permission/PermissionModal.tsx b/ui/desktop/src/components/settings/permission/PermissionModal.tsx index b29097e7014d..8455fc3f4c83 100644 --- a/ui/desktop/src/components/settings/permission/PermissionModal.tsx +++ b/ui/desktop/src/components/settings/permission/PermissionModal.tsx @@ -1,9 +1,14 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { Button } from '../../ui/button'; import { ChevronDownIcon, SlidersHorizontal } from 'lucide-react'; import { getTools, PermissionLevel, ToolInfo, upsertPermissions } from '../../../api'; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '../../ui/dialog'; -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, +} from '../../ui/dropdown-menu'; function getFirstSentence(text: string): string { const match = text.match(/^([^.?!]+[.?!])/); @@ -25,6 +30,13 @@ export default function PermissionModal({ extensionName, onClose }: PermissionMo const [tools, setTools] = useState([]); const [updatedPermissions, setUpdatedPermissions] = useState>({}); + const hasChanges = useMemo(() => { + return Object.keys(updatedPermissions).some( + (toolName) => + updatedPermissions[toolName] !== tools.find((tool) => tool.name === toolName)?.permission + ); + }, [updatedPermissions, tools]); + useEffect(() => { const fetchTools = async () => { try { @@ -53,6 +65,10 @@ export default function PermissionModal({ extensionName, onClose }: PermissionMo })); }; + const handleClose = () => { + onClose(); + }; + const handleSave = async () => { try { const payload = { @@ -82,7 +98,14 @@ export default function PermissionModal({ extensionName, onClose }: PermissionMo }; return ( - !open && onClose()}> + { + if (!open) { + handleClose(); + } + }} + > @@ -127,32 +150,29 @@ export default function PermissionModal({ extensionName, onClose }: PermissionMo {getFirstSentence(tool.description)}

- - - + + + + + + {permissionOptions.map((option) => ( + + handleSettingChange(tool.name, option.value as PermissionLevel) + } + > + {option.label} + + ))} + +
))}
@@ -160,10 +180,12 @@ export default function PermissionModal({ extensionName, onClose }: PermissionMo
- - + diff --git a/ui/desktop/src/components/settings/permission/PermissionSetting.tsx b/ui/desktop/src/components/settings/permission/PermissionSetting.tsx index 12e6f3c7abaf..6b701fb25a71 100644 --- a/ui/desktop/src/components/settings/permission/PermissionSetting.tsx +++ b/ui/desktop/src/components/settings/permission/PermissionSetting.tsx @@ -15,19 +15,22 @@ function RuleItem({ title, description }: { title: string; description: string } }; return ( -
-
-

{title}

-

{description}

-
-
- -
- {/* Modal for updating tool permission */} + <> + {isModalOpen && } -
+ ); } @@ -77,20 +80,20 @@ export default function PermissionSettingsView({ onClose }: { onClose: () => voi }, []); return ( -
+
onClose()} className="mb-4" /> -
+
code.bg-inline-code) { li > code.bg-inline-code, p > code.bg-inline-code { - color: var(--block-orange); + color: var(--color-block-orange); padding: 2px 4px; }