diff --git a/text11.txt b/text11.txt index 2e9452b..3a0b934 100644 --- a/text11.txt +++ b/text11.txt @@ -1079,3 +1079,67 @@ host_wasm_opfs_browser_smokes=ok 继续推进实质控制网页界面,但保持只读边界。下一批建议把这个 DOM consumer 提取为一个 可复用的纯 renderer helper,例如 `renderMachineSessionControlView(container, view)`, 供真实页面和 smoke 共用;仍不新增控制按钮,不解析 G-code。 + +十二、2026-06-14 继续执行记录:INI panel control view renderer helper + +本轮按“加快实质性推进”把上一批 browser smoke 内的只读 DOM consumer 提取成 runtime +可复用 renderer,并接入真实 INI panel 页面,使控制页展示不再依赖测试内临时函数。 + +完成内容: + +- 新增 `wasm-port/runtime/ui/ini-panel/control-view-renderer.js`; +- 导出 `renderMachineSessionControlView(container, view)`; +- renderer 只负责把 `createMachineSessionControlView(...)` 的 status、sections、rows 渲染 + 到 DOM; +- `wasm-port/runtime/ui/ini-panel/index.html` 新增 + `#machine-session-control-view` 只读控制视图容器和对应样式; +- `wasm-port/runtime/ui/ini-panel/app.js` 新增 + `window.linuxCncIniPanelApi.renderMachineSessionControlView(container)`; +- panel state 每次更新后会刷新内置 control view; +- `wasm-port/tests/browser/ini_panel_smoke.html` 删除测试内临时 `renderControlView(...)`, + 改为导入 runtime renderer; +- browser smoke 同时验证外部 consumer 容器和真实页面内置 control view 容器; +- `wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs` 覆盖纯 renderer DOM 输出; +- `wasm-port/docs/ui-panel-state-summary.md` 补充 renderer API 和外部页面消费方式; +- 未新增控制按钮; +- 未解析 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_state_summary.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_state_summary_node_smoke=ok +ui_node_smokes=ok +browser_ini_opfs_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 +``` + +下一步建议: + +继续保持只读边界,下一批建议把 control view renderer 的页面消费路径进一步固化为一个 +小型控制页入口或 example page:只读取 `linuxCncIniPanelApi`,展示当前 machine/session/run +状态,不新增控制按钮,不解析 G-code。若要优先 SDK/API surface,也可以先把当前 +state bundle/control view renderer 的导出面整理进一个明确的 UI module 文档入口。 diff --git a/wasm-port/docs/ui-panel-state-summary.md b/wasm-port/docs/ui-panel-state-summary.md index 3f00c22..0698e04 100644 --- a/wasm-port/docs/ui-panel-state-summary.md +++ b/wasm-port/docs/ui-panel-state-summary.md @@ -23,6 +23,9 @@ For control-page polling, use smaller workflow-facing object derived from the same report data. For a single read call, use `window.linuxCncIniPanelApi.getMachineSessionStateBundle()`. For direct rendering, use `window.linuxCncIniPanelApi.getMachineSessionControlView()`. +The panel also exposes +`window.linuxCncIniPanelApi.renderMachineSessionControlView(container)`, which +renders the current view into a supplied DOM container. ## `createIniPanelStateSummary(input)` @@ -164,15 +167,22 @@ canonical events, or LinuxCNC-owned runtime content. A minimal read-only browser consumer can render the sections directly: ```js -const view = window.linuxCncIniPanelApi.getMachineSessionControlView(); -for (const section of view.sections) { - renderSection(section.title, section.rows); -} +window.linuxCncIniPanelApi.renderMachineSessionControlView( + document.getElementById("machine-session-control-view"), +); ``` Consumers should treat `rows` as display data and keep actions wired through explicit workflow APIs instead of deriving behavior from labels. +For external pages that only have a view object, import the pure renderer: + +```js +import { renderMachineSessionControlView } from "./control-view-renderer.js"; + +renderMachineSessionControlView(container, view); +``` + ## Boundary Rules - Do not parse G-code text in this helper. diff --git a/wasm-port/runtime/ui/ini-panel/app.js b/wasm-port/runtime/ui/ini-panel/app.js index 83947d7..2ce957c 100644 --- a/wasm-port/runtime/ui/ini-panel/app.js +++ b/wasm-port/runtime/ui/ini-panel/app.js @@ -53,6 +53,9 @@ import { createMachineSessionStateReportExport, createMachineSessionWorkflowStatus, } from "./panel-state-summary.js"; +import { + renderMachineSessionControlView, +} from "./control-view-renderer.js"; const SAMPLE_PATH = "../../../vendor/linuxcnc/configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini"; @@ -114,6 +117,7 @@ const runProgressLabelNode = document.getElementById("run-progress-label"); const runMotionNode = document.getElementById("run-motion"); const runLineNode = document.getElementById("run-line"); const runStatementNode = document.getElementById("run-statement"); +const controlViewNode = document.getElementById("machine-session-control-view"); const axisNodes = { x: document.getElementById("axis-x"), y: document.getElementById("axis-y"), @@ -176,6 +180,10 @@ function getMachineSessionControlView() { return createMachineSessionControlView(getMachineSessionStateBundle()); } +function renderCurrentMachineSessionControlView(container = controlViewNode) { + return renderMachineSessionControlView(container, getMachineSessionControlView()); +} + function updatePanelMachineSessionState(patch = {}) { panelMachineSessionState = createMachineSessionStateSnapshot({ ...panelMachineSessionState, @@ -186,6 +194,7 @@ function updatePanelMachineSessionState(patch = {}) { }, }); window.linuxCncIniPanelMachineSessionState = panelMachineSessionState; + renderCurrentMachineSessionControlView(); return panelMachineSessionState; } @@ -195,13 +204,16 @@ window.linuxCncIniPanelApi = { getMachineSessionStateReport, getMachineSessionStateReportExport, getMachineSessionWorkflowStatus, + renderMachineSessionControlView: renderCurrentMachineSessionControlView, }; window.getMachineSessionControlView = getMachineSessionControlView; window.getMachineSessionStateBundle = getMachineSessionStateBundle; window.getMachineSessionStateReport = getMachineSessionStateReport; window.getMachineSessionStateReportExport = getMachineSessionStateReportExport; window.getMachineSessionWorkflowStatus = getMachineSessionWorkflowStatus; +window.renderMachineSessionControlView = renderCurrentMachineSessionControlView; window.linuxCncIniPanelMachineSessionState = panelMachineSessionState; +renderCurrentMachineSessionControlView(); function setBadge(node, text, className = "badge") { node.className = className; diff --git a/wasm-port/runtime/ui/ini-panel/control-view-renderer.js b/wasm-port/runtime/ui/ini-panel/control-view-renderer.js new file mode 100644 index 0000000..6f23a77 --- /dev/null +++ b/wasm-port/runtime/ui/ini-panel/control-view-renderer.js @@ -0,0 +1,60 @@ +function displayValue(value) { + return String(value ?? "-"); +} + +function statusText(status = {}) { + return [ + status.lastAction ?? "-", + status.runStatus ?? "-", + status.error ?? "-", + ].join(" | "); +} + +export function renderMachineSessionControlView(container, view = {}) { + if (!container) { + throw new Error("A control view container is required."); + } + + const document = container.ownerDocument; + container.textContent = ""; + + const statusNode = document.createElement("div"); + statusNode.dataset.controlViewStatus = ""; + statusNode.textContent = statusText(view.status); + container.appendChild(statusNode); + + const sectionsNode = document.createElement("div"); + sectionsNode.dataset.controlViewSections = ""; + container.appendChild(sectionsNode); + + const sectionNodes = []; + for (const section of view.sections ?? []) { + const sectionNode = document.createElement("section"); + sectionNode.dataset.sectionId = section.id; + sectionNode.dataset.controlViewSectionId = section.id; + + const heading = document.createElement("h3"); + heading.textContent = section.title; + sectionNode.appendChild(heading); + + const list = document.createElement("dl"); + for (const row of section.rows ?? []) { + const term = document.createElement("dt"); + term.textContent = row.label; + const def = document.createElement("dd"); + def.dataset.controlViewRow = row.label; + def.textContent = displayValue(row.value); + list.append(term, def); + } + + sectionNode.appendChild(list); + sectionsNode.appendChild(sectionNode); + sectionNodes.push(sectionNode); + } + + return { + statusNode, + sectionsNode, + sectionNodes, + }; +} diff --git a/wasm-port/runtime/ui/ini-panel/index.html b/wasm-port/runtime/ui/ini-panel/index.html index 0210e45..a75d630 100644 --- a/wasm-port/runtime/ui/ini-panel/index.html +++ b/wasm-port/runtime/ui/ini-panel/index.html @@ -85,6 +85,47 @@ background: #fbfdff; } .status { display: grid; gap: 8px; } + .control-view { + border-top: 1px solid #edf2f6; + padding-top: 12px; + display: grid; + gap: 12px; + } + .control-view [data-control-view-status] { + color: var(--muted); + font: 13px/1.4 "SFMono-Regular", "Consolas", monospace; + overflow-wrap: anywhere; + } + .control-view [data-control-view-sections] { + display: grid; + gap: 10px; + } + .control-view section { + border-top: 1px solid #edf2f6; + padding-top: 10px; + display: grid; + gap: 8px; + } + .control-view h3 { + margin: 0; + font-size: 13px; + font-weight: 700; + } + .control-view dl { + display: grid; + grid-template-columns: 132px 1fr; + gap: 6px 10px; + margin: 0; + } + .control-view dt { + color: var(--muted); + font-size: 12px; + } + .control-view dd { + margin: 0; + font: 12px/1.4 "SFMono-Regular", "Consolas", monospace; + overflow-wrap: anywhere; + } .badge { display: inline-block; padding: 4px 8px; @@ -247,6 +288,7 @@
Interpreter WASM: loading
OPFS: checking
+
Machine
diff --git a/wasm-port/tests/browser/ini_panel_smoke.html b/wasm-port/tests/browser/ini_panel_smoke.html index 70ac4ec..ee82de7 100644 --- a/wasm-port/tests/browser/ini_panel_smoke.html +++ b/wasm-port/tests/browser/ini_panel_smoke.html @@ -6,12 +6,10 @@
running
-
-
-
-
+