Refine signals layout and alert labeling
- Rework the Signals grid to prioritize alerts and move dark flow below - Normalize alert severity/direction labels and tighten feed status copy - Add helper tests for severity, direction, anchoring, and status text
This commit is contained in:
parent
d3ff19b5c0
commit
5cdf4a43e0
4 changed files with 191 additions and 32 deletions
|
|
@ -594,8 +594,8 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-grid-signals {
|
.page-grid-signals {
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
|
||||||
grid-template-rows: minmax(0, 1fr);
|
grid-template-rows: minmax(0, 2fr) minmax(0, 1fr);
|
||||||
height: calc(100vh - var(--topbar-height) - 172px);
|
height: calc(100vh - var(--topbar-height) - 172px);
|
||||||
min-height: 620px;
|
min-height: 620px;
|
||||||
}
|
}
|
||||||
|
|
@ -659,7 +659,7 @@ h3 {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-pane-body,
|
.terminal-pane-body,
|
||||||
|
|
@ -774,15 +774,21 @@ h3 {
|
||||||
.tape-controls {
|
.tape-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tape-controls button {
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.missed-count {
|
.missed-count {
|
||||||
min-width: 62px;
|
width: 86px;
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
|
|
@ -815,6 +821,21 @@ h3 {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-grid-signals > .signals-pane-alerts {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-grid-signals > .signals-pane-rules {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-grid-signals > .signals-pane-dark {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
.page-grid-tape > :first-child {
|
.page-grid-tape > :first-child {
|
||||||
height: clamp(460px, 64vh, 880px);
|
height: clamp(460px, 64vh, 880px);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
import { describe, expect, it } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import {
|
import {
|
||||||
buildDefaultFlowFilters,
|
buildDefaultFlowFilters,
|
||||||
|
deriveAlertDirection,
|
||||||
countActiveFlowFilterGroups,
|
countActiveFlowFilterGroups,
|
||||||
flushPausableTapeData,
|
flushPausableTapeData,
|
||||||
|
getAlertWindowAnchorTs,
|
||||||
getLiveFeedStatus,
|
getLiveFeedStatus,
|
||||||
|
normalizeAlertSeverity,
|
||||||
nextFlowFilterPopoverState,
|
nextFlowFilterPopoverState,
|
||||||
projectPausableTapeState,
|
projectPausableTapeState,
|
||||||
reducePausableTapeData,
|
reducePausableTapeData,
|
||||||
shouldRetainLiveSnapshotHistory,
|
shouldRetainLiveSnapshotHistory,
|
||||||
shouldShowEquitiesSilentFeedWarning,
|
shouldShowEquitiesSilentFeedWarning,
|
||||||
|
statusLabel,
|
||||||
toggleFilterValue
|
toggleFilterValue
|
||||||
} from "./terminal";
|
} from "./terminal";
|
||||||
|
|
||||||
|
|
@ -18,6 +22,17 @@ const makeItem = (traceId: string, seq: number, ts: number) => ({
|
||||||
ts
|
ts
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const makeAlert = (overrides: Record<string, unknown> = {}) =>
|
||||||
|
({
|
||||||
|
trace_id: "alert-1",
|
||||||
|
seq: 1,
|
||||||
|
source_ts: 1_000,
|
||||||
|
severity: "low",
|
||||||
|
score: 20,
|
||||||
|
hits: [],
|
||||||
|
...overrides
|
||||||
|
}) as any;
|
||||||
|
|
||||||
describe("live tape pausable helpers", () => {
|
describe("live tape pausable helpers", () => {
|
||||||
it("queues new items while paused and flushes them on resume", () => {
|
it("queues new items while paused and flushes them on resume", () => {
|
||||||
let state = reducePausableTapeData(
|
let state = reducePausableTapeData(
|
||||||
|
|
@ -128,3 +143,58 @@ describe("flow filter popup helpers", () => {
|
||||||
expect(buildDefaultFlowFilters()).toEqual(defaults);
|
expect(buildDefaultFlowFilters()).toEqual(defaults);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("signals helpers", () => {
|
||||||
|
it("normalizes severity aliases/casing and falls back to score", () => {
|
||||||
|
expect(normalizeAlertSeverity(makeAlert({ severity: "HIGH", score: 1 }))).toBe("high");
|
||||||
|
expect(normalizeAlertSeverity(makeAlert({ severity: "med", score: 1 }))).toBe("medium");
|
||||||
|
expect(normalizeAlertSeverity(makeAlert({ severity: "informational", score: 99 }))).toBe("low");
|
||||||
|
expect(normalizeAlertSeverity(makeAlert({ severity: "unknown", score: 80 }))).toBe("high");
|
||||||
|
expect(normalizeAlertSeverity(makeAlert({ severity: "unknown", score: 45 }))).toBe("medium");
|
||||||
|
expect(normalizeAlertSeverity(makeAlert({ severity: "unknown", score: 44 }))).toBe("low");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("derives dominant direction with confidence tie-break and neutral fallback", () => {
|
||||||
|
expect(
|
||||||
|
deriveAlertDirection(
|
||||||
|
makeAlert({
|
||||||
|
hits: [
|
||||||
|
{ direction: "bullish", confidence: 0.4 },
|
||||||
|
{ direction: "bullish", confidence: 0.2 },
|
||||||
|
{ direction: "bearish", confidence: 0.9 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe("bullish");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
deriveAlertDirection(
|
||||||
|
makeAlert({
|
||||||
|
hits: [
|
||||||
|
{ direction: "bullish", confidence: 0.4 },
|
||||||
|
{ direction: "bearish", confidence: 0.9 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).toBe("bearish");
|
||||||
|
|
||||||
|
expect(deriveAlertDirection(makeAlert({ hits: [{ direction: "weird", confidence: 0.4 }] }))).toBe(
|
||||||
|
"neutral"
|
||||||
|
);
|
||||||
|
expect(deriveAlertDirection(makeAlert({ hits: [] }))).toBe("neutral");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("anchors strip window to latest visible alert timestamp", () => {
|
||||||
|
const alerts = [
|
||||||
|
makeAlert({ source_ts: 1_700_000_000_000, severity: "high" }),
|
||||||
|
makeAlert({ source_ts: 1_700_000_000_000 - 10 * 60 * 1000, severity: "low" })
|
||||||
|
];
|
||||||
|
expect(getAlertWindowAnchorTs(alerts, 42)).toBe(1_700_000_000_000);
|
||||||
|
expect(getAlertWindowAnchorTs([], 42)).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns connected/stale live status labels without live wording", () => {
|
||||||
|
expect(statusLabel("connected", false, "live")).toBe("Connected");
|
||||||
|
expect(statusLabel("stale", false, "live")).toBe("Feed behind");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -582,6 +582,66 @@ const normalizeDirection = (value: string): "bullish" | "bearish" | "neutral" =>
|
||||||
return "neutral";
|
return "neutral";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeAlertSeverityValue = (value: string): "high" | "medium" | "low" | null => {
|
||||||
|
const normalized = value.trim().toLowerCase();
|
||||||
|
if (["high", "critical", "severe", "sev1", "p0", "p1"].includes(normalized)) {
|
||||||
|
return "high";
|
||||||
|
}
|
||||||
|
if (["medium", "med", "moderate", "sev2", "p2"].includes(normalized)) {
|
||||||
|
return "medium";
|
||||||
|
}
|
||||||
|
if (["low", "minor", "info", "informational", "sev3", "p3", "p4"].includes(normalized)) {
|
||||||
|
return "low";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const normalizeAlertSeverity = (alert: AlertEvent): "high" | "medium" | "low" => {
|
||||||
|
const normalized = normalizeAlertSeverityValue(alert.severity);
|
||||||
|
if (normalized) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
if (alert.score >= 80) {
|
||||||
|
return "high";
|
||||||
|
}
|
||||||
|
if (alert.score >= 45) {
|
||||||
|
return "medium";
|
||||||
|
}
|
||||||
|
return "low";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deriveAlertDirection = (alert: AlertEvent): "bullish" | "bearish" | "neutral" => {
|
||||||
|
const totals = {
|
||||||
|
bullish: { count: 0, confidence: 0 },
|
||||||
|
bearish: { count: 0, confidence: 0 },
|
||||||
|
neutral: { count: 0, confidence: 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const hit of alert.hits) {
|
||||||
|
const direction = normalizeDirection(hit.direction);
|
||||||
|
totals[direction].count += 1;
|
||||||
|
totals[direction].confidence += Number.isFinite(hit.confidence) ? hit.confidence : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ranked = (Object.entries(totals) as Array<
|
||||||
|
["bullish" | "bearish" | "neutral", { count: number; confidence: number }]
|
||||||
|
>).sort((a, b) => {
|
||||||
|
if (b[1].count !== a[1].count) {
|
||||||
|
return b[1].count - a[1].count;
|
||||||
|
}
|
||||||
|
return b[1].confidence - a[1].confidence;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ranked[0] && ranked[0][1].count > 0 ? ranked[0][0] : "neutral";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAlertWindowAnchorTs = (alerts: AlertEvent[], fallbackNow = Date.now()): number => {
|
||||||
|
if (alerts.length === 0) {
|
||||||
|
return fallbackNow;
|
||||||
|
}
|
||||||
|
return alerts.reduce((max, alert) => Math.max(max, alert.source_ts), alerts[0]?.source_ts ?? fallbackNow);
|
||||||
|
};
|
||||||
|
|
||||||
const extractUnderlying = (contractId: string): string => {
|
const extractUnderlying = (contractId: string): string => {
|
||||||
const match = contractId.match(/^(.+)-\d{4}-\d{2}-\d{2}-/);
|
const match = contractId.match(/^(.+)-\d{4}-\d{2}-\d{2}-/);
|
||||||
if (match?.[1]) {
|
if (match?.[1]) {
|
||||||
|
|
@ -1142,7 +1202,7 @@ const prunePinnedEntries = <T,>(
|
||||||
return new Map(trimmed);
|
return new Map(trimmed);
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusLabel = (status: WsStatus, paused: boolean, mode: TapeMode): string => {
|
export const statusLabel = (status: WsStatus, paused: boolean, mode: TapeMode): string => {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
return "Paused";
|
return "Paused";
|
||||||
}
|
}
|
||||||
|
|
@ -1153,9 +1213,9 @@ const statusLabel = (status: WsStatus, paused: boolean, mode: TapeMode): string
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "connected":
|
case "connected":
|
||||||
return "Live";
|
return "Connected";
|
||||||
case "stale":
|
case "stale":
|
||||||
return "Live feed behind";
|
return "Feed behind";
|
||||||
case "connecting":
|
case "connecting":
|
||||||
return "Connecting";
|
return "Connecting";
|
||||||
case "disconnected":
|
case "disconnected":
|
||||||
|
|
@ -3020,15 +3080,16 @@ type AlertSeverityStripProps = {
|
||||||
|
|
||||||
const AlertSeverityStrip = ({ alerts }: AlertSeverityStripProps) => {
|
const AlertSeverityStrip = ({ alerts }: AlertSeverityStripProps) => {
|
||||||
const windowMs = 30 * 60 * 1000;
|
const windowMs = 30 * 60 * 1000;
|
||||||
const now = Date.now();
|
const windowAnchor = getAlertWindowAnchorTs(alerts);
|
||||||
const severityCounts = alerts.reduce(
|
const severityCounts = alerts.reduce(
|
||||||
(acc, alert) => {
|
(acc, alert) => {
|
||||||
if (now - alert.source_ts > windowMs) {
|
if (windowAnchor - alert.source_ts > windowMs) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
if (alert.severity === "high") {
|
const severity = normalizeAlertSeverity(alert);
|
||||||
|
if (severity === "high") {
|
||||||
acc.high += 1;
|
acc.high += 1;
|
||||||
} else if (alert.severity === "medium") {
|
} else if (severity === "medium") {
|
||||||
acc.medium += 1;
|
acc.medium += 1;
|
||||||
} else {
|
} else {
|
||||||
acc.low += 1;
|
acc.low += 1;
|
||||||
|
|
@ -3040,10 +3101,10 @@ const AlertSeverityStrip = ({ alerts }: AlertSeverityStripProps) => {
|
||||||
|
|
||||||
const directionCounts = alerts.reduce(
|
const directionCounts = alerts.reduce(
|
||||||
(acc, alert) => {
|
(acc, alert) => {
|
||||||
if (now - alert.source_ts > windowMs) {
|
if (windowAnchor - alert.source_ts > windowMs) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
const direction = normalizeDirection(alert.hits[0]?.direction ?? "neutral");
|
const direction = deriveAlertDirection(alert);
|
||||||
acc[direction] += 1;
|
acc[direction] += 1;
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
|
|
@ -3119,7 +3180,8 @@ type AlertDrawerProps = {
|
||||||
|
|
||||||
const AlertDrawer = ({ alert, flowPacket, evidence, onClose }: AlertDrawerProps) => {
|
const AlertDrawer = ({ alert, flowPacket, evidence, onClose }: AlertDrawerProps) => {
|
||||||
const primary = alert.hits[0];
|
const primary = alert.hits[0];
|
||||||
const direction = primary ? normalizeDirection(primary.direction) : "neutral";
|
const direction = deriveAlertDirection(alert);
|
||||||
|
const severity = normalizeAlertSeverity(alert);
|
||||||
const evidencePrints = evidence.filter((item) => item.kind === "print");
|
const evidencePrints = evidence.filter((item) => item.kind === "print");
|
||||||
const unknownCount = evidence.filter((item) => item.kind === "unknown").length;
|
const unknownCount = evidence.filter((item) => item.kind === "unknown").length;
|
||||||
|
|
||||||
|
|
@ -3137,9 +3199,9 @@ const AlertDrawer = ({ alert, flowPacket, evidence, onClose }: AlertDrawerProps)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="drawer-meta">
|
<div className="drawer-meta">
|
||||||
<span className={`pill severity-${alert.severity}`}>{alert.severity}</span>
|
<span className={`pill severity-${severity}`}>{severity}</span>
|
||||||
<span className="drawer-chip">Score {Math.round(alert.score)}</span>
|
<span className="drawer-chip">Score {Math.round(alert.score)}</span>
|
||||||
{primary ? <span className={`pill direction-${direction}`}>{direction}</span> : null}
|
<span className={`pill direction-${direction}`}>{direction}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="drawer-section">
|
<div className="drawer-section">
|
||||||
|
|
@ -4875,7 +4937,7 @@ const OptionsPane = ({ limit }: OptionsPaneProps) => {
|
||||||
? "No option prints match the current filter."
|
? "No option prints match the current filter."
|
||||||
: state.mode === "live"
|
: state.mode === "live"
|
||||||
? state.options.status === "stale"
|
? state.options.status === "stale"
|
||||||
? "Live feed behind. Waiting for fresh option prints."
|
? "Feed behind. Waiting for fresh option prints."
|
||||||
: "No option prints yet. Start ingest-options."
|
: "No option prints yet. Start ingest-options."
|
||||||
: "Replay queue empty. Ensure ClickHouse has data."}
|
: "Replay queue empty. Ensure ClickHouse has data."}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -5000,8 +5062,8 @@ const EquitiesPane = ({ limit }: EquitiesPaneProps) => {
|
||||||
: state.mode === "live"
|
: state.mode === "live"
|
||||||
? state.equitiesSilentWarning
|
? state.equitiesSilentWarning
|
||||||
? "Connected but no equity prints received. Check ingest-equities."
|
? "Connected but no equity prints received. Check ingest-equities."
|
||||||
: state.equities.status === "stale"
|
: state.equities.status === "stale"
|
||||||
? "Live feed behind. Waiting for fresh equity prints."
|
? "Feed behind. Waiting for fresh equity prints."
|
||||||
: "No equity prints yet. Start ingest-equities."
|
: "No equity prints yet. Start ingest-equities."
|
||||||
: "Replay queue empty. Ensure ClickHouse has data."}
|
: "Replay queue empty. Ensure ClickHouse has data."}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -5079,7 +5141,7 @@ const FlowPane = ({ limit, title = "Flow" }: FlowPaneProps) => {
|
||||||
? "No flow packets match the current filter."
|
? "No flow packets match the current filter."
|
||||||
: state.mode === "live"
|
: state.mode === "live"
|
||||||
? state.flow.status === "stale"
|
? state.flow.status === "stale"
|
||||||
? "Live feed behind. Waiting for fresh flow packets."
|
? "Feed behind. Waiting for fresh flow packets."
|
||||||
: "No flow packets yet. Start compute."
|
: "No flow packets yet. Start compute."
|
||||||
: "Replay queue empty. Ensure ClickHouse has data."}
|
: "Replay queue empty. Ensure ClickHouse has data."}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -5180,15 +5242,17 @@ const FlowPane = ({ limit, title = "Flow" }: FlowPaneProps) => {
|
||||||
type AlertsPaneProps = {
|
type AlertsPaneProps = {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
withStrip?: boolean;
|
withStrip?: boolean;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AlertsPane = ({ limit, withStrip = false }: AlertsPaneProps) => {
|
const AlertsPane = ({ limit, withStrip = false, className }: AlertsPaneProps) => {
|
||||||
const state = useTerminal();
|
const state = useTerminal();
|
||||||
const items = limit ? state.filteredAlerts.slice(0, limit) : state.filteredAlerts;
|
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, 92);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pane
|
<Pane
|
||||||
|
className={className}
|
||||||
title="Alerts"
|
title="Alerts"
|
||||||
status={
|
status={
|
||||||
<TapeStatus
|
<TapeStatus
|
||||||
|
|
@ -5228,7 +5292,8 @@ const AlertsPane = ({ limit, withStrip = false }: AlertsPaneProps) => {
|
||||||
) : null}
|
) : null}
|
||||||
{virtual.visibleItems.map((alert) => {
|
{virtual.visibleItems.map((alert) => {
|
||||||
const primary = alert.hits[0];
|
const primary = alert.hits[0];
|
||||||
const direction = primary ? normalizeDirection(primary.direction) : "neutral";
|
const direction = deriveAlertDirection(alert);
|
||||||
|
const severity = normalizeAlertSeverity(alert);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|
@ -5246,12 +5311,10 @@ const AlertsPane = ({ limit, withStrip = false }: AlertsPaneProps) => {
|
||||||
{primary ? humanizeClassifierId(primary.classifier_id) : "Alert"}
|
{primary ? humanizeClassifierId(primary.classifier_id) : "Alert"}
|
||||||
</div>
|
</div>
|
||||||
<div className="meta">
|
<div className="meta">
|
||||||
<span className={`pill severity-${alert.severity}`}>{alert.severity}</span>
|
<span className={`pill severity-${severity}`}>{severity}</span>
|
||||||
<span>Score {Math.round(alert.score)}</span>
|
<span>Score {Math.round(alert.score)}</span>
|
||||||
<span>{alert.hits.length} hits</span>
|
<span>{alert.hits.length} hits</span>
|
||||||
{primary ? (
|
<span className={`pill direction-${direction}`}>{direction}</span>
|
||||||
<span className={`pill direction-${direction}`}>{direction}</span>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
{primary?.explanations?.[0] ? (
|
{primary?.explanations?.[0] ? (
|
||||||
<div className="note">{primary.explanations[0]}</div>
|
<div className="note">{primary.explanations[0]}</div>
|
||||||
|
|
@ -5273,15 +5336,17 @@ const AlertsPane = ({ limit, withStrip = false }: AlertsPaneProps) => {
|
||||||
|
|
||||||
type ClassifierPaneProps = {
|
type ClassifierPaneProps = {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClassifierPane = ({ limit }: ClassifierPaneProps) => {
|
const ClassifierPane = ({ limit, className }: ClassifierPaneProps) => {
|
||||||
const state = useTerminal();
|
const state = useTerminal();
|
||||||
const items = limit ? state.filteredClassifierHits.slice(0, limit) : state.filteredClassifierHits;
|
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, 88);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pane
|
<Pane
|
||||||
|
className={className}
|
||||||
title="Rules"
|
title="Rules"
|
||||||
status={
|
status={
|
||||||
<TapeStatus
|
<TapeStatus
|
||||||
|
|
@ -5351,15 +5416,17 @@ const ClassifierPane = ({ limit }: ClassifierPaneProps) => {
|
||||||
|
|
||||||
type DarkPaneProps = {
|
type DarkPaneProps = {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DarkPane = ({ limit }: DarkPaneProps) => {
|
const DarkPane = ({ limit, className }: DarkPaneProps) => {
|
||||||
const state = useTerminal();
|
const state = useTerminal();
|
||||||
const items = limit ? state.filteredInferredDark.slice(0, limit) : state.filteredInferredDark;
|
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, 88);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pane
|
<Pane
|
||||||
|
className={className}
|
||||||
title="Dark"
|
title="Dark"
|
||||||
status={
|
status={
|
||||||
<TapeStatus
|
<TapeStatus
|
||||||
|
|
@ -5713,9 +5780,9 @@ export function SignalsRoute() {
|
||||||
return (
|
return (
|
||||||
<PageFrame title="Signals">
|
<PageFrame title="Signals">
|
||||||
<div className="page-grid page-grid-signals">
|
<div className="page-grid page-grid-signals">
|
||||||
<AlertsPane withStrip />
|
<AlertsPane withStrip className="signals-pane-alerts" />
|
||||||
<ClassifierPane />
|
<ClassifierPane className="signals-pane-rules" />
|
||||||
<DarkPane />
|
<DarkPane className="signals-pane-dark" />
|
||||||
</div>
|
</div>
|
||||||
</PageFrame>
|
</PageFrame>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
1
apps/web/tsconfig.tsbuildinfo
Normal file
1
apps/web/tsconfig.tsbuildinfo
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue