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:
parent
bb1df9b58b
commit
9c351d12d1
5 changed files with 955 additions and 582 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
6
deploy-branch.sh
Executable 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
7
deploy.sh
Executable 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue