/** * @license * SPDX-License-Identifier: Apache-2.0 */ import { useEffect, useMemo, useRef, useState } from 'react'; import { Monitor, Users, TrendingUp, Library, Wallet, Brain, FileText, LifeBuoy, Bell, CreditCard, User, Search, ChevronUp, Cpu, BarChart3, Globe, Newspaper, Zap, Activity, Calendar, LineChart, } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; import { CrosshairMode, ColorType, LineStyle, createChart, type IChartApi, type IPriceLine, type ISeriesApi, type SeriesMarker, type Time, } from 'lightweight-charts'; import { api, SPECIALIST_DISPLAY, statusFromSignal, useStockerEnv, type CouncilDecision, type EnvironmentState, type MarketObservation, type OhlcvBar, type OhlcvResponse, type Side, type StockerEnv, type TrainingMetrics, } from './api'; type Tab = 'Terminal' | 'Council' | 'Training' | 'Gallery' | 'Portfolio' | 'Intelligence'; // --------------------------------------------------------------------- atoms const SidebarItem = ({ icon: Icon, label, active, onClick, }: { icon: any; label: string; active: boolean; onClick: () => void; }) => ( ); const AgentCard = ({ title, icon: Icon, stat, color, children, active, }: { title: string; icon: any; stat?: string; color: string; children: any; active?: boolean; }) => (

{title}

{stat && {stat}}
{children}
); // ---------------------------------------------------------------------- chart export type ChartRange = '1M' | '3M' | '6M' | 'All'; const RANGE_BARS: Record, number> = { '1M': 22, '3M': 66, '6M': 132, }; const CandlestickChart = ({ bars, currentDate, currentPrice, range, }: { bars: OhlcvBar[]; currentDate?: string | null; currentPrice?: number | null; range?: ChartRange; }) => { const containerRef = useRef(null); const chartRef = useRef(null); const candleRef = useRef | null>(null); const volumeRef = useRef | null>(null); const priceLineRef = useRef(null); // mount once useEffect(() => { if (!containerRef.current) return; const chart = createChart(containerRef.current, { layout: { background: { type: ColorType.Solid, color: 'transparent' }, textColor: '#a3a3a3', fontFamily: 'JetBrains Mono, ui-monospace, monospace', }, grid: { vertLines: { color: 'rgba(255,255,255,0.04)' }, horzLines: { color: 'rgba(255,255,255,0.04)' }, }, timeScale: { borderVisible: false, timeVisible: false, secondsVisible: false, }, rightPriceScale: { borderVisible: false, scaleMargins: { top: 0.05, bottom: 0.28 }, }, crosshair: { mode: CrosshairMode.Normal, vertLine: { color: 'rgba(0,255,65,0.3)', width: 1, style: 3, labelBackgroundColor: '#00FF41' }, horzLine: { color: 'rgba(0,255,65,0.3)', width: 1, style: 3, labelBackgroundColor: '#00FF41' }, }, autoSize: false, }); chartRef.current = chart; candleRef.current = chart.addCandlestickSeries({ upColor: '#00FF41', downColor: '#ffb4ab', wickUpColor: '#00FF41', wickDownColor: '#ffb4ab', borderVisible: false, }); volumeRef.current = chart.addHistogramSeries({ priceFormat: { type: 'volume' }, priceScaleId: '', color: 'rgba(0,255,65,0.3)', }); volumeRef.current.priceScale().applyOptions({ scaleMargins: { top: 0.78, bottom: 0 }, }); const ro = new ResizeObserver((entries) => { const rect = entries[0]?.contentRect; if (rect) chart.applyOptions({ width: Math.floor(rect.width), height: Math.floor(rect.height) }); }); ro.observe(containerRef.current); return () => { ro.disconnect(); chart.remove(); chartRef.current = null; candleRef.current = null; volumeRef.current = null; priceLineRef.current = null; }; }, []); // push data when bars change useEffect(() => { if (!candleRef.current || !volumeRef.current) return; if (!bars || bars.length === 0) { candleRef.current.setData([]); volumeRef.current.setData([]); return; } candleRef.current.setData( bars.map((b) => ({ time: b.time, open: b.open, high: b.high, low: b.low, close: b.close, })), ); volumeRef.current.setData( bars.map((b) => ({ time: b.time, value: b.volume, color: b.close >= b.open ? 'rgba(0,255,65,0.35)' : 'rgba(255,180,171,0.35)', })), ); }, [bars]); // apply range filter (or fitContent for All) useEffect(() => { const chart = chartRef.current; if (!chart || !bars || bars.length === 0) return; const r = range ?? 'All'; if (r === 'All') { chart.timeScale().fitContent(); return; } const n = RANGE_BARS[r]; const fromIdx = Math.max(0, bars.length - n); chart.timeScale().setVisibleRange({ from: bars[fromIdx].time as Time, to: bars[bars.length - 1].time as Time, }); }, [bars, range]); // current-step marker on the candle series useEffect(() => { const series = candleRef.current; if (!series) return; if (currentDate) { const markers: SeriesMarker