Unify loading goose messages and usechatstream determines chat state (#5306)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Zane
2025-10-23 15:51:01 -07:00
committed by GitHub
parent 6e76bb825c
commit 4acb4871b7
6 changed files with 76 additions and 36 deletions
+9 -10
View File
@@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { SearchView } from './conversation/SearchView';
import LoadingGoose from './LoadingGoose';
import { getThinkingMessage } from '../types/message';
import PopularChatTopics from './PopularChatTopics';
import ProgressiveMessageList from './ProgressiveMessageList';
import { MainPanelLayout } from './Layout/MainPanelLayout';
@@ -23,6 +22,7 @@ import { scanRecipe } from '../recipe';
import { useCostTracking } from '../hooks/useCostTracking';
import RecipeActivities from './recipes/RecipeActivities';
import { useToolCount } from './alerts/useToolCount';
import { getThinkingMessage } from '../types/message';
interface BaseChatProps {
setChat: (chat: ChatType) => void;
@@ -179,13 +179,6 @@ function BaseChatContent({
const initialPrompt = messages.length == 0 && recipe?.prompt ? recipe.prompt : '';
// Map chatState to LoadingGoose message
const getLoadingMessage = (): string | undefined => {
if (messages.length === 0 && chatState === ChatState.Thinking) {
return 'loading conversation...';
}
return getThinkingMessage(messages[messages.length - 1]);
};
return (
<div className="h-full flex flex-col min-h-0">
<h2>Warning: BaseChat2!</h2>
@@ -255,10 +248,16 @@ function BaseChatContent({
) : null}
</ScrollArea>
{/* Fixed loading indicator at bottom left of chat container */}
{chatState !== ChatState.Idle && !sessionLoadError && (
<div className="absolute bottom-1 left-4 z-20 pointer-events-none">
<LoadingGoose message={getLoadingMessage()} chatState={chatState} />
<LoadingGoose
chatState={chatState}
message={
messages.length > 0
? getThinkingMessage(messages[messages.length - 1])
: undefined
}
/>
</div>
)}
</div>
+24 -21
View File
@@ -8,18 +8,29 @@ interface LoadingGooseProps {
chatState?: ChatState;
}
const STATE_MESSAGES: Record<ChatState, string> = {
[ChatState.LoadingConversation]: 'loading conversation...',
[ChatState.Thinking]: 'goose is thinking…',
[ChatState.Streaming]: 'goose is working on it…',
[ChatState.WaitingForUserInput]: 'goose is waiting…',
[ChatState.Compacting]: 'goose is compacting the conversation...',
[ChatState.Idle]: 'goose is working on it…',
};
const STATE_ICONS: Record<ChatState, React.ReactNode> = {
[ChatState.LoadingConversation]: <AnimatedIcons className="flex-shrink-0" cycleInterval={600} />,
[ChatState.Thinking]: <AnimatedIcons className="flex-shrink-0" cycleInterval={600} />,
[ChatState.Streaming]: <FlyingBird className="flex-shrink-0" cycleInterval={150} />,
[ChatState.WaitingForUserInput]: (
<AnimatedIcons className="flex-shrink-0" cycleInterval={600} variant="waiting" />
),
[ChatState.Compacting]: <AnimatedIcons className="flex-shrink-0" cycleInterval={600} />,
[ChatState.Idle]: <GooseLogo size="small" hover={false} />,
};
const LoadingGoose = ({ message, chatState = ChatState.Idle }: LoadingGooseProps) => {
// Determine the appropriate message based on state
const getLoadingMessage = () => {
if (message) return message; // Custom message takes priority
if (chatState === ChatState.Thinking) return 'goose is thinking…';
if (chatState === ChatState.Streaming) return 'goose is working on it…';
if (chatState === ChatState.WaitingForUserInput) return 'goose is waiting…';
// Default fallback
return 'goose is working on it…';
};
const displayMessage = message || STATE_MESSAGES[chatState];
const icon = STATE_ICONS[chatState];
return (
<div className="w-full animate-fade-slide-up">
@@ -27,16 +38,8 @@ const LoadingGoose = ({ message, chatState = ChatState.Idle }: LoadingGooseProps
data-testid="loading-indicator"
className="flex items-center gap-2 text-xs text-textStandard py-2"
>
{chatState === ChatState.Thinking ? (
<AnimatedIcons className="flex-shrink-0" cycleInterval={600} />
) : chatState === ChatState.Streaming ? (
<FlyingBird className="flex-shrink-0" cycleInterval={150} />
) : chatState === ChatState.WaitingForUserInput ? (
<AnimatedIcons className="flex-shrink-0" cycleInterval={600} variant="waiting" />
) : (
<GooseLogo size="small" hover={false} />
)}
{getLoadingMessage()}
{icon}
{displayMessage}
</div>
</div>
);