diff --git a/ui/desktop/package-lock.json b/ui/desktop/package-lock.json
index 4392ad14..c72dee99 100644
--- a/ui/desktop/package-lock.json
+++ b/ui/desktop/package-lock.json
@@ -37,7 +37,7 @@
"express": "^4.21.1",
"framer-motion": "^11.11.11",
"lodash": "^4.17.21",
- "lucide-react": "^0.454.0",
+ "lucide-react": "^0.475.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
@@ -11454,12 +11454,12 @@
}
},
"node_modules/lucide-react": {
- "version": "0.454.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.454.0.tgz",
- "integrity": "sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==",
+ "version": "0.475.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.475.0.tgz",
+ "integrity": "sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg==",
"license": "ISC",
"peerDependencies": {
- "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/magic-string": {
diff --git a/ui/desktop/package.json b/ui/desktop/package.json
index cffe67cd..4eb409e1 100644
--- a/ui/desktop/package.json
+++ b/ui/desktop/package.json
@@ -109,7 +109,7 @@
"express": "^4.21.1",
"framer-motion": "^11.11.11",
"lodash": "^4.17.21",
- "lucide-react": "^0.454.0",
+ "lucide-react": "^0.475.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
diff --git a/ui/desktop/src/components/Input.tsx b/ui/desktop/src/components/ChatInput.tsx
similarity index 68%
rename from ui/desktop/src/components/Input.tsx
rename to ui/desktop/src/components/ChatInput.tsx
index 6127d0bb..669eaef3 100644
--- a/ui/desktop/src/components/Input.tsx
+++ b/ui/desktop/src/components/ChatInput.tsx
@@ -1,8 +1,10 @@
import React, { useRef, useState, useEffect, useCallback } from 'react';
import { Button } from './ui/button';
+import type { View } from '../App';
import Stop from './ui/Stop';
import { Attach, Send } from './icons';
import { debounce } from 'lodash';
+import BottomMenu from './bottom_menu/BottomMenu';
interface InputProps {
handleSubmit: (e: React.FormEvent) => void;
@@ -11,6 +13,8 @@ interface InputProps {
commandHistory?: string[];
initialValue?: string;
droppedFiles?: string[];
+ setView: (view: View) => void;
+ numTokens?: number;
}
export default function Input({
@@ -19,10 +23,13 @@ export default function Input({
onStop,
commandHistory = [],
initialValue = '',
+ setView,
+ numTokens,
droppedFiles = [],
}: InputProps) {
const [_value, setValue] = useState(initialValue);
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
+ const [isFocused, setIsFocused] = useState(false);
// Update internal value when initialValue changes
useEffect(() => {
@@ -205,65 +212,82 @@ export default function Input({
};
return (
-
+ className="w-full pl-4 pr-[68px] outline-none border-none focus:ring-0 bg-transparent pt-3 pb-1.5 text-sm resize-none text-textStandard"
+ />
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+
+
+
);
}
diff --git a/ui/desktop/src/components/ChatView.tsx b/ui/desktop/src/components/ChatView.tsx
index bbc402d8..f930c2ab 100644
--- a/ui/desktop/src/components/ChatView.tsx
+++ b/ui/desktop/src/components/ChatView.tsx
@@ -1,9 +1,7 @@
import React, { useEffect, useRef, useState, useMemo } from 'react';
import { getApiUrl } from '../config';
-import BottomMenu from './bottom_menu/BottomMenu';
import FlappyGoose from './FlappyGoose';
import GooseMessage from './GooseMessage';
-import Input from './Input';
import { type View, ViewOptions } from '../App';
import LoadingGoose from './LoadingGoose';
import MoreMenuLayout from './more_menu/MoreMenuLayout';
@@ -34,6 +32,7 @@ import {
ToolResponseMessageContent,
ToolConfirmationRequestMessageContent,
} from '../types/message';
+import ChatInput from './ChatInput';
export interface ChatType {
id: string;
@@ -469,9 +468,11 @@ function ChatContent({
{/* Loader when generating recipe */}
{isGeneratingRecipe &&
}
-
-
-
+
)}
-
+
)}
-
+
{isLoading && }
-
-
diff --git a/ui/desktop/src/components/LoadingGoose.tsx b/ui/desktop/src/components/LoadingGoose.tsx
index 1d37abe4..38677d18 100644
--- a/ui/desktop/src/components/LoadingGoose.tsx
+++ b/ui/desktop/src/components/LoadingGoose.tsx
@@ -6,7 +6,7 @@ const LoadingGoose = () => {
goose is working on it…
diff --git a/ui/desktop/src/components/WelcomeView.tsx b/ui/desktop/src/components/WelcomeView.tsx
index 5c651ccd..5003d740 100644
--- a/ui/desktop/src/components/WelcomeView.tsx
+++ b/ui/desktop/src/components/WelcomeView.tsx
@@ -3,6 +3,7 @@ import { ProviderGrid } from './ProviderGrid';
import { ScrollArea } from './ui/scroll-area';
import { Button } from './ui/button';
import WelcomeGooseLogo from './WelcomeGooseLogo';
+import MoreMenuLayout from './more_menu/MoreMenuLayout';
// Extending React CSSProperties to include custom webkit property
declare module 'react' {
@@ -18,11 +19,7 @@ interface WelcomeScreenProps {
export default function WelcomeScreen({ onSubmit }: WelcomeScreenProps) {
return (
- {/* Draggable title bar region */}
-
+
{/* Content area - explicitly set as non-draggable */}
void;
numTokens?: number;
}) {
@@ -34,10 +31,6 @@ export default function BottomMenu({
const toolCount = useToolCount();
const { getProviders, read } = useConfig();
const [tokenLimit, setTokenLimit] = useState
(TOKEN_LIMIT_DEFAULT);
- const [isDirTruncated, setIsDirTruncated] = useState(false);
- // eslint-disable-next-line no-undef
- const dirRef = useRef(null);
- const [isTooltipOpen, setIsTooltipOpen] = useState(false);
// Load providers and get current model's token limit
const loadProviderDetails = async () => {
@@ -142,62 +135,12 @@ export default function BottomMenu({
};
}, [isModelMenuOpen]);
- useEffect(() => {
- const checkTruncation = () => {
- if (dirRef.current) {
- setIsDirTruncated(dirRef.current.scrollWidth > dirRef.current.clientWidth);
- }
- };
- checkTruncation();
- window.addEventListener('resize', checkTruncation);
- return () => window.removeEventListener('resize', checkTruncation);
- }, []);
-
- useEffect(() => {
- setIsTooltipOpen(false);
- }, [isDirTruncated]);
-
return (
-
- {/* Directory Chooser - Always visible */}
-
{
- if (hasMessages) {
- window.electron.directoryChooser();
- } else {
- window.electron.directoryChooser(true);
- }
- }}
- >
-
-
-
-
-
- {window.appConfig.get('GOOSE_WORKING_DIR') as string}
-
-
- {isDirTruncated && (
-
- {window.appConfig.get('GOOSE_WORKING_DIR') as string}
-
- )}
-
-
-
-
-
- {/* Goose Mode Selector Dropdown */}
-
-
- {/* Right-side section with ToolCount and Model Selector together */}
-
+
+
{/* Tool and Token count */}
{}
+
{/* Model Selector Dropdown */}
{settingsV2Enabled ? (
@@ -262,13 +205,19 @@ export default function BottomMenu({
}}
>
Tools and Settings
-
+
)}
)}
+
+ {/* Separator */}
+
+
+ {/* Goose Mode Selector Dropdown */}
+
);
diff --git a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx
index 6858c89b..c0f6a309 100644
--- a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx
+++ b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx
@@ -1,10 +1,10 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
import { getApiUrl, getSecretKey } from '../../config';
-import { ChevronDown, ChevronUp } from '../icons';
import { all_goose_modes, ModeSelectionItem } from '../settings_v2/mode/ModeSelectionItem';
import { useConfig } from '../ConfigContext';
import { settingsV2Enabled } from '../../flags';
-import { View, ViewOptions } from '../App';
+import { View, ViewOptions } from '../../App';
+import { Orbit } from 'lucide-react';
interface BottomMenuModeSelectionProps {
setView: (view: View, viewOptions?: ViewOptions) => void;
@@ -119,23 +119,25 @@ export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProp
return (
-
setIsGooseModeMenuOpen(!isGooseModeMenuOpen)}
>
-
- Goose Mode: {getValueByKey(gooseMode)}
-
- {isGooseModeMenuOpen ? (
-
- ) : (
-
- )}
-
+
{getValueByKey(gooseMode).toLowerCase()}
+
+ {/*
*/}
+ {/* Goose Mode: {getValueByKey(gooseMode)}*/}
+ {/**/}
+ {/*{isGooseModeMenuOpen ? (*/}
+ {/*
*/}
+ {/*) : (*/}
+ {/*
*/}
+ {/*)}*/}
+
{/* Dropdown Menu */}
{isGooseModeMenuOpen && (
-
+
{all_goose_modes.map((mode) => (
diff --git a/ui/desktop/src/components/more_menu/MoreMenuLayout.tsx b/ui/desktop/src/components/more_menu/MoreMenuLayout.tsx
index f697b690..e86148fb 100644
--- a/ui/desktop/src/components/more_menu/MoreMenuLayout.tsx
+++ b/ui/desktop/src/components/more_menu/MoreMenuLayout.tsx
@@ -1,22 +1,56 @@
+import { useState } from 'react';
import MoreMenu from './MoreMenu';
-import React from 'react';
-import { View, ViewOptions } from '../../App';
+import type { View, ViewOptions } from '../../App';
+import { Document } from '../icons';
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/Tooltip';
export default function MoreMenuLayout({
+ hasMessages,
+ showMenu = true,
setView,
setIsGoosehintsModalOpen,
}: {
- setView: (view: View, viewOptions?: ViewOptions) => void;
- setIsGoosehintsModalOpen: (isOpen: boolean) => void;
+ hasMessages?: boolean;
+ showMenu?: boolean;
+ setView?: (view: View, viewOptions?: ViewOptions) => void;
+ setIsGoosehintsModalOpen?: (isOpen: boolean) => void;
}) {
+ const [isTooltipOpen, setIsTooltipOpen] = useState(false);
return (
-
-
-
-
+
+ {showMenu && (
+
+
+
+
+
+
+
+ {window.appConfig.get('GOOSE_WORKING_DIR') as string}
+
+
+
+
-
+ )}
);
}
diff --git a/ui/desktop/src/components/sessions/SessionHistoryView.tsx b/ui/desktop/src/components/sessions/SessionHistoryView.tsx
index 40c84ae4..8cadecf0 100644
--- a/ui/desktop/src/components/sessions/SessionHistoryView.tsx
+++ b/ui/desktop/src/components/sessions/SessionHistoryView.tsx
@@ -17,6 +17,7 @@ import { createSharedSession } from '../../sharedSessions';
import { Modal, ModalContent } from '../ui/modal';
import { Button } from '../ui/button';
import { toast } from 'react-toastify';
+import MoreMenuLayout from '../more_menu/MoreMenuLayout';
interface SessionHistoryViewProps {
session: SessionDetails;
@@ -109,7 +110,7 @@ const SessionHistoryView: React.FC
= ({
return (
-
+
{/* Top Row - back, info, reopen thread (fixed) */}
diff --git a/ui/desktop/src/components/sessions/SessionListView.tsx b/ui/desktop/src/components/sessions/SessionListView.tsx
index ddeebd72..17322379 100644
--- a/ui/desktop/src/components/sessions/SessionListView.tsx
+++ b/ui/desktop/src/components/sessions/SessionListView.tsx
@@ -15,6 +15,7 @@ import BackButton from '../ui/BackButton';
import { ScrollArea } from '../ui/scroll-area';
import { View, ViewOptions } from '../../App';
import { formatMessageTimestamp } from '../../utils/timeUtils';
+import MoreMenuLayout from '../more_menu/MoreMenuLayout';
interface SessionListViewProps {
setView: (view: View, viewOptions?: ViewOptions) => void;
@@ -48,7 +49,7 @@ const SessionListView: React.FC = ({ setView, onSelectSess
return (
-
+
diff --git a/ui/desktop/src/components/settings/SettingsView.tsx b/ui/desktop/src/components/settings/SettingsView.tsx
index e0f8c2ea..b2220db3 100644
--- a/ui/desktop/src/components/settings/SettingsView.tsx
+++ b/ui/desktop/src/components/settings/SettingsView.tsx
@@ -18,6 +18,7 @@ import { View, ViewOptions } from '../../App';
import { ModeSelection } from './basic/ModeSelection';
import SessionSharingSection from './session/SessionSharingSection';
import { toastSuccess } from '../../toasts';
+import MoreMenuLayout from '../more_menu/MoreMenuLayout';
const EXTENSIONS_DESCRIPTION =
'The Model Context Protocol (MCP) is a system that allows AI models to securely connect with local or remote resources using standard server setups. It works like a client-server setup and expands AI capabilities using three main components: Prompts, Resources, and Tools.';
@@ -190,7 +191,7 @@ export default function SettingsView({
return (
-
+
diff --git a/ui/desktop/src/components/settings/basic/ModeSelectionItem.tsx b/ui/desktop/src/components/settings/basic/ModeSelectionItem.tsx
index f9de90ce..f025b80a 100644
--- a/ui/desktop/src/components/settings/basic/ModeSelectionItem.tsx
+++ b/ui/desktop/src/components/settings/basic/ModeSelectionItem.tsx
@@ -11,7 +11,7 @@ export interface GooseMode {
export const all_goose_modes: GooseMode[] = [
{
key: 'auto',
- label: 'Completely Autonomous',
+ label: 'Autonomous',
description: 'Full file modification capabilities, edit, create, and delete files freely.',
},
{
@@ -90,17 +90,14 @@ export function ModeSelectionItem({
return (
-
handleModeChange(mode.key)}
- >
-
-
{mode.label}
- {showDescription && (
-
- {mode.description}
-
- )}
+
handleModeChange(mode.key)}>
+
+
+
{mode.label}
+ {showDescription && (
+
{mode.description}
+ )}
+
{!isApproveModeConfigure && (mode.key == 'approve' || mode.key == 'smart_approve') && (
diff --git a/ui/desktop/src/components/settings/models/MoreModelsView.tsx b/ui/desktop/src/components/settings/models/MoreModelsView.tsx
index 1e8c0725..0ca8bc0c 100644
--- a/ui/desktop/src/components/settings/models/MoreModelsView.tsx
+++ b/ui/desktop/src/components/settings/models/MoreModelsView.tsx
@@ -6,6 +6,7 @@ import { SearchBar } from './Search';
import { AddModelInline } from './AddModelInline';
import { ScrollArea } from '../../ui/scroll-area';
import type { View } from '../../../App';
+import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
export default function MoreModelsView({
onClose,
@@ -16,7 +17,7 @@ export default function MoreModelsView({
}) {
return (
-
+
diff --git a/ui/desktop/src/components/settings/providers/ConfigureProvidersView.tsx b/ui/desktop/src/components/settings/providers/ConfigureProvidersView.tsx
index 796494ec..4a48bf3a 100644
--- a/ui/desktop/src/components/settings/providers/ConfigureProvidersView.tsx
+++ b/ui/desktop/src/components/settings/providers/ConfigureProvidersView.tsx
@@ -2,11 +2,12 @@ import React from 'react';
import { ScrollArea } from '../../ui/scroll-area';
import BackButton from '../../ui/BackButton';
import { ConfigureProvidersGrid } from './ConfigureProvidersGrid';
+import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
export default function ConfigureProvidersView({ onClose }: { onClose: () => void }) {
return (
-
+
diff --git a/ui/desktop/src/components/settings_v2/SettingsView.tsx b/ui/desktop/src/components/settings_v2/SettingsView.tsx
index 6722ff89..091959e3 100644
--- a/ui/desktop/src/components/settings_v2/SettingsView.tsx
+++ b/ui/desktop/src/components/settings_v2/SettingsView.tsx
@@ -6,6 +6,7 @@ import ModelsSection from './models/ModelsSection';
import { ModeSection } from './mode/ModeSection';
import SessionSharingSection from './sessions/SessionSharingSection';
import { ExtensionConfig } from '../../api';
+import MoreMenuLayout from '../more_menu/MoreMenuLayout';
export type SettingsViewOptions = {
deepLinkConfig?: ExtensionConfig;
@@ -23,7 +24,7 @@ export default function SettingsView({
}) {
return (
-
+
diff --git a/ui/desktop/src/components/settings_v2/extensions/modal/ExtensionInfoFields.tsx b/ui/desktop/src/components/settings_v2/extensions/modal/ExtensionInfoFields.tsx
index c89de340..0b426c70 100644
--- a/ui/desktop/src/components/settings_v2/extensions/modal/ExtensionInfoFields.tsx
+++ b/ui/desktop/src/components/settings_v2/extensions/modal/ExtensionInfoFields.tsx
@@ -1,6 +1,5 @@
import { Input } from '../../../ui/input';
import { Select } from '../../../ui/Select';
-import React from 'react';
interface ExtensionInfoFieldsProps {
name: string;
diff --git a/ui/desktop/src/components/settings_v2/mode/ModeSelectionItem.tsx b/ui/desktop/src/components/settings_v2/mode/ModeSelectionItem.tsx
index 33826069..f21badb6 100644
--- a/ui/desktop/src/components/settings_v2/mode/ModeSelectionItem.tsx
+++ b/ui/desktop/src/components/settings_v2/mode/ModeSelectionItem.tsx
@@ -12,17 +12,17 @@ export interface GooseMode {
export const all_goose_modes: GooseMode[] = [
{
key: 'auto',
- label: 'Completely autonomous',
+ label: 'Autonomous',
description: 'Full file modification capabilities, edit, create, and delete files freely.',
},
{
key: 'approve',
- label: 'Manual approval',
+ label: 'Manual',
description: 'All tools, extensions and file modifications will require human approval',
},
{
key: 'smart_approve',
- label: 'Smart approval',
+ label: 'Smart',
description: 'Intelligently determine which actions need approval based on risk level ',
},
{
@@ -61,21 +61,19 @@ export function ModeSelectionItem({
return (
handleModeChange(mode.key)}
>
-
{mode.label}
+
{mode.label}
{showDescription && (
-
- {mode.description}
-
+
{mode.description}
)}
-
+
{!isApproveModeConfigure && (mode.key == 'approve' || mode.key == 'smart_approve') && (
)}
+
setIsModelMenuOpen(!isModelMenuOpen)}
>
@@ -92,23 +91,18 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
)}
- {isModelMenuOpen ? (
-
- ) : (
-
- )}
{/* Dropdown Menu */}
{isModelMenuOpen && (
-
+
-
Current:
+
Current:
{model} -- {provider}
{
setIsModelMenuOpen(false);
@@ -116,7 +110,7 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
}}
>
Change Model
-
+
diff --git a/ui/desktop/src/components/settings_v2/models/subcomponents/AddModelModal.tsx b/ui/desktop/src/components/settings_v2/models/subcomponents/AddModelModal.tsx
index f3cf992b..015a7c79 100644
--- a/ui/desktop/src/components/settings_v2/models/subcomponents/AddModelModal.tsx
+++ b/ui/desktop/src/components/settings_v2/models/subcomponents/AddModelModal.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState, useCallback } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import { ArrowLeftRight, ExternalLink } from 'lucide-react';
import Modal from '../../../Modal';
diff --git a/ui/desktop/src/components/settings_v2/permission/PermissionSetting.tsx b/ui/desktop/src/components/settings_v2/permission/PermissionSetting.tsx
index 8c936539..000d5dfc 100644
--- a/ui/desktop/src/components/settings_v2/permission/PermissionSetting.tsx
+++ b/ui/desktop/src/components/settings_v2/permission/PermissionSetting.tsx
@@ -4,6 +4,7 @@ import BackButton from '../../ui/BackButton';
import { FixedExtensionEntry, useConfig } from '../../ConfigContext';
import { ChevronRight } from 'lucide-react';
import PermissionModal from './PermissionModal';
+import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
function RuleItem({ title, description }: { title: string; description: string }) {
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -76,7 +77,7 @@ export default function PermissionSettingsView({ onClose }: { onClose: () => voi
return (
-
+
diff --git a/ui/desktop/src/components/settings_v2/providers/ProviderRegistry.tsx b/ui/desktop/src/components/settings_v2/providers/ProviderRegistry.tsx
index 6ef080d5..5f5b1fd8 100644
--- a/ui/desktop/src/components/settings_v2/providers/ProviderRegistry.tsx
+++ b/ui/desktop/src/components/settings_v2/providers/ProviderRegistry.tsx
@@ -173,7 +173,8 @@ export const PROVIDER_REGISTRY: ProviderRegistry[] = [
details: {
id: 'azure_openai',
name: 'Azure OpenAI',
- description: 'Access Azure OpenAI models using API key or Azure credentials. If no API key is provided, Azure credential chain will be used.',
+ description:
+ 'Access Azure OpenAI models using API key or Azure credentials. If no API key is provided, Azure credential chain will be used.',
parameters: [
{
name: 'AZURE_OPENAI_API_KEY',
diff --git a/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx b/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx
index 7f2fc394..1b422f06 100644
--- a/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx
+++ b/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx
@@ -7,6 +7,7 @@ import { ProviderDetails } from '../../../api/types.gen';
import { initializeSystem } from '../../../utils/providerUtils';
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
import { toastService } from '../../../toasts';
+import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
interface ProviderSettingsProps {
onClose: () => void;
@@ -88,7 +89,8 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
return (
-
+
+
{isOnboarding && (
diff --git a/ui/desktop/src/components/ui/scroll-area.tsx b/ui/desktop/src/components/ui/scroll-area.tsx
index 0be52b56..79836a2e 100644
--- a/ui/desktop/src/components/ui/scroll-area.tsx
+++ b/ui/desktop/src/components/ui/scroll-area.tsx
@@ -97,12 +97,7 @@ const ScrollArea = React.forwardRef
(
data-scrolled={isScrolled}
{...props}
>
-
+