推进 INI 面板 shell handoff summary browser API

This commit is contained in:
2026-06-15 08:12:33 +08:00
parent b31b9a2f8a
commit 01817f6493
9 changed files with 133 additions and 9 deletions

View File

@@ -2008,3 +2008,81 @@ host_wasm_opfs_browser_smokes=ok
继续推进 browser/UI workflow。下一批建议把 `shellSessionHandoffSummary` 暴露到 launch browser API 继续推进 browser/UI workflow。下一批建议把 `shellSessionHandoffSummary` 暴露到 launch browser API
或 shell integration browser workflow smoke 中,让真实 browser 外部 shell 可以直接读取 summary 或 shell integration browser workflow smoke 中,让真实 browser 外部 shell 可以直接读取 summary
并与 readonly status handoff 结果交叉校验;仍保持只读,不新增控制按钮,不解析 G-code。 并与 readonly status handoff 结果交叉校验;仍保持只读,不新增控制按钮,不解析 G-code。
二十五、2026-06-15 继续执行记录INI panel shell session handoff summary browser API
本轮按“第一原则:加快实质性推进”继续推进 browser/UI workflow
`shellSessionHandoffSummary` 暴露到真实 launch browser API并在 shell integration browser
workflow smoke 中用 summary 驱动 readonly status handoff。
完成内容:
- `createIniPanelLaunchApiManifest().methods` 新增 `getShellSessionHandoffSummary`
- `launch.html` 的 `window.linuxCncIniPanelLaunchApi` 新增真实 browser 入口
`getShellSessionHandoffSummary()`
- `ini_panel_launch_smoke.html` 在真实 launch iframe 中校验:
- `getShellSessionHandoffSummary()` 存在;
- launch API manifest methods 包含该方法;
- summary `ready === true`
- summary target page 为 `./control-page.html`
- summary readonly fields 包含 `session`
- summary persistence capabilities 数量为 3
- `ini_panel_shell_integration_workflow_smoke.html` 改为读取
`getShellSessionHandoffSummary()`,并使用 summary 的 handoff methods 调用:
- `loadControlPageSessionState()`
- `readControlPageStatus()`
- shell integration browser workflow smoke 现在交叉校验:
- summary target 与 integration manifest target 一致;
- summary handoff methods 可调用;
- summary readonly status fields 全部存在于 `readControlPageStatus()` 返回值;
- summary persistence capabilities 与 blocking reasons
- `verify_ini_panel_launch_api_manifest.mjs`、`verify_ini_panel_entry_docs.mjs` 与
`docs/panel-entry.md` 同步记录和校验 browser API
- `verify_ini_panel_shell_integration_manifest.mjs` 将 launch method count 更新为 9
- 未新增控制按钮;
- 未解析 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_launch_api_manifest.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_launch_api_manifest_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
```
下一步建议:
继续推进 SDK/API surface。下一批建议为 shell/session handoff summary 增加 focused compatibility
predicate 或 versioned summary schema例如 `createIniPanelShellSessionHandoffSummarySchema()`
让外部 shell 能独立判断 summary shape 是否可消费;仍保持只读,不新增控制按钮,不解析 G-code。

View File

@@ -132,7 +132,9 @@ control-page API methods, readiness gate, and readonly status handoff fields
before opening a browser frame. before opening a browser frame.
Use `createIniPanelShellSessionHandoffSummary()` when the outer shell needs one Use `createIniPanelShellSessionHandoffSummary()` when the outer shell needs one
read-only display summary of readiness, target page, persistence capabilities, read-only display summary of readiness, target page, persistence capabilities,
readonly status fields, handoff methods, and blocking reasons. readonly status fields, handoff methods, and blocking reasons. Browser shells
can read the same summary through
`linuxCncIniPanelLaunchApi.getShellSessionHandoffSummary()`.
The browser workflow smoke The browser workflow smoke
`tests/browser/ini_panel_shell_integration_workflow_smoke.html` verifies the `tests/browser/ini_panel_shell_integration_workflow_smoke.html` verifies the
same external shell handoff: read integration manifest, pass readiness gate, same external shell handoff: read integration manifest, pass readiness gate,

View File

@@ -97,6 +97,7 @@
getShellIntegrationSchema: () => shellViewModel.shellIntegrationSchema, getShellIntegrationSchema: () => shellViewModel.shellIntegrationSchema,
getShellIntegrationManifest: () => shellViewModel.shellIntegrationManifest, getShellIntegrationManifest: () => shellViewModel.shellIntegrationManifest,
getShellIntegrationReadiness: () => shellViewModel.shellIntegrationReadiness, getShellIntegrationReadiness: () => shellViewModel.shellIntegrationReadiness,
getShellSessionHandoffSummary: () => shellViewModel.shellSessionHandoffSummary,
getLauncherLinks: () => shellViewModel.launcherLinks, getLauncherLinks: () => shellViewModel.launcherLinks,
getControlPageApiMethods: () => shellViewModel.controlPage.browserApiMethods, getControlPageApiMethods: () => shellViewModel.controlPage.browserApiMethods,
}; };

View File

@@ -126,6 +126,7 @@ export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntr
"getShellIntegrationSchema", "getShellIntegrationSchema",
"getShellIntegrationManifest", "getShellIntegrationManifest",
"getShellIntegrationReadiness", "getShellIntegrationReadiness",
"getShellSessionHandoffSummary",
"getLauncherLinks", "getLauncherLinks",
"getControlPageApiMethods", "getControlPageApiMethods",
], ],

View File

@@ -73,6 +73,7 @@
!launchApi?.getShellIntegrationSchema || !launchApi?.getShellIntegrationSchema ||
!launchApi?.getShellIntegrationManifest || !launchApi?.getShellIntegrationManifest ||
!launchApi?.getShellIntegrationReadiness || !launchApi?.getShellIntegrationReadiness ||
!launchApi?.getShellSessionHandoffSummary ||
!launchApi?.getLauncherLinks || !launchApi?.getLauncherLinks ||
!launchApi?.getControlPageApiMethods !launchApi?.getControlPageApiMethods
) { ) {
@@ -112,12 +113,13 @@
); );
assertEqual( assertEqual(
launchApiManifest.methods.join(","), launchApiManifest.methods.join(","),
"isSupportedLaunchApiManifest,getLaunchApiSchema,getLaunchApiManifest,getShellIntegrationSchema,getShellIntegrationManifest,getShellIntegrationReadiness,getLauncherLinks,getControlPageApiMethods", "isSupportedLaunchApiManifest,getLaunchApiSchema,getLaunchApiManifest,getShellIntegrationSchema,getShellIntegrationManifest,getShellIntegrationReadiness,getShellSessionHandoffSummary,getLauncherLinks,getControlPageApiMethods",
"launch API manifest methods", "launch API manifest methods",
); );
const shellIntegrationSchema = launchApi.getShellIntegrationSchema(); const shellIntegrationSchema = launchApi.getShellIntegrationSchema();
const shellIntegrationManifest = launchApi.getShellIntegrationManifest(); const shellIntegrationManifest = launchApi.getShellIntegrationManifest();
const shellIntegrationReadiness = launchApi.getShellIntegrationReadiness(); const shellIntegrationReadiness = launchApi.getShellIntegrationReadiness();
const shellSessionHandoffSummary = launchApi.getShellSessionHandoffSummary();
assertEqual( assertEqual(
shellIntegrationSchema.manifestVersion, shellIntegrationSchema.manifestVersion,
1, 1,
@@ -158,6 +160,26 @@
"", "",
"shell integration readiness missing checks", "shell integration readiness missing checks",
); );
assertEqual(
shellSessionHandoffSummary.ready,
true,
"shell session handoff summary readiness",
);
assertEqual(
shellSessionHandoffSummary.targetPage.href,
"./control-page.html",
"shell session handoff summary target",
);
assertEqual(
shellSessionHandoffSummary.readonlyStatusFields.includes("session"),
true,
"shell session handoff summary readonly fields",
);
assertEqual(
shellSessionHandoffSummary.persistenceCapabilities.length,
3,
"shell session handoff summary persistence capabilities",
);
assertEqual( assertEqual(
JSON.stringify(shellIntegrationManifest), JSON.stringify(shellIntegrationManifest),
JSON.stringify(createIniPanelShellIntegrationManifest()), JSON.stringify(createIniPanelShellIntegrationManifest()),

View File

@@ -63,16 +63,24 @@ TOOL_TABLE = browser-shell-tool.tbl
const launchApi = launchDoc.defaultView.linuxCncIniPanelLaunchApi; const launchApi = launchDoc.defaultView.linuxCncIniPanelLaunchApi;
if ( if (
!launchApi?.getShellIntegrationManifest || !launchApi?.getShellIntegrationManifest ||
!launchApi?.getShellIntegrationReadiness !launchApi?.getShellIntegrationReadiness ||
!launchApi?.getShellSessionHandoffSummary
) { ) {
throw new Error("launch API namespace did not expose shell integration helpers"); throw new Error("launch API namespace did not expose shell integration helpers");
} }
const integrationManifest = launchApi.getShellIntegrationManifest(); const integrationManifest = launchApi.getShellIntegrationManifest();
const readiness = launchApi.getShellIntegrationReadiness(); const readiness = launchApi.getShellIntegrationReadiness();
const handoffSummary = launchApi.getShellSessionHandoffSummary();
assertEqual(readiness.phase, "ready", "shell integration readiness phase"); assertEqual(readiness.phase, "ready", "shell integration readiness phase");
assertEqual(readiness.ready, true, "shell integration readiness"); assertEqual(readiness.ready, true, "shell integration readiness");
assertEqual(readiness.missing.join(","), "", "shell integration readiness missing checks"); assertEqual(readiness.missing.join(","), "", "shell integration readiness missing checks");
assertEqual(handoffSummary.ready, true, "shell handoff summary readiness");
assertEqual(handoffSummary.blockingReasons.join(","), "", "shell handoff summary blocking reasons");
assertEqual(handoffSummary.handoffMethods.loadSessionState, "loadControlPageSessionState", "shell handoff load method");
assertEqual(handoffSummary.handoffMethods.readStatus, "readControlPageStatus", "shell handoff status method");
assertEqual(handoffSummary.readonlyStatusFields.includes("session"), true, "shell handoff readonly session field");
assertEqual(handoffSummary.persistenceCapabilities.length, 3, "shell handoff persistence capabilities");
assertEqual(readiness.apiNames.launch, "ini-panel-launch", "shell integration launch API name"); assertEqual(readiness.apiNames.launch, "ini-panel-launch", "shell integration launch API name");
assertEqual( assertEqual(
readiness.apiNames.controlPage, readiness.apiNames.controlPage,
@@ -94,6 +102,7 @@ TOOL_TABLE = browser-shell-tool.tbl
(page) => page.entryId === "control-view", (page) => page.entryId === "control-view",
); );
assertEqual(controlTarget.href, "./control-page.html", "shell integration control target"); assertEqual(controlTarget.href, "./control-page.html", "shell integration control target");
assertEqual(handoffSummary.targetPage.href, controlTarget.href, "shell handoff summary target");
const controlDoc = await loadFrame(`../../runtime/ui/ini-panel/${controlTarget.href.slice(2)}`); const controlDoc = await loadFrame(`../../runtime/ui/ini-panel/${controlTarget.href.slice(2)}`);
const controlPageApi = controlDoc.defaultView.linuxCncIniPanelControlPageApi; const controlPageApi = controlDoc.defaultView.linuxCncIniPanelControlPageApi;
@@ -120,12 +129,12 @@ TOOL_TABLE = browser-shell-tool.tbl
"embedded INI panel API", "embedded INI panel API",
); );
assertEqual( assertEqual(
controlPageApi.loadControlPageSessionState().phase, controlPageApi[handoffSummary.handoffMethods.loadSessionState]().phase,
"ready", "ready",
"shell integration control-page session readiness", "shell integration control-page session readiness",
); );
assertEqual( assertEqual(
controlPageApi.readControlPageStatus().status.lastAction, controlPageApi[handoffSummary.handoffMethods.readStatus]().status.lastAction,
null, null,
"shell integration initial readonly status", "shell integration initial readonly status",
); );
@@ -147,7 +156,7 @@ TOOL_TABLE = browser-shell-tool.tbl
"shell integration load session", "shell integration load session",
); );
const readonlyStatus = controlPageApi.readControlPageStatus(); const readonlyStatus = controlPageApi[handoffSummary.handoffMethods.readStatus]();
assertEqual( assertEqual(
readonlyStatus.workflowStatus.lastAction, readonlyStatus.workflowStatus.lastAction,
"load-session", "load-session",
@@ -163,6 +172,11 @@ TOOL_TABLE = browser-shell-tool.tbl
"browser-shell-tool.tbl", "browser-shell-tool.tbl",
"shell integration readonly state bundle tool table", "shell integration readonly state bundle tool table",
); );
assertEqual(
handoffSummary.readonlyStatusFields.every((fieldName) => Object.hasOwn(readonlyStatus, fieldName)),
true,
"shell handoff readonly status fields",
);
assertEqual( assertEqual(
controlDoc.getElementById("control-page-status").textContent, controlDoc.getElementById("control-page-status").textContent,
"load-session | not-run | -", "load-session | not-run | -",

View File

@@ -221,6 +221,7 @@ assert.match(launchText, /\bgetLaunchApiManifest\b/);
assert.match(launchText, /\bgetShellIntegrationSchema\b/); assert.match(launchText, /\bgetShellIntegrationSchema\b/);
assert.match(launchText, /\bgetShellIntegrationManifest\b/); assert.match(launchText, /\bgetShellIntegrationManifest\b/);
assert.match(launchText, /\bgetShellIntegrationReadiness\b/); assert.match(launchText, /\bgetShellIntegrationReadiness\b/);
assert.match(launchText, /\bgetShellSessionHandoffSummary\b/);
assert.match(launchText, /\bgetControlPageApiMethods\b/); assert.match(launchText, /\bgetControlPageApiMethods\b/);
assert.match(controlPageText, /\bgetControlPageContract\b/); assert.match(controlPageText, /\bgetControlPageContract\b/);
assert.match(controlPageText, /\breadControlPageStatus\b/); assert.match(controlPageText, /\breadControlPageStatus\b/);
@@ -249,10 +250,13 @@ assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getLaunchApiManifest\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationSchema\b/); assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationSchema\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationManifest\b/); assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationManifest\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationReadiness\b/); assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationReadiness\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffSummary\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getControlPageApiMethods\b/); assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getControlPageApiMethods\b/);
assert.match(docText, /\bini_panel_shell_integration_workflow_smoke\.html\b/); assert.match(docText, /\bini_panel_shell_integration_workflow_smoke\.html\b/);
assert.match(browserIniPanelText, /\bini_panel_shell_integration_workflow_smoke\.html\b/); assert.match(browserIniPanelText, /\bini_panel_shell_integration_workflow_smoke\.html\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellIntegrationReadiness\b/); assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellIntegrationReadiness\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffSummary\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bhandoffSummary\.handoffMethods\.readStatus\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\breadControlPageStatus\b/); assert.match(shellIntegrationWorkflowSmokeText, /\breadControlPageStatus\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bbrowser_ini_shell_integration_workflow_smoke=ok\b/); assert.match(shellIntegrationWorkflowSmokeText, /\bbrowser_ini_shell_integration_workflow_smoke=ok\b/);
assert.match(docText, /\bOPFS\/session persistence capabilities\b/); assert.match(docText, /\bOPFS\/session persistence capabilities\b/);

View File

@@ -46,6 +46,7 @@ assert.deepEqual(manifest, {
"getShellIntegrationSchema", "getShellIntegrationSchema",
"getShellIntegrationManifest", "getShellIntegrationManifest",
"getShellIntegrationReadiness", "getShellIntegrationReadiness",
"getShellSessionHandoffSummary",
"getLauncherLinks", "getLauncherLinks",
"getControlPageApiMethods", "getControlPageApiMethods",
], ],
@@ -146,6 +147,7 @@ assert.match(launchText, /\bgetLaunchApiManifest\b/);
assert.match(launchText, /\bgetShellIntegrationSchema\b/); assert.match(launchText, /\bgetShellIntegrationSchema\b/);
assert.match(launchText, /\bgetShellIntegrationManifest\b/); assert.match(launchText, /\bgetShellIntegrationManifest\b/);
assert.match(launchText, /\bgetShellIntegrationReadiness\b/); assert.match(launchText, /\bgetShellIntegrationReadiness\b/);
assert.match(launchText, /\bgetShellSessionHandoffSummary\b/);
assert.match(launchText, /\blinuxCncIniPanelLaunchApi\b/); assert.match(launchText, /\blinuxCncIniPanelLaunchApi\b/);
console.log("ini_panel_launch_api_manifest_node_smoke=ok"); console.log("ini_panel_launch_api_manifest_node_smoke=ok");

View File

@@ -85,7 +85,7 @@ assert.deepEqual(readiness, {
counts: { counts: {
pages: 2, pages: 2,
launcherLinks: 2, launcherLinks: 2,
launchMethods: 8, launchMethods: 9,
controlPageMethods: 11, controlPageMethods: 11,
persistenceCapabilities: 3, persistenceCapabilities: 3,
}, },
@@ -145,7 +145,7 @@ assert.deepEqual(handoffSummary, {
counts: { counts: {
pages: 2, pages: 2,
launcherLinks: 2, launcherLinks: 2,
launchMethods: 8, launchMethods: 9,
controlPageMethods: 11, controlPageMethods: 11,
persistenceCapabilities: 3, persistenceCapabilities: 3,
}, },
@@ -210,7 +210,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), {
counts: { counts: {
pages: 2, pages: 2,
launcherLinks: 2, launcherLinks: 2,
launchMethods: 8, launchMethods: 9,
controlPageMethods: 11, controlPageMethods: 11,
persistenceCapabilities: 3, persistenceCapabilities: 3,
}, },