import type { SessionInfo } from "@umt/contracts"; import { Clapperboard, LoaderCircle, Menu, PanelLeftClose, PanelLeftOpen } from "lucide-react"; import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react"; import { Link, Outlet, useLocation, useMatches } from "react-router"; import { useSession } from "~/api/auth.ts"; import { AgentDrawer, AgentDrawerTrigger } from "~/components/agent/agent-drawer.tsx"; import { outletKey } from "~/components/agent/agent-store.ts"; import { AccountMenu } from "~/components/console/account-menu.tsx"; import { AgentLauncher, AgentNavEntry } from "~/components/console/agent-launcher.tsx"; import { ErrorState } from "~/components/console/empty-state.tsx"; import { InboxBell } from "~/components/console/inbox-bell.tsx"; import { findNavItem, NAV_GROUPS, type NavGroup, pageTitle } from "~/components/console/nav.ts"; import { LiquidSurface } from "~/components/liquid-surface.tsx"; import PrismLeak from "~/components/prism-leak.tsx"; import { CommandPalette } from "~/components/search/command-palette.tsx"; import { SearchTrigger } from "~/components/search/search-trigger.tsx"; import { ThemeToggle } from "~/components/theme-toggle.tsx"; import { Button } from "~/components/ui/button.tsx"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "~/components/ui/sheet.tsx"; import { redirectToLogin } from "~/lib/api.ts"; import { cn } from "~/lib/utils.ts"; const PORTAL_NAME = "媒体自动化"; // ─── 展开态导航:选中项由一枚玻璃滑块(seg-thumb)滑到激活链接背后 ─── function ExpandedNav({ groups, onNavigate }: { groups: NavGroup[]; onNavigate?: () => void }) { const location = useLocation(); const activeTo = findNavItem(location.pathname)?.to; const navRef = useRef(null); const [pill, setPill] = useState<{ x: number; y: number; w: number; h: number } | null>(null); const measure = useCallback(() => { const nav = navRef.current; if (!nav) return; const active = nav.querySelector('[aria-current="page"]'); if (!active) { setPill(null); return; } const navRect = nav.getBoundingClientRect(); const rect = active.getBoundingClientRect(); setPill({ x: rect.left - navRect.left, y: rect.top - navRect.top + nav.scrollTop, w: rect.width, h: rect.height }); }, []); // biome-ignore lint/correctness/useExhaustiveDependencies: 路由变化时重新量测 useLayoutEffect(() => { measure(); }, [measure, location.pathname]); useEffect(() => { window.addEventListener("resize", measure); return () => window.removeEventListener("resize", measure); }, [measure]); return ( ); } // ─── 折叠态:只显示图标,组间细线分隔 ─── function CollapsedNav() { const location = useLocation(); const activeTo = findNavItem(location.pathname)?.to; return ( ); } function Brand({ className }: { className?: string }) { return (

UHD Media Tool

); } /** 会话探测:未登录跳登录页;探测中/失败给全屏占位 */ export default function ConsoleLayout() { const session = useSession(); const unauthenticated = session.data && !session.data.authenticated; useEffect(() => { if (unauthenticated) redirectToLogin(); }, [unauthenticated]); if (session.isError) { return (
session.refetch()} />
); } if (!session.data || unauthenticated) { return (
); } return ; } function ConsoleShell({ session }: { session: SessionInfo }) { const location = useLocation(); const [collapsed, setCollapsed] = useState(false); const [sheetOpen, setSheetOpen] = useState(false); const activeLabel = pageTitle(location.pathname) ?? PORTAL_NAME; // 路由 handle.layout === "wide"(文件管理):内容区不限最大宽度、不留外边距,页面自己排版 const wide = useMatches().some((m) => (m.handle as { layout?: string } | undefined)?.layout === "wide"); return (
{/* 桌面端侧栏 */} {/* 侧栏贴边,不做浮起卡片;右边不画硬分隔线,由 pg-chrome-side 给内侧细亮边、外侧细暗线和淡投影 */} {/* 主内容区 */}
{/* 顶栏不铺磨砂(磨砂栏会成为背景根,里面的玻璃糊不到页面):页面底色渐隐托住标题, 右侧通知、智能体、日夜切换与账户收进一枚玻璃胶囊 */}
setSheetOpen(false)} /> {activeLabel}
{/* 桌面有侧栏主入口与右下角悬浮按钮,顶栏的智能体按钮只在窄屏保留 */}
{/* 底部留出悬浮按钮的高度,页面末尾的内容能滚到它上面 */}
); }