From ee665c47be926fa543fde0c18a38b4e571f1b559 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Mon, 15 Jun 2026 07:59:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A8=E8=BF=9B=20INI=20=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=20shell=20integration=20workflow=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- text12.txt | 86 +++++++++++++++++++ wasm-port/docs/panel-entry.md | 5 ++ wasm-port/runtime/ui/ini-panel/ui-shell.js | 64 +++++++++++++- .../ui/node/verify_ini_panel_entry_docs.mjs | 11 +++ ...y_ini_panel_shell_integration_manifest.mjs | 69 +++++++++++++++ .../ui/node/verify_ini_panel_ui_shell.mjs | 10 +++ 6 files changed, 244 insertions(+), 1 deletion(-) diff --git a/text12.txt b/text12.txt index d922eb5..6a18537 100644 --- a/text12.txt +++ b/text12.txt @@ -1840,3 +1840,89 @@ host_wasm_opfs_browser_smokes=ok helper(例如 `createIniPanelShellIntegrationWorkflowPlan()`),让外部 shell 在不开 browser 的情况下 获得 target page、readiness、required methods 与 readonly status handoff plan;仍保持只读,不新增 控制按钮,不解析 G-code。 + +二十三、2026-06-15 继续执行记录:INI panel shell integration workflow plan helper + +本轮按“第一原则:加快实质性推进”继续推进 SDK/API surface,把 shell integration browser workflow +的只读 handoff 抽成 Node 可测 helper,让外部 shell 在不开 browser 的情况下获得 control-page target、 +readiness、required methods 与 readonly status handoff plan。 + +完成内容: + +- `wasm-port/runtime/ui/ini-panel/ui-shell.js` 新增 + `createIniPanelShellIntegrationWorkflowPlan()`; +- workflow plan 从 integration manifest 与 readiness 派生: + - `phase: "ready" | "blocked"`; + - `ready`; + - `controlEntryId`; + - `targetPage`; + - `readiness`; + - `requiredLaunchApiMethods`; + - `requiredControlPageApiMethods`; + - `missingLaunchApiMethods`; + - `missingControlPageApiMethods`; + - `missing`; + - `readonlyStatusHandoff.loadSessionStateMethod`; + - `readonlyStatusHandoff.readStatusMethod`; + - `readonlyStatusHandoff.expectedStatusFields`; +- `createIniPanelShellViewModel()` 新增: + - `shellIntegrationWorkflowPlan`; + - `createShellIntegrationWorkflowPlan`; +- `verify_ini_panel_shell_integration_manifest.mjs` 覆盖: + - ready workflow plan 完整 shape; + - blocked readiness 传递到 plan missing; + - control target 缺失; + - control-page required method 缺失; + - readonly status handoff fields; +- `verify_ini_panel_ui_shell.mjs` 覆盖 shell view-model 的 workflow plan; +- `verify_ini_panel_entry_docs.mjs` 与 `docs/panel-entry.md` 记录并校验 workflow plan helper; +- 未新增控制按钮; +- 未解析 G-code; +- 未解释 canonical events; +- 未访问 OPFS 或启动 browser/frame; +- 未改变 OPFS/session persistence、LinuxCNC interpreter、tool、parameter、planner 或 + LinuxCNC-owned runtime semantics。 + +验证已通过: + +```bash +git diff --check +wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.sh +wasm-port/tests/ui/node/verify_ini_panel_ui_shell.sh +wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh +wasm-port/tests/ui/node/verify_ui_node_smokes.sh +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 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_shell_integration_manifest_node_smoke=ok +ini_panel_ui_shell_node_smoke=ok +ini_panel_entry_docs_node_smoke=ok +ui_node_smokes=ok +browser_ini_opfs_smoke=ok +browser_ini_control_page_smoke=ok +browser_ini_launch_smoke=ok +browser_ini_shell_integration_workflow_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 +``` + +下一步建议: + +继续推进 OPFS/session persistence。下一批建议把 workflow plan 扩展为包含 session handoff summary +helper(例如 `createIniPanelShellSessionHandoffSummary()`),把 readiness、target page、readonly +status fields 与 persistence capabilities 压成外部 shell 可直接显示的一页摘要;仍保持只读,不新增 +控制按钮,不解析 G-code。 diff --git a/wasm-port/docs/panel-entry.md b/wasm-port/docs/panel-entry.md index a7bcf71..7b1b4d2 100644 --- a/wasm-port/docs/panel-entry.md +++ b/wasm-port/docs/panel-entry.md @@ -20,6 +20,7 @@ import { createIniPanelShellIntegrationManifest, createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, + createIniPanelShellIntegrationWorkflowPlan, createIniPanelShellViewModel, getIniPanelEntryByHref, getVisibleIniPanelEntries, @@ -124,6 +125,10 @@ Use `createIniPanelShellIntegrationReadiness()` or needs a read-only `ready`/`blocked` status, failed check names, API names, and counts for pages, methods, and persistence capabilities before mounting any embedded control page. +Use `createIniPanelShellIntegrationWorkflowPlan()` when the outer shell needs a +Node-testable read-only plan for the control-page target, required launch and +control-page API methods, readiness gate, and readonly status handoff fields +before opening a browser frame. The browser workflow smoke `tests/browser/ini_panel_shell_integration_workflow_smoke.html` verifies the same external shell handoff: read integration manifest, pass readiness gate, diff --git a/wasm-port/runtime/ui/ini-panel/ui-shell.js b/wasm-port/runtime/ui/ini-panel/ui-shell.js index 00574df..3c2d076 100644 --- a/wasm-port/runtime/ui/ini-panel/ui-shell.js +++ b/wasm-port/runtime/ui/ini-panel/ui-shell.js @@ -324,8 +324,65 @@ export function createIniPanelShellIntegrationReadiness( }; } +export function createIniPanelShellIntegrationWorkflowPlan({ + manifest = createIniPanelShellIntegrationManifest(), + readiness = createIniPanelShellIntegrationReadiness(manifest), + controlEntryId = "control-view", +} = {}) { + const targetPage = manifest?.launchApiManifest?.targetPages?.find( + (page) => page.entryId === controlEntryId, + ) ?? null; + const requiredLaunchApiMethods = [ + "getShellIntegrationManifest", + "getShellIntegrationReadiness", + ]; + const requiredControlPageApiMethods = [ + "getControlPageApiManifest", + "loadControlPageSessionState", + "readControlPageStatus", + ]; + const launchMethods = Array.isArray(manifest?.launchApiManifest?.methods) + ? manifest.launchApiManifest.methods + : []; + const controlPageMethods = Array.isArray(manifest?.controlPageApiManifest?.methods) + ? manifest.controlPageApiManifest.methods + : []; + const missingLaunchApiMethods = requiredLaunchApiMethods.filter( + (methodName) => !launchMethods.includes(methodName), + ); + const missingControlPageApiMethods = requiredControlPageApiMethods.filter( + (methodName) => !controlPageMethods.includes(methodName), + ); + const missing = [ + ...readiness.missing, + ...(targetPage ? [] : ["controlTargetPage"]), + ...missingLaunchApiMethods.map((methodName) => `launchApi.${methodName}`), + ...missingControlPageApiMethods.map((methodName) => `controlPageApi.${methodName}`), + ]; + const ready = readiness.ready && missing.length === 0; + + return { + phase: ready ? "ready" : "blocked", + ready, + controlEntryId, + targetPage, + readiness, + requiredLaunchApiMethods, + requiredControlPageApiMethods, + missingLaunchApiMethods, + missingControlPageApiMethods, + missing, + readonlyStatusHandoff: { + loadSessionStateMethod: "loadControlPageSessionState", + readStatusMethod: "readControlPageStatus", + expectedStatusFields: manifest?.controlPageApiManifest?.readonlyStatusApiManifest?.statusFields ?? [], + }, + }; +} + export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries()) { const shellIntegrationManifest = createIniPanelShellIntegrationManifest(entries); + const shellIntegrationReadiness = createIniPanelShellIntegrationReadiness(shellIntegrationManifest); return { pages: entries.map(({ id, href, title }) => ({ id, @@ -335,11 +392,16 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries launcherLinks: createIniPanelLauncherLinkViewModel(entries), shellIntegrationSchema: createIniPanelShellIntegrationSchema(), shellIntegrationManifest, - shellIntegrationReadiness: createIniPanelShellIntegrationReadiness(shellIntegrationManifest), + shellIntegrationReadiness, + shellIntegrationWorkflowPlan: createIniPanelShellIntegrationWorkflowPlan({ + manifest: shellIntegrationManifest, + readiness: shellIntegrationReadiness, + }), launchApiSchema: createIniPanelLaunchApiSchema(), launchApiManifest: createIniPanelLaunchApiManifest(entries), isSupportedShellIntegrationManifest: isSupportedIniPanelShellIntegrationManifest, createShellIntegrationReadiness: createIniPanelShellIntegrationReadiness, + createShellIntegrationWorkflowPlan: createIniPanelShellIntegrationWorkflowPlan, controlPage: { browserApiSchema: createControlPageBrowserApiSchema(), browserApiManifest: createControlPageBrowserApiManifest(), diff --git a/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs b/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs index 5e84046..60835b8 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs @@ -17,6 +17,7 @@ import { createIniPanelShellIntegrationManifest, createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, + createIniPanelShellIntegrationWorkflowPlan, createIniPanelShellViewModel, createMachineSessionReadonlyStatus, createMachineSessionReadonlyStatusApiManifest, @@ -63,6 +64,7 @@ const requiredShellExports = [ "createIniPanelShellIntegrationManifest", "createIniPanelShellIntegrationReadiness", "createIniPanelShellIntegrationSchema", + "createIniPanelShellIntegrationWorkflowPlan", "createIniPanelShellViewModel", "getIniPanelEntryByHref", "getVisibleIniPanelEntries", @@ -133,6 +135,13 @@ assert.deepEqual( createIniPanelShellViewModel().shellIntegrationReadiness, createIniPanelShellIntegrationReadiness(createIniPanelShellViewModel().shellIntegrationManifest), ); +assert.deepEqual( + createIniPanelShellViewModel().shellIntegrationWorkflowPlan, + createIniPanelShellIntegrationWorkflowPlan({ + manifest: createIniPanelShellViewModel().shellIntegrationManifest, + readiness: createIniPanelShellViewModel().shellIntegrationReadiness, + }), +); assert.equal(isSupportedIniPanelShellIntegrationManifest(createIniPanelShellViewModel().shellIntegrationManifest), true); assert.deepEqual(createIniPanelShellViewModel().launchApiSchema, createIniPanelLaunchApiSchema()); assert.deepEqual(createIniPanelShellViewModel().launchApiManifest, createIniPanelLaunchApiManifest()); @@ -186,6 +195,7 @@ assert.match(shellText, /\bexport function createIniPanelLaunchApiManifest\b/); assert.match(shellText, /\bexport function createIniPanelShellIntegrationSchema\b/); assert.match(shellText, /\bexport function createIniPanelShellIntegrationManifest\b/); assert.match(shellText, /\bexport function createIniPanelShellIntegrationReadiness\b/); +assert.match(shellText, /\bexport function createIniPanelShellIntegrationWorkflowPlan\b/); assert.match(shellText, /\bexport function isSupportedIniPanelControlPageApiManifest\b/); assert.match(shellText, /\bexport function isSupportedIniPanelLaunchApiManifest\b/); assert.match(shellText, /\bexport function isSupportedIniPanelShellIntegrationManifest\b/); @@ -217,6 +227,7 @@ assert.match(docText, /\bcreateIniPanelLaunchApiSchema\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationManifest\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationReadiness\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationSchema\b/); +assert.match(docText, /\bcreateIniPanelShellIntegrationWorkflowPlan\b/); assert.match(docText, /\bisSupportedIniPanelControlPageApiManifest\b/); assert.match(docText, /\bisSupportedIniPanelLaunchApiManifest\b/); assert.match(docText, /\bisSupportedIniPanelShellIntegrationManifest\b/); diff --git a/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs b/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs index 068a1d0..95cab0e 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs @@ -13,6 +13,7 @@ import { createIniPanelShellIntegrationManifest, createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, + createIniPanelShellIntegrationWorkflowPlan, getVisibleIniPanelEntries, isSupportedIniPanelShellIntegrationManifest, } from "../../../runtime/ui/ini-panel/ui-shell.js"; @@ -26,6 +27,7 @@ const docText = readFileSync(resolve(root, "docs/panel-entry.md"), "utf8"); const schema = createIniPanelShellIntegrationSchema(); const manifest = createIniPanelShellIntegrationManifest(); const readiness = createIniPanelShellIntegrationReadiness(manifest); +const workflowPlan = createIniPanelShellIntegrationWorkflowPlan({ manifest, readiness }); assert.deepEqual(schema, { apiName: "ini-panel-shell-integration", @@ -91,6 +93,43 @@ assert.deepEqual(readiness, { controlPage: "ini-panel-control-page", }, }); +assert.deepEqual(workflowPlan, { + phase: "ready", + ready: true, + controlEntryId: "control-view", + targetPage: { + entryId: "control-view", + href: "./control-page.html", + }, + readiness, + requiredLaunchApiMethods: [ + "getShellIntegrationManifest", + "getShellIntegrationReadiness", + ], + requiredControlPageApiMethods: [ + "getControlPageApiManifest", + "loadControlPageSessionState", + "readControlPageStatus", + ], + missingLaunchApiMethods: [], + missingControlPageApiMethods: [], + missing: [], + readonlyStatusHandoff: { + loadSessionStateMethod: "loadControlPageSessionState", + readStatusMethod: "readControlPageStatus", + expectedStatusFields: [ + "stateBundle", + "workflowStatus", + "overview", + "controlView", + "report", + "runReadiness", + "run", + "session", + "status", + ], + }, +}); const blockedManifest = { ...createIniPanelShellIntegrationManifest(), compatibility: { @@ -122,6 +161,34 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), { controlPage: "ini-panel-control-page", }, }); +assert.deepEqual( + createIniPanelShellIntegrationWorkflowPlan({ + manifest: blockedManifest, + readiness: createIniPanelShellIntegrationReadiness(blockedManifest), + }).missing, + ["launchCompatibility"], +); +const missingTargetPlan = createIniPanelShellIntegrationWorkflowPlan({ + manifest, + controlEntryId: "missing-entry", +}); +assert.equal(missingTargetPlan.ready, false); +assert.deepEqual(missingTargetPlan.missing, ["controlTargetPage"]); +assert.equal(missingTargetPlan.targetPage, null); +const missingControlMethodManifest = { + ...createIniPanelShellIntegrationManifest(), + controlPageApiManifest: { + ...createControlPageBrowserApiManifest(), + methods: ["getControlPageApiManifest", "readControlPageStatus"], + }, +}; +const missingControlMethodPlan = createIniPanelShellIntegrationWorkflowPlan({ + manifest: missingControlMethodManifest, +}); +assert.equal(missingControlMethodPlan.ready, false); +assert.deepEqual(missingControlMethodPlan.missing, [ + "controlPageApi.loadControlPageSessionState", +]); const malformedReadiness = createIniPanelShellIntegrationReadiness({ apiName: "other", }); @@ -194,12 +261,14 @@ assert.deepEqual(customManifest.launchApiManifest.targetPages, [ assert.match(shellText, /\bexport function createIniPanelShellIntegrationSchema\b/); assert.match(shellText, /\bexport function createIniPanelShellIntegrationManifest\b/); assert.match(shellText, /\bexport function createIniPanelShellIntegrationReadiness\b/); +assert.match(shellText, /\bexport function createIniPanelShellIntegrationWorkflowPlan\b/); assert.match(shellText, /\bexport function isSupportedIniPanelShellIntegrationManifest\b/); assert.match(launchText, /\bgetShellIntegrationSchema\b/); assert.match(launchText, /\bgetShellIntegrationManifest\b/); assert.match(launchText, /\bgetShellIntegrationReadiness\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationManifest\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationReadiness\b/); +assert.match(docText, /\bcreateIniPanelShellIntegrationWorkflowPlan\b/); assert.match(docText, /\bisSupportedIniPanelShellIntegrationManifest\b/); assert.doesNotMatch(shellText, /\bparseGcode\b|\bcanonicalEvents\b|\btoolTable\b|\bplanner\b/); diff --git a/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs b/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs index 332bfaf..da43d2e 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_ui_shell.mjs @@ -13,6 +13,7 @@ import { createIniPanelShellIntegrationManifest, createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, + createIniPanelShellIntegrationWorkflowPlan, createIniPanelShellViewModel, createMachineSessionReadonlyStatus, createMachineSessionReadonlyStatusApiManifest, @@ -64,6 +65,7 @@ assert.equal(typeof createIniPanelShellControlPageContract, "function"); assert.equal(typeof createIniPanelShellIntegrationManifest, "function"); assert.equal(typeof createIniPanelShellIntegrationReadiness, "function"); assert.equal(typeof createIniPanelShellIntegrationSchema, "function"); +assert.equal(typeof createIniPanelShellIntegrationWorkflowPlan, "function"); assert.equal(typeof loadMachineSessionForControlPageWorkflow, "function"); assert.equal(typeof readMachineSessionReadonlyStatus, "function"); assert.equal(typeof refreshMachineSessionControlPage, "function"); @@ -113,6 +115,13 @@ assert.deepEqual( shellViewModel.shellIntegrationReadiness, createIniPanelShellIntegrationReadiness(shellViewModel.shellIntegrationManifest), ); +assert.deepEqual( + shellViewModel.shellIntegrationWorkflowPlan, + createIniPanelShellIntegrationWorkflowPlan({ + manifest: shellViewModel.shellIntegrationManifest, + readiness: shellViewModel.shellIntegrationReadiness, + }), +); assert.equal( isSupportedIniPanelShellIntegrationManifest(shellViewModel.shellIntegrationManifest), true, @@ -144,6 +153,7 @@ assert.equal( assert.equal(shellViewModel.controlPage.createController, createMachineSessionControlPageController); assert.equal(shellViewModel.isSupportedShellIntegrationManifest, isSupportedIniPanelShellIntegrationManifest); assert.equal(shellViewModel.createShellIntegrationReadiness, createIniPanelShellIntegrationReadiness); +assert.equal(shellViewModel.createShellIntegrationWorkflowPlan, createIniPanelShellIntegrationWorkflowPlan); assert.deepEqual(shellViewModel.controlPage.contract, createIniPanelShellControlPageContract()); assert.equal(shellViewModel.controlPage.isSupportedBrowserApiManifest, isSupportedIniPanelControlPageApiManifest); assert.equal(shellViewModel.controlPage.loadSessionState, loadMachineSessionForControlPageWorkflow);