推进 INI 面板 shell handoff workflow summary helper

This commit is contained in:
2026-06-15 13:58:17 +08:00
parent 859d01356b
commit 4c02f33d57
9 changed files with 256 additions and 3 deletions

View File

@@ -34,6 +34,7 @@ import {
createIniPanelShellSessionHandoffMountDomReadiness,
mountIniPanelShellSessionHandoff,
mountIniPanelShellSessionHandoffFromControlPage,
createIniPanelShellSessionHandoffWorkflowSummary,
renderIniPanelShellSessionHandoffMountState,
createIniPanelShellSessionHandoffPackageReport,
createIniPanelShellSessionHandoffPackageRenderState,
@@ -250,6 +251,11 @@ view-model, mount render-state, and DOM mount workflow result. Use
`linuxCncIniPanelLaunchApi.readShellSessionHandoffStatusFromControlPage()`
when an outer shell needs to read the control-page readonly status and verify
the expected handoff status fields as a structured ready/blocked result. Use
`createIniPanelShellSessionHandoffWorkflowSummary()` or
`linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowSummary()` when an
outer shell needs one stable end-to-end summary that combines mount workflow,
control-page mount workflow, readonly status result, and current blocking
reasons into display rows. Use
`renderIniPanelShellSessionHandoffMountState()` or
`linuxCncIniPanelLaunchApi.renderShellSessionHandoffMountState()` when an outer
shell needs the shared read-only DOM renderer for those rows. The launch page

View File

@@ -111,6 +111,7 @@
mountIniPanelShellSessionHandoff,
mountIniPanelShellSessionHandoffFromControlPage,
readIniPanelShellSessionHandoffStatusFromControlPage,
createIniPanelShellSessionHandoffWorkflowSummary,
renderIniPanelShellSessionHandoffMountState,
} from "./ui-shell.js";
@@ -214,6 +215,23 @@
...(options ?? {}),
})
),
getShellSessionHandoffWorkflowSummary: (options) => (
createIniPanelShellSessionHandoffWorkflowSummary(options ?? {
mountWorkflow: {
phase: shellViewModel.shellSessionHandoffPackageMountRenderState.phase,
ready: shellViewModel.shellSessionHandoffPackageMountRenderState.ready,
statusLine: shellViewModel.shellSessionHandoffPackageMountRenderState.statusLine,
},
controlPageMountWorkflow: {
phase: shellViewModel.shellSessionHandoffPackageMountState.phase,
ready: shellViewModel.shellSessionHandoffPackageMountState.ready,
statusLine: shellViewModel.shellSessionHandoffPackageMountState.statusLine,
},
readonlyStatusResult: shellViewModel.readShellSessionHandoffStatusFromControlPage({
handoffSummary: shellViewModel.shellSessionHandoffSummary,
}),
})
),
isSupportedShellSessionHandoffPackage: shellViewModel.isSupportedShellSessionHandoffPackage,
getLauncherLinks: () => shellViewModel.launcherLinks,
getControlPageApiMethods: () => shellViewModel.controlPage.browserApiMethods,

View File

