diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-21 16:06:48 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-21 16:06:48 +0800 |
| commit | 8a1e79e46605291234314bd1a02dcab7a8c9bdc1 (patch) | |
| tree | 4ee6e820a07f6ce52ccddf6fb7ffe2d26fa86f8c /public | |
| parent | d2a4cb4e645979f20c22a5c46c1c767db460f54a (diff) | |
| download | somhairles-dream-fsharp-8a1e79e46605291234314bd1a02dcab7a8c9bdc1.tar.gz | |
feat(ui): replace id form with clickable model library cards
Diffstat (limited to 'public')
| -rw-r--r-- | public/App.js | 47 | ||||
| -rw-r--r-- | public/index.html | 41 | ||||
| -rw-r--r-- | public/styles.css | 55 |
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; |
