Fix BIM startup loading shell

This commit is contained in:
CODEX 2026-07-02 20:20:44 +03:00
parent ef7179dc6c
commit 2f1c7d979a
3 changed files with 94 additions and 16 deletions

View File

@ -128,13 +128,15 @@ body {
margin: 0; margin: 0;
min-height: 100vh; min-height: 100vh;
font-family: "Manrope", "Segoe UI", system-ui, -apple-system, sans-serif; font-family: "Manrope", "Segoe UI", system-ui, -apple-system, sans-serif;
background: radial-gradient(circle at 20% 20%, rgba(76, 224, 210, 0.08), transparent 28%), background: var(--bg-outer);
radial-gradient(circle at 82% 22%, rgba(116, 240, 229, 0.08), transparent 25%),
var(--bg-outer);
color: var(--text); color: var(--text);
overflow: hidden; overflow: hidden;
} }
body.app-starting {
background: linear-gradient(180deg, var(--loading-top), var(--loading-bottom));
}
html, html,
body, body,
.viewer-wrapper, .viewer-wrapper,
@ -339,6 +341,25 @@ html.is-embedded-viewer #navCube {
display: none; display: none;
} }
body.app-starting header,
body.app-starting #dropZone,
body.app-starting #status,
body.app-starting #bottomToolbar,
body.app-starting #sectionToolbar,
body.app-starting #navCube,
body.app-starting .side-panel,
body.app-starting .comments-panel,
body.app-starting .templates-panel,
body.app-starting .settings-panel {
opacity: 0 !important;
pointer-events: none !important;
visibility: hidden !important;
}
body.app-starting #loader.hidden {
display: block;
}
.loader__figure, .loader__figure,
.loader { .loader {
position: absolute; position: absolute;

View File

@ -29,6 +29,26 @@ const detectEmbeddedViewer = () => {
const embeddedViewer = detectEmbeddedViewer(); const embeddedViewer = detectEmbeddedViewer();
document.documentElement.classList.toggle("is-embedded-viewer", embeddedViewer); document.documentElement.classList.toggle("is-embedded-viewer", embeddedViewer);
let appShellSetupComplete = false;
let appStartupContentReady = false;
const revealAppShell = ({ force = false } = {}) => {
if (!force && (!appShellSetupComplete || !appStartupContentReady)) return;
appShellSetupComplete = true;
appStartupContentReady = true;
const preloadHidden = document.getElementById("preload-hidden");
if (preloadHidden) {
preloadHidden.remove();
}
document.body?.classList.remove("app-starting");
document.documentElement.classList.add("app-ready");
};
const markStartupContentReady = () => {
appStartupContentReady = true;
revealAppShell();
};
const dropZone = document.getElementById("dropZone"); const dropZone = document.getElementById("dropZone");
const viewerWrapper = document.querySelector(".viewer-wrapper"); const viewerWrapper = document.querySelector(".viewer-wrapper");
const headerEl = document.querySelector("header"); const headerEl = document.querySelector("header");
@ -5967,12 +5987,6 @@ const initViewer = async () => {
setupNodeDCSessionSync(); setupNodeDCSessionSync();
setupNodeDCSessionWatcher(); setupNodeDCSessionWatcher();
const preloadHidden = document.getElementById("preload-hidden");
if (preloadHidden) {
preloadHidden.remove();
}
document.body.style.opacity = "1";
const sdk = await import(SDK_URL); const sdk = await import(SDK_URL);
sdkRef = sdk; sdkRef = sdk;
const { Viewer, NavCubePlugin, DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl, SectionPlanesPlugin } = sdk; const { Viewer, NavCubePlugin, DistanceMeasurementsPlugin, DistanceMeasurementsMouseControl, SectionPlanesPlugin } = sdk;
@ -6417,7 +6431,7 @@ const initViewer = async () => {
const loadModel = (options) => { const loadModel = (options) => {
const debugOptions = options || {}; const debugOptions = options || {};
try { try {
const { type, url, name = "", replace = true, id: forcedId, meta, edges, preserveMeta = false } = options; const { type, url, name = "", replace = true, id: forcedId, meta, edges, preserveMeta = false, startupReveal = false } = options;
const modelSettings = meta?.viewerSettings; const modelSettings = meta?.viewerSettings;
const normalizedType = normalizeType(type, typeof url === "string" ? url : ""); const normalizedType = normalizeType(type, typeof url === "string" ? url : "");
const inferredType = normalizedType || normalizeType(guessTypeFromName(typeof url === "string" ? url : "")); const inferredType = normalizedType || normalizeType(guessTypeFromName(typeof url === "string" ? url : ""));
@ -6517,6 +6531,9 @@ const initViewer = async () => {
} }
setStatus(`Загружено: ${name || inferredType}`); setStatus(`Загружено: ${name || inferredType}`);
dropZone.classList.add("hidden"); dropZone.classList.add("hidden");
if (startupReveal) {
markStartupContentReady();
}
setLoading(false); setLoading(false);
if (inferredType === "las") { if (inferredType === "las") {
snapshotPointCloudColors(id); snapshotPointCloudColors(id);
@ -6553,6 +6570,9 @@ const initViewer = async () => {
src: srcToLoad, src: srcToLoad,
}, "error"); }, "error");
showError(`Ошибка загрузки: ${err}`); showError(`Ошибка загрузки: ${err}`);
if (startupReveal) {
markStartupContentReady();
}
setLoading(false); setLoading(false);
updateNavCubeVisibility(); updateNavCubeVisibility();
updatePanelsVisibility(); updatePanelsVisibility();
@ -6583,6 +6603,9 @@ const initViewer = async () => {
error: e?.message || String(e), error: e?.message || String(e),
}, "error"); }, "error");
showError(e.message || "Ошибка загрузки"); showError(e.message || "Ошибка загрузки");
if (debugOptions.startupReveal) {
markStartupContentReady();
}
setLoading(false); setLoading(false);
return null; return null;
} }
@ -6630,9 +6653,10 @@ const initViewer = async () => {
nameParam: sharedViewer?.name || params.get("name"), nameParam: sharedViewer?.name || params.get("name"),
}); });
if (!src) { if (!src) {
reportViewerEvent("startup-no-src", { search: window.location.search }, "error"); reportViewerEvent("startup-no-src", { search: window.location.search });
clearShareStartupPending(); clearShareStartupPending();
setLoading(false); setLoading(false);
markStartupContentReady();
return; return;
} }
@ -6643,6 +6667,7 @@ const initViewer = async () => {
showError("Не удалось определить формат модели из URL"); showError("Не удалось определить формат модели из URL");
clearShareStartupPending(); clearShareStartupPending();
setLoading(false); setLoading(false);
markStartupContentReady();
return; return;
} }
@ -6753,14 +6778,18 @@ const initViewer = async () => {
settingsSrc, settingsSrc,
hasViewerSettings: !!sharedViewerSettings, hasViewerSettings: !!sharedViewerSettings,
}); });
loadModel({ const startupModel = loadModel({
type, type,
url: src, url: src,
name, name,
replace: params.get("replace") !== "false", replace: params.get("replace") !== "false",
edges: params.has("edges") ? params.get("edges") !== "false" : undefined, edges: params.has("edges") ? params.get("edges") !== "false" : undefined,
meta: startupMeta, meta: startupMeta,
startupReveal: true,
}); });
if (!startupModel) {
markStartupContentReady();
}
}; };
const startupModelLoadPromise = loadStartupModelFromQuery().catch((err) => { const startupModelLoadPromise = loadStartupModelFromQuery().catch((err) => {
@ -6770,6 +6799,7 @@ const initViewer = async () => {
stack: err?.stack, stack: err?.stack,
}, "error"); }, "error");
showError(err.message || "Не удалось загрузить модель из URL"); showError(err.message || "Не удалось загрузить модель из URL");
markStartupContentReady();
}); });
const loadProjectSnapshot = async (project) => { const loadProjectSnapshot = async (project) => {
@ -7744,6 +7774,8 @@ const loadProjectSnapshot = async (project) => {
}); });
} }
appShellSetupComplete = true;
revealAppShell();
void startupModelLoadPromise; void startupModelLoadPromise;
}; };
@ -7754,6 +7786,7 @@ const loadProjectSnapshot = async (project) => {
stack: err?.stack, stack: err?.stack,
}, "error"); }, "error");
showError(err?.message || "Не удалось инициализировать viewer"); showError(err?.message || "Не удалось инициализировать viewer");
revealAppShell({ force: true });
}); });
}); });

