diff options
Diffstat (limited to 'src/SomhairlesDream.Frontend')
| -rw-r--r-- | src/SomhairlesDream.Frontend/App.fs | 38 | ||||
| -rw-r--r-- | src/SomhairlesDream.Frontend/Bindings.fs | 9 |
2 files changed, 45 insertions, 2 deletions
diff --git a/src/SomhairlesDream.Frontend/App.fs b/src/SomhairlesDream.Frontend/App.fs index 315a0fc..3665c43 100644 --- a/src/SomhairlesDream.Frontend/App.fs +++ b/src/SomhairlesDream.Frontend/App.fs @@ -269,6 +269,12 @@ let initializeView () = let bounceLight = createHemisphereLight("#3d4a56", "#141a1e", 0.9) scene.add(bounceLight) + // Coordinate ground grid so the model reads as anchored in a workspace + // instead of floating in a void. + let grid = createGridHelper (24.0, 48, "#5a4a2a", "#2a2f33") + setNodePositionY (grid, -1.35) + scene.add(grid) + let controls = createOrbitControls camera canvas setControlsTarget controls 0.0 0.0 0.0 setControlsDamping controls true @@ -361,10 +367,18 @@ let startStepLoad (view: ViewState) (owner: LoadOwner) (historical: bool) = viewMode <- StepMode failedLoad <- None setNoteRetryable false + // Swap the viewport to a loading state immediately: drop the previous + // mesh so the old geometry never lingers under the spinner. + clearGroup view.Group + displayedNode <- None + loadedStep <- None + toggleHidden ("viewport-loading", false) let loader = createGLTFLoader () let projectId = JS.encodeURIComponent owner.ProjectId let runId = JS.encodeURIComponent owner.RunId - let url = sprintf "/api/artifacts/steps?projectId=%s&runId=%s&path=%s" projectId runId (JS.encodeURIComponent owner.Path) + // Cache-buster: the GLB is always re-fetched from the server. + let stamp = int (JS.Constructors.Date.now () % 100000000.0) + let url = sprintf "/api/artifacts/steps?projectId=%s&runId=%s&path=%s&ts=%d" projectId runId (JS.encodeURIComponent owner.Path) stamp if historical then setText "viewport-note" (sprintf "历史几何加载中 · %s" owner.Path) @@ -380,6 +394,7 @@ let startStepLoad (view: ViewState) (owner: LoadOwner) (historical: bool) = displayedNode <- Some gltf.scene loadedStep <- Some owner frameObject view gltf.scene + toggleHidden ("viewport-loading", true) view.Renderer.render(view.Scene, view.Camera) if historical then @@ -393,6 +408,7 @@ let startStepLoad (view: ViewState) (owner: LoadOwner) (historical: bool) = pendingLoad <- None loadedStep <- None failedLoad <- Some(owner, historical) + toggleHidden ("viewport-loading", true) setNoteRetryable true if historical then @@ -790,8 +806,15 @@ let loadHistoryFor (selection: RunSelection) = match viewState with | Some view -> loadStepArtifact view steps[steps.Length - 1] true | None -> () + else + // Manifest answered but nothing is replayable: close the + // spinner instead of leaving it spinning forever. + toggleHidden ("viewport-loading", true) + setText "viewport-note" "该运行尚无可用历史几何" with _ -> - () + // Manifest unreachable: fail honestly rather than spin forever. + toggleHidden ("viewport-loading", true) + setText "viewport-note" "历史几何加载失败 · 清单不可用" } |> Async.StartImmediate let startRun (selection: RunSelection) = @@ -859,6 +882,17 @@ let selectModel (card: HTMLElement) = activeModel <- selection highlightActiveCard selection setText "run-message" "载入模型历史" + // Enter the loading state before the manifest round-trip: the previous + // mesh is dropped and the spinner appears the instant a card is clicked. + (match viewState with + | Some view -> + clearGroup view.Group + displayedNode <- None + loadedStep <- None + | None -> ()) + + toggleHidden ("viewport-loading", false) + setText "viewport-note" "历史几何加载中" loadHistoryFor selection let boot () = diff --git a/src/SomhairlesDream.Frontend/Bindings.fs b/src/SomhairlesDream.Frontend/Bindings.fs index b6c4353..7f0ce31 100644 --- a/src/SomhairlesDream.Frontend/Bindings.fs +++ b/src/SomhairlesDream.Frontend/Bindings.fs @@ -163,6 +163,15 @@ let setToneMappingACES (renderer: obj) : unit = jsNative [<Emit("$0.toneMappingExposure = $1")>] let setToneMappingExposure (renderer: obj, exposure: float) : unit = jsNative +[<Emit("new THREE.GridHelper($0, $1, $2, $3)")>] +let createGridHelper (size: float, divisions: int, centerColor: string, edgeColor: string) : ThreeNode = jsNative + +[<Emit("$0.position.y = $1")>] +let setNodePositionY (node: ThreeNode, y: float) : unit = jsNative + +[<Emit("document.getElementById($0).classList.toggle('is-hidden', $1)")>] +let toggleHidden (id: string, hidden: bool) : unit = jsNative + [<Emit("$0.outputColorSpace = THREE.SRGBColorSpace")>] let setSRGBOutput (renderer: obj) : unit = jsNative |
