terminal: fill Orchestra status footer with live summaries; chart-first order
All checks were successful
deploy / deploy (push) Successful in 7s
All checks were successful
deploy / deploy (push) Successful in 7s
Orchestra's bottom legend was just labels — now each status category shows a brief live readout (live = price·24hΔ, computed = RSI·MACD·ATR, the call = decision·conf, paper = paper size), still doubling as the colour legend. Also moved the workspace (chart + Market/Indicators + Committee) to the top so the chart is the first block; VERDICT + the Orchestra‖Agent-flow row follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dbc1b9f705
commit
6ab20d3a72
2 changed files with 66 additions and 27 deletions
|
|
@ -1,6 +1,10 @@
|
|||
import type { CSSProperties } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { Direction, Vote } from '@/lib/signals'
|
||||
|
||||
/** Brief live summary shown per status category in the footer readout. */
|
||||
export type OrchestraStatus = { live?: string; computed?: string; call?: string; callTone?: string; paper?: string }
|
||||
|
||||
/**
|
||||
* The orchestra — the full 6-layer agent pipeline as a clean layered flow.
|
||||
* Data → Analysis → Signal → Decision → Execution → Comms. Each node carries an
|
||||
|
|
@ -136,6 +140,27 @@ function Chip({
|
|||
)
|
||||
}
|
||||
|
||||
/** One status-category readout in the footer: dot + label + a brief live value. */
|
||||
function StatusCell({ dotStyle, label, value, tone, dim, loading }: {
|
||||
dotStyle: CSSProperties; label: string; value?: string; tone?: string; dim?: boolean; loading?: boolean
|
||||
}) {
|
||||
return (
|
||||
<div className={cn('flex items-center gap-2 bg-inset px-3 py-2', dim ? 'opacity-80' : '')}>
|
||||
<span className="inline-block h-1.5 w-1.5 shrink-0 rounded-full" style={dotStyle} aria-hidden />
|
||||
<div className="min-w-0">
|
||||
<div className="label-sm !text-[0.5625rem] !text-faint">{label}</div>
|
||||
{loading ? (
|
||||
<div className="skeleton mt-1 h-2.5 w-20" />
|
||||
) : (
|
||||
<div className="tnum mt-0.5 truncate font-mono text-[0.625rem] font-medium" style={{ color: tone ?? 'var(--color-bone)' }}>
|
||||
{value ?? '—'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Orchestra({
|
||||
votes,
|
||||
decision,
|
||||
|
|
@ -144,6 +169,7 @@ export function Orchestra({
|
|||
onHover,
|
||||
onInspect,
|
||||
loading,
|
||||
status,
|
||||
gridClass = 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',
|
||||
}: {
|
||||
votes: Vote[]
|
||||
|
|
@ -153,6 +179,8 @@ export function Orchestra({
|
|||
onHover?: (a: string | null) => void
|
||||
onInspect?: (s: string | null) => void
|
||||
loading?: boolean
|
||||
/** Brief live summary per status category, shown in the footer readout. */
|
||||
status?: OrchestraStatus
|
||||
/** Tailwind grid-cols classes for the layer columns — override when placed side-by-side. */
|
||||
gridClass?: string
|
||||
}) {
|
||||
|
|
@ -230,12 +258,12 @@ export function Orchestra({
|
|||
))}
|
||||
</div>
|
||||
|
||||
{/* legend (status is never colour-only) */}
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1.5 border-t border-line-soft px-3 py-2 font-mono text-[0.5625rem] uppercase tracking-[0.06em] text-faint">
|
||||
<span className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full" style={{ background: 'var(--color-bone)' }} /> live</span>
|
||||
<span className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full" style={{ background: 'var(--color-muted)' }} /> computed</span>
|
||||
<span className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full" style={{ background: 'var(--color-accent)' }} /> the call</span>
|
||||
<span className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full opacity-60" style={{ border: '1px solid var(--color-faint)' }} /> paper / planned</span>
|
||||
{/* status readout — a brief live summary per category (doubles as the legend; never colour-only) */}
|
||||
<div className="grid grid-cols-2 gap-px border-t border-line-soft bg-line-soft">
|
||||
<StatusCell dotStyle={{ background: 'var(--color-bone)' }} label="live" value={status?.live} tone="var(--color-bone)" loading={loading} />
|
||||
<StatusCell dotStyle={{ background: 'var(--color-muted)' }} label="computed" value={status?.computed} tone="var(--color-bone-dim)" loading={loading} />
|
||||
<StatusCell dotStyle={{ background: 'var(--color-accent)' }} label="the call" value={status?.call} tone={status?.callTone} loading={loading} />
|
||||
<StatusCell dotStyle={{ border: '1px solid var(--color-faint)' }} label="paper / planned" value={status?.paper} tone="var(--color-faint)" dim loading={loading} />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -416,6 +416,17 @@ export function Terminal() {
|
|||
const decision = (v?.decision ?? 'SKIP') as Direction
|
||||
const marketMeta = live ? 'LIVE · HL' : updatedAt > 0 ? 'STALE' : 'CONNECTING…'
|
||||
|
||||
// brief live summary per Orchestra status category (live / computed / the call / paper)
|
||||
const orchestraStatus = {
|
||||
live: live
|
||||
? `$${fmtPrice(stat?.mark ?? ta?.price ?? 0)} · ${sign(changePct)}${changePct.toFixed(2)}%`
|
||||
: updatedAt > 0 ? 'stale feed' : 'connecting…',
|
||||
computed: ta ? `RSI ${rsi.toFixed(0)} · MACD ${macdHist >= 0 ? '▲' : '▼'} · ATR ${(ta.atrPct ?? 0).toFixed(2)}%` : '—',
|
||||
call: `${decision} · conf ${Math.round((v?.confidence ?? 0.5) * 100)}%`,
|
||||
callTone: dirColor(decision),
|
||||
paper: decision === 'SKIP' ? 'no position · dry-run' : `paper ${decision} ${(v?.sizePct ?? 0).toFixed(1)}%`,
|
||||
}
|
||||
|
||||
const onAgentHover = (a: string | null) => {
|
||||
setHoverAgent(a)
|
||||
setInspect(a ? AGENT_RULES[a] ?? null : null)
|
||||
|
|
@ -480,27 +491,7 @@ export function Terminal() {
|
|||
|
||||
{guideOpen && <GuideCard onClose={() => setGuideOpen(false)} />}
|
||||
|
||||
{/* ── the call (compact) ── */}
|
||||
<VerdictBar
|
||||
decision={decision}
|
||||
confidence={v?.confidence ?? 0.5}
|
||||
sizePct={v?.sizePct ?? 1}
|
||||
tpPct={v?.tpPct ?? 2}
|
||||
slPct={v?.slPct ?? 1.2}
|
||||
net={net}
|
||||
votes={votes}
|
||||
reasoning={v?.reasoning ?? 'Awaiting live market data…'}
|
||||
live={live}
|
||||
loading={loading}
|
||||
/>
|
||||
|
||||
{/* ── pipeline row · Orchestra (left) ‖ Agent flow graph (right) ── */}
|
||||
<div className="grid grid-cols-1 items-stretch gap-2.5 xl:grid-cols-[minmax(0,0.85fr)_minmax(0,1.15fr)]">
|
||||
<Orchestra votes={votes} decision={decision} activeAgent={activeAgent} onSelect={setSelectedAgent} onHover={onAgentHover} onInspect={setInspect} loading={loading} gridClass="grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 xl:grid-cols-3" />
|
||||
<AgentWorkflow data={data} decision={decision} activeAgent={activeAgent} onSelect={setSelectedAgent} onHover={onAgentHover} onInspect={setInspect} updatedAt={updatedAt} live={live} loading={loading} />
|
||||
</div>
|
||||
|
||||
{/* ── workspace ── */}
|
||||
{/* ── workspace · chart with side blocks (moved to top — first) ── */}
|
||||
<div className="grid items-stretch gap-2.5 lg:grid-cols-[250px_minmax(0,1fr)_330px]">
|
||||
{/* LEFT — stats + pipeline */}
|
||||
<div className="space-y-2.5">
|
||||
|
|
@ -539,6 +530,26 @@ export function Terminal() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── the call (compact) ── */}
|
||||
<VerdictBar
|
||||
decision={decision}
|
||||
confidence={v?.confidence ?? 0.5}
|
||||
sizePct={v?.sizePct ?? 1}
|
||||
tpPct={v?.tpPct ?? 2}
|
||||
slPct={v?.slPct ?? 1.2}
|
||||
net={net}
|
||||
votes={votes}
|
||||
reasoning={v?.reasoning ?? 'Awaiting live market data…'}
|
||||
live={live}
|
||||
loading={loading}
|
||||
/>
|
||||
|
||||
{/* ── pipeline row · Orchestra (left) ‖ Agent flow graph (right) ── */}
|
||||
<div className="grid grid-cols-1 items-stretch gap-2.5 xl:grid-cols-[minmax(0,0.85fr)_minmax(0,1.15fr)]">
|
||||
<Orchestra votes={votes} decision={decision} status={orchestraStatus} activeAgent={activeAgent} onSelect={setSelectedAgent} onHover={onAgentHover} onInspect={setInspect} loading={loading} gridClass="grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 xl:grid-cols-3" />
|
||||
<AgentWorkflow data={data} decision={decision} activeAgent={activeAgent} onSelect={setSelectedAgent} onHover={onAgentHover} onInspect={setInspect} updatedAt={updatedAt} live={live} loading={loading} />
|
||||
</div>
|
||||
|
||||
{/* ── live feed · the agent chain streaming in real time ── */}
|
||||
<LiveFeed data={data} coin={coin} tf={tf} updatedAt={updatedAt} live={live} loading={loading} onInspect={setInspect} />
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue