SurfSense/surfsense_web/components/layout/ui/header/Header.tsx
Anish Sarkar a8c1fb660d feat(rename): complete searchSpace to workspace transition across frontend and backend
- Introduced a comprehensive specification for renaming `searchSpace` to `workspace` in `surfsense_web` and `surfsense_desktop`, ensuring all TypeScript identifiers, React props, and local data structures are updated.
- Implemented migration shims for persisted local state to prevent data loss during the transition.
- Updated observability metrics and IPC channels to reflect the new naming convention.
- Removed legacy `active-search-space` module and replaced it with `active-workspace` to maintain consistency.
- Ensured no behavioral changes or data loss for users during the renaming process.
2026-07-06 15:12:40 +05:30

80 lines
2.9 KiB
TypeScript

"use client";
import { useAtomValue } from "jotai";
import { usePathname } from "next/navigation";
import { currentThreadAtom } from "@/atoms/chat/current-thread.atom";
import { activeTabAtom } from "@/atoms/tabs/tabs.atom";
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { ActionLogButton } from "@/components/agent-action-log/action-log-button";
import { ChatShareButton } from "@/components/new-chat/chat-share-button";
import { ArtifactsToggleButton } from "@/features/chat-artifacts";
import type { ThreadRecord } from "@/lib/chat/thread-persistence";
interface HeaderProps {
mobileMenuTrigger?: React.ReactNode;
}
export function Header({ mobileMenuTrigger }: HeaderProps) {
const pathname = usePathname();
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
const activeTab = useAtomValue(activeTabAtom);
const isFreePage = pathname?.startsWith("/free") ?? false;
const isChatPage = pathname?.includes("/new-chat") ?? false;
const isDocumentTab = activeTab?.type === "document";
const currentThreadState = useAtomValue(currentThreadAtom);
const hasThread = isChatPage && !isDocumentTab && currentThreadState.id !== null;
const activeWorkspaceId = workspaceId ? Number(workspaceId) : null;
const canRenderShareButton =
hasThread &&
currentThreadState.id !== null &&
currentThreadState.visibility !== null &&
currentThreadState.workspaceId !== null &&
activeWorkspaceId !== null &&
currentThreadState.workspaceId === activeWorkspaceId;
// Free chat pages have their own header with model selector; only render mobile trigger
if (isFreePage) {
if (!mobileMenuTrigger) return null;
return (
<header className="sticky top-0 z-10 flex h-14 shrink-0 items-center gap-2 bg-main-panel/95 backdrop-blur supports-backdrop-filter:bg-main-panel/60 px-4">
{mobileMenuTrigger}
</header>
);
}
let threadForButton: ThreadRecord | null = null;
if (
canRenderShareButton &&
currentThreadState.id !== null &&
currentThreadState.visibility !== null &&
currentThreadState.workspaceId !== null
) {
threadForButton = {
id: currentThreadState.id,
visibility: currentThreadState.visibility,
created_by_id: null,
workspace_id: currentThreadState.workspaceId,
title: "",
archived: false,
created_at: "",
updated_at: "",
};
}
return (
<header className="sticky top-0 z-10 flex h-14 shrink-0 items-center gap-2 bg-main-panel/95 backdrop-blur supports-backdrop-filter:bg-main-panel/60 px-4">
{/* Left side - Mobile menu trigger */}
<div className="flex flex-1 items-center gap-2 min-w-0">{mobileMenuTrigger}</div>
{/* Right side - Actions */}
<div className="ml-auto flex items-center gap-2">
{hasThread && <ActionLogButton threadId={currentThreadState.id} />}
{hasThread && <ArtifactsToggleButton />}
{threadForButton && <ChatShareButton thread={threadForButton} />}
</div>
</header>
);
}