diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx
index aa88af42d4..bce7fc67d2 100644
--- a/ui/desktop/src/components/ChatInput.tsx
+++ b/ui/desktop/src/components/ChatInput.tsx
@@ -40,11 +40,7 @@ export default function ChatInput({
onStop,
commandHistory = [],
initialValue = '',
- setView,
- numTokens,
droppedFiles = [],
- messages = [],
- setMessages,
}: ChatInputProps) {
const [_value, setValue] = useState(initialValue);
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
@@ -432,11 +428,11 @@ export default function ChatInput({
return (
-
-
-
-
-
-
-
-
-
-
- {isLoading ? (
-
-
-
- ) : (
-
-
-
- )}
-
-
-
);
}
diff --git a/ui/desktop/src/components/ChatView.tsx b/ui/desktop/src/components/ChatView.tsx
index d979f55e58..d59235f1b4 100644
--- a/ui/desktop/src/components/ChatView.tsx
+++ b/ui/desktop/src/components/ChatView.tsx
@@ -43,8 +43,9 @@ import {
TextContent,
} from '../types/message';
import SessionsSidebar from './sessions/SessionsSideView';
-import { SidebarProvider, SidebarInset, SidebarTrigger } from './ui/sidebar';
+import { SidebarProvider, SidebarInset, SidebarTrigger, useSidebar } from './ui/sidebar';
import MoreMenu from './more_menu/MoreMenu';
+import BottomMenu from './bottom_menu/BottomMenu';
// Context for sharing current model info
const CurrentModelContext = createContext<{ model: string; mode: string } | null>(null);
@@ -106,6 +107,34 @@ function ChatContent({
setView: (view: View, viewOptions?: ViewOptions) => void;
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
}) {
+ return (
+
+
+
+ );
+}
+
+function ChatContentWithSidebar({
+ chat,
+ setChat,
+ setView,
+ setIsGoosehintsModalOpen,
+}: {
+ chat: ChatType;
+ setChat: (chat: ChatType) => void;
+ setView: (view: View, viewOptions?: ViewOptions) => void;
+ setIsGoosehintsModalOpen: (isOpen: boolean) => void;
+}) {
+ const safeIsMacOS = (window?.electron?.platform || 'darwin') === 'darwin';
+
+ // Calculate padding based on sidebar state and macOS
+ const headerPadding = safeIsMacOS ? 'pl-22' : 'pl-8';
+
const [hasMessages, setHasMessages] = useState(false);
const [lastInteractionTime, setLastInteractionTime] = useState(Date.now());
const [showGame, setShowGame] = useState(false);
@@ -536,153 +565,159 @@ function ChatContent({
}
};
- const safeIsMacOS = (window?.electron?.platform || 'darwin') === 'darwin';
-
return (
-
+
{/* Loader when generating recipe */}
{isGeneratingRecipe &&
}
-
-
-
-
-
-
-
- {})}
- setIsGoosehintsModalOpen={setIsGoosehintsModalOpen || (() => {})}
- />
-
-
-
+
+
+
+
- {recipeConfig?.title && messages.length > 0 && (
-
{
- // Handle profile change
- console.log('Change profile clicked');
- }}
- />
- )}
- {messages.length === 0 ? (
-
- ) : (
- <>
-
- {filteredMessages.map((message, index) => (
-
- {isUserMessage(message) ? (
- <>
- {hasContextHandlerContent(message) ? (
-
- ) : (
-
- )}
- >
- ) : (
- <>
- {/* Only render GooseMessage if it's not a message invoking some context management */}
- {hasContextHandlerContent(message) ? (
-
- ) : (
- {
- const updatedMessages = [...messages, newMessage];
- setMessages(updatedMessages);
- }}
- toolCallNotifications={toolCallNotifications}
- />
- )}
- >
- )}
-
- ))}
-
-
- {error && (
-
-
- {error.message || 'Honk! Goose experienced an error while responding'}
-
-
{
- // Find the last user message
- const lastUserMessage = messages.reduceRight(
- (found, m) => found || (m.role === 'user' ? m : null),
- null as Message | null
- );
- if (lastUserMessage) {
- append(lastUserMessage);
- }
- }}
- >
- Retry Last Message
-
-
- )}
-
- >
- )}
-
-
- {isLoading && }
-
+ {})}
+ setIsGoosehintsModalOpen={setIsGoosehintsModalOpen || (() => {})}
/>
-
-
+
+
+ {recipeConfig?.title && messages.length > 0 && (
+
{
+ // Handle profile change
+ console.log('Change profile clicked');
+ }}
+ />
+ )}
+ {messages.length === 0 ? (
+
+ ) : (
+ <>
+
+ {filteredMessages.map((message, index) => (
+
+ {isUserMessage(message) ? (
+ <>
+ {hasContextHandlerContent(message) ? (
+
+ ) : (
+
+ )}
+ >
+ ) : (
+ <>
+ {/* Only render GooseMessage if it's not a message invoking some context management */}
+ {hasContextHandlerContent(message) ? (
+
+ ) : (
+ {
+ const updatedMessages = [...messages, newMessage];
+ setMessages(updatedMessages);
+ }}
+ toolCallNotifications={toolCallNotifications}
+ />
+ )}
+ >
+ )}
+
+ ))}
+
+
+ {error && (
+
+
+ {error.message || 'Honk! Goose experienced an error while responding'}
+
+
{
+ // Find the last user message
+ const lastUserMessage = messages.reduceRight(
+ (found, m) => found || (m.role === 'user' ? m : null),
+ null as Message | null
+ );
+ if (lastUserMessage) {
+ append(lastUserMessage);
+ }
+ }}
+ >
+ Retry Last Message
+
+
+ )}
+
+ >
+ )}
+
+
+ {isLoading && }
+
+
+
+
+
+
{showGame && setShowGame(false)} />}
diff --git a/ui/desktop/src/components/Splash.tsx b/ui/desktop/src/components/Splash.tsx
index b43d5d5338..96db791746 100644
--- a/ui/desktop/src/components/Splash.tsx
+++ b/ui/desktop/src/components/Splash.tsx
@@ -26,47 +26,49 @@ export default function Splash({ append, activities, title }: SplashProps) {
? [...pills.slice(0, messagePillIndex), ...pills.slice(messagePillIndex + 1)]
: pills;
- const [timeOfDay, setTimeOfDay] = useState<'morning' | 'afternoon' | 'night'>('morning');
+ const [selectedGreeting, setSelectedGreeting] = useState<{
+ prefix: string;
+ message: string;
+ } | null>(null);
- // Update time of day, current time, and date based on current time
+ // Select a random greeting on component mount
useEffect(() => {
- const updateTime = () => {
- const now = new Date();
- const hour = now.getHours();
+ const prefixes = ['Hello.', 'Welcome.', 'Greetings.', 'Welcome back.', 'Hello there.'];
- // Update time of day
- if (hour >= 5 && hour < 12) {
- setTimeOfDay('morning');
- } else if (hour >= 12 && hour < 18) {
- setTimeOfDay('afternoon');
- } else {
- setTimeOfDay('night');
- }
- };
+ const messages = [
+ ' Ready to get started?',
+ ' What would you like to work on?',
+ ' Ready to build something amazing?',
+ ' What would you like to explore?',
+ " What's on your mind?",
+ ' What shall we create today?',
+ ' What project needs attention?',
+ ' What would you like to tackle?',
+ ' What would you like to explore?',
+ ' What needs to be done?',
+ " What's the plan for today?",
+ ' Ready to create something great?',
+ ' What can be built today?',
+ " What's the next challenge?",
+ ' What progress can be made?',
+ ' What would you like to accomplish?',
+ ' What task awaits?',
+ " What's the mission today?",
+ ' What can be achieved?',
+ ' What project is ready to begin?',
+ ];
- updateTime();
- const interval = setInterval(updateTime, 1000);
- return () => clearInterval(interval);
+ const randomPrefixIndex = Math.floor(Math.random() * prefixes.length);
+ const randomMessageIndex = Math.floor(Math.random() * messages.length);
+
+ setSelectedGreeting({
+ prefix: prefixes[randomPrefixIndex],
+ message: messages[randomMessageIndex],
+ });
}, []);
const getGreeting = () => {
- switch (timeOfDay) {
- case 'morning':
- return {
- prefix: 'Morning.',
- message: " Let's get things done.",
- };
- case 'afternoon':
- return {
- prefix: 'Afternoon.',
- message: ' Keep the momentum going.',
- };
- case 'night':
- return {
- prefix: 'Evening.',
- message: ' Time to wrap things up.',
- };
- }
+ return selectedGreeting || { prefix: 'Hello.', message: ' How can I help you today?' };
};
const greeting = getGreeting();
@@ -91,43 +93,41 @@ export default function Splash({ append, activities, title }: SplashProps) {
)}
-
- {/*
+ {/*
*/}
-
-
- {greeting.prefix}
- {greeting.message}
-
+
+
+ {greeting.prefix}
+
+
{greeting.message}
+
+
+
+ {messagePill && (
+
+ {messagePill.replace(/^message:/i, '').trim()}
+
+ )}
+
+
+ {remainingPills.map((content, index) => (
+ append(content)}
+ title={content.length > 100 ? content : undefined}
+ className="cursor-pointer px-6 w-[256px]"
+ >
+ {content.length > 100 ? content.slice(0, 100) + '...' : content}
+
+ ))}
-
- {messagePill && (
-
- {messagePill.replace(/^message:/i, '').trim()}
-
- )}
-
-
- {remainingPills.map((content, index) => (
- append(content)}
- title={content.length > 100 ? content : undefined}
- className="cursor-pointer px-6 w-[256px]"
- >
- {content.length > 100 ? content.slice(0, 100) + '...' : content}
-
- ))}
-
-
-
-
-
+
+
diff --git a/ui/desktop/src/components/bottom_menu/BottomMenu.tsx b/ui/desktop/src/components/bottom_menu/BottomMenu.tsx
index 587670ec07..3bab14eb9f 100644
--- a/ui/desktop/src/components/bottom_menu/BottomMenu.tsx
+++ b/ui/desktop/src/components/bottom_menu/BottomMenu.tsx
@@ -202,32 +202,30 @@ export default function BottomMenu({
}, [isModelMenuOpen]);
return (
-
-
- {/* Tool and Token count */}
- {
}
+
+ {/* Tool and Token count */}
+ {
}
- {/* Model Selector Dropdown */}
-
+ {/* Model Selector Dropdown */}
+
- {/* Separator */}
-
+ {/* Separator */}
+
- {/* Goose Mode Selector Dropdown */}
-
+ {/* Goose Mode Selector Dropdown */}
+
- {/* Summarize Context Button - ADD THIS */}
- {messages.length > 0 && (
- <>
-
-
- >
- )}
-
+ {/* Summarize Context Button - ADD THIS */}
+ {messages.length > 0 && (
+ <>
+
+
+ >
+ )}
);
}
diff --git a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx
index 8524208520..254f830be6 100644
--- a/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx
+++ b/ui/desktop/src/components/bottom_menu/BottomMenuModeSelection.tsx
@@ -3,6 +3,7 @@ import { all_goose_modes, ModeSelectionItem } from '../settings/mode/ModeSelecti
import { useConfig } from '../ConfigContext';
import { View, ViewOptions } from '../../App';
import { Orbit } from 'lucide-react';
+import { Button } from '../ui/button';
interface BottomMenuModeSelectionProps {
setView: (view: View, viewOptions?: ViewOptions) => void;
@@ -85,13 +86,12 @@ export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProp
return (
-
setIsGooseModeMenuOpen(!isGooseModeMenuOpen)}
>
- {getValueByKey(gooseMode).toLowerCase()}
-
-
+ {getValueByKey(gooseMode).toLowerCase()}
+
{/* Dropdown Menu */}
{isGooseModeMenuOpen && (
diff --git a/ui/desktop/src/components/more_menu/MoreMenu.tsx b/ui/desktop/src/components/more_menu/MoreMenu.tsx
index 802dae1e25..ad06eed2ef 100644
--- a/ui/desktop/src/components/more_menu/MoreMenu.tsx
+++ b/ui/desktop/src/components/more_menu/MoreMenu.tsx
@@ -238,7 +238,13 @@ export default function MoreMenu({
return (
-
+
diff --git a/ui/desktop/src/components/sessions/SessionsSideView.tsx b/ui/desktop/src/components/sessions/SessionsSideView.tsx
index 8d44c84465..2d12a0be8c 100644
--- a/ui/desktop/src/components/sessions/SessionsSideView.tsx
+++ b/ui/desktop/src/components/sessions/SessionsSideView.tsx
@@ -19,6 +19,12 @@ import { cn } from '../../utils';
interface SessionsSidebarProps {
onSelectSession: (sessionId: string) => void;
+ currentSession?: {
+ id: string;
+ title: string;
+ messageHistoryIndex: number;
+ messages: any[];
+ };
}
interface GroupedSessions {
@@ -27,7 +33,7 @@ interface GroupedSessions {
older: { [key: string]: Session[] };
}
-const SessionsSidebar: React.FC = ({ onSelectSession }) => {
+const SessionsSidebar: React.FC = ({ onSelectSession, currentSession }) => {
const [sessions, setSessions] = useState([]);
// @ts-expect-error
const [isLoading, setIsLoading] = useState(true);
@@ -143,11 +149,9 @@ const SessionsSidebar: React.FC = ({ onSelectSession }) =>
-
+
-
- {title}
-
+ {title}
@@ -182,18 +186,52 @@ const SessionsSidebar: React.FC = ({ onSelectSession }) =>
);
};
+ const renderCurrentSession = () => {
+ if (!currentSession || currentSession.messages.length === 0) return null;
+
+ return (
+
+
+
+ Current Session
+
+
+
+
+
+
+
+
+ {currentSession.title || currentSession.id}
+
+
+
+ {window.appConfig.get('GOOSE_WORKING_DIR') as string}
+
+
+
+
+
+
+
+ );
+ };
+
return (
-
+
= ({ onSelectSession }) =>
+ {renderCurrentSession()}
{(() => {
let groupIndex = 0;
const groups = [
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 12911d80d9..13abe696a3 100644
--- a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx
+++ b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx
@@ -8,6 +8,7 @@ import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '../../
import Modal from '../../../Modal';
import { useCurrentModelInfo } from '../../../ChatView';
import { useConfig } from '../../../ConfigContext';
+import { Button } from '../../../ui/button';
interface ModelsBottomBarProps {
dropdownRef: React.RefObject;
@@ -43,7 +44,10 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
}, [read]);
// Determine which model to display - activeModel takes priority when lead/worker is active
- const displayModel = (isLeadWorkerActive && currentModelInfo?.model) ? currentModelInfo.model : (currentModel || 'Select Model');
+ const displayModel =
+ isLeadWorkerActive && currentModelInfo?.model
+ ? currentModelInfo.model
+ : currentModel || 'Select Model';
const modelMode = currentModelInfo?.mode;
// Update display provider when current provider changes
@@ -102,13 +106,11 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
{displayModel}
{isLeadWorkerActive && modelMode && (
-
- ({modelMode})
-
+ ({modelMode})
)}
@@ -116,9 +118,7 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
{displayModel}
{isLeadWorkerActive && modelMode && (
-
- ({modelMode})
-
+ ({modelMode})
)}
)}
@@ -128,7 +128,7 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
{/* Dropdown Menu */}
{isModelMenuOpen && (
-
+
Current:
@@ -164,7 +164,7 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
{isAddModelModalOpen ? (
setIsAddModelModalOpen(false)} />
) : null}
-
+
{isLeadWorkerModalOpen ? (
setIsLeadWorkerModalOpen(false)}>
setIsLeadWorkerModalOpen(false)} />
diff --git a/ui/desktop/src/components/ui/button.tsx b/ui/desktop/src/components/ui/button.tsx
index 60d94ededf..cea041c4e5 100644
--- a/ui/desktop/src/components/ui/button.tsx
+++ b/ui/desktop/src/components/ui/button.tsx
@@ -11,7 +11,7 @@ const buttonVariants = cva(
default: 'bg-background-accent text-text-on-accent hover:bg-background-accent/90 shadow-xs',
destructive:
'bg-background-danger text-white hover:bg-background-danger/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-background-danger/60 shadow-xs',
- outline: 'bg-background-default border-none shadow-default',
+ outline: 'border hover:bg-background-default',
secondary: 'bg-background-muted text-text-default hover:bg-background-muted/80 shadow-xs',
ghost: 'hover:bg-background-muted dark:hover:bg-background-muted/50',
link: 'text-text-accent underline-offset-4 hover:underline',
diff --git a/ui/desktop/src/components/ui/input.tsx b/ui/desktop/src/components/ui/input.tsx
index 25aa37381d..fe0f2a46b1 100644
--- a/ui/desktop/src/components/ui/input.tsx
+++ b/ui/desktop/src/components/ui/input.tsx
@@ -8,7 +8,7 @@ const Input = React.forwardRef>(
{
onClick?.(event);
diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts
index 693046e607..adbbb4f8d4 100644
--- a/ui/desktop/src/main.ts
+++ b/ui/desktop/src/main.ts
@@ -437,7 +437,7 @@ const createChat = async (
const mainWindow = new BrowserWindow({
titleBarStyle: process.platform === 'darwin' ? 'hidden' : 'default',
- trafficLightPosition: process.platform === 'darwin' ? { x: 20, y: 26 } : undefined,
+ trafficLightPosition: process.platform === 'darwin' ? { x: 20, y: 21 } : undefined,
vibrancy: process.platform === 'darwin' ? 'window' : undefined,
frame: process.platform === 'darwin' ? false : true,
x: mainWindowState.x,
@@ -446,7 +446,6 @@ const createChat = async (
height: mainWindowState.height,
minWidth: 650,
resizable: true,
- transparent: false,
useContentSize: true,
icon: path.join(__dirname, '../images/icon'),
webPreferences: {
diff --git a/ui/desktop/src/styles/main.css b/ui/desktop/src/styles/main.css
index 5dcb52f731..abdcefdd68 100644
--- a/ui/desktop/src/styles/main.css
+++ b/ui/desktop/src/styles/main.css
@@ -189,6 +189,16 @@
}
}
+@font-face {
+ font-family: 'Cash Sans';
+ src:
+ url(https://cash-f.squarecdn.com/static/fonts/cashsans/woff2/CashSans-Light.woff2)
+ format('woff2'),
+ url(https://cash-f.squarecdn.com/static/fonts/cashsans/woff/CashSans-Light.woff) format('woff');
+ font-weight: 300;
+ font-style: normal;
+}
+
@font-face {
font-family: 'Cash Sans';
src: