terminal: align grid rows to even boxes, kill empty voids
All checks were successful
deploy / deploy (push) Successful in 8s

Both pipeline (Orchestra ‖ Agent flow) and the workspace grid now use
items-stretch so columns share one height — no page-background gaps under the
shorter column. The chart fills its column (TVChart/ChartSkeleton fill parent;
fixed 440px only when stacked) instead of a fixed 540px, the Committee
distributes its 4 agents to fill the column height, and the Agent flow graph
centres vertically. Workspace is also ~135px shorter, so it fits the page better.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zefiroff 2026-06-09 19:20:57 +04:00
parent 34eb14f848
commit dbc1b9f705
3 changed files with 19 additions and 19 deletions

View file

@ -233,7 +233,7 @@ export function AgentWorkflow({
}
return (
<section className="overflow-hidden rounded-md border border-line bg-panel">
<section className="flex h-full flex-col overflow-hidden rounded-md border border-line bg-panel">
<header className="flex items-center justify-between gap-3 border-b border-line-soft px-3 py-2">
<div className="flex items-baseline gap-2.5">
<span className="label !text-[0.625rem]">Agent flow</span>
@ -245,8 +245,8 @@ export function AgentWorkflow({
</span>
</header>
<div className="overflow-x-auto bg-inset px-2 py-3 sm:px-3">
<div className="mx-auto" style={{ maxWidth: 1140 }}>
<div className="flex flex-1 flex-col justify-center overflow-x-auto bg-inset px-2 py-3 sm:px-3">
<div className="mx-auto w-full" style={{ maxWidth: 1140 }}>
<svg
viewBox={`0 0 ${VB_W} ${VB_H}`}
preserveAspectRatio="xMidYMid meet"

View file

@ -16,7 +16,7 @@ import type { Candle } from '@/lib/hyperliquid'
* crosshair. Carbon Desk themed. Re-fits only on instrument/timeframe change so
* a user's zoom survives the live poll.
*/
export function TVChart({ candles, height = 540, fitKey }: { candles: Candle[]; height?: number; fitKey: string }) {
export function TVChart({ candles, fitKey }: { candles: Candle[]; fitKey: string }) {
const wrapRef = useRef<HTMLDivElement>(null)
const chartRef = useRef<IChartApi | null>(null)
const seriesRef = useRef<ISeriesApi<'Candlestick'> | null>(null)
@ -86,7 +86,7 @@ export function TVChart({ candles, height = 540, fitKey }: { candles: Candle[];
}
}, [candles])
return <div ref={wrapRef} style={{ height, width: '100%' }} />
return <div ref={wrapRef} className="h-full w-full" />
}
// deterministic faux-candle heights (%) for the connecting skeleton — no random, no real data
@ -95,10 +95,10 @@ const SKELETON_BARS = Array.from({ length: 60 }, (_, i) => {
return Math.max(14, Math.min(90, h))
})
/** Chart placeholder shown until live data for the instrument arrives. */
export function ChartSkeleton({ height = 540 }: { height?: number }) {
/** Chart placeholder shown until live data for the instrument arrives. Fills its parent. */
export function ChartSkeleton() {
return (
<div className="relative w-full overflow-hidden" style={{ height }} aria-busy="true" aria-label="Connecting to market data">
<div className="relative h-full w-full overflow-hidden" aria-busy="true" aria-label="Connecting to market data">
<div className="absolute inset-x-1 bottom-0 top-1 flex items-end gap-[3px] animate-pulse">
{SKELETON_BARS.map((h, i) => (
<div key={i} className="flex-1 rounded-[1px]" style={{ height: `${h}%`, background: 'var(--color-track)' }} />

View file

@ -244,8 +244,8 @@ function Committee({ votes, net, activeAgent, onSelect, onHover, loading }: {
}) {
if (loading) {
return (
<Panel title="Committee · analytics" meta="4 · L3">
<div className="divide-y divide-line-soft" aria-busy="true">
<Panel title="Committee · analytics" meta="4 · L3" className="flex h-full flex-col">
<div className="flex flex-1 flex-col divide-y divide-line-soft" aria-busy="true">
{[0, 1, 2, 3].map((i) => (
<div key={i} className="grid grid-cols-[5rem_1fr] gap-x-3 px-3 py-2.5">
<div className="space-y-1.5 self-center">
@ -265,8 +265,8 @@ function Committee({ votes, net, activeAgent, onSelect, onHover, loading }: {
const active = votes.filter((v) => v.direction !== 'SKIP')
const lean = active.length ? clampN(net / active.length, -1, 1) : 0
return (
<Panel title="Committee · analytics" meta={`${votes.length} · L3`}>
<div className="divide-y divide-line-soft">
<Panel title="Committee · analytics" meta={`${votes.length} · L3`} className="flex h-full flex-col">
<div className="flex flex-1 flex-col divide-y divide-line-soft">
{votes.map((v) => {
const meta = AGENT_META[v.agent] ?? { label: v.agent.replace('Agent', ''), reads: '' }
const signal = v.direction !== 'SKIP'
@ -280,7 +280,7 @@ function Committee({ votes, net, activeAgent, onSelect, onHover, loading }: {
onMouseEnter={() => onHover?.(v.agent)}
onMouseLeave={() => onHover?.(null)}
aria-pressed={on}
className={['grid w-full cursor-pointer grid-cols-[5rem_1fr] gap-x-3 border-l-2 px-3 py-2.5 text-left transition-colors', on ? 'border-accent bg-panel-2' : 'border-transparent hover:bg-panel-2'].join(' ')}
className={['grid w-full flex-1 cursor-pointer grid-cols-[5rem_1fr] content-center gap-x-3 border-l-2 px-3 py-2.5 text-left transition-colors', on ? 'border-accent bg-panel-2' : 'border-transparent hover:bg-panel-2'].join(' ')}
>
<div className="self-center">
<div className="font-mono text-data font-medium text-bone">{meta.label}</div>
@ -495,13 +495,13 @@ export function Terminal() {
/>
{/* ── pipeline row · Orchestra (left) ‖ Agent flow graph (right) ── */}
<div className="grid grid-cols-1 items-start gap-2.5 xl:grid-cols-[minmax(0,0.85fr)_minmax(0,1.15fr)]">
<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 ── */}
<div className="grid items-start gap-2.5 lg:grid-cols-[250px_minmax(0,1fr)_330px]">
<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">
<Panel title="Market" meta={marketMeta}>
@ -526,10 +526,10 @@ export function Terminal() {
</Panel>
</div>
{/* CENTER — chart */}
<Panel title={`${coin}-PERP · OHLC`} meta={live ? `LIVE · ${tf.toUpperCase()} · 120` : marketMeta}>
<div className="bg-inset px-2 py-2 sm:px-3">
{updatedAt > 0 ? <TVChart candles={candles} height={540} fitKey={`${coin}-${tf}`} /> : <ChartSkeleton height={540} />}
{/* CENTER — chart (fills the row height; fixed height only when stacked) */}
<Panel title={`${coin}-PERP · OHLC`} meta={live ? `LIVE · ${tf.toUpperCase()} · 120` : marketMeta} className="lg:flex lg:flex-col">
<div className="h-[440px] bg-inset px-2 py-2 sm:px-3 lg:h-auto lg:min-h-0 lg:flex-1">
{updatedAt > 0 ? <TVChart candles={candles} fitKey={`${coin}-${tf}`} /> : <ChartSkeleton />}
</div>
</Panel>