summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 16:06:48 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 16:06:48 +0800
commit8a1e79e46605291234314bd1a02dcab7a8c9bdc1 (patch)
tree4ee6e820a07f6ce52ccddf6fb7ffe2d26fa86f8c /public
parentd2a4cb4e645979f20c22a5c46c1c767db460f54a (diff)
downloadsomhairles-dream-fsharp-8a1e79e46605291234314bd1a02dcab7a8c9bdc1.tar.gz
feat(ui): replace id form with clickable model library cards
Diffstat (limited to 'public')
-rw-r--r--public/App.js47
-rw-r--r--public/index.html41
-rw-r--r--public/styles.css55
3 files changed, 108 insertions, 35 deletions
diff --git a/public/App.js b/public/App.js
index 4716bb3..f63ae7e 100644
--- a/public/App.js
+++ b/public/App.js
@@ -6,7 +6,7 @@ import { sortBy, map, equalsWith, tryFind, iterateIndexed, item } from "./fable_
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";
-import { defaultArg } from "./fable_modules/fable-library-js.4.24.0/Option.js";
+import { some, defaultArg } from "./fable_modules/fable-library-js.4.24.0/Option.js";
import { awaitPromise, startImmediate } from "./fable_modules/fable-library-js.4.24.0/Async.js";
import { singleton } from "./fable_modules/fable-library-js.4.24.0/AsyncBuilder.js";
@@ -867,8 +867,29 @@ export function inputValue(id) {
return element(id).value;
}
+export const probeSelection = new RunSelection("probe-001", "run-probe-real-2", "probe-base-000", "probe-target-007", false);
+
+export const shipSelection = new RunSelection("ship-001", "run-ship-001", "ship-base-000", "ship-target-007", false);
+
+export let activeModel = createAtom(probeSelection);
+
export function selectedRun() {
- return new RunSelection(inputValue("project-id-input"), inputValue("run-id-input"), inputValue("base-id-input"), inputValue("target-id-input"), element("run-render-input").checked);
+ return activeModel();
+}
+
+export function highlightActiveCard(selection) {
+ const library = element("model-library");
+ for (let index = 0; index <= (library.children.length - 1); index++) {
+ const card = library.children.item(some(index));
+ if (card.classList.contains("model-card")) {
+ if ((card.getAttribute("data-project") === selection.ProjectId) && (card.getAttribute("data-run") === selection.RunId)) {
+ card.classList.add("is-active");
+ }
+ else {
+ card.classList.remove("is-active");
+ }
+ }
+ }
}
export function freshLiveRunId(base_) {
@@ -993,6 +1014,14 @@ export function loadBootHistory() {
loadHistoryFor(selectedRun());
}
+export function selectModel(card) {
+ const selection = new RunSelection(card.getAttribute("data-project"), card.getAttribute("data-run"), card.getAttribute("data-base"), card.getAttribute("data-target"), false);
+ activeModel(selection);
+ highlightActiveCard(selection);
+ setText("run-message", "载入模型历史");
+ loadHistoryFor(selection);
+}
+
export function boot() {
showFrameMetadata(item(0, frames));
try {
@@ -1010,10 +1039,16 @@ export function boot() {
element("viewport-note").addEventListener("click", (_arg_2) => {
retryFailedLoad();
});
- element("run-controls").addEventListener("submit", (event) => {
- event.preventDefault();
- startRun(selectionForLiveStart(selectedRun()));
- });
+ const library = element("model-library");
+ for (let index = 0; index <= (library.children.length - 1); index++) {
+ const card = library.children.item(some(index));
+ if (card.classList.contains("model-card")) {
+ card.addEventListener("click", (_arg_3) => {
+ selectModel(card);
+ });
+ }
+ }
+ highlightActiveCard(activeModel());
loadBootHistory();
renderTimeline();
}
diff --git a/public/index.html b/public/index.html
index b2f8703..63fa727 100644
--- a/public/index.html
+++ b/public/index.html
@@ -54,34 +54,17 @@
</div>
</div>
<p id="run-message" class="status-message">当前没有下一版正在开发</p>
- <form id="run-controls" class="run-controls">
- <div class="control-grid">
- <label>
- <span>项目 ID</span>
- <input id="project-id-input" value="probe-001" autocomplete="off">
- </label>
- <label>
- <span>运行 ID</span>
- <input id="run-id-input" value="run-probe-real-2" autocomplete="off">
- </label>
- <label>
- <span>基准 ID</span>
- <input id="base-id-input" value="probe-base-000" autocomplete="off">
- </label>
- <label>
- <span>目标 ID</span>
- <input id="target-id-input" value="probe-target-007" autocomplete="off">
- </label>
- </div>
- <div class="control-actions">
- <label class="render-toggle">
- <input id="run-render-input" type="checkbox">
- <span>渲染预览 PNG</span>
- </label>
- <button id="run-start-button" class="run-start-button" type="submit">启动设计运行</button>
- </div>
- <a id="artifact-manifest-link" class="artifact-link is-hidden" target="_blank" rel="noreferrer">打开 artifact manifest</a>
- </form>
+ <div id="model-library" class="model-library">
+ <p class="library-title">模型库 · 点击浏览</p>
+ <button type="button" class="model-card" data-project="probe-001" data-run="run-probe-real-2" data-base="probe-base-000" data-target="probe-target-007">
+ <span class="model-name">YJ-01 盘形探测器</span>
+ <span class="model-desc">无人星际探测器 · 光帆/RTG · 7 步构建</span>
+ </button>
+ <button type="button" class="model-card" data-project="ship-001" data-run="run-ship-001" data-base="ship-base-000" data-target="ship-target-007">
+ <span class="model-name">HNX-01 "Keel" 载人星舰</span>
+ <span class="model-desc">旋转 habitat 环 · 水屏蔽 · 聚变火炬 · 7 步构建</span>
+ </button>
+ </div>
<div class="version-block">
<div class="label-row">
<span>设计版本</span>
@@ -196,6 +179,6 @@
<span>heritage 参考线保留占位演示 · F# / OPEN CODE</span>
</footer>
</main>
- <script type="module" src="/App.js?v=ship1"></script>
+ <script type="module" src="/App.js?v=model1"></script>
</body>
</html>
diff --git a/public/styles.css b/public/styles.css
index a925e58..d6cfbb1 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -229,6 +229,61 @@ button:focus-visible {
display: none !important;
}
+.model-library {
+ display: grid;
+ gap: 9px;
+ margin: 0 0 22px;
+ padding: 14px 0 20px;
+ border-top: 1px solid var(--line);
+ border-bottom: 1px solid var(--line);
+}
+
+.library-title {
+ color: var(--muted);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.62rem;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+
+.model-card {
+ display: grid;
+ gap: 5px;
+ padding: 11px 13px;
+ border: 1px solid var(--line-bright);
+ border-radius: 0;
+ background: var(--panel-raised);
+ color: var(--ivory);
+ text-align: left;
+ cursor: pointer;
+ transition: border-color 0.15s ease, background-color 0.15s ease;
+}
+
+.model-card:hover {
+ border-color: var(--gold-soft);
+ background: rgba(200, 168, 92, 0.08);
+}
+
+.model-card:focus-visible {
+ outline: 2px solid var(--gold-soft);
+ outline-offset: 2px;
+}
+
+.model-card.is-active {
+ border-color: var(--gold-soft);
+ background: rgba(200, 168, 92, 0.1);
+}
+
+.model-name {
+ font-size: 0.95rem;
+ font-weight: 600;
+}
+
+.model-desc {
+ color: var(--muted);
+ font-size: 0.68rem;
+}
+
.run-controls {
display: grid;
gap: 14px;