推进 INI 面板 entry manifest

This commit is contained in:
2026-06-14 23:16:52 +08:00
parent c18aa21d70
commit 77d32a3f17
8 changed files with 154 additions and 2 deletions

View File

@@ -1327,3 +1327,66 @@ host_wasm_opfs_browser_smokes=ok
继续保持实质 UI workflow 优先。下一批建议把 launcher 中的入口清单抽成一个小的 继续保持实质 UI workflow 优先。下一批建议把 launcher 中的入口清单抽成一个小的
`entry-manifest.js`,供 launcher、docs 或未来外层 shell 共用,并用 node/browser smoke 验证 `entry-manifest.js`,供 launcher、docs 或未来外层 shell 共用,并用 node/browser smoke 验证
入口 href 与标题一致;仍保持只读,不新增控制按钮,不解析 G-code。 入口 href 与标题一致;仍保持只读,不新增控制按钮,不解析 G-code。
十六、2026-06-14 继续执行记录INI panel entry manifest
本轮按“加快实质性推进”把 launcher 的入口清单抽成共享 manifest使入口 href/title 成为
可复用的 UI entry contract而不是散落在 HTML 和 smoke 断言里。
完成内容:
- 新增 `wasm-port/runtime/ui/ini-panel/entry-manifest.js`
- 导出:
- `INI_PANEL_ENTRIES`
- `getIniPanelEntryManifest()`
- `getIniPanelEntryByHref(href)`
- `wasm-port/runtime/ui/ini-panel/launch.html` 改为从 manifest 同步入口标题和 href
- `wasm-port/tests/browser/ini_panel_launch_smoke.html` 改为同时验证 manifest 与 launcher DOM
- 新增 `wasm-port/tests/ui/node/verify_ini_panel_entry_manifest.mjs`
- 新增 `wasm-port/tests/ui/node/verify_ini_panel_entry_manifest.sh`
- `wasm-port/tests/ui/node/verify_ui_node_smokes.sh` 纳入 entry manifest node smoke
- `wasm-port/docs/ui-panel-state-summary.md` 补充 entry manifest 说明;
- 未新增控制按钮;
- 未解析 G-code
- 未解释 canonical events
- 未改变 OPFS/session persistence、LinuxCNC interpreter、tool、parameter、planner 或
LinuxCNC-owned runtime semantics。
验证已通过:
```bash
git diff --check
wasm-port/tests/ui/node/verify_ini_panel_entry_manifest.sh
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
wasm-port/tests/browser/verify_ini_panel_browser.sh
wasm-port/tools/verify_vendor_sync.sh
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/host/verify_host_smokes.sh
```
关键输出:
```text
ini_panel_entry_manifest_node_smoke=ok
ui_node_smokes=ok
browser_ini_opfs_smoke=ok
browser_ini_control_page_smoke=ok
browser_ini_launch_smoke=ok
vendor sync up to date
standalone CNC semantics guard complete
interp_wasm_node_smoke=ok
sim_configs_wasm_node_inventory_executed=28
sim_configs_wasm_node_inventory_passed=28
sim_configs_wasm_node_inventory_skipped=131
sim_configs_wasm_node_inventory_unexpected_fail=0
host_wasm_opfs_browser_smokes=ok
```
下一步建议:
继续优先推进可复用 UI/API surface。下一批建议把 `entry-manifest.js` 与 control page
controller/renderer 组合成一个更明确的 `ui-shell` 或 `panel-entry` 文档/API 入口,或者新增
一个 node smoke 验证 docs 中列出的入口与 manifest 完全一致;仍保持只读,不新增控制按钮,
不解析 G-code。

View File

@@ -33,6 +33,7 @@ The page uses `createMachineSessionControlPageController(...)` from
`runtime/ui/ini-panel/control-page-controller.js` so other read-only shells can `runtime/ui/ini-panel/control-page-controller.js` so other read-only shells can
reuse the same iframe/API polling contract. reuse the same iframe/API polling contract.
For a light navigation entry, open `runtime/ui/ini-panel/launch.html`. For a light navigation entry, open `runtime/ui/ini-panel/launch.html`.
Its entry list is defined in `runtime/ui/ini-panel/entry-manifest.js`.
## `createIniPanelStateSummary(input)` ## `createIniPanelStateSummary(input)`

View File

@@ -0,0 +1,20 @@
export const INI_PANEL_ENTRIES = [
{
id: "edit-run",
title: "Open edit and run panel",
href: "./index.html",
},
{
id: "control-view",
title: "Open read-only control view",
href: "./control-page.html",
},
];
export function getIniPanelEntryManifest() {
return INI_PANEL_ENTRIES.map((entry) => ({ ...entry }));
}
export function getIniPanelEntryByHref(href) {
return INI_PANEL_ENTRIES.find((entry) => entry.href === href) ?? null;
}

