feat(lab): standardize evidence workspace template
This commit is contained in:
@@ -315,6 +315,8 @@ export default function App() {
|
||||
const observationFullscreenActive = Boolean(
|
||||
activeDefinition?.kind === "spatial"
|
||||
? observationLayout.maximizedFloatingSourceId || focusedObservationSource?.modality === "point-cloud"
|
||||
: activeDefinition?.kind === "lab-archive"
|
||||
? observationLayout.maximizedFloatingSourceId || focusedObservationSource?.modality === "point-cloud"
|
||||
: activeDefinition?.kind === "cameras" && focusedObservationSource,
|
||||
);
|
||||
|
||||
|
||||
@@ -2385,7 +2385,8 @@
|
||||
|
||||
.recordings-workspace,
|
||||
.lab-archive-workspace,
|
||||
.lab-result-surface {
|
||||
.lab-result-surface,
|
||||
.laboratory-work-template {
|
||||
display: grid;
|
||||
gap: 0.85rem;
|
||||
}
|
||||
@@ -2542,12 +2543,52 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.lab-result-surface {
|
||||
.laboratory-evidence-frame {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.lab-result-surface[data-resizable="true"] .laboratory-evidence-frame {
|
||||
height: clamp(42rem, 68vh, 58rem);
|
||||
min-height: 42rem;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.laboratory-evidence-frame > .spatial-workspace {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.lab-result-surface .spatial-workspace {
|
||||
min-height: 34rem;
|
||||
.laboratory-evidence-frame > .lidar-quality-workspace {
|
||||
min-height: 42rem;
|
||||
}
|
||||
|
||||
.lab-archive-workspace[data-viewer-focused="true"] {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.lab-archive-workspace[data-viewer-focused="true"] > .laboratory-selector,
|
||||
.lab-archive-workspace[data-viewer-focused="true"]
|
||||
.laboratory-work-template > :not(.lab-result-surface),
|
||||
.lab-archive-workspace[data-viewer-focused="true"] .lab-result-surface > header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lab-archive-workspace[data-viewer-focused="true"] > .laboratory-work-output,
|
||||
.lab-archive-workspace[data-viewer-focused="true"] .laboratory-work-template,
|
||||
.lab-archive-workspace[data-viewer-focused="true"] .lab-result-surface,
|
||||
.lab-archive-workspace[data-viewer-focused="true"] .laboratory-evidence-frame {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.lab-archive-workspace[data-viewer-focused="true"] .laboratory-evidence-frame {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.laboratory-result-pending {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
type ComponentType,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import {
|
||||
Button,
|
||||
@@ -1281,6 +1282,54 @@ function LaboratoryTask({
|
||||
);
|
||||
}
|
||||
|
||||
function LaboratoryEvidence({
|
||||
eyebrow,
|
||||
title,
|
||||
resizable = false,
|
||||
children,
|
||||
}: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
resizable?: boolean;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section
|
||||
className="lab-result-surface"
|
||||
data-resizable={resizable ? "true" : undefined}
|
||||
>
|
||||
<header>
|
||||
<span className="section-eyebrow">{eyebrow}</span>
|
||||
<strong>{title}</strong>
|
||||
</header>
|
||||
<div className="laboratory-evidence-frame">
|
||||
{children}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function LaboratoryWorkTemplate({
|
||||
task,
|
||||
evidence,
|
||||
result = null,
|
||||
details = null,
|
||||
}: {
|
||||
task: ReactNode;
|
||||
evidence: ReactNode;
|
||||
result?: ReactNode;
|
||||
details?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="laboratory-work-template">
|
||||
{task}
|
||||
{evidence}
|
||||
{result}
|
||||
{details}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function formatSeconds(value: number): string {
|
||||
return `${value.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} с`;
|
||||
}
|
||||
@@ -1334,8 +1383,9 @@ function E29LaboratoryResult({
|
||||
}, [result.resultId, selectedFrameIndex]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LaboratoryTask
|
||||
<LaboratoryWorkTemplate
|
||||
task={(
|
||||
<LaboratoryTask
|
||||
title="LAB E29 · camera-first semantics + независимая геометрия"
|
||||
description="Проверяется рабочая продуктовая граница: камера сохраняет семантический класс и идентичность объекта, LiDAR подтверждает метрическую дальность и занятую геометрию по локальной поверхности L2.6. Отсутствие точек не объявляется свободным пространством."
|
||||
status="Проверенные артефакты"
|
||||
@@ -1354,13 +1404,14 @@ function E29LaboratoryResult({
|
||||
value: "Control plane · read-only · hash verified",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="lab-result-surface">
|
||||
<header>
|
||||
<span className="section-eyebrow">ИСХОДНЫЕ ДАННЫЕ</span>
|
||||
<strong>LiDAR, траектория и камера RAVNOVES00</strong>
|
||||
</header>
|
||||
/>
|
||||
)}
|
||||
evidence={(
|
||||
<LaboratoryEvidence
|
||||
eyebrow="ИСХОДНЫЕ ДАННЫЕ"
|
||||
title="LiDAR, траектория и камера RAVNOVES00"
|
||||
resizable
|
||||
>
|
||||
{replayReady ? (
|
||||
<SpatialWorkspace {...props} />
|
||||
) : (
|
||||
@@ -1374,9 +1425,10 @@ function E29LaboratoryResult({
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="laboratory-result-summary">
|
||||
</LaboratoryEvidence>
|
||||
)}
|
||||
result={(
|
||||
<section className="laboratory-result-summary">
|
||||
<header>
|
||||
<div>
|
||||
<span className="section-eyebrow">РЕЗУЛЬТАТ И ВЫВОД</span>
|
||||
@@ -1409,9 +1461,10 @@ function E29LaboratoryResult({
|
||||
</div>
|
||||
</div>
|
||||
<p>{result.decision.nextGate}</p>
|
||||
</section>
|
||||
|
||||
<section className="laboratory-frame-review">
|
||||
</section>
|
||||
)}
|
||||
details={(
|
||||
<section className="laboratory-frame-review">
|
||||
<header>
|
||||
<div>
|
||||
<span className="section-eyebrow">КАДРЫ С КОНФЛИКТОМ</span>
|
||||
@@ -1475,8 +1528,9 @@ function E29LaboratoryResult({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
</section>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1524,8 +1578,9 @@ function PublishedLaboratoryResult({
|
||||
const replayReady = props.recordedReplay?.sessionId === session.id;
|
||||
|
||||
return (
|
||||
<>
|
||||
<LaboratoryTask
|
||||
<LaboratoryWorkTemplate
|
||||
task={(
|
||||
<LaboratoryTask
|
||||
title={`${lab?.labId ?? "LAB"} · ${laboratorySessionTitle(session)}`}
|
||||
description="Опубликованная лабораторная работа открывается по своей неизменяемой записи. Viewer показывает исходные синхронные каналы; за ним приведены только метрики, зафиксированные в LAB-provenance этого результата."
|
||||
status="Зафиксированный результат"
|
||||
@@ -1535,13 +1590,14 @@ function PublishedLaboratoryResult({
|
||||
{ label: "Конфигурация", value: lab?.configSha256?.slice(0, 16) ?? "Не зафиксирована" },
|
||||
{ label: "Полномочия", value: "Диагностика · без навигации и команд" },
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="lab-result-surface">
|
||||
<header>
|
||||
<span className="section-eyebrow">ВИЗУАЛЬНОЕ ДОКАЗАТЕЛЬСТВО</span>
|
||||
<strong>Исходная запись выбранной лабораторной работы</strong>
|
||||
</header>
|
||||
/>
|
||||
)}
|
||||
evidence={(
|
||||
<LaboratoryEvidence
|
||||
eyebrow="ВИЗУАЛЬНОЕ ДОКАЗАТЕЛЬСТВО"
|
||||
title="Исходная запись выбранной лабораторной работы"
|
||||
resizable
|
||||
>
|
||||
{replayReady ? (
|
||||
<SpatialWorkspace {...props} />
|
||||
) : (
|
||||
@@ -1555,9 +1611,10 @@ function PublishedLaboratoryResult({
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="laboratory-result-summary">
|
||||
</LaboratoryEvidence>
|
||||
)}
|
||||
result={(
|
||||
<section className="laboratory-result-summary">
|
||||
<header>
|
||||
<div>
|
||||
<span className="section-eyebrow">ЗАФИКСИРОВАННЫЕ МЕТРИКИ</span>
|
||||
@@ -1584,8 +1641,9 @@ function PublishedLaboratoryResult({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
</section>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1792,8 +1850,16 @@ function LabArchiveWorkspace(props: WorkspaceRendererProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const viewerFocused = Boolean(
|
||||
props.observationLayout.focusedSourceId
|
||||
|| props.observationLayout.maximizedFloatingSourceId,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="lab-archive-workspace">
|
||||
<div
|
||||
className="lab-archive-workspace"
|
||||
data-viewer-focused={viewerFocused ? "true" : undefined}
|
||||
>
|
||||
<LaboratorySelector
|
||||
eyebrow="ПРОФИЛЬ ЛАБОРАТОРНОГО КОНТУРА"
|
||||
title={profiles.find((profile) => profile.id === profileId)?.label ?? rigLabel}
|
||||
@@ -1817,8 +1883,9 @@ function LabArchiveWorkspace(props: WorkspaceRendererProps) {
|
||||
|
||||
<div className="laboratory-work-output">
|
||||
{workId === "e28-local-surface" ? (
|
||||
<>
|
||||
<LaboratoryTask
|
||||
<LaboratoryWorkTemplate
|
||||
task={(
|
||||
<LaboratoryTask
|
||||
title="LAB E28 · локальная модель поверхности L2.6"
|
||||
description="Полная запись RAVNOVES00 воспроизводится через bounded shadow-контур. Модель оценивает локальную поверхность, наблюдаемые препятствия, временные скачки и ошибки предсказания без изменения исходного сенсора и без командного канала."
|
||||
status="Проверенные артефакты"
|
||||
@@ -1831,13 +1898,21 @@ function LabArchiveWorkspace(props: WorkspaceRendererProps) {
|
||||
{ label: "Режим", value: "Recorded-source-paced shadow" },
|
||||
{ label: "Артефакты", value: "Control plane · read-only · hash verified" },
|
||||
]}
|
||||
/>
|
||||
<LidarQualityWorkspace
|
||||
embedded
|
||||
deviceLabel={props.deviceLabel}
|
||||
onOpenObservation={() => props.navigation.openView("spatial-scene")}
|
||||
/>
|
||||
</>
|
||||
/>
|
||||
)}
|
||||
evidence={(
|
||||
<LaboratoryEvidence
|
||||
eyebrow="ВИЗУАЛЬНОЕ ДОКАЗАТЕЛЬСТВО"
|
||||
title="Диагностическая поверхность и кадры LAB E28"
|
||||
>
|
||||
<LidarQualityWorkspace
|
||||
embedded
|
||||
deviceLabel={props.deviceLabel}
|
||||
onOpenObservation={() => props.navigation.openView("spatial-scene")}
|
||||
/>
|
||||
</LaboratoryEvidence>
|
||||
)}
|
||||
/>
|
||||
) : workId === "e29-camera-geometry" && e29Result && e29SourceSession ? (
|
||||
<E29LaboratoryResult
|
||||
props={props}
|
||||
|
||||
Reference in New Issue
Block a user