refactor: remove ChatHeader from Thread component and update related logic, improving code clarity and maintaining consistent header display in chat interface

This commit is contained in:
Anish Sarkar 2026-03-06 22:38:49 +05:30
parent 662f4db13d
commit 8d5d8e490c
4 changed files with 19 additions and 25 deletions

View file

@ -32,7 +32,6 @@ import { closeReportPanelAtom } from "@/atoms/chat/report-panel.atom";
import { membersAtom } from "@/atoms/members/members-query.atoms"; import { membersAtom } from "@/atoms/members/members-query.atoms";
import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { Thread } from "@/components/assistant-ui/thread"; import { Thread } from "@/components/assistant-ui/thread";
import { ChatHeader } from "@/components/new-chat/chat-header";
import { ReportPanel } from "@/components/report-panel/report-panel"; import { ReportPanel } from "@/components/report-panel/report-panel";
import type { ThinkingStep } from "@/components/tool-ui/deepagent-thinking"; import type { ThinkingStep } from "@/components/tool-ui/deepagent-thinking";
import { DisplayImageToolUI } from "@/components/tool-ui/display-image"; import { DisplayImageToolUI } from "@/components/tool-ui/display-image";
@ -476,7 +475,7 @@ export default function NewChatPage() {
const newThread = await createThread(searchSpaceId, initialTitle); const newThread = await createThread(searchSpaceId, initialTitle);
currentThreadId = newThread.id; currentThreadId = newThread.id;
setThreadId(currentThreadId); setThreadId(currentThreadId);
// Set currentThread so ChatHeader can show share button immediately // Set currentThread so share button in header appears immediately
setCurrentThread(newThread); setCurrentThread(newThread);
// Track chat creation // Track chat creation
@ -1670,10 +1669,9 @@ export default function NewChatPage() {
{/* <WriteTodosToolUI /> Disabled for now */} {/* <WriteTodosToolUI /> Disabled for now */}
<div className="flex h-[calc(100dvh-64px)] overflow-hidden"> <div className="flex h-[calc(100dvh-64px)] overflow-hidden">
<div className="flex-1 flex flex-col min-w-0 overflow-hidden"> <div className="flex-1 flex flex-col min-w-0 overflow-hidden">
<Thread <Thread
messageThinkingSteps={messageThinkingSteps} messageThinkingSteps={messageThinkingSteps}
header={<ChatHeader searchSpaceId={searchSpaceId} />} />
/>
</div> </div>
<ReportPanel /> <ReportPanel />
</div> </div>

View file

@ -79,18 +79,17 @@ const CYCLING_PLACEHOLDERS = [
interface ThreadProps { interface ThreadProps {
messageThinkingSteps?: Map<string, ThinkingStep[]>; messageThinkingSteps?: Map<string, ThinkingStep[]>;
header?: React.ReactNode;
} }
export const Thread: FC<ThreadProps> = ({ messageThinkingSteps = new Map(), header }) => { export const Thread: FC<ThreadProps> = ({ messageThinkingSteps = new Map() }) => {
return ( return (
<ThinkingStepsContext.Provider value={messageThinkingSteps}> <ThinkingStepsContext.Provider value={messageThinkingSteps}>
<ThreadContent header={header} /> <ThreadContent />
</ThinkingStepsContext.Provider> </ThinkingStepsContext.Provider>
); );
}; };
const ThreadContent: FC<{ header?: React.ReactNode }> = ({ header }) => { const ThreadContent: FC = () => {
const showGutter = useAtomValue(showCommentsGutterAtom); const showGutter = useAtomValue(showCommentsGutterAtom);
return ( return (
@ -108,8 +107,6 @@ const ThreadContent: FC<{ header?: React.ReactNode }> = ({ header }) => {
showGutter && "lg:pr-30" showGutter && "lg:pr-30"
)} )}
> >
{header && <div className="sticky top-0 z-10 mb-4">{header}</div>}
<AssistantIf condition={({ thread }) => thread.isEmpty}> <AssistantIf condition={({ thread }) => thread.isEmpty}>
<ThreadWelcome /> <ThreadWelcome />
</AssistantIf> </AssistantIf>

View file

@ -3,6 +3,8 @@
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { currentThreadAtom } from "@/atoms/chat/current-thread.atom"; import { currentThreadAtom } from "@/atoms/chat/current-thread.atom";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { ChatHeader } from "@/components/new-chat/chat-header";
import { ChatShareButton } from "@/components/new-chat/chat-share-button"; import { ChatShareButton } from "@/components/new-chat/chat-share-button";
import type { ChatVisibility, ThreadRecord } from "@/lib/chat/thread-persistence"; import type { ChatVisibility, ThreadRecord } from "@/lib/chat/thread-persistence";
@ -12,23 +14,19 @@ interface HeaderProps {
export function Header({ mobileMenuTrigger }: HeaderProps) { export function Header({ mobileMenuTrigger }: HeaderProps) {
const pathname = usePathname(); const pathname = usePathname();
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
// Check if we're on a chat page
const isChatPage = pathname?.includes("/new-chat") ?? false; const isChatPage = pathname?.includes("/new-chat") ?? false;
// Use Jotai atom for thread state (synced from chat page)
const currentThreadState = useAtomValue(currentThreadAtom); const currentThreadState = useAtomValue(currentThreadAtom);
// Show button only when we have a thread id (thread exists and is synced to Jotai)
const hasThread = isChatPage && currentThreadState.id !== null; const hasThread = isChatPage && currentThreadState.id !== null;
// Create minimal thread object for ChatShareButton (used for API calls)
const threadForButton: ThreadRecord | null = const threadForButton: ThreadRecord | null =
hasThread && currentThreadState.id !== null hasThread && currentThreadState.id !== null
? { ? {
id: currentThreadState.id, id: currentThreadState.id,
visibility: currentThreadState.visibility ?? "PRIVATE", visibility: currentThreadState.visibility ?? "PRIVATE",
// These fields are not used by ChatShareButton for display, only for checks
created_by_id: null, created_by_id: null,
search_space_id: 0, search_space_id: 0,
title: "", title: "",
@ -38,21 +36,20 @@ export function Header({ mobileMenuTrigger }: HeaderProps) {
} }
: null; : null;
const handleVisibilityChange = (_visibility: ChatVisibility) => { const handleVisibilityChange = (_visibility: ChatVisibility) => {};
// Visibility change is handled by ChatShareButton internally via Jotai
// This callback can be used for additional side effects if needed
};
return ( return (
<header className="sticky top-0 z-10 flex h-14 shrink-0 items-center gap-2 border-b bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 px-4"> <header className="sticky top-0 z-10 flex h-14 shrink-0 items-center gap-2 bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 px-4">
{/* Left side - Mobile menu trigger */} {/* Left side - Mobile menu trigger + Model selector */}
<div className="flex flex-1 items-center gap-2 min-w-0"> <div className="flex flex-1 items-center gap-2 min-w-0">
{mobileMenuTrigger} {mobileMenuTrigger}
{isChatPage && searchSpaceId && (
<ChatHeader searchSpaceId={Number(searchSpaceId)} className="md:h-9 md:px-4 md:text-sm" />
)}
</div> </div>
{/* Right side - Actions */} {/* Right side - Actions */}
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{/* Share button - only show on chat pages when thread exists */}
{hasThread && ( {hasThread && (
<ChatShareButton thread={threadForButton} onVisibilityChange={handleVisibilityChange} /> <ChatShareButton thread={threadForButton} onVisibilityChange={handleVisibilityChange} />
)} )}

View file

@ -13,9 +13,10 @@ import { ModelSelector } from "./model-selector";
interface ChatHeaderProps { interface ChatHeaderProps {
searchSpaceId: number; searchSpaceId: number;
className?: string;
} }
export function ChatHeader({ searchSpaceId }: ChatHeaderProps) { export function ChatHeader({ searchSpaceId, className }: ChatHeaderProps) {
// LLM config sidebar state // LLM config sidebar state
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
const [selectedConfig, setSelectedConfig] = useState< const [selectedConfig, setSelectedConfig] = useState<
@ -85,6 +86,7 @@ export function ChatHeader({ searchSpaceId }: ChatHeaderProps) {
onAddNewLLM={handleAddNewLLM} onAddNewLLM={handleAddNewLLM}
onEditImage={handleEditImageConfig} onEditImage={handleEditImageConfig}
onAddNewImage={handleAddImageModel} onAddNewImage={handleAddImageModel}
className={className}
/> />
<ModelConfigSidebar <ModelConfigSidebar
open={sidebarOpen} open={sidebarOpen}