import React, { Suspense, lazy, useRef, useEffect, useMemo } from 'react'; import GlobalStyles from '../styles/GlobalStyles'; import Header from './Header.jsx'; import RuntimeSettingsPanel from './RuntimeSettingsPanel.jsx'; import StockLogo from './StockLogo.jsx'; import NetValueChart from './NetValueChart.jsx'; import { AGENTS } from '../config/constants'; import { useRuntimeStore } from '../store/runtimeStore'; import { useUIStore } from '../store/uiStore'; import { formatNumber, formatTickerPrice } from '../utils/formatters'; const RoomView = lazy(() => import('./RoomView')); const AgentFeed = lazy(() => import('./AgentFeed')); const StatisticsView = lazy(() => import('./StatisticsView')); const StockExplainView = lazy(() => import('./StockExplainView.jsx')); const TraderView = lazy(() => import('./TraderView.jsx')); const OpenClawView = lazy(() => import('./OpenClawView.jsx')); function ViewLoadingFallback({ label = '加载中...' }) { return (
{label}
); } /** * AppShell - Layout shell containing Header, TickerBar, ViewNavBar, View container, and AgentFeed */ export default function AppShell({ // Connection & status isConnected, virtualTime, now, marketStatus, serverMode, marketStatusLabel, dataSourceLabel, runtimeSummaryLabel, isUpdating, // Handlers onManualTrigger, onOpenRuntimeLogs, onRuntimeSettingsToggle, // Runtime settings panel props isRuntimeSettingsOpen, isRuntimeConfigSaving, isWatchlistSaving, runtimeConfigFeedback, watchlistFeedback, launchModeDraft, restoreRunIdDraft, runtimeHistoryRuns, scheduleModeDraft, intervalMinutesDraft, triggerTimeDraft, maxCommCyclesDraft, initialCashDraft, marginRequirementDraft, enableMemoryDraft, modeDraft, pollIntervalDraft, startDateDraft, endDateDraft, watchlistDraftSymbols, watchlistInputValue, watchlistSuggestions, onLaunchModeChange, onRestoreRunIdChange, onScheduleModeChange, onIntervalMinutesChange, onTriggerTimeChange, onMaxCommCyclesChange, onInitialCashChange, onMarginRequirementChange, onEnableMemoryChange, onModeChange, onPollIntervalChange, onStartDateChange, onEndDateChange, onWatchlistInputChange, onWatchlistInputKeyDown, onWatchlistAdd, onWatchlistRemove, onWatchlistRestoreCurrent, onWatchlistRestoreDefault, onWatchlistSuggestionClick, onLaunchConfigSave, onRestoreDefaults, // Ticker and portfolio data displayTickers, portfolioData, rollingTickers, // Feed data feed, bubbles, bubbleFor, leaderboard, // Views data currentView, chartTab, holdings, trades, stats, priceHistoryByTicker, ohlcHistoryByTicker, selectedExplainSymbol, onSelectedExplainSymbolChange, historySourceByTicker, explainEventsByTicker, newsByTicker, insiderTradesByTicker, technicalIndicatorsByTicker, currentDate, // Stock request handlers stockRequests, // Agent request handlers agentRequests, agentProfilesByAgent, // Layout leftWidth, isResizing, onMouseDown, agentFeedRef }) { const containerRef = useRef(null); const { setIsRuntimeSettingsOpen, setIsWatchlistPanelOpen } = useRuntimeStore(); const { setChartTab, setCurrentView, setIsResizing, setLeftWidth } = useUIStore(); // Resize handler useEffect(() => { if (!isResizing) return; const handleMouseMove = (e) => { if (!containerRef.current) return; const containerRect = containerRef.current.getBoundingClientRect(); const newLeftWidth = ((e.clientX - containerRect.left) / containerRect.width) * 100; if (newLeftWidth >= 30 && newLeftWidth <= 85) { setLeftWidth(newLeftWidth); } }; const handleMouseUp = () => setIsResizing(false); document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; }, [isResizing, setIsResizing, setLeftWidth]); const handleJumpToMessage = (bubble) => { if (agentFeedRef.current && agentFeedRef.current.scrollToMessage) { agentFeedRef.current.scrollToMessage(bubble); } }; const viewClassName = useMemo(() => { const base = `view-slider-five ${currentView === 'traders' ? 'show-traders' : currentView === 'room' ? 'show-room' : currentView === 'explain' ? 'show-explain' : currentView === 'chart' ? 'show-chart' : currentView === 'statistics' ? 'show-statistics' : 'show-openclaw'}`; return base; }, [currentView]); return (
{/* Header */}
{/* Unified Status Indicator */}
{isConnected ? (isUpdating ? '同步中' : '在线') : '离线'} {marketStatus && ( <> · {marketStatusLabel} )} {dataSourceLabel && ( <> · {dataSourceLabel} )} {runtimeSummaryLabel && ( <> · {runtimeSummaryLabel} )} · {now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })}
{serverMode !== 'backtest' && (
{onOpenRuntimeLogs && ( )}
)} setIsRuntimeSettingsOpen(false)} onLaunchModeChange={onLaunchModeChange} onRestoreRunIdChange={onRestoreRunIdChange} onScheduleModeChange={onScheduleModeChange} onIntervalMinutesChange={onIntervalMinutesChange} onTriggerTimeChange={onTriggerTimeChange} onMaxCommCyclesChange={onMaxCommCyclesChange} onInitialCashChange={onInitialCashChange} onMarginRequirementChange={onMarginRequirementChange} onEnableMemoryChange={onEnableMemoryChange} onModeChange={onModeChange} onPollIntervalChange={onPollIntervalChange} onStartDateChange={onStartDateChange} onEndDateChange={onEndDateChange} onWatchlistInputChange={onWatchlistInputChange} onWatchlistInputKeyDown={onWatchlistInputKeyDown} onWatchlistAdd={onWatchlistAdd} onWatchlistRemove={onWatchlistRemove} onWatchlistRestoreCurrent={onWatchlistRestoreCurrent} onWatchlistRestoreDefault={onWatchlistRestoreDefault} onWatchlistSuggestionClick={onWatchlistSuggestionClick} onSave={onLaunchConfigSave} onRestoreDefaults={onRestoreDefaults} />
{/* Main Content */} <> {/* Ticker Bar */}
{[0, 1].map((groupIdx) => (
{displayTickers.map(ticker => (
{ticker.symbol} {ticker.price !== null && ticker.price !== undefined ? `$${formatTickerPrice(ticker.price)}` : '-'} = 0 ? 'positive' : 'negative' }`}> {ticker.change !== null && ticker.change !== undefined ? `${ticker.change >= 0 ? '+' : ''}${ticker.change.toFixed(2)}%` : '-'}
))}
))}
投资组合 ${formatNumber(portfolioData.netValue)}
{/* Left Panel */}
{/* Traders View */}
}>
{/* Room View Panel */}
}> setIsRuntimeSettingsOpen(true)} />
{/* Stock Explain View Panel */}
}>
{/* Chart View Panel */}
{currentView === 'chart' ? ( ) : (
)}
{/* Statistics View Panel */}
}>
{/* OpenClaw View Panel */}
}>
{/* Resizer */}
{/* Right Panel: Agent Feed */}
}>
); }