Refine runtime data flow and UI layering

This commit is contained in:
2026-03-24 15:00:35 +08:00
parent c5eaf2b5ad
commit 6413edf8c9
17 changed files with 373 additions and 114 deletions

View File

@@ -57,7 +57,7 @@ export default function AgentCard({ agent, onClose, isClosing }) {
background: '#ffffff',
borderBottom: '2px solid #000000',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
zIndex: 1000,
zIndex: 800,
animation: isClosing ? 'slideUp 0.2s ease-out forwards' : 'slideDown 0.25s ease-out'
}}>
{/* Horizontal scrollable content */}

View File

@@ -455,6 +455,9 @@ export default function AppShell({
newsSnapshot={newsByTicker[selectedExplainSymbol] || null}
insiderTradesSnapshot={insiderTradesByTicker[selectedExplainSymbol] || null}
technicalIndicatorsSnapshot={technicalIndicatorsByTicker[selectedExplainSymbol] || null}
onRequestHistory={stockRequests?.requestStockHistory}
onRequestExplainEvents={stockRequests?.requestStockExplainEvents}
onRequestNews={stockRequests?.requestStockNews}
onRequestRangeExplain={stockRequests?.requestStockRangeExplain}
onRequestNewsForDate={stockRequests?.requestStockNewsForDate}
onRequestStory={stockRequests?.requestStockStory}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
export default function RuntimeLogsModal({
@@ -9,6 +9,32 @@ export default function RuntimeLogsModal({
onClose,
onRefresh
}) {
const logRef = useRef(null);
const [autoRefresh, setAutoRefresh] = useState(true);
const [followTail, setFollowTail] = useState(true);
const refreshIntervalMs = useMemo(() => 2000, []);
useEffect(() => {
if (!isOpen || !autoRefresh) {
return undefined;
}
const timerId = window.setInterval(() => {
onRefresh();
}, refreshIntervalMs);
return () => window.clearInterval(timerId);
}, [autoRefresh, isOpen, onRefresh, refreshIntervalMs]);
useEffect(() => {
if (!isOpen || !followTail || !logRef.current) {
return;
}
logRef.current.scrollTop = logRef.current.scrollHeight;
}, [followTail, isOpen, logPayload?.content]);
if (!isOpen) {
return null;
}
@@ -108,8 +134,35 @@ export default function RuntimeLogsModal({
) : null}
</div>
<div style={{
padding: '0 20px 12px',
display: 'flex',
gap: 16,
alignItems: 'center',
flexWrap: 'wrap'
}}>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 11, color: '#374151', cursor: 'pointer' }}>
<input
type="checkbox"
checked={autoRefresh}
onChange={(event) => setAutoRefresh(event.target.checked)}
/>
实时刷新
</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 11, color: '#374151', cursor: 'pointer' }}>
<input
type="checkbox"
checked={followTail}
onChange={(event) => setFollowTail(event.target.checked)}
/>
自动滚底
</label>
</div>
<div style={{ padding: '0 20px 20px', minHeight: 0 }}>
<pre style={{
<pre
ref={logRef}
style={{
margin: 0,
height: '100%',
minHeight: 320,
@@ -125,7 +178,8 @@ export default function RuntimeLogsModal({
fontFamily: '"SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word'
}}>
}}
>
{logPayload?.content || '暂无日志输出'}
</pre>
</div>

View File

@@ -33,6 +33,9 @@ export default function StockExplainView({
insiderTradesSnapshot,
technicalIndicatorsSnapshot,
onRequestRangeExplain,
onRequestHistory,
onRequestExplainEvents,
onRequestNews,
onRequestNewsForDate,
onRequestStory,
onRequestInsiderTrades,
@@ -142,6 +145,32 @@ export default function StockExplainView({
setActiveNewsSentiment('all');
}, [selectedSymbol, selectedEventDate]);
useEffect(() => {
if (!selectedSymbol) {
return;
}
if (onRequestHistory && (!Array.isArray(ohlcHistoryByTicker?.[selectedSymbol]) || ohlcHistoryByTicker[selectedSymbol].length === 0)) {
onRequestHistory(selectedSymbol);
}
if (onRequestExplainEvents && !explainEventsSnapshot) {
onRequestExplainEvents(selectedSymbol);
}
if (onRequestNews && (!Array.isArray(newsSnapshot?.items) || newsSnapshot.items.length === 0)) {
onRequestNews(selectedSymbol);
}
}, [
explainEventsSnapshot,
newsSnapshot,
ohlcHistoryByTicker,
onRequestExplainEvents,
onRequestHistory,
onRequestNews,
selectedSymbol,
]);
useEffect(() => {
if (!selectedSymbol || !selectedEventDate || !onRequestNewsForDate) {
return;

View File

@@ -578,7 +578,7 @@ export default function GlobalStyles() {
left: 0;
right: 0;
bottom: 0;
z-index: 999;
z-index: 700;
}
.room-scene-wrapper {
@@ -680,7 +680,7 @@ export default function GlobalStyles() {
line-height: 1.5;
animation: bubbleAppear 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
overflow: hidden;
z-index: 30;
z-index: 1500;
}
@keyframes bubbleAppear {
@@ -713,7 +713,7 @@ export default function GlobalStyles() {
right: 8px;
display: flex;
gap: 4px;
z-index: 10;
z-index: 1510;
}
.bubble-jump-btn,