From 2a7485c14d52589e5daf017c895c4847abf3a2b8 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Mon, 15 Jun 2026 10:00:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A8=E8=BF=9B=20INI=20=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=20shell=20handoff=20action=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- text13.txt | 103 +++++++++++++++ wasm-port/docs/panel-entry.md | 7 + wasm-port/runtime/ui/ini-panel/launch.html | 1 + wasm-port/runtime/ui/ini-panel/ui-shell.js | 65 ++++++++++ .../tests/browser/ini_panel_launch_smoke.html | 29 ++++- ...anel_shell_integration_workflow_smoke.html | 11 +- .../ui/node/verify_ini_panel_entry_docs.mjs | 14 ++ .../verify_ini_panel_launch_api_manifest.mjs | 2 + ...y_ini_panel_shell_integration_manifest.mjs | 120 +++++++++++++++++- .../ui/node/verify_ini_panel_ui_shell.mjs | 45 +++++++ 10 files changed, 392 insertions(+), 5 deletions(-) diff --git a/text13.txt b/text13.txt index 118b301..dea9f48 100644 --- a/text13.txt +++ b/text13.txt @@ -603,3 +603,106 @@ host_wasm_opfs_browser_smokes=ok `createIniPanelShellSessionHandoffActionPlan()` 或等价 helper,把 render-state 的 ready/blocked 状态转成外部 shell 可执行的下一步计划(例如 target page、required methods、blocked reasons、 display rows),但仍只描述流程,不新增控制按钮,不解析 G-code。 + +五、2026-06-15 继续执行记录:INI panel shell handoff action plan + +本轮按“第一原则:加快实质性推进”继续推进 SDK/API surface 与 browser/UI workflow 的交界, +新增只读 shell handoff action-plan helper,把 snapshot/render-state 转成外部 shell 可消费的 +下一步流程描述。 + +完成内容: + +- `ui-shell.js` 新增 `createIniPanelShellSessionHandoffActionPlan()`; +- action-plan 输出: + - `apiName: "ini-panel-shell-session-handoff-action-plan"`; + - `planVersion: 1`; + - `phase`; + - `ready`; + - `statusLine`; + - `targetPage`; + - `requiredMethods.loadSessionState`; + - `requiredMethods.readStatus`; + - `blockingReasons`; + - `displayRows`; + - `nextSteps`; +- ready plan 输出两个只读流程步骤: + - `open-control-page`; + - `read-readonly-status`; +- blocked plan 输出一个诊断步骤: + - `resolve-blockers`; +- `createIniPanelShellViewModel()` 新增: + - `shellSessionHandoffActionPlan`; + - `createShellSessionHandoffActionPlan`; +- `createIniPanelLaunchApiManifest().methods` 新增 + `getShellSessionHandoffActionPlan`; +- `launch.html` 的 `window.linuxCncIniPanelLaunchApi` 新增真实 browser 入口 + `getShellSessionHandoffActionPlan()`; +- `ini_panel_launch_smoke.html` 校验真实 launch iframe: + - action-plan version 为 1; + - action-plan `ready === true`; + - next step id 为 `open-control-page,read-readonly-status`; + - `requiredMethods.readStatus === "readControlPageStatus"`; + - `displayRows` 与 render-state rows 一致; +- `ini_panel_shell_integration_workflow_smoke.html` 校验外部 shell workflow 能读取 + action-plan,并确认 target page 与 next steps; +- `verify_ini_panel_shell_integration_manifest.mjs` 覆盖: + - ready action-plan 完整 shape; + - default action-plan next steps; + - blocked/mismatch snapshot action-plan; + - blocked manifest action-plan `blockingReasons`; +- `verify_ini_panel_ui_shell.mjs` 覆盖 shell view-model 的 action-plan 聚合关系; +- `verify_ini_panel_launch_api_manifest.mjs`、`verify_ini_panel_entry_docs.mjs` 与 + `docs/panel-entry.md` 同步记录和校验新 helper/API; +- 未新增控制按钮; +- 未执行 action-plan; +- 未解析 G-code; +- 未解释 canonical events; +- 未新增 JS CNC 语义; +- 未改变 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_launch_api_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_launch_api_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 与 browser/UI workflow 的交界。下一批建议新增一个只读 +`createIniPanelShellSessionHandoffChecklist()` 或等价 helper,把 action-plan、render-state、 +required methods 和 blocking reasons 压成外部 shell 可显示的 checklist/health rows,并补真实 +browser workflow smoke;仍保持只读,不新增控制按钮,不解析 G-code。 diff --git a/wasm-port/docs/panel-entry.md b/wasm-port/docs/panel-entry.md index 4943744..14204f8 100644 --- a/wasm-port/docs/panel-entry.md +++ b/wasm-port/docs/panel-entry.md @@ -21,6 +21,7 @@ import { createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, createIniPanelShellIntegrationWorkflowPlan, + createIniPanelShellSessionHandoffActionPlan, createIniPanelShellSessionHandoffCompatibilityReport, createIniPanelShellSessionHandoffDisplayViewModel, createIniPanelShellSessionHandoffRenderState, @@ -165,6 +166,12 @@ shell needs the snapshot compressed into launch-side DOM state with a status line, stable rows, and `[data-handoff-shell]` phase/readiness flags. The launch page renders `[data-handoff-status]` and `[data-handoff-rows]` from this helper without adding control actions. +Use `createIniPanelShellSessionHandoffActionPlan()` or +`linuxCncIniPanelLaunchApi.getShellSessionHandoffActionPlan()` when an outer +shell needs a read-only next-step description derived from the same snapshot: +target page, required readonly methods, blocking reasons, display rows, and +ready/blocked next step ids. The action plan describes what an outer shell can +do next; it does not execute actions or add controls. 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/launch.html b/wasm-port/runtime/ui/ini-panel/launch.html index ef9ac1c..7e4b13e 100644 --- a/wasm-port/runtime/ui/ini-panel/launch.html +++ b/wasm-port/runtime/ui/ini-panel/launch.html @@ -144,6 +144,7 @@ getShellSessionHandoffDisplayViewModel: () => shellViewModel.shellSessionHandoffDisplayViewModel, getShellSessionHandoffSnapshot: () => shellViewModel.shellSessionHandoffSnapshot, getShellSessionHandoffRenderState: () => shellViewModel.shellSessionHandoffRenderState, + getShellSessionHandoffActionPlan: () => shellViewModel.shellSessionHandoffActionPlan, getLauncherLinks: () => shellViewModel.launcherLinks, getControlPageApiMethods: () => shellViewModel.controlPage.browserApiMethods, }; diff --git a/wasm-port/runtime/ui/ini-panel/ui-shell.js b/wasm-port/runtime/ui/ini-panel/ui-shell.js index bd34ee1..7dbb3b5 100644 --- a/wasm-port/runtime/ui/ini-panel/ui-shell.js +++ b/wasm-port/runtime/ui/ini-panel/ui-shell.js @@ -133,6 +133,7 @@ export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntr "getShellSessionHandoffDisplayViewModel", "getShellSessionHandoffSnapshot", "getShellSessionHandoffRenderState", + "getShellSessionHandoffActionPlan", "getLauncherLinks", "getControlPageApiMethods", ], @@ -588,6 +589,64 @@ export function createIniPanelShellSessionHandoffRenderState( }; } +export function createIniPanelShellSessionHandoffActionPlan({ + snapshot = createIniPanelShellSessionHandoffSnapshot(), + renderState = createIniPanelShellSessionHandoffRenderState(snapshot), +} = {}) { + const summary = snapshot?.summary && typeof snapshot.summary === "object" ? snapshot.summary : {}; + const handoffMethods = summary.handoffMethods && typeof summary.handoffMethods === "object" + ? summary.handoffMethods + : {}; + const blockingReasons = Array.isArray(summary.blockingReasons) + ? [...summary.blockingReasons] + : []; + const displayRows = Array.isArray(renderState?.rows) ? renderState.rows.map(({ id, label, value }) => ({ + id, + label, + value, + })) : []; + const ready = snapshot?.ready === true && renderState?.ready === true && blockingReasons.length === 0; + + return { + apiName: "ini-panel-shell-session-handoff-action-plan", + planVersion: 1, + phase: ready ? "ready" : "blocked", + ready, + statusLine: renderState?.statusLine ?? "Blocked: No handoff render state", + targetPage: summary.targetPage ?? null, + requiredMethods: { + loadSessionState: handoffMethods.loadSessionState ?? null, + readStatus: handoffMethods.readStatus ?? null, + }, + blockingReasons, + displayRows, + nextSteps: ready + ? [ + { + id: "open-control-page", + label: "Open control page target", + targetPage: summary.targetPage ?? null, + }, + { + id: "read-readonly-status", + label: "Read readonly status", + methods: { + loadSessionState: handoffMethods.loadSessionState ?? null, + readStatus: handoffMethods.readStatus ?? null, + }, + }, + ] + : [ + { + id: "resolve-blockers", + label: "Resolve handoff blockers", + blockingReasons, + displayRows, + }, + ], + }; +} + export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries()) { const shellIntegrationManifest = createIniPanelShellIntegrationManifest(entries); const shellIntegrationReadiness = createIniPanelShellIntegrationReadiness(shellIntegrationManifest); @@ -615,6 +674,10 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries const shellSessionHandoffRenderState = createIniPanelShellSessionHandoffRenderState( shellSessionHandoffSnapshot, ); + const shellSessionHandoffActionPlan = createIniPanelShellSessionHandoffActionPlan({ + snapshot: shellSessionHandoffSnapshot, + renderState: shellSessionHandoffRenderState, + }); return { pages: entries.map(({ id, href, title }) => ({ id, @@ -632,6 +695,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries shellSessionHandoffDisplayViewModel, shellSessionHandoffSnapshot, shellSessionHandoffRenderState, + shellSessionHandoffActionPlan, launchApiSchema: createIniPanelLaunchApiSchema(), launchApiManifest: createIniPanelLaunchApiManifest(entries), isSupportedShellIntegrationManifest: isSupportedIniPanelShellIntegrationManifest, @@ -644,6 +708,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries createShellSessionHandoffDisplayViewModel: createIniPanelShellSessionHandoffDisplayViewModel, createShellSessionHandoffSnapshot: createIniPanelShellSessionHandoffSnapshot, createShellSessionHandoffRenderState: createIniPanelShellSessionHandoffRenderState, + createShellSessionHandoffActionPlan: createIniPanelShellSessionHandoffActionPlan, controlPage: { browserApiSchema: createControlPageBrowserApiSchema(), browserApiManifest: createControlPageBrowserApiManifest(), diff --git a/wasm-port/tests/browser/ini_panel_launch_smoke.html b/wasm-port/tests/browser/ini_panel_launch_smoke.html index 9e171fd..e90b81c 100644 --- a/wasm-port/tests/browser/ini_panel_launch_smoke.html +++ b/wasm-port/tests/browser/ini_panel_launch_smoke.html @@ -80,6 +80,7 @@ !launchApi?.getShellSessionHandoffDisplayViewModel || !launchApi?.getShellSessionHandoffSnapshot || !launchApi?.getShellSessionHandoffRenderState || + !launchApi?.getShellSessionHandoffActionPlan || !launchApi?.getLauncherLinks || !launchApi?.getControlPageApiMethods ) { @@ -119,7 +120,7 @@ ); assertEqual( launchApiManifest.methods.join(","), - "isSupportedLaunchApiManifest,getLaunchApiSchema,getLaunchApiManifest,getShellIntegrationSchema,getShellIntegrationManifest,getShellIntegrationReadiness,getShellSessionHandoffSummarySchema,getShellSessionHandoffSummary,isSupportedShellSessionHandoffSummary,getShellSessionHandoffCompatibilityReport,getShellSessionHandoffDisplayViewModel,getShellSessionHandoffSnapshot,getShellSessionHandoffRenderState,getLauncherLinks,getControlPageApiMethods", + "isSupportedLaunchApiManifest,getLaunchApiSchema,getLaunchApiManifest,getShellIntegrationSchema,getShellIntegrationManifest,getShellIntegrationReadiness,getShellSessionHandoffSummarySchema,getShellSessionHandoffSummary,isSupportedShellSessionHandoffSummary,getShellSessionHandoffCompatibilityReport,getShellSessionHandoffDisplayViewModel,getShellSessionHandoffSnapshot,getShellSessionHandoffRenderState,getShellSessionHandoffActionPlan,getLauncherLinks,getControlPageApiMethods", "launch API manifest methods", ); const shellIntegrationSchema = launchApi.getShellIntegrationSchema(); @@ -131,6 +132,7 @@ const shellSessionHandoffDisplayViewModel = launchApi.getShellSessionHandoffDisplayViewModel(); const shellSessionHandoffSnapshot = launchApi.getShellSessionHandoffSnapshot(); const shellSessionHandoffRenderState = launchApi.getShellSessionHandoffRenderState(); + const shellSessionHandoffActionPlan = launchApi.getShellSessionHandoffActionPlan(); assertEqual( shellIntegrationSchema.manifestVersion, 1, @@ -241,6 +243,26 @@ "Ready: No blocking reasons", "shell session handoff render-state status", ); + assertEqual( + shellSessionHandoffActionPlan.planVersion, + 1, + "shell session handoff action-plan version", + ); + assertEqual( + shellSessionHandoffActionPlan.ready, + true, + "shell session handoff action-plan readiness", + ); + assertEqual( + shellSessionHandoffActionPlan.nextSteps.map(({ id }) => id).join(","), + "open-control-page,read-readonly-status", + "shell session handoff action-plan next steps", + ); + assertEqual( + shellSessionHandoffActionPlan.requiredMethods.readStatus, + "readControlPageStatus", + "shell session handoff action-plan read method", + ); assertEqual( JSON.stringify(shellSessionHandoffSnapshot.readiness), JSON.stringify(shellIntegrationReadiness), @@ -261,6 +283,11 @@ JSON.stringify(shellSessionHandoffDisplayViewModel.rows), "shell session handoff render-state rows", ); + assertEqual( + JSON.stringify(shellSessionHandoffActionPlan.displayRows), + JSON.stringify(shellSessionHandoffRenderState.rows), + "shell session handoff action-plan rows", + ); assertEqual( launchDoc.querySelector("[data-handoff-status]").textContent, "Ready: No blocking reasons", diff --git a/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html b/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html index bbc94d7..d191fa7 100644 --- a/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html +++ b/wasm-port/tests/browser/ini_panel_shell_integration_workflow_smoke.html @@ -70,7 +70,8 @@ TOOL_TABLE = browser-shell-tool.tbl !launchApi?.getShellSessionHandoffCompatibilityReport || !launchApi?.getShellSessionHandoffDisplayViewModel || !launchApi?.getShellSessionHandoffSnapshot || - !launchApi?.getShellSessionHandoffRenderState + !launchApi?.getShellSessionHandoffRenderState || + !launchApi?.getShellSessionHandoffActionPlan ) { throw new Error("launch API namespace did not expose shell integration helpers"); } @@ -83,6 +84,7 @@ TOOL_TABLE = browser-shell-tool.tbl const handoffDisplay = launchApi.getShellSessionHandoffDisplayViewModel(); const handoffSnapshot = launchApi.getShellSessionHandoffSnapshot(); const handoffRenderState = launchApi.getShellSessionHandoffRenderState(); + const handoffActionPlan = launchApi.getShellSessionHandoffActionPlan(); assertEqual(handoffSummarySchema.summaryVersion, 1, "shell handoff summary schema version"); assertEqual( launchApi.isSupportedShellSessionHandoffSummary(handoffSummary), @@ -99,6 +101,13 @@ TOOL_TABLE = browser-shell-tool.tbl assertEqual(handoffRenderState.ready, true, "shell handoff render-state readiness"); assertEqual(handoffRenderState.statusLine, "Ready: No blocking reasons", "shell handoff render-state status"); assertEqual(handoffRenderState.rows.length, handoffDisplay.rows.length, "shell handoff render-state rows"); + assertEqual(handoffActionPlan.ready, true, "shell handoff action-plan readiness"); + assertEqual(handoffActionPlan.targetPage.href, "./control-page.html", "shell handoff action-plan target"); + assertEqual( + handoffActionPlan.nextSteps.map(({ id }) => id).join(","), + "open-control-page,read-readonly-status", + "shell handoff action-plan next steps", + ); assertEqual(readiness.phase, "ready", "shell integration readiness phase"); assertEqual(readiness.ready, true, "shell integration readiness"); assertEqual(readiness.missing.join(","), "", "shell integration readiness missing checks"); 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 ffb7f83..ad53af8 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 @@ -18,6 +18,7 @@ import { createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, createIniPanelShellIntegrationWorkflowPlan, + createIniPanelShellSessionHandoffActionPlan, createIniPanelShellSessionHandoffCompatibilityReport, createIniPanelShellSessionHandoffDisplayViewModel, createIniPanelShellSessionHandoffRenderState, @@ -72,6 +73,7 @@ const requiredShellExports = [ "createIniPanelShellIntegrationReadiness", "createIniPanelShellIntegrationSchema", "createIniPanelShellIntegrationWorkflowPlan", + "createIniPanelShellSessionHandoffActionPlan", "createIniPanelShellSessionHandoffCompatibilityReport", "createIniPanelShellSessionHandoffDisplayViewModel", "createIniPanelShellSessionHandoffRenderState", @@ -190,6 +192,13 @@ assert.deepEqual( createIniPanelShellViewModel().shellSessionHandoffRenderState, createIniPanelShellSessionHandoffRenderState(createIniPanelShellViewModel().shellSessionHandoffSnapshot), ); +assert.deepEqual( + createIniPanelShellViewModel().shellSessionHandoffActionPlan, + createIniPanelShellSessionHandoffActionPlan({ + snapshot: createIniPanelShellViewModel().shellSessionHandoffSnapshot, + renderState: createIniPanelShellViewModel().shellSessionHandoffRenderState, + }), +); assert.equal(isSupportedIniPanelShellIntegrationManifest(createIniPanelShellViewModel().shellIntegrationManifest), true); assert.deepEqual(createIniPanelShellViewModel().launchApiSchema, createIniPanelLaunchApiSchema()); assert.deepEqual(createIniPanelShellViewModel().launchApiManifest, createIniPanelLaunchApiManifest()); @@ -244,6 +253,7 @@ assert.match(shellText, /\bexport function createIniPanelShellIntegrationSchema\ 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 createIniPanelShellSessionHandoffActionPlan\b/); assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffCompatibilityReport\b/); assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffDisplayViewModel\b/); assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffRenderState\b/); @@ -272,6 +282,7 @@ assert.match(launchText, /\bgetShellSessionHandoffCompatibilityReport\b/); assert.match(launchText, /\bgetShellSessionHandoffDisplayViewModel\b/); assert.match(launchText, /\bgetShellSessionHandoffSnapshot\b/); assert.match(launchText, /\bgetShellSessionHandoffRenderState\b/); +assert.match(launchText, /\bgetShellSessionHandoffActionPlan\b/); assert.match(launchText, /\bdata-handoff-shell\b/); assert.match(launchText, /\bdata-handoff-status\b/); assert.match(launchText, /\bdata-handoff-rows\b/); @@ -293,6 +304,7 @@ assert.match(docText, /\bcreateIniPanelShellIntegrationManifest\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationReadiness\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationSchema\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationWorkflowPlan\b/); +assert.match(docText, /\bcreateIniPanelShellSessionHandoffActionPlan\b/); assert.match(docText, /\bcreateIniPanelShellSessionHandoffCompatibilityReport\b/); assert.match(docText, /\bcreateIniPanelShellSessionHandoffDisplayViewModel\b/); assert.match(docText, /\bcreateIniPanelShellSessionHandoffRenderState\b/); @@ -316,6 +328,7 @@ assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffCompat assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffDisplayViewModel\b/); assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffSnapshot\b/); assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffRenderState\b/); +assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffActionPlan\b/); assert.match(docText, /\bdata-handoff-shell\b/); assert.match(docText, /\bdata-handoff-status\b/); assert.match(docText, /\bdata-handoff-rows\b/); @@ -329,6 +342,7 @@ assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffCompati assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffDisplayViewModel\b/); assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffSnapshot\b/); assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffRenderState\b/); +assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffActionPlan\b/); assert.match(shellIntegrationWorkflowSmokeText, /\bhandoffSummary\.handoffMethods\.readStatus\b/); assert.match(shellIntegrationWorkflowSmokeText, /\breadControlPageStatus\b/); assert.match(shellIntegrationWorkflowSmokeText, /\bbrowser_ini_shell_integration_workflow_smoke=ok\b/); diff --git a/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs b/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs index 010598b..d47d33b 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_launch_api_manifest.mjs @@ -53,6 +53,7 @@ assert.deepEqual(manifest, { "getShellSessionHandoffDisplayViewModel", "getShellSessionHandoffSnapshot", "getShellSessionHandoffRenderState", + "getShellSessionHandoffActionPlan", "getLauncherLinks", "getControlPageApiMethods", ], @@ -160,6 +161,7 @@ assert.match(launchText, /\bgetShellSessionHandoffCompatibilityReport\b/); assert.match(launchText, /\bgetShellSessionHandoffDisplayViewModel\b/); assert.match(launchText, /\bgetShellSessionHandoffSnapshot\b/); assert.match(launchText, /\bgetShellSessionHandoffRenderState\b/); +assert.match(launchText, /\bgetShellSessionHandoffActionPlan\b/); assert.match(launchText, /\blinuxCncIniPanelLaunchApi\b/); console.log("ini_panel_launch_api_manifest_node_smoke=ok"); 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 5935b9b..7ed7a1a 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 @@ -14,6 +14,7 @@ import { createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, createIniPanelShellIntegrationWorkflowPlan, + createIniPanelShellSessionHandoffActionPlan, createIniPanelShellSessionHandoffCompatibilityReport, createIniPanelShellSessionHandoffDisplayViewModel, createIniPanelShellSessionHandoffRenderState, @@ -46,6 +47,10 @@ const handoffSnapshot = createIniPanelShellSessionHandoffSnapshot({ displayViewModel: handoffDisplayViewModel, }); const handoffRenderState = createIniPanelShellSessionHandoffRenderState(handoffSnapshot); +const handoffActionPlan = createIniPanelShellSessionHandoffActionPlan({ + snapshot: handoffSnapshot, + renderState: handoffRenderState, +}); assert.deepEqual(schema, { apiName: "ini-panel-shell-integration", @@ -101,7 +106,7 @@ assert.deepEqual(readiness, { counts: { pages: 2, launcherLinks: 2, - launchMethods: 15, + launchMethods: 16, controlPageMethods: 11, persistenceCapabilities: 3, }, @@ -163,7 +168,7 @@ assert.deepEqual(handoffSummary, { counts: { pages: 2, launcherLinks: 2, - launchMethods: 15, + launchMethods: 16, controlPageMethods: 11, persistenceCapabilities: 3, }, @@ -327,8 +332,44 @@ assert.deepEqual(handoffRenderState, { handoffReady: "true", }, }); +assert.deepEqual(handoffActionPlan, { + apiName: "ini-panel-shell-session-handoff-action-plan", + planVersion: 1, + phase: "ready", + ready: true, + statusLine: "Ready: No blocking reasons", + targetPage: { + entryId: "control-view", + href: "./control-page.html", + }, + requiredMethods: { + loadSessionState: "loadControlPageSessionState", + readStatus: "readControlPageStatus", + }, + blockingReasons: [], + displayRows: handoffRenderState.rows, + nextSteps: [ + { + id: "open-control-page", + label: "Open control page target", + targetPage: { + entryId: "control-view", + href: "./control-page.html", + }, + }, + { + id: "read-readonly-status", + label: "Read readonly status", + methods: { + loadSessionState: "loadControlPageSessionState", + readStatus: "readControlPageStatus", + }, + }, + ], +}); assert.equal(createIniPanelShellSessionHandoffSnapshot().displayViewModel.rows.length, 5); assert.equal(createIniPanelShellSessionHandoffRenderState().statusLine, "Ready: No blocking reasons"); +assert.equal(createIniPanelShellSessionHandoffActionPlan().nextSteps.length, 2); assert.deepEqual( createIniPanelShellSessionHandoffDisplayViewModel( createIniPanelShellSessionHandoffCompatibilityReport({ ...handoffSummary, summaryVersion: 2 }), @@ -415,6 +456,60 @@ assert.deepEqual( }, }, ); +assert.deepEqual( + createIniPanelShellSessionHandoffActionPlan({ + snapshot: createIniPanelShellSessionHandoffSnapshot({ + summary: { + ...handoffSummary, + summaryVersion: 2, + }, + compatibilityReport: createIniPanelShellSessionHandoffCompatibilityReport({ + ...handoffSummary, + summaryVersion: 2, + }), + }), + renderState: createIniPanelShellSessionHandoffRenderState(createIniPanelShellSessionHandoffSnapshot({ + compatibilityReport: createIniPanelShellSessionHandoffCompatibilityReport({ + ...handoffSummary, + summaryVersion: 2, + }), + })), + }).nextSteps, + [ + { + id: "resolve-blockers", + label: "Resolve handoff blockers", + blockingReasons: [], + displayRows: [ + { + id: "summary-supported", + label: "Summary schema", + value: "blocked", + }, + { + id: "handoff-ready", + label: "Handoff readiness", + value: "blocked", + }, + { + id: "missing-fields", + label: "Missing fields", + value: "none", + }, + { + id: "mismatch-fields", + label: "Mismatched fields", + value: "summaryVersion", + }, + { + id: "blocking-reasons", + label: "Blocking reasons", + value: "none", + }, + ], + }, + ], +); const blockedManifest = { ...createIniPanelShellIntegrationManifest(), compatibility: { @@ -436,7 +531,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), { counts: { pages: 2, launcherLinks: 2, - launchMethods: 15, + launchMethods: 16, controlPageMethods: 11, persistenceCapabilities: 3, }, @@ -481,6 +576,23 @@ assert.equal( })).compatible, false, ); +assert.deepEqual( + createIniPanelShellSessionHandoffActionPlan({ + snapshot: createIniPanelShellSessionHandoffSnapshot({ + summary: createIniPanelShellSessionHandoffSummary({ + manifest: blockedManifest, + readiness: createIniPanelShellIntegrationReadiness(blockedManifest), + workflowPlan: blockedWorkflowPlan, + }), + compatibilityReport: createIniPanelShellSessionHandoffCompatibilityReport(createIniPanelShellSessionHandoffSummary({ + manifest: blockedManifest, + readiness: createIniPanelShellIntegrationReadiness(blockedManifest), + workflowPlan: blockedWorkflowPlan, + })), + }), + }).blockingReasons, + ["launchCompatibility"], +); const missingTargetPlan = createIniPanelShellIntegrationWorkflowPlan({ manifest, controlEntryId: "missing-entry", @@ -579,6 +691,7 @@ assert.match(shellText, /\bexport function createIniPanelShellIntegrationSchema\ 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 createIniPanelShellSessionHandoffActionPlan\b/); assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffCompatibilityReport\b/); assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffDisplayViewModel\b/); assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffRenderState\b/); @@ -593,6 +706,7 @@ assert.match(launchText, /\bgetShellIntegrationReadiness\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationManifest\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationReadiness\b/); assert.match(docText, /\bcreateIniPanelShellIntegrationWorkflowPlan\b/); +assert.match(docText, /\bcreateIniPanelShellSessionHandoffActionPlan\b/); assert.match(docText, /\bcreateIniPanelShellSessionHandoffCompatibilityReport\b/); assert.match(docText, /\bcreateIniPanelShellSessionHandoffDisplayViewModel\b/); assert.match(docText, /\bcreateIniPanelShellSessionHandoffRenderState\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 770aee4..8d3f261 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 @@ -14,6 +14,7 @@ import { createIniPanelShellIntegrationReadiness, createIniPanelShellIntegrationSchema, createIniPanelShellIntegrationWorkflowPlan, + createIniPanelShellSessionHandoffActionPlan, createIniPanelShellSessionHandoffCompatibilityReport, createIniPanelShellSessionHandoffDisplayViewModel, createIniPanelShellSessionHandoffRenderState, @@ -73,6 +74,7 @@ assert.equal(typeof createIniPanelShellIntegrationManifest, "function"); assert.equal(typeof createIniPanelShellIntegrationReadiness, "function"); assert.equal(typeof createIniPanelShellIntegrationSchema, "function"); assert.equal(typeof createIniPanelShellIntegrationWorkflowPlan, "function"); +assert.equal(typeof createIniPanelShellSessionHandoffActionPlan, "function"); assert.equal(typeof createIniPanelShellSessionHandoffCompatibilityReport, "function"); assert.equal(typeof createIniPanelShellSessionHandoffDisplayViewModel, "function"); assert.equal(typeof createIniPanelShellSessionHandoffRenderState, "function"); @@ -167,6 +169,13 @@ assert.deepEqual( shellViewModel.shellSessionHandoffRenderState, createIniPanelShellSessionHandoffRenderState(shellViewModel.shellSessionHandoffSnapshot), ); +assert.deepEqual( + shellViewModel.shellSessionHandoffActionPlan, + createIniPanelShellSessionHandoffActionPlan({ + snapshot: shellViewModel.shellSessionHandoffSnapshot, + renderState: shellViewModel.shellSessionHandoffRenderState, + }), +); assert.deepEqual(shellViewModel.shellSessionHandoffDisplayViewModel, { phase: "ready", title: "Shell handoff compatibility", @@ -215,6 +224,41 @@ assert.deepEqual(shellViewModel.shellSessionHandoffRenderState, { handoffReady: "true", }, }); +assert.deepEqual(shellViewModel.shellSessionHandoffActionPlan, { + apiName: "ini-panel-shell-session-handoff-action-plan", + planVersion: 1, + phase: "ready", + ready: true, + statusLine: "Ready: No blocking reasons", + targetPage: { + entryId: "control-view", + href: "./control-page.html", + }, + requiredMethods: { + loadSessionState: "loadControlPageSessionState", + readStatus: "readControlPageStatus", + }, + blockingReasons: [], + displayRows: shellViewModel.shellSessionHandoffRenderState.rows, + nextSteps: [ + { + id: "open-control-page", + label: "Open control page target", + targetPage: { + entryId: "control-view", + href: "./control-page.html", + }, + }, + { + id: "read-readonly-status", + label: "Read readonly status", + methods: { + loadSessionState: "loadControlPageSessionState", + readStatus: "readControlPageStatus", + }, + }, + ], +}); assert.equal( isSupportedIniPanelShellIntegrationManifest(shellViewModel.shellIntegrationManifest), true, @@ -260,6 +304,7 @@ assert.equal( ); assert.equal(shellViewModel.createShellSessionHandoffSnapshot, createIniPanelShellSessionHandoffSnapshot); assert.equal(shellViewModel.createShellSessionHandoffRenderState, createIniPanelShellSessionHandoffRenderState); +assert.equal(shellViewModel.createShellSessionHandoffActionPlan, createIniPanelShellSessionHandoffActionPlan); assert.deepEqual(shellViewModel.controlPage.contract, createIniPanelShellControlPageContract()); assert.equal(shellViewModel.controlPage.isSupportedBrowserApiManifest, isSupportedIniPanelControlPageApiManifest); assert.equal(shellViewModel.controlPage.loadSessionState, loadMachineSessionForControlPageWorkflow);