推进 text12 接续和 control-page session readiness workflow
This commit is contained in:
@@ -15,6 +15,8 @@ import {
|
||||
getIniPanelEntryByHref,
|
||||
getVisibleIniPanelEntries,
|
||||
createMachineSessionControlPageController,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
createControlPageSessionLoadState,
|
||||
refreshMachineSessionControlPage,
|
||||
controlPageStatusText,
|
||||
renderMachineSessionControlView,
|
||||
@@ -57,6 +59,13 @@ refresh result with `phase`, `statusText`, `view`, `renderResult`, and `error`.
|
||||
the read-only status line, and `createControlPageRefreshState()` is the stable
|
||||
state-shape helper used by the workflow.
|
||||
|
||||
The control-page session workflow lives in
|
||||
`runtime/ui/ini-panel/control-page-session-workflow.js`. Use
|
||||
`loadMachineSessionForControlPageWorkflow()` when an outer shell needs a
|
||||
read-only readiness snapshot for the embedded panel API and first control view.
|
||||
`createControlPageSessionLoadState()` is the stable state-shape helper for
|
||||
`waiting-panel`, `waiting-api`, `waiting-session`, `ready`, and `error` phases.
|
||||
|
||||
## Boundary
|
||||
|
||||
The entry shell only links pages and renders already-observed panel state. It
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import {
|
||||
refreshMachineSessionControlPage,
|
||||
} from "./control-page-refresh-workflow.js";
|
||||
import {
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
} from "./control-page-session-workflow.js";
|
||||
|
||||
export function createMachineSessionControlPageController({
|
||||
panelFrame,
|
||||
@@ -50,6 +53,14 @@ export function createMachineSessionControlPageController({
|
||||
});
|
||||
}
|
||||
|
||||
function loadSessionState() {
|
||||
return loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow,
|
||||
getPanelApi,
|
||||
getControlView,
|
||||
});
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
if (!refreshTimer) {
|
||||
refreshTimer = setIntervalFn(render, pollMs);
|
||||
@@ -72,6 +83,7 @@ export function createMachineSessionControlPageController({
|
||||
getControlView,
|
||||
getPanelApi,
|
||||
getPanelWindow,
|
||||
loadSessionState,
|
||||
mount,
|
||||
refresh,
|
||||
render,
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
export function createControlPageSessionLoadState({
|
||||
phase,
|
||||
panelReady = false,
|
||||
apiReady = false,
|
||||
controlViewReady = false,
|
||||
stateBundle = null,
|
||||
workflowStatus = null,
|
||||
controlView = null,
|
||||
error = null,
|
||||
} = {}) {
|
||||
return {
|
||||
phase,
|
||||
panelReady,
|
||||
apiReady,
|
||||
controlViewReady,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
controlView,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow,
|
||||
getPanelApi,
|
||||
getControlView,
|
||||
} = {}) {
|
||||
if (typeof getPanelWindow !== "function") {
|
||||
throw new Error("A panel window getter is required.");
|
||||
}
|
||||
if (typeof getPanelApi !== "function") {
|
||||
throw new Error("A panel API getter is required.");
|
||||
}
|
||||
if (typeof getControlView !== "function") {
|
||||
throw new Error("A control view getter is required.");
|
||||
}
|
||||
|
||||
let panelWindow = null;
|
||||
let panelApi = null;
|
||||
let controlView = null;
|
||||
try {
|
||||
panelWindow = getPanelWindow();
|
||||
panelApi = getPanelApi();
|
||||
controlView = getControlView();
|
||||
} catch (error) {
|
||||
return createControlPageSessionLoadState({
|
||||
phase: "error",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
|
||||
if (!panelWindow) {
|
||||
return createControlPageSessionLoadState({
|
||||
phase: "waiting-panel",
|
||||
});
|
||||
}
|
||||
|
||||
if (!panelApi) {
|
||||
return createControlPageSessionLoadState({
|
||||
phase: "waiting-api",
|
||||
panelReady: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!controlView) {
|
||||
return createControlPageSessionLoadState({
|
||||
phase: "waiting-session",
|
||||
panelReady: true,
|
||||
apiReady: true,
|
||||
stateBundle: panelApi.getMachineSessionStateBundle?.() ?? null,
|
||||
workflowStatus: panelApi.getMachineSessionWorkflowStatus?.() ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
return createControlPageSessionLoadState({
|
||||
phase: "ready",
|
||||
panelReady: true,
|
||||
apiReady: true,
|
||||
controlViewReady: true,
|
||||
stateBundle: panelApi.getMachineSessionStateBundle?.() ?? null,
|
||||
workflowStatus: panelApi.getMachineSessionWorkflowStatus?.() ?? null,
|
||||
controlView,
|
||||
});
|
||||
}
|
||||
@@ -21,6 +21,7 @@ controller.mount();
|
||||
window.linuxCncIniPanelControlPageApi = {
|
||||
getControlView: controller.getControlView,
|
||||
getPanelApi: controller.getPanelApi,
|
||||
loadControlPageSessionState: controller.loadSessionState,
|
||||
refreshControlPage: controller.refresh,
|
||||
renderControlPage: controller.render,
|
||||
};
|
||||
|
||||
@@ -14,11 +14,19 @@ import {
|
||||
createControlPageRefreshState,
|
||||
refreshMachineSessionControlPage,
|
||||
} from "./control-page-refresh-workflow.js";
|
||||
import {
|
||||
createControlPageSessionLoadState,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
} from "./control-page-session-workflow.js";
|
||||
export {
|
||||
controlPageStatusText,
|
||||
createControlPageRefreshState,
|
||||
refreshMachineSessionControlPage,
|
||||
} from "./control-page-refresh-workflow.js";
|
||||
export {
|
||||
createControlPageSessionLoadState,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
} from "./control-page-session-workflow.js";
|
||||
export {
|
||||
INI_PANEL_ENTRIES,
|
||||
getIniPanelEntryByHref,
|
||||
@@ -53,6 +61,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
|
||||
launcherLinks: createIniPanelLauncherLinkViewModel(entries),
|
||||
controlPage: {
|
||||
createController: createMachineSessionControlPageController,
|
||||
loadSessionState: loadMachineSessionForControlPageWorkflow,
|
||||
refresh: refreshMachineSessionControlPage,
|
||||
renderView: renderMachineSessionControlView,
|
||||
},
|
||||
|
||||
@@ -64,6 +64,7 @@ TOOL_TABLE = browser-tool.tbl
|
||||
if (
|
||||
!controlPageApi?.getControlView ||
|
||||
!controlPageApi?.getPanelApi ||
|
||||
!controlPageApi?.loadControlPageSessionState ||
|
||||
!controlPageApi?.refreshControlPage ||
|
||||
!controlPageApi?.renderControlPage
|
||||
) {
|
||||
@@ -88,6 +89,11 @@ TOOL_TABLE = browser-tool.tbl
|
||||
"rendered",
|
||||
"control page refresh phase",
|
||||
);
|
||||
assertEqual(
|
||||
controlPageApi.loadControlPageSessionState().phase,
|
||||
"ready",
|
||||
"control page session load phase",
|
||||
);
|
||||
|
||||
panelDoc.getElementById("ini-editor").value = iniText;
|
||||
panelDoc.getElementById("query").click();
|
||||
|
||||
@@ -39,6 +39,17 @@ const expectedView = {
|
||||
},
|
||||
],
|
||||
};
|
||||
const expectedStateBundle = {
|
||||
report: {
|
||||
readiness: {
|
||||
ini: "INI WASM: ready",
|
||||
},
|
||||
},
|
||||
};
|
||||
const expectedWorkflowStatus = {
|
||||
lastAction: "run-gcode",
|
||||
error: null,
|
||||
};
|
||||
|
||||
let intervalCallback = null;
|
||||
const panelFrame = createPanelFrame();
|
||||
@@ -69,11 +80,23 @@ assert.equal(typeof intervalCallback, "function");
|
||||
|
||||
const panelApi = {
|
||||
getMachineSessionControlView: () => expectedView,
|
||||
getMachineSessionStateBundle: () => expectedStateBundle,
|
||||
getMachineSessionWorkflowStatus: () => expectedWorkflowStatus,
|
||||
};
|
||||
panelFrame.contentWindow = { linuxCncIniPanelApi: panelApi };
|
||||
|
||||
assert.equal(controller.getPanelApi(), panelApi);
|
||||
assert.equal(controller.getControlView(), expectedView);
|
||||
assert.deepEqual(controller.loadSessionState(), {
|
||||
phase: "ready",
|
||||
panelReady: true,
|
||||
apiReady: true,
|
||||
controlViewReady: true,
|
||||
stateBundle: expectedStateBundle,
|
||||
workflowStatus: expectedWorkflowStatus,
|
||||
controlView: expectedView,
|
||||
error: null,
|
||||
});
|
||||
const renderResult = controller.render();
|
||||
assert.deepEqual(renderResult, { statusNode: { textContent: "rendered" } });
|
||||
assert.equal(statusNode.textContent, "run-gcode | ok | -");
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createControlPageSessionLoadState,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
} from "../../../runtime/ui/ini-panel/control-page-session-workflow.js";
|
||||
|
||||
const stateBundle = {
|
||||
report: {
|
||||
readiness: {
|
||||
ini: "INI WASM: ready",
|
||||
interp: "Interpreter WASM: ready",
|
||||
opfs: "OPFS: ready",
|
||||
},
|
||||
},
|
||||
};
|
||||
const workflowStatus = {
|
||||
lastAction: "load-session",
|
||||
error: null,
|
||||
};
|
||||
const controlView = {
|
||||
status: {
|
||||
lastAction: "load-session",
|
||||
runStatus: null,
|
||||
error: null,
|
||||
},
|
||||
sections: [],
|
||||
};
|
||||
|
||||
assert.deepEqual(createControlPageSessionLoadState({ phase: "waiting-panel" }), {
|
||||
phase: "waiting-panel",
|
||||
panelReady: false,
|
||||
apiReady: false,
|
||||
controlViewReady: false,
|
||||
stateBundle: null,
|
||||
workflowStatus: null,
|
||||
controlView: null,
|
||||
error: null,
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow: () => null,
|
||||
getPanelApi: () => null,
|
||||
getControlView: () => null,
|
||||
}),
|
||||
createControlPageSessionLoadState({ phase: "waiting-panel" }),
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow: () => ({}),
|
||||
getPanelApi: () => null,
|
||||
getControlView: () => null,
|
||||
}),
|
||||
createControlPageSessionLoadState({
|
||||
phase: "waiting-api",
|
||||
panelReady: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const panelApiWithoutControlView = {
|
||||
getMachineSessionStateBundle: () => stateBundle,
|
||||
getMachineSessionWorkflowStatus: () => workflowStatus,
|
||||
};
|
||||
assert.deepEqual(
|
||||
loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow: () => ({}),
|
||||
getPanelApi: () => panelApiWithoutControlView,
|
||||
getControlView: () => null,
|
||||
}),
|
||||
createControlPageSessionLoadState({
|
||||
phase: "waiting-session",
|
||||
panelReady: true,
|
||||
apiReady: true,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
}),
|
||||
);
|
||||
|
||||
const panelApi = {
|
||||
...panelApiWithoutControlView,
|
||||
getMachineSessionControlView: () => controlView,
|
||||
};
|
||||
assert.deepEqual(
|
||||
loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow: () => ({}),
|
||||
getPanelApi: () => panelApi,
|
||||
getControlView: () => controlView,
|
||||
}),
|
||||
createControlPageSessionLoadState({
|
||||
phase: "ready",
|
||||
panelReady: true,
|
||||
apiReady: true,
|
||||
controlViewReady: true,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
controlView,
|
||||
}),
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow: () => {
|
||||
throw new Error("cross-origin frame");
|
||||
},
|
||||
getPanelApi: () => null,
|
||||
getControlView: () => null,
|
||||
}),
|
||||
createControlPageSessionLoadState({
|
||||
phase: "error",
|
||||
error: "cross-origin frame",
|
||||
}),
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => loadMachineSessionForControlPageWorkflow(),
|
||||
/panel window getter/,
|
||||
);
|
||||
|
||||
console.log("ini_panel_control_page_session_workflow_node_smoke=ok");
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/ui/node/verify_ini_panel_control_page_session_workflow.mjs"
|
||||
@@ -6,8 +6,10 @@ import { dirname, resolve } from "node:path";
|
||||
import {
|
||||
INI_PANEL_ENTRIES,
|
||||
controlPageStatusText,
|
||||
createControlPageSessionLoadState,
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellViewModel,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
refreshMachineSessionControlPage,
|
||||
getVisibleIniPanelEntries,
|
||||
} from "../../../runtime/ui/ini-panel/ui-shell.js";
|
||||
@@ -20,15 +22,21 @@ const refreshWorkflowText = readFileSync(
|
||||
resolve(root, "runtime/ui/ini-panel/control-page-refresh-workflow.js"),
|
||||
"utf8",
|
||||
);
|
||||
const sessionWorkflowText = readFileSync(
|
||||
resolve(root, "runtime/ui/ini-panel/control-page-session-workflow.js"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const requiredShellExports = [
|
||||
"INI_PANEL_ENTRIES",
|
||||
"controlPageStatusText",
|
||||
"createControlPageSessionLoadState",
|
||||
"createIniPanelLauncherLinkViewModel",
|
||||
"createIniPanelShellViewModel",
|
||||
"getIniPanelEntryByHref",
|
||||
"getVisibleIniPanelEntries",
|
||||
"createMachineSessionControlPageController",
|
||||
"loadMachineSessionForControlPageWorkflow",
|
||||
"refreshMachineSessionControlPage",
|
||||
"renderMachineSessionControlView",
|
||||
];
|
||||
@@ -47,6 +55,14 @@ for (const exportName of [
|
||||
assert.match(docText, new RegExp(`\\b${exportName}\\b`));
|
||||
}
|
||||
|
||||
for (const exportName of [
|
||||
"createControlPageSessionLoadState",
|
||||
"loadMachineSessionForControlPageWorkflow",
|
||||
]) {
|
||||
assert.match(sessionWorkflowText, new RegExp(`\\bexport function ${exportName}\\b`));
|
||||
assert.match(docText, new RegExp(`\\b${exportName}\\b`));
|
||||
}
|
||||
|
||||
const requiredPages = [
|
||||
["Launch", "runtime/ui/ini-panel/launch.html"],
|
||||
["Edit and run", "runtime/ui/ini-panel/index.html"],
|
||||
@@ -69,7 +85,18 @@ assert.deepEqual(
|
||||
assert.deepEqual(createIniPanelShellViewModel().pages, getVisibleIniPanelEntries());
|
||||
assert.deepEqual(createIniPanelShellViewModel().launcherLinks, createIniPanelLauncherLinkViewModel());
|
||||
assert.equal(controlPageStatusText({ lastAction: "run-gcode", runStatus: "ok", error: null }), "run-gcode | ok | -");
|
||||
assert.deepEqual(createControlPageSessionLoadState({ phase: "waiting-panel" }), {
|
||||
phase: "waiting-panel",
|
||||
panelReady: false,
|
||||
apiReady: false,
|
||||
controlViewReady: false,
|
||||
stateBundle: null,
|
||||
workflowStatus: null,
|
||||
controlView: null,
|
||||
error: null,
|
||||
});
|
||||
assert.equal(typeof createIniPanelShellViewModel().controlPage.createController, "function");
|
||||
assert.equal(createIniPanelShellViewModel().controlPage.loadSessionState, loadMachineSessionForControlPageWorkflow);
|
||||
assert.equal(createIniPanelShellViewModel().controlPage.refresh, refreshMachineSessionControlPage);
|
||||
assert.equal(typeof createIniPanelShellViewModel().controlPage.renderView, "function");
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@ import {
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellViewModel,
|
||||
createControlPageRefreshState,
|
||||
createControlPageSessionLoadState,
|
||||
createMachineSessionControlPageController,
|
||||
getIniPanelEntryByHref,
|
||||
getIniPanelEntryManifest,
|
||||
getVisibleIniPanelEntries,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
refreshMachineSessionControlPage,
|
||||
renderMachineSessionControlView,
|
||||
} from "../../../runtime/ui/ini-panel/ui-shell.js";
|
||||
@@ -24,8 +26,19 @@ assert.deepEqual(createControlPageRefreshState({ phase: "waiting", statusText: "
|
||||
renderResult: null,
|
||||
error: null,
|
||||
});
|
||||
assert.deepEqual(createControlPageSessionLoadState({ phase: "waiting-panel" }), {
|
||||
phase: "waiting-panel",
|
||||
panelReady: false,
|
||||
apiReady: false,
|
||||
controlViewReady: false,
|
||||
stateBundle: null,
|
||||
workflowStatus: null,
|
||||
controlView: null,
|
||||
error: null,
|
||||
});
|
||||
assert.equal(getIniPanelEntryByHref("./index.html")?.id, "edit-run");
|
||||
assert.equal(typeof createMachineSessionControlPageController, "function");
|
||||
assert.equal(typeof loadMachineSessionForControlPageWorkflow, "function");
|
||||
assert.equal(typeof refreshMachineSessionControlPage, "function");
|
||||
assert.equal(typeof renderMachineSessionControlView, "function");
|
||||
|
||||
@@ -65,6 +78,7 @@ assert.deepEqual(shellViewModel.pages, [
|
||||
]);
|
||||
assert.deepEqual(shellViewModel.launcherLinks, createIniPanelLauncherLinkViewModel());
|
||||
assert.equal(shellViewModel.controlPage.createController, createMachineSessionControlPageController);
|
||||
assert.equal(shellViewModel.controlPage.loadSessionState, loadMachineSessionForControlPageWorkflow);
|
||||
assert.equal(shellViewModel.controlPage.refresh, refreshMachineSessionControlPage);
|
||||
assert.equal(shellViewModel.controlPage.renderView, renderMachineSessionControlView);
|
||||
shellViewModel.pages[0].title = "changed";
|
||||
@@ -102,6 +116,7 @@ const controller = createMachineSessionControlPageController({
|
||||
assert.deepEqual(controller.render(), { rendered: true });
|
||||
assert.equal(statusNode.textContent, "run-gcode | ok | -");
|
||||
assert.equal(renderedView.status.runStatus, "ok");
|
||||
assert.equal(controller.loadSessionState().phase, "ready");
|
||||
assert.equal(controller.refresh().phase, "rendered");
|
||||
|
||||
console.log("ini_panel_ui_shell_node_smoke=ok");
|
||||
|
||||
@@ -11,6 +11,7 @@ ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_state_summary.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_control_page_controller.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_control_page_refresh_workflow.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_control_page_session_workflow.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_entry_manifest.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_ui_shell.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_entry_docs.sh"
|
||||
|
||||
Reference in New Issue
Block a user