View File

@ -8,7 +8,31 @@
<link rel="alternate icon" href="/assets/favicon/favicon.ico"> <link rel="alternate icon" href="/assets/favicon/favicon.ico">
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png"> <link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png">
<link rel="manifest" href="/assets/favicon/manifest.webmanifest.json"> <link rel="manifest" href="/assets/favicon/manifest.webmanifest.json">
<style id="preload-hidden">body{opacity:0;}</style> <style id="preload-hidden">
html,
body {
background: #1c1c1c;
}
body.app-starting header,
body.app-starting #dropZone,
body.app-starting #status,
body.app-starting #bottomToolbar,
body.app-starting #sectionToolbar,
body.app-starting #navCube,
body.app-starting .side-panel,
body.app-starting .comments-panel,
body.app-starting .templates-panel,
body.app-starting .settings-panel {
opacity: 0 !important;
pointer-events: none !important;
visibility: hidden !important;
}
body.app-starting #loader {
display: block !important;
}
</style>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap">
@ -231,9 +255,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/dcViewer.css?v=41"> <link rel="stylesheet" href="/dcViewer.css?v=42">
</head> </head>
<body> <body class="app-starting">
<header> <header>
<button class="logo-btn" id="homeButton" type="button" title="Открыть NODE.DC Hub"></button> <button class="logo-btn" id="homeButton" type="button" title="Открыть NODE.DC Hub"></button>
<div class="toolbar"></div> <div class="toolbar"></div>
@ -702,6 +726,6 @@
<input id="fileInput" class="hidden" type="file" <input id="fileInput" class="hidden" type="file"
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl"> accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
<script type="module" src="/dcViewer.js?v=63"></script> <script type="module" src="/dcViewer.js?v=64"></script>
</body> </body>
</html> </html>