fix(ui): scope spatial tools to scene

This commit is contained in:
DCCONSTRUCTIONS
2026-07-18 11:31:44 +03:00
parent 574a494759
commit 0ac6424c46
9 changed files with 216 additions and 142 deletions
+22 -39
View File
@@ -46,7 +46,6 @@ import { useWorkspaceLayoutProfile } from "./core/observation/useWorkspaceLayout
import {
OBSERVATION_WORKSPACE_ID,
OBSERVATION_WORKSPACE_LAYOUT_VERSION,
type ObservationToolWindowId,
type ObservationWorkspaceLayoutProfile,
} from "./core/observation/workspaceLayout";
import {
@@ -67,9 +66,7 @@ import { DeviceWorkspace } from "./workspaces/DeviceWorkspace";
import { WorkspaceRenderer } from "./workspaces/Workspaces";
import "./styles/scene-windows.css";
type SceneToolWindowId = ObservationToolWindowId;
const sceneToolWindowIds: readonly SceneToolWindowId[] = ["sources", "display", "layers"];
type SceneToolWindowId = "sources" | "display" | "layers";
const viewerSettingsQuietPeriodMs = 750;
const colorModeOptions: Array<{ value: PointColorMode; label: string; description: string }> = [
@@ -166,6 +163,9 @@ export default function App() {
const currentRoot = rootById(activeRoot);
const activeDefinition = workspaceById(workspace.activeView);
const spatialWorkspaceActive = Boolean(
workspace.contentOpen && activeDefinition?.kind === "spatial",
);
const rootWorkspaces = workspacesForRoot(activeRoot);
const activeSceneWindow = sceneWindowOrder[sceneWindowOrder.length - 1] ?? null;
const automaticSourceUrl = runtime.state?.spatialSource?.url.trim() ?? "";
@@ -292,17 +292,19 @@ export default function App() {
confirmedSceneSettingsRef.current = profile.sceneSettings;
setSceneSettings(profile.sceneSettings);
setDisplayDraft(profile.sceneSettings);
setSourceWindowOpen(profile.toolWindows.sourcesOpen);
setDisplayWindowOpen(profile.toolWindows.displayOpen);
setLayerInspectorOpen(profile.toolWindows.layersOpen);
setSceneWindowOrder(profile.toolWindows.order.filter((windowId) => {
if (windowId === "sources") return profile.toolWindows.sourcesOpen;
if (windowId === "display") return profile.toolWindows.displayOpen;
return profile.toolWindows.layersOpen;
}));
observationLayout.restore(profile);
}, [observationLayout.restore, workspaceLayoutProfile.profile]);
useEffect(() => {
if (spatialWorkspaceActive) return;
// Scene tools are transient children of the spatial workspace. They must
// never survive a route/root/content close or appear over the landing page.
setSourceWindowOpen(false);
setDisplayWindowOpen(false);
setLayerInspectorOpen(false);
setSceneWindowOrder([]);
}, [spatialWorkspaceActive]);
useEffect(() => {
sceneSettingsCommitterActiveRef.current = true;
return () => {
@@ -355,7 +357,7 @@ export default function App() {
}, []);
useEffect(() => {
if (!activeSceneWindow) return;
if (!spatialWorkspaceActive || !activeSceneWindow) return;
const closeActiveWindow = (event: KeyboardEvent) => {
if (event.key !== "Escape" || event.defaultPrevented) return;
@@ -367,7 +369,7 @@ export default function App() {
document.addEventListener("keydown", closeActiveWindow);
return () => document.removeEventListener("keydown", closeActiveWindow);
}, [activeSceneWindow, closeSceneWindow]);
}, [activeSceneWindow, closeSceneWindow, spatialWorkspaceActive]);
const selectRoot = (rootId: RootId) => {
setActiveRoot(rootId);
@@ -383,6 +385,7 @@ export default function App() {
};
const openSource = () => {
if (!spatialWorkspaceActive) return;
setSourceDraft(sourceUrl);
setSourceWindowOpen(true);
activateSceneWindow("sources");
@@ -423,11 +426,13 @@ export default function App() {
}, [commitDisplaySettings]);
const openDisplay = () => {
if (!spatialWorkspaceActive) return;
setDisplayWindowOpen(true);
activateSceneWindow("display");
};
const openLayers = () => {
if (!spatialWorkspaceActive) return;
setLayerInspectorOpen(true);
activateSceneWindow("layers");
};
@@ -494,37 +499,18 @@ export default function App() {
setLayoutSaveNotice("Сцена ещё не измерила рабочую область.");
return;
}
const openWindowOrder = sceneWindowOrder.filter((windowId) => {
if (windowId === "sources") return sourceWindowOpen;
if (windowId === "display") return displayWindowOpen;
return layerInspectorOpen;
});
const completeWindowOrder = [
...sceneToolWindowIds.filter((windowId) => !openWindowOrder.includes(windowId)),
...openWindowOrder,
];
const draft: ObservationWorkspaceLayoutProfile = {
version: OBSERVATION_WORKSPACE_LAYOUT_VERSION,
revision: workspaceLayoutProfile.profile?.revision ?? 0,
workspaceId: OBSERVATION_WORKSPACE_ID,
sceneSettings: sceneSettingsRef.current,
toolWindows: {
sourcesOpen: sourceWindowOpen,
displayOpen: displayWindowOpen,
layersOpen: layerInspectorOpen,
order: completeWindowOrder,
},
...layout,
};
const saved = await workspaceLayoutProfile.save(draft);
setLayoutSaveNotice(saved ? "Компоновка сохранена" : workspaceLayoutProfile.error);
}, [
displayWindowOpen,
flushDisplaySettings,
layerInspectorOpen,
observationLayout.snapshot,
sceneWindowOrder,
sourceWindowOpen,
workspaceLayoutProfile,
]);
@@ -555,9 +541,6 @@ export default function App() {
disabled: workspaceLayoutProfile.state === "saving",
onClick: () => void saveWorkspaceLayout(),
},
{ label: "Настроить визуальный движок", icon: "network", onClick: openSource },
{ label: "Настроить отображение", icon: "sliders", onClick: openDisplay },
{ label: "Открыть слои", icon: "list", onClick: openLayers },
);
}
@@ -731,7 +714,7 @@ export default function App() {
/>
<Window
open={sourceWindowOpen}
open={spatialWorkspaceActive && sourceWindowOpen}
title="Визуальный движок"
subtitle="Rerun gRPC и записи пространственной сцены"
placement="end"
@@ -857,7 +840,7 @@ export default function App() {
</Window>
<Window
open={displayWindowOpen}
open={spatialWorkspaceActive && displayWindowOpen}
title="Отображение"
subtitle="Параметры пространственной сцены"
placement="end"
@@ -1002,7 +985,7 @@ export default function App() {
</Window>
<Window
open={layerInspectorOpen}
open={spatialWorkspaceActive && layerInspectorOpen}
title="Слои сцены"
subtitle="Сущности пространственной сцены"
placement="end"