diff --git a/web/src/components/LivePulseTicker.tsx b/web/src/components/LivePulseTicker.tsx index fb11304..972f828 100644 --- a/web/src/components/LivePulseTicker.tsx +++ b/web/src/components/LivePulseTicker.tsx @@ -1,13 +1,45 @@ -import React from 'react'; -import type { BotState } from '../hooks/useWebSocket'; -import { TrendingUp, Zap } from 'lucide-react'; +import React from 'react'; +import type { CSSProperties } from 'react'; +import type { BotState } from '../hooks/useWebSocket'; +import { TrendingUp, Zap } from 'lucide-react'; interface LivePulseTickerProps { botState: BotState; } -export const LivePulseTicker: React.FC = ({ botState }) => { - const symbols = Object.keys(botState.symbols); +export const LivePulseTicker: React.FC = ({ botState }) => { + const tickerShellStyle: CSSProperties = { + height: '40px', + background: 'var(--bl-surface-overlay)', + backdropFilter: 'blur(10px)', + borderTop: '1px solid var(--bl-border-subtle)', + borderBottom: '1px solid var(--bl-border-subtle)', + display: 'flex', + alignItems: 'center', + overflow: 'hidden', + position: 'sticky', + top: '0', + zIndex: 1000, + padding: '0 24px' + }; + const marketLabelStyle: CSSProperties = { + fontSize: '10px', + fontWeight: 900, + color: 'var(--bl-text-faint)', + textTransform: 'uppercase', + letterSpacing: '1px' + }; + const aiShellStyle: CSSProperties = { + display: 'flex', + alignItems: 'center', + gap: '16px', + marginLeft: 'auto', + background: 'var(--bl-success-muted)', + padding: '0 16px', + height: '100%', + borderLeft: '1px solid var(--bl-success-border)', + }; + const symbols = Object.keys(botState.symbols); const volatileSymbols = symbols .sort((a, b) => Math.abs(botState.symbols[b].change24h || 0) - Math.abs(botState.symbols[a].change24h || 0)) .slice(0, 8); @@ -18,71 +50,58 @@ export const LivePulseTicker: React.FC = ({ botState }) => .slice(0, 3); return ( -
- {/* Market Ticker Section */} -
- - Market Pulse -
- -
- {volatileSymbols.map(s => { - const change = botState.symbols[s].change24h || 0; - return ( -
- {s.split('/')[0]} - = 0 ? '#00ff88' : '#ff3366', - fontFamily: 'monospace' - }}> - {change >= 0 ? '+' : ''}{change.toFixed(2)}% - -
+
+ {/* Market Ticker Section */} +
+ + Market Pulse +
+ +
+ {volatileSymbols.map(s => { + const change = botState.symbols[s].change24h || 0; + return ( +
+ {s.split('/')[0]} + = 0 ? 'var(--bl-success)' : 'var(--bl-danger)', + fontFamily: 'monospace' + }}> + {change >= 0 ? '+' : ''}{change.toFixed(2)}% + +
); })}
- - {/* AI Highlight Section */} -
-
- - AI Top Picks -
-
- {aiSetups.map(s => ( -
- {s.split('/')[0]} - {botState.symbols[s].rules['AIAnalysisRule']?.metadata?.confidence}% -
- ))} - {aiSetups.length === 0 && SCANNING...} -
-
- - -
- ); -}; + + {/* AI Highlight Section */} +
+
+ + AI Top Picks +
+
+ {aiSetups.map(s => ( +
+ {s.split('/')[0]} + {botState.symbols[s].rules['AIAnalysisRule']?.metadata?.confidence}% +
+ ))} + {aiSetups.length === 0 && SCANNING...} +
+
+ + +
+ ); +}; diff --git a/web/src/components/Login.tsx b/web/src/components/Login.tsx index 9b63aa8..667814a 100644 --- a/web/src/components/Login.tsx +++ b/web/src/components/Login.tsx @@ -75,13 +75,13 @@ export function Login() {
)} - {error &&
{error}
} - {message &&
{error}
} + {message &&
{message}
} @@ -111,77 +111,77 @@ export function Login() { - - ); -} + .link-button { + background: none; + border: none; + color: var(--bl-text-quiet); + cursor: pointer; + font-size: 0.9rem; + text-decoration: underline; + } + + .link-button:hover { + color: var(--foreground); + } + `} + + ); +} diff --git a/web/src/components/ProductAccessibilityGate.tsx b/web/src/components/ProductAccessibilityGate.tsx index 8db63f7..5e89ac9 100644 --- a/web/src/components/ProductAccessibilityGate.tsx +++ b/web/src/components/ProductAccessibilityGate.tsx @@ -74,24 +74,24 @@ function CenteredMessage({ title, body }: { title: string; body?: string }) { display: 'flex', alignItems: 'center', justifyContent: 'center', - background: '#0a0b0d', - color: '#f5f5f5', + background: 'var(--background)', + color: 'var(--foreground)', padding: '2rem', }} >

{title}

{body ? ( -

{body}

+

{body}

) : null}
diff --git a/web/src/index.css b/web/src/index.css index 7c1f557..a1a1f03 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -51,6 +51,16 @@ --bl-danger-muted: color-mix(in oklab, var(--bl-danger) 12%, var(--background)); --bl-info: var(--accent); --bl-info-muted: var(--accent-soft); + --bl-info-strong: #3b82f6; + --bl-attention: #06b6d4; + --bl-emphasis: #a855f7; + --bl-surface-strong: color-mix(in oklab, var(--card) 88%, var(--foreground)); + --bl-surface-overlay: color-mix(in oklab, var(--background) 90%, var(--foreground)); + --bl-surface-highlight: color-mix(in oklab, var(--card-elevated) 86%, var(--foreground)); + --bl-border-subtle: color-mix(in oklab, var(--border) 62%, transparent); + --bl-border-soft: color-mix(in oklab, var(--border-strong) 50%, transparent); + --bl-text-quiet: color-mix(in oklab, var(--muted-foreground) 82%, var(--background)); + --bl-text-faint: color-mix(in oklab, var(--muted-foreground) 58%, var(--background)); --bl-focus-ring: var(--ring); --bl-focus-ring-muted: var(--ring-soft); --bl-radius-control: 0.5rem; diff --git a/web/src/tabs/AdminTab.tsx b/web/src/tabs/AdminTab.tsx index 61280d0..4be7fa7 100644 --- a/web/src/tabs/AdminTab.tsx +++ b/web/src/tabs/AdminTab.tsx @@ -34,12 +34,12 @@ const ruleDescriptions: { [key: string]: { desc: string; category: string; icon: }; const categoryColors: { [key: string]: string } = { - 'Trend': '#3b82f6', - 'Filter': '#a855f7', - 'Entry': '#f59e0b', - 'Momentum': '#06b6d4', - 'Risk': '#ef4444', - 'AI': '#10b981', + 'Trend': 'var(--bl-info-strong)', + 'Filter': 'var(--bl-emphasis)', + 'Entry': 'var(--bl-warning)', + 'Momentum': 'var(--bl-attention)', + 'Risk': 'var(--bl-danger)', + 'AI': 'var(--bl-success)', }; export const AdminTab = ({ botState, socket }: AdminTabProps) => { @@ -147,8 +147,13 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { || hasReconciliationBacklog ); const systemBadgeLabel = systemCritical ? 'Critical' : systemDegraded ? 'Degraded' : 'Healthy'; - const systemBadgeDotClass = systemCritical ? 'bg-red-500' : systemDegraded ? 'bg-orange-500' : 'bg-[#00ff88]'; - const systemBadgeTextClass = systemCritical ? 'text-red-400' : systemDegraded ? 'text-orange-400' : 'text-[#00ff88]'; + const systemBadgeDotClass = systemCritical ? 'bg-red-500' : systemDegraded ? 'bg-orange-500' : 'bg-[var(--bl-success)]'; + const systemBadgeTextClass = systemCritical ? 'text-red-400' : systemDegraded ? 'text-orange-400' : 'text-[var(--bl-success)]'; + const adminPanelClass = 'bg-[var(--bl-surface-strong)] border border-[var(--bl-border-subtle)]'; + const adminPanelOverlayClass = 'bg-[var(--bl-surface-overlay)] border border-[var(--bl-border-subtle)]'; + const adminPanelHoverClass = 'bg-[var(--bl-surface-strong)] border border-[var(--bl-border-subtle)] hover:border-[var(--bl-border-soft)]'; + const adminNavigationClass = 'bg-[var(--bl-surface-overlay)] border border-[var(--bl-border-subtle)]'; + const adminActiveAccentClass = 'bg-[var(--bl-success)]/10 border border-[var(--bl-success)]/15'; const handlePauseTrading = async () => { setIsControlLoading(true); @@ -305,7 +310,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { {/* Health Summary Grid */}
{/* Trading Engine Card */} -
+

Trading Engine

@@ -322,7 +327,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
{/* Position Monitor Card */} -
+

Position Monitor

@@ -339,7 +344,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
{/* Reconciliation Card */} -
+

Reconciliation

@@ -363,7 +368,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
{/* Operational Events (Admin Error Panel) */} -
+
@@ -467,7 +472,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
{/* Detailed Metrics */} -
+

Performance Telemetry

@@ -552,7 +557,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { {Object.entries(import.meta.env) .filter(([key]) => key.startsWith('VITE_') || key === 'MODE') .map(([key, value]) => ( -
+
{key} {String(value)} @@ -571,7 +576,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { {botConfig ? (
{Object.entries(botConfig).map(([key, value]) => ( -
+
{key} {Array.isArray(value) ? value.join(', ') : String(value)} @@ -580,7 +585,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { ))}
) : ( -
+

Bot offline — unable to fetch config

@@ -596,7 +601,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
Buffer: 100 entries
-
+
@@ -631,7 +636,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { return (
{rules.length === 0 ? ( -
+

No Active Rules

Connect to the bot service to load the pipeline.

@@ -644,7 +649,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { {rules.length} rules execute sequentially per trading cycle

- All active + All active
@@ -652,13 +657,13 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
{rules.map((rule, idx) => { const info = ruleDescriptions[rule] || { desc: 'Technical strategy rule.', category: 'System', icon: Hexagon }; - const color = categoryColors[info.category] || '#6b7280'; + const color = categoryColors[info.category] || 'var(--bl-text-quiet)'; const RuleIcon = info.icon; const isLast = idx === rules.length - 1; return ( -
+
{/* Left accent */}
{ {/* Status */}
- - - Active + + + Active
@@ -831,7 +836,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
-
+

State Snapshots

@@ -900,7 +905,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { if (profile?.role !== 'admin') { return ( -
+

Access Denied

You do not have administrative privileges to access this area.

@@ -913,8 +918,8 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => { {/* Header */}
-
- +
+

Admin Panel

@@ -923,7 +928,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
-
+
{botConfig ? ( <> @@ -938,7 +943,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
{/* System Status Badge */} -
+
System: {systemBadgeLabel} @@ -948,7 +953,7 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
{/* Sub Navigation */} -