Refine route-scoped tape subscriptions and table virtualization

- Scope live channels by route and trim unused feed work
- Switch tape tables to fixed-height virtual rows with separate scroll containers
- Add tests for route feature maps and virtual config
This commit is contained in:
dirtydishes 2026-05-07 22:03:09 -04:00
parent bb1df9b58b
commit 9c351d12d1
5 changed files with 955 additions and 582 deletions

View file

@ -956,17 +956,27 @@ h3 {
.data-table-wrap { .data-table-wrap {
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
overflow: auto; overflow-x: auto;
overflow-y: hidden;
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
background: rgba(5, 8, 12, 0.42); background: rgba(5, 8, 12, 0.42);
} }
.data-table { .data-table {
display: block; display: flex;
flex-direction: column;
min-height: 0;
min-width: 980px; min-width: 980px;
} }
.data-table-scroll {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
}
.data-table-body { .data-table-body {
position: relative; position: relative;
min-width: 100%; min-width: 100%;
@ -1004,10 +1014,8 @@ h3 {
} }
.data-table-head { .data-table-head {
position: sticky; flex: 0 0 auto;
top: 0; height: 30px;
z-index: 2;
min-height: 30px;
padding: 0 10px; padding: 0 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.095); border-bottom: 1px solid rgba(255, 255, 255, 0.095);
background: rgba(8, 11, 16, 0.98); background: rgba(8, 11, 16, 0.98);
@ -1019,7 +1027,7 @@ h3 {
.data-table-row { .data-table-row {
width: 100%; width: 100%;
min-height: 40px; height: 40px;
padding: 0 10px; padding: 0 10px;
border: 0; border: 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.055); border-bottom: 1px solid rgba(255, 255, 255, 0.055);
@ -1035,6 +1043,7 @@ h3 {
.data-table-virtual-row { .data-table-virtual-row {
position: absolute; position: absolute;
top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
} }
@ -1050,18 +1059,18 @@ h3 {
} }
.data-table-row-options { .data-table-row-options {
min-height: 36px; height: 36px;
} }
.data-table-row-equities { .data-table-row-equities {
min-height: 34px; height: 36px;
} }
.data-table-row-flow, .data-table-row-flow,
.data-table-row-alerts, .data-table-row-alerts,
.data-table-row-classifier, .data-table-row-classifier,
.data-table-row-dark { .data-table-row-dark {
min-height: 44px; height: 44px;
} }
.data-table-row-classified { .data-table-row-classified {

View file

@ -19,12 +19,15 @@ import {
getOptionTableSnapshot, getOptionTableSnapshot,
getLiveFeedStatus, getLiveFeedStatus,
getLiveManifest, getLiveManifest,
getRouteFeatures,
getTapeVirtualConfig,
mergeNewestWithOverflow, mergeNewestWithOverflow,
normalizeAlertSeverity, normalizeAlertSeverity,
nextFlowFilterPopoverState, nextFlowFilterPopoverState,
projectPausableTapeState, projectPausableTapeState,
reducePausableTapeData, reducePausableTapeData,
shouldRetainLiveSnapshotHistory, shouldRetainLiveSnapshotHistory,
shouldIncludeEquitiesForDarkUnderlyingFallback,
shouldShowEquitiesSilentFeedWarning, shouldShowEquitiesSilentFeedWarning,
selectPrimaryClassifierHit, selectPrimaryClassifierHit,
smartMoneyProfileLabel, smartMoneyProfileLabel,
@ -51,15 +54,13 @@ const makeAlert = (overrides: Record<string, unknown> = {}) =>
}) as any; }) as any;
describe("live manifest", () => { describe("live manifest", () => {
it("includes options on home and tape", () => { it("includes only tape channels on /tape", () => {
const filters = buildDefaultFlowFilters(); const filters = buildDefaultFlowFilters();
for (const pathname of ["/", "/tape"]) { const channels = getLiveManifest("/tape", "SPY", 60000, filters).map(
expect( (subscription) => subscription.channel
getLiveManifest(pathname, "SPY", 60000, filters).some( );
(subscription) => subscription.channel === "options"
) expect(channels).toEqual(["options", "nbbo", "equities", "flow"]);
).toBe(true);
}
}); });
it("dedupes tape options subscription", () => { it("dedupes tape options subscription", () => {
@ -72,37 +73,29 @@ describe("live manifest", () => {
expect(tapeOptionsSubscriptions).toHaveLength(1); expect(tapeOptionsSubscriptions).toHaveLength(1);
}); });
it("keeps option filters on baseline subscription across page changes", () => { it("keeps option filters on /tape options subscriptions", () => {
const filters = { const filters = {
...buildDefaultFlowFilters(), ...buildDefaultFlowFilters(),
minNotional: 125_000 minNotional: 125_000
}; };
const homeOptionsSubscription = getLiveManifest("/", "SPY", 60000, filters).find(
(subscription) => subscription.channel === "options"
);
const tapeOptionsSubscription = getLiveManifest("/tape", "SPY", 60000, filters).find( const tapeOptionsSubscription = getLiveManifest("/tape", "SPY", 60000, filters).find(
(subscription) => subscription.channel === "options" (subscription) => subscription.channel === "options"
); );
expect(homeOptionsSubscription?.filters).toBe(filters);
expect(tapeOptionsSubscription?.filters).toBe(filters); expect(tapeOptionsSubscription?.filters).toBe(filters);
}); });
it("applies global flow filters to flow subscriptions on home and tape", () => { it("applies global flow filters to flow subscriptions on /tape", () => {
const filters = { const filters = {
...buildDefaultFlowFilters(), ...buildDefaultFlowFilters(),
minNotional: 50_000 minNotional: 50_000
}; };
const homeFlowSubscription = getLiveManifest("/", "SPY", 60000, filters).find(
(subscription) => subscription.channel === "flow"
);
const tapeFlowSubscription = getLiveManifest("/tape", "SPY", 60000, filters).find( const tapeFlowSubscription = getLiveManifest("/tape", "SPY", 60000, filters).find(
(subscription) => subscription.channel === "flow" (subscription) => subscription.channel === "flow"
); );
expect(homeFlowSubscription?.filters).toBe(filters);
expect(tapeFlowSubscription?.filters).toBe(filters); expect(tapeFlowSubscription?.filters).toBe(filters);
}); });
@ -131,6 +124,90 @@ describe("live manifest", () => {
expect(optionsSubscription?.option_contract_id).toBe("AAPL-2025-01-17-200-C"); expect(optionsSubscription?.option_contract_id).toBe("AAPL-2025-01-17-200-C");
expect(equitiesSubscription?.underlying_ids).toEqual(["AAPL"]); expect(equitiesSubscription?.underlying_ids).toEqual(["AAPL"]);
}); });
it("scopes /signals subscriptions to signals channels only", () => {
const channels = getLiveManifest("/signals", "SPY", 60000, buildDefaultFlowFilters()).map(
(subscription) => subscription.channel
);
expect(channels).toEqual([
"alerts",
"smart-money",
"classifier-hits",
"inferred-dark",
"equity-joins"
]);
});
it("scopes /charts subscriptions to chart channels only", () => {
const channels = getLiveManifest("/charts", "SPY", 60000, buildDefaultFlowFilters()).map(
(subscription) => subscription.channel
);
expect(channels).toEqual([
"smart-money",
"inferred-dark",
"equity-joins",
"equity-candles",
"equity-overlay"
]);
});
});
describe("route feature map", () => {
it("maps /tape to tape panes and dependencies", () => {
const features = getRouteFeatures("/tape");
expect(features.showOptionsPane).toBe(true);
expect(features.showEquitiesPane).toBe(true);
expect(features.showFlowPane).toBe(true);
expect(features.needsClassifierDecor).toBe(true);
expect(features.alerts).toBe(false);
});
it("maps /signals to signal panes and dependencies", () => {
const features = getRouteFeatures("/signals");
expect(features.showAlertsPane).toBe(true);
expect(features.showClassifierPane).toBe(true);
expect(features.showDarkPane).toBe(true);
expect(features.options).toBe(false);
expect(features.equityJoins).toBe(true);
});
it("maps /charts to chart panes and dependencies", () => {
const features = getRouteFeatures("/charts");
expect(features.showChartPane).toBe(true);
expect(features.showFocusPane).toBe(true);
expect(features.equityCandles).toBe(true);
expect(features.equityOverlay).toBe(true);
expect(features.alerts).toBe(false);
});
});
describe("fixed tape virtualization config", () => {
it("uses expected fixed row heights and overscan by table", () => {
expect(getTapeVirtualConfig("options")).toEqual({ rowHeight: 36, overscan: 24, debugLabel: "options" });
expect(getTapeVirtualConfig("equities")).toEqual({ rowHeight: 36, overscan: 20, debugLabel: "equities" });
expect(getTapeVirtualConfig("flow")).toEqual({ rowHeight: 44, overscan: 16, debugLabel: "flow" });
expect(getTapeVirtualConfig("alerts")).toEqual({ rowHeight: 44, overscan: 16, debugLabel: "alerts" });
expect(getTapeVirtualConfig("classifier")).toEqual({ rowHeight: 44, overscan: 16, debugLabel: "classifier" });
expect(getTapeVirtualConfig("dark")).toEqual({ rowHeight: 44, overscan: 16, debugLabel: "dark" });
});
});
describe("dark underlying route dependency helper", () => {
it("does not keep extra equities subscriptions when joins+trace fallback are sufficient", () => {
expect(shouldIncludeEquitiesForDarkUnderlyingFallback()).toBe(false);
expect(
getLiveManifest("/signals", "SPY", 60000, buildDefaultFlowFilters()).some(
(subscription) => subscription.channel === "equities"
)
).toBe(false);
expect(
getLiveManifest("/charts", "SPY", 60000, buildDefaultFlowFilters()).some(
(subscription) => subscription.channel === "equities"
)
).toBe(false);
});
}); });
describe("terminal navigation", () => { describe("terminal navigation", () => {

File diff suppressed because it is too large Load diff

6
deploy-branch.sh Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
git fetch
git pull
docker compose up -d --build --force-recreate

7
deploy.sh Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
git fetch
git switch deployment
git pull
docker compose up -d --build --force-recreate