This commit is contained in:
parent
65139bf8d0
commit
44431c4e66
71 changed files with 2262 additions and 1173 deletions
|
|
@ -9,11 +9,8 @@ export async function GET(): Promise<Response> {
|
|||
}
|
||||
|
||||
export async function PUT(req: Request): Promise<Response> {
|
||||
return proxySyntheticAdminRequest(
|
||||
"/admin/synthetic/control",
|
||||
{
|
||||
method: "PUT",
|
||||
body: await req.text()
|
||||
}
|
||||
);
|
||||
return proxySyntheticAdminRequest("/admin/synthetic/control", {
|
||||
method: "PUT",
|
||||
body: await req.text()
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
|
||||
import {
|
||||
getSyntheticAdminProxyConfig,
|
||||
isSyntheticAdminFeatureEnabled
|
||||
} from "./shared";
|
||||
import { getSyntheticAdminProxyConfig, isSyntheticAdminFeatureEnabled } from "./shared";
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,25 +18,29 @@ const variants: Record<
|
|||
> = {
|
||||
mock1: {
|
||||
title: "Command Deck",
|
||||
premise: "Closest to the reference: left navigation, ticker ribbon, dense evidence panes, replay rail.",
|
||||
premise:
|
||||
"Closest to the reference: left navigation, ticker ribbon, dense evidence panes, replay rail.",
|
||||
mode: "Dense ops",
|
||||
layout: "classic"
|
||||
},
|
||||
mock2: {
|
||||
title: "Investigation Stack",
|
||||
premise: "A calmer analyst layout with the selected symbol story in the center and context wrapped around it.",
|
||||
premise:
|
||||
"A calmer analyst layout with the selected symbol story in the center and context wrapped around it.",
|
||||
mode: "Forensic",
|
||||
layout: "focus"
|
||||
},
|
||||
mock3: {
|
||||
title: "Signal Wall",
|
||||
premise: "Prioritizes alert triage and cross-symbol scanning before a user drills into price action.",
|
||||
premise:
|
||||
"Prioritizes alert triage and cross-symbol scanning before a user drills into price action.",
|
||||
mode: "Triage",
|
||||
layout: "signals"
|
||||
},
|
||||
mock4: {
|
||||
title: "Replay Lab",
|
||||
premise: "A replay-first structure with timeline, event tape, and causality context always visible.",
|
||||
premise:
|
||||
"A replay-first structure with timeline, event tape, and causality context always visible.",
|
||||
mode: "Replay",
|
||||
layout: "replay"
|
||||
}
|
||||
|
|
@ -93,7 +97,10 @@ export function DashboardMock({ variant }: DashboardMockProps) {
|
|||
const config = variants[variant];
|
||||
|
||||
return (
|
||||
<section className={`mock-terminal mock-terminal-${config.layout}`} aria-labelledby="mock-title">
|
||||
<section
|
||||
className={`mock-terminal mock-terminal-${config.layout}`}
|
||||
aria-labelledby="mock-title"
|
||||
>
|
||||
<MockHeader config={config} active={variant} />
|
||||
<TickerRail />
|
||||
{variant === "mock1" ? <ClassicLayout /> : null}
|
||||
|
|
@ -277,7 +284,11 @@ function OptionTape({ condensed = false }: { condensed?: boolean }) {
|
|||
|
||||
function ChartPanel({ compact = false }: { compact?: boolean }) {
|
||||
return (
|
||||
<Panel title="AAPL | Price & Flow" meta="1m / 5m / 15m" className={compact ? "mock-chart is-compact" : "mock-chart"}>
|
||||
<Panel
|
||||
title="AAPL | Price & Flow"
|
||||
meta="1m / 5m / 15m"
|
||||
className={compact ? "mock-chart is-compact" : "mock-chart"}
|
||||
>
|
||||
<div className="mock-chart-meta">
|
||||
<strong>194.88</strong>
|
||||
<span className="mock-move is-up">+2.34 (+1.22%)</span>
|
||||
|
|
@ -306,16 +317,24 @@ function ChartPanel({ compact = false }: { compact?: boolean }) {
|
|||
|
||||
function SignalPanel({ hero = false }: { hero?: boolean }) {
|
||||
return (
|
||||
<Panel title="Signals & Alerts" meta="All / Signals / System" className={hero ? "mock-signals is-hero" : "mock-signals"}>
|
||||
<Panel
|
||||
title="Signals & Alerts"
|
||||
meta="All / Signals / System"
|
||||
className={hero ? "mock-signals is-hero" : "mock-signals"}
|
||||
>
|
||||
<div className="mock-signal-list">
|
||||
{signals.map(([time, title, symbol, value, tag]) => (
|
||||
<article className="mock-signal-item" key={`${time}-${title}`}>
|
||||
<time>{time}</time>
|
||||
<div>
|
||||
<strong>{title}</strong>
|
||||
<span>{symbol} / {value}</span>
|
||||
<span>
|
||||
{symbol} / {value}
|
||||
</span>
|
||||
</div>
|
||||
<span className={`mock-pill ${tag === "Bearish" ? "is-bearish" : tag === "News" ? "is-news" : "is-bullish"}`}>
|
||||
<span
|
||||
className={`mock-pill ${tag === "Bearish" ? "is-bearish" : tag === "News" ? "is-news" : "is-bullish"}`}
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
</article>
|
||||
|
|
@ -332,7 +351,9 @@ function FeedHealth() {
|
|||
{feedHealth.map(([feed, status, lag, rate]) => (
|
||||
<div className="mock-table-row" key={feed}>
|
||||
<span>{feed}</span>
|
||||
<span className={`mock-pill ${status === "Degraded" ? "is-warning" : "is-bullish"}`}>{status}</span>
|
||||
<span className={`mock-pill ${status === "Degraded" ? "is-warning" : "is-bullish"}`}>
|
||||
{status}
|
||||
</span>
|
||||
<span>{lag}</span>
|
||||
<span>{rate}/s</span>
|
||||
</div>
|
||||
|
|
@ -350,7 +371,9 @@ function DarkFlow() {
|
|||
<div className="mock-table-row" key={`${time}-${side}-${size}`}>
|
||||
<span>{time}</span>
|
||||
<strong>{symbol}</strong>
|
||||
<span className={`mock-pill ${side === "Sell" ? "is-bearish" : "is-bullish"}`}>{side}</span>
|
||||
<span className={`mock-pill ${side === "Sell" ? "is-bearish" : "is-bullish"}`}>
|
||||
{side}
|
||||
</span>
|
||||
<span>{size}</span>
|
||||
<span>{notional}</span>
|
||||
<span>{type}</span>
|
||||
|
|
@ -402,7 +425,11 @@ function EventContext() {
|
|||
|
||||
function ReplayRail({ compact = false }: { compact?: boolean }) {
|
||||
return (
|
||||
<Panel title="Replay" meta="May 16, 2024" className={compact ? "mock-replay is-compact" : "mock-replay"}>
|
||||
<Panel
|
||||
title="Replay"
|
||||
meta="May 16, 2024"
|
||||
className={compact ? "mock-replay is-compact" : "mock-replay"}
|
||||
>
|
||||
<div className="mock-replay-controls">
|
||||
<button type="button">Prev</button>
|
||||
<button type="button">Pause</button>
|
||||
|
|
@ -430,8 +457,9 @@ function SymbolBrief() {
|
|||
<span className="mock-move is-up">+1.22%</span>
|
||||
</div>
|
||||
<p>
|
||||
Dark sweep pressure aligns with short-window momentum and a fresh news catalyst. Context confidence is high, but
|
||||
the largest block remains off-exchange and should be checked against next print behavior.
|
||||
Dark sweep pressure aligns with short-window momentum and a fresh news catalyst. Context
|
||||
confidence is high, but the largest block remains off-exchange and should be checked against
|
||||
next print behavior.
|
||||
</p>
|
||||
<div className="mock-brief-tags">
|
||||
<span className="mock-pill is-bullish">Bullish</span>
|
||||
|
|
@ -444,7 +472,12 @@ function SymbolBrief() {
|
|||
|
||||
function Sparkline({ direction }: { direction: string }) {
|
||||
return (
|
||||
<svg className="mock-sparkline" viewBox="0 0 96 28" role="img" aria-label={`${direction} sparkline`}>
|
||||
<svg
|
||||
className="mock-sparkline"
|
||||
viewBox="0 0 96 28"
|
||||
role="img"
|
||||
aria-label={`${direction} sparkline`}
|
||||
>
|
||||
<polyline
|
||||
fill="none"
|
||||
points={
|
||||
|
|
|
|||
|
|
@ -161,7 +161,10 @@ input {
|
|||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
font-size: 0.76rem;
|
||||
transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
|
||||
transition:
|
||||
border-color 0.15s ease,
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease;
|
||||
}
|
||||
|
||||
.terminal-nav-link:hover {
|
||||
|
|
@ -800,8 +803,7 @@ h3 {
|
|||
border: 1px solid var(--border-strong);
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(135deg, oklch(0.78 0.12 74 / 0.7), oklch(0.28 0.035 250)),
|
||||
var(--accent-soft);
|
||||
linear-gradient(135deg, oklch(0.78 0.12 74 / 0.7), oklch(0.28 0.035 250)), var(--accent-soft);
|
||||
}
|
||||
|
||||
.command-deck-kicker,
|
||||
|
|
@ -1608,19 +1610,31 @@ h3 {
|
|||
|
||||
.data-table-row-classified {
|
||||
background:
|
||||
linear-gradient(90deg, rgba(var(--classifier-rgb, 192, 200, 210), calc(0.012 + var(--classifier-intensity, 0) * 0.06)), transparent 62%),
|
||||
linear-gradient(
|
||||
90deg,
|
||||
rgba(
|
||||
var(--classifier-rgb, 192, 200, 210),
|
||||
calc(0.012 + var(--classifier-intensity, 0) * 0.06)
|
||||
),
|
||||
transparent 62%
|
||||
),
|
||||
oklch(0.98 0.008 250 / 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.02 + var(--classifier-intensity, 0) * 0.1)), transparent 68%),
|
||||
linear-gradient(
|
||||
90deg,
|
||||
rgba(var(--classifier-rgb, 192, 200, 210), calc(0.02 + var(--classifier-intensity, 0) * 0.1)),
|
||||
transparent 68%
|
||||
),
|
||||
oklch(0.78 0.12 74 / 0.035);
|
||||
}
|
||||
|
||||
.data-table-row-classified.is-classified {
|
||||
box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.16 + var(--classifier-intensity) * 0.12));
|
||||
box-shadow: inset 0 0 0 1px
|
||||
rgba(var(--classifier-rgb), calc(0.16 + var(--classifier-intensity) * 0.12));
|
||||
}
|
||||
|
||||
.data-table-row-warn,
|
||||
|
|
@ -1641,32 +1655,56 @@ h3 {
|
|||
|
||||
.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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 {
|
||||
|
|
@ -1698,7 +1736,13 @@ h3 {
|
|||
.options-table-head,
|
||||
.options-table-row {
|
||||
display: grid;
|
||||
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);
|
||||
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);
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
|
@ -1729,7 +1773,14 @@ h3 {
|
|||
border: 0;
|
||||
border-bottom: 1px solid oklch(0.72 0.012 250 / 0.08);
|
||||
background:
|
||||
linear-gradient(90deg, rgba(var(--classifier-rgb, 192, 200, 210), calc(0.012 + var(--classifier-intensity, 0) * 0.06)), transparent 62%),
|
||||
linear-gradient(
|
||||
90deg,
|
||||
rgba(
|
||||
var(--classifier-rgb, 192, 200, 210),
|
||||
calc(0.012 + var(--classifier-intensity, 0) * 0.06)
|
||||
),
|
||||
transparent 62%
|
||||
),
|
||||
oklch(0.98 0.008 250 / 0.012);
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
|
|
@ -1740,13 +1791,18 @@ h3 {
|
|||
.options-table-row:focus-visible {
|
||||
outline: none;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(var(--classifier-rgb, 192, 200, 210), calc(0.02 + var(--classifier-intensity, 0) * 0.1)), transparent 68%),
|
||||
linear-gradient(
|
||||
90deg,
|
||||
rgba(var(--classifier-rgb, 192, 200, 210), calc(0.02 + var(--classifier-intensity, 0) * 0.1)),
|
||||
transparent 68%
|
||||
),
|
||||
oklch(0.78 0.12 74 / 0.03);
|
||||
}
|
||||
|
||||
.options-table-row.is-classified {
|
||||
cursor: pointer;
|
||||
box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.16 + var(--classifier-intensity) * 0.12));
|
||||
box-shadow: inset 0 0 0 1px
|
||||
rgba(var(--classifier-rgb), calc(0.16 + var(--classifier-intensity) * 0.12));
|
||||
}
|
||||
|
||||
.options-table-row > span {
|
||||
|
|
@ -1761,17 +1817,39 @@ h3 {
|
|||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.classifier-green { --classifier-rgb: 37, 193, 122; }
|
||||
.classifier-red { --classifier-rgb: 255, 107, 95; }
|
||||
.classifier-amber { --classifier-rgb: 245, 166, 35; }
|
||||
.classifier-copper { --classifier-rgb: 198, 122, 75; }
|
||||
.classifier-blue { --classifier-rgb: 77, 163, 255; }
|
||||
.classifier-teal { --classifier-rgb: 64, 210, 190; }
|
||||
.classifier-yellowgreen { --classifier-rgb: 174, 210, 78; }
|
||||
.classifier-violet { --classifier-rgb: 170, 130, 255; }
|
||||
.classifier-cyan { --classifier-rgb: 94, 214, 255; }
|
||||
.classifier-magenta { --classifier-rgb: 255, 92, 205; }
|
||||
.classifier-neutral { --classifier-rgb: 192, 200, 210; }
|
||||
.classifier-green {
|
||||
--classifier-rgb: 37, 193, 122;
|
||||
}
|
||||
.classifier-red {
|
||||
--classifier-rgb: 255, 107, 95;
|
||||
}
|
||||
.classifier-amber {
|
||||
--classifier-rgb: 245, 166, 35;
|
||||
}
|
||||
.classifier-copper {
|
||||
--classifier-rgb: 198, 122, 75;
|
||||
}
|
||||
.classifier-blue {
|
||||
--classifier-rgb: 77, 163, 255;
|
||||
}
|
||||
.classifier-teal {
|
||||
--classifier-rgb: 64, 210, 190;
|
||||
}
|
||||
.classifier-yellowgreen {
|
||||
--classifier-rgb: 174, 210, 78;
|
||||
}
|
||||
.classifier-violet {
|
||||
--classifier-rgb: 170, 130, 255;
|
||||
}
|
||||
.classifier-cyan {
|
||||
--classifier-rgb: 94, 214, 255;
|
||||
}
|
||||
.classifier-magenta {
|
||||
--classifier-rgb: 255, 92, 205;
|
||||
}
|
||||
.classifier-neutral {
|
||||
--classifier-rgb: 192, 200, 210;
|
||||
}
|
||||
|
||||
.contract,
|
||||
.drawer-row-title {
|
||||
|
|
@ -1921,7 +1999,9 @@ h3 {
|
|||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(8px);
|
||||
transition: opacity 0.15s ease, transform 0.15s ease;
|
||||
transition:
|
||||
opacity 0.15s ease,
|
||||
transform 0.15s ease;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
|
|
@ -2047,7 +2127,10 @@ h3 {
|
|||
color: var(--text-dim);
|
||||
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
|
||||
z-index: 45;
|
||||
transition: border-color 0.16s ease, background-color 0.16s ease, color 0.16s ease;
|
||||
transition:
|
||||
border-color 0.16s ease,
|
||||
background-color 0.16s ease,
|
||||
color 0.16s ease;
|
||||
}
|
||||
|
||||
.synthetic-control-gear:hover,
|
||||
|
|
@ -2213,7 +2296,9 @@ h3 {
|
|||
background: oklch(0.18 0.012 250 / 0.6);
|
||||
color: var(--text);
|
||||
text-align: left;
|
||||
transition: border-color 150ms ease, background 150ms ease;
|
||||
transition:
|
||||
border-color 150ms ease,
|
||||
background 150ms ease;
|
||||
}
|
||||
|
||||
.news-row:hover {
|
||||
|
|
@ -2520,7 +2605,11 @@ h3 {
|
|||
|
||||
@media (max-width: 720px) {
|
||||
.terminal-shell {
|
||||
background-size: 24px 24px, 24px 24px, 100% 100%, auto;
|
||||
background-size:
|
||||
24px 24px,
|
||||
24px 24px,
|
||||
100% 100%,
|
||||
auto;
|
||||
}
|
||||
|
||||
.terminal-nav-drawer {
|
||||
|
|
@ -2877,9 +2966,7 @@ h3 {
|
|||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 9px;
|
||||
background:
|
||||
linear-gradient(135deg, oklch(0.68 0.14 246), oklch(0.68 0.12 164)),
|
||||
var(--blue-soft);
|
||||
background: linear-gradient(135deg, oklch(0.68 0.14 246), oklch(0.68 0.12 164)), var(--blue-soft);
|
||||
box-shadow: inset 0 0 0 1px oklch(0.94 0.02 240 / 0.24);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -311,12 +311,16 @@ describe("live manifest", () => {
|
|||
});
|
||||
|
||||
it("includes news subscriptions on home and /news", () => {
|
||||
expect(getLiveManifest("/", "SPY", 60000, buildDefaultFlowFilters()).map((subscription) => subscription.channel)).toContain(
|
||||
"news"
|
||||
);
|
||||
expect(getLiveManifest("/news", "SPY", 60000, buildDefaultFlowFilters()).map((subscription) => subscription.channel)).toEqual([
|
||||
"news"
|
||||
]);
|
||||
expect(
|
||||
getLiveManifest("/", "SPY", 60000, buildDefaultFlowFilters()).map(
|
||||
(subscription) => subscription.channel
|
||||
)
|
||||
).toContain("news");
|
||||
expect(
|
||||
getLiveManifest("/news", "SPY", 60000, buildDefaultFlowFilters()).map(
|
||||
(subscription) => subscription.channel
|
||||
)
|
||||
).toEqual(["news"]);
|
||||
});
|
||||
|
||||
it("scopes /charts subscriptions to chart channels only", () => {
|
||||
|
|
@ -520,12 +524,36 @@ describe("route feature map", () => {
|
|||
|
||||
describe("fixed tape virtualization config", () => {
|
||||
it("uses expected fixed row heights and overscan by table", () => {
|
||||
expect(getTapeVirtualConfig("options")).toEqual({ rowHeight: 36, overscan: 44, debugLabel: "options" });
|
||||
expect(getTapeVirtualConfig("equities")).toEqual({ rowHeight: 36, overscan: 36, debugLabel: "equities" });
|
||||
expect(getTapeVirtualConfig("flow")).toEqual({ rowHeight: 44, overscan: 24, debugLabel: "flow" });
|
||||
expect(getTapeVirtualConfig("alerts")).toEqual({ rowHeight: 44, overscan: 24, debugLabel: "alerts" });
|
||||
expect(getTapeVirtualConfig("classifier")).toEqual({ rowHeight: 44, overscan: 24, debugLabel: "classifier" });
|
||||
expect(getTapeVirtualConfig("dark")).toEqual({ rowHeight: 44, overscan: 24, debugLabel: "dark" });
|
||||
expect(getTapeVirtualConfig("options")).toEqual({
|
||||
rowHeight: 36,
|
||||
overscan: 44,
|
||||
debugLabel: "options"
|
||||
});
|
||||
expect(getTapeVirtualConfig("equities")).toEqual({
|
||||
rowHeight: 36,
|
||||
overscan: 36,
|
||||
debugLabel: "equities"
|
||||
});
|
||||
expect(getTapeVirtualConfig("flow")).toEqual({
|
||||
rowHeight: 44,
|
||||
overscan: 24,
|
||||
debugLabel: "flow"
|
||||
});
|
||||
expect(getTapeVirtualConfig("alerts")).toEqual({
|
||||
rowHeight: 44,
|
||||
overscan: 24,
|
||||
debugLabel: "alerts"
|
||||
});
|
||||
expect(getTapeVirtualConfig("classifier")).toEqual({
|
||||
rowHeight: 44,
|
||||
overscan: 24,
|
||||
debugLabel: "classifier"
|
||||
});
|
||||
expect(getTapeVirtualConfig("dark")).toEqual({
|
||||
rowHeight: 44,
|
||||
overscan: 24,
|
||||
debugLabel: "dark"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -712,7 +740,11 @@ describe("live tape history helpers", () => {
|
|||
});
|
||||
|
||||
it("promotes hot-window overflow into the history tail", () => {
|
||||
const currentHot = [makeItem("hot-3", 3, 300), makeItem("hot-2", 2, 200), makeItem("hot-1", 1, 100)];
|
||||
const currentHot = [
|
||||
makeItem("hot-3", 3, 300),
|
||||
makeItem("hot-2", 2, 200),
|
||||
makeItem("hot-1", 1, 100)
|
||||
];
|
||||
const incoming = [makeItem("hot-4", 4, 400)];
|
||||
|
||||
const { kept, evicted } = mergeNewestWithOverflow(incoming, currentHot, 3);
|
||||
|
|
@ -727,7 +759,11 @@ describe("live tape history helpers", () => {
|
|||
let history: Array<ReturnType<typeof makeItem>> = [];
|
||||
|
||||
for (let seq = 1; seq <= 5; seq += 1) {
|
||||
const { kept, evicted } = mergeNewestWithOverflow([makeItem(`row-${seq}`, seq, seq * 100)], hot, 2);
|
||||
const { kept, evicted } = mergeNewestWithOverflow(
|
||||
[makeItem(`row-${seq}`, seq, seq * 100)],
|
||||
hot,
|
||||
2
|
||||
);
|
||||
hot = kept;
|
||||
history = appendHistoryTail(history, evicted, hot, 5000);
|
||||
}
|
||||
|
|
@ -762,13 +798,24 @@ describe("live tape history helpers", () => {
|
|||
});
|
||||
|
||||
it("dedupes the seam between promoted overflow and fetched history", () => {
|
||||
const currentHot = [makeItem("hot-3", 3, 300), makeItem("hot-2", 2, 200), makeItem("hot-1", 1, 100)];
|
||||
const currentHot = [
|
||||
makeItem("hot-3", 3, 300),
|
||||
makeItem("hot-2", 2, 200),
|
||||
makeItem("hot-1", 1, 100)
|
||||
];
|
||||
const { kept, evicted } = mergeNewestWithOverflow([makeItem("hot-4", 4, 400)], currentHot, 3);
|
||||
const promoted = appendHistoryTail([], evicted, kept, 5000);
|
||||
const merged = appendHistoryTail(promoted, [makeItem("hot-1", 1, 100), makeItem("older", 0, 50)], kept, 5000);
|
||||
const merged = appendHistoryTail(
|
||||
promoted,
|
||||
[makeItem("hot-1", 1, 100), makeItem("older", 0, 50)],
|
||||
kept,
|
||||
5000
|
||||
);
|
||||
|
||||
expect(merged.map((item) => item.trace_id)).toEqual(["hot-1", "older"]);
|
||||
expect(new Set([...kept, ...merged].map((item) => item.trace_id)).size).toBe(kept.length + merged.length);
|
||||
expect(new Set([...kept, ...merged].map((item) => item.trace_id)).size).toBe(
|
||||
kept.length + merged.length
|
||||
);
|
||||
});
|
||||
|
||||
it("trims the history tail to the soft cap", () => {
|
||||
|
|
@ -821,10 +868,9 @@ describe("live tape history helpers", () => {
|
|||
makeItem("hist-2", 2, 200)
|
||||
];
|
||||
|
||||
expect(mergeHeldTapeHistory(displayed, incoming, frozenLive).map((item) => item.trace_id)).toEqual([
|
||||
"hist-3",
|
||||
"hist-2"
|
||||
]);
|
||||
expect(
|
||||
mergeHeldTapeHistory(displayed, incoming, frozenLive).map((item) => item.trace_id)
|
||||
).toEqual(["hist-3", "hist-2"]);
|
||||
});
|
||||
|
||||
it("appends truly older lazy-loaded rows to the held history tail", () => {
|
||||
|
|
@ -837,12 +883,9 @@ describe("live tape history helpers", () => {
|
|||
makeItem("older-0", 0, 50)
|
||||
];
|
||||
|
||||
expect(mergeHeldTapeHistory(displayed, incoming, frozenLive).map((item) => item.trace_id)).toEqual([
|
||||
"hist-3",
|
||||
"hist-2",
|
||||
"older-1",
|
||||
"older-0"
|
||||
]);
|
||||
expect(
|
||||
mergeHeldTapeHistory(displayed, incoming, frozenLive).map((item) => item.trace_id)
|
||||
).toEqual(["hist-3", "hist-2", "older-1", "older-0"]);
|
||||
});
|
||||
|
||||
it("resyncs buffered live history by replacing the held segment after resume", () => {
|
||||
|
|
@ -855,7 +898,12 @@ describe("live tape history helpers", () => {
|
|||
const resynced = appendHistoryTail([], [makeItem("overflow-newer", 6, 600), ...held], [], 0);
|
||||
|
||||
expect(held.map((item) => item.trace_id)).toEqual(["hist-3", "hist-2", "older-1"]);
|
||||
expect(resynced.map((item) => item.trace_id)).toEqual(["overflow-newer", "hist-3", "hist-2", "older-1"]);
|
||||
expect(resynced.map((item) => item.trace_id)).toEqual([
|
||||
"overflow-newer",
|
||||
"hist-3",
|
||||
"hist-2",
|
||||
"older-1"
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -935,9 +983,21 @@ describe("classifier row decoration helpers", () => {
|
|||
|
||||
it("selects primary hits by confidence, source timestamp, then seq", () => {
|
||||
const hit = selectPrimaryClassifierHit([
|
||||
{ ...makeAlert({ classifier_id: "old", confidence: 0.9, source_ts: 1_000, seq: 1 }), direction: "bullish", explanations: [] },
|
||||
{ ...makeAlert({ classifier_id: "new", confidence: 0.9, source_ts: 2_000, seq: 1 }), direction: "bullish", explanations: [] },
|
||||
{ ...makeAlert({ classifier_id: "low", confidence: 0.5, source_ts: 3_000, seq: 9 }), direction: "bullish", explanations: [] }
|
||||
{
|
||||
...makeAlert({ classifier_id: "old", confidence: 0.9, source_ts: 1_000, seq: 1 }),
|
||||
direction: "bullish",
|
||||
explanations: []
|
||||
},
|
||||
{
|
||||
...makeAlert({ classifier_id: "new", confidence: 0.9, source_ts: 2_000, seq: 1 }),
|
||||
direction: "bullish",
|
||||
explanations: []
|
||||
},
|
||||
{
|
||||
...makeAlert({ classifier_id: "low", confidence: 0.5, source_ts: 3_000, seq: 9 }),
|
||||
direction: "bullish",
|
||||
explanations: []
|
||||
}
|
||||
]);
|
||||
|
||||
expect(hit?.classifier_id).toBe("new");
|
||||
|
|
@ -1010,9 +1070,9 @@ describe("signals helpers", () => {
|
|||
)
|
||||
).toBe("bearish");
|
||||
|
||||
expect(deriveAlertDirection(makeAlert({ hits: [{ direction: "weird", confidence: 0.4 }] }))).toBe(
|
||||
"neutral"
|
||||
);
|
||||
expect(
|
||||
deriveAlertDirection(makeAlert({ hits: [{ direction: "weird", confidence: 0.4 }] }))
|
||||
).toBe("neutral");
|
||||
expect(deriveAlertDirection(makeAlert({ hits: [] }))).toBe("neutral");
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue