import { useState, useCallback, useRef, useEffect } from 'react'; import { FolderOpen, FileText, Terminal, ChevronDown, ChevronUp, Activity, Wifi, Save, GitBranch, X, LayoutGrid } from 'lucide-react'; import type { MessageHub, LogService } from '@esengine/editor-core'; import { ContentBrowser } from './ContentBrowser'; import { OutputLogPanel } from './OutputLogPanel'; import { useLocale } from '../hooks/useLocale'; import '../styles/StatusBar.css'; interface StatusBarProps { pluginCount?: number; entityCount?: number; messageHub?: MessageHub | null; logService?: LogService | null; locale?: string; projectPath?: string | null; onOpenScene?: (scenePath: string) => void; /** 停靠内容管理器到布局中的回调 | Callback to dock content browser in layout */ onDockContentBrowser?: () => void; /** 重置布局回调 | Callback to reset layout */ onResetLayout?: () => void; } type ActiveTab = 'output' | 'cmd'; export function StatusBar({ pluginCount = 0, entityCount = 0, messageHub, logService, locale = 'en', projectPath, onOpenScene, onDockContentBrowser, onResetLayout }: StatusBarProps) { const { t } = useLocale(); const [consoleInput, setConsoleInput] = useState(''); const [activeTab, setActiveTab] = useState('output'); const [contentDrawerOpen, setContentDrawerOpen] = useState(false); const [outputLogDrawerOpen, setOutputLogDrawerOpen] = useState(false); const [contentDrawerHeight, setContentDrawerHeight] = useState(300); const [outputLogDrawerHeight, setOutputLogDrawerHeight] = useState(300); const [isResizingContent, setIsResizingContent] = useState(false); const [isResizingOutputLog, setIsResizingOutputLog] = useState(false); const [revealPath, setRevealPath] = useState(null); const inputRef = useRef(null); const startY = useRef(0); const startHeight = useRef(0); // Subscribe to asset:reveal event useEffect(() => { if (!messageHub) return; const unsubscribe = messageHub.subscribe('asset:reveal', (payload: { path: string }) => { if (payload.path) { // Generate unique key to force re-trigger even with same path setRevealPath(`${payload.path}?t=${Date.now()}`); setContentDrawerOpen(true); setOutputLogDrawerOpen(false); } }); return unsubscribe; }, [messageHub]); // Clear revealPath when drawer closes useEffect(() => { if (!contentDrawerOpen) { setRevealPath(null); } }, [contentDrawerOpen]); const handleSelectPanel = useCallback((panelId: string) => { if (messageHub) { messageHub.publish('panel:select', { panelId }); } }, [messageHub]); const handleContentDrawerClick = useCallback(() => { setContentDrawerOpen(!contentDrawerOpen); if (!contentDrawerOpen) { setOutputLogDrawerOpen(false); } }, [contentDrawerOpen]); const handleOutputLogClick = useCallback(() => { setActiveTab('output'); setOutputLogDrawerOpen(!outputLogDrawerOpen); if (!outputLogDrawerOpen) { setContentDrawerOpen(false); } }, [outputLogDrawerOpen]); const handleCmdClick = useCallback(() => { setActiveTab('cmd'); handleSelectPanel('console'); setTimeout(() => { inputRef.current?.focus(); }, 100); }, [handleSelectPanel]); const handleConsoleSubmit = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Enter' && consoleInput.trim()) { const command = consoleInput.trim(); console.info(`> ${command}`); try { if (command.startsWith('help')) { console.info('Available commands: help, clear, echo '); } else if (command === 'clear') { logService?.clear(); } else if (command.startsWith('echo ')) { console.info(command.substring(5)); } else { console.warn(`Unknown command: ${command}`); } } catch (error) { console.error(`Error executing command: ${error}`); } setConsoleInput(''); } }, [consoleInput, logService]); useEffect(() => { if (activeTab === 'cmd') { inputRef.current?.focus(); } }, [activeTab]); // Handle content drawer resize const handleContentResizeStart = useCallback((e: React.MouseEvent) => { e.preventDefault(); setIsResizingContent(true); startY.current = e.clientY; startHeight.current = contentDrawerHeight; }, [contentDrawerHeight]); // Handle output log drawer resize const handleOutputLogResizeStart = useCallback((e: React.MouseEvent) => { e.preventDefault(); setIsResizingOutputLog(true); startY.current = e.clientY; startHeight.current = outputLogDrawerHeight; }, [outputLogDrawerHeight]); useEffect(() => { if (!isResizingContent && !isResizingOutputLog) return; const handleMouseMove = (e: MouseEvent) => { const delta = startY.current - e.clientY; const newHeight = Math.max(200, Math.min(startHeight.current + delta, window.innerHeight * 0.7)); if (isResizingContent) { setContentDrawerHeight(newHeight); } else if (isResizingOutputLog) { setOutputLogDrawerHeight(newHeight); } }; const handleMouseUp = () => { setIsResizingContent(false); setIsResizingOutputLog(false); }; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; }, [isResizingContent, isResizingOutputLog]); // Close drawer on Escape useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { if (contentDrawerOpen) { setContentDrawerOpen(false); } if (outputLogDrawerOpen) { setOutputLogDrawerOpen(false); } } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [contentDrawerOpen, outputLogDrawerOpen]); return ( <> {/* Drawer Backdrop */} {(contentDrawerOpen || outputLogDrawerOpen) && (
{ setContentDrawerOpen(false); setOutputLogDrawerOpen(false); }} /> )} {/* Content Drawer Panel */}
Content Browser
{ // 关闭抽屉并停靠到布局 | Close drawer and dock to layout setContentDrawerOpen(false); onDockContentBrowser?.(); }} />
{/* Output Log Drawer Panel */}
{logService && ( setOutputLogDrawerOpen(false)} /> )}
{/* Status Bar */}
> setConsoleInput(e.target.value)} onKeyDown={handleConsoleSubmit} onFocus={() => setActiveTab('cmd')} />
{t('statusBar.allSaved')}
{t('statusBar.revisionControl')}
); }