summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 10:14:24 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 10:14:24 +0800
commit958e24b0fd08e4bc92656964e832606dbf7d3fa3 (patch)
tree01606b1c010fecc4d3379cbc796c100c32363bac /public
parent07d6690b9b9de7db7debe25e8e3def5c0d68bb26 (diff)
downloadsomhairles-dream-fsharp-958e24b0fd08e4bc92656964e832606dbf7d3fa3.tar.gz
feat(boot): surface pre-published probe replay on landing page
- boot loads the completed run from /api/artifacts/manifest and renders its latest checkpoint with concrete build-review labels (no fake unknown-state wording); offline fallback still shows the honest placeholder - artifact manifest/steps endpoints now fall back to disk when a completed run has no in-memory registry entry, keeping pre-published history browsable after a service restart (digest-verified, .glb-only / steps/ / logs/ rejected) - driver: heritage flows pin heritage ids explicitly; new checks for the boot probe replay and its labels; 72/72 green with zero console errors
Diffstat (limited to 'public')
-rw-r--r--public/App.js65
-rw-r--r--public/index.html18
2 files changed, 72 insertions, 11 deletions
diff --git a/public/App.js b/public/App.js
index ab4ae59..a401102 100644
--- a/public/App.js
+++ b/public/App.js
@@ -1,8 +1,8 @@
import { Union, Record } from "./fable_modules/fable-library-js.4.24.0/Types.js";
import { union_type, class_type, record_type, bool_type, string_type } from "./fable_modules/fable-library-js.4.24.0/Reflection.js";
import { Replay_frames } from "./SomhairlesDream.Shared/Replay.js";
-import { defaultOf, equals, createAtom } from "./fable_modules/fable-library-js.4.24.0/Util.js";
-import { equalsWith, tryFind, iterateIndexed, item } from "./fable_modules/fable-library-js.4.24.0/Array.js";
+import { comparePrimitives, defaultOf, equals, createAtom } from "./fable_modules/fable-library-js.4.24.0/Util.js";
+import { sortBy, map, equalsWith, tryFind, iterateIndexed, item } from "./fable_modules/fable-library-js.4.24.0/Array.js";
import { join, isNullOrWhiteSpace, printf, toText } from "./fable_modules/fable-library-js.4.24.0/String.js";
import { MeshData_triangleIndices, MeshData_positionData } from "./SomhairlesDream.Shared/Domain.js";
import { min, max } from "./fable_modules/fable-library-js.4.24.0/Double.js";
@@ -881,6 +881,66 @@ export function startRun(selection) {
})));
}
+/**
+ * Boot-time historical load: surface the pre-published probe build replay so
+ * first-time visitors see the real vehicle instead of an empty placeholder.
+ * Any failure leaves the honest placeholder idle state untouched.
+ */
+export function loadBootHistory() {
+ const selection = selectedRun();
+ const projectId = encodeURIComponent(selection.ProjectId);
+ const runId = encodeURIComponent(selection.RunId);
+ const url = toText(printf("/api/artifacts/manifest?projectId=%s&runId=%s"))(projectId)(runId);
+ const requestOptions = {
+ method: "GET",
+ };
+ startImmediate(singleton.Delay(() => singleton.TryWith(singleton.Delay(() => singleton.Bind(awaitPromise(fetch(url, requestOptions)), (_arg) => {
+ const response = _arg;
+ return response.ok ? singleton.Bind(awaitPromise(response.json()), (_arg_1) => {
+ let arg_2, arg_3, arg_4, arg_5, arg_6, arg_7, arg_8;
+ const payload = _arg_1;
+ if ((payload.status === "complete") && (payload.steps.length > 0)) {
+ const steps = map((step_1) => step_1.artifactPath, sortBy((step) => step.stepIndex, payload.steps, {
+ Compare: comparePrimitives,
+ }));
+ if (steps.length > 0) {
+ currentProjectId(selection.ProjectId);
+ currentRunId(selection.RunId);
+ historicalSteps(steps);
+ setText("project-id", selection.ProjectId);
+ setText("run-id", selection.RunId);
+ setText("target-version", (arg_2 = (steps.length | 0), toText(printf("V%02d"))(arg_2)));
+ setText("current-version", (arg_3 = (steps.length | 0), toText(printf("V%02d"))(arg_3)));
+ setText("version-readout", (arg_4 = (steps.length | 0), (arg_5 = (steps.length | 0), toText(printf("%02d / %02d"))(arg_4)(arg_5))));
+ setWidth("progress-fill", "100%");
+ setText("updated-at", "预发布历史");
+ setText("mesh-label", (arg_6 = item(steps.length - 1, steps), toText(printf("历史 · %s"))(arg_6)));
+ setText("timeline-current", (arg_7 = (steps.length | 0), (arg_8 = item(steps.length - 1, steps), toText(printf("V%02d · 构建重演 · %s"))(arg_7)(arg_8))));
+ setText("replay-button", "播放历史");
+ setText("replay-state", "构建重演 · 预发布历史");
+ renderTimeline();
+ updateFollowButton();
+ if (viewState() == null) {
+ return singleton.Zero();
+ }
+ else {
+ loadStepArtifact(viewState(), item(steps.length - 1, steps), true);
+ return singleton.Zero();
+ }
+ }
+ else {
+ return singleton.Zero();
+ }
+ }
+ else {
+ return singleton.Zero();
+ }
+ }) : singleton.Zero();
+ })), (_arg_2) => {
+ return singleton.Zero();
+ })));
+}
+
export function boot() {
showFrameMetadata(item(0, frames));
try {
@@ -902,6 +962,7 @@ export function boot() {
event.preventDefault();
startRun(selectedRun());
});
+ loadBootHistory();
renderTimeline();
}
diff --git a/public/index.html b/public/index.html
index 71e9e62..d6966b1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -32,11 +32,11 @@
<section class="mission-strip" aria-label="当前任务">
<div>
<span class="eyebrow">PROJECT</span>
- <strong id="project-id">heritage-001</strong>
+ <strong id="project-id">probe-001</strong>
</div>
<div>
<span class="eyebrow">RUN</span>
- <strong id="run-id">run-001</strong>
+ <strong id="run-id">run-probe-real-2</strong>
</div>
<div class="mission-note">
<span class="eyebrow">MODE</span>
@@ -58,19 +58,19 @@
<div class="control-grid">
<label>
<span>项目 ID</span>
- <input id="project-id-input" value="heritage-001" autocomplete="off">
+ <input id="project-id-input" value="probe-001" autocomplete="off">
</label>
<label>
<span>运行 ID</span>
- <input id="run-id-input" value="run-001" autocomplete="off">
+ <input id="run-id-input" value="run-probe-real-2" autocomplete="off">
</label>
<label>
<span>基准 ID</span>
- <input id="base-id-input" value="base-000" autocomplete="off">
+ <input id="base-id-input" value="probe-base-000" autocomplete="off">
</label>
<label>
<span>目标 ID</span>
- <input id="target-id-input" value="target-003" autocomplete="off">
+ <input id="target-id-input" value="probe-target-007" autocomplete="off">
</label>
</div>
<div class="control-actions">
@@ -85,7 +85,7 @@
<div class="version-block">
<div class="label-row">
<span>设计版本</span>
- <strong id="version-readout">00 / 03</strong>
+ <strong id="version-readout">07 / 07</strong>
</div>
<div class="progress-track" aria-hidden="true">
<span id="progress-fill" class="progress-fill" style="width: 0%"></span>
@@ -192,8 +192,8 @@
</section>
<footer class="footer-note">
- <span>状态快照 · 原子发布 · 心跳监视</span>
- <span>OPEN CODE / F# FIRST</span>
+ <span>首页构建重演:预发布历史透视探针(非飞行设计 · 盘式天线 / RTG / 科学吊臂 简化几何)</span>
+ <span>heritage 参考线保留占位演示 · F# / OPEN CODE</span>
</footer>
</main>
<script type="module" src="/App.js"></script>