Make tape feeds compact tables

This commit is contained in:
dirtydishes 2026-05-04 04:16:42 -04:00
parent c31d59ea79
commit 5fcdb015c0
2 changed files with 363 additions and 162 deletions

View file

@ -897,6 +897,7 @@ h3 {
background: linear-gradient(180deg, rgba(245, 166, 35, 0.07), rgba(255, 255, 255, 0.018));
}
.data-table-shell,
.options-table-wrap {
display: flex;
flex: 1 1 auto;
@ -905,6 +906,191 @@ h3 {
overflow: hidden;
}
.data-table-wrap {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
background: rgba(5, 8, 12, 0.42);
}
.data-table {
display: block;
min-width: 980px;
}
.data-table-options {
min-width: 1280px;
}
.data-table-equities {
min-width: 660px;
}
.data-table-flow {
min-width: 1260px;
}
.data-table-alerts {
min-width: 900px;
}
.data-table-classifier {
min-width: 760px;
}
.data-table-dark {
min-width: 820px;
}
.data-table-head,
.data-table-row {
display: grid;
align-items: center;
column-gap: 8px;
}
.data-table-head {
position: sticky;
top: 0;
z-index: 2;
min-height: 30px;
padding: 0 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.095);
background: rgba(8, 11, 16, 0.98);
color: var(--text-faint);
font-size: 0.64rem;
font-weight: 700;
letter-spacing: 0.08em;
}
.data-table-row {
width: 100%;
min-height: 40px;
padding: 0 10px;
border: 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.055);
background: rgba(255, 255, 255, 0.008);
color: inherit;
font: inherit;
text-align: left;
}
.data-table-row:nth-child(even) {
background: rgba(255, 255, 255, 0.022);
}
.data-table-row:hover,
.data-table-row:focus-visible {
outline: none;
background: rgba(245, 166, 35, 0.055);
}
.data-table-row-button {
cursor: pointer;
}
.data-table-row-options {
min-height: 36px;
}
.data-table-row-equities {
min-height: 34px;
}
.data-table-row-flow,
.data-table-row-alerts,
.data-table-row-classifier,
.data-table-row-dark {
min-height: 44px;
}
.data-table-row-classified {
background:
linear-gradient(90deg, rgba(var(--classifier-rgb, 192, 200, 210), calc(0.02 + var(--classifier-intensity, 0) * 0.12)), transparent 62%),
rgba(255, 255, 255, 0.008);
}
.data-table-row-classified:hover,
.data-table-row-classified:focus-visible {
background:
linear-gradient(90deg, rgba(var(--classifier-rgb, 192, 200, 210), calc(0.04 + var(--classifier-intensity, 0) * 0.18)), transparent 68%),
rgba(245, 166, 35, 0.04);
}
.data-table-row-classified.is-classified {
border-left: 3px solid rgba(var(--classifier-rgb), calc(0.35 + var(--classifier-intensity) * 0.45));
padding-left: 7px;
}
.data-table-row-warn,
.data-table-row-severity-high,
.data-table-row-direction-bearish {
border-left: 3px solid rgba(255, 107, 95, 0.58);
padding-left: 7px;
}
.data-table-row-severity-medium,
.data-table-row-direction-neutral {
border-left: 3px solid rgba(77, 163, 255, 0.46);
padding-left: 7px;
}
.data-table-row-severity-low,
.data-table-row-direction-bullish {
border-left: 3px solid rgba(37, 193, 122, 0.5);
padding-left: 7px;
}
.data-table-options .data-table-head,
.data-table-options .data-table-row {
grid-template-columns: minmax(72px, 0.8fr) minmax(50px, 0.55fr) minmax(64px, 0.7fr) minmax(58px, 0.6fr) minmax(34px, 0.35fr) minmax(62px, 0.65fr) minmax(104px, 1fr) minmax(54px, 0.55fr) minmax(66px, 0.7fr) minmax(48px, 0.5fr) minmax(42px, 0.45fr) minmax(92px, 0.9fr);
}
.data-table-equities .data-table-head,
.data-table-equities .data-table-row {
grid-template-columns: minmax(76px, 0.9fr) minmax(70px, 0.8fr) minmax(76px, 0.8fr) minmax(70px, 0.75fr) minmax(80px, 0.8fr) minmax(76px, 0.75fr);
}
.data-table-flow .data-table-head,
.data-table-flow .data-table-row {
grid-template-columns: minmax(148px, 1.1fr) minmax(180px, 1.4fr) minmax(62px, 0.45fr) minmax(70px, 0.5fr) minmax(88px, 0.7fr) minmax(74px, 0.55fr) minmax(132px, 1fr) minmax(110px, 0.8fr) minmax(210px, 1.6fr);
}
.data-table-alerts .data-table-head,
.data-table-alerts .data-table-row {
grid-template-columns: minmax(76px, 0.75fr) minmax(170px, 1.4fr) minmax(52px, 0.45fr) minmax(58px, 0.45fr) minmax(52px, 0.4fr) minmax(66px, 0.55fr) minmax(260px, 2fr);
}
.data-table-classifier .data-table-head,
.data-table-classifier .data-table-row {
grid-template-columns: minmax(76px, 0.75fr) minmax(180px, 1.45fr) minmax(70px, 0.6fr) minmax(74px, 0.65fr) minmax(300px, 2.2fr);
}
.data-table-dark .data-table-head,
.data-table-dark .data-table-row {
grid-template-columns: minmax(76px, 0.75fr) minmax(170px, 1.35fr) minmax(76px, 0.65fr) minmax(74px, 0.65fr) minmax(74px, 0.65fr) minmax(260px, 2fr);
}
.data-table-cell {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.72rem;
}
.data-table-cell-number {
font-family: var(--font-mono), monospace;
font-variant-numeric: tabular-nums;
}
.data-table-spacer {
min-width: 100%;
pointer-events: none;
}
.options-table {
display: flex;
min-height: 0;

View file

@ -5343,7 +5343,7 @@ type OptionsPaneProps = {
const OptionsPane = ({ limit }: OptionsPaneProps) => {
const state = useTerminal();
const items = limit ? state.filteredOptions.slice(0, limit) : state.filteredOptions;
const virtual = useVirtualList(items, state.optionsScroll.listRef, !limit, 34);
const virtual = useVirtualList(items, state.optionsScroll.listRef, !limit, 36);
return (
<Pane
@ -5369,7 +5369,7 @@ const OptionsPane = ({ limit }: OptionsPaneProps) => {
/>
}
>
<div className="options-table-wrap">
<div className="data-table-shell">
{items.length === 0 ? (
<div className="empty">
{state.tickerSet.size > 0
@ -5381,24 +5381,24 @@ const OptionsPane = ({ limit }: OptionsPaneProps) => {
: "Replay queue empty. Ensure ClickHouse has data."}
</div>
) : (
<div className="options-table" role="table" aria-label="Options tape">
<div className="options-table-head" role="row">
<span>TIME</span>
<span>SYM</span>
<span>EXP</span>
<span>STRIKE</span>
<span>C/P</span>
<span>SPOT</span>
<span>DETAILS</span>
<span>TYPE</span>
<span>VALUE</span>
<span>SIDE</span>
<span>IV</span>
<span>CLASSIFIER</span>
<div className="data-table-wrap" ref={state.optionsScroll.listRef}>
<div className="data-table data-table-options" role="table" aria-label="Options tape">
<div className="data-table-head" role="row">
<span className="data-table-cell">TIME</span>
<span className="data-table-cell">SYM</span>
<span className="data-table-cell">EXP</span>
<span className="data-table-cell">STRIKE</span>
<span className="data-table-cell">C/P</span>
<span className="data-table-cell">SPOT</span>
<span className="data-table-cell">DETAILS</span>
<span className="data-table-cell">TYPE</span>
<span className="data-table-cell">VALUE</span>
<span className="data-table-cell">SIDE</span>
<span className="data-table-cell">IV</span>
<span className="data-table-cell">CLASSIFIER</span>
</div>
<div className="options-table-body" ref={state.optionsScroll.listRef}>
{virtual.topSpacerHeight > 0 ? (
<div style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
) : null}
{virtual.visibleItems.map((print) => {
const contractId = normalizeContractId(print.option_contract_id);
@ -5415,31 +5415,31 @@ const OptionsPane = ({ limit }: OptionsPaneProps) => {
const iv = print.execution_iv;
const decor = state.classifierDecorByOptionTraceId.get(print.trace_id);
const commonProps = {
className: `options-table-row${decor ? ` is-classified classifier-${decor.tone}` : ""}`,
className: `data-table-row data-table-row-button data-table-row-classified data-table-row-options${decor ? ` is-classified classifier-${decor.tone}` : ""}`,
style: decor ? ({ "--classifier-intensity": decor.intensity } as CSSProperties) : undefined
};
const cells = (
<>
<span className="mono">{formatTime(print.ts)}</span>
<span>{contractDisplay?.ticker ?? parsed?.root ?? formatContractLabel(contractId)}</span>
<span>{contractDisplay?.expiration ?? parsed?.expiry ?? "--"}</span>
<span>{contractDisplay?.strike.replace(/[CP]$/, "") ?? "--"}</span>
<span>{parsed?.right ?? contractDisplay?.strike.slice(-1) ?? "--"}</span>
<span>{typeof spot === "number" ? formatPrice(spot) : "--"}</span>
<span className="mono">
<span className="data-table-cell data-table-cell-number">{formatTime(print.ts)}</span>
<span className="data-table-cell">{contractDisplay?.ticker ?? parsed?.root ?? formatContractLabel(contractId)}</span>
<span className="data-table-cell">{contractDisplay?.expiration ?? parsed?.expiry ?? "--"}</span>
<span className="data-table-cell data-table-cell-number">{contractDisplay?.strike.replace(/[CP]$/, "") ?? "--"}</span>
<span className="data-table-cell">{parsed?.right ?? contractDisplay?.strike.slice(-1) ?? "--"}</span>
<span className="data-table-cell data-table-cell-number">{typeof spot === "number" ? formatPrice(spot) : "--"}</span>
<span className="data-table-cell data-table-cell-number">
{formatSize(print.size)}@{formatPrice(print.price)}_{nbboSide ?? "--"}
</span>
<span>{print.option_type ?? "--"}</span>
<span className="notional-emphasis">${formatCompactUsd(notional)}</span>
<span>
<span className="data-table-cell">{print.option_type ?? "--"}</span>
<span className="data-table-cell data-table-cell-number notional-emphasis">${formatCompactUsd(notional)}</span>
<span className="data-table-cell">
{nbboSide ? (
<span className={`nbbo-tag nbbo-tag-${nbboSide.toLowerCase()}`}>{nbboSide}</span>
) : (
"--"
)}
</span>
<span>{typeof iv === "number" ? formatPct(iv) : "--"}</span>
<span>{decor ? humanizeClassifierId(decor.family) : "--"}</span>
<span className="data-table-cell data-table-cell-number">{typeof iv === "number" ? formatPct(iv) : "--"}</span>
<span className="data-table-cell">{decor ? humanizeClassifierId(decor.family) : "--"}</span>
</>
);
@ -5465,7 +5465,7 @@ const OptionsPane = ({ limit }: OptionsPaneProps) => {
);
})}
{virtual.bottomSpacerHeight > 0 ? (
<div style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
) : null}
</div>
</div>
@ -5483,7 +5483,7 @@ type EquitiesPaneProps = {
const EquitiesPane = ({ limit }: EquitiesPaneProps) => {
const state = useTerminal();
const items = limit ? state.filteredEquities.slice(0, limit) : state.filteredEquities;
const virtual = useVirtualList(items, state.equitiesScroll.listRef, !limit, 78);
const virtual = useVirtualList(items, state.equitiesScroll.listRef, !limit, 36);
return (
<Pane
@ -5509,7 +5509,7 @@ const EquitiesPane = ({ limit }: EquitiesPaneProps) => {
/>
}
>
<div className="list terminal-list" ref={state.equitiesScroll.listRef}>
<div className="data-table-shell">
{items.length === 0 ? (
<div className="empty">
{state.tickerSet.size > 0
@ -5523,34 +5523,36 @@ const EquitiesPane = ({ limit }: EquitiesPaneProps) => {
: "Replay queue empty. Ensure ClickHouse has data."}
</div>
) : (
<>
<div className="data-table-wrap" ref={state.equitiesScroll.listRef}>
<div className="data-table data-table-equities" role="table" aria-label="Equity prints">
<div className="data-table-head" role="row">
<span className="data-table-cell">TIME</span>
<span className="data-table-cell">SYM</span>
<span className="data-table-cell">PRICE</span>
<span className="data-table-cell">SIZE</span>
<span className="data-table-cell">VENUE</span>
<span className="data-table-cell">TAPE</span>
</div>
{virtual.topSpacerHeight > 0 ? (
<div style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
) : null}
{virtual.visibleItems.map((print) => (
<div className="row" key={`${print.trace_id}-${print.seq}`}>
<div>
<div className="contract">{print.underlying_id}</div>
<div className="meta">
<span>${formatPrice(print.price)}</span>
<span>{formatSize(print.size)}x</span>
<span>{print.exchange}</span>
{print.offExchangeFlag ? (
<span className="flag">Off-Ex</span>
) : (
<span className="flag flag-muted">Lit</span>
)}
</div>
</div>
<div className="time">{formatTime(print.ts)}</div>
<div className="data-table-row data-table-row-equities" key={`${print.trace_id}-${print.seq}`}>
<span className="data-table-cell data-table-cell-number">{formatTime(print.ts)}</span>
<span className="data-table-cell">{print.underlying_id}</span>
<span className="data-table-cell data-table-cell-number">${formatPrice(print.price)}</span>
<span className="data-table-cell data-table-cell-number">{formatSize(print.size)}x</span>
<span className="data-table-cell">{print.exchange}</span>
<span className="data-table-cell">{print.offExchangeFlag ? "Off-Ex" : "Lit"}</span>
</div>
))}
{virtual.bottomSpacerHeight > 0 ? (
<div style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
) : null}
{!limit ? <LoadOlderControl channel="equities" /> : null}
</>
</div>
</div>
)}
{!limit ? <LoadOlderControl channel="equities" /> : null}
</div>
</Pane>
);
@ -5564,7 +5566,7 @@ type FlowPaneProps = {
const FlowPane = ({ limit, title = "Flow" }: FlowPaneProps) => {
const state = useTerminal();
const items = limit ? state.filteredFlow.slice(0, limit) : state.filteredFlow;
const virtual = useVirtualList(items, state.flowScroll.listRef, !limit, 104);
const virtual = useVirtualList(items, state.flowScroll.listRef, !limit, 44);
return (
<Pane
@ -5590,7 +5592,7 @@ const FlowPane = ({ limit, title = "Flow" }: FlowPaneProps) => {
/>
}
>
<div className="list terminal-list" ref={state.flowScroll.listRef}>
<div className="data-table-shell">
{items.length === 0 ? (
<div className="empty">
{state.tickerSet.size > 0
@ -5602,9 +5604,21 @@ const FlowPane = ({ limit, title = "Flow" }: FlowPaneProps) => {
: "Replay queue empty. Ensure ClickHouse has data."}
</div>
) : (
<>
<div className="data-table-wrap" ref={state.flowScroll.listRef}>
<div className="data-table data-table-flow" role="table" aria-label="Flow packets">
<div className="data-table-head" role="row">
<span className="data-table-cell">TIME</span>
<span className="data-table-cell">CONTRACT</span>
<span className="data-table-cell">PRINTS</span>
<span className="data-table-cell">SIZE</span>
<span className="data-table-cell">NOTIONAL</span>
<span className="data-table-cell">WINDOW</span>
<span className="data-table-cell">STRUCTURE</span>
<span className="data-table-cell">NBBO</span>
<span className="data-table-cell">QUALITY</span>
</div>
{virtual.topSpacerHeight > 0 ? (
<div style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
) : null}
{virtual.visibleItems.map((packet) => {
const features = packet.features ?? {};
@ -5638,59 +5652,46 @@ const FlowPane = ({ limit, title = "Flow" }: FlowPaneProps) => {
const nbboAge = parseNumber(packet.join_quality.nbbo_age_ms, Number.NaN);
const nbboStale = parseNumber(packet.join_quality.nbbo_stale, 0) > 0;
const nbboMissing = parseNumber(packet.join_quality.nbbo_missing, 0) > 0;
const structureLabel = structureType
? `${structureType.replace(/_/g, " ")}${structureRights ? ` ${structureRights}` : ""}${structureLegs > 0 ? ` ${structureLegs}L` : ""}${structureStrikes > 0 ? ` ${structureStrikes}K` : ""}`
: "--";
const nbboLabel = Number.isFinite(nbboBid) && Number.isFinite(nbboAsk)
? `${formatPrice(nbboBid)} x ${formatPrice(nbboAsk)}`
: Number.isFinite(nbboMid)
? `Mid ${formatPrice(nbboMid)}`
: "--";
const qualityLabel = [
Number.isFinite(aggressiveCoverage) && aggressiveCoverage > 0
? `Agg ${formatPct(aggressiveBuyRatio)}/${formatPct(aggressiveSellRatio)} ${formatPct(aggressiveCoverage)} cov`
: null,
Number.isFinite(insideRatio) && insideRatio > 0 ? `In ${formatPct(insideRatio)}` : null,
Number.isFinite(nbboSpread) ? `Spr ${formatPrice(nbboSpread)}` : null,
Number.isFinite(nbboAge) ? `${Math.round(nbboAge)}ms` : null,
nbboStale ? "Stale" : null,
nbboMissing ? "Missing" : null
].filter(Boolean).join(" | ");
return (
<div className="row" key={packet.id}>
<div>
<div className="contract">{contract}</div>
<div className="meta flow-meta">
<span>{formatFlowMetric(count)} prints</span>
<span>{formatFlowMetric(totalSize)} size</span>
<span>Notional ${formatUsd(notional)}</span>
{windowMs > 0 ? <span>{formatFlowMetric(windowMs, "ms")}</span> : null}
{structureType ? (
<span className="pill structure-tag">
{structureType.replace(/_/g, " ")}
{structureRights ? ` ${structureRights}` : ""}
{structureLegs > 0 ? ` ${structureLegs}L` : ""}
{structureStrikes > 0 ? ` ${structureStrikes}K` : ""}
</span>
) : null}
{Number.isFinite(aggressiveCoverage) && aggressiveCoverage > 0 ? (
<span className="pill aggressor-tag">
Agg {formatPct(aggressiveBuyRatio)} / {formatPct(aggressiveSellRatio)}
{Number.isFinite(insideRatio) && insideRatio > 0
? ` · In ${formatPct(insideRatio)}`
: ""}
{` · ${formatPct(aggressiveCoverage)} cov`}
</span>
) : null}
{Number.isFinite(nbboBid) && Number.isFinite(nbboAsk) ? (
<span>
NBBO ${formatPrice(nbboBid)} x ${formatPrice(nbboAsk)}
</span>
) : null}
{Number.isFinite(nbboMid) ? <span>Mid ${formatPrice(nbboMid)}</span> : null}
{Number.isFinite(nbboSpread) ? (
<span>Spread ${formatPrice(nbboSpread)}</span>
) : null}
{Number.isFinite(nbboAge) ? <span>{Math.round(nbboAge)}ms</span> : null}
{nbboStale ? <span className="pill nbbo-stale">NBBO stale</span> : null}
{nbboMissing ? <span className="pill nbbo-missing">NBBO missing</span> : null}
</div>
</div>
<div className="time">
{formatTime(startTs)} {formatTime(endTs)}
</div>
<div className={`data-table-row data-table-row-flow${nbboStale || nbboMissing ? " data-table-row-warn" : ""}`} key={packet.id}>
<span className="data-table-cell data-table-cell-number">{formatTime(startTs)} {formatTime(endTs)}</span>
<span className="data-table-cell">{contract}</span>
<span className="data-table-cell data-table-cell-number">{formatFlowMetric(count)}</span>
<span className="data-table-cell data-table-cell-number">{formatFlowMetric(totalSize)}</span>
<span className="data-table-cell data-table-cell-number">${formatUsd(notional)}</span>
<span className="data-table-cell data-table-cell-number">{windowMs > 0 ? formatFlowMetric(windowMs, "ms") : "--"}</span>
<span className="data-table-cell">{structureLabel}</span>
<span className="data-table-cell data-table-cell-number">{nbboLabel}</span>
<span className="data-table-cell">{qualityLabel || "--"}</span>
</div>
);
})}
{virtual.bottomSpacerHeight > 0 ? (
<div style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
) : null}
{!limit ? <LoadOlderControl channel="flow" /> : null}
</>
</div>
</div>
)}
{!limit ? <LoadOlderControl channel="flow" /> : null}
</div>
</Pane>
);
@ -5705,7 +5706,7 @@ type AlertsPaneProps = {
const AlertsPane = ({ limit, withStrip = false, className }: AlertsPaneProps) => {
const state = useTerminal();
const items = limit ? state.filteredAlerts.slice(0, limit) : state.filteredAlerts;
const virtual = useVirtualList(items, state.alertsScroll.listRef, !limit, 92);
const virtual = useVirtualList(items, state.alertsScroll.listRef, !limit, 46);
return (
<Pane
@ -5733,7 +5734,7 @@ const AlertsPane = ({ limit, withStrip = false, className }: AlertsPaneProps) =>
}
>
{withStrip ? <AlertSeverityStrip alerts={state.filteredAlerts} /> : null}
<div className="list terminal-list" ref={state.alertsScroll.listRef}>
<div className="data-table-shell">
{items.length === 0 ? (
<div className="empty">
{state.tickerSet.size > 0
@ -5743,9 +5744,19 @@ const AlertsPane = ({ limit, withStrip = false, className }: AlertsPaneProps) =>
: "Replay queue empty. Ensure ClickHouse has data."}
</div>
) : (
<>
<div className="data-table-wrap" ref={state.alertsScroll.listRef}>
<div className="data-table data-table-alerts" role="table" aria-label="Alerts">
<div className="data-table-head" role="row">
<span className="data-table-cell">TIME</span>
<span className="data-table-cell">ALERT</span>
<span className="data-table-cell">SEV</span>
<span className="data-table-cell">SCORE</span>
<span className="data-table-cell">HITS</span>
<span className="data-table-cell">DIR</span>
<span className="data-table-cell">NOTE</span>
</div>
{virtual.topSpacerHeight > 0 ? (
<div style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
) : null}
{virtual.visibleItems.map((alert) => {
const primary = alert.hits[0];
@ -5754,7 +5765,7 @@ const AlertsPane = ({ limit, withStrip = false, className }: AlertsPaneProps) =>
return (
<button
className="row row-button"
className={`data-table-row data-table-row-button data-table-row-alerts data-table-row-severity-${severity}`}
key={`${alert.trace_id}-${alert.seq}`}
type="button"
onClick={() => {
@ -5763,30 +5774,23 @@ const AlertsPane = ({ limit, withStrip = false, className }: AlertsPaneProps) =>
state.setSelectedAlert(alert);
}}
>
<div>
<div className="contract">
{primary ? humanizeClassifierId(primary.classifier_id) : "Alert"}
</div>
<div className="meta">
<span className={`pill severity-${severity}`}>{severity}</span>
<span>Score {Math.round(alert.score)}</span>
<span>{alert.hits.length} hits</span>
<span className={`pill direction-${direction}`}>{direction}</span>
</div>
{primary?.explanations?.[0] ? (
<div className="note">{primary.explanations[0]}</div>
) : null}
</div>
<div className="time">{formatTime(alert.source_ts)}</div>
<span className="data-table-cell data-table-cell-number">{formatTime(alert.source_ts)}</span>
<span className="data-table-cell">{primary ? humanizeClassifierId(primary.classifier_id) : "Alert"}</span>
<span className="data-table-cell">{severity}</span>
<span className="data-table-cell data-table-cell-number">{Math.round(alert.score)}</span>
<span className="data-table-cell data-table-cell-number">{alert.hits.length}</span>
<span className="data-table-cell">{direction}</span>
<span className="data-table-cell">{primary?.explanations?.[0] ?? "--"}</span>
</button>
);
})}
{virtual.bottomSpacerHeight > 0 ? (
<div style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
) : null}
{!limit ? <LoadOlderControl channel="alerts" /> : null}
</>
</div>
</div>
)}
{!limit ? <LoadOlderControl channel="alerts" /> : null}
</div>
</Pane>
);
@ -5800,7 +5804,7 @@ type ClassifierPaneProps = {
const ClassifierPane = ({ limit, className }: ClassifierPaneProps) => {
const state = useTerminal();
const items = limit ? state.filteredClassifierHits.slice(0, limit) : state.filteredClassifierHits;
const virtual = useVirtualList(items, state.classifierScroll.listRef, !limit, 88);
const virtual = useVirtualList(items, state.classifierScroll.listRef, !limit, 44);
return (
<Pane
@ -5827,7 +5831,7 @@ const ClassifierPane = ({ limit, className }: ClassifierPaneProps) => {
/>
}
>
<div className="list terminal-list" ref={state.classifierScroll.listRef}>
<div className="data-table-shell">
{items.length === 0 ? (
<div className="empty">
{state.tickerSet.size > 0
@ -5837,37 +5841,42 @@ const ClassifierPane = ({ limit, className }: ClassifierPaneProps) => {
: "Replay queue empty. Ensure ClickHouse has data."}
</div>
) : (
<>
<div className="data-table-wrap" ref={state.classifierScroll.listRef}>
<div className="data-table data-table-classifier" role="table" aria-label="Classifier hits">
<div className="data-table-head" role="row">
<span className="data-table-cell">TIME</span>
<span className="data-table-cell">RULE</span>
<span className="data-table-cell">DIR</span>
<span className="data-table-cell">CONF</span>
<span className="data-table-cell">NOTE</span>
</div>
{virtual.topSpacerHeight > 0 ? (
<div style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
) : null}
{virtual.visibleItems.map((hit) => {
const direction = normalizeDirection(hit.direction);
return (
<button
className="row row-button"
className={`data-table-row data-table-row-button data-table-row-classifier data-table-row-direction-${direction}`}
key={`${hit.trace_id}-${hit.seq}`}
type="button"
onClick={() => state.openFromClassifierHit(hit)}
>
<div>
<div className="contract">{humanizeClassifierId(hit.classifier_id)}</div>
<div className="meta">
<span className={`pill direction-${direction}`}>{direction}</span>
<span>Confidence {formatConfidence(hit.confidence)}</span>
</div>
{hit.explanations?.[0] ? <div className="note">{hit.explanations[0]}</div> : null}
</div>
<div className="time">{formatTime(hit.source_ts)}</div>
<span className="data-table-cell data-table-cell-number">{formatTime(hit.source_ts)}</span>
<span className="data-table-cell">{humanizeClassifierId(hit.classifier_id)}</span>
<span className="data-table-cell">{direction}</span>
<span className="data-table-cell data-table-cell-number">{formatConfidence(hit.confidence)}</span>
<span className="data-table-cell">{hit.explanations?.[0] ?? "--"}</span>
</button>
);
})}
{virtual.bottomSpacerHeight > 0 ? (
<div style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
) : null}
{!limit ? <LoadOlderControl channel="classifier-hits" /> : null}
</>
</div>
</div>
)}
{!limit ? <LoadOlderControl channel="classifier-hits" /> : null}
</div>
</Pane>
);
@ -5881,7 +5890,7 @@ type DarkPaneProps = {
const DarkPane = ({ limit, className }: DarkPaneProps) => {
const state = useTerminal();
const items = limit ? state.filteredInferredDark.slice(0, limit) : state.filteredInferredDark;
const virtual = useVirtualList(items, state.darkScroll.listRef, !limit, 88);
const virtual = useVirtualList(items, state.darkScroll.listRef, !limit, 44);
return (
<Pane
@ -5908,7 +5917,7 @@ const DarkPane = ({ limit, className }: DarkPaneProps) => {
/>
}
>
<div className="list terminal-list" ref={state.darkScroll.listRef}>
<div className="data-table-shell">
{items.length === 0 ? (
<div className="empty">
{state.tickerSet.size > 0
@ -5918,9 +5927,18 @@ const DarkPane = ({ limit, className }: DarkPaneProps) => {
: "Replay queue empty. Ensure ClickHouse has data."}
</div>
) : (
<>
<div className="data-table-wrap" ref={state.darkScroll.listRef}>
<div className="data-table data-table-dark" role="table" aria-label="Dark events">
<div className="data-table-head" role="row">
<span className="data-table-cell">TIME</span>
<span className="data-table-cell">TYPE</span>
<span className="data-table-cell">SYM</span>
<span className="data-table-cell">CONF</span>
<span className="data-table-cell">EVIDENCE</span>
<span className="data-table-cell">NOTE</span>
</div>
{virtual.topSpacerHeight > 0 ? (
<div style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.topSpacerHeight}px` }} aria-hidden />
) : null}
{virtual.visibleItems.map((event) => {
const underlying = inferDarkUnderlying(event, state.equityPrintMap, state.equityJoinMap);
@ -5928,7 +5946,7 @@ const DarkPane = ({ limit, className }: DarkPaneProps) => {
return (
<button
className="row row-button"
className="data-table-row data-table-row-button data-table-row-dark"
key={`${event.trace_id}-${event.seq}`}
type="button"
onClick={() => {
@ -5937,25 +5955,22 @@ const DarkPane = ({ limit, className }: DarkPaneProps) => {
state.setSelectedDarkEvent(event);
}}
>
<div>
<div className="contract">{humanizeClassifierId(event.type)}</div>
<div className="meta">
{underlying ? <span>{underlying}</span> : <span className="pill">Unknown</span>}
<span>Confidence {formatConfidence(event.confidence)}</span>
<span>Evidence {evidenceCount}</span>
</div>
{underlying ? null : <div className="note">Underlying not in current equity cache.</div>}
</div>
<div className="time">{formatTime(event.source_ts)}</div>
<span className="data-table-cell data-table-cell-number">{formatTime(event.source_ts)}</span>
<span className="data-table-cell">{humanizeClassifierId(event.type)}</span>
<span className="data-table-cell">{underlying ?? "Unknown"}</span>
<span className="data-table-cell data-table-cell-number">{formatConfidence(event.confidence)}</span>
<span className="data-table-cell data-table-cell-number">{evidenceCount}</span>
<span className="data-table-cell">{underlying ? "--" : "Underlying not in current equity cache."}</span>
</button>
);
})}
{virtual.bottomSpacerHeight > 0 ? (
<div style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
<div className="data-table-spacer" style={{ height: `${virtual.bottomSpacerHeight}px` }} aria-hidden />
) : null}
{!limit ? <LoadOlderControl channel="inferred-dark" /> : null}
</>
</div>
</div>
)}
{!limit ? <LoadOlderControl channel="inferred-dark" /> : null}
</div>
</Pane>
);