From 13479cd194cd992c67805093e59f8b1dd1b5f3a2 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 21 Sep 2026 06:05:15 +0800 Subject: feat(viewer): add user retry for failed step loads and regressions for same-run ordering and replay invalidation --- public/App.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ public/styles.css | 5 +++++ 2 files changed, 59 insertions(+) (limited to 'public') diff --git a/public/App.js b/public/App.js index 4b842d1..70d2377 100644 --- a/public/App.js +++ b/public/App.js @@ -92,6 +92,8 @@ export let loadedStep = createAtom(undefined); export let viewMode = createAtom(new ViewMode(0, [])); +export let failedLoad = createAtom(undefined); + export let displayedNode = createAtom(undefined); export let freezeRotation = createAtom(false); @@ -218,6 +220,7 @@ export function clearGroup(group) { export function invalidateLoads() { loadGeneration(loadGeneration() + 1); pendingLoad(undefined); + failedLoad(undefined); } export function renderFrame(frame) { @@ -320,6 +323,16 @@ export function fallbackView() { element("viewport-panel").classList.add("viewport-fallback"); } +export function setNoteRetryable(retryable) { + const note = element("viewport-note"); + if (retryable) { + note.classList.add("is-retryable"); + } + else { + note.classList.remove("is-retryable"); + } +} + export function showFrameMetadata(frame) { setText("mesh-label", frame.Label); setText("timeline-current", toText(printf("V%02d · %s"))(frame.Version)(frame.Label)); @@ -343,6 +356,8 @@ export function startStepLoad(view, owner) { loadGeneration(generation); pendingLoad([generation, owner]); viewMode(new ViewMode(1, [])); + failedLoad(undefined); + setNoteRetryable(false); const loader = new GLTFLoader(); const projectId = encodeURIComponent(owner.ProjectId); const runId = encodeURIComponent(owner.RunId); @@ -368,6 +383,8 @@ export function startStepLoad(view, owner) { if (equals(pendingLoad(), [generation, owner])) { pendingLoad(undefined); loadedStep(undefined); + failedLoad(owner); + setNoteRetryable(true); setText("viewport-note", toText(printf("管线几何加载失败 · %s"))(owner.Path)); } })); @@ -399,6 +416,40 @@ export function loadStepArtifact(view, stepPath) { } } +export function retryFailedLoad() { + let view, owner; + const matchValue = viewState(); + const matchValue_1 = failedLoad(); + let matchResult, owner_1, view_1; + if (matchValue != null) { + if (matchValue_1 != null) { + if ((view = matchValue, (owner = matchValue_1, (owner.ProjectId === currentProjectId()) && (owner.RunId === currentRunId())))) { + matchResult = 0; + owner_1 = matchValue_1; + view_1 = matchValue; + } + else { + matchResult = 1; + } + } + else { + matchResult = 1; + } + } + else { + matchResult = 1; + } + switch (matchResult) { + case 0: { + loadStepArtifact(view_1, owner_1.Path); + break; + } + case 1: { + break; + } + } +} + export function updateState(payload) { let arg, arg_3; setStatusClass(statusName(payload)); @@ -573,6 +624,9 @@ export function boot() { element("replay-button").addEventListener("click", (_arg) => { toggleReplay(); }); + element("viewport-note").addEventListener("click", (_arg_1) => { + retryFailedLoad(); + }); element("run-controls").addEventListener("submit", (event) => { event.preventDefault(); startRun(selectedRun()); diff --git a/public/styles.css b/public/styles.css index 0ee02ef..6346df5 100644 --- a/public/styles.css +++ b/public/styles.css @@ -690,3 +690,8 @@ button:focus-visible { animation-iteration-count: 1 !important; } } + +.viewport-note.is-retryable { + cursor: pointer; + pointer-events: auto; +} -- cgit v1.2.3