@@ -149,6 +149,7 @@ export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntr
"mountShellSessionHandoff",
"mountShellSessionHandoffFromControlPage",
"readShellSessionHandoffStatusFromControlPage",
"getShellSessionHandoffWorkflowSummary",
"isSupportedShellSessionHandoffPackage",
"getLauncherLinks",
"getControlPageApiMethods",
@@ -1376,6 +1377,103 @@ export function readIniPanelShellSessionHandoffStatusFromControlPage({
};
}
export function createIniPanelShellSessionHandoffWorkflowSummary({
mountWorkflow = {
phase: "blocked",
ready: false,
statusLine: "Blocked: mount workflow unavailable",
},
controlPageMountWorkflow = {
phase: "blocked",
ready: false,
statusLine: "Blocked: control-page mount workflow unavailable",
},
readonlyStatusResult = {
phase: "blocked",
ready: false,
statusLine: "Blocked: readonly status unavailable",
missingStatusFields: [],
},
} = {}) {
const blockingReasons = [];
if (mountWorkflow?.ready !== true) {
blockingReasons.push(`mount:${mountWorkflow?.phase ?? "blocked"}`);
}
if (controlPageMountWorkflow?.ready !== true) {
blockingReasons.push(`control-page-mount:${controlPageMountWorkflow?.phase ?? "blocked"}`);
}
if (readonlyStatusResult?.ready !== true) {
const readonlyDetail = readonlyStatusResult?.readError
?? (
Array.isArray(readonlyStatusResult?.missingStatusFields)
&& readonlyStatusResult.missingStatusFields.length > 0
? readonlyStatusResult.missingStatusFields.join(", ")
: readonlyStatusResult?.phase ?? "blocked"
);
blockingReasons.push(`readonly-status:${readonlyDetail}`);
}
const ready = blockingReasons.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-workflow-summary",
summaryVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
statusLine: ready
? "Ready: shell handoff end-to-end workflow available"
: `Blocked: ${blockingReasons.join("; ")}`,
mountWorkflow: {
phase: mountWorkflow?.phase ?? "blocked",
ready: mountWorkflow?.ready === true,
statusLine: mountWorkflow?.statusLine ?? null,
},
controlPageMountWorkflow: {
phase: controlPageMountWorkflow?.phase ?? "blocked",
ready: controlPageMountWorkflow?.ready === true,
statusLine: controlPageMountWorkflow?.statusLine ?? null,
},
readonlyStatusResult: {
phase: readonlyStatusResult?.phase ?? "blocked",
ready: readonlyStatusResult?.ready === true,
statusLine: readonlyStatusResult?.statusLine ?? null,
missingStatusFields: Array.isArray(readonlyStatusResult?.missingStatusFields)
? [...readonlyStatusResult.missingStatusFields]
: [],
},
blockingReasons,
rows: [
{
id: "mount-workflow",
label: "Mount workflow",
value: mountWorkflow?.statusLine ?? "missing",
},
{
id: "control-page-mount-workflow",
label: "Control-page mount workflow",
value: controlPageMountWorkflow?.statusLine ?? "missing",
},
{
id: "readonly-status-workflow",
label: "Readonly status workflow",
value: readonlyStatusResult?.statusLine ?? "missing",
},
{
id: "readonly-status-missing-fields",
label: "Readonly missing fields",
value: Array.isArray(readonlyStatusResult?.missingStatusFields)
&& readonlyStatusResult.missingStatusFields.length > 0
? readonlyStatusResult.missingStatusFields.join(", ")
: "none",
},
{
id: "blocking-reasons",
label: "Blocking reasons",
value: blockingReasons.length > 0 ? blockingReasons.join("; ") : "none",
},
],
};
}
export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries()) {
const shellIntegrationManifest = createIniPanelShellIntegrationManifest(entries);
const shellIntegrationReadiness = createIniPanelShellIntegrationReadiness(shellIntegrationManifest);
@@ -1496,6 +1594,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
mountShellSessionHandoff: mountIniPanelShellSessionHandoff,
mountShellSessionHandoffFromControlPage: mountIniPanelShellSessionHandoffFromControlPage,
readShellSessionHandoffStatusFromControlPage: readIniPanelShellSessionHandoffStatusFromControlPage,
createShellSessionHandoffWorkflowSummary: createIniPanelShellSessionHandoffWorkflowSummary,
isSupportedShellSessionHandoffPackage: isSupportedIniPanelShellSessionHandoffPackage,
controlPage: {
browserApiSchema: createControlPageBrowserApiSchema(),

View File

@@ -91,6 +91,7 @@ TOOL_TABLE = browser-shell-tool.tbl
!launchApi?.mountShellSessionHandoff ||
!launchApi?.mountShellSessionHandoffFromControlPage ||
!launchApi?.readShellSessionHandoffStatusFromControlPage ||
!launchApi?.getShellSessionHandoffWorkflowSummary ||
!launchApi?.isSupportedShellSessionHandoffPackage
) {
throw new Error("launch API namespace did not expose shell integration helpers");
@@ -357,6 +358,11 @@ TOOL_TABLE = browser-shell-tool.tbl
const readonlyStatusResult = launchApi.readShellSessionHandoffStatusFromControlPage({
controlPageApi,
});
const workflowSummaryResult = launchApi.getShellSessionHandoffWorkflowSummary({
mountWorkflow: readyMountWorkflow,
controlPageMountWorkflow: readyControlPageMountWorkflow,
readonlyStatusResult,
});
assertEqual(readonlyStatusResult.phase, "ready", "shell handoff readonly status result phase");
assertEqual(readonlyStatusResult.ready, true, "shell handoff readonly status result readiness");
assertEqual(
@@ -364,6 +370,9 @@ TOOL_TABLE = browser-shell-tool.tbl
"",
"shell handoff readonly status missing fields",
);
assertEqual(workflowSummaryResult.phase, "ready", "shell handoff workflow summary phase");
assertEqual(workflowSummaryResult.ready, true, "shell handoff workflow summary readiness");
assertEqual(workflowSummaryResult.blockingReasons.join(","), "", "shell handoff workflow summary blocking reasons");
assertEqual(
readonlyStatus.workflowStatus.lastAction,
"load-session",

View File

@@ -32,6 +32,7 @@ import {
mountIniPanelShellSessionHandoff,
mountIniPanelShellSessionHandoffFromControlPage,
readIniPanelShellSessionHandoffStatusFromControlPage,
createIniPanelShellSessionHandoffWorkflowSummary,
renderIniPanelShellSessionHandoffMountState,
createIniPanelShellSessionHandoffPackageReport,
createIniPanelShellSessionHandoffPackageRenderState,
@@ -103,6 +104,7 @@ const requiredShellExports = [
"mountIniPanelShellSessionHandoff",
"mountIniPanelShellSessionHandoffFromControlPage",
"readIniPanelShellSessionHandoffStatusFromControlPage",
"createIniPanelShellSessionHandoffWorkflowSummary",
"renderIniPanelShellSessionHandoffMountState",
"createIniPanelShellSessionHandoffPackageReport",
"createIniPanelShellSessionHandoffPackageRenderState",
@@ -309,6 +311,10 @@ assert.equal(
readIniPanelShellSessionHandoffStatusFromControlPage().phase,
"blocked",
);
assert.equal(
createIniPanelShellSessionHandoffWorkflowSummary().phase,
"blocked",
);
assert.equal(isSupportedIniPanelShellSessionHandoffPackage(createIniPanelShellViewModel().shellSessionHandoffPackage), true);
assert.equal(isSupportedIniPanelShellIntegrationManifest(createIniPanelShellViewModel().shellIntegrationManifest), true);
assert.deepEqual(createIniPanelShellViewModel().launchApiSchema, createIniPanelLaunchApiSchema());
@@ -471,6 +477,7 @@ assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffMountD
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.mountShellSessionHandoff\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.mountShellSessionHandoffFromControlPage\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.readShellSessionHandoffStatusFromControlPage\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffWorkflowSummary\b/);
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getControlPageApiMethods\b/);
assert.match(docText, /\bini_panel_shell_integration_workflow_smoke\.html\b/);
assert.match(browserIniPanelText, /\bini_panel_shell_integration_workflow_smoke\.html\b/);
@@ -492,6 +499,7 @@ assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffMountDo
assert.match(shellIntegrationWorkflowSmokeText, /\bmountShellSessionHandoff\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bmountShellSessionHandoffFromControlPage\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\breadShellSessionHandoffStatusFromControlPage\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffWorkflowSummary\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\brenderShellSessionHandoffMountState\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bdata-external-shell-mount\b/);
assert.match(shellIntegrationWorkflowSmokeText, /\bdata-external-mount-value\b/);

View File

@@ -69,6 +69,7 @@ assert.deepEqual(manifest, {
"mountShellSessionHandoff",
"mountShellSessionHandoffFromControlPage",
"readShellSessionHandoffStatusFromControlPage",
"getShellSessionHandoffWorkflowSummary",
"isSupportedShellSessionHandoffPackage",
"getLauncherLinks",
"getControlPageApiMethods",
@@ -193,6 +194,7 @@ assert.match(launchText, /\brenderShellSessionHandoffMountState\b/);
assert.match(launchText, /\bmountShellSessionHandoff\b/);
assert.match(launchText, /\bmountShellSessionHandoffFromControlPage\b/);
assert.match(launchText, /\breadShellSessionHandoffStatusFromControlPage\b/);
assert.match(launchText, /\bgetShellSessionHandoffWorkflowSummary\b/);
assert.match(launchText, /\bisSupportedShellSessionHandoffPackage\b/);
assert.match(launchText, /\bdata-handoff-mount\b/);
assert.match(launchText, /\bdata-handoff-mount-status\b/);

View File

@@ -28,6 +28,7 @@ import {
mountIniPanelShellSessionHandoff,
mountIniPanelShellSessionHandoffFromControlPage,
readIniPanelShellSessionHandoffStatusFromControlPage,
createIniPanelShellSessionHandoffWorkflowSummary,
createIniPanelShellSessionHandoffPackageReport,
createIniPanelShellSessionHandoffPackageRenderState,
createIniPanelShellSessionHandoffPackageSchema,
@@ -154,7 +155,7 @@ assert.deepEqual(readiness, {
counts: {
pages: 2,
launcherLinks: 2,
launchMethods: 32,
launchMethods: 33,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -216,7 +217,7 @@ assert.deepEqual(handoffSummary, {
counts: {
pages: 2,
launcherLinks: 2,
launchMethods: 32,
launchMethods: 33,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -716,6 +717,10 @@ assert.equal(
readIniPanelShellSessionHandoffStatusFromControlPage().readError,
"Missing readControlPageStatus",
);
assert.equal(
createIniPanelShellSessionHandoffWorkflowSummary().phase,
"blocked",
);
assert.deepEqual(
createIniPanelShellSessionHandoffPackageMountState({
handoffPackage,
@@ -1127,7 +1132,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), {
counts: {
pages: 2,
launcherLinks: 2,
launchMethods: 32,
launchMethods: 33,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -1301,6 +1306,7 @@ assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffMoun
assert.match(shellText, /\bexport function mountIniPanelShellSessionHandoff\b/);
assert.match(shellText, /\bexport function mountIniPanelShellSessionHandoffFromControlPage\b/);
assert.match(shellText, /\bexport function readIniPanelShellSessionHandoffStatusFromControlPage\b/);
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffWorkflowSummary\b/);
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffPackageReport\b/);
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffPackageRenderState\b/);
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffPackageSchema\b/);
@@ -1331,6 +1337,7 @@ assert.match(docText, /\bcreateIniPanelShellSessionHandoffMountDomReadiness\b/);
assert.match(docText, /\bmountIniPanelShellSessionHandoff\b/);
assert.match(docText, /\bmountIniPanelShellSessionHandoffFromControlPage\b/);
assert.match(docText, /\breadIniPanelShellSessionHandoffStatusFromControlPage\b/);
assert.match(docText, /\bcreateIniPanelShellSessionHandoffWorkflowSummary\b/);
assert.match(docText, /\bcreateIniPanelShellSessionHandoffPackageReport\b/);
assert.match(docText, /\bcreateIniPanelShellSessionHandoffPackageRenderState\b/);
assert.match(docText, /\bcreateIniPanelShellSessionHandoffPackageSchema\b/);

View File

@@ -28,6 +28,7 @@ import {
mountIniPanelShellSessionHandoff,
mountIniPanelShellSessionHandoffFromControlPage,
readIniPanelShellSessionHandoffStatusFromControlPage,
createIniPanelShellSessionHandoffWorkflowSummary,
renderIniPanelShellSessionHandoffMountState,
createIniPanelShellSessionHandoffPackageReport,
createIniPanelShellSessionHandoffPackageRenderState,
@@ -104,6 +105,7 @@ assert.equal(typeof createIniPanelShellSessionHandoffMountDomReadiness, "functio
assert.equal(typeof mountIniPanelShellSessionHandoff, "function");
assert.equal(typeof mountIniPanelShellSessionHandoffFromControlPage, "function");
assert.equal(typeof readIniPanelShellSessionHandoffStatusFromControlPage, "function");
assert.equal(typeof createIniPanelShellSessionHandoffWorkflowSummary, "function");
assert.equal(typeof renderIniPanelShellSessionHandoffMountState, "function");
assert.equal(typeof createIniPanelShellSessionHandoffPackageReport, "function");
assert.equal(typeof createIniPanelShellSessionHandoffPackageRenderState, "function");
@@ -714,6 +716,10 @@ assert.equal(
shellViewModel.readShellSessionHandoffStatusFromControlPage,
readIniPanelShellSessionHandoffStatusFromControlPage,
);
assert.equal(
shellViewModel.createShellSessionHandoffWorkflowSummary,
createIniPanelShellSessionHandoffWorkflowSummary,
);
assert.equal(shellViewModel.createShellSessionHandoffPackageReport, createIniPanelShellSessionHandoffPackageReport);
assert.equal(shellViewModel.createShellSessionHandoffPackageSchema, createIniPanelShellSessionHandoffPackageSchema);
assert.equal(shellViewModel.isSupportedShellSessionHandoffPackage, isSupportedIniPanelShellSessionHandoffPackage);
@@ -937,6 +943,29 @@ assert.equal(
}).readError,
"Missing readControlPageStatus",
);
const workflowSummaryResult = createIniPanelShellSessionHandoffWorkflowSummary({
mountWorkflow: mountWorkflowResult,
controlPageMountWorkflow,
readonlyStatusResult,
});
assert.equal(workflowSummaryResult.apiName, "ini-panel-shell-session-handoff-workflow-summary");
assert.equal(workflowSummaryResult.summaryVersion, 1);
assert.equal(workflowSummaryResult.phase, "blocked");
assert.equal(workflowSummaryResult.ready, false);
assert.equal(workflowSummaryResult.rows.length, 5);
assert.equal(workflowSummaryResult.blockingReasons[0], "mount:blocked");
const readyWorkflowSummaryResult = createIniPanelShellSessionHandoffWorkflowSummary({
mountWorkflow: {
phase: "ready",
ready: true,
statusLine: "Ready: mount workflow available",
},
controlPageMountWorkflow,
readonlyStatusResult,
});
assert.equal(readyWorkflowSummaryResult.phase, "ready");
assert.equal(readyWorkflowSummaryResult.ready, true);
assert.equal(readyWorkflowSummaryResult.blockingReasons.length, 0);
assert.deepEqual(
mountIniPanelShellSessionHandoff({ documentRef: mountDocument }).renderResult,
null,