Files
cnc_wams/wasm-port/runtime/ui/ini-panel/ui-shell.js

2379 lines
84 KiB
JavaScript

import {
INI_PANEL_ENTRIES,
getIniPanelEntryByHref,
getIniPanelEntryManifest,
} from "./entry-manifest.js";
import {
createMachineSessionControlPageController,
} from "./control-page-controller.js";
import {
renderMachineSessionControlView,
} from "./control-view-renderer.js";
import {
controlPageStatusText,
createControlPageRefreshState,
refreshMachineSessionControlPage,
} from "./control-page-refresh-workflow.js";
import {
createControlPageSessionLoadState,
loadMachineSessionForControlPageWorkflow,
} from "./control-page-session-workflow.js";
import {
createMachineSessionReadonlyStatus,
createMachineSessionReadonlyStatusApiManifest,
readMachineSessionReadonlyStatus,
} from "./readonly-status-api.js";
export {
controlPageStatusText,
createControlPageRefreshState,
refreshMachineSessionControlPage,
} from "./control-page-refresh-workflow.js";
export {
createControlPageSessionLoadState,
loadMachineSessionForControlPageWorkflow,
} from "./control-page-session-workflow.js";
export {
createMachineSessionReadonlyStatus,
createMachineSessionReadonlyStatusApiManifest,
readMachineSessionReadonlyStatus,
} from "./readonly-status-api.js";
export {
INI_PANEL_ENTRIES,
getIniPanelEntryByHref,
getIniPanelEntryManifest,
} from "./entry-manifest.js";
export {
createMachineSessionControlPageController,
} from "./control-page-controller.js";
export {
renderMachineSessionControlView,
} from "./control-view-renderer.js";
export function getVisibleIniPanelEntries() {
return getIniPanelEntryManifest();
}
export function createIniPanelLauncherLinkViewModel(entries = getVisibleIniPanelEntries()) {
return entries.map(({ id, href, title }) => ({
entryId: id,
href,
label: title,
}));
}
export function createIniPanelLaunchApiSchema() {
return {
apiName: "ini-panel-launch",
manifestVersion: 1,
fields: [
"apiName",
"manifestVersion",
"methods",
"visibleEntries",
"targetPages",
"controlPageApiMethods",
"persistenceCapabilities",
],
};
}
export function isSupportedIniPanelLaunchApiManifest(manifest, schema = createIniPanelLaunchApiSchema()) {
if (!manifest || typeof manifest !== "object") {
return false;
}
if (manifest.apiName !== schema.apiName || manifest.manifestVersion !== schema.manifestVersion) {
return false;
}
return schema.fields.every((fieldName) => Object.hasOwn(manifest, fieldName));
}
export function createControlPageBrowserApiSchema() {
return {
apiName: "ini-panel-control-page",
manifestVersion: 1,
fields: [
"apiName",
"manifestVersion",
"methods",
"contract",
"readonlyStatusApiManifest",
],
};
}
export function isSupportedIniPanelControlPageApiManifest(
manifest,
schema = createControlPageBrowserApiSchema(),
) {
if (!manifest || typeof manifest !== "object") {
return false;
}
if (manifest.apiName !== schema.apiName || manifest.manifestVersion !== schema.manifestVersion) {
return false;
}
return schema.fields.every((fieldName) => Object.hasOwn(manifest, fieldName));
}
export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntries()) {
const schema = createIniPanelLaunchApiSchema();
return {
apiName: schema.apiName,
manifestVersion: schema.manifestVersion,
methods: [
"isSupportedLaunchApiManifest",
"getLaunchApiSchema",
"getLaunchApiManifest",
"getShellIntegrationSchema",
"getShellIntegrationManifest",
"getShellIntegrationReadiness",
"getShellSessionHandoffSummarySchema",
"getShellSessionHandoffSummary",
"isSupportedShellSessionHandoffSummary",
"getShellSessionHandoffCompatibilityReport",
"getShellSessionHandoffDisplayViewModel",
"getShellSessionHandoffSnapshot",
"getShellSessionHandoffRenderState",
"getShellSessionHandoffActionPlan",
"getShellSessionHandoffChecklist",
"getShellSessionHandoffPackageSchema",
"getShellSessionHandoffPackage",
"getShellSessionHandoffPackageReport",
"getShellSessionHandoffPackageDisplayViewModel",
"getShellSessionHandoffPackageRenderState",
"getShellSessionHandoffPackageMountState",
"getShellSessionHandoffPackageMountDisplayViewModel",
"getShellSessionHandoffPackageMountRenderState",
"getShellSessionHandoffMountDomContract",
"getShellSessionHandoffMountDomReadiness",
"renderShellSessionHandoffMountState",
"mountShellSessionHandoff",
"mountShellSessionHandoffFromControlPage",
"readShellSessionHandoffStatusFromControlPage",
"getShellSessionHandoffWorkflowSummary",
"getShellSessionHandoffWorkflowDisplayViewModel",
"getShellSessionHandoffWorkflowRenderState",
"getShellSessionHandoffWorkflowDomContract",
"getShellSessionHandoffWorkflowDomReadiness",
"renderShellSessionHandoffWorkflowState",
"mountShellSessionHandoffWorkflow",
"getWorkflowOverviewContract",
"getWorkflowOverviewReadiness",
"mountWorkflowOverviewEmbedding",
"getWorkflowOverviewEmbeddingReport",
"getWorkflowOverviewEmbeddingDisplayViewModel",
"getWorkflowOverviewEmbeddingRenderState",
"getWorkflowOverviewEmbeddingDomContract",
"getWorkflowOverviewEmbeddingDomReadiness",
"renderWorkflowOverviewEmbeddingState",
"mountWorkflowOverviewEmbeddingState",
"getWorkflowOverviewEmbeddingMountDisplayViewModel",
"isSupportedShellSessionHandoffPackage",
"getLauncherLinks",
"getControlPageApiMethods",
],
visibleEntries: entries.map(({ id, href, title }) => ({
id,
href,
title,
})),
targetPages: entries.map(({ id, href }) => ({
entryId: id,
href,
})),
controlPageApiMethods: createControlPageBrowserApiMethods(),
persistenceCapabilities: [
{
id: "machine-files",
label: "Machine text files",
scope: "opfs",
},
{
id: "session-snapshot",
label: "Machine session snapshot",
scope: "opfs",
},
{
id: "readonly-status",
label: "Read-only machine session status",
scope: "control-page",
},
],
};
}
export function createIniPanelShellControlPageContract() {
const readonlyStatusApiManifest = createMachineSessionReadonlyStatusApiManifest();
return {
methodNames: [
"createController",
"loadSessionState",
"readStatus",
"refresh",
"renderView",
],
readonlyStatusApiManifest,
readonlyStatusApiMethodNames: Object.keys(readonlyStatusApiManifest.methods),
readonlyStatusApiStatusFields: readonlyStatusApiManifest.statusFields,
relatedControlPageMethods: readonlyStatusApiManifest.relatedControlPageMethods,
};
}
export function createControlPageBrowserApiMethods() {
return [
"isSupportedControlPageApiManifest",
"getControlPageApiSchema",
"getControlPageApiManifest",
"getControlPageApiMethods",
"getControlPageContract",
"getControlView",
"getPanelApi",
"loadControlPageSessionState",
"readControlPageStatus",
"refreshControlPage",
"renderControlPage",
];
}
export function createControlPageBrowserApiManifest() {
const schema = createControlPageBrowserApiSchema();
const contract = createIniPanelShellControlPageContract();
return {
apiName: schema.apiName,
manifestVersion: schema.manifestVersion,
methods: createControlPageBrowserApiMethods(),
contract,
readonlyStatusApiManifest: contract.readonlyStatusApiManifest,
};
}
export function createIniPanelShellIntegrationSchema() {
return {
apiName: "ini-panel-shell-integration",
manifestVersion: 1,
fields: [
"apiName",
"manifestVersion",
"pages",
"launcherLinks",
"launchApiSchema",
"launchApiManifest",
"controlPageApiSchema",
"controlPageApiManifest",
"compatibility",
"persistenceCapabilities",
],
};
}
export function isSupportedIniPanelShellIntegrationManifest(
manifest,
schema = createIniPanelShellIntegrationSchema(),
) {
if (!manifest || typeof manifest !== "object") {
return false;
}
if (manifest.apiName !== schema.apiName || manifest.manifestVersion !== schema.manifestVersion) {
return false;
}
return schema.fields.every((fieldName) => Object.hasOwn(manifest, fieldName));
}
export function createIniPanelShellIntegrationManifest(entries = getVisibleIniPanelEntries()) {
const schema = createIniPanelShellIntegrationSchema();
const launchApiSchema = createIniPanelLaunchApiSchema();
const launchApiManifest = createIniPanelLaunchApiManifest(entries);
const controlPageApiSchema = createControlPageBrowserApiSchema();
const controlPageApiManifest = createControlPageBrowserApiManifest();
return {
apiName: schema.apiName,
manifestVersion: schema.manifestVersion,
pages: entries.map(({ id, href, title }) => ({
id,
href,
title,
})),
launcherLinks: createIniPanelLauncherLinkViewModel(entries),
launchApiSchema,
launchApiManifest,
controlPageApiSchema,
controlPageApiManifest,
compatibility: {
launchApiManifest: isSupportedIniPanelLaunchApiManifest(launchApiManifest, launchApiSchema),
controlPageApiManifest: isSupportedIniPanelControlPageApiManifest(
controlPageApiManifest,
controlPageApiSchema,
),
},
persistenceCapabilities: launchApiManifest.persistenceCapabilities,
};
}
export function createIniPanelShellIntegrationReadiness(
manifest = createIniPanelShellIntegrationManifest(),
) {
const integrationManifestSupported = isSupportedIniPanelShellIntegrationManifest(manifest);
const launchApiManifestSupported = integrationManifestSupported
&& isSupportedIniPanelLaunchApiManifest(manifest.launchApiManifest, manifest.launchApiSchema);
const controlPageApiManifestSupported = integrationManifestSupported
&& isSupportedIniPanelControlPageApiManifest(
manifest.controlPageApiManifest,
manifest.controlPageApiSchema,
);
const compatibility = integrationManifestSupported && manifest.compatibility
? {
launchApiManifest: Boolean(manifest.compatibility.launchApiManifest),
controlPageApiManifest: Boolean(manifest.compatibility.controlPageApiManifest),
}
: {
launchApiManifest: false,
controlPageApiManifest: false,
};
const checks = {
integrationManifestSupported,
launchApiManifestSupported,
controlPageApiManifestSupported,
launchCompatibility: compatibility.launchApiManifest,
controlPageCompatibility: compatibility.controlPageApiManifest,
};
const missing = Object.entries(checks)
.filter(([, passed]) => !passed)
.map(([name]) => name);
const ready = missing.length === 0;
return {
phase: ready ? "ready" : "blocked",
ready,
checks,
missing,
counts: {
pages: Array.isArray(manifest?.pages) ? manifest.pages.length : 0,
launcherLinks: Array.isArray(manifest?.launcherLinks) ? manifest.launcherLinks.length : 0,
launchMethods: Array.isArray(manifest?.launchApiManifest?.methods)
? manifest.launchApiManifest.methods.length
: 0,
controlPageMethods: Array.isArray(manifest?.controlPageApiManifest?.methods)
? manifest.controlPageApiManifest.methods.length
: 0,
persistenceCapabilities: Array.isArray(manifest?.persistenceCapabilities)
? manifest.persistenceCapabilities.length
: 0,
},
apiNames: {
integration: manifest?.apiName ?? null,
launch: manifest?.launchApiManifest?.apiName ?? null,
controlPage: manifest?.controlPageApiManifest?.apiName ?? null,
},
};
}
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 createIniPanelShellSessionHandoffSummarySchema() {
return {
apiName: "ini-panel-shell-session-handoff-summary",
summaryVersion: 1,
fields: [
"apiName",
"summaryVersion",
"phase",
"ready",
"targetPage",
"readiness",
"persistenceCapabilities",
"readonlyStatusFields",
"handoffMethods",
"blockingReasons",
],
};
}
export function isSupportedIniPanelShellSessionHandoffSummary(
summary,
schema = createIniPanelShellSessionHandoffSummarySchema(),
) {
if (!summary || typeof summary !== "object") {
return false;
}
if (summary.apiName !== schema.apiName || summary.summaryVersion !== schema.summaryVersion) {
return false;
}
return schema.fields.every((fieldName) => Object.hasOwn(summary, fieldName));
}
export function createIniPanelShellSessionHandoffSummary({
manifest = createIniPanelShellIntegrationManifest(),
readiness = createIniPanelShellIntegrationReadiness(manifest),
workflowPlan = createIniPanelShellIntegrationWorkflowPlan({ manifest, readiness }),
} = {}) {
const schema = createIniPanelShellSessionHandoffSummarySchema();
const persistenceCapabilities = Array.isArray(manifest?.persistenceCapabilities)
? manifest.persistenceCapabilities.map(({ id, label, scope }) => ({ id, label, scope }))
: [];
const readonlyStatusFields = Array.isArray(workflowPlan?.readonlyStatusHandoff?.expectedStatusFields)
? [...workflowPlan.readonlyStatusHandoff.expectedStatusFields]
: [];
return {
apiName: schema.apiName,
summaryVersion: schema.summaryVersion,
phase: workflowPlan.ready ? "ready" : "blocked",
ready: Boolean(workflowPlan.ready),
targetPage: workflowPlan.targetPage,
readiness: {
phase: readiness.phase,
missing: [...readiness.missing],
counts: { ...readiness.counts },
apiNames: { ...readiness.apiNames },
},
persistenceCapabilities,
readonlyStatusFields,
handoffMethods: {
loadSessionState: workflowPlan.readonlyStatusHandoff.loadSessionStateMethod,
readStatus: workflowPlan.readonlyStatusHandoff.readStatusMethod,
},
blockingReasons: [...workflowPlan.missing],
};
}
export function createIniPanelShellSessionHandoffCompatibilityReport(
summary = createIniPanelShellSessionHandoffSummary(),
schema = createIniPanelShellSessionHandoffSummarySchema(),
) {
const summaryObject = summary && typeof summary === "object" ? summary : {};
const missingFields = schema.fields.filter((fieldName) => !Object.hasOwn(summaryObject, fieldName));
const mismatchFields = [
...(summaryObject.apiName === schema.apiName ? [] : ["apiName"]),
...(summaryObject.summaryVersion === schema.summaryVersion ? [] : ["summaryVersion"]),
];
const summarySupported = missingFields.length === 0 && mismatchFields.length === 0;
const blockingReasons = Array.isArray(summaryObject.blockingReasons)
? [...summaryObject.blockingReasons]
: [];
const handoffReady = summarySupported && summaryObject.ready === true && blockingReasons.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-compatibility-report",
reportVersion: 1,
phase: handoffReady ? "ready" : "blocked",
compatible: handoffReady,
summarySupported,
handoffReady,
schema: {
apiName: schema.apiName,
summaryVersion: schema.summaryVersion,
fields: [...schema.fields],
},
summary: {
apiName: summaryObject.apiName ?? null,
summaryVersion: summaryObject.summaryVersion ?? null,
phase: summaryObject.phase ?? null,
ready: summaryObject.ready ?? false,
},
missingFields,
mismatchFields,
blockingReasons,
};
}
export function createIniPanelShellSessionHandoffDisplayViewModel(
report = createIniPanelShellSessionHandoffCompatibilityReport(),
) {
const missingFields = Array.isArray(report?.missingFields) ? report.missingFields : [];
const mismatchFields = Array.isArray(report?.mismatchFields) ? report.mismatchFields : [];
const blockingReasons = Array.isArray(report?.blockingReasons) ? report.blockingReasons : [];
const problems = [...missingFields, ...mismatchFields, ...blockingReasons];
return {
phase: report?.phase === "ready" ? "ready" : "blocked",
title: "Shell handoff compatibility",
statusText: report?.compatible ? "Ready" : "Blocked",
detailText: problems.length === 0 ? "No blocking reasons" : problems.join(", "),
rows: [
{
id: "summary-supported",
label: "Summary schema",
value: report?.summarySupported ? "supported" : "blocked",
},
{
id: "handoff-ready",
label: "Handoff readiness",
value: report?.handoffReady ? "ready" : "blocked",
},
{
id: "missing-fields",
label: "Missing fields",
value: missingFields.length === 0 ? "none" : missingFields.join(", "),
},
{
id: "mismatch-fields",
label: "Mismatched fields",
value: mismatchFields.length === 0 ? "none" : mismatchFields.join(", "),
},
{
id: "blocking-reasons",
label: "Blocking reasons",
value: blockingReasons.length === 0 ? "none" : blockingReasons.join(", "),
},
],
};
}
export function createIniPanelShellSessionHandoffSnapshot({
readiness = createIniPanelShellIntegrationReadiness(),
summary = createIniPanelShellSessionHandoffSummary(),
compatibilityReport = createIniPanelShellSessionHandoffCompatibilityReport(summary),
displayViewModel = createIniPanelShellSessionHandoffDisplayViewModel(compatibilityReport),
} = {}) {
return {
apiName: "ini-panel-shell-session-handoff-snapshot",
snapshotVersion: 1,
phase: compatibilityReport.phase,
ready: Boolean(compatibilityReport.compatible),
readiness,
summary,
compatibilityReport,
displayViewModel,
};
}
export function createIniPanelShellSessionHandoffRenderState(
snapshot = createIniPanelShellSessionHandoffSnapshot(),
) {
const displayViewModel = snapshot?.displayViewModel && typeof snapshot.displayViewModel === "object"
? snapshot.displayViewModel
: createIniPanelShellSessionHandoffDisplayViewModel(snapshot?.compatibilityReport);
const rows = Array.isArray(displayViewModel.rows)
? displayViewModel.rows.map(({ id, label, value }) => ({
id,
label,
value: value == null ? "" : String(value),
}))
: [];
const ready = snapshot?.ready === true && displayViewModel.phase === "ready";
const statusText = displayViewModel.statusText ?? (ready ? "Ready" : "Blocked");
const detailText = displayViewModel.detailText ?? "No blocking reasons";
return {
apiName: "ini-panel-shell-session-handoff-render-state",
renderStateVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
title: displayViewModel.title ?? "Shell handoff compatibility",
statusText,
detailText,
statusLine: `${statusText}: ${detailText}`,
rows,
dataset: {
handoffPhase: ready ? "ready" : "blocked",
handoffReady: ready ? "true" : "false",
},
};
}
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 createIniPanelShellSessionHandoffChecklist({
actionPlan = createIniPanelShellSessionHandoffActionPlan(),
renderState = createIniPanelShellSessionHandoffRenderState(),
} = {}) {
const displayRows = Array.isArray(actionPlan?.displayRows)
? actionPlan.displayRows.map(({ id, label, value }) => ({ id, label, value }))
: [];
const blockingReasons = Array.isArray(actionPlan?.blockingReasons)
? [...actionPlan.blockingReasons]
: [];
const targetPageReady = Boolean(actionPlan?.targetPage?.href);
const loadMethodReady = Boolean(actionPlan?.requiredMethods?.loadSessionState);
const readMethodReady = Boolean(actionPlan?.requiredMethods?.readStatus);
const ready = actionPlan?.ready === true
&& renderState?.ready === true
&& targetPageReady
&& loadMethodReady
&& readMethodReady
&& blockingReasons.length === 0;
const checks = [
{
id: "render-state",
label: "Render state",
status: renderState?.ready ? "pass" : "blocked",
value: renderState?.statusLine ?? "missing",
},
{
id: "target-page",
label: "Control target",
status: targetPageReady ? "pass" : "blocked",
value: actionPlan?.targetPage?.href ?? "missing",
},
{
id: "load-session-method",
label: "Load session method",
status: loadMethodReady ? "pass" : "blocked",
value: actionPlan?.requiredMethods?.loadSessionState ?? "missing",
},
{
id: "read-status-method",
label: "Read status method",
status: readMethodReady ? "pass" : "blocked",
value: actionPlan?.requiredMethods?.readStatus ?? "missing",
},
{
id: "blocking-reasons",
label: "Blocking reasons",
status: blockingReasons.length === 0 ? "pass" : "blocked",
value: blockingReasons.length === 0 ? "none" : blockingReasons.join(", "),
},
];
return {
apiName: "ini-panel-shell-session-handoff-checklist",
checklistVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
statusLine: actionPlan?.statusLine ?? renderState?.statusLine ?? "Blocked: No handoff action plan",
checks,
displayRows,
nextStepIds: Array.isArray(actionPlan?.nextSteps)
? actionPlan.nextSteps.map(({ id }) => id)
: [],
blockingReasons,
};
}
export function createIniPanelShellSessionHandoffPackage({
shellIntegrationManifest = createIniPanelShellIntegrationManifest(),
launchApiManifest = shellIntegrationManifest.launchApiManifest ?? createIniPanelLaunchApiManifest(),
controlPageApiManifest = shellIntegrationManifest.controlPageApiManifest ?? createControlPageBrowserApiManifest(),
snapshot = createIniPanelShellSessionHandoffSnapshot(),
renderState = createIniPanelShellSessionHandoffRenderState(snapshot),
actionPlan = createIniPanelShellSessionHandoffActionPlan({ snapshot, renderState }),
checklist = createIniPanelShellSessionHandoffChecklist({ actionPlan, renderState }),
} = {}) {
return {
apiName: "ini-panel-shell-session-handoff-package",
packageVersion: 1,
phase: checklist.phase,
ready: Boolean(checklist.ready),
statusLine: checklist.statusLine,
manifests: {
shellIntegration: shellIntegrationManifest,
launchApi: launchApiManifest,
controlPageApi: controlPageApiManifest,
launchMethods: Array.isArray(launchApiManifest?.methods) ? [...launchApiManifest.methods] : [],
controlPageMethods: Array.isArray(controlPageApiManifest?.methods) ? [...controlPageApiManifest.methods] : [],
},
snapshot,
renderState,
actionPlan,
checklist,
};
}
export function createIniPanelShellSessionHandoffPackageSchema() {
return {
apiName: "ini-panel-shell-session-handoff-package",
packageVersion: 1,
fields: [
"apiName",
"packageVersion",
"phase",
"ready",
"statusLine",
"manifests",
"snapshot",
"renderState",
"actionPlan",
"checklist",
],
manifestFields: [
"shellIntegration",
"launchApi",
"controlPageApi",
"launchMethods",
"controlPageMethods",
],
};
}
export function isSupportedIniPanelShellSessionHandoffPackage(
handoffPackage,
schema = createIniPanelShellSessionHandoffPackageSchema(),
) {
if (!handoffPackage || typeof handoffPackage !== "object") {
return false;
}
if (handoffPackage.apiName !== schema.apiName || handoffPackage.packageVersion !== schema.packageVersion) {
return false;
}
if (!schema.fields.every((fieldName) => Object.hasOwn(handoffPackage, fieldName))) {
return false;
}
const manifests = handoffPackage.manifests;
if (!manifests || typeof manifests !== "object") {
return false;
}
return schema.manifestFields.every((fieldName) => Object.hasOwn(manifests, fieldName));
}
export function createIniPanelShellSessionHandoffPackageReport(
handoffPackage = createIniPanelShellSessionHandoffPackage(),
schema = createIniPanelShellSessionHandoffPackageSchema(),
) {
const packageObject = handoffPackage && typeof handoffPackage === "object" ? handoffPackage : {};
const manifests = packageObject.manifests && typeof packageObject.manifests === "object"
? packageObject.manifests
: {};
const missingPackageFields = schema.fields.filter((fieldName) => !Object.hasOwn(packageObject, fieldName));
const missingManifestFields = schema.manifestFields.filter((fieldName) => !Object.hasOwn(manifests, fieldName));
const mismatchFields = [
...(packageObject.apiName === schema.apiName ? [] : ["apiName"]),
...(packageObject.packageVersion === schema.packageVersion ? [] : ["packageVersion"]),
];
const blockingReasons = Array.isArray(packageObject?.checklist?.blockingReasons)
? [...packageObject.checklist.blockingReasons]
: [];
const packageSupported = missingPackageFields.length === 0
&& missingManifestFields.length === 0
&& mismatchFields.length === 0;
const ready = packageSupported && packageObject.ready === true && blockingReasons.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-package-report",
reportVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
packageSupported,
packageReady: ready,
statusLine: packageObject.statusLine ?? (ready ? "Ready: No blocking reasons" : "Blocked: Package unsupported"),
schema: {
apiName: schema.apiName,
packageVersion: schema.packageVersion,
fields: [...schema.fields],
manifestFields: [...schema.manifestFields],
},
package: {
apiName: packageObject.apiName ?? null,
packageVersion: packageObject.packageVersion ?? null,
phase: packageObject.phase ?? null,
ready: packageObject.ready ?? false,
},
missingPackageFields,
missingManifestFields,
mismatchFields,
blockingReasons,
rows: [
{
id: "package-supported",
label: "Package schema",
value: packageSupported ? "supported" : "blocked",
},
{
id: "package-ready",
label: "Package readiness",
value: ready ? "ready" : "blocked",
},
{
id: "missing-package-fields",
label: "Missing package fields",
value: missingPackageFields.length === 0 ? "none" : missingPackageFields.join(", "),
},
{
id: "missing-manifest-fields",
label: "Missing manifest fields",
value: missingManifestFields.length === 0 ? "none" : missingManifestFields.join(", "),
},
{
id: "mismatch-fields",
label: "Mismatched fields",
value: mismatchFields.length === 0 ? "none" : mismatchFields.join(", "),
},
{
id: "blocking-reasons",
label: "Blocking reasons",
value: blockingReasons.length === 0 ? "none" : blockingReasons.join(", "),
},
],
};
}
export function createIniPanelShellSessionHandoffPackageDisplayViewModel(
report = createIniPanelShellSessionHandoffPackageReport(),
) {
const missingPackageFields = Array.isArray(report?.missingPackageFields) ? report.missingPackageFields : [];
const missingManifestFields = Array.isArray(report?.missingManifestFields) ? report.missingManifestFields : [];
const mismatchFields = Array.isArray(report?.mismatchFields) ? report.mismatchFields : [];
const blockingReasons = Array.isArray(report?.blockingReasons) ? report.blockingReasons : [];
const problems = [
...missingPackageFields,
...missingManifestFields,
...mismatchFields,
...blockingReasons,
];
return {
phase: report?.phase === "ready" ? "ready" : "blocked",
title: "Shell handoff package",
statusText: report?.ready ? "Ready" : "Blocked",
detailText: problems.length === 0 ? "No blocking reasons" : problems.join(", "),
rows: Array.isArray(report?.rows)
? report.rows.map(({ id, label, value }) => ({
id,
label,
value,
}))
: [],
};
}
export function createIniPanelShellSessionHandoffPackageRenderState(
displayViewModel = createIniPanelShellSessionHandoffPackageDisplayViewModel(),
) {
const rows = Array.isArray(displayViewModel?.rows)
? displayViewModel.rows.map(({ id, label, value }) => ({
id,
label,
value: value == null ? "" : String(value),
}))
: [];
const ready = displayViewModel?.phase === "ready" && displayViewModel?.statusText === "Ready";
const statusText = displayViewModel?.statusText ?? (ready ? "Ready" : "Blocked");
const detailText = displayViewModel?.detailText ?? "No blocking reasons";
return {
apiName: "ini-panel-shell-session-handoff-package-render-state",
renderStateVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
title: displayViewModel?.title ?? "Shell handoff package",
statusText,
detailText,
statusLine: `${statusText}: ${detailText}`,
rows,
dataset: {
handoffPhase: ready ? "ready" : "blocked",
handoffReady: ready ? "true" : "false",
handoffScope: "package",
},
};
}
export function createIniPanelShellSessionHandoffPackageMountState({
handoffPackage = createIniPanelShellSessionHandoffPackage(),
packageRenderState = createIniPanelShellSessionHandoffPackageRenderState(),
sessionLoadState = createControlPageSessionLoadState({ phase: "waiting-panel" }),
} = {}) {
const actionPlan = handoffPackage?.actionPlan && typeof handoffPackage.actionPlan === "object"
? handoffPackage.actionPlan
: {};
const requiredMethods = actionPlan.requiredMethods && typeof actionPlan.requiredMethods === "object"
? actionPlan.requiredMethods
: {};
const targetPage = actionPlan.targetPage ?? null;
const blockingReasons = [
...(Array.isArray(actionPlan.blockingReasons) ? actionPlan.blockingReasons : []),
...(sessionLoadState?.phase === "error" ? ["controlPageSessionError"] : []),
];
const packageReady = handoffPackage?.ready === true && packageRenderState?.ready === true;
const targetReady = Boolean(targetPage?.href);
const methodsReady = Boolean(requiredMethods.loadSessionState && requiredMethods.readStatus);
const sessionReady = sessionLoadState?.phase === "ready";
const ready = packageReady && targetReady && methodsReady && sessionReady && blockingReasons.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-package-mount-state",
mountStateVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
packageReady,
targetReady,
methodsReady,
sessionReady,
statusLine: ready
? "Ready: Control page session mounted"
: `Blocked: ${blockingReasons.length === 0 ? sessionLoadState?.phase ?? "waiting-panel" : blockingReasons.join(", ")}`,
packageRenderState,
targetPage,
requiredMethods: {
loadSessionState: requiredMethods.loadSessionState ?? null,
readStatus: requiredMethods.readStatus ?? null,
},
sessionLoadState,
blockingReasons,
checks: [
{
id: "package-render-state",
label: "Package render state",
status: packageReady ? "pass" : "blocked",
value: packageRenderState?.statusLine ?? "missing",
},
{
id: "target-page",
label: "Control target",
status: targetReady ? "pass" : "blocked",
value: targetPage?.href ?? "missing",
},
{
id: "required-methods",
label: "Readonly methods",
status: methodsReady ? "pass" : "blocked",
value: methodsReady
? `${requiredMethods.loadSessionState}, ${requiredMethods.readStatus}`
: "missing",
},
{
id: "session-load-state",
label: "Session load state",
status: sessionReady ? "pass" : "blocked",
value: sessionLoadState?.phase ?? "missing",
},
{
id: "blocking-reasons",
label: "Blocking reasons",
status: blockingReasons.length === 0 ? "pass" : "blocked",
value: blockingReasons.length === 0 ? "none" : blockingReasons.join(", "),
},
],
};
}
export function createIniPanelShellSessionHandoffPackageMountDisplayViewModel(
mountState = createIniPanelShellSessionHandoffPackageMountState(),
) {
const checks = Array.isArray(mountState?.checks) ? mountState.checks : [];
const blockingChecks = checks.filter(({ status }) => status !== "pass");
const blockingReasons = Array.isArray(mountState?.blockingReasons) ? mountState.blockingReasons : [];
const problems = [
...blockingReasons,
...blockingChecks.map(({ id }) => id),
];
return {
phase: mountState?.phase === "ready" ? "ready" : "blocked",
title: "Shell handoff mount",
statusText: mountState?.ready ? "Ready" : "Blocked",
detailText: problems.length === 0 ? "No blocking reasons" : problems.join(", "),
rows: checks.map(({ id, label, status, value }) => ({
id,
label,
value,
status,
})),
};
}
export function createIniPanelShellSessionHandoffPackageMountRenderState(
displayViewModel = createIniPanelShellSessionHandoffPackageMountDisplayViewModel(),
) {
const rows = Array.isArray(displayViewModel?.rows)
? displayViewModel.rows.map(({ id, label, value, status }) => ({
id,
label,
value: value == null ? "" : String(value),
status: status ?? "blocked",
}))
: [];
const ready = displayViewModel?.phase === "ready" && displayViewModel?.statusText === "Ready";
const statusText = displayViewModel?.statusText ?? (ready ? "Ready" : "Blocked");
const detailText = displayViewModel?.detailText ?? "No blocking reasons";
return {
apiName: "ini-panel-shell-session-handoff-package-mount-render-state",
renderStateVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
title: displayViewModel?.title ?? "Shell handoff mount",
statusText,
detailText,
statusLine: `${statusText}: ${detailText}`,
rows,
dataset: {
handoffPhase: ready ? "ready" : "blocked",
handoffReady: ready ? "true" : "false",
handoffScope: "mount",
},
};
}
export function createIniPanelShellSessionHandoffMountDomContract({
rowDatasetPrefix = "handoffMount",
selectors = {},
} = {}) {
const mountSelector = selectors.mount ?? "[data-handoff-mount]";
const statusSelector = selectors.status ?? "[data-handoff-mount-status]";
const rowsSelector = selectors.rows ?? "[data-handoff-mount-rows]";
return {
apiName: "ini-panel-shell-session-handoff-mount-dom-contract",
contractVersion: 1,
requiredNodeIds: ["mount", "status", "rows"],
selectors: {
mount: mountSelector,
status: statusSelector,
rows: rowsSelector,
},
mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"],
rowDatasetPrefix,
rowDatasetKeys: {
term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`],
detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`],
},
renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"],
};
}
export function createIniPanelShellSessionHandoffMountDomReadiness({
rowDatasetPrefix = "handoffMount",
contract = createIniPanelShellSessionHandoffMountDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
const checks = {
document: Boolean(documentRef?.createElement),
mount: Boolean(mountNode),
status: Boolean(statusNode),
rows: Boolean(rowsNode),
rowDatasetPrefix: Boolean(contract.rowDatasetPrefix),
};
const missing = Object.entries(checks)
.filter(([, passed]) => !passed)
.map(([name]) => name);
const ready = missing.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-mount-dom-readiness",
readinessVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
missing,
checks,
contract,
};
}
export function renderIniPanelShellSessionHandoffMountState(
renderState = createIniPanelShellSessionHandoffPackageMountRenderState(),
{
rowDatasetPrefix = "handoffMount",
contract = createIniPanelShellSessionHandoffMountDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {},
) {
const readiness = createIniPanelShellSessionHandoffMountDomReadiness({
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
if (!readiness.ready) {
throw new Error(
`Shell handoff mount DOM is not ready: ${readiness.missing.join(", ")}`,
);
}
mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked";
mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false";
mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope ?? "mount";
statusNode.textContent = renderState?.statusLine ?? "Blocked: No mount render state";
rowsNode.replaceChildren();
const rows = Array.isArray(renderState?.rows) ? renderState.rows : [];
const effectiveRowDatasetPrefix = contract.rowDatasetPrefix;
for (const row of rows) {
const term = documentRef.createElement("dt");
term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id;
term.dataset[`${effectiveRowDatasetPrefix}Status`] = row.status;
term.textContent = row.label;
const detail = documentRef.createElement("dd");
detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id;
detail.dataset[`${effectiveRowDatasetPrefix}Status`] = row.status;
detail.textContent = row.value;
rowsNode.append(term, detail);
}
return {
rendered: true,
statusLine: statusNode.textContent,
rowCount: rows.length,
rowIds: rows.map(({ id }) => id),
dataset: {
handoffPhase: mountNode.dataset.handoffPhase,
handoffReady: mountNode.dataset.handoffReady,
handoffScope: mountNode.dataset.handoffScope,
},
};
}
export function mountIniPanelShellSessionHandoff({
renderState = createIniPanelShellSessionHandoffPackageMountRenderState(),
rowDatasetPrefix = "handoffMount",
contract = createIniPanelShellSessionHandoffMountDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
const domReadiness = createIniPanelShellSessionHandoffMountDomReadiness({
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
if (!domReadiness.ready) {
return {
apiName: "ini-panel-shell-session-handoff-mount-workflow",
workflowVersion: 1,
phase: "blocked",
ready: false,
statusLine: `Blocked: ${domReadiness.missing.join(", ")}`,
contract,
domReadiness,
renderState,
renderResult: null,
error: null,
};
}
try {
const renderResult = renderIniPanelShellSessionHandoffMountState(renderState, {
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
return {
apiName: "ini-panel-shell-session-handoff-mount-workflow",
workflowVersion: 1,
phase: renderState?.ready ? "ready" : "blocked",
ready: renderState?.ready === true,
statusLine: renderResult.statusLine,
contract,
domReadiness,
renderState,
renderResult,
error: null,
};
} catch (error) {
return {
apiName: "ini-panel-shell-session-handoff-mount-workflow",
workflowVersion: 1,
phase: "blocked",
ready: false,
statusLine: `Blocked: ${error?.message ?? "mount workflow error"}`,
contract,
domReadiness,
renderState,
renderResult: null,
error: error?.message ?? String(error),
};
}
}
export function mountIniPanelShellSessionHandoffFromControlPage({
controlPageApi = null,
sessionLoadState = null,
loadSessionStateMethod = "loadControlPageSessionState",
handoffPackage = createIniPanelShellSessionHandoffPackage(),
packageRenderState = createIniPanelShellSessionHandoffPackageRenderState(),
rowDatasetPrefix = "handoffMount",
contract = createIniPanelShellSessionHandoffMountDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
let observedSessionLoadState = sessionLoadState;
let sessionLoadError = null;
if (!observedSessionLoadState) {
const loadSessionState = controlPageApi?.[loadSessionStateMethod];
if (typeof loadSessionState === "function") {
try {
observedSessionLoadState = loadSessionState.call(controlPageApi);
} catch (error) {
sessionLoadError = error?.message ?? String(error);
observedSessionLoadState = createControlPageSessionLoadState({
phase: "error",
error: sessionLoadError,
});
}
} else {
observedSessionLoadState = createControlPageSessionLoadState({ phase: "waiting-api" });
}
}
const mountState = createIniPanelShellSessionHandoffPackageMountState({
handoffPackage,
packageRenderState,
sessionLoadState: observedSessionLoadState,
});
const mountDisplayViewModel = createIniPanelShellSessionHandoffPackageMountDisplayViewModel(mountState);
const mountRenderState = createIniPanelShellSessionHandoffPackageMountRenderState(mountDisplayViewModel);
const mountWorkflow = mountIniPanelShellSessionHandoff({
renderState: mountRenderState,
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
return {
apiName: "ini-panel-shell-session-handoff-control-page-mount-workflow",
workflowVersion: 1,
phase: mountWorkflow.phase,
ready: mountWorkflow.ready,
statusLine: mountWorkflow.statusLine,
loadSessionStateMethod,
sessionLoadState: observedSessionLoadState,
sessionLoadError,
mountState,
mountDisplayViewModel,
mountRenderState,
mountWorkflow,
};
}
export function readIniPanelShellSessionHandoffStatusFromControlPage({
controlPageApi = null,
readonlyStatus = null,
readStatusMethod = "readControlPageStatus",
handoffSummary = createIniPanelShellSessionHandoffSummary(),
expectedStatusFields = handoffSummary?.readonlyStatusFields ?? [],
} = {}) {
let observedStatus = readonlyStatus;
let readError = null;
if (!observedStatus) {
const readStatus = controlPageApi?.[readStatusMethod];
if (typeof readStatus === "function") {
try {
observedStatus = readStatus.call(controlPageApi);
} catch (error) {
readError = error?.message ?? String(error);
observedStatus = null;
}
} else {
readError = `Missing ${readStatusMethod}`;
observedStatus = null;
}
}
const statusObject = observedStatus && typeof observedStatus === "object" ? observedStatus : {};
const expectedFields = Array.isArray(expectedStatusFields) ? [...expectedStatusFields] : [];
const presentStatusFields = expectedFields.filter((fieldName) => Object.hasOwn(statusObject, fieldName));
const missingStatusFields = expectedFields.filter((fieldName) => !Object.hasOwn(statusObject, fieldName));
const ready = !readError && missingStatusFields.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-readonly-status-result",
resultVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
statusLine: ready
? "Ready: readonly status fields available"
: `Blocked: ${readError ?? missingStatusFields.join(", ")}`,
readStatusMethod,
expectedStatusFields: expectedFields,
presentStatusFields,
missingStatusFields,
readonlyStatus: observedStatus,
readError,
};
}
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 createIniPanelShellSessionHandoffWorkflowDisplayViewModel(
workflowSummary = createIniPanelShellSessionHandoffWorkflowSummary(),
) {
const blockingReasons = Array.isArray(workflowSummary?.blockingReasons)
? workflowSummary.blockingReasons
: [];
return {
phase: workflowSummary?.phase === "ready" ? "ready" : "blocked",
title: "Shell handoff workflow",
statusText: workflowSummary?.ready ? "Ready" : "Blocked",
detailText: blockingReasons.length > 0 ? blockingReasons.join(", ") : "No blocking reasons",
rows: Array.isArray(workflowSummary?.rows)
? workflowSummary.rows.map(({ id, label, value }) => ({
id,
label,
value,
}))
: [],
};
}
export function createIniPanelShellSessionHandoffWorkflowRenderState(
displayViewModel = createIniPanelShellSessionHandoffWorkflowDisplayViewModel(),
) {
const rows = Array.isArray(displayViewModel?.rows)
? displayViewModel.rows.map(({ id, label, value }) => ({
id,
label,
value: value == null ? "" : String(value),
}))
: [];
const ready = displayViewModel?.phase === "ready" && displayViewModel?.statusText === "Ready";
const statusText = displayViewModel?.statusText ?? (ready ? "Ready" : "Blocked");
const detailText = displayViewModel?.detailText ?? "No blocking reasons";
return {
apiName: "ini-panel-shell-session-handoff-workflow-render-state",
renderStateVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
title: displayViewModel?.title ?? "Shell handoff workflow",
statusText,
detailText,
statusLine: `${statusText}: ${detailText}`,
rows,
dataset: {
handoffPhase: ready ? "ready" : "blocked",
handoffReady: ready ? "true" : "false",
handoffScope: "workflow",
},
};
}
export function createIniPanelShellSessionHandoffWorkflowDomContract({
rowDatasetPrefix = "handoffWorkflow",
selectors = {},
} = {}) {
const mountSelector = selectors.mount ?? "[data-handoff-workflow]";
const statusSelector = selectors.status ?? "[data-handoff-workflow-status]";
const rowsSelector = selectors.rows ?? "[data-handoff-workflow-rows]";
return {
apiName: "ini-panel-shell-session-handoff-workflow-dom-contract",
contractVersion: 1,
requiredNodeIds: ["mount", "status", "rows"],
selectors: {
mount: mountSelector,
status: statusSelector,
rows: rowsSelector,
},
mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"],
rowDatasetPrefix,
rowDatasetKeys: {
term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`],
detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`],
},
renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"],
};
}
export function createIniPanelShellWorkflowOverviewContract() {
return {
pageHref: "./workflow-overview.html",
entryId: "workflow-overview",
apiName: "linuxCncIniPanelWorkflowOverviewApi",
methodNames: [
"getWorkflowOverviewRenderState",
"getWorkflowOverviewDisplayViewModel",
"getWorkflowOverviewSummary",
"getWorkflowOverviewDomContract",
"getWorkflowOverviewDomReadiness",
"renderWorkflowOverview",
"mountWorkflowOverview",
"createWorkflowOverviewRenderState",
],
};
}
export function createIniPanelShellWorkflowOverviewReadiness({
overviewApi = null,
contract = createIniPanelShellWorkflowOverviewContract(),
} = {}) {
const methodNames = Array.isArray(contract?.methodNames) ? contract.methodNames : [];
const missing = methodNames.filter((methodName) => typeof overviewApi?.[methodName] !== "function");
const ready = missing.length === 0;
return {
apiName: "ini-panel-shell-workflow-overview-readiness",
readinessVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
missing,
contract,
};
}
export function createIniPanelShellSessionHandoffWorkflowDomReadiness({
rowDatasetPrefix = "handoffWorkflow",
contract = createIniPanelShellSessionHandoffWorkflowDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
const checks = {
document: Boolean(documentRef?.createElement),
mount: Boolean(mountNode),
status: Boolean(statusNode),
rows: Boolean(rowsNode),
rowDatasetPrefix: Boolean(contract.rowDatasetPrefix),
};
const missing = Object.entries(checks)
.filter(([, passed]) => !passed)
.map(([name]) => name);
const ready = missing.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-workflow-dom-readiness",
readinessVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
missing,
checks,
contract,
};
}
export function renderIniPanelShellSessionHandoffWorkflowState(
renderState = createIniPanelShellSessionHandoffWorkflowRenderState(),
{
rowDatasetPrefix = "handoffWorkflow",
contract = createIniPanelShellSessionHandoffWorkflowDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {},
) {
const readiness = createIniPanelShellSessionHandoffWorkflowDomReadiness({
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
if (!readiness.ready) {
throw new Error(
`Shell handoff workflow DOM is not ready: ${readiness.missing.join(", ")}`,
);
}
mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked";
mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false";
mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope ?? "workflow";
statusNode.textContent = renderState?.statusLine ?? "Blocked: No workflow render state";
rowsNode.replaceChildren();
const rows = Array.isArray(renderState?.rows) ? renderState.rows : [];
const effectiveRowDatasetPrefix = contract.rowDatasetPrefix;
for (const row of rows) {
const term = documentRef.createElement("dt");
term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id;
term.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked";
term.textContent = row.label;
const detail = documentRef.createElement("dd");
detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id;
detail.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked";
detail.textContent = row.value;
rowsNode.append(term, detail);
}
return {
rendered: true,
statusLine: statusNode.textContent,
rowCount: rows.length,
rowIds: rows.map(({ id }) => id),
dataset: {
handoffPhase: mountNode.dataset.handoffPhase,
handoffReady: mountNode.dataset.handoffReady,
handoffScope: mountNode.dataset.handoffScope,
},
};
}
export function mountIniPanelShellSessionHandoffWorkflow({
renderState = createIniPanelShellSessionHandoffWorkflowRenderState(),
rowDatasetPrefix = "handoffWorkflow",
contract = createIniPanelShellSessionHandoffWorkflowDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
const domReadiness = createIniPanelShellSessionHandoffWorkflowDomReadiness({
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
if (!domReadiness.ready) {
return {
apiName: "ini-panel-shell-session-handoff-workflow-dom-mount",
workflowVersion: 1,
phase: "blocked",
ready: false,
statusLine: `Blocked: ${domReadiness.missing.join(", ")}`,
contract,
domReadiness,
renderState,
renderResult: null,
error: null,
};
}
try {
const renderResult = renderIniPanelShellSessionHandoffWorkflowState(renderState, {
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
return {
apiName: "ini-panel-shell-session-handoff-workflow-dom-mount",
workflowVersion: 1,
phase: renderState?.phase === "ready" ? "ready" : "blocked",
ready: renderState?.ready === true,
statusLine: renderResult.statusLine,
contract,
domReadiness,
renderState,
renderResult,
error: null,
};
} catch (error) {
return {
apiName: "ini-panel-shell-session-handoff-workflow-dom-mount",
workflowVersion: 1,
phase: "blocked",
ready: false,
statusLine: `Blocked: ${error?.message ?? "workflow DOM mount error"}`,
contract,
domReadiness,
renderState,
renderResult: null,
error: error?.message ?? String(error),
};
}
}
export function mountIniPanelShellWorkflowOverviewEmbedding({
overviewApi = null,
contract = createIniPanelShellWorkflowOverviewContract(),
mountOptions = {},
} = {}) {
const readiness = createIniPanelShellWorkflowOverviewReadiness({
overviewApi,
contract,
});
if (!readiness.ready) {
return {
apiName: "ini-panel-shell-workflow-overview-embedding",
embeddingVersion: 1,
phase: "blocked",
ready: false,
pageHref: contract.pageHref,
contract,
readiness,
renderState: null,
mountResult: null,
error: null,
};
}
try {
const renderState = overviewApi.getWorkflowOverviewRenderState();
const mountResult = overviewApi.mountWorkflowOverview(mountOptions);
return {
apiName: "ini-panel-shell-workflow-overview-embedding",
embeddingVersion: 1,
phase: mountResult?.phase ?? "blocked",
ready: mountResult?.ready === true,
pageHref: contract.pageHref,
contract,
readiness,
renderState,
mountResult,
error: null,
};
} catch (error) {
return {
apiName: "ini-panel-shell-workflow-overview-embedding",
embeddingVersion: 1,
phase: "blocked",
ready: false,
pageHref: contract.pageHref,
contract,
readiness,
renderState: null,
mountResult: null,
error: error?.message ?? String(error),
};
}
}
export function createIniPanelShellWorkflowOverviewEmbeddingReport(
embeddingResult = mountIniPanelShellWorkflowOverviewEmbedding(),
contract = createIniPanelShellWorkflowOverviewContract(),
) {
const result = embeddingResult && typeof embeddingResult === "object" ? embeddingResult : {};
const readiness = result.readiness && typeof result.readiness === "object" ? result.readiness : {};
const resultContract = result.contract && typeof result.contract === "object" ? result.contract : contract;
const mountResult = result.mountResult && typeof result.mountResult === "object" ? result.mountResult : {};
const renderResult = mountResult.renderResult && typeof mountResult.renderResult === "object"
? mountResult.renderResult
: {};
const missingMethods = Array.isArray(readiness.missing) ? [...readiness.missing] : [];
const error = result.error ?? mountResult.error ?? null;
const overviewReady = readiness.ready === true;
const embeddingReady = result.ready === true;
const mountPhase = mountResult.phase ?? null;
const mountReady = mountResult.ready === true;
const renderRowCount = Number.isFinite(renderResult.rowCount) ? renderResult.rowCount : 0;
const blockingReasons = [
...missingMethods.map((methodName) => `missing:${methodName}`),
...(embeddingReady ? [] : [`embedding:${result.phase ?? "blocked"}`]),
...(mountPhase && !mountReady ? [`mount:${mountPhase}`] : []),
...(error ? [`error:${error}`] : []),
];
const ready = overviewReady && embeddingReady && mountReady && !error && blockingReasons.length === 0;
const statusLine = ready
? "Ready: No blocking reasons"
: `Blocked: ${blockingReasons.length > 0 ? blockingReasons.join(", ") : "workflow overview embedding unavailable"}`;
return {
apiName: "ini-panel-shell-workflow-overview-embedding-report",
reportVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
overviewReady,
embeddingReady,
mountReady,
statusLine,
pageHref: result.pageHref ?? resultContract.pageHref ?? null,
contract: resultContract,
missingMethods,
mountPhase,
renderRowCount,
error,
blockingReasons,
rows: [
{
id: "overview-api",
label: "Overview API",
value: overviewReady ? "ready" : "blocked",
},
{
id: "missing-methods",
label: "Missing methods",
value: missingMethods.length === 0 ? "none" : missingMethods.join(", "),
},
{
id: "embedding-ready",
label: "Embedding readiness",
value: embeddingReady ? "ready" : "blocked",
},
{
id: "mount-phase",
label: "Mount phase",
value: mountPhase ?? "missing",
},
{
id: "render-row-count",
label: "Render row count",
value: String(renderRowCount),
},
{
id: "error",
label: "Error",
value: error ?? "none",
},
],
};
}
export function createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel(
report = createIniPanelShellWorkflowOverviewEmbeddingReport(),
) {
const blockingReasons = Array.isArray(report?.blockingReasons) ? report.blockingReasons : [];
const rows = Array.isArray(report?.rows)
? report.rows.map(({ id, label, value }) => ({
id,
label,
value: value == null ? "" : String(value),
}))
: [];
return {
phase: report?.phase === "ready" ? "ready" : "blocked",
title: "Workflow overview embedding",
statusText: report?.ready ? "Ready" : "Blocked",
detailText: blockingReasons.length === 0 ? "No blocking reasons" : blockingReasons.join(", "),
rows,
};
}
export function createIniPanelShellWorkflowOverviewEmbeddingRenderState(
displayViewModel = createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel(),
) {
const rows = Array.isArray(displayViewModel?.rows)
? displayViewModel.rows.map(({ id, label, value }) => ({
id,
label,
value: value == null ? "" : String(value),
}))
: [];
const ready = displayViewModel?.phase === "ready" && displayViewModel?.statusText === "Ready";
const statusText = displayViewModel?.statusText ?? (ready ? "Ready" : "Blocked");
const detailText = displayViewModel?.detailText ?? "No blocking reasons";
return {
apiName: "ini-panel-shell-workflow-overview-embedding-render-state",
renderStateVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
title: displayViewModel?.title ?? "Workflow overview embedding",
statusText,
detailText,
statusLine: `${statusText}: ${detailText}`,
rows,
dataset: {
handoffPhase: ready ? "ready" : "blocked",
handoffReady: ready ? "true" : "false",
handoffScope: "workflow-overview-embedding",
},
};
}
export function createIniPanelShellWorkflowOverviewEmbeddingDomContract({
rowDatasetPrefix = "workflowOverviewEmbedding",
selectors = {},
} = {}) {
const mountSelector = selectors.mount ?? "[data-workflow-overview-embedding]";
const statusSelector = selectors.status ?? "[data-workflow-overview-embedding-status]";
const rowsSelector = selectors.rows ?? "[data-workflow-overview-embedding-rows]";
return {
apiName: "ini-panel-shell-workflow-overview-embedding-dom-contract",
contractVersion: 1,
requiredNodeIds: ["mount", "status", "rows"],
selectors: {
mount: mountSelector,
status: statusSelector,
rows: rowsSelector,
},
mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"],
rowDatasetPrefix,
rowDatasetKeys: {
term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`],
detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`],
},
renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"],
};
}
export function createIniPanelShellWorkflowOverviewEmbeddingDomReadiness({
rowDatasetPrefix = "workflowOverviewEmbedding",
contract = createIniPanelShellWorkflowOverviewEmbeddingDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
const checks = {
document: Boolean(documentRef?.createElement),
mount: Boolean(mountNode),
status: Boolean(statusNode),
rows: Boolean(rowsNode),
rowDatasetPrefix: Boolean(contract.rowDatasetPrefix),
};
const missing = Object.entries(checks)
.filter(([, passed]) => !passed)
.map(([name]) => name);
const ready = missing.length === 0;
return {
apiName: "ini-panel-shell-workflow-overview-embedding-dom-readiness",
readinessVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
missing,
checks,
contract,
};
}
export function renderIniPanelShellWorkflowOverviewEmbeddingState(
renderState = createIniPanelShellWorkflowOverviewEmbeddingRenderState(),
{
rowDatasetPrefix = "workflowOverviewEmbedding",
contract = createIniPanelShellWorkflowOverviewEmbeddingDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {},
) {
const readiness = createIniPanelShellWorkflowOverviewEmbeddingDomReadiness({
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
if (!readiness.ready) {
throw new Error(
`Workflow overview embedding DOM is not ready: ${readiness.missing.join(", ")}`,
);
}
mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked";
mountNode.dataset.handoffReady = renderState?.dataset?.handoffReady ?? "false";
mountNode.dataset.handoffScope = renderState?.dataset?.handoffScope ?? "workflow-overview-embedding";
statusNode.textContent = renderState?.statusLine ?? "Blocked: No workflow overview embedding render state";
rowsNode.replaceChildren();
const rows = Array.isArray(renderState?.rows) ? renderState.rows : [];
const effectiveRowDatasetPrefix = contract.rowDatasetPrefix;
for (const row of rows) {
const term = documentRef.createElement("dt");
term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id;
term.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked";
term.textContent = row.label;
const detail = documentRef.createElement("dd");
detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id;
detail.dataset[`${effectiveRowDatasetPrefix}Status`] = renderState?.ready ? "pass" : "blocked";
detail.textContent = row.value;
rowsNode.append(term, detail);
}
return {
rendered: true,
statusLine: statusNode.textContent,
rowCount: rows.length,
rowIds: rows.map(({ id }) => id),
dataset: {
handoffPhase: mountNode.dataset.handoffPhase,
handoffReady: mountNode.dataset.handoffReady,
handoffScope: mountNode.dataset.handoffScope,
},
};
}
export function mountIniPanelShellWorkflowOverviewEmbeddingState({
renderState = createIniPanelShellWorkflowOverviewEmbeddingRenderState(),
rowDatasetPrefix = "workflowOverviewEmbedding",
contract = createIniPanelShellWorkflowOverviewEmbeddingDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
const domReadiness = createIniPanelShellWorkflowOverviewEmbeddingDomReadiness({
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
if (!domReadiness.ready) {
return {
apiName: "ini-panel-shell-workflow-overview-embedding-dom-mount",
workflowVersion: 1,
phase: "blocked",
ready: false,
statusLine: `Blocked: ${domReadiness.missing.join(", ")}`,
contract,
domReadiness,
renderState,
renderResult: null,
error: null,
};
}
try {
const renderResult = renderIniPanelShellWorkflowOverviewEmbeddingState(renderState, {
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
return {
apiName: "ini-panel-shell-workflow-overview-embedding-dom-mount",
workflowVersion: 1,
phase: renderState?.phase === "ready" ? "ready" : "blocked",
ready: renderState?.ready === true,
statusLine: renderResult.statusLine,
contract,
domReadiness,
renderState,
renderResult,
error: null,
};
} catch (error) {
return {
apiName: "ini-panel-shell-workflow-overview-embedding-dom-mount",
workflowVersion: 1,
phase: "blocked",
ready: false,
statusLine: `Blocked: ${error?.message ?? "workflow overview embedding DOM mount error"}`,
contract,
domReadiness,
renderState,
renderResult: null,
error: error?.message ?? String(error),
};
}
}
export function createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel(
mountResult = mountIniPanelShellWorkflowOverviewEmbeddingState(),
) {
const domMissing = Array.isArray(mountResult?.domReadiness?.missing)
? mountResult.domReadiness.missing
: [];
const renderResult = mountResult?.renderResult && typeof mountResult.renderResult === "object"
? mountResult.renderResult
: {};
const renderState = mountResult?.renderState && typeof mountResult.renderState === "object"
? mountResult.renderState
: {};
const error = mountResult?.error ?? null;
const ready = mountResult?.ready === true;
const problems = [
...(ready ? [] : [`mount:${mountResult?.phase ?? "blocked"}`]),
...domMissing.map((name) => `dom:${name}`),
...(error ? [`error:${error}`] : []),
];
return {
phase: ready ? "ready" : "blocked",
title: "Workflow overview embedding mount",
statusText: ready ? "Ready" : "Blocked",
detailText: problems.length === 0 ? "No blocking reasons" : problems.join(", "),
rows: [
{
id: "mount-ready",
label: "Mount readiness",
value: ready ? "ready" : "blocked",
},
{
id: "dom-readiness",
label: "DOM readiness",
value: mountResult?.domReadiness?.ready ? "ready" : "blocked",
},
{
id: "missing-dom",
label: "Missing DOM",
value: domMissing.length === 0 ? "none" : domMissing.join(", "),
},
{
id: "render-state",
label: "Render state",
value: renderState.statusLine ?? "missing",
},
{
id: "render-result",
label: "Render result",
value: renderResult.rendered ? "rendered" : "missing",
},
{
id: "render-row-count",
label: "Render row count",
value: Number.isFinite(renderResult.rowCount) ? String(renderResult.rowCount) : "0",
},
{
id: "error",
label: "Error",
value: error ?? "none",
},
],
};
}
export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries()) {
const shellIntegrationManifest = createIniPanelShellIntegrationManifest(entries);
const shellIntegrationReadiness = createIniPanelShellIntegrationReadiness(shellIntegrationManifest);
const shellIntegrationWorkflowPlan = createIniPanelShellIntegrationWorkflowPlan({
manifest: shellIntegrationManifest,
readiness: shellIntegrationReadiness,
});
const shellSessionHandoffSummary = createIniPanelShellSessionHandoffSummary({
manifest: shellIntegrationManifest,
readiness: shellIntegrationReadiness,
workflowPlan: shellIntegrationWorkflowPlan,
});
const shellSessionHandoffCompatibilityReport = createIniPanelShellSessionHandoffCompatibilityReport(
shellSessionHandoffSummary,
);
const shellSessionHandoffDisplayViewModel = createIniPanelShellSessionHandoffDisplayViewModel(
shellSessionHandoffCompatibilityReport,
);
const shellSessionHandoffSnapshot = createIniPanelShellSessionHandoffSnapshot({
readiness: shellIntegrationReadiness,
summary: shellSessionHandoffSummary,
compatibilityReport: shellSessionHandoffCompatibilityReport,
displayViewModel: shellSessionHandoffDisplayViewModel,
});
const shellSessionHandoffRenderState = createIniPanelShellSessionHandoffRenderState(
shellSessionHandoffSnapshot,
);
const shellSessionHandoffActionPlan = createIniPanelShellSessionHandoffActionPlan({
snapshot: shellSessionHandoffSnapshot,
renderState: shellSessionHandoffRenderState,
});
const shellSessionHandoffChecklist = createIniPanelShellSessionHandoffChecklist({
actionPlan: shellSessionHandoffActionPlan,
renderState: shellSessionHandoffRenderState,
});
const shellSessionHandoffPackage = createIniPanelShellSessionHandoffPackage({
shellIntegrationManifest,
launchApiManifest: shellIntegrationManifest.launchApiManifest,
controlPageApiManifest: shellIntegrationManifest.controlPageApiManifest,
snapshot: shellSessionHandoffSnapshot,
renderState: shellSessionHandoffRenderState,
actionPlan: shellSessionHandoffActionPlan,
checklist: shellSessionHandoffChecklist,
});
const shellSessionHandoffPackageReport = createIniPanelShellSessionHandoffPackageReport(
shellSessionHandoffPackage,
);
const shellSessionHandoffPackageDisplayViewModel = createIniPanelShellSessionHandoffPackageDisplayViewModel(
shellSessionHandoffPackageReport,
);
const shellSessionHandoffPackageRenderState = createIniPanelShellSessionHandoffPackageRenderState(
shellSessionHandoffPackageDisplayViewModel,
);
const shellSessionHandoffPackageMountState = createIniPanelShellSessionHandoffPackageMountState({
handoffPackage: shellSessionHandoffPackage,
packageRenderState: shellSessionHandoffPackageRenderState,
});
const shellSessionHandoffPackageMountDisplayViewModel = createIniPanelShellSessionHandoffPackageMountDisplayViewModel(
shellSessionHandoffPackageMountState,
);
const shellSessionHandoffPackageMountRenderState = createIniPanelShellSessionHandoffPackageMountRenderState(
shellSessionHandoffPackageMountDisplayViewModel,
);
const shellSessionHandoffMountDomContract = createIniPanelShellSessionHandoffMountDomContract();
const shellSessionHandoffWorkflowSummary = createIniPanelShellSessionHandoffWorkflowSummary({
mountWorkflow: {
phase: shellSessionHandoffPackageMountRenderState.phase,
ready: shellSessionHandoffPackageMountRenderState.ready,
statusLine: shellSessionHandoffPackageMountRenderState.statusLine,
},
controlPageMountWorkflow: {
phase: shellSessionHandoffPackageMountState.phase,
ready: shellSessionHandoffPackageMountState.ready,
statusLine: shellSessionHandoffPackageMountState.statusLine,
},
readonlyStatusResult: readIniPanelShellSessionHandoffStatusFromControlPage({
handoffSummary: shellSessionHandoffSummary,
}),
});
const shellSessionHandoffWorkflowDisplayViewModel = createIniPanelShellSessionHandoffWorkflowDisplayViewModel(
shellSessionHandoffWorkflowSummary,
);
const shellSessionHandoffWorkflowRenderState = createIniPanelShellSessionHandoffWorkflowRenderState(
shellSessionHandoffWorkflowDisplayViewModel,
);
const shellSessionHandoffWorkflowDomContract = createIniPanelShellSessionHandoffWorkflowDomContract();
const shellWorkflowOverviewContract = createIniPanelShellWorkflowOverviewContract();
return {
pages: entries.map(({ id, href, title }) => ({
id,
href,
title,
})),
launcherLinks: createIniPanelLauncherLinkViewModel(entries),
shellIntegrationSchema: createIniPanelShellIntegrationSchema(),
shellIntegrationManifest,
shellIntegrationReadiness,
shellIntegrationWorkflowPlan,
shellSessionHandoffSummarySchema: createIniPanelShellSessionHandoffSummarySchema(),
shellSessionHandoffSummary,
shellSessionHandoffCompatibilityReport,
shellSessionHandoffDisplayViewModel,
shellSessionHandoffSnapshot,
shellSessionHandoffRenderState,
shellSessionHandoffActionPlan,
shellSessionHandoffChecklist,
shellSessionHandoffPackage,
shellSessionHandoffPackageSchema: createIniPanelShellSessionHandoffPackageSchema(),
shellSessionHandoffPackageReport,
shellSessionHandoffPackageDisplayViewModel,
shellSessionHandoffPackageRenderState,
shellSessionHandoffPackageMountState,
shellSessionHandoffPackageMountDisplayViewModel,
shellSessionHandoffPackageMountRenderState,
shellSessionHandoffMountDomContract,
shellSessionHandoffWorkflowSummary,
shellSessionHandoffWorkflowDisplayViewModel,
shellSessionHandoffWorkflowRenderState,
shellSessionHandoffWorkflowDomContract,
shellWorkflowOverviewContract,
launchApiSchema: createIniPanelLaunchApiSchema(),
launchApiManifest: createIniPanelLaunchApiManifest(entries),
isSupportedShellIntegrationManifest: isSupportedIniPanelShellIntegrationManifest,
createShellIntegrationReadiness: createIniPanelShellIntegrationReadiness,
createShellIntegrationWorkflowPlan: createIniPanelShellIntegrationWorkflowPlan,
isSupportedShellSessionHandoffSummary: isSupportedIniPanelShellSessionHandoffSummary,
createShellSessionHandoffSummarySchema: createIniPanelShellSessionHandoffSummarySchema,
createShellSessionHandoffSummary: createIniPanelShellSessionHandoffSummary,
createShellSessionHandoffCompatibilityReport: createIniPanelShellSessionHandoffCompatibilityReport,
createShellSessionHandoffDisplayViewModel: createIniPanelShellSessionHandoffDisplayViewModel,
createShellSessionHandoffSnapshot: createIniPanelShellSessionHandoffSnapshot,
createShellSessionHandoffRenderState: createIniPanelShellSessionHandoffRenderState,
createShellSessionHandoffActionPlan: createIniPanelShellSessionHandoffActionPlan,
createShellSessionHandoffChecklist: createIniPanelShellSessionHandoffChecklist,
createShellSessionHandoffPackage: createIniPanelShellSessionHandoffPackage,
createShellSessionHandoffPackageSchema: createIniPanelShellSessionHandoffPackageSchema,
createShellSessionHandoffPackageReport: createIniPanelShellSessionHandoffPackageReport,
createShellSessionHandoffPackageDisplayViewModel: createIniPanelShellSessionHandoffPackageDisplayViewModel,
createShellSessionHandoffPackageRenderState: createIniPanelShellSessionHandoffPackageRenderState,
createShellSessionHandoffPackageMountState: createIniPanelShellSessionHandoffPackageMountState,
createShellSessionHandoffPackageMountDisplayViewModel: createIniPanelShellSessionHandoffPackageMountDisplayViewModel,
createShellSessionHandoffPackageMountRenderState: createIniPanelShellSessionHandoffPackageMountRenderState,
createShellSessionHandoffMountDomContract: createIniPanelShellSessionHandoffMountDomContract,
createShellSessionHandoffMountDomReadiness: createIniPanelShellSessionHandoffMountDomReadiness,
renderShellSessionHandoffMountState: renderIniPanelShellSessionHandoffMountState,
mountShellSessionHandoff: mountIniPanelShellSessionHandoff,
mountShellSessionHandoffFromControlPage: mountIniPanelShellSessionHandoffFromControlPage,
readShellSessionHandoffStatusFromControlPage: readIniPanelShellSessionHandoffStatusFromControlPage,
createShellSessionHandoffWorkflowSummary: createIniPanelShellSessionHandoffWorkflowSummary,
createShellSessionHandoffWorkflowDisplayViewModel: createIniPanelShellSessionHandoffWorkflowDisplayViewModel,
createShellSessionHandoffWorkflowRenderState: createIniPanelShellSessionHandoffWorkflowRenderState,
createShellSessionHandoffWorkflowDomContract: createIniPanelShellSessionHandoffWorkflowDomContract,
createShellSessionHandoffWorkflowDomReadiness: createIniPanelShellSessionHandoffWorkflowDomReadiness,
renderShellSessionHandoffWorkflowState: renderIniPanelShellSessionHandoffWorkflowState,
mountShellSessionHandoffWorkflow: mountIniPanelShellSessionHandoffWorkflow,
createWorkflowOverviewContract: createIniPanelShellWorkflowOverviewContract,
createWorkflowOverviewReadiness: createIniPanelShellWorkflowOverviewReadiness,
mountWorkflowOverviewEmbedding: mountIniPanelShellWorkflowOverviewEmbedding,
createWorkflowOverviewEmbeddingReport: createIniPanelShellWorkflowOverviewEmbeddingReport,
createWorkflowOverviewEmbeddingDisplayViewModel: createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel,
createWorkflowOverviewEmbeddingRenderState: createIniPanelShellWorkflowOverviewEmbeddingRenderState,
createWorkflowOverviewEmbeddingDomContract: createIniPanelShellWorkflowOverviewEmbeddingDomContract,
createWorkflowOverviewEmbeddingDomReadiness: createIniPanelShellWorkflowOverviewEmbeddingDomReadiness,
renderWorkflowOverviewEmbeddingState: renderIniPanelShellWorkflowOverviewEmbeddingState,
mountWorkflowOverviewEmbeddingState: mountIniPanelShellWorkflowOverviewEmbeddingState,
createWorkflowOverviewEmbeddingMountDisplayViewModel:
createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel,
isSupportedShellSessionHandoffPackage: isSupportedIniPanelShellSessionHandoffPackage,
controlPage: {
browserApiSchema: createControlPageBrowserApiSchema(),
browserApiManifest: createControlPageBrowserApiManifest(),
browserApiMethods: createControlPageBrowserApiMethods(),
contract: createIniPanelShellControlPageContract(),
createController: createMachineSessionControlPageController,
isSupportedBrowserApiManifest: isSupportedIniPanelControlPageApiManifest,
loadSessionState: loadMachineSessionForControlPageWorkflow,
readonlyStatusApiManifest: createMachineSessionReadonlyStatusApiManifest(),
readStatus: readMachineSessionReadonlyStatus,
refresh: refreshMachineSessionControlPage,
renderView: renderMachineSessionControlView,
},
};
}