Files
artex/web/src/app/(main)/_components/main-content.tsx
T
dela 0335d572de
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
First Commit
2026-10-09 08:38:16 +08:00

89 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { type ReactNode, useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import { useTranslations } from "next-intl";
import { Separator } from "@/components/ui/separator";
import { SidebarTrigger } from "@/components/ui/sidebar";
import { useCurrentUser } from "@/hooks/use-current-user";
import { api } from "@/lib/api";
import { cn } from "@/lib/utils";
import { AccountSwitcher } from "./sidebar/account-switcher";
import { LayoutControls } from "./sidebar/layout-controls";
import { SearchDialog } from "./sidebar/search-dialog";
import { ThemeSwitcher } from "./sidebar/theme-switcher";
import { UpdateBadge } from "./update-badge";
// 任务详情页保持原样:它自带头部/Tabs 与内边距,这里不再叠加全局头部和 padding。
function isFullBleed(pathname: string) {
const p = (() => {
try {
return decodeURIComponent(pathname);
} catch {
return pathname;
}
})();
// 静态导出开了 trailingSlash,列表页自身的 pathname 是 "/function/tasks/",
// 先去掉尾斜杠再判前缀,否则列表页会被误判成详情页而丢掉全局头部。
const normalized = p.replace(/\/+$/, "");
return normalized.startsWith("/function/tasks/");
}
export function MainContent({ children }: { children: ReactNode }) {
const currentUser = useCurrentUser();
const pathname = usePathname();
const t = useTranslations("header");
const [version, setVersion] = useState("");
useEffect(() => {
api
.health()
.then((h) => setVersion((h.version ?? "").replace(/^v(?=\d)/, "")))
.catch(() => setVersion(""));
}, []);
if (isFullBleed(pathname)) {
return <>{children}</>;
}
return (
<>
<header
className={cn(
"flex h-12 shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12",
"[html[data-navbar-style=sticky]_&]:sticky [html[data-navbar-style=sticky]_&]:top-0 [html[data-navbar-style=sticky]_&]:z-50 [html[data-navbar-style=sticky]_&]:overflow-hidden [html[data-navbar-style=sticky]_&]:rounded-t-[inherit] [html[data-navbar-style=sticky]_&]:bg-background/50 [html[data-navbar-style=sticky]_&]:backdrop-blur-md",
)}
>
<div className="flex w-full items-center justify-between px-4 lg:px-6">
<div className="flex items-center gap-1 lg:gap-2">
<SidebarTrigger className="-ml-1" />
<Separator
orientation="vertical"
className="mx-2 data-[orientation=vertical]:h-4 data-[orientation=vertical]:self-center"
/>
<SearchDialog />
</div>
<div className="flex items-center gap-2">
{version && (
<span className="font-medium text-muted-foreground text-xs tabular-nums">
{t("version", { version })}
</span>
)}
<UpdateBadge />
<LayoutControls />
<ThemeSwitcher />
<AccountSwitcher users={[currentUser]} />
</div>
</div>
</header>
<div className="min-h-0 min-w-0 flex-1 overflow-x-hidden p-4 has-data-[content-padding=false]:p-0 md:p-6 md:has-data-[content-padding=false]:p-0">
{children}
</div>
</>
);
}