diff --git a/src/components/chat/ChatEmptyState.tsx b/src/components/chat/ChatEmptyState.tsx
new file mode 100644
index 0000000..c473135
--- /dev/null
+++ b/src/components/chat/ChatEmptyState.tsx
@@ -0,0 +1,48 @@
+import { useI18n } from '../../i18n';
+
+export default function ChatEmptyState() {
+ const { t } = useI18n();
+
+ const suggestions = [
+ {
+ key: 'task',
+ title: t('conversation.emptyState.suggestions.task.title'),
+ },
+ {
+ key: 'continuous',
+ title: t('conversation.emptyState.suggestions.continuous.title'),
+ },
+ {
+ key: 'parallel',
+ title: t('conversation.emptyState.suggestions.parallel.title'),
+ },
+ ] as const;
+
+ return (
+
+
+
+
+
+ {t('conversation.emptyState.title')}
+
+
+
+ {suggestions.map((suggestion) => (
+
+ {suggestion.title}
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/chat/ChatMessageList.tsx b/src/components/chat/ChatMessageList.tsx
index 543035a..633b600 100644
--- a/src/components/chat/ChatMessageList.tsx
+++ b/src/components/chat/ChatMessageList.tsx
@@ -1,15 +1,18 @@
import { memo, useEffect, useRef } from 'react';
import type { ChatMessageItem } from './types';
import { useI18n } from '../../i18n';
+import ChatEmptyState from './ChatEmptyState';
type ChatMessageListProps = {
messages: ChatMessageItem[];
loading?: boolean;
+ showWelcomeState?: boolean;
};
-function ChatMessageList({ messages, loading }: ChatMessageListProps) {
+function ChatMessageList({ messages, loading, showWelcomeState }: ChatMessageListProps) {
const containerRef = useRef(null);
const { t } = useI18n();
+ const shouldShowWelcomeState = !loading && (showWelcomeState || messages.length === 0);
useEffect(() => {
const container = containerRef.current;
@@ -25,11 +28,7 @@ function ChatMessageList({ messages, loading }: ChatMessageListProps) {
{t('conversation.messageList.loading')}
) : null}
- {!loading && messages.length === 0 ? (
-
- {t('conversation.messageList.emptyHint')}
-
- ) : null}
+ {shouldShowWelcomeState ? : null}
{messages.map((message) => (
void;
+ onSend: () => void;
+ onAttach: (files: File[]) => void | Promise;
+ onRemoveAttachment: (index: number) => void;
+};
+
+function HomeChatComposerSection({
+ value,
+ attachments,
+ onChange,
+ onSend,
+ onAttach,
+ onRemoveAttachment,
+}: HomeChatComposerSectionProps) {
const error = useChatStore((state) => state.error);
const isSending = useChatStore((state) => state.sending);
- const [inputMessage, setInputMessage] = useState('');
- const [attachments, setAttachments] = useState([]);
-
- async function handleSendMessage(): Promise {
- const sent = await chatStore.sendMessage(inputMessage, attachments);
- if (sent) {
- setInputMessage('');
- setAttachments([]);
- }
- }
-
- async function handleAttach(files: File[]): Promise {
- const stagedFiles = await chatStore.stageAttachmentFiles(files);
- setAttachments((currentAttachments) => [...currentAttachments, ...stagedFiles]);
- }
return (
{
- setAttachments((currentAttachments) => currentAttachments.filter((_, currentIndex) => currentIndex !== index));
- }}
- onSend={() => {
- void handleSendMessage();
- }}
+ onRemoveAttachment={onRemoveAttachment}
+ onSend={onSend}
onStop={() => {
void chatStore.abortRun();
}}
@@ -252,6 +249,8 @@ export default function HomePage() {
title: string;
} | null>(null);
const [deletingConversation, setDeletingConversation] = useState(false);
+ const [inputMessage, setInputMessage] = useState('');
+ const [attachments, setAttachments] = useState([]);
useEffect(() => {
void agentsStore.init();
@@ -289,6 +288,11 @@ export default function HomePage() {
return buckets;
}, [chatSessionLabels, chatSessionLastActivity, chatSessions, locale, t]);
+ const hasConversationHistory = useMemo(
+ () => historyBuckets.some((bucket) => bucket.sessions.length > 0),
+ [historyBuckets],
+ );
+
const visibleMessages = useMemo(
() => mapMessages(chatMessages, chatStreamingMessage, locale, t),
[chatMessages, chatStreamingMessage, locale, t],
@@ -381,6 +385,19 @@ export default function HomePage() {
setTaskCenterNotice(result.error || t('task.taskCenter.notices.retryFailed'));
}
+ async function handleSendMessage(): Promise {
+ const sent = await chatStore.sendMessage(inputMessage, attachments);
+ if (sent) {
+ setInputMessage('');
+ setAttachments([]);
+ }
+ }
+
+ async function handleAttach(files: File[]): Promise {
+ const stagedFiles = await chatStore.stageAttachmentFiles(files);
+ setAttachments((currentAttachments) => [...currentAttachments, ...stagedFiles]);
+ }
+
function handleRenameConversation(conversationId: string): void {
const nextTitle = chatSessionLabels[conversationId]
|| chatSessions.find((session) => session.key === conversationId)?.displayName
@@ -484,8 +501,23 @@ export default function HomePage() {
-
-
+
+ {
+ setAttachments((currentAttachments) => currentAttachments.filter((_, currentIndex) => currentIndex !== index));
+ }}
+ onSend={() => {
+ void handleSendMessage();
+ }}
+ />
{/*
diff --git a/src/stores/chat.ts b/src/stores/chat.ts
index f5344f0..24c798c 100644
--- a/src/stores/chat.ts
+++ b/src/stores/chat.ts
@@ -675,6 +675,7 @@ async function deleteSession(sessionKey: string): Promise {
if (state.currentSessionKey === sessionKey) {
const nextSession = remaining[0]?.key ?? getDefaultMainSessionKey();
+ const hasRemainingSessions = remaining.length > 0;
patchState({
...basePatch,
currentSessionKey: nextSession,
@@ -688,7 +689,7 @@ async function deleteSession(sessionKey: string): Promise {
lastUserMessageAt: null,
});
- if (nextSession) {
+ if (hasRemainingSessions && nextSession) {
await loadHistory(nextSession);
}
return;