View File

@@ -67,10 +67,23 @@
<h1>LinuxCNC INI Panel</h1> <h1>LinuxCNC INI Panel</h1>
<p>Choose the editable panel or the read-only control view.</p> <p>Choose the editable panel or the read-only control view.</p>
<div class="links"> <div class="links">
<a href="./index.html">Open edit and run panel</a> <a data-entry-id="edit-run" href="./index.html">Open edit and run panel</a>
<a href="./control-page.html">Open read-only control view</a> <a data-entry-id="control-view" href="./control-page.html">Open read-only control view</a>
</div> </div>
</section> </section>
</main> </main>
<script type="module">
import { getIniPanelEntryManifest } from "./entry-manifest.js";
const manifest = getIniPanelEntryManifest();
for (const entry of manifest) {
const link = document.querySelector(`a[data-entry-id="${entry.id}"]`);
if (!link) {
throw new Error(`Missing launcher entry ${entry.id}`);
}
link.textContent = entry.title;
link.href = entry.href;
}
</script>
</body> </body>
</html> </html>

View File

@@ -7,6 +7,11 @@
<body> <body>
<pre id="status">running</pre> <pre id="status">running</pre>
<script type="module"> <script type="module">
import {
getIniPanelEntryByHref,
getIniPanelEntryManifest,
} from "../../runtime/ui/ini-panel/entry-manifest.js";
function assertEqual(actual, expected, label) { function assertEqual(actual, expected, label) {
if (actual !== expected) { if (actual !== expected) {
throw new Error(`${label}: expected ${expected}, got ${actual}`); throw new Error(`${label}: expected ${expected}, got ${actual}`);
@@ -25,6 +30,15 @@
} }
try { try {
const manifest = getIniPanelEntryManifest();
assertEqual(manifest.length, 2, "launch manifest length");
assertEqual(getIniPanelEntryByHref("./index.html").id, "edit-run", "launch manifest edit entry");
assertEqual(
getIniPanelEntryByHref("./control-page.html").title,
"Open read-only control view",
"launch manifest control entry",
);
const launchDoc = await loadLaunchFrame(); const launchDoc = await loadLaunchFrame();
assertEqual( assertEqual(
launchDoc.querySelector('a[href="./index.html"]').textContent, launchDoc.querySelector('a[href="./index.html"]').textContent,

View File

@@ -0,0 +1,34 @@
import assert from "node:assert/strict";
import {
INI_PANEL_ENTRIES,
getIniPanelEntryByHref,
getIniPanelEntryManifest,
} from "../../../runtime/ui/ini-panel/entry-manifest.js";
assert.deepEqual(INI_PANEL_ENTRIES, [
{
id: "edit-run",
title: "Open edit and run panel",
href: "./index.html",
},
{
id: "control-view",
title: "Open read-only control view",
href: "./control-page.html",
},
]);
const manifest = getIniPanelEntryManifest();
assert.deepEqual(manifest, INI_PANEL_ENTRIES);
assert.notStrictEqual(manifest, INI_PANEL_ENTRIES);
assert.notStrictEqual(manifest[0], INI_PANEL_ENTRIES[0]);
assert.deepEqual(getIniPanelEntryByHref("./index.html"), INI_PANEL_ENTRIES[0]);
assert.deepEqual(getIniPanelEntryByHref("./control-page.html"), INI_PANEL_ENTRIES[1]);
assert.equal(getIniPanelEntryByHref("./missing.html"), null);
manifest[0].title = "changed";
assert.equal(INI_PANEL_ENTRIES[0].title, "Open edit and run panel");
console.log("ini_panel_entry_manifest_node_smoke=ok");

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
node "$ROOT_DIR/tests/ui/node/verify_ini_panel_entry_manifest.mjs"

View File

@@ -10,5 +10,6 @@ ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
"$ROOT_DIR/tests/ui/node/verify_ini_panel_machine_file_summary.sh" "$ROOT_DIR/tests/ui/node/verify_ini_panel_machine_file_summary.sh"
"$ROOT_DIR/tests/ui/node/verify_ini_panel_state_summary.sh" "$ROOT_DIR/tests/ui/node/verify_ini_panel_state_summary.sh"
"$ROOT_DIR/tests/ui/node/verify_ini_panel_control_page_controller.sh" "$ROOT_DIR/tests/ui/node/verify_ini_panel_control_page_controller.sh"
"$ROOT_DIR/tests/ui/node/verify_ini_panel_entry_manifest.sh"
echo "ui_node_smokes=ok" echo "ui_node_smokes=ok"