refactor: update navigation and chat initialization logic

- Changed navigation from researcher to new-chat in EditorPage.
- Updated NewChatPage to support lazy thread creation on first message.
- Adjusted breadcrumb to not display thread ID for new-chat sections.
- Improved user experience by modifying error messages and loading states.
- Enhanced styling in model-selector component for better visibility.
This commit is contained in:
Anish Sarkar 2025-12-25 12:18:45 +05:30
parent 85ddaedd9e
commit bf22156664
5 changed files with 53 additions and 35 deletions

View file

@ -7,7 +7,7 @@ import {
useExternalStoreRuntime,
} from "@assistant-ui/react";
import { useAtomValue, useSetAtom } from "jotai";
import { useParams, useRouter } from "next/navigation";
import { useParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import {
@ -126,7 +126,6 @@ interface ThinkingStepData {
export default function NewChatPage() {
const params = useParams();
const router = useRouter();
const [isInitializing, setIsInitializing] = useState(true);
const [threadId, setThreadId] = useState<number | null>(null);
const [messages, setMessages] = useState<ThreadMessageLike[]>([]);
@ -168,6 +167,7 @@ export default function NewChatPage() {
}, [params.chat_id]);
// Initialize thread and load messages
// For new chats (no urlChatId), we use lazy creation - thread is created on first message
const initializeThread = useCallback(async () => {
setIsInitializing(true);
@ -206,22 +206,20 @@ export default function NewChatPage() {
setMessageDocumentsMap(restoredDocsMap);
}
}
} else {
// Create new thread
const newThread = await createThread(searchSpaceId, "New Chat");
setThreadId(newThread.id);
router.replace(`/dashboard/${searchSpaceId}/new-chat/${newThread.id}`);
}
// For new chats (urlChatId === 0), don't create thread yet
// Thread will be created lazily when user sends first message
// This improves UX (instant load) and avoids orphan threads
} catch (error) {
console.error("[NewChatPage] Failed to initialize thread:", error);
// Keep threadId as null - don't use Date.now() as it creates an invalid ID
// that will cause 404 errors on subsequent API calls
setThreadId(null);
toast.error("Failed to initialize chat. Please try again.");
toast.error("Failed to load chat. Please try again.");
} finally {
setIsInitializing(false);
}
}, [urlChatId, searchSpaceId, router, setMessageDocumentsMap]);
}, [urlChatId, setMessageDocumentsMap]);
// Initialize on mount
useEffect(() => {
@ -240,8 +238,6 @@ export default function NewChatPage() {
// Handle new message from user
const onNew = useCallback(
async (message: AppendMessage) => {
if (!threadId) return;
// Extract user query text from content parts
let userQuery = "";
for (const part of message.content) {
@ -273,6 +269,27 @@ export default function NewChatPage() {
return;
}
// Lazy thread creation: create thread on first message if it doesn't exist
let currentThreadId = threadId;
if (!currentThreadId) {
try {
const newThread = await createThread(searchSpaceId, "New Chat");
currentThreadId = newThread.id;
setThreadId(currentThreadId);
// Update URL silently using browser API (not router.replace) to avoid
// interrupting the ongoing fetch/streaming with React navigation
window.history.replaceState(
null,
"",
`/dashboard/${searchSpaceId}/new-chat/${currentThreadId}`
);
} catch (error) {
console.error("[NewChatPage] Failed to create thread:", error);
toast.error("Failed to start chat. Please try again.");
return;
}
}
// Add user message to state
const userMsgId = `msg-user-${Date.now()}`;
const userMessage: ThreadMessageLike = {
@ -311,7 +328,7 @@ export default function NewChatPage() {
},
]
: message.content;
appendMessage(threadId, {
appendMessage(currentThreadId, {
role: "user",
content: persistContent,
}).catch((err) => console.error("Failed to persist user message:", err));
@ -468,7 +485,7 @@ export default function NewChatPage() {
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
chat_id: threadId,
chat_id: currentThreadId,
user_query: userQuery.trim(),
search_space_id: searchSpaceId,
messages: messageHistory,
@ -601,7 +618,7 @@ export default function NewChatPage() {
// Persist assistant message (with thinking steps for restoration on refresh)
const finalContent = buildContentForPersistence();
if (contentParts.length > 0) {
appendMessage(threadId, {
appendMessage(currentThreadId, {
role: "assistant",
content: finalContent,
}).catch((err) => console.error("Failed to persist assistant message:", err));
@ -678,7 +695,7 @@ export default function NewChatPage() {
},
});
// Show loading state
// Show loading state only when loading an existing thread
if (isInitializing) {
return (
<div className="flex h-[calc(100vh-64px)] items-center justify-center">
@ -687,11 +704,12 @@ export default function NewChatPage() {
);
}
// Show error state if thread initialization failed
if (!threadId) {
// Show error state only if we tried to load an existing thread but failed
// For new chats (urlChatId === 0), threadId being null is expected (lazy creation)
if (!threadId && urlChatId > 0) {
return (
<div className="flex h-[calc(100vh-64px)] flex-col items-center justify-center gap-4">
<div className="text-destructive">Failed to initialize chat</div>
<div className="text-destructive">Failed to load chat</div>
<button
type="button"
onClick={() => {