diff --git a/ui/desktop/src/ChatWindow.tsx b/ui/desktop/src/ChatWindow.tsx
index a79a071d6d..4ab6e28a3c 100644
--- a/ui/desktop/src/ChatWindow.tsx
+++ b/ui/desktop/src/ChatWindow.tsx
@@ -10,8 +10,10 @@ import UserMessage from './components/UserMessage';
import Input from './components/Input';
import Tabs from './components/Tabs';
import MoreMenu from './components/MoreMenu';
+import { Bird } from './components/ui/icons';
import LoadingGoose from './components/LoadingGoose';
+
export interface Chat {
id: number;
title: string;
@@ -22,28 +24,29 @@ export interface Chat {
}>;
}
-const handleResize = (mode: 'expanded' | 'compact') => {
- if (window.electron) {
- if (mode === 'expanded') {
- const width = window.innerWidth;
- const height = window.innerHeight;
- window.electron.resizeWindow(width, height);
- } else if (mode === 'compact') {
- const width = window.innerWidth;
- const height = 100; // Make it very thin
- window.electron.resizeWindow(width, height);
- }
- }
-};
+enum Working {
+ Idle = 'Idle',
+ Working = 'Working',
+}
-const WingView: React.FC<{ onExpand: () => void; status: string }> = ({ onExpand, status }) => {
+const WingView: React.FC<{
+ onExpand: () => void;
+ progressMessage: string;
+ working: Working;
+}> = ({ onExpand, progressMessage, working }) => {
return (
-
-
{status}
+ className="flex items-center w-full h-28 bg-gradient-to-r from-gray-100 via-gray-200 to-gray-300 shadow-md rounded-lg p-4 cursor-pointer hover:shadow-lg transition-all duration-200">
+ {working === Working.Working && (
+
+
+
+ )}
+
+ {/* Status Text */}
+
+ {progressMessage}
);
@@ -55,19 +58,19 @@ function ChatContent({
selectedChatId,
setSelectedChatId,
initialQuery,
- setStatus,
+ setProgressMessage,
+ setWorking,
}: {
chats: Chat[];
setChats: React.Dispatch
>;
selectedChatId: number;
setSelectedChatId: React.Dispatch>;
initialQuery: string | null;
- setStatus: React.Dispatch>;
+ setProgressMessage: React.Dispatch>;
+ setWorking: React.Dispatch>;
}) {
const chat = chats.find((c: Chat) => c.id === selectedChatId);
- //window.electron.logInfo('chats' + JSON.stringify(chats, null, 2));
-
const [messageMetadata, setMessageMetadata] = useState>({});
const {
@@ -78,23 +81,26 @@ function ChatContent({
append,
stop,
isLoading,
- error
+ error,
} = useChat({
api: getApiUrl('/reply'),
initialMessages: chat?.messages || [],
onToolCall: ({ toolCall }) => {
- setStatus(`Executing tool: ${toolCall.toolName}`);
- // Optionally handle tool call result here
+ setWorking(Working.Working);
+ setProgressMessage(`Executing tool: ${toolCall.toolName}`);
},
onResponse: (response) => {
if (!response.ok) {
- setStatus('An error occurred while receiving the response.');
+ setProgressMessage('An error occurred while receiving the response.');
+ setWorking(Working.Idle);
} else {
- setStatus('Receiving response...');
+ setProgressMessage('thinking...');
+ setWorking(Working.Working);
}
},
onFinish: async (message, options) => {
- setStatus('Goose is ready');
+ setProgressMessage('Task finished. Click here to expand.');
+ setWorking(Working.Idle);
const promptTemplates = [
"Take a look at this content, if this looks like it could be asking for a confirmation, return QUESTION. If it looks like it is a list of options or plans to choose from, return OPTIONS. It can't just be a list, but clearly must be asking the user to pick one or more of the plan or option alternatives, otherwise return READY. \n ### Message Content:\n" + message.content,
@@ -103,7 +109,7 @@ function ChatContent({
const fetchResponses = await askAi(promptTemplates);
- setMessageMetadata(prev => ({ ...prev, [message.id]: fetchResponses }));
+ setMessageMetadata((prev) => ({ ...prev, [message.id]: fetchResponses }));
console.log('All responses:', fetchResponses);
},
@@ -191,7 +197,7 @@ function ChatContent({
{error && (
- {error.message|| 'An error occurred while processing your request'}
+ {error.message || 'An error occurred while processing your request'}
)}
@@ -214,11 +220,8 @@ export default function ChatWindow() {
// Get initial query and history from URL parameters
const searchParams = new URLSearchParams(window.location.search);
const initialQuery = searchParams.get('initialQuery');
- //window.electron.logInfo('initialQuery: ' + initialQuery);
const historyParam = searchParams.get('history');
- const initialHistory = historyParam
- ? JSON.parse(decodeURIComponent(historyParam))
- : [];
+ const initialHistory = historyParam ? JSON.parse(decodeURIComponent(historyParam)) : [];
const [chats, setChats] = useState(() => {
const firstChat = {
@@ -235,13 +238,13 @@ export default function ChatWindow() {
initialQuery ? 'compact' : 'expanded'
);
- const [status, setStatus] = useState('Goose is ready');
+ const [working, setWorking] = useState(Working.Idle);
+ const [progressMessage, setProgressMessage] = useState('');
const toggleMode = () => {
const newMode = mode === 'expanded' ? 'compact' : 'expanded';
console.log(`Toggle to ${newMode}`);
setMode(newMode);
- handleResize(newMode);
};
window.electron.logInfo('ChatWindow loaded');
@@ -261,7 +264,8 @@ export default function ChatWindow() {
selectedChatId={selectedChatId}
setSelectedChatId={setSelectedChatId}
initialQuery={initialQuery}
- setStatus={setStatus} // Pass setStatus to ChatContent
+ setProgressMessage={setProgressMessage}
+ setWorking={setWorking}
/>
}
/>
@@ -270,36 +274,36 @@ export default function ChatWindow() {
{/* Always render WingView but control its visibility */}
-
-
-
+
);
}
/**
- * Utillity to ask the LLM any question to clarify without wider context.
+ * Utility to ask the LLM any question to clarify without wider context.
*/
async function askAi(promptTemplates: string[]) {
console.log('askAi called...');
- const responses = await Promise.all(promptTemplates.map(async (template) => {
- const response = await fetch(getApiUrl('/ask'), {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ prompt: template })
- });
+ const responses = await Promise.all(
+ promptTemplates.map(async (template) => {
+ const response = await fetch(getApiUrl('/ask'), {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ prompt: template }),
+ });
- if (!response.ok) {
- throw new Error('Failed to get response');
- }
+ if (!response.ok) {
+ throw new Error('Failed to get response');
+ }
- const data = await response.json();
- console.log('ask Response:', data.response);
+ const data = await response.json();
+ console.log('ask Response:', data.response);
- return data.response;
- }));
+ return data.response;
+ })
+ );
return responses;
}
diff --git a/ui/desktop/src/components/Tabs.tsx b/ui/desktop/src/components/Tabs.tsx
index 230b34604b..bee1ded786 100644
--- a/ui/desktop/src/components/Tabs.tsx
+++ b/ui/desktop/src/components/Tabs.tsx
@@ -199,7 +199,7 @@ export default function Tabs({ chats, selectedChatId, setSelectedChatId, setChat
{/* Adjusted padding on the left side and reduced margin for the close button */}
- {chat.title}
+ {chat.title.length > 8 ? `${chat.title.slice(0, 7)}...` : chat.title}
{chats.length > 1 && (