feat: update Makelore modules and conversations
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* TitleBar at top, then sidebar + content below.
|
||||
*/
|
||||
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useEffect } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { LockKeyhole, Settings2 } from 'lucide-react';
|
||||
import { Sidebar } from './Sidebar';
|
||||
import { TitleBar } from './TitleBar';
|
||||
@@ -11,16 +11,51 @@ import { Button } from '@/components/ui/button';
|
||||
import { getAiModuleForPath } from '@/lib/ai-modules';
|
||||
import { useOpencodeStore } from '@/stores/opencode';
|
||||
import { useProjectConfigStore } from '@/stores/project-config';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
|
||||
export function MainLayout() {
|
||||
const activeProject = useOpencodeStore((state) => state.activeProject);
|
||||
const sidebarCollapsed = useSettingsStore((state) => state.sidebarCollapsed);
|
||||
const config = useProjectConfigStore((state) => activeProject ? state.configsByProjectId[activeProject.id] : undefined);
|
||||
const load = useProjectConfigStore((state) => state.load);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [sidebarPeekOpen, setSidebarPeekOpen] = useState(false);
|
||||
const sidebarPeekCloseTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const isPaintingModule = getAiModuleForPath(location.pathname) === 'painting';
|
||||
const isInitializationSafeRoute = location.pathname === '/project-config' || isPaintingModule;
|
||||
|
||||
const handleSidebarPeekChange = useCallback((open: boolean) => {
|
||||
if (!sidebarCollapsed) return;
|
||||
|
||||
if (sidebarPeekCloseTimerRef.current) {
|
||||
clearTimeout(sidebarPeekCloseTimerRef.current);
|
||||
sidebarPeekCloseTimerRef.current = null;
|
||||
}
|
||||
|
||||
if (open) {
|
||||
setSidebarPeekOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
sidebarPeekCloseTimerRef.current = setTimeout(() => {
|
||||
setSidebarPeekOpen(false);
|
||||
sidebarPeekCloseTimerRef.current = null;
|
||||
}, 120);
|
||||
}, [sidebarCollapsed]);
|
||||
|
||||
useEffect(() => {
|
||||
setSidebarPeekOpen(false);
|
||||
if (sidebarPeekCloseTimerRef.current) {
|
||||
clearTimeout(sidebarPeekCloseTimerRef.current);
|
||||
sidebarPeekCloseTimerRef.current = null;
|
||||
}
|
||||
}, [sidebarCollapsed]);
|
||||
|
||||
useEffect(() => () => {
|
||||
if (sidebarPeekCloseTimerRef.current) clearTimeout(sidebarPeekCloseTimerRef.current);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeProject && !isPaintingModule) void load(activeProject.id).catch(() => undefined);
|
||||
}, [activeProject, isPaintingModule, load]);
|
||||
@@ -29,20 +64,20 @@ export function MainLayout() {
|
||||
return (
|
||||
<div data-testid="main-layout" className="flex h-screen flex-col overflow-hidden bg-background">
|
||||
{/* Title bar: drag region on macOS, icon + controls on Windows */}
|
||||
<TitleBar integrated />
|
||||
<TitleBar integrated sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={handleSidebarPeekChange} />
|
||||
|
||||
{/* Below the title bar: sidebar + content */}
|
||||
<div className="flex min-h-0 flex-1 overflow-hidden">
|
||||
<Sidebar />
|
||||
<main data-testid="main-content" className="relative min-h-0 flex-1 overflow-auto p-6">
|
||||
<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">
|
||||
<Outlet />
|
||||
{initializationBlocked ? (
|
||||
<div data-testid="project-initialization-gate" className="absolute inset-0 z-50 flex items-center justify-center bg-white/95 p-6">
|
||||
<div className="max-w-md rounded-2xl border-2 border-[#26384D] bg-[#FFFFFF] p-8 text-center shadow-[8px_8px_0_#26384D]">
|
||||
<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 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-black">项目尚未初始化</h1>
|
||||
<p className="mt-3 text-sm font-bold text-[#68788B]">完成模板 Agent 名称确认后,聊天、任务和其他项目功能才会启用。</p>
|
||||
<Button onClick={() => navigate('/project-config')} className="mt-6 border-2 border-[#26384D] bg-[#3A5578] font-black text-white shadow-[4px_4px_0_#26384D]"><Settings2 className="mr-2 h-4 w-4" />打开项目配置</Button>
|
||||
<h1 className="mt-4 text-2xl font-semibold">项目尚未初始化</h1>
|
||||
<p className="mt-3 text-sm font-medium text-muted-foreground">请先在项目配置中完成至少一个手动 Agent 的名称、模型和职责设置。</p>
|
||||
<Button onClick={() => navigate('/project-config')} className="mt-6 border border-brand/20 bg-brand font-semibold text-primary-foreground"><Settings2 className="mr-2 h-4 w-4" />打开项目配置</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user