fix(ui): render shared primitives in product shell

This commit is contained in:
Saravana Achu Mac 2026-05-09 02:29:19 -07:00
parent ff17c635e3
commit 00d8683987
2 changed files with 56 additions and 150 deletions

View File

@ -1,5 +1,6 @@
import type { BotState } from '../hooks/useWebSocket';
import { Activity, BrainCircuit } from 'lucide-react';
import { Badge, EmptyState, Panel, PanelDescription, PanelHeader, PanelTitle } from './ui/Primitives';
interface MarketOpportunitiesProps {
botState: BotState;
@ -12,27 +13,37 @@ export const TopVolatile = ({ botState }: MarketOpportunitiesProps) => {
.slice(0, 5);
return (
<section className="opportunity-card-sidebar volatile-corner" aria-labelledby="top-volatile-title">
<div className="opportunity-card-header">
<span className="opportunity-card-icon"><Activity size={16} /></span>
<div>
<h3 id="top-volatile-title">Top movers</h3>
<p>24h change by absolute move.</p>
<Panel aria-labelledby="top-volatile-title" className="min-w-0">
<PanelHeader>
<div className="flex min-w-0 items-start gap-3">
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-[var(--bl-border)] bg-[var(--bl-surface-muted)] text-[var(--bl-accent)]">
<Activity size={16} />
</span>
<div className="min-w-0">
<PanelTitle id="top-volatile-title">Top movers</PanelTitle>
<PanelDescription>24h change by absolute move.</PanelDescription>
</div>
</div>
</div>
<div className="opp-list">
</PanelHeader>
<div className="mt-4 grid gap-2">
{topSymbols
.map(symbol => (
<div key={symbol} className="opp-item">
<span className="opp-symbol">{symbol}</span>
<span className={`opp-value ${botState.symbols[symbol].change24h >= 0 ? 'up' : 'down'}`}>
{botState.symbols[symbol].change24h >= 0 ? '+' : ''}{botState.symbols[symbol].change24h?.toFixed(2)}%
</span>
</div>
))}
{symbols.length === 0 && <div className="opp-empty">Waiting for live market ticks...</div>}
<div key={symbol} className="flex min-w-0 items-center justify-between gap-3 rounded-lg border border-[var(--bl-border-subtle)] bg-[var(--bl-surface-muted)] px-3 py-2">
<span className="min-w-0 truncate font-mono text-sm font-semibold text-[var(--bl-text-primary)]">{symbol}</span>
<Badge variant={botState.symbols[symbol].change24h >= 0 ? 'success' : 'danger'} size="sm">
{botState.symbols[symbol].change24h >= 0 ? '+' : ''}{botState.symbols[symbol].change24h?.toFixed(2)}%
</Badge>
</div>
))}
{symbols.length === 0 && (
<EmptyState
title="Waiting for live market ticks"
description="Movers will appear as soon as the market stream starts publishing."
className="min-h-40"
/>
)}
</div>
</section>
</Panel>
);
};
@ -44,28 +55,36 @@ export const AISetups = ({ botState }: MarketOpportunitiesProps) => {
.slice(0, 5);
return (
<section className="opportunity-card-sidebar" aria-labelledby="ai-setups-title">
<div className="opportunity-card-header">
<span className="opportunity-card-icon"><BrainCircuit size={16} /></span>
<div>
<h3 id="ai-setups-title">AI setups</h3>
<p>Highest-confidence symbols from strategy rules.</p>
<Panel aria-labelledby="ai-setups-title" className="min-w-0">
<PanelHeader>
<div className="flex min-w-0 items-start gap-3">
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-[var(--bl-border)] bg-[var(--bl-surface-muted)] text-[var(--bl-accent)]">
<BrainCircuit size={16} />
</span>
<div className="min-w-0">
<PanelTitle id="ai-setups-title">AI setups</PanelTitle>
<PanelDescription>Highest-confidence symbols from strategy rules.</PanelDescription>
</div>
</div>
</div>
<div className="opp-list">
</PanelHeader>
<div className="mt-4 grid gap-2">
{aiSymbols
.map(symbol => (
<div key={symbol} className="opp-item">
<span className="opp-symbol">{symbol}</span>
<span className="opp-value ai">
{botState.symbols[symbol].rules['AIAnalysisRule']?.metadata?.confidence}% Conf
</span>
</div>
))}
<div key={symbol} className="flex min-w-0 items-center justify-between gap-3 rounded-lg border border-[var(--bl-border-subtle)] bg-[var(--bl-surface-muted)] px-3 py-2">
<span className="min-w-0 truncate font-mono text-sm font-semibold text-[var(--bl-text-primary)]">{symbol}</span>
<Badge variant="accent" size="sm">
{botState.symbols[symbol].rules['AIAnalysisRule']?.metadata?.confidence}% Conf
</Badge>
</div>
))}
{aiSymbols.length === 0 && (
<div className="opp-empty">No AI confidence signals yet.</div>
<EmptyState
title="No AI confidence signals yet"
description="High-confidence setups will appear after strategy rules publish analysis metadata."
className="min-h-40"
/>
)}
</div>
</section>
</Panel>
);
};

