fix(control-station): align lidar viewer and expose polygon

This commit is contained in:
DCCONSTRUCTIONS
2026-07-25 13:23:34 +03:00
parent c01712c30d
commit 881e97312b
9 changed files with 51 additions and 71 deletions
+2 -7
View File
@@ -47,7 +47,6 @@ import type {
import { useRecordedSessionAdmission } from "./core/observation/useRecordedSessionAdmission";
import { useWorkspaceLayoutProfile } from "./core/observation/useWorkspaceLayoutProfile";
import { resolvePolygonRunRoute } from "./core/polygon/runArchive";
import { usePolygonWorkerCapability } from "./core/polygon/usePolygonWorkerCapability";
import {
OBSERVATION_WORKSPACE_ID,
OBSERVATION_WORKSPACE_LAYOUT_VERSION,
@@ -55,7 +54,7 @@ import {
} from "./core/observation/workspaceLayout";
import {
rootById,
rootsForCapabilities,
roots,
workspaceById,
workspacesForRoot,
type RootId,
@@ -151,7 +150,6 @@ function mergeViewerSettings(
export default function App() {
const runtime = useMissionRuntime();
const { selection } = useDevicePluginHost();
const polygonWorker = usePolygonWorkerCapability();
const polygonRunRoute = useMemo(
() => resolvePolygonRunRoute(typeof window === "undefined" ? "" : window.location.search),
[],
@@ -199,10 +197,7 @@ export default function App() {
const polygonRunRouteOpenedRef = useRef(false);
const currentRoot = rootById(activeRoot);
const visibleRoots = useMemo(
() => rootsForCapabilities({ polygonWorkerAvailable: polygonWorker.available }),
[polygonWorker.available],
);
const visibleRoots = roots;
const activeDefinition = workspaceById(workspace.activeView);
const spatialWorkspaceActive = Boolean(
workspace.contentOpen && activeDefinition?.kind === "spatial",
-8
View File
@@ -796,14 +796,6 @@ export function rootById(id: RootId | null): RootDefinition | null {
return id ? roots.find((root) => root.id === id) ?? null : null;
}
export function rootsForCapabilities({
polygonWorkerAvailable,
}: {
polygonWorkerAvailable: boolean;
}): RootDefinition[] {
return roots.filter((root) => root.id !== "polygon" || polygonWorkerAvailable);
}
export function workspaceById(id: string | null): WorkspaceDefinition | null {
return id ? workspaces.find((workspace) => workspace.id === id) ?? null : null;
}
+20 -23
View File
@@ -1441,9 +1441,8 @@
.lidar-field-cloud {
overflow: hidden;
min-width: 0;
border: 1px solid rgb(255 255 255 / 0.09);
border-radius: 0.9rem;
background: #071018;
background: #06070a;
}
.lidar-field-camera {
@@ -1461,7 +1460,6 @@
.lidar-field-camera > .lidar-field-stage__label,
.lidar-field-cloud > header {
min-height: 3.45rem;
border-bottom: 1px solid rgb(255 255 255 / 0.08);
padding: 0.66rem 0.72rem;
}
@@ -1487,7 +1485,6 @@
display: flex;
justify-content: space-between;
gap: 0.5rem;
border-top: 1px solid rgb(255 255 255 / 0.08);
padding: 0.52rem 0.7rem;
}
@@ -1548,7 +1545,7 @@
overflow: hidden;
margin-top: 0.8rem;
border-radius: 0.9rem;
background: #071018;
background: #06070a;
}
.lidar-fallback-review > header {
@@ -1557,7 +1554,6 @@
align-items: center;
justify-content: space-between;
gap: 0.8rem;
border-bottom: 1px solid rgb(255 255 255 / 0.08);
padding: 0.65rem 0.75rem;
}
@@ -1569,7 +1565,6 @@
}
.lidar-fallback-review > footer {
border-top: 1px solid rgb(255 255 255 / 0.08);
padding: 0.58rem 0.75rem;
color: var(--nodedc-text-muted);
font-size: 0.58rem;
@@ -1646,11 +1641,8 @@
display: grid;
gap: 0.75rem;
margin-top: 0.85rem;
border: 1px solid rgb(74 215 255 / 0.18);
border-radius: 1rem;
background:
radial-gradient(circle at 18% 0%, rgb(56 124 255 / 0.12), transparent 34%),
rgb(4 12 19 / 0.72);
background: #06070a;
padding: 0.85rem;
}
@@ -1716,9 +1708,9 @@
.lidar-ground-modes button,
.lidar-ground-frame-control button,
.lidar-ground-scene__toolbar button {
border: 1px solid var(--station-hairline);
border: 0;
border-radius: 999px;
background: rgb(255 255 255 / 0.035);
background: transparent;
padding: 0.4rem 0.62rem;
color: var(--nodedc-text-secondary);
font: inherit;
@@ -1730,9 +1722,16 @@
.lidar-ground-modes button[data-active="true"],
.lidar-ground-frame-control button:hover:not(:disabled),
.lidar-ground-scene__toolbar button:hover {
border-color: rgb(74 215 255 / 0.48);
background: rgb(74 215 255 / 0.1);
background: rgb(255 255 255 / 0.11);
color: var(--nodedc-text-primary);
outline: none;
}
.lidar-ground-modes button:focus-visible,
.lidar-ground-frame-control button:focus-visible,
.lidar-ground-scene__toolbar button:focus-visible {
background: rgb(255 255 255 / 0.14);
outline: none;
}
.lidar-ground-frame-control button {
@@ -1751,16 +1750,15 @@
.lidar-ground-frame-control input {
width: min(16rem, 24vw);
accent-color: #4ad7ff;
accent-color: #f2f2f2;
}
.lidar-ground-scene {
position: relative;
overflow: hidden;
min-height: 30rem;
border: 1px solid rgb(255 255 255 / 0.09);
border-radius: 0.9rem;
background: #071018;
background: #06070a;
}
.lidar-ground-scene__viewport {
@@ -1782,15 +1780,15 @@
display: flex;
align-items: center;
gap: 0.5rem;
border: 1px solid rgb(255 255 255 / 0.08);
border: 0;
border-radius: 999px;
background: rgb(5 13 21 / 0.82);
background: rgb(10 11 14 / 0.78);
padding: 0.28rem;
backdrop-filter: blur(14px);
}
.lidar-ground-scene__toolbar button {
background: rgb(74 215 255 / 0.08);
background: rgb(255 255 255 / 0.06);
}
.lidar-ground-scene__error {
@@ -1808,9 +1806,8 @@
place-content: center;
justify-items: center;
gap: 0.55rem;
border: 1px solid var(--station-hairline);
border-radius: 0.9rem;
background: #071018;
background: #06070a;
text-align: center;
}
@@ -60,12 +60,13 @@ function frameColors(
const candidateAssigned = frame.masks.candidateAssigned[index] === 1;
if (mode === "intensity") {
const intensity = (frame.intensity0To255?.[index] ?? 96) / 255;
const neutral = 0.16 + intensity * 0.8;
setRgb(
colors,
offset,
0.12 + intensity * 0.74,
0.24 + intensity * 0.68,
0.34 + intensity * 0.6,
neutral,
neutral,
neutral,
);
} else if (mode === "current") {
setRgb(
@@ -131,7 +132,7 @@ export function LidarGroundPointCloud({
}
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.setClearColor(0x071018, 0.96);
renderer.setClearColor(0x06070a, 1);
renderer.domElement.setAttribute(
"aria-label",
"Интерактивное облако LiDAR",
@@ -139,7 +140,7 @@ export function LidarGroundPointCloud({
host.prepend(renderer.domElement);
const scene = new THREE.Scene();
const fog = new THREE.FogExp2(0x071018, 0.035);
const fog = new THREE.FogExp2(0x06070a, 0.035);
scene.fog = fog;
fogRef.current = fog;
const camera = new THREE.PerspectiveCamera(48, 1, 0.01, 1_000);
@@ -172,7 +173,7 @@ export function LidarGroundPointCloud({
materialRef.current = material;
scene.add(new THREE.Points(geometry, material));
const grid = new THREE.GridHelper(10, 40, 0x71963d, 0x26312a);
const grid = new THREE.GridHelper(10, 40, 0x454846, 0x252725);
gridRef.current = grid;
const gridMaterials = Array.isArray(grid.material)
? grid.material
@@ -378,7 +378,7 @@ export function PolygonRoverScene({
<span>
{workerAvailable
? "Запустите PX4/Gazebo — модель останется доступна для осмотра."
: олигон появится в шапке после подтверждения Simulation Worker."}
: рогоны и датасеты доступны; запуск и управление откроются после подтверждения Simulation Worker."}
</span>
</div>
) : null}
@@ -21,7 +21,7 @@ let stopPolygonWorker;
let PolygonWorkerContractError;
let workspaceById;
let workspacesForRoot;
let rootsForCapabilities;
let roots;
before(async () => {
server = await createServer({
@@ -51,7 +51,7 @@ before(async () => {
({
workspaceById,
workspacesForRoot,
rootsForCapabilities,
roots,
} = await server.ssrLoadModule("/src/productModel.ts"));
});
@@ -290,7 +290,7 @@ function commandAcceptance(overrides = {}) {
};
}
test("Polygon is a worker-gated product root and keeps a compatible direct route", () => {
test("Polygon stays visible while worker capability gates live actions", () => {
assert.deepEqual(resolvePolygonRunRoute("?workspace=polygon-run"), {
active: true,
runId: null,
@@ -320,16 +320,7 @@ test("Polygon is a worker-gated product root and keeps a compatible direct route
workspacesForRoot("system").some(({ id }) => id === "polygon-run"),
false,
);
assert.equal(
rootsForCapabilities({ polygonWorkerAvailable: false })
.some(({ id }) => id === "polygon"),
false,
);
assert.equal(
rootsForCapabilities({ polygonWorkerAvailable: true })
.some(({ id }) => id === "polygon"),
true,
);
assert.equal(roots.some(({ id }) => id === "polygon"), true);
});
test("Polygon contracts decode path-free evidence and preserve the safety boundary", () => {