fix(lab): align spatial replay controls

This commit is contained in:
DCCONSTRUCTIONS
2026-08-06 09:14:20 +03:00
parent e38f2fb1da
commit b8fb4ebcba
7 changed files with 119 additions and 86 deletions
@@ -4,6 +4,7 @@ import { Icon, IconButton } from "@nodedc/ui-react";
import { ObservationTimeline } from "../../components/ObservationTimeline";
import {
LaboratoryMetricEvidenceScene,
type LaboratoryMetricEvidenceSceneHandle,
type LaboratoryMetricSceneMode,
} from "../../components/laboratory/LaboratoryMetricEvidenceScene";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
@@ -66,7 +67,10 @@ function SpatialState({ message: text }: { message: string }) {
export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
const [mode, setMode] = useState<M4ThreatViewMode>("video");
const [spatialMode, setSpatialMode] = useState<LaboratoryMetricSceneMode>("3d");
const [showCurrentIncrement, setShowCurrentIncrement] = useState(true);
const [showRollingMap, setShowRollingMap] = useState(true);
const [expanded, setExpanded] = useState(false);
const metricSceneRef = useRef<LaboratoryMetricEvidenceSceneHandle | null>(null);
const metadata = useM4ThreatTimelineMetadata(resultId);
const playbackRange = useMemo(() => metadata.timeline ? ({
startSeconds: metadata.timeline.timelineStartSeconds,
@@ -189,9 +193,44 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
{mode === "3d" || mode === "plan" ? (
<div
className="nodedc-segmented m4-replay-threat-visual__layer-controls"
role="group"
aria-label="Слои пространственного evidence"
>
<button
type="button"
className="nodedc-segmented__item"
data-active={showCurrentIncrement ? "true" : undefined}
aria-pressed={showCurrentIncrement}
onClick={() => setShowCurrentIncrement((visible) => !visible)}
>
CURRENT INCREMENT
</button>
<button
type="button"
className="nodedc-segmented__item"
data-active={showRollingMap ? "true" : undefined}
aria-pressed={showRollingMap}
onClick={() => setShowRollingMap((visible) => !visible)}
>
ROLLING MAP
</button>
</div>
) : null}
</div>
);
const trailingActions = mode === "3d" || mode === "plan" ? (
<IconButton
label="Сбросить ракурс"
onClick={() => metricSceneRef.current?.resetView()}
>
<Icon name="refresh" size={16} />
</IconButton>
) : null;
const overlay = metadata.timeline && frame ? (
<div className="l3-visual-audit__overlay m4-replay-threat-visual__overlay">
<div>
@@ -287,6 +326,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
>
{frame ? (
<LaboratoryMetricEvidenceScene
ref={metricSceneRef}
pointCloudBodyXyzM={frame.pointCloudBodyXyzM}
obstacles={sceneObstacles}
rig={timeline.rig}
@@ -294,6 +334,8 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
occupiedVoxelSizeM={timeline.occupiedVoxelSizeM}
mode={spatialMode}
label="M4.6 recorded-realtime current increment and rolling occupancy"
showCurrentIncrement={showCurrentIncrement}
showRollingMap={showRollingMap}
/>
) : null}
</div>
@@ -336,7 +378,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
onSeek={(timeNs) => playbackController.seek(timeNs / 1_000_000_000)}
onPlayingChange={playbackController.setPlaying}
onPlaybackRateChange={playbackController.setRate}
onJumpToEnd={() => playbackController.seek(timeline.timelineEndSeconds)}
showJumpToEnd={false}
/>
) : undefined;
@@ -358,6 +400,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
actions={actions}
overlay={overlay}
transport={transport}
trailingActions={trailingActions}
>
{content}
</LaboratoryEvidenceViewer>