Align AXIS control flow and fix pause and program rendering

Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks.

Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
This commit is contained in:
2026-09-08 10:46:27 +08:00
parent 845075f4a3
commit bade560372
55 changed files with 2495 additions and 286 deletions

View File

@@ -15,6 +15,9 @@ const [axisPy, axisTcl, taskSource, contract, app, worker, programCore, runtime]
read("web/worker/wasm-program-runtime.mjs"),
]);
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
const glnav = await read("linuxcnc/lib/python/glnav.py");
assert.equal(sha256(glnav), "b2d811a21954966655a40f107c2af217cc7d57bae0dc308dc4698f5859ed3f11", "glnav.py drifted from the documented source lock");
assert.match(glnav, /def set_view_z2\(self\):[\s\S]*?glRotatef\(-90, 0, 0, 1\)[\s\S]*?self\.perspective = False/);
const squash = (value) => value.replace(/\\\s*/g, " ").replace(/\s+/g, " ");
const axisPySquashed = squash(axisPy);
const axisTclSquashed = squash(axisTcl);
@@ -60,8 +63,11 @@ for (const sourceHandler of ["PROGRAM_LOAD", "PROGRAM_RUN", "PROGRAM_RESTART"])
const start = worker.indexOf(`case "${sourceHandler}"`);
const next = worker.indexOf("\n case ", start + 8);
assert.ok(start >= 0, `missing Worker handler ${sourceHandler}`);
assert.doesNotMatch(worker.slice(start, next < 0 ? worker.length : next), /livePathEpoch\s*\+=/, `${sourceHandler} must not clear native Live Plot`);
assert.doesNotMatch(worker.slice(start, next < 0 ? worker.length : next), /livePathEpoch\s*\+=/, `${sourceHandler} must use the shared explicit clear boundary`);
assert.doesNotMatch(worker.slice(start, next < 0 ? worker.length : next), /clearLivePlot\(/, `${sourceHandler} must preserve the native Live Plot`);
}
assert.doesNotMatch(app, /clear_live_plot_on_replace/);
assert.doesNotMatch(worker, /clear_live_plot_on_replace/);
for (const term of ["SOURCE_EXACT", "SOURCE_ADAPTER", "SOURCE_BLOCKED", "WEB_EXTENSION", "Source-lock manifest", "Source-exact widget enable formulas", "Exhaustive native-action ledger", "manual_ok", "ensure_mode", "open_file_guts", "EMC_TASK_PLAN_RUN", "PROGRAM_LOAD", "PROGRAM_RUN", "PROGRAM_STEP", "PROGRAM_PAUSE", "PROGRAM_RESUME", "PROGRAM_ABORT", "JOG_START", "HOME", "TOUCH_OFF", "Run after completion or Stop", "Toolpath display lifecycle", "live_path_epoch", "positionlogger", "blocked_by_abi_tool_offsets", "Button Dependency Matrix", "explicit `VISUAL_CLEAR` source adapter advances the epoch"]) assert.match(contract, new RegExp(term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")), `contract missing ${term}`);
assert.match(contract, /Native AXIS does \*\*not\*\* bind lowercase `c` to C-axis selection/);