"use client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { useSearchParams } from "next/navigation"; import { useCallback, useEffect, useLayoutEffect, useRef, useState, } from "react"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { Toaster } from "@/components/ui/sonner"; import { CommandPalette } from "@/components/workspace/command-palette"; import { WorkspaceSidebar } from "@/components/workspace/workspace-sidebar"; import { getLocalSettings, useLocalSettings } from "@/core/settings"; const queryClient = new QueryClient(); export default function WorkspaceLayout({ children, }: Readonly<{ children: React.ReactNode }>) { const [settings, setSettings] = useLocalSettings(); const [open, setOpen] = useState(false); // SSR default: open (matches server render) const [showWorkspaceSidebar, setShowWorkspaceSidebar] = useState(false); const pressedKeysRef = useRef>(new Set()); const comboTriggeredRef = useRef(false); const searchParams = useSearchParams(); // iframe 技能模式(mode=skill)时隐藏侧边栏 const isSkillMode = searchParams.get("mode") === "skill"; useLayoutEffect(() => { // Runs synchronously before first paint on the client — no visual flash setOpen(!getLocalSettings().layout.sidebar_collapsed); }, []); useEffect(() => { setOpen(!settings.layout.sidebar_collapsed); }, [settings.layout.sidebar_collapsed]); useEffect(() => { const resetComboTrigger = () => { comboTriggeredRef.current = false; }; const handleKeyDown = (event: KeyboardEvent) => { const target = event.target as HTMLElement | null; if ( target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target?.isContentEditable ) { return; } pressedKeysRef.current.add(event.key.toLowerCase()); const hasCtrlOrMeta = event.ctrlKey || event.metaKey; const hasShift = event.shiftKey; const hasL = pressedKeysRef.current.has("l"); const hasD = pressedKeysRef.current.has("d"); if ( hasCtrlOrMeta && hasShift && hasL && hasD && !comboTriggeredRef.current ) { event.preventDefault(); comboTriggeredRef.current = true; setShowWorkspaceSidebar((prev) => !prev); } }; const handleKeyUp = (event: KeyboardEvent) => { pressedKeysRef.current.delete(event.key.toLowerCase()); if ( !pressedKeysRef.current.has("l") || !pressedKeysRef.current.has("d") || (!event.ctrlKey && !event.metaKey) || !event.shiftKey ) { resetComboTrigger(); } }; const handleBlur = () => { pressedKeysRef.current.clear(); resetComboTrigger(); }; window.addEventListener("keydown", handleKeyDown); window.addEventListener("keyup", handleKeyUp); window.addEventListener("blur", handleBlur); return () => { window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keyup", handleKeyUp); window.removeEventListener("blur", handleBlur); }; }, []); const handleOpenChange = useCallback( (open: boolean) => { setOpen(open); setSettings("layout", { sidebar_collapsed: !open }); }, [setSettings], ); return ( {!isSkillMode && showWorkspaceSidebar && ( )} {children} [data-icon]]:hidden", ].join(" "), title: "text-primary-foreground! text-sm font-normal text-center w-full leading-snug", description: "hidden", icon: "hidden", }, }} /> ); }