deerflow2/frontend/src/components/workspace/workspace-header.tsx

116 lines
3.8 KiB
TypeScript

"use client";
import { MessageSquarePlus } from "lucide-react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { toast } from "sonner";
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar";
import { useThreadChat } from "@/components/workspace/chats";
import { useI18n } from "@/core/i18n/hooks";
import { POST_MESSAGE_TYPES, sendToParent } from "@/core/iframe-messages";
import { env } from "@/env";
import { cn } from "@/lib/utils";
import { copyToClipboard } from "@/lib/utils";
export function WorkspaceHeader({ className }: { className?: string }) {
const { t } = useI18n();
const { state } = useSidebar();
const pathname = usePathname();
const { threadId } = useThreadChat();
const threadUrl = threadId ? `/workspace/chats/${threadId}` : "";
const handleCopyThreadId = async () => {
if (!threadId) return;
sendToParent({
type: POST_MESSAGE_TYPES.COPY_TO_CLIPBOARD,
text: threadId,
});
try {
await copyToClipboard(threadId);
toast.success(t.clipboard.copiedToClipboard);
} catch {
toast.error(t.clipboard.failedToCopyToClipboard);
}
};
return (
<>
<div
className={cn(
"group/workspace-header flex h-12 flex-col justify-center",
className,
)}
>
{state === "collapsed" ? (
<div className="group-has-data-[collapsible=icon]/sidebar-wrapper:-translate-y flex w-full cursor-pointer items-center justify-center">
<div className="text-primary block pt-1 font-serif group-hover/workspace-header:hidden">
XC
</div>
<SidebarTrigger className="hidden pl-2 group-hover/workspace-header:block" />
</div>
) : (
<div className="flex items-center justify-between gap-2">
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ? (
<Link href="/" className="text-primary ml-2 font-serif">
{t.workspaceHeader.sidebarTitle}
</Link>
) : (
<div className="text-primary ml-2 cursor-default font-serif">
{/* TODO: 测试标识 */}
XClaw{" "}
<span className="text-sm text-ws-text-subtle-strong">v3.2.9 </span>{" "}
<span
className={cn(
"text-xs font-mono",
threadId
? "cursor-pointer underline decoration-dotted underline-offset-4"
: "text-ws-text-subtle-strong",
)}
onClick={() => {
void handleCopyThreadId();
}}
title={threadId ? t.clipboard.copyToClipboard : undefined}
>
id:{threadId ? threadId.slice(0, 5) : "-"}
</span>
{" "}
{threadId && (
<a
href={threadUrl}
target="_blank"
rel="noopener noreferrer"
className="text-xs underline decoration-dotted underline-offset-4"
>
</a>
)}
</div>
)}
<SidebarTrigger />
</div>
)}
</div>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
isActive={pathname === "/workspace/chats/new"}
asChild
>
<Link className="text-muted-foreground" href="/workspace/chats/new">
<MessageSquarePlus size={16} />
<span>{t.sidebar.newChat}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</>
);
}