jashdoshi77 commited on
Commit
e85ce30
·
1 Parent(s): 0366c3e

added dark mode

Browse files
frontend/src/App.tsx CHANGED
@@ -29,7 +29,7 @@ function AppLayout({ children }: { children: React.ReactNode }) {
29
  return (
30
  <div className="app-layout">
31
  <Sidebar onExpandChange={setSidebarExpanded} />
32
- <main className="app-main" style={{ marginLeft: sidebarExpanded ? 260 : 68 }}>
33
  {children}
34
  </main>
35
  </div>
 
29
  return (
30
  <div className="app-layout">
31
  <Sidebar onExpandChange={setSidebarExpanded} />
32
+ <main className="app-main" style={{ marginLeft: sidebarExpanded ? 260 : 68, transition: 'margin-left 0.25s cubic-bezier(0.4, 0, 0.2, 1)' }}>
33
  {children}
34
  </main>
35
  </div>
frontend/src/components/Sidebar.tsx CHANGED
@@ -12,6 +12,9 @@ export default function Sidebar({ onExpandChange }: SidebarProps) {
12
  const [drawerOpen, setDrawerOpen] = useState(false);
13
  const [isMobile, setIsMobile] = useState(false);
14
  const [isExpanded, setIsExpanded] = useState(false);
 
 
 
15
 
16
  useEffect(() => {
17
  const check = () => setIsMobile(window.innerWidth <= 768);
@@ -20,6 +23,12 @@ export default function Sidebar({ onExpandChange }: SidebarProps) {
20
  return () => window.removeEventListener('resize', check);
21
  }, []);
22
 
 
 
 
 
 
 
23
  // Close drawer on route change
24
  useEffect(() => { setDrawerOpen(false); }, [location.pathname]);
25
 
@@ -29,6 +38,14 @@ export default function Sidebar({ onExpandChange }: SidebarProps) {
29
  navigate('/login');
30
  };
31
 
 
 
 
 
 
 
 
 
32
  const allLinks = [
33
  {
34
  section: 'Overview',
@@ -119,6 +136,10 @@ export default function Sidebar({ onExpandChange }: SidebarProps) {
119
  </div>
120
  </div>
121
  )}
 
 
 
 
122
  <button className="sidebar-link sidebar-logout" onClick={handleLogout}>
123
  <span className="sidebar-icon">
124
  <svg viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z" clipRule="evenodd"/></svg>
@@ -192,6 +213,10 @@ export default function Sidebar({ onExpandChange }: SidebarProps) {
192
  </div>
193
  </div>
194
  )}
 
 
 
 
195
  <button className="sidebar-link sidebar-logout" onClick={handleLogout} style={{ width: '100%' }}>
196
  <span className="sidebar-icon">
197
  <svg viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z" clipRule="evenodd"/></svg>
 
12
  const [drawerOpen, setDrawerOpen] = useState(false);
13
  const [isMobile, setIsMobile] = useState(false);
14
  const [isExpanded, setIsExpanded] = useState(false);
15
+ const [isDark, setIsDark] = useState(() => {
16
+ return localStorage.getItem('qh_theme') === 'dark' || document.documentElement.getAttribute('data-theme') === 'dark';
17
+ });
18
 
19
  useEffect(() => {
20
  const check = () => setIsMobile(window.innerWidth <= 768);
 
23
  return () => window.removeEventListener('resize', check);
24
  }, []);
25
 
26
+ // Apply theme on mount and when toggled
27
+ useEffect(() => {
28
+ document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
29
+ localStorage.setItem('qh_theme', isDark ? 'dark' : 'light');
30
+ }, [isDark]);
31
+
32
  // Close drawer on route change
33
  useEffect(() => { setDrawerOpen(false); }, [location.pathname]);
34
 
 
38
  navigate('/login');
39
  };
40
 
41
+ const toggleTheme = () => setIsDark(prev => !prev);
42
+
43
+ const themeIcon = isDark ? (
44
+ <svg viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clipRule="evenodd"/></svg>
45
+ ) : (
46
+ <svg viewBox="0 0 20 20" fill="currentColor"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"/></svg>
47
+ );
48
+
49
  const allLinks = [
50
  {
51
  section: 'Overview',
 
136
  </div>
137
  </div>
138
  )}
139
+ <button className="sidebar-link" onClick={toggleTheme}>
140
+ <span className="sidebar-icon">{themeIcon}</span>
141
+ <span className="sidebar-label">{isDark ? 'Light Mode' : 'Dark Mode'}</span>
142
+ </button>
143
  <button className="sidebar-link sidebar-logout" onClick={handleLogout}>
144
  <span className="sidebar-icon">
145
  <svg viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z" clipRule="evenodd"/></svg>
 
213
  </div>
214
  </div>
215
  )}
216
+ <button className="sidebar-link" onClick={toggleTheme} style={{ width: '100%' }}>
217
+ <span className="sidebar-icon">{themeIcon}</span>
218
+ <span className="sidebar-label">{isDark ? 'Light Mode' : 'Dark Mode'}</span>
219
+ </button>
220
  <button className="sidebar-link sidebar-logout" onClick={handleLogout} style={{ width: '100%' }}>
221
  <span className="sidebar-icon">
222
  <svg viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z" clipRule="evenodd"/></svg>
frontend/src/index.css CHANGED
@@ -60,8 +60,102 @@
60
  --transition-fast: 150ms ease;
61
  --transition-base: 250ms ease;
62
  --transition-slow: 400ms ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  /* ── Reset ───────────────────────────────────────────────────────────── */
66
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
67
 
@@ -464,7 +558,7 @@ tr:hover td { background: var(--bg-hover); }
464
  }
465
  .badge-primary { background: var(--accent-lighter); color: var(--accent); }
466
  .badge-emerald { background: #e6f9f0; color: var(--green-positive); }
467
- .badge-rose { background: #fef2f2; color: var(--red-negative); }
468
  .badge-amber { background: #fffbeb; color: var(--amber-neutral); }
469
 
470
  /* ── Tabs ────────────────────────────────────────────────────────────── */
 
60
  --transition-fast: 150ms ease;
61
  --transition-base: 250ms ease;
62
  --transition-slow: 400ms ease;
63
+
64
+ /* Chart-specific variables */
65
+ --chart-axis: #8892a4;
66
+ --chart-grid: rgba(148, 163, 184, 0.08);
67
+ --chart-tooltip-bg: #ffffff;
68
+ --chart-tooltip-border: #e2e5ea;
69
+ --chart-bar-bg: #f1f5f9;
70
+ --chart-green: #00b386;
71
+ --chart-green-fill: rgba(0, 179, 134, 0.15);
72
+ --error-bg: #fef2f2;
73
+ --error-border: #fecaca;
74
+ }
75
+
76
+ /* ═══════════════════════════════════════════════════════════════════════
77
+ Dark Mode Theme — True Black
78
+ ═══════════════════════════════════════════════════════════════════════ */
79
+ [data-theme="dark"] {
80
+ --bg-primary: #0a0a0a;
81
+ --bg-secondary: #141414;
82
+ --bg-tertiary: #1c1c1c;
83
+ --bg-card: #111111;
84
+ --bg-glass: rgba(17, 17, 17, 0.92);
85
+ --bg-hover: rgba(0, 125, 99, 0.1);
86
+
87
+ --text-primary: #e8e8e8;
88
+ --text-secondary: #a0a8b8;
89
+ --text-muted: #6b7588;
90
+ --text-accent: #00c896;
91
+ --text-on-dark: #f0f4f2;
92
+ --text-on-dark-muted: #a3b8b0;
93
+
94
+ --accent: #00a87a;
95
+ --accent-light: #00c896;
96
+ --accent-lighter: rgba(0, 168, 122, 0.12);
97
+ --accent-dark: #008f68;
98
+ --accent-gold: #d4a843;
99
+ --accent-gold-light: #e6c470;
100
+ --green-positive: #10b981;
101
+ --red-negative: #ef4444;
102
+ --amber-neutral: #d4a843;
103
+ --blue-info: #3b82f6;
104
+
105
+ --gradient-accent: linear-gradient(135deg, #00a87a, #00c896);
106
+ --gradient-card-hover: linear-gradient(145deg, rgba(0,168,122,0.04), rgba(0,168,122,0.08));
107
+
108
+ --border-color: #1f2228;
109
+ --border-subtle: #181b20;
110
+ --border-accent: rgba(0, 168, 122, 0.25);
111
+
112
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
113
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
114
+ --shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.5);
115
+ --shadow-card: 0 1px 3px rgba(0,0,0,0.2), 0 1px 2px rgba(0,0,0,0.15);
116
+ --shadow-card-hover: 0 10px 40px rgba(0, 0, 0, 0.35);
117
+
118
+ --chart-axis: #6b7588;
119
+ --chart-grid: rgba(100, 116, 139, 0.1);
120
+ --chart-tooltip-bg: #1c1c1c;
121
+ --chart-tooltip-border: #2a2d32;
122
+ --chart-bar-bg: #1c1c1c;
123
+ --chart-green: #00d09c;
124
+ --chart-green-fill: rgba(0, 208, 156, 0.2);
125
+ --error-bg: rgba(239, 68, 68, 0.1);
126
+ --error-border: rgba(239, 68, 68, 0.25);
127
  }
128
 
129
+ /* Dark mode body transition */
130
+ body {
131
+ transition: background-color 0.3s ease, color 0.3s ease;
132
+ }
133
+
134
+ /* Dark mode: badge overrides */
135
+ [data-theme="dark"] .badge-primary { background: rgba(0,168,122,0.15); color: var(--accent); }
136
+ [data-theme="dark"] .badge-emerald { background: rgba(16,185,129,0.15); color: var(--green-positive); }
137
+ [data-theme="dark"] .badge-rose { background: rgba(239,68,68,0.15); color: var(--red-negative); }
138
+ [data-theme="dark"] .badge-amber { background: rgba(212,168,67,0.15); color: var(--amber-neutral); }
139
+
140
+ /* Dark mode: table header */
141
+ [data-theme="dark"] th { background: var(--bg-tertiary); }
142
+
143
+ /* Dark mode: scrollbar */
144
+ [data-theme="dark"] ::-webkit-scrollbar-track { background: var(--bg-secondary); }
145
+ [data-theme="dark"] ::-webkit-scrollbar-thumb { background: #2a2d32; }
146
+ [data-theme="dark"] ::-webkit-scrollbar-thumb:hover { background: var(--accent); }
147
+
148
+ /* Dark mode: selection */
149
+ [data-theme="dark"] ::selection { background: rgba(0, 168, 122, 0.25); color: var(--text-primary); }
150
+
151
+ /* Dark mode: React Flow (Strategy Builder) */
152
+ [data-theme="dark"] .react-flow__controls { background: var(--bg-tertiary); border-color: var(--border-color); border-radius: var(--radius-md); }
153
+ [data-theme="dark"] .react-flow__controls-button { background: var(--bg-card); border-color: var(--border-color); fill: var(--text-secondary); }
154
+ [data-theme="dark"] .react-flow__controls-button:hover { background: var(--bg-hover); }
155
+ [data-theme="dark"] .react-flow__minimap { background: var(--bg-tertiary) !important; }
156
+ [data-theme="dark"] .react-flow__attribution { background: transparent !important; }
157
+ [data-theme="dark"] .react-flow__attribution a { color: var(--text-muted) !important; }
158
+
159
  /* ── Reset ───────────────────────────────────────────────────────────── */
160
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
161
 
 
558
  }
559
  .badge-primary { background: var(--accent-lighter); color: var(--accent); }
560
  .badge-emerald { background: #e6f9f0; color: var(--green-positive); }
561
+ .badge-rose { background: var(--error-bg); color: var(--red-negative); }
562
  .badge-amber { background: #fffbeb; color: var(--amber-neutral); }
563
 
564
  /* ── Tabs ────────────────────────────────────────────────────────────── */
frontend/src/pages/BacktestResults.tsx CHANGED
@@ -66,11 +66,11 @@ export default function BacktestResults() {
66
  <div className="card-header"><h3>Equity Curve</h3></div>
67
  <div className="chart-container" style={{height:260,padding:'0.5rem'}}>
68
  <ResponsiveContainer><AreaChart data={selectedBt.equity_curve||[]}>
69
- <defs><linearGradient id="eqG" x1="0" y1="0" x2="0" y2="1"><stop offset="5%" stopColor="#005241" stopOpacity={0.15}/><stop offset="95%" stopColor="#005241" stopOpacity={0}/></linearGradient></defs>
70
- <XAxis dataKey="date" tick={{fontSize:9,fill:'#8892a4'}} tickFormatter={v=>v?.slice(5)}/>
71
- <YAxis tick={{fontSize:9,fill:'#8892a4'}} tickFormatter={v=>`$${(v/1e6).toFixed(1)}M`}/>
72
- <Tooltip contentStyle={{background:'#fff',border:'1px solid #e2e5ea',borderRadius:8,fontSize:'0.8rem',boxShadow:'0 4px 12px rgba(0,0,0,0.08)'}}/>
73
- <Area type="monotone" dataKey="portfolio_value" stroke="#005241" fill="url(#eqG)" strokeWidth={2} dot={false}/>
74
  </AreaChart></ResponsiveContainer>
75
  </div>
76
  </div>
 
66
  <div className="card-header"><h3>Equity Curve</h3></div>
67
  <div className="chart-container" style={{height:260,padding:'0.5rem'}}>
68
  <ResponsiveContainer><AreaChart data={selectedBt.equity_curve||[]}>
69
+ <defs><linearGradient id="eqG" x1="0" y1="0" x2="0" y2="1"><stop offset="5%" stopColor="var(--chart-green)" stopOpacity={0.18}/><stop offset="95%" stopColor="var(--chart-green)" stopOpacity={0}/></linearGradient></defs>
70
+ <XAxis dataKey="date" tick={{fontSize:9,fill:'var(--chart-axis)'}} tickFormatter={v=>v?.slice(5)}/>
71
+ <YAxis tick={{fontSize:9,fill:'var(--chart-axis)'}} tickFormatter={v=>`$${(v/1e6).toFixed(1)}M`}/>
72
+ <Tooltip contentStyle={{background:'var(--chart-tooltip-bg)',border:'1px solid var(--chart-tooltip-border)',borderRadius:8,fontSize:'0.8rem',boxShadow:'var(--shadow-md)'}}/>
73
+ <Area type="monotone" dataKey="portfolio_value" stroke="var(--chart-green)" fill="url(#eqG)" strokeWidth={2} dot={false}/>
74
  </AreaChart></ResponsiveContainer>
75
  </div>
76
  </div>
frontend/src/pages/Dashboard.tsx CHANGED
@@ -211,18 +211,18 @@ export default function Dashboard() {
211
  <AreaChart data={marketData}>
212
  <defs>
213
  <linearGradient id="colorPrice" x1="0" y1="0" x2="0" y2="1">
214
- <stop offset="5%" stopColor="#005241" stopOpacity={0.12} />
215
- <stop offset="95%" stopColor="#005241" stopOpacity={0} />
216
  </linearGradient>
217
  </defs>
218
- <CartesianGrid strokeDasharray="3 3" stroke="rgba(148,163,184,0.08)" />
219
- <XAxis dataKey="date" tick={{fontSize:10,fill:'#8892a4'}} tickFormatter={v => v?.slice(5)} />
220
- <YAxis tick={{fontSize:10,fill:'#8892a4'}} domain={['auto','auto']} tickFormatter={v => v?.toLocaleString()} />
221
  <Tooltip
222
  contentStyle={{background:'var(--bg-card)',border:'1px solid var(--border-color)',borderRadius:8,fontSize:'0.8rem',boxShadow:'var(--shadow-md)'}}
223
  formatter={(v: any) => [Number(v).toLocaleString('en-US', {minimumFractionDigits:2}), 'Price']}
224
  />
225
- <Area type="monotone" dataKey="price" stroke="#005241" fill="url(#colorPrice)" strokeWidth={1.5} dot={false} />
226
  </AreaChart>
227
  </ResponsiveContainer>
228
  </div>
 
211
  <AreaChart data={marketData}>
212
  <defs>
213
  <linearGradient id="colorPrice" x1="0" y1="0" x2="0" y2="1">
214
+ <stop offset="5%" stopColor="var(--chart-green)" stopOpacity={0.15} />
215
+ <stop offset="95%" stopColor="var(--chart-green)" stopOpacity={0} />
216
  </linearGradient>
217
  </defs>
218
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--chart-grid)" />
219
+ <XAxis dataKey="date" tick={{fontSize:10,fill:'var(--chart-axis)'}} tickFormatter={v => v?.slice(5)} />
220
+ <YAxis tick={{fontSize:10,fill:'var(--chart-axis)'}} domain={['auto','auto']} tickFormatter={v => v?.toLocaleString()} />
221
  <Tooltip
222
  contentStyle={{background:'var(--bg-card)',border:'1px solid var(--border-color)',borderRadius:8,fontSize:'0.8rem',boxShadow:'var(--shadow-md)'}}
223
  formatter={(v: any) => [Number(v).toLocaleString('en-US', {minimumFractionDigits:2}), 'Price']}
224
  />
225
+ <Area type="monotone" dataKey="price" stroke="var(--chart-green)" fill="url(#colorPrice)" strokeWidth={2} dot={false} />
226
  </AreaChart>
227
  </ResponsiveContainer>
228
  </div>
frontend/src/pages/FactorAnalysis.tsx CHANGED
@@ -137,13 +137,13 @@ export default function FactorAnalysis() {
137
  <div style={{height:300}}>
138
  <ResponsiveContainer>
139
  <RadarChart data={getRadarData()}>
140
- <PolarGrid stroke="rgba(148,163,184,0.1)" />
141
- <PolarAngleAxis dataKey="factor" tick={{fontSize:11,fill:'#94a3b8'}} />
142
- <PolarRadiusAxis tick={{fontSize:9,fill:'#64748b'}} />
143
  {results.tickers?.slice(0, 4).map((t: string, i: number) => (
144
  <Radar key={t} name={t} dataKey={t} stroke={['#6366f1','#10b981','#f59e0b','#f43f5e'][i]} fill={['#6366f1','#10b981','#f59e0b','#f43f5e'][i]} fillOpacity={0.1} strokeWidth={2} />
145
  ))}
146
- <Tooltip contentStyle={{background:'#fff',border:'1px solid #e2e5ea',borderRadius:8,fontSize:'0.8rem',boxShadow:'0 4px 12px rgba(0,0,0,0.08)'}} />
147
  </RadarChart>
148
  </ResponsiveContainer>
149
  </div>
@@ -256,10 +256,10 @@ export default function FactorAnalysis() {
256
  <div style={{height:220}}>
257
  <ResponsiveContainer>
258
  <BarChart data={prediction.top_features} layout="vertical" margin={{left:100,right:20,top:5,bottom:5}}>
259
- <CartesianGrid strokeDasharray="3 3" stroke="rgba(148,163,184,0.1)" />
260
- <XAxis type="number" tick={{fontSize:10,fill:'#94a3b8'}} />
261
- <YAxis type="category" dataKey="name" tick={{fontSize:10,fill:'#64748b'}} width={95} />
262
- <Tooltip contentStyle={{background:'#fff',border:'1px solid #e2e5ea',borderRadius:8,fontSize:'0.8rem'}} />
263
  <Bar dataKey="importance" radius={[0,4,4,0]}>
264
  {prediction.top_features?.map((_: any, i: number) => (
265
  <Cell key={i} fill={['#6366f1','#818cf8','#a5b4fc','#c7d2fe','#e0e7ff','#eef2ff','#f0f0ff','#f5f5ff','#fafafe','#fdfdfd'][i] || '#c7d2fe'} />
@@ -304,7 +304,7 @@ export default function FactorAnalysis() {
304
  {regime.regime_probabilities && Object.entries(regime.regime_probabilities).map(([label, prob]: [string, any]) => (
305
  <div key={label} style={{display:'flex',alignItems:'center',gap:'0.75rem'}}>
306
  <span style={{width:120,fontSize:'0.8rem',fontWeight:500,display:'flex',alignItems:'center',gap:'0.35rem'}}><RegimeIcon regime={label} size={12}/> {label.replace('_',' ')}</span>
307
- <div style={{flex:1,height:20,borderRadius:10,background:'#f1f5f9',overflow:'hidden'}}>
308
  <div style={{width:`${prob*100}%`,height:'100%',borderRadius:10,background:regimeColor(label),transition:'width 0.5s ease'}} />
309
  </div>
310
  <span style={{fontSize:'0.8rem',fontWeight:600,width:45,textAlign:'right'}}>{(prob*100).toFixed(1)}%</span>
 
137
  <div style={{height:300}}>
138
  <ResponsiveContainer>
139
  <RadarChart data={getRadarData()}>
140
+ <PolarGrid stroke="var(--chart-grid)" />
141
+ <PolarAngleAxis dataKey="factor" tick={{fontSize:11,fill:'var(--chart-axis)'}} />
142
+ <PolarRadiusAxis tick={{fontSize:9,fill:'var(--chart-axis)'}} />
143
  {results.tickers?.slice(0, 4).map((t: string, i: number) => (
144
  <Radar key={t} name={t} dataKey={t} stroke={['#6366f1','#10b981','#f59e0b','#f43f5e'][i]} fill={['#6366f1','#10b981','#f59e0b','#f43f5e'][i]} fillOpacity={0.1} strokeWidth={2} />
145
  ))}
146
+ <Tooltip contentStyle={{background:'var(--chart-tooltip-bg)',border:'1px solid var(--chart-tooltip-border)',borderRadius:8,fontSize:'0.8rem',boxShadow:'var(--shadow-md)'}} />
147
  </RadarChart>
148
  </ResponsiveContainer>
149
  </div>
 
256
  <div style={{height:220}}>
257
  <ResponsiveContainer>
258
  <BarChart data={prediction.top_features} layout="vertical" margin={{left:100,right:20,top:5,bottom:5}}>
259
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--chart-grid)" />
260
+ <XAxis type="number" tick={{fontSize:10,fill:'var(--chart-axis)'}} />
261
+ <YAxis type="category" dataKey="name" tick={{fontSize:10,fill:'var(--chart-axis)'}} width={95} />
262
+ <Tooltip contentStyle={{background:'var(--chart-tooltip-bg)',border:'1px solid var(--chart-tooltip-border)',borderRadius:8,fontSize:'0.8rem',boxShadow:'var(--shadow-md)'}} />
263
  <Bar dataKey="importance" radius={[0,4,4,0]}>
264
  {prediction.top_features?.map((_: any, i: number) => (
265
  <Cell key={i} fill={['#6366f1','#818cf8','#a5b4fc','#c7d2fe','#e0e7ff','#eef2ff','#f0f0ff','#f5f5ff','#fafafe','#fdfdfd'][i] || '#c7d2fe'} />
 
304
  {regime.regime_probabilities && Object.entries(regime.regime_probabilities).map(([label, prob]: [string, any]) => (
305
  <div key={label} style={{display:'flex',alignItems:'center',gap:'0.75rem'}}>
306
  <span style={{width:120,fontSize:'0.8rem',fontWeight:500,display:'flex',alignItems:'center',gap:'0.35rem'}}><RegimeIcon regime={label} size={12}/> {label.replace('_',' ')}</span>
307
+ <div style={{flex:1,height:20,borderRadius:10,background:'var(--chart-bar-bg)',overflow:'hidden'}}>
308
  <div style={{width:`${prob*100}%`,height:'100%',borderRadius:10,background:regimeColor(label),transition:'width 0.5s ease'}} />
309
  </div>
310
  <span style={{fontSize:'0.8rem',fontWeight:600,width:45,textAlign:'right'}}>{(prob*100).toFixed(1)}%</span>
frontend/src/pages/HoldingsTracker.tsx CHANGED
@@ -305,9 +305,9 @@ export default function HoldingsTracker() {
305
  <div style={{ width: '100%', height: 280 }}>
306
  <ResponsiveContainer>
307
  <BarChart data={holdings.map(h => ({ ticker: h.ticker, pnl: h.pnl, pnl_pct: h.pnl_pct }))} layout="vertical">
308
- <CartesianGrid strokeDasharray="3 3" stroke="rgba(148,163,184,0.08)" />
309
- <XAxis type="number" tick={{ fontSize: 10, fill: '#8892a4' }} tickFormatter={v => `$${Number(v).toLocaleString()}`} />
310
- <YAxis dataKey="ticker" type="category" tick={{ fontSize: 11, fill: '#64748b', fontWeight: 500 }} width={60} />
311
  <Tooltip formatter={(v: any) => `$${fmt(Number(v))}`} contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem' }} />
312
  <Bar dataKey="pnl" radius={[0, 4, 4, 0]}>
313
  {holdings.map((h, i) => <Cell key={i} fill={h.pnl >= 0 ? '#0a8f5c' : '#c23030'} />)}
@@ -338,9 +338,9 @@ export default function HoldingsTracker() {
338
  <div style={{ width: '100%', height: 350 }}>
339
  <ResponsiveContainer>
340
  <BarChart data={allScenarios} layout="vertical">
341
- <CartesianGrid strokeDasharray="3 3" stroke="rgba(148,163,184,0.08)" />
342
- <XAxis type="number" tick={{ fontSize: 10, fill:'#8892a4' }} tickFormatter={v => `${v.toFixed(0)}%`} />
343
- <YAxis dataKey="name" type="category" tick={{ fontSize: 10, fill:'#64748b' }} width={140} />
344
  <Tooltip formatter={(v: any) => `${Number(v).toFixed(2)}%`} contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem' }} />
345
  <Bar dataKey="total_impact_pct" radius={[0, 4, 4, 0]}>
346
  {allScenarios.map((s, i) => <Cell key={i} fill={s.total_impact_pct >= 0 ? '#0a8f5c' : '#c23030'} />)}
 
305
  <div style={{ width: '100%', height: 280 }}>
306
  <ResponsiveContainer>
307
  <BarChart data={holdings.map(h => ({ ticker: h.ticker, pnl: h.pnl, pnl_pct: h.pnl_pct }))} layout="vertical">
308
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--chart-grid)" />
309
+ <XAxis type="number" tick={{ fontSize: 10, fill: 'var(--chart-axis)' }} tickFormatter={v => `$${Number(v).toLocaleString()}`} />
310
+ <YAxis dataKey="ticker" type="category" tick={{ fontSize: 11, fill: 'var(--chart-axis)', fontWeight: 500 }} width={60} />
311
  <Tooltip formatter={(v: any) => `$${fmt(Number(v))}`} contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem' }} />
312
  <Bar dataKey="pnl" radius={[0, 4, 4, 0]}>
313
  {holdings.map((h, i) => <Cell key={i} fill={h.pnl >= 0 ? '#0a8f5c' : '#c23030'} />)}
 
338
  <div style={{ width: '100%', height: 350 }}>
339
  <ResponsiveContainer>
340
  <BarChart data={allScenarios} layout="vertical">
341
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--chart-grid)" />
342
+ <XAxis type="number" tick={{ fontSize: 10, fill:'var(--chart-axis)' }} tickFormatter={v => `${v.toFixed(0)}%`} />
343
+ <YAxis dataKey="name" type="category" tick={{ fontSize: 10, fill:'var(--chart-axis)' }} width={140} />
344
  <Tooltip formatter={(v: any) => `${Number(v).toFixed(2)}%`} contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem' }} />
345
  <Bar dataKey="total_impact_pct" radius={[0, 4, 4, 0]}>
346
  {allScenarios.map((s, i) => <Cell key={i} fill={s.total_impact_pct >= 0 ? '#0a8f5c' : '#c23030'} />)}
frontend/src/pages/Login.tsx CHANGED
@@ -42,7 +42,7 @@ export default function Login() {
42
  </div>
43
 
44
  {error && (
45
- <div style={{padding:'0.75rem 1rem',background:'#fef2f2',border:'1px solid #fecaca',borderRadius:'var(--radius-md)',color:'var(--red-negative)',fontSize:'0.85rem',marginBottom:'1.25rem'}}>
46
  {error}
47
  </div>
48
  )}
 
42
  </div>
43
 
44
  {error && (
45
+ <div style={{padding:'0.75rem 1rem',background:'var(--error-bg)',border:'1px solid var(--error-border)',borderRadius:'var(--radius-md)',color:'var(--red-negative)',fontSize:'0.85rem',marginBottom:'1.25rem'}}>
46
  {error}
47
  </div>
48
  )}
frontend/src/pages/MarketExplorer.tsx CHANGED
@@ -96,14 +96,14 @@ export default function MarketExplorer() {
96
  <AreaChart data={priceData}>
97
  <defs>
98
  <linearGradient id="colorClose" x1="0" y1="0" x2="0" y2="1">
99
- <stop offset="5%" stopColor="#005241" stopOpacity={0.15} />
100
- <stop offset="95%" stopColor="#005241" stopOpacity={0} />
101
  </linearGradient>
102
  </defs>
103
- <XAxis dataKey="date" tick={{fontSize:10,fill:'#8892a4'}} tickFormatter={v => v?.slice(5)} />
104
- <YAxis tick={{fontSize:10,fill:'#8892a4'}} domain={['auto','auto']} />
105
- <Tooltip contentStyle={{background:'#fff',border:'1px solid #e2e5ea',borderRadius:8,fontSize:'0.8rem',boxShadow:'0 4px 12px rgba(0,0,0,0.08)'}} />
106
- <Area type="monotone" dataKey="close" stroke="#005241" fill="url(#colorClose)" strokeWidth={2} dot={false} />
107
  </AreaChart>
108
  </ResponsiveContainer>
109
  </div>
 
96
  <AreaChart data={priceData}>
97
  <defs>
98
  <linearGradient id="colorClose" x1="0" y1="0" x2="0" y2="1">
99
+ <stop offset="5%" stopColor="var(--chart-green)" stopOpacity={0.18} />
100
+ <stop offset="95%" stopColor="var(--chart-green)" stopOpacity={0} />
101
  </linearGradient>
102
  </defs>
103
+ <XAxis dataKey="date" tick={{fontSize:10,fill:'var(--chart-axis)'}} tickFormatter={v => v?.slice(5)} />
104
+ <YAxis tick={{fontSize:10,fill:'var(--chart-axis)'}} domain={['auto','auto']} />
105
+ <Tooltip contentStyle={{background:'var(--chart-tooltip-bg)',border:'1px solid var(--chart-tooltip-border)',borderRadius:8,fontSize:'0.8rem',boxShadow:'var(--shadow-md)'}} />
106
+ <Area type="monotone" dataKey="close" stroke="var(--chart-green)" fill="url(#colorClose)" strokeWidth={2} dot={false} />
107
  </AreaChart>
108
  </ResponsiveContainer>
109
  </div>
frontend/src/pages/PortfolioAnalysis.tsx CHANGED
@@ -154,7 +154,7 @@ export default function PortfolioAnalysis() {
154
  strokeWidth={2} stroke="#fff" label={({name, value}) => `${name}: ${value}%`}>
155
  {pieData.map((_: any, i: number) => <Cell key={i} fill={COLORS[i % COLORS.length]} />)}
156
  </Pie>
157
- <Tooltip contentStyle={{background:'#fff',border:'1px solid #e2e5ea',borderRadius:8,fontSize:'0.8rem',boxShadow:'0 4px 12px rgba(0,0,0,0.08)'}} />
158
  </PieChart>
159
  </ResponsiveContainer>
160
  </div>
 
154
  strokeWidth={2} stroke="#fff" label={({name, value}) => `${name}: ${value}%`}>
155
  {pieData.map((_: any, i: number) => <Cell key={i} fill={COLORS[i % COLORS.length]} />)}
156
  </Pie>
157
+ <Tooltip contentStyle={{background:'var(--chart-tooltip-bg)',border:'1px solid var(--chart-tooltip-border)',borderRadius:8,fontSize:'0.8rem',boxShadow:'var(--shadow-md)'}} />
158
  </PieChart>
159
  </ResponsiveContainer>
160
  </div>
frontend/src/pages/Register.tsx CHANGED
@@ -62,7 +62,7 @@ export default function Register() {
62
  </div>
63
 
64
  {error && (
65
- <div style={{padding:'0.75rem 1rem',background:'#fef2f2',border:'1px solid #fecaca',borderRadius:'var(--radius-md)',color:'var(--red-negative)',fontSize:'0.85rem',marginBottom:'1.25rem'}}>
66
  {error}
67
  </div>
68
  )}
 
62
  </div>
63
 
64
  {error && (
65
+ <div style={{padding:'0.75rem 1rem',background:'var(--error-bg)',border:'1px solid var(--error-border)',borderRadius:'var(--radius-md)',color:'var(--red-negative)',fontSize:'0.85rem',marginBottom:'1.25rem'}}>
66
  {error}
67
  </div>
68
  )}
frontend/src/pages/Sentiment.tsx CHANGED
@@ -87,7 +87,7 @@ export default function Sentiment() {
87
  <div style={{height:130,display:'flex',alignItems:'center',justifyContent:'center'}}>
88
  <ResponsiveContainer width="100%" height="100%">
89
  <RadialBarChart cx="50%" cy="85%" innerRadius="65%" outerRadius="100%" startAngle={180} endAngle={0} data={gaugeData} barSize={12}>
90
- <RadialBar background={{ fill: '#f0f2f5' }} dataKey="value" cornerRadius={6} />
91
  </RadialBarChart>
92
  </ResponsiveContainer>
93
  </div>
@@ -108,9 +108,9 @@ export default function Sentiment() {
108
  <div style={{ width: '100%', height: 320 }}>
109
  <ResponsiveContainer>
110
  <BarChart data={marketMood.breakdown}>
111
- <CartesianGrid strokeDasharray="3 3" stroke="rgba(148,163,184,0.08)" />
112
- <XAxis dataKey="ticker" tick={{ fontSize: 11, fill: '#64748b', fontWeight: 500 }} />
113
- <YAxis tick={{ fontSize: 10, fill: '#8892a4' }} domain={[-1, 1]} />
114
  <Tooltip
115
  formatter={(v: any) => (Number(v)).toFixed(3)}
116
  contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem', boxShadow:'var(--shadow-md)' }}
@@ -231,7 +231,7 @@ export default function Sentiment() {
231
  ]}
232
  cx="50%" cy="50%" innerRadius={50} outerRadius={85} dataKey="value"
233
  label={({ name, percent }) => `${name} ${((percent ?? 0) * 100).toFixed(0)}%`}
234
- labelLine={{ stroke: '#8892a4', strokeWidth: 1 }}
235
  />
236
  <Tooltip contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem' }} />
237
  </PieChart>
 
87
  <div style={{height:130,display:'flex',alignItems:'center',justifyContent:'center'}}>
88
  <ResponsiveContainer width="100%" height="100%">
89
  <RadialBarChart cx="50%" cy="85%" innerRadius="65%" outerRadius="100%" startAngle={180} endAngle={0} data={gaugeData} barSize={12}>
90
+ <RadialBar background={{ fill: 'var(--bg-tertiary)' }} dataKey="value" cornerRadius={6} />
91
  </RadialBarChart>
92
  </ResponsiveContainer>
93
  </div>
 
108
  <div style={{ width: '100%', height: 320 }}>
109
  <ResponsiveContainer>
110
  <BarChart data={marketMood.breakdown}>
111
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--chart-grid)" />
112
+ <XAxis dataKey="ticker" tick={{ fontSize: 11, fill: 'var(--chart-axis)', fontWeight: 500 }} />
113
+ <YAxis tick={{ fontSize: 10, fill: 'var(--chart-axis)' }} domain={[-1, 1]} />
114
  <Tooltip
115
  formatter={(v: any) => (Number(v)).toFixed(3)}
116
  contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem', boxShadow:'var(--shadow-md)' }}
 
231
  ]}
232
  cx="50%" cy="50%" innerRadius={50} outerRadius={85} dataKey="value"
233
  label={({ name, percent }) => `${name} ${((percent ?? 0) * 100).toFixed(0)}%`}
234
+ labelLine={{ stroke: 'var(--chart-axis)', strokeWidth: 1 }}
235
  />
236
  <Tooltip contentStyle={{ background:'var(--bg-card)', border:'1px solid var(--border-color)', borderRadius:8, fontSize:'0.8rem' }} />
237
  </PieChart>
frontend/src/pages/StrategyBuilder.tsx CHANGED
@@ -35,7 +35,7 @@ export default function StrategyBuilder() {
35
  border: `2px solid ${nodeType.color}`,
36
  borderRadius: '12px',
37
  padding: '12px 20px',
38
- color: '#1a1a1a',
39
  fontSize: '13px',
40
  fontWeight: '600',
41
  minWidth: '160px',
@@ -104,11 +104,11 @@ export default function StrategyBuilder() {
104
  nodes={nodes} edges={edges}
105
  onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect}
106
  fitView
107
- style={{background:'#f8f9fa'}}
108
  >
109
- <Background color="#e2e5ea" gap={20} />
110
  <Controls />
111
- <MiniMap nodeColor="#005241" style={{background:'#f0f2f5'}} />
112
  <Panel position="top-left">
113
  <div style={{display:'flex',gap:'0.375rem',flexWrap:'wrap',maxWidth:'400px'}}>
114
  {NODE_TYPES.map(nt => (
@@ -130,6 +130,9 @@ export default function StrategyBuilder() {
130
  <p style={{fontSize:'0.8rem',color:'var(--text-secondary)',marginBottom:'1rem'}}>
131
  Add nodes from the palette, connect them, then generate the config.
132
  </p>
 
 
 
133
  <button className="btn btn-primary" style={{width:'100%',marginBottom:'0.75rem'}} onClick={convertToConfig} disabled={nodes.length === 0}>
134
  Generate Config
135
  </button>
 
35
  border: `2px solid ${nodeType.color}`,
36
  borderRadius: '12px',
37
  padding: '12px 20px',
38
+ color: 'var(--text-primary)',
39
  fontSize: '13px',
40
  fontWeight: '600',
41
  minWidth: '160px',
 
104
  nodes={nodes} edges={edges}
105
  onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect}
106
  fitView
107
+ style={{background:'var(--bg-secondary)'}}
108
  >
109
+ <Background color="var(--border-color)" gap={20} />
110
  <Controls />
111
+ <MiniMap nodeColor="var(--accent)" style={{background:'var(--bg-tertiary)'}} />
112
  <Panel position="top-left">
113
  <div style={{display:'flex',gap:'0.375rem',flexWrap:'wrap',maxWidth:'400px'}}>
114
  {NODE_TYPES.map(nt => (
 
130
  <p style={{fontSize:'0.8rem',color:'var(--text-secondary)',marginBottom:'1rem'}}>
131
  Add nodes from the palette, connect them, then generate the config.
132
  </p>
133
+ <p style={{fontSize:'0.72rem',color:'var(--text-muted)',marginBottom:'1rem',display:'flex',alignItems:'center',gap:'0.35rem'}}>
134
+ <kbd style={{padding:'0.1rem 0.35rem',borderRadius:4,border:'1px solid var(--border-color)',background:'var(--bg-tertiary)',fontSize:'0.65rem',fontFamily:'var(--font-mono)'}}>⌫</kbd> Press Backspace to delete a selected node
135
+ </p>
136
  <button className="btn btn-primary" style={{width:'100%',marginBottom:'0.75rem'}} onClick={convertToConfig} disabled={nodes.length === 0}>
137
  Generate Config
138
  </button>