Skip to content

Commit dadcb56

Browse files
committed
fix(tarko-agent-ui): prevent empty state during historical events loading
- Add loadingSessionEventsAtom to track when historical events are being loaded - Create LoadingState component to show while events are loading - Update ChatPanel to show LoadingState instead of EmptyState during loading - Prevent 'Start a conversation' state from showing during session initialization
1 parent 1fef3d7 commit dadcb56

5 files changed

Lines changed: 85 additions & 9 deletions

File tree

multimodal/tarko/agent-ui/src/common/hooks/useSession.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { messagesAtom, groupedMessagesAtom } from '../state/atoms/message';
44
import { toolResultsAtom } from '../state/atoms/tool';
55

66
import { sessionFilesAtom } from '../state/atoms/files';
7-
import { isProcessingAtom, activePanelContentAtom, connectionStatusAtom } from '../state/atoms/ui';
7+
import { isProcessingAtom, activePanelContentAtom, connectionStatusAtom, loadingSessionEventsAtom } from '../state/atoms/ui';
88
import { replayStateAtom } from '../state/atoms/replay';
99
import {
1010
loadSessionsAction,
@@ -35,6 +35,7 @@ export function useSession() {
3535
const [isProcessing, setIsProcessing] = useAtom(isProcessingAtom);
3636
const [activePanelContent, setActivePanelContent] = useAtom(activePanelContentAtom);
3737
const [connectionStatus, setConnectionStatus] = useAtom(connectionStatusAtom);
38+
const [loadingSessionEvents, setLoadingSessionEvents] = useAtom(loadingSessionEventsAtom);
3839

3940
const [replayState, setReplayState] = useAtom(replayStateAtom);
4041

@@ -91,6 +92,7 @@ export function useSession() {
9192
isProcessing,
9293
activePanelContent,
9394
connectionStatus,
95+
loadingSessionEvents,
9496

9597
replayState,
9698
sessionMetadata,
@@ -122,6 +124,7 @@ export function useSession() {
122124
isProcessing,
123125
activePanelContent,
124126
connectionStatus,
127+
loadingSessionEvents,
125128
replayState,
126129
sessionMetadata,
127130
loadSessions,

multimodal/tarko/agent-ui/src/common/state/actions/sessionActions.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { toolResultsAtom, toolCallResultMap } from '../atoms/tool';
77
import { sessionPanelContentAtom, isProcessingAtom } from '../atoms/ui';
88
import { processEventAction } from './eventProcessors';
99
import { Message, SessionInfo } from '@/common/types';
10-
import { connectionStatusAtom } from '../atoms/ui';
10+
import { connectionStatusAtom, loadingSessionEventsAtom } from '../atoms/ui';
1111
import { replayStateAtom } from '../atoms/replay';
1212
import { sessionFilesAtom, FileItem } from '../atoms/files';
1313
import { ChatCompletionContentPart, AgentEventStream } from '@tarko/agent-interface';
@@ -160,12 +160,28 @@ export const setActiveSessionAction = atom(null, async (get, set, sessionId: str
160160

161161
if (!hasExistingMessages) {
162162
console.log(`Loading events for session ${sessionId}`);
163-
const events = await apiService.getSessionEvents(sessionId);
163+
164+
// Set loading state
165+
set(loadingSessionEventsAtom, (prev) => ({
166+
...prev,
167+
[sessionId]: true,
168+
}));
164169

165-
const processedEvents = preprocessStreamingEvents(events);
170+
try {
171+
const events = await apiService.getSessionEvents(sessionId);
166172

167-
for (const event of processedEvents) {
168-
await set(processEventAction, { sessionId, event });
173+
const processedEvents = preprocessStreamingEvents(events);
174+
175+
for (const event of processedEvents) {
176+
await set(processEventAction, { sessionId, event });
177+
}
178+
} finally {
179+
// Clear loading state
180+
set(loadingSessionEventsAtom, (prev) => {
181+
const newState = { ...prev };
182+
delete newState[sessionId];
183+
return newState;
184+
});
169185
}
170186
}
171187

multimodal/tarko/agent-ui/src/common/state/atoms/ui.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ export const workspacePanelCollapsedAtom = atom<boolean>(false);
6060
*/
6161
export const isProcessingAtom = atom<boolean>(false);
6262

63+
/**
64+
* Atom for tracking when historical events are being loaded for a session
65+
*/
66+
export const loadingSessionEventsAtom = atom<Record<string, boolean>>({});
67+
6368
/**
6469
* Atom for offline mode state (view-only when disconnected)
6570
*/

multimodal/tarko/agent-ui/src/standalone/chat/ChatPanel.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import { useReplayMode } from '@/common/hooks/useReplayMode';
1010
import { useScrollToBottom } from './hooks/useScrollToBottom';
1111
import { ScrollToBottomButton } from './components/ScrollToBottomButton';
1212
import { EmptyState } from './components/EmptyState';
13+
import { LoadingState } from './components/LoadingState';
1314
import { OfflineBanner } from './components/OfflineBanner';
1415
import { SessionCreatingState } from './components/SessionCreatingState';
1516

1617
import './ChatPanel.css';
1718

1819
export const ChatPanel: React.FC = () => {
1920
const { sessionId: urlSessionId } = useParams<{ sessionId: string }>();
20-
const { activeSessionId, isProcessing, connectionStatus, checkServerStatus, sendMessage } =
21+
const { activeSessionId, isProcessing, connectionStatus, checkServerStatus, sendMessage, loadingSessionEvents } =
2122
useSession();
2223

2324
const currentSessionId = urlSessionId || activeSessionId;
@@ -41,7 +42,8 @@ export const ChatPanel: React.FC = () => {
4142

4243
const isCreatingSession = !currentSessionId || currentSessionId === 'creating';
4344
const hasMessages = activeMessages.length > 0;
44-
const showEmptyState = !isCreatingSession && !hasMessages;
45+
const isLoadingEvents = currentSessionId && loadingSessionEvents[currentSessionId];
46+
const showEmptyState = !isCreatingSession && !hasMessages && !isLoadingEvents;
4547

4648
if (isCreatingSession) {
4749
return <SessionCreatingState isCreating={currentSessionId === 'creating'} />;
@@ -60,7 +62,9 @@ export const ChatPanel: React.FC = () => {
6062
onReconnect={checkServerStatus}
6163
/>
6264

63-
{showEmptyState ? (
65+
{isLoadingEvents ? (
66+
<LoadingState />
67+
) : showEmptyState ? (
6468
<EmptyState replayState={replayState} isReplayMode={isReplayMode} />
6569
) : (
6670
<div className="space-y-4 pb-2">
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import { motion } from 'framer-motion';
3+
import { FiMessageSquare } from 'react-icons/fi';
4+
5+
/**
6+
* LoadingState component - Shows loading state while historical events are being loaded
7+
*/
8+
export const LoadingState: React.FC = () => {
9+
return (
10+
<div className="flex items-center justify-center h-full min-h-[400px] animate-in fade-in duration-600">
11+
<div className="text-center p-8 max-w-lg">
12+
{/* Loading icon with animation */}
13+
<div className="relative mb-8 animate-in zoom-in duration-700">
14+
{/* Background glow */}
15+
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/15 via-purple-500/15 to-green-500/15 rounded-full blur-xl animate-pulse" />
16+
17+
{/* Main icon container */}
18+
<div className="relative w-20 h-20 bg-gradient-to-br from-white via-gray-50 to-white dark:from-gray-800 dark:via-gray-750 dark:to-gray-800 rounded-3xl flex items-center justify-center mx-auto shadow-lg border border-gray-200/60 dark:border-gray-700/60 backdrop-blur-sm">
19+
{/* Animated icon */}
20+
<motion.div
21+
animate={{
22+
rotate: 360,
23+
}}
24+
transition={{
25+
duration: 2,
26+
repeat: Infinity,
27+
ease: 'linear',
28+
}}
29+
className="text-blue-600 dark:text-blue-400"
30+
>
31+
<FiMessageSquare size={28} />
32+
</motion.div>
33+
</div>
34+
</div>
35+
36+
{/* Loading title */}
37+
<h3 className="text-2xl font-semibold mb-4 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-900 dark:from-gray-100 dark:via-white dark:to-gray-100 text-transparent bg-clip-text tracking-tight animate-in slide-in-from-bottom-4 fade-in duration-600">
38+
Loading conversation
39+
</h3>
40+
41+
{/* Loading description */}
42+
<p className="text-gray-600 dark:text-gray-400 leading-relaxed max-w-sm mx-auto animate-in slide-in-from-bottom-4 fade-in duration-600 delay-150">
43+
Loading historical events for this conversation...
44+
</p>
45+
</div>
46+
</div>
47+
);
48+
};

0 commit comments

Comments
 (0)