收口 Makelore 客户端变更
This commit is contained in:
@@ -12,6 +12,10 @@ import { getAiModuleForPath } from '@/lib/ai-modules';
|
||||
import { useOpencodeStore } from '@/stores/opencode';
|
||||
import { useProjectConfigStore } from '@/stores/project-config';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { SidebarPeekSource } from './sidebar-peek';
|
||||
|
||||
const SIDEBAR_PEEK_CLOSE_DELAY_MS = 180;
|
||||
|
||||
export function MainLayout() {
|
||||
const activeProject = useOpencodeStore((state) => state.activeProject);
|
||||
@@ -22,29 +26,48 @@ export function MainLayout() {
|
||||
const navigate = useNavigate();
|
||||
const [sidebarPeekOpen, setSidebarPeekOpen] = useState(false);
|
||||
const sidebarPeekCloseTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const sidebarPeekSourcesRef = useRef<Record<SidebarPeekSource, boolean>>({
|
||||
toggle: false,
|
||||
focus: false,
|
||||
sidebar: false,
|
||||
titlebar: false,
|
||||
});
|
||||
const isPaintingModule = getAiModuleForPath(location.pathname) === 'painting';
|
||||
const isChatWorkspace = location.pathname === '/opencode-chat';
|
||||
const isInitializationSafeRoute = location.pathname === '/project-config' || isPaintingModule;
|
||||
|
||||
const handleSidebarPeekChange = useCallback((open: boolean) => {
|
||||
const handleSidebarPeekChange = useCallback((open: boolean, source: SidebarPeekSource) => {
|
||||
if (!sidebarCollapsed) return;
|
||||
|
||||
const sources = sidebarPeekSourcesRef.current;
|
||||
if (sources[source] === open) return;
|
||||
|
||||
if (sidebarPeekCloseTimerRef.current) {
|
||||
clearTimeout(sidebarPeekCloseTimerRef.current);
|
||||
sidebarPeekCloseTimerRef.current = null;
|
||||
}
|
||||
|
||||
sources[source] = open;
|
||||
|
||||
if (open) {
|
||||
setSidebarPeekOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object.values(sources).some(Boolean)) return;
|
||||
|
||||
sidebarPeekCloseTimerRef.current = setTimeout(() => {
|
||||
if (Object.values(sidebarPeekSourcesRef.current).some(Boolean)) return;
|
||||
setSidebarPeekOpen(false);
|
||||
sidebarPeekCloseTimerRef.current = null;
|
||||
}, 120);
|
||||
}, SIDEBAR_PEEK_CLOSE_DELAY_MS);
|
||||
}, [sidebarCollapsed]);
|
||||
|
||||
useEffect(() => {
|
||||
sidebarPeekSourcesRef.current = { toggle: false, focus: false, sidebar: false, titlebar: false };
|
||||
// This transient state mirrors the external persisted sidebar preference;
|
||||
// reset it when that preference changes so a stale preview cannot reopen.
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setSidebarPeekOpen(false);
|
||||
if (sidebarPeekCloseTimerRef.current) {
|
||||
clearTimeout(sidebarPeekCloseTimerRef.current);
|
||||
@@ -62,17 +85,32 @@ export function MainLayout() {
|
||||
|
||||
const initializationBlocked = Boolean(activeProject && (!config || !config.initialized) && !isInitializationSafeRoute);
|
||||
return (
|
||||
<div data-testid="main-layout" className="flex h-screen flex-col overflow-hidden bg-background">
|
||||
<div data-testid="main-layout" className="flex h-screen flex-col overflow-hidden bg-transparent">
|
||||
{/* Title bar: drag region on macOS, icon + controls on Windows */}
|
||||
<TitleBar integrated sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={handleSidebarPeekChange} />
|
||||
<TitleBar
|
||||
integrated
|
||||
workspaceLayout={isChatWorkspace}
|
||||
sidebarPeekOpen={sidebarPeekOpen}
|
||||
onSidebarPeekChange={handleSidebarPeekChange}
|
||||
/>
|
||||
|
||||
{/* Below the title bar: sidebar + content */}
|
||||
<div className="relative flex min-h-0 flex-1 overflow-hidden">
|
||||
<Sidebar sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={handleSidebarPeekChange} />
|
||||
<main data-testid="main-content" className="relative min-h-0 flex-1 overflow-auto bg-background p-5 sm:p-6">
|
||||
<Sidebar
|
||||
workspaceLayout={isChatWorkspace}
|
||||
sidebarPeekOpen={sidebarPeekOpen}
|
||||
onSidebarPeekChange={handleSidebarPeekChange}
|
||||
/>
|
||||
<main
|
||||
data-testid="main-content"
|
||||
className={cn(
|
||||
'relative min-h-0 min-w-0 flex-1 overflow-auto bg-background p-5 sm:p-6',
|
||||
isChatWorkspace && 'basis-0 overflow-hidden p-0',
|
||||
)}
|
||||
>
|
||||
<Outlet />
|
||||
{initializationBlocked ? (
|
||||
<div data-testid="project-initialization-gate" className="glass-surface absolute inset-0 z-50 flex items-center justify-center bg-background/90 p-6">
|
||||
<div data-testid="project-initialization-gate" className="glass-surface absolute inset-0 z-[100] flex items-center justify-center bg-background/90 p-6">
|
||||
<div className="surface-card max-w-md rounded-2xl border border-border/80 bg-background p-8 text-center shadow-float">
|
||||
<LockKeyhole className="mx-auto h-10 w-10" />
|
||||
<h1 className="mt-4 text-2xl font-semibold">项目尚未初始化</h1>
|
||||
|
||||
Reference in New Issue
Block a user