收口 INI 面板 persistence summary 发布门禁

This commit is contained in:
2026-06-16 09:00:29 +08:00
parent 65e95327b3
commit d3ee5d49ff
21 changed files with 1458 additions and 46 deletions

View File

@@ -76,6 +76,9 @@ import {
createIniPanelShellViewModel,
getIniPanelEntryByHref,
getVisibleIniPanelEntries,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createMachineSessionControlPageController,
createMachineSessionReadonlyStatus,
createMachineSessionReadonlyStatusApiManifest,
@@ -138,6 +141,17 @@ needs a read-only `ready`/`blocked` readiness report before loading a machine
session into WASM. `createMachineSessionReadinessFieldState()` and
`machineSessionReadinessLogLines()` turn that report into stable fields and
status text without parsing G-code or executing LinuxCNC behavior.
Use `createMachineSessionPersistenceSummary()` or
`linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` when a browser
panel or outer shell needs one read-only OPFS/session persistence summary with
machine-file, session snapshot, session readiness, session load, readonly
status, and handoff readiness rows. The launch page exposes the same helper as
`linuxCncIniPanelLaunchApi.getMachineSessionPersistenceSummary()`, and the
workflow overview page exposes it as
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewPersistenceSummary()`.
Use `createMachineSessionPersistenceDisplayViewModel()` and
`createMachineSessionPersistenceRenderState()` when that summary needs stable
display text, rows, status line, and dataset fields for caller-owned DOM.
Use `createIniPanelShellControlPageContract()` or `controlPage.contract` when
an outer shell needs a fast contract check for `createController`,
`loadSessionState`, `readStatus`, `refresh`, `renderView`, the readonly status
@@ -306,9 +320,16 @@ the same report through
Use `createIniPanelShellApiSurfaceInventory()` or
`linuxCncIniPanelLaunchApi.getShellApiSurfaceInventory()` when an outer shell
needs a machine-readable inventory of launch, control-page, workflow overview,
persistence, and session readiness report API counts. The workflow overview
page exposes the same inventory through
persistence capabilities, session readiness report rows, and persistence
summary rows. The workflow overview page exposes the same inventory through
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewApiSurfaceInventory()`.
When provided to `createIniPanelShellSessionHandoffWorkflowSummary()`, the
inventory is also rendered as the read-only `api-surface-inventory` workflow
summary row for browser overview pages and external shells. The same workflow
summary accepts `persistenceSummary` from
`createMachineSessionPersistenceSummary()` and renders it as the
`persistence-summary` row, so a shell overview can show API coverage and
OPFS/session persistence state together.
Use
`createIniPanelShellSessionHandoffWorkflowDisplayViewModel()` or
`linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowDisplayViewModel()`

View File

@@ -32,7 +32,16 @@ continuation records are in `../text14.txt`.
- SDK callers should import stable helpers from `runtime/sdk/src/index.js`,
including `createIniPanelShellApiSurfaceInventory()` for API discovery and
`createIniPanelShellSessionReadinessWorkflowReport()` when they need
machine-readable session readiness phase and missing reasons.
machine-readable session readiness phase and missing reasons. The workflow
overview exposes the API surface inventory as a read-only summary row.
- OPFS/session callers can use `createMachineSessionPersistenceSummary()` or
`linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` to inspect
machine files, session snapshot, session readiness, session load, readonly
status, and shell handoff readiness as stable rows. Shell handoff workflow
summaries can include that report as the `persistence-summary` row beside the
API surface inventory row, and launch/workflow-overview APIs expose the same
helper for external shells. The summary also has display and render-state
helpers for caller-owned DOM.
- OPFS/session persistence is host-side storage glue. Parameter and tool-table
behavior still comes from the LinuxCNC-backed interpreter SDK calls.
- Sim-config inventory promotion remains evidence-driven and must keep

View File

@@ -23,22 +23,41 @@ Use `src/index.js` for stable imports:
```js
import {
INI_PANEL_ENTRIES,
analyzeIniRuntimeBoundaries,
createControlPageBrowserApiManifest,
createControlPageBrowserApiMethods,
createControlPageBrowserApiSchema,
createIniPanelLaunchApiManifest,
createIniPanelLauncherLinkViewModel,
createIniPanelShellApiSurfaceInventory,
createIniPanelShellControlPageContract,
createIniPanelShellIntegrationManifest,
createIniPanelShellIntegrationReadiness,
createIniPanelShellIntegrationSchema,
createIniPanelShellIntegrationWorkflowPlan,
createIniPanelShellSessionReadinessWorkflowReport,
createIniPanelShellViewModel,
createIniPanelShellWorkflowOverviewContract,
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
createLinuxCncIniSdk,
createLinuxCncInterpSdk,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createMachineSessionSnapshotPayload,
createSessionSnapshot,
defaultMachinePaths,
gcodeFilenameFromProgramPath,
gcodeProgramPath,
getIniPanelEntryByHref,
getIniPanelEntryManifest,
getOpfsRoot,
getVisibleIniPanelEntries,
isSupportedIniPanelControlPageApiManifest,
isSupportedIniPanelLaunchApiManifest,
isSupportedIniPanelShellIntegrationManifest,
loadMachineSessionFromOpfs,
loadMachineSessionSnapshot,
loadMachineTextFiles,
@@ -189,13 +208,42 @@ effects still come from the LinuxCNC-backed interpreter SDK methods they call.
The SDK entrypoint also re-exports the stable INI panel shell handoff surface
for external browser shells:
- `INI_PANEL_ENTRIES`, `getIniPanelEntryManifest()`,
`getIniPanelEntryByHref()`, `getVisibleIniPanelEntries()`, and
`createIniPanelLauncherLinkViewModel()` for discovering launch pages and
rendering shell-owned launcher links from the stable entry manifest.
- `createIniPanelLaunchApiManifest()` and
`isSupportedIniPanelLaunchApiManifest()` for launch API negotiation.
- `createControlPageBrowserApiSchema()`,
`createControlPageBrowserApiManifest()`,
`createControlPageBrowserApiMethods()`,
`createIniPanelShellControlPageContract()`, and
`isSupportedIniPanelControlPageApiManifest()` for read-only control-page API
negotiation.
- `createIniPanelShellIntegrationSchema()`,
`createIniPanelShellIntegrationManifest()`,
`createIniPanelShellIntegrationReadiness()`,
`createIniPanelShellIntegrationWorkflowPlan()`, and
`isSupportedIniPanelShellIntegrationManifest()` for checking launch and
control-page compatibility before embedding.
- `createIniPanelShellViewModel()` for a complete read-only shell integration
bundle.
- `createMachineSessionPersistenceSummary()` for a machine-readable OPFS/session
persistence summary covering machine files, session snapshot, session
readiness, session load, readonly status, and shell handoff readiness rows.
`createMachineSessionPersistenceDisplayViewModel()` and
`createMachineSessionPersistenceRenderState()` turn the summary into stable
display and render data.
`createIniPanelShellSessionHandoffWorkflowSummary()` can include this as the
read-only `persistence-summary` row.
- `createIniPanelShellWorkflowOverviewContract()` for discovering the workflow
overview iframe API contract.
- `createIniPanelShellApiSurfaceInventory()` for a machine-readable inventory
of launch, control-page, workflow overview, persistence, and session
readiness report API counts.
readiness report API counts, including persistence summary row counts.
`createIniPanelShellViewModel()` also includes
this inventory as a read-only workflow summary row so browser overview pages
and external shells can display API coverage without private imports.
- `createIniPanelShellSessionReadinessWorkflowReport()` for a machine-readable
session readiness workflow report with phase, missing reasons, status line,
and stable rows.

View File

@@ -54,12 +54,21 @@ export { loadMachineSessionFromOpfs } from "../../opfs/linuxcnc-machine-session-
export { readMachineSessionReadiness } from "../../opfs/machine-session-readiness.js";
export {
INI_PANEL_ENTRIES,
createControlPageBrowserApiManifest,
createControlPageBrowserApiMethods,
createControlPageBrowserApiSchema,
createIniPanelLaunchApiManifest,
createIniPanelLaunchApiSchema,
createIniPanelLauncherLinkViewModel,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createIniPanelShellIntegrationManifest,
createIniPanelShellIntegrationReadiness,
createIniPanelShellIntegrationSchema,
createIniPanelShellIntegrationWorkflowPlan,
createIniPanelShellControlPageContract,
createIniPanelShellSessionHandoffCompatibilityReport,
createIniPanelShellSessionHandoffPackage,
createIniPanelShellSessionHandoffPackageMountDisplayViewModel,
@@ -84,6 +93,10 @@ export {
createIniPanelShellWorkflowOverviewEmbeddingRenderState,
createIniPanelShellWorkflowOverviewEmbeddingReport,
createIniPanelShellWorkflowOverviewReadiness,
getIniPanelEntryByHref,
getIniPanelEntryManifest,
getVisibleIniPanelEntries,
isSupportedIniPanelControlPageApiManifest,
isSupportedIniPanelLaunchApiManifest,
isSupportedIniPanelShellIntegrationManifest,
isSupportedIniPanelShellSessionHandoffPackage,

View File

@@ -46,6 +46,9 @@ import {
} from "./session-workflow.js";
import {
createMachineSessionControlView,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createMachineSessionStateSnapshot,
createMachineSessionStateBundle,
createMachineSessionStateReport,
@@ -171,6 +174,25 @@ function getMachineSessionWorkflowStatus() {
return createMachineSessionWorkflowStatus(window.linuxCncIniPanelMachineSessionState);
}
function getMachineSessionPersistenceSummary(options = {}) {
return createMachineSessionPersistenceSummary({
state: window.linuxCncIniPanelMachineSessionState,
...options,
});
}
function getMachineSessionPersistenceDisplayViewModel(options = {}) {
return createMachineSessionPersistenceDisplayViewModel(
getMachineSessionPersistenceSummary(options),
);
}
function getMachineSessionPersistenceRenderState(options = {}) {
return createMachineSessionPersistenceRenderState(
getMachineSessionPersistenceDisplayViewModel(options),
);
}
function getMachineSessionStateBundle() {
return createMachineSessionStateBundle(window.linuxCncIniPanelMachineSessionState);
}
@@ -203,6 +225,9 @@ window.linuxCncIniPanelApi = {
getMachineSessionStateReport,
getMachineSessionStateReportExport,
getMachineSessionWorkflowStatus,
getMachineSessionPersistenceSummary,
getMachineSessionPersistenceDisplayViewModel,
getMachineSessionPersistenceRenderState,
renderMachineSessionControlView: renderCurrentMachineSessionControlView,
};
window.getMachineSessionControlView = getMachineSessionControlView;
@@ -210,6 +235,9 @@ window.getMachineSessionStateBundle = getMachineSessionStateBundle;
window.getMachineSessionStateReport = getMachineSessionStateReport;
window.getMachineSessionStateReportExport = getMachineSessionStateReportExport;
window.getMachineSessionWorkflowStatus = getMachineSessionWorkflowStatus;
window.getMachineSessionPersistenceSummary = getMachineSessionPersistenceSummary;
window.getMachineSessionPersistenceDisplayViewModel = getMachineSessionPersistenceDisplayViewModel;
window.getMachineSessionPersistenceRenderState = getMachineSessionPersistenceRenderState;
window.renderMachineSessionControlView = renderCurrentMachineSessionControlView;
window.linuxCncIniPanelMachineSessionState = panelMachineSessionState;
renderCurrentMachineSessionControlView();

View File

@@ -116,6 +116,7 @@
mountIniPanelShellSessionHandoff,
mountIniPanelShellSessionHandoffFromControlPage,
readIniPanelShellSessionHandoffStatusFromControlPage,
createMachineSessionPersistenceSummary,
createIniPanelShellSessionHandoffWorkflowSummary,
createIniPanelShellSessionReadinessWorkflowReport,
createIniPanelShellSessionHandoffWorkflowDisplayViewModel,
@@ -229,6 +230,9 @@
...(options ?? {}),
})
),
getMachineSessionPersistenceSummary: (options) => (
createMachineSessionPersistenceSummary(options)
),
getShellSessionHandoffWorkflowSummary: (options) => (
createIniPanelShellSessionHandoffWorkflowSummary(options ?? {
mountWorkflow: {

View File

@@ -156,6 +156,156 @@ export function createMachineSessionWorkflowStatus(state = {}) {
};
}
export function createMachineSessionPersistenceSummary({
state = {},
report = createMachineSessionStateReport(state),
workflowStatus = createMachineSessionWorkflowStatus(state),
readonlyStatus = null,
handoffSummary = null,
handoffWorkflowSummary = null,
} = {}) {
const readiness = report.readiness ?? {};
const machineFiles = state.machineFiles ?? {};
const session = report.session ?? {};
const loaded = report.loaded ?? {};
const sessionReadiness = report.sessionReadiness ?? workflowStatus.sessionReadiness ?? null;
const opfsReady = String(readiness.opfs ?? "").toLowerCase().includes("ready");
const machineFileLabels = [
machineFiles.iniOpfsPath || session.ini ? "ini" : null,
machineFiles.parameterOpfsPath || session.parameters ? "parameters" : null,
machineFiles.toolTableOpfsPath || session.toolTable ? "tool-table" : null,
machineFiles.gcodeOpfsPath || session.gcode ? "g-code" : null,
].filter(Boolean);
const machineFilesReady = machineFileLabels.length > 0;
const snapshotReady = Boolean(session.snapshotLabel);
const sessionReadinessReady = sessionReadiness?.ready === true;
const sessionLoadReady = Boolean(loaded.iniWasmPath);
const readonlyStatusValue = readonlyStatus
? (readonlyStatus.ready === true ? "ready" : readonlyStatus.phase ?? "available")
: "not-provided";
const handoffValue = handoffWorkflowSummary
? (handoffWorkflowSummary.ready === true ? "ready" : handoffWorkflowSummary.phase ?? "blocked")
: (
handoffSummary
? (handoffSummary.ready === true ? "ready" : handoffSummary.phase ?? "blocked")
: "not-provided"
);
const checks = {
opfs: opfsReady,
machineFiles: machineFilesReady,
sessionSnapshot: snapshotReady,
sessionReadiness: sessionReadinessReady,
sessionLoad: sessionLoadReady,
};
const missing = Object.entries(checks)
.filter(([, passed]) => !passed)
.map(([name]) => name);
const ready = missing.length === 0;
return {
apiName: "machine-session-persistence-summary",
summaryVersion: 1,
phase: ready ? "ready" : "waiting",
ready,
checks,
missing,
counts: {
machineFiles: machineFileLabels.length,
missing: missing.length,
},
rows: [
{
id: "opfs",
label: "OPFS",
value: readiness.opfs ?? "missing",
},
{
id: "machine-files",
label: "Machine files",
value: machineFileLabels.length > 0 ? machineFileLabels.join(", ") : "missing",
},
{
id: "session-snapshot",
label: "Session snapshot",
value: session.snapshotLabel ?? "missing",
},
{
id: "session-readiness",
label: "Session readiness",
value: sessionReadiness
? (sessionReadiness.ready ? "ready" : `blocked: ${sessionReadiness.missing?.join(", ") || "unknown"}`)
: "missing",
},
{
id: "session-load",
label: "Session load",
value: loaded.iniWasmPath ? `loaded: ${loaded.iniWasmPath}` : "missing",
},
{
id: "readonly-status",
label: "Readonly status",
value: readonlyStatusValue,
},
{
id: "handoff-readiness",
label: "Handoff readiness",
value: handoffValue,
},
],
};
}
export function createMachineSessionPersistenceDisplayViewModel(
summary = createMachineSessionPersistenceSummary(),
) {
const missing = Array.isArray(summary?.missing) ? summary.missing : [];
const rows = Array.isArray(summary?.rows)
? summary.rows.map(({ id, label, value }) => ({
id,
label,
value: value == null ? "" : String(value),
}))
: [];
return {
phase: summary?.phase === "ready" ? "ready" : "waiting",
title: "OPFS/session persistence",
statusText: summary?.ready ? "Ready" : "Waiting",
detailText: missing.length > 0 ? missing.join(", ") : "No missing persistence requirements",
rows,
};
}
export function createMachineSessionPersistenceRenderState(
displayViewModel = createMachineSessionPersistenceDisplayViewModel(),
) {
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" : "Waiting");
const detailText = displayViewModel?.detailText ?? "No missing persistence requirements";
return {
apiName: "machine-session-persistence-render-state",
renderStateVersion: 1,
phase: ready ? "ready" : "waiting",
ready,
title: displayViewModel?.title ?? "OPFS/session persistence",
statusText,
detailText,
statusLine: `${statusText}: ${detailText}`,
rows,
dataset: {
persistencePhase: ready ? "ready" : "waiting",
persistenceReady: ready ? "true" : "false",
persistenceScope: "opfs-session",
},
};
}
export function createMachineSessionWorkflowOverview(report = {}) {
const readiness = report.readiness ?? {};
const loaded = report.loaded ?? {};

View File

@@ -28,6 +28,9 @@ import {
createMachineSessionReadonlyStatusApiManifest,
readMachineSessionReadonlyStatus,
} from "./readonly-status-api.js";
import {
createMachineSessionPersistenceSummary,
} from "./panel-state-summary.js";
export {
controlPageStatusText,
createControlPageRefreshState,
@@ -58,6 +61,17 @@ export {
export {
renderMachineSessionControlView,
} from "./control-view-renderer.js";
export {
createMachineSessionControlView,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createMachineSessionStateBundle,
createMachineSessionStateReport,
createMachineSessionStateReportExport,
createMachineSessionWorkflowOverview,
createMachineSessionWorkflowStatus,
} from "./panel-state-summary.js";
export function getVisibleIniPanelEntries() {
return getIniPanelEntryManifest();
@@ -159,6 +173,7 @@ export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntr
"mountShellSessionHandoff",
"mountShellSessionHandoffFromControlPage",
"readShellSessionHandoffStatusFromControlPage",
"getMachineSessionPersistenceSummary",
"getShellSessionHandoffWorkflowSummary",
"getShellSessionReadinessWorkflowReport",
"getShellApiSurfaceInventory",
@@ -1428,6 +1443,8 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({
statusLine: "Blocked: readonly status unavailable",
missingStatusFields: [],
},
persistenceSummary = null,
apiSurfaceInventory = null,
} = {}) {
const sessionReadiness = readonlyStatusResult?.readonlyStatus?.sessionReadiness ?? null;
const sessionReadinessMissing = Array.isArray(sessionReadiness?.missing)
@@ -1456,6 +1473,32 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({
blockingReasons.push(`readonly-status:${readonlyDetail}`);
}
const ready = blockingReasons.length === 0;
const apiSurfaceRows = Array.isArray(apiSurfaceInventory?.rows)
? apiSurfaceInventory.rows
: [];
const apiSurfaceValue = apiSurfaceInventory
? (
apiSurfaceInventory.ready
? `ready: ${apiSurfaceRows.length} rows`
: `blocked: ${apiSurfaceInventory.phase ?? "unknown"}`
)
: "missing";
const apiSurfaceCounts = apiSurfaceInventory?.counts && typeof apiSurfaceInventory.counts === "object"
? { ...apiSurfaceInventory.counts }
: null;
const persistenceRows = Array.isArray(persistenceSummary?.rows)
? persistenceSummary.rows
: [];
const persistenceMissing = Array.isArray(persistenceSummary?.missing)
? persistenceSummary.missing
: [];
const persistenceValue = persistenceSummary
? (
persistenceSummary.ready
? `ready: ${persistenceRows.length} rows`
: `${persistenceSummary.phase ?? "waiting"}: ${persistenceMissing.length} missing`
)
: "missing";
return {
apiName: "ini-panel-shell-session-handoff-workflow-summary",
@@ -1490,6 +1533,22 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({
missing: sessionReadinessMissing,
}
: null,
apiSurfaceInventory: apiSurfaceInventory
? {
phase: apiSurfaceInventory.phase ?? (apiSurfaceInventory.ready ? "ready" : "blocked"),
ready: apiSurfaceInventory.ready === true,
counts: apiSurfaceCounts,
apiNames: apiSurfaceInventory.apiNames ? { ...apiSurfaceInventory.apiNames } : null,
}
: null,
persistenceSummary: persistenceSummary
? {
phase: persistenceSummary.phase ?? (persistenceSummary.ready ? "ready" : "waiting"),
ready: persistenceSummary.ready === true,
counts: persistenceSummary.counts ? { ...persistenceSummary.counts } : null,
missing: persistenceMissing,
}
: null,
blockingReasons,
rows: [
{
@@ -1520,6 +1579,16 @@ export function createIniPanelShellSessionHandoffWorkflowSummary({
? readonlyStatusResult.missingStatusFields.join(", ")
: "none",
},
{
id: "persistence-summary",
label: "Persistence summary",
value: persistenceValue,
},
{
id: "api-surface-inventory",
label: "API surface inventory",
value: apiSurfaceValue,
},
{
id: "blocking-reasons",
label: "Blocking reasons",
@@ -1609,6 +1678,7 @@ export function createIniPanelShellApiSurfaceInventory({
controlPageApiManifest = createControlPageBrowserApiManifest(),
workflowOverviewContract = createIniPanelShellWorkflowOverviewContract(),
sessionReadinessWorkflowReport = createIniPanelShellSessionReadinessWorkflowReport(),
persistenceSummary = createMachineSessionPersistenceSummary(),
} = {}) {
const launchMethods = Array.isArray(launchApiManifest?.methods) ? launchApiManifest.methods : [];
const controlPageMethods = Array.isArray(controlPageApiManifest?.methods) ? controlPageApiManifest.methods : [];
@@ -1621,11 +1691,15 @@ export function createIniPanelShellApiSurfaceInventory({
const sessionReadinessRows = Array.isArray(sessionReadinessWorkflowReport?.rows)
? sessionReadinessWorkflowReport.rows
: [];
const persistenceSummaryRows = Array.isArray(persistenceSummary?.rows)
? persistenceSummary.rows
: [];
const ready = Boolean(
launchApiManifest?.apiName
&& controlPageApiManifest?.apiName
&& workflowOverviewContract?.apiName
&& sessionReadinessWorkflowReport?.apiName,
&& sessionReadinessWorkflowReport?.apiName
&& persistenceSummary?.apiName,
);
return {
@@ -1639,12 +1713,14 @@ export function createIniPanelShellApiSurfaceInventory({
workflowOverviewMethods: workflowOverviewMethods.length,
persistenceCapabilities: persistenceCapabilities.length,
sessionReadinessRows: sessionReadinessRows.length,
persistenceSummaryRows: persistenceSummaryRows.length,
},
apiNames: {
launch: launchApiManifest?.apiName ?? null,
controlPage: controlPageApiManifest?.apiName ?? null,
workflowOverview: workflowOverviewContract?.apiName ?? null,
sessionReadinessReport: sessionReadinessWorkflowReport?.apiName ?? null,
persistenceSummary: persistenceSummary?.apiName ?? null,
},
entries: [
{
@@ -1671,6 +1747,12 @@ export function createIniPanelShellApiSurfaceInventory({
apiName: sessionReadinessWorkflowReport?.apiName ?? null,
count: sessionReadinessRows.length,
},
{
id: "persistence-summary",
label: "Persistence summary",
apiName: persistenceSummary?.apiName ?? null,
count: persistenceSummaryRows.length,
},
{
id: "persistence-capabilities",
label: "Persistence capabilities",
@@ -1699,6 +1781,11 @@ export function createIniPanelShellApiSurfaceInventory({
label: "Session readiness report",
value: sessionReadinessWorkflowReport?.apiName ?? "missing",
},
{
id: "persistence-summary",
label: "Persistence summary",
value: persistenceSummary?.apiName ?? "missing",
},
{
id: "persistence-capabilities",
label: "Persistence capabilities",
@@ -1777,6 +1864,7 @@ export function createIniPanelShellWorkflowOverviewContract() {
"getWorkflowOverviewSummary",
"getWorkflowOverviewSessionReadinessReport",
"getWorkflowOverviewApiSurfaceInventory",
"getWorkflowOverviewPersistenceSummary",
"getWorkflowOverviewDomContract",
"getWorkflowOverviewDomReadiness",
"renderWorkflowOverview",
@@ -2668,7 +2756,10 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
shellSessionHandoffPackageMountDisplayViewModel,
);
const shellSessionHandoffMountDomContract = createIniPanelShellSessionHandoffMountDomContract();
const shellSessionHandoffWorkflowSummary = createIniPanelShellSessionHandoffWorkflowSummary({
const shellSessionHandoffReadonlyStatusResult = readIniPanelShellSessionHandoffStatusFromControlPage({
handoffSummary: shellSessionHandoffSummary,
});
const baseShellSessionHandoffWorkflowSummary = createIniPanelShellSessionHandoffWorkflowSummary({
mountWorkflow: {
phase: shellSessionHandoffPackageMountRenderState.phase,
ready: shellSessionHandoffPackageMountRenderState.ready,
@@ -2679,12 +2770,27 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
ready: shellSessionHandoffPackageMountState.ready,
statusLine: shellSessionHandoffPackageMountState.statusLine,
},
readonlyStatusResult: readIniPanelShellSessionHandoffStatusFromControlPage({
handoffSummary: shellSessionHandoffSummary,
}),
readonlyStatusResult: shellSessionHandoffReadonlyStatusResult,
});
const shellSessionHandoffWorkflowDomContract = createIniPanelShellSessionHandoffWorkflowDomContract();
const shellWorkflowOverviewContract = createIniPanelShellWorkflowOverviewContract();
const shellSessionReadinessWorkflowReport = createIniPanelShellSessionReadinessWorkflowReport({
workflowSummary: shellSessionHandoffWorkflowSummary,
workflowSummary: baseShellSessionHandoffWorkflowSummary,
});
const shellPersistenceSummary = createMachineSessionPersistenceSummary();
const shellApiSurfaceInventory = createIniPanelShellApiSurfaceInventory({
launchApiManifest: shellIntegrationManifest.launchApiManifest,
controlPageApiManifest: shellIntegrationManifest.controlPageApiManifest,
workflowOverviewContract: shellWorkflowOverviewContract,
sessionReadinessWorkflowReport: shellSessionReadinessWorkflowReport,
persistenceSummary: shellPersistenceSummary,
});
const shellSessionHandoffWorkflowSummary = createIniPanelShellSessionHandoffWorkflowSummary({
mountWorkflow: baseShellSessionHandoffWorkflowSummary.mountWorkflow,
controlPageMountWorkflow: baseShellSessionHandoffWorkflowSummary.controlPageMountWorkflow,
readonlyStatusResult: shellSessionHandoffReadonlyStatusResult,
persistenceSummary: shellPersistenceSummary,
apiSurfaceInventory: shellApiSurfaceInventory,
});
const shellSessionHandoffWorkflowDisplayViewModel = createIniPanelShellSessionHandoffWorkflowDisplayViewModel(
shellSessionHandoffWorkflowSummary,
@@ -2692,14 +2798,6 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
const shellSessionHandoffWorkflowRenderState = createIniPanelShellSessionHandoffWorkflowRenderState(
shellSessionHandoffWorkflowDisplayViewModel,
);
const shellSessionHandoffWorkflowDomContract = createIniPanelShellSessionHandoffWorkflowDomContract();
const shellWorkflowOverviewContract = createIniPanelShellWorkflowOverviewContract();
const shellApiSurfaceInventory = createIniPanelShellApiSurfaceInventory({
launchApiManifest: shellIntegrationManifest.launchApiManifest,
controlPageApiManifest: shellIntegrationManifest.controlPageApiManifest,
workflowOverviewContract: shellWorkflowOverviewContract,
sessionReadinessWorkflowReport: shellSessionReadinessWorkflowReport,
});
return {
pages: entries.map(({ id, href, title }) => ({
id,
@@ -2734,6 +2832,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
shellSessionHandoffWorkflowRenderState,
shellSessionHandoffWorkflowDomContract,
shellWorkflowOverviewContract,
shellPersistenceSummary,
shellApiSurfaceInventory,
launchApiSchema: createIniPanelLaunchApiSchema(),
launchApiManifest: createIniPanelLaunchApiManifest(entries),
@@ -2764,6 +2863,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
mountShellSessionHandoffFromControlPage: mountIniPanelShellSessionHandoffFromControlPage,
readShellSessionHandoffStatusFromControlPage: readIniPanelShellSessionHandoffStatusFromControlPage,
createShellSessionHandoffWorkflowSummary: createIniPanelShellSessionHandoffWorkflowSummary,
createMachineSessionPersistenceSummary,
createShellSessionReadinessWorkflowReport: createIniPanelShellSessionReadinessWorkflowReport,
createShellApiSurfaceInventory: createIniPanelShellApiSurfaceInventory,
createShellSessionHandoffWorkflowDisplayViewModel: createIniPanelShellSessionHandoffWorkflowDisplayViewModel,

View File

@@ -96,6 +96,7 @@
createIniPanelShellWorkflowOverviewEmbeddingDomContract,
createIniPanelShellWorkflowOverviewEmbeddingDomReadiness,
mountIniPanelShellWorkflowOverviewEmbedding,
createMachineSessionPersistenceSummary,
renderIniPanelShellWorkflowOverviewEmbeddingState,
mountIniPanelShellWorkflowOverviewEmbeddingState,
createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel,
@@ -129,6 +130,9 @@
sessionReadinessWorkflowReport: shellViewModel.shellSessionReadinessWorkflowReport,
})
),
getWorkflowOverviewPersistenceSummary: (options) => (
createMachineSessionPersistenceSummary(options)
),
getWorkflowOverviewDomContract: (options) => (
createIniPanelShellSessionHandoffWorkflowDomContract(options)
),

View File

@@ -101,6 +101,7 @@
!launchApi?.getShellSessionHandoffPackageMountRenderState ||
!launchApi?.getShellSessionReadinessWorkflowReport ||
!launchApi?.getShellApiSurfaceInventory ||
!launchApi?.getMachineSessionPersistenceSummary ||
!launchApi?.renderShellSessionHandoffMountState ||
!launchApi?.getWorkflowOverviewContract ||
!launchApi?.getWorkflowOverviewReadiness ||
@@ -182,6 +183,7 @@
const shellSessionHandoffPackageMountRenderState = launchApi.getShellSessionHandoffPackageMountRenderState();
const shellSessionReadinessWorkflowReport = launchApi.getShellSessionReadinessWorkflowReport();
const shellApiSurfaceInventory = launchApi.getShellApiSurfaceInventory();
const launchPersistenceSummary = launchApi.getMachineSessionPersistenceSummary();
const workflowOverviewContract = launchApi.getWorkflowOverviewContract();
assertEqual(
shellIntegrationSchema.manifestVersion,
@@ -448,6 +450,21 @@
3,
"shell API surface inventory session readiness rows",
);
assertEqual(
shellApiSurfaceInventory.counts.persistenceSummaryRows,
7,
"shell API surface inventory persistence summary rows",
);
assertEqual(
launchPersistenceSummary.apiName,
"machine-session-persistence-summary",
"launch API persistence summary API",
);
assertEqual(
launchPersistenceSummary.rows.length,
7,
"launch API persistence summary rows",
);
assertEqual(
workflowOverviewContract.pageHref,
"./workflow-overview.html",
@@ -709,12 +726,12 @@
assertEqual(workflowOverviewEmbedding.pageHref, "./workflow-overview.html", "workflow overview embedding page");
assertEqual(workflowOverviewEmbedding.ready, false, "workflow overview embedding default readiness");
assertEqual(workflowOverviewEmbedding.renderState.dataset.handoffScope, "workflow", "workflow overview embedding scope");
assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 5, "workflow overview embedding rows");
assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 8, "workflow overview embedding rows");
assertEqual(workflowOverviewEmbedding.error, null, "workflow overview embedding error");
assertEqual(workflowOverviewEmbeddingReport.apiName, "ini-panel-shell-workflow-overview-embedding-report", "workflow overview embedding report API name");
assertEqual(workflowOverviewEmbeddingReport.ready, false, "workflow overview embedding report readiness");
assertEqual(workflowOverviewEmbeddingReport.missingMethods.join(","), "", "workflow overview embedding report missing methods");
assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 5, "workflow overview embedding report row count");
assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 8, "workflow overview embedding report row count");
assertEqual(workflowOverviewEmbeddingReport.rows.length, 6, "workflow overview embedding report rows");
assertEqual(workflowOverviewEmbeddingDisplay.title, "Workflow overview embedding", "workflow overview embedding display title");
assertEqual(workflowOverviewEmbeddingDisplay.statusText, "Blocked", "workflow overview embedding display status");

View File

@@ -372,11 +372,11 @@ TOOL_TABLE = browser-shell-tool.tbl
assertEqual(workflowOverviewReadiness.missing.join(","), "", "shell workflow overview iframe missing methods");
assertEqual(workflowOverviewEmbedding.apiName, "ini-panel-shell-workflow-overview-embedding", "shell workflow overview embedding API");
assertEqual(workflowOverviewEmbedding.renderState.dataset.handoffScope, "workflow", "shell workflow overview embedding scope");
assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 5, "shell workflow overview embedding rows");
assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 8, "shell workflow overview embedding rows");
assertEqual(workflowOverviewEmbedding.error, null, "shell workflow overview embedding error");
assertEqual(workflowOverviewEmbeddingReport.ready, false, "shell workflow overview embedding report readiness");
assertEqual(workflowOverviewEmbeddingReport.rows.length, 6, "shell workflow overview embedding report rows");
assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 5, "shell workflow overview embedding report row count");
assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 8, "shell workflow overview embedding report row count");
assertEqual(workflowOverviewEmbeddingReport.error, null, "shell workflow overview embedding report error");
assertEqual(workflowOverviewEmbeddingDisplay.statusText, "Blocked", "shell workflow overview embedding display status");
assertEqual(workflowOverviewEmbeddingDisplay.rows.length, 6, "shell workflow overview embedding display rows");
@@ -508,16 +508,27 @@ TOOL_TABLE = browser-shell-tool.tbl
const readonlyStatusResult = launchApi.readShellSessionHandoffStatusFromControlPage({
controlPageApi,
});
const workflowSummaryResult = launchApi.getShellSessionHandoffWorkflowSummary({
const baseWorkflowSummaryResult = launchApi.getShellSessionHandoffWorkflowSummary({
mountWorkflow: readyMountWorkflow,
controlPageMountWorkflow: readyControlPageMountWorkflow,
readonlyStatusResult,
});
const sessionReadinessWorkflowReport = launchApi.getShellSessionReadinessWorkflowReport({
workflowSummary: workflowSummaryResult,
workflowSummary: baseWorkflowSummaryResult,
});
const shellApiSurfaceInventory = launchApi.getShellApiSurfaceInventory({
sessionReadinessWorkflowReport,
persistenceSummary: panelDoc.defaultView.linuxCncIniPanelApi.getMachineSessionPersistenceSummary(),
});
const persistenceSummary = panelDoc.defaultView.linuxCncIniPanelApi.getMachineSessionPersistenceSummary({
readonlyStatus,
});
const workflowSummaryResult = launchApi.getShellSessionHandoffWorkflowSummary({
mountWorkflow: readyMountWorkflow,
controlPageMountWorkflow: readyControlPageMountWorkflow,
readonlyStatusResult,
persistenceSummary,
apiSurfaceInventory: shellApiSurfaceInventory,
});
const workflowDisplayResult = launchApi.getShellSessionHandoffWorkflowDisplayViewModel(
workflowSummaryResult,
@@ -576,6 +587,21 @@ TOOL_TABLE = browser-shell-tool.tbl
3,
"shell handoff API surface inventory session readiness rows",
);
assertEqual(
shellApiSurfaceInventory.counts.persistenceSummaryRows,
7,
"shell handoff API surface inventory persistence summary rows",
);
assertEqual(
workflowSummaryResult.rows.some((row) => row.id === "persistence-summary" && row.value === "ready: 7 rows"),
true,
"shell handoff workflow summary persistence summary row",
);
assertEqual(
workflowSummaryResult.rows.some((row) => row.id === "api-surface-inventory" && row.value === "ready: 6 rows"),
true,
"shell handoff workflow summary API surface inventory row",
);
assertEqual(
workflowSummaryResult.sessionReadiness.ready,
true,
@@ -595,7 +621,7 @@ TOOL_TABLE = browser-shell-tool.tbl
assertEqual(workflowRenderState.dataset.handoffScope, "workflow", "shell handoff workflow render-state scope");
assertEqual(externalWorkflowDomReadiness.ready, true, "shell handoff workflow DOM readiness");
assertEqual(workflowDomMountResult.phase, "ready", "shell handoff workflow DOM mount phase");
assertEqual(workflowDomMountResult.renderResult.rowCount, 6, "shell handoff workflow DOM mount rows");
assertEqual(workflowDomMountResult.renderResult.rowCount, 8, "shell handoff workflow DOM mount rows");
assertEqual(
document.querySelector("[data-external-shell-workflow]").dataset.handoffScope,
"workflow",

View File

@@ -376,6 +376,9 @@ TOOL_TABLE = browser-tool.tbl
!uiApi?.getMachineSessionStateReport ||
!uiApi?.getMachineSessionStateReportExport ||
!uiApi?.getMachineSessionWorkflowStatus ||
!uiApi?.getMachineSessionPersistenceSummary ||
!uiApi?.getMachineSessionPersistenceDisplayViewModel ||
!uiApi?.getMachineSessionPersistenceRenderState ||
!uiApi?.renderMachineSessionControlView
) {
throw new Error("INI panel API namespace did not expose state getters");
@@ -813,6 +816,44 @@ TOOL_TABLE = browser-tool.tbl
2,
"UI machine session workflow status canonical event count",
);
const machineSessionPersistenceSummary = uiApi.getMachineSessionPersistenceSummary();
assertEqual(
machineSessionPersistenceSummary.apiName,
"machine-session-persistence-summary",
"UI machine session persistence summary API name",
);
assertEqual(
machineSessionPersistenceSummary.ready,
true,
"UI machine session persistence summary readiness",
);
assertEqual(
machineSessionPersistenceSummary.rows.find((row) => row.id === "machine-files")?.value,
"ini, parameters, tool-table, g-code",
"UI machine session persistence summary machine files",
);
assertEqual(
machineSessionPersistenceSummary.rows.find((row) => row.id === "session-load")?.value,
"loaded: /work/session-machine.ini",
"UI machine session persistence summary session load",
);
const machineSessionPersistenceDisplay = uiApi.getMachineSessionPersistenceDisplayViewModel();
assertEqual(
machineSessionPersistenceDisplay.statusText,
"Ready",
"UI machine session persistence display status",
);
const machineSessionPersistenceRenderState = uiApi.getMachineSessionPersistenceRenderState();
assertEqual(
machineSessionPersistenceRenderState.statusLine,
"Ready: No missing persistence requirements",
"UI machine session persistence render-state status",
);
assertEqual(
machineSessionPersistenceRenderState.dataset.persistenceScope,
"opfs-session",
"UI machine session persistence render-state scope",
);
const machineSessionStateBundle = uiApi.getMachineSessionStateBundle();
assertEqual(
machineSessionStateBundle.report.status.lastAction,

View File

@@ -33,6 +33,7 @@
!workflowApi?.getWorkflowOverviewSummary ||
!workflowApi?.getWorkflowOverviewSessionReadinessReport ||
!workflowApi?.getWorkflowOverviewApiSurfaceInventory ||
!workflowApi?.getWorkflowOverviewPersistenceSummary ||
!workflowApi?.getWorkflowOverviewDomContract ||
!workflowApi?.getWorkflowOverviewDomReadiness ||
!workflowApi?.renderWorkflowOverview ||
@@ -60,6 +61,7 @@
const summary = workflowApi.getWorkflowOverviewSummary();
const sessionReadinessReport = workflowApi.getWorkflowOverviewSessionReadinessReport();
const apiSurfaceInventory = workflowApi.getWorkflowOverviewApiSurfaceInventory();
const persistenceSummary = workflowApi.getWorkflowOverviewPersistenceSummary();
const domContract = workflowApi.getWorkflowOverviewDomContract();
const domReadiness = workflowApi.getWorkflowOverviewDomReadiness();
const renderResult = workflowApi.renderWorkflowOverview();
@@ -93,19 +95,30 @@
"ini-panel-shell-api-surface-inventory",
"workflow overview API surface inventory API name",
);
assertEqual(apiSurfaceInventory.rows.length, 5, "workflow overview API surface inventory rows");
assertEqual(apiSurfaceInventory.rows.length, 6, "workflow overview API surface inventory rows");
assertEqual(
apiSurfaceInventory.counts.persistenceSummaryRows,
7,
"workflow overview API surface inventory persistence summary rows",
);
assertEqual(
persistenceSummary.apiName,
"machine-session-persistence-summary",
"workflow overview persistence summary API name",
);
assertEqual(persistenceSummary.rows.length, 7, "workflow overview persistence summary rows");
assertEqual(displayViewModel.title, "Shell handoff workflow", "workflow overview display title");
assertEqual(renderState.renderStateVersion, 1, "workflow overview render-state version");
assertEqual(renderState.dataset.handoffScope, "workflow", "workflow overview render-state scope");
assertEqual(domContract.selectors.mount, "[data-handoff-workflow]", "workflow overview DOM contract mount selector");
assertEqual(domReadiness.ready, true, "workflow overview DOM readiness");
assertEqual(renderResult.rendered, true, "workflow overview render result");
assertEqual(renderResult.rowCount, 5, "workflow overview render row count");
assertEqual(renderResult.rowCount, 8, "workflow overview render row count");
assertEqual(mountResult.ready, false, "workflow overview mount result readiness");
assertEqual(mountResult.renderResult.rendered, true, "workflow overview mount result rendered");
assertEqual(embeddingReport.apiName, "ini-panel-shell-workflow-overview-embedding-report", "workflow overview embedding report API name");
assertEqual(embeddingReport.ready, false, "workflow overview embedding report readiness");
assertEqual(embeddingReport.renderRowCount, 5, "workflow overview embedding report row count");
assertEqual(embeddingReport.renderRowCount, 8, "workflow overview embedding report row count");
assertEqual(embeddingReport.rows.length, 6, "workflow overview embedding report rows");
assertEqual(embeddingDisplay.title, "Workflow overview embedding", "workflow overview embedding display title");
assertEqual(embeddingDisplay.statusText, "Blocked", "workflow overview embedding display status");
@@ -191,6 +204,16 @@
true,
"workflow overview DOM blocking reasons row",
);
assertEqual(
workflowDoc.querySelector('[data-handoff-workflow-value="api-surface-inventory"]').textContent,
"ready: 6 rows",
"workflow overview DOM API surface inventory row",
);
assertEqual(
workflowDoc.querySelector('[data-handoff-workflow-value="persistence-summary"]').textContent,
"waiting: 5 missing",
"workflow overview DOM persistence summary row",
);
document.getElementById("status").textContent = "browser_ini_workflow_overview_smoke=ok";
console.log("browser_ini_workflow_overview_smoke=ok");

View File

@@ -4,22 +4,41 @@ import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
INI_PANEL_ENTRIES,
analyzeIniRuntimeBoundaries,
createControlPageBrowserApiManifest,
createControlPageBrowserApiMethods,
createControlPageBrowserApiSchema,
createIniPanelLaunchApiManifest,
createIniPanelLauncherLinkViewModel,
createIniPanelShellApiSurfaceInventory,
createIniPanelShellControlPageContract,
createIniPanelShellIntegrationManifest,
createIniPanelShellIntegrationReadiness,
createIniPanelShellIntegrationSchema,
createIniPanelShellIntegrationWorkflowPlan,
createIniPanelShellSessionReadinessWorkflowReport,
createIniPanelShellViewModel,
createIniPanelShellWorkflowOverviewContract,
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
createLinuxCncIniSdk,
createLinuxCncInterpSdk,
createMachineSessionSnapshotPayload,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createSessionSnapshot,
defaultMachinePaths,
gcodeFilenameFromProgramPath,
gcodeProgramPath,
getIniPanelEntryByHref,
getIniPanelEntryManifest,
getOpfsRoot,
getVisibleIniPanelEntries,
isSupportedIniPanelControlPageApiManifest,
isSupportedIniPanelLaunchApiManifest,
isSupportedIniPanelShellIntegrationManifest,
loadMachineSessionFromOpfs,
loadMachineSessionSnapshot,
loadMachineTextFiles,
@@ -56,6 +75,13 @@ const requiredExports = [
["planIniFileContextStaging", planIniFileContextStaging],
["planSimConfigStaging", planSimConfigStaging],
["analyzeIniRuntimeBoundaries", analyzeIniRuntimeBoundaries],
["getIniPanelEntryManifest", getIniPanelEntryManifest],
["getIniPanelEntryByHref", getIniPanelEntryByHref],
["getVisibleIniPanelEntries", getVisibleIniPanelEntries],
["createIniPanelLauncherLinkViewModel", createIniPanelLauncherLinkViewModel],
["createMachineSessionPersistenceDisplayViewModel", createMachineSessionPersistenceDisplayViewModel],
["createMachineSessionPersistenceRenderState", createMachineSessionPersistenceRenderState],
["createMachineSessionPersistenceSummary", createMachineSessionPersistenceSummary],
["getOpfsRoot", getOpfsRoot],
["saveTextFile", saveTextFile],
["loadTextFile", loadTextFile],
@@ -80,11 +106,22 @@ const requiredExports = [
["loadMachineToolTableFromOpfs", loadMachineToolTableFromOpfs],
["loadMachineSessionFromOpfs", loadMachineSessionFromOpfs],
["readMachineSessionReadiness", readMachineSessionReadiness],
["createControlPageBrowserApiSchema", createControlPageBrowserApiSchema],
["createControlPageBrowserApiManifest", createControlPageBrowserApiManifest],
["createControlPageBrowserApiMethods", createControlPageBrowserApiMethods],
["createIniPanelShellControlPageContract", createIniPanelShellControlPageContract],
["isSupportedIniPanelControlPageApiManifest", isSupportedIniPanelControlPageApiManifest],
["createIniPanelLaunchApiManifest", createIniPanelLaunchApiManifest],
["isSupportedIniPanelLaunchApiManifest", isSupportedIniPanelLaunchApiManifest],
["createIniPanelShellIntegrationSchema", createIniPanelShellIntegrationSchema],
["createIniPanelShellIntegrationManifest", createIniPanelShellIntegrationManifest],
["createIniPanelShellIntegrationReadiness", createIniPanelShellIntegrationReadiness],
["createIniPanelShellIntegrationWorkflowPlan", createIniPanelShellIntegrationWorkflowPlan],
["isSupportedIniPanelShellIntegrationManifest", isSupportedIniPanelShellIntegrationManifest],
["createIniPanelShellViewModel", createIniPanelShellViewModel],
["createIniPanelShellApiSurfaceInventory", createIniPanelShellApiSurfaceInventory],
["createIniPanelShellSessionReadinessWorkflowReport", createIniPanelShellSessionReadinessWorkflowReport],
["createIniPanelShellWorkflowOverviewContract", createIniPanelShellWorkflowOverviewContract],
[
"createIniPanelShellWorkflowOverviewEmbeddingMountDomContract",
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
@@ -116,12 +153,80 @@ assert.equal(sessionSnapshotPath("sdk-session"), "linuxcnc/sessions/sdk-session/
assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc/machines/sdk-mill/tool.tbl");
assert.equal(gcodeFilenameFromProgramPath("linuxcnc/gcode/demo.ngc"), "demo.ngc");
assert.deepEqual(getIniPanelEntryManifest(), INI_PANEL_ENTRIES);
assert.deepEqual(getVisibleIniPanelEntries(), INI_PANEL_ENTRIES);
assert.deepEqual(getIniPanelEntryByHref("./control-page.html"), {
id: "control-view",
title: "Open read-only control view",
href: "./control-page.html",
});
assert.deepEqual(createIniPanelLauncherLinkViewModel(), [
{
entryId: "edit-run",
href: "./index.html",
label: "Open edit and run panel",
},
{
entryId: "control-view",
href: "./control-page.html",
label: "Open read-only control view",
},
{
entryId: "workflow-overview",
href: "./workflow-overview.html",
label: "Open shell handoff overview",
},
]);
assert.equal(
createMachineSessionPersistenceSummary({
state: {
badges: { opfs: "OPFS: ready" },
fields: { sessionSnapshot: "sdk-session/snapshot.json", sessionIni: "/work/session-machine.ini" },
sessionLoad: { iniWasmPath: "/work/session-machine.ini" },
sessionReadiness: { phase: "ready", ready: true, missing: [] },
},
}).rows.find(({ id }) => id === "session-load")?.value,
"loaded: /work/session-machine.ini",
);
assert.equal(
createMachineSessionPersistenceRenderState(
createMachineSessionPersistenceDisplayViewModel(createMachineSessionPersistenceSummary()),
).apiName,
"machine-session-persistence-render-state",
);
const controlPageApiSchema = createControlPageBrowserApiSchema();
const controlPageApiManifest = createControlPageBrowserApiManifest();
assert.equal(controlPageApiSchema.apiName, "ini-panel-control-page");
assert.equal(isSupportedIniPanelControlPageApiManifest(controlPageApiManifest), true);
assert.deepEqual(controlPageApiManifest.methods, createControlPageBrowserApiMethods());
assert.equal(
createIniPanelShellControlPageContract().readonlyStatusApiManifest.apiName,
"machine-session-readonly-status",
);
const launchManifest = createIniPanelLaunchApiManifest();
assert.equal(isSupportedIniPanelLaunchApiManifest(launchManifest), true);
assert.equal(
launchManifest.methods.includes("mountWorkflowOverviewEmbeddingMountState"),
true,
);
assert.deepEqual(
launchManifest.visibleEntries,
getVisibleIniPanelEntries().map(({ id, href, title }) => ({ id, href, title })),
);
const shellIntegrationManifest = createIniPanelShellIntegrationManifest();
assert.equal(createIniPanelShellIntegrationSchema().apiName, "ini-panel-shell-integration");
assert.equal(isSupportedIniPanelShellIntegrationManifest(shellIntegrationManifest), true);
assert.equal(createIniPanelShellIntegrationReadiness(shellIntegrationManifest).ready, true);
assert.equal(createIniPanelShellIntegrationWorkflowPlan({
manifest: shellIntegrationManifest,
}).ready, true);
assert.equal(
createIniPanelShellWorkflowOverviewContract().methodNames.includes("getWorkflowOverviewApiSurfaceInventory"),
true,
);
const shellViewModel = createIniPanelShellViewModel();
assert.equal(
@@ -140,6 +245,19 @@ assert.equal(
createIniPanelShellApiSurfaceInventory().apiName,
"ini-panel-shell-api-surface-inventory",
);
assert.equal(shellViewModel.shellApiSurfaceInventory.ready, true);
assert.equal(
shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(
({ id }) => id === "persistence-summary",
)?.value,
"waiting: 5 missing",
);
assert.equal(
shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(
({ id }) => id === "api-surface-inventory",
)?.value,
"ready: 6 rows",
);
assert.equal(
shellViewModel.mountWorkflowOverviewEmbeddingMountState,
mountIniPanelShellWorkflowOverviewEmbeddingMountState,

View File

@@ -167,6 +167,9 @@ const requiredShellExports = [
"createIniPanelShellViewModel",
"getIniPanelEntryByHref",
"getVisibleIniPanelEntries",
"createMachineSessionPersistenceDisplayViewModel",
"createMachineSessionPersistenceRenderState",
"createMachineSessionPersistenceSummary",
"isSupportedIniPanelControlPageApiManifest",
"isSupportedIniPanelLaunchApiManifest",
"isSupportedIniPanelShellIntegrationManifest",

View File

@@ -69,6 +69,7 @@ assert.deepEqual(manifest, {
"mountShellSessionHandoff",
"mountShellSessionHandoffFromControlPage",
"readShellSessionHandoffStatusFromControlPage",
"getMachineSessionPersistenceSummary",
"getShellSessionHandoffWorkflowSummary",
"getShellSessionReadinessWorkflowReport",
"getShellApiSurfaceInventory",
@@ -227,6 +228,7 @@ assert.match(launchText, /\brenderShellSessionHandoffMountState\b/);
assert.match(launchText, /\bmountShellSessionHandoff\b/);
assert.match(launchText, /\bmountShellSessionHandoffFromControlPage\b/);
assert.match(launchText, /\breadShellSessionHandoffStatusFromControlPage\b/);
assert.match(launchText, /\bgetMachineSessionPersistenceSummary\b/);
assert.match(launchText, /\bgetShellSessionHandoffWorkflowSummary\b/);
assert.match(launchText, /\bgetShellSessionReadinessWorkflowReport\b/);
assert.match(launchText, /\bgetShellApiSurfaceInventory\b/);

View File

@@ -179,7 +179,7 @@ assert.deepEqual(readiness, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 57,
launchMethods: 58,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -242,7 +242,7 @@ assert.deepEqual(handoffSummary, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 57,
launchMethods: 58,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -779,6 +779,7 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), {
"getWorkflowOverviewSummary",
"getWorkflowOverviewSessionReadinessReport",
"getWorkflowOverviewApiSurfaceInventory",
"getWorkflowOverviewPersistenceSummary",
"getWorkflowOverviewDomContract",
"getWorkflowOverviewDomReadiness",
"renderWorkflowOverview",
@@ -827,6 +828,7 @@ assert.deepEqual(
[
"getWorkflowOverviewDisplayViewModel",
"getWorkflowOverviewSummary",
"getWorkflowOverviewPersistenceSummary",
"getWorkflowOverviewDomContract",
"getWorkflowOverviewDomReadiness",
"renderWorkflowOverview",
@@ -1246,7 +1248,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 57,
launchMethods: 58,
controlPageMethods: 11,
persistenceCapabilities: 3,
},

View File

@@ -3,6 +3,9 @@ import assert from "node:assert/strict";
import {
createIniPanelStateSummary,
createMachineSessionControlView,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createMachineSessionStateSnapshot,
createMachineSessionStateBundle,
createMachineSessionStateReport,
@@ -566,6 +569,145 @@ const expectedBundleReport = {
},
};
const readyPersistenceSummary = createMachineSessionPersistenceSummary({
state: {
badges: {
ini: "INI WASM: ready",
interp: "Interpreter WASM: ready",
opfs: "OPFS: ready",
},
machineFiles: {
iniOpfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini",
parameterOpfsPath: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
toolTableOpfsPath: "linuxcnc/machines/xyzab-tdr/tool.tbl",
gcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
},
fields: {
sessionSnapshot: "ui-machine-session/ui-machine-session.json",
sessionIni: "/work/session-machine.ini",
sessionParameters: "/work/session-linuxcnc.var",
sessionToolTable: "/work/session-tool.tbl",
sessionGcode: "linuxcnc/gcode/ui-session.ngc",
},
sessionLoad: {
iniWasmPath: "/work/session-machine.ini",
parameterWasmPath: "/work/session-linuxcnc.var",
toolTableWasmPath: "/work/session-tool.tbl",
},
sessionReadiness: sessionReadinessFixture,
},
readonlyStatus: {
phase: "ready",
ready: true,
},
handoffWorkflowSummary: {
phase: "ready",
ready: true,
},
});
assert.deepEqual(
readyPersistenceSummary,
{
apiName: "machine-session-persistence-summary",
summaryVersion: 1,
phase: "ready",
ready: true,
checks: {
opfs: true,
machineFiles: true,
sessionSnapshot: true,
sessionReadiness: true,
sessionLoad: true,
},
missing: [],
counts: {
machineFiles: 4,
missing: 0,
},
rows: [
{
id: "opfs",
label: "OPFS",
value: "OPFS: ready",
},
{
id: "machine-files",
label: "Machine files",
value: "ini, parameters, tool-table, g-code",
},
{
id: "session-snapshot",
label: "Session snapshot",
value: "ui-machine-session/ui-machine-session.json",
},
{
id: "session-readiness",
label: "Session readiness",
value: "ready",
},
{
id: "session-load",
label: "Session load",
value: "loaded: /work/session-machine.ini",
},
{
id: "readonly-status",
label: "Readonly status",
value: "ready",
},
{
id: "handoff-readiness",
label: "Handoff readiness",
value: "ready",
},
],
},
);
assert.deepEqual(
createMachineSessionPersistenceSummary().missing,
["opfs", "machineFiles", "sessionSnapshot", "sessionReadiness", "sessionLoad"],
);
assert.deepEqual(
createMachineSessionPersistenceDisplayViewModel(readyPersistenceSummary),
{
phase: "ready",
title: "OPFS/session persistence",
statusText: "Ready",
detailText: "No missing persistence requirements",
rows: readyPersistenceSummary.rows,
},
);
assert.deepEqual(
createMachineSessionPersistenceRenderState(
createMachineSessionPersistenceDisplayViewModel(readyPersistenceSummary),
),
{
apiName: "machine-session-persistence-render-state",
renderStateVersion: 1,
phase: "ready",
ready: true,
title: "OPFS/session persistence",
statusText: "Ready",
detailText: "No missing persistence requirements",
statusLine: "Ready: No missing persistence requirements",
rows: readyPersistenceSummary.rows,
dataset: {
persistencePhase: "ready",
persistenceReady: "true",
persistenceScope: "opfs-session",
},
},
);
assert.equal(
createMachineSessionPersistenceRenderState().statusLine,
"Waiting: opfs, machineFiles, sessionSnapshot, sessionReadiness, sessionLoad",
);
assert.deepEqual(
createMachineSessionWorkflowOverview(expectedBundleReport),
expectedWorkflowOverview,

View File

@@ -67,6 +67,9 @@ import {
createControlPageRefreshState,
createControlPageSessionLoadState,
createMachineSessionControlPageController,
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
getIniPanelEntryByHref,
getIniPanelEntryManifest,
getVisibleIniPanelEntries,
@@ -104,6 +107,9 @@ assert.deepEqual(createControlPageSessionLoadState({ phase: "waiting-panel" }),
});
assert.equal(getIniPanelEntryByHref("./index.html")?.id, "edit-run");
assert.equal(typeof createMachineSessionControlPageController, "function");
assert.equal(typeof createMachineSessionPersistenceDisplayViewModel, "function");
assert.equal(typeof createMachineSessionPersistenceRenderState, "function");
assert.equal(typeof createMachineSessionPersistenceSummary, "function");
assert.equal(typeof createMachineSessionReadonlyStatus, "function");
assert.equal(typeof createMachineSessionReadonlyStatusApiManifest, "function");
assert.equal(typeof createControlPageBrowserApiManifest, "function");
@@ -667,6 +673,7 @@ assert.deepEqual(shellViewModel.shellWorkflowOverviewContract, {
"getWorkflowOverviewSummary",
"getWorkflowOverviewSessionReadinessReport",
"getWorkflowOverviewApiSurfaceInventory",
"getWorkflowOverviewPersistenceSummary",
"getWorkflowOverviewDomContract",
"getWorkflowOverviewDomReadiness",
"renderWorkflowOverview",
@@ -1113,13 +1120,17 @@ assert.equal(workflowSummaryResult.apiName, "ini-panel-shell-session-handoff-wor
assert.equal(workflowSummaryResult.summaryVersion, 1);
assert.equal(workflowSummaryResult.phase, "blocked");
assert.equal(workflowSummaryResult.ready, false);
assert.deepEqual(workflowSummaryResult.apiSurfaceInventory, null);
assert.deepEqual(workflowSummaryResult.persistenceSummary, null);
assert.deepEqual(workflowSummaryResult.sessionReadiness, {
phase: "ready",
ready: true,
missing: [],
});
assert.equal(workflowSummaryResult.rows.find(({ id }) => id === "session-readiness")?.value, "ready");
assert.equal(workflowSummaryResult.rows.length, 6);
assert.equal(workflowSummaryResult.rows.find(({ id }) => id === "persistence-summary")?.value, "missing");
assert.equal(workflowSummaryResult.rows.find(({ id }) => id === "api-surface-inventory")?.value, "missing");
assert.equal(workflowSummaryResult.rows.length, 8);
assert.equal(workflowSummaryResult.blockingReasons[0], "mount:blocked");
const sessionReadinessWorkflowReport = createIniPanelShellSessionReadinessWorkflowReport({
workflowSummary: workflowSummaryResult,
@@ -1137,7 +1148,18 @@ assert.equal(shellViewModel.shellSessionReadinessWorkflowReport.rows.length, 3);
assert.equal(shellViewModel.shellApiSurfaceInventory.apiName, "ini-panel-shell-api-surface-inventory");
assert.equal(shellViewModel.shellApiSurfaceInventory.ready, true);
assert.equal(shellViewModel.shellApiSurfaceInventory.counts.sessionReadinessRows, 3);
assert.equal(shellViewModel.shellApiSurfaceInventory.rows.length, 5);
assert.equal(shellViewModel.shellApiSurfaceInventory.counts.persistenceSummaryRows, 7);
assert.equal(shellViewModel.shellApiSurfaceInventory.rows.length, 6);
assert.equal(shellViewModel.shellPersistenceSummary.ready, false);
assert.equal(
shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(({ id }) => id === "persistence-summary")?.value,
"waiting: 5 missing",
);
assert.equal(shellViewModel.shellSessionHandoffWorkflowSummary.apiSurfaceInventory.ready, true);
assert.equal(
shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(({ id }) => id === "api-surface-inventory")?.value,
"ready: 6 rows",
);
const readyWorkflowSummaryResult = createIniPanelShellSessionHandoffWorkflowSummary({
mountWorkflow: {
phase: "ready",
@@ -1157,7 +1179,7 @@ assert.equal(workflowDisplayViewModel.phase, "blocked");
assert.equal(workflowDisplayViewModel.title, "Shell handoff workflow");
assert.equal(workflowDisplayViewModel.statusText, "Blocked");
assert.equal(workflowDisplayViewModel.detailText, "mount:blocked");
assert.equal(workflowDisplayViewModel.rows.length, 6);
assert.equal(workflowDisplayViewModel.rows.length, 8);
const readyWorkflowDisplayViewModel = createIniPanelShellSessionHandoffWorkflowDisplayViewModel(
readyWorkflowSummaryResult,
);
@@ -1172,7 +1194,7 @@ assert.equal(workflowRenderState.renderStateVersion, 1);
assert.equal(workflowRenderState.phase, "blocked");
assert.equal(workflowRenderState.statusLine, "Blocked: mount:blocked");
assert.equal(workflowRenderState.dataset.handoffScope, "workflow");
assert.equal(workflowRenderState.rows.length, 6);
assert.equal(workflowRenderState.rows.length, 8);
const readyWorkflowRenderState = createIniPanelShellSessionHandoffWorkflowRenderState(
readyWorkflowDisplayViewModel,
);
@@ -1215,13 +1237,15 @@ assert.deepEqual(
{
rendered: true,
statusLine: "Blocked: mount:blocked",
rowCount: 6,
rowCount: 8,
rowIds: [
"mount-workflow",
"control-page-mount-workflow",
"readonly-status-workflow",
"session-readiness",
"readonly-status-missing-fields",
"persistence-summary",
"api-surface-inventory",
"blocking-reasons",
],
dataset: {
@@ -1242,7 +1266,7 @@ assert.equal(workflowDomMountResult.apiName, "ini-panel-shell-session-handoff-wo
assert.equal(workflowDomMountResult.workflowVersion, 1);
assert.equal(workflowDomMountResult.phase, "blocked");
assert.equal(workflowDomMountResult.ready, false);
assert.equal(workflowDomMountResult.renderResult.rowCount, 6);
assert.equal(workflowDomMountResult.renderResult.rowCount, 8);
assert.equal(workflowDomMountResult.renderResult.dataset.handoffScope, "workflow");
assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.shellWorkflowOverviewContract);
assert.deepEqual(createIniPanelShellWorkflowOverviewReadiness(), {
@@ -1259,6 +1283,7 @@ const workflowOverviewApi = {
getWorkflowOverviewSummary: () => workflowSummaryResult,
getWorkflowOverviewSessionReadinessReport: () => sessionReadinessWorkflowReport,
getWorkflowOverviewApiSurfaceInventory: () => shellViewModel.shellApiSurfaceInventory,
getWorkflowOverviewPersistenceSummary: () => shellViewModel.shellPersistenceSummary,
getWorkflowOverviewDomContract: createIniPanelShellSessionHandoffWorkflowDomContract,
getWorkflowOverviewDomReadiness: createIniPanelShellSessionHandoffWorkflowDomReadiness,
renderWorkflowOverview: renderIniPanelShellSessionHandoffWorkflowState,
@@ -1291,7 +1316,7 @@ assert.equal(workflowOverviewEmbedding.apiName, "ini-panel-shell-workflow-overvi
assert.equal(workflowOverviewEmbedding.embeddingVersion, 1);
assert.equal(workflowOverviewEmbedding.ready, false);
assert.equal(workflowOverviewEmbedding.renderState.statusLine, "Blocked: mount:blocked");
assert.equal(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 6);
assert.equal(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 8);
assert.equal(workflowOverviewEmbedding.error, null);
const workflowOverviewEmbeddingReport = createIniPanelShellWorkflowOverviewEmbeddingReport(workflowOverviewEmbedding);
assert.deepEqual(workflowOverviewEmbeddingReport, {
@@ -1307,7 +1332,7 @@ assert.deepEqual(workflowOverviewEmbeddingReport, {
contract: shellViewModel.shellWorkflowOverviewContract,
missingMethods: [],
mountPhase: "blocked",
renderRowCount: 6,
renderRowCount: 8,
error: null,
blockingReasons: ["embedding:blocked", "mount:blocked"],
rows: [
@@ -1334,7 +1359,7 @@ assert.deepEqual(workflowOverviewEmbeddingReport, {
{
id: "render-row-count",
label: "Render row count",
value: "6",
value: "8",
},
{
id: "error",