View File

@ -1,8 +1,8 @@
@import '@bytelyst/design-tokens/generated/tokens.css';
@import 'tailwindcss';
@tailwind base;
@tailwind components;
@tailwind utilities;
@source './**/*.{ts,tsx}';
@source '../../../learning_ai_common_plat/packages/ui/src/**/*.{ts,tsx}';
:root {
font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
@ -2287,119 +2287,6 @@ body {
min-width: 0;
}
.opportunity-card-sidebar {
display: grid;
gap: 18px;
min-width: 0;
border: 1px solid var(--border);
border-radius: 18px;
background: var(--card);
padding: 20px;
box-shadow: 0 18px 50px rgba(15, 23, 42, 0.07);
}
.opportunity-card-header {
display: flex;
align-items: flex-start;
gap: 12px;
min-width: 0;
}
.opportunity-card-icon {
display: grid;
width: 34px;
height: 34px;
flex: 0 0 auto;
place-items: center;
border: 1px solid color-mix(in oklab, var(--accent) 24%, var(--border));
border-radius: 12px;
background: var(--accent-soft);
color: var(--accent);
}
.opportunity-card-sidebar h3 {
margin: 0;
color: var(--foreground);
font-size: 16px;
font-weight: 820;
letter-spacing: 0;
}
.opportunity-card-sidebar p {
margin: 4px 0 0;
color: var(--muted-foreground);
font-size: 13px;
line-height: 1.5;
}
.opp-list {
display: grid;
gap: 8px;
}
.opp-item {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 12px;
min-height: 42px;
border: 1px solid var(--border);
border-radius: 12px;
background: color-mix(in oklab, var(--muted) 48%, var(--card));
padding: 8px 12px;
}
.opp-symbol {
min-width: 0;
overflow: hidden;
color: var(--foreground);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 13px;
font-weight: 800;
text-overflow: ellipsis;
white-space: nowrap;
}
.opp-value {
display: inline-flex;
align-items: center;
min-height: 24px;
border-radius: 999px;
padding: 0 9px;
font-size: 11px;
font-weight: 800;
white-space: nowrap;
}
.opp-value.up {
background: var(--bl-success-muted);
color: var(--bl-success);
}
.opp-value.down {
background: var(--bl-danger-muted);
color: var(--bl-danger);
}
.opp-value.ai {
background: var(--bl-info-muted);
color: var(--bl-info);
}
.opp-empty {
display: grid;
min-height: 120px;
place-items: center;
border: 1px dashed var(--border);
border-radius: 14px;
background: color-mix(in oklab, var(--muted) 42%, transparent);
color: var(--muted-foreground);
padding: 18px;
text-align: center;
font-size: 13px;
line-height: 1.5;
}
.screener-filter-card {
overflow: hidden;
}