# AXIS-Style Browser Simulation Implementation Plan

This document defines the implementation plan for evolving
`runtime/ui/simulation/index.html` toward a browser simulation surface inspired
by LinuxCNC AXIS. It is a product/UI implementation guide, not permission to
reimplement LinuxCNC CNC semantics in JavaScript.

## Reference Target

The reference UI is LinuxCNC AXIS with:

- a top menu and compact icon toolbar;
- Manual Control and MDI tabs;
- machine controls for axis selection, jog mode, homing, spindle, coolant,
  feed/rapid/spindle overrides, jog speed, and max velocity;
- active G-code modal display;
- a combined preview/DRO area;
- a 3D toolpath preview with machine extents and tool marker;
- DRO rows for relative/absolute position, distance-to-go, work offsets,
  G92 offsets, tool length offsets, and velocity;
- a scrollable G-code source pane with active line;
- a bottom status bar for E-stop, tool, and coordinate mode.

The browser implementation should match the operational shape of AXIS while
staying suitable for a web runtime and the current WASM/OPFS architecture.

## Non-Negotiable Boundary

LinuxCNC remains the only CNC semantic source.

Browser JavaScript may:

- render UI layout and interaction state;
- call the existing LinuxCNC-backed WASM SDK;
- stage G-code, INI, parameter, and tool-table text into OPFS or the
  Emscripten filesystem;
- display LinuxCNC-produced canonical output, motion events, modal state,
  run status, and validated runtime summaries;
- animate playback over LinuxCNC-produced motion events.

Browser JavaScript must not implement:

- G-code interpretation;
- modal semantics;
- cutter/tool compensation semantics;
- parameter semantics;
- kinematics;
- remap behavior;
- planner behavior;
- canonical motion semantics beyond display mapping of LinuxCNC-produced
  canonical fields.

## Current Baseline

The current simulation page already has:

- LinuxCNC interpreter WASM execution through `createLinuxCncInterpSdk()`;
- selectable test programs under `runtime/ui/simulation/programs/`;
- custom G-code text and browser File execution through LinuxCNC WASM;
- editable G-code pane plus run-editor-text workflow;
- canonical output rendering;
- program-line rendering;
- motion table;
- final and playback axis readout;
- SVG toolpath preview;
- reset/step/play/finish playback over LinuxCNC canonical motion events;
- browser smoke coverage in `tests/browser/verify_real_simulation_browser.sh`;
- Node contract coverage in `tests/ui/node/verify_real_simulation_programs.sh`.

## Target Layout

The first AXIS-style browser layout should be a single operator workspace:

```text
menu/toolbar/status badges
┌───────────────────────┬─────────────────────────────────────┐
│ Manual / MDI tabs     │ Preview / DRO tabs                   │
│ Jog controls          │ 3D or 2.5D toolpath viewport          │
│ spindle/coolant       │ DRO overlay / panel                  │
│ overrides/speeds      │ active tool marker                   │
│ active modal codes    │ machine/work extents                 │
├───────────────────────┴─────────────────────────────────────┤
│ G-code source with active execution line                      │
├───────────────────────────────────────────────────────────────┤
│ E-stop | tool | position mode | run state | program state      │
└───────────────────────────────────────────────────────────────┘
```

The Phase 1 implementation has replaced the former three-column MVP with this
AXIS-style shell while preserving the existing LinuxCNC-backed execution and
playback APIs.

## Implementation Phases

### Phase 1: AXIS-Style Shell

Status: implemented.

Build the visual shell around the existing simulation state:

- menu bar: File, Machine, View, Help;
- toolbar: open, reload, run, pause, step, stop, reset, zoom controls;
- left panel tabs: Manual Control and MDI;
- right panel tabs: Preview and DRO;
- lower G-code pane;
- bottom status bar.

Controls may initially be display-only or disabled when no real runtime action
exists, but they must show explicit readiness/blocked state.

Validation:

- browser smoke proves all AXIS-style regions render;
- current program selection and playback still work;
- no existing simulation API is removed.

Current implemented regions:

- `data-axis-shell="titlebar"`;
- `data-axis-shell="menubar"`;
- `data-axis-shell="toolbar"`;
- `data-axis-shell="manual-mdi"`;
- `data-axis-shell="preview-dro"`;
- `data-axis-shell="gcode-pane"`;
- `data-axis-shell="machine-state"`;
- `data-axis-shell="statusbar"`.

### Phase 2: Program Execution Workflow

Status: active; custom text/file/editor execution and OPFS program persistence
are implemented. MDI scratch text can now be loaded into the AXIS G-code pane
or executed through the same LinuxCNC-backed WASM path.

Upgrade the G-code pane and execution controls:

- load a selected built-in test program;
- run through LinuxCNC WASM;
- reset/step/play/pause/finish playback from toolbar and keyboard-accessible
  buttons;
- reload the current built-in, editor, MDI, file, or OPFS-backed program
  through the same LinuxCNC-backed execution path;
- highlight active G-code line and current motion row;
- show run status, source program, motion count, and current frame.
- load operator-provided G-code text through `runProgramText()`;
- stage operator-provided G-code text without executing through
  `loadProgramText()`;
- load browser `File` objects through `loadProgramFile()`;
- edit visible G-code in the AXIS shell and run it through
  `runEditorProgramText()`;
- load or run the MDI panel scratch program through LinuxCNC-backed text
  execution;
- record successful LinuxCNC-backed MDI executions in an AXIS-style MDI
  history list, with click-to-restore and clear controls;
- record successful LinuxCNC-backed built-in, text, editor, file, OPFS, MDI,
  and session-backed executions in an AXIS-style recent program list, with
  click-to-stage and clear controls;
- save the current editor G-code through OPFS with `saveProgramToOpfs()`;
- load an OPFS G-code program through `loadProgramFromOpfs()` and execute it
  through LinuxCNC WASM;

Built-in test programs must live in:

```text
runtime/ui/simulation/programs/
```

Each program should have its own module and be exported through
`runtime/ui/simulation/programs/index.js`. The simulation renderer should keep
importing the inventory through the program index rather than embedding G-code
fixtures in `simulation-app.js`.

Validation:

- browser smoke runs at least one linear program, one arc program, and one
  canned-cycle program through toolbar actions;
- active-line, toolhead, executed path, and status bar update together.
- browser smoke saves an edited G-code program to OPFS, changes the editor
  text, reloads the OPFS program, and verifies the reloaded text executes
  through LinuxCNC WASM.
- browser smoke verifies `loadProgramText()` stages text without executing and
  that MDI text execution produces LinuxCNC canonical output.
- browser smoke verifies successful MDI execution records history, restores
  history text into the MDI pane, and clears history without changing
  LinuxCNC-produced motion state.
- browser smoke verifies successful LinuxCNC-backed executions populate recent
  programs, recent entries stage text back into the editor without implicit
  execution, and the recent list can be cleared.

### Phase 3: DRO and Modal Display

Status: partially implemented; actual axis readout and LinuxCNC `UPDATE_TAG`
modal display are implemented.

Add a DRO panel modeled after AXIS:

- relative actual X/Y/Z/A/B/C;
- distance-to-go when available from LinuxCNC-produced or validated runtime
  events;
- G54 and G92 rows when available from canonical output or runtime summaries;
- tool length offset rows when available;
- velocity from playback delta or LinuxCNC-produced runtime field when
  available;
- active G-code modal display from LinuxCNC output only.

Fallback values must be marked as unavailable rather than invented.

Current implemented behavior:

- relative actual X/Y/Z/A/B/C values are rendered from LinuxCNC canonical
  motion frames;
- distance-to-go, G54, G92, tool-length offset, and velocity fields render as
  explicit `n/a` until LinuxCNC-produced or validated runtime fields are
  available;
- modal rows are derived from LinuxCNC `canon_event=UPDATE_TAG` output;
- spindle, coolant, tool, and override rows are derived from LinuxCNC canonical
  events when present and otherwise remain explicit `n/a`;
- browser API exposes `getDroState()`, `getModalState()`, and
  `getMachineStatusState()`.

Validation:

- Node and browser tests verify no unavailable field is silently fabricated;
- modal/DRO display is derived from LinuxCNC output or explicit unavailable
  state.

### Phase 4: Preview Upgrade

Status: implemented for the current browser simulation scope; AXIS-like
Three.js viewport controls are available with SVG fallback state for
compatibility tests.

Improve the preview from a simple SVG polyline into an AXIS-like viewport:

- dark viewport theme;
- work envelope and extents grid;
- full path versus executed path;
- active tool marker;
- zoom fit / pan / reset view;
- orthographic 3D using Three.js `^0.183.2`, kept stable in browser smoke.

Current implemented behavior:

- dark viewport theme;
- full path versus executed path;
- active tool marker;
- motion-derived extents rectangle, origin axes/marker, and preview legend;
- Three.js canvas renders the primary toolpath preview from LinuxCNC canonical
  motion, while SVG remains a hidden fallback/state surface for compatibility;
- runtime imports the local `runtime/ui/simulation/vendor/three/three.module.js`
  build, with `runtime/ui/simulation/package.json` documenting the requested
  `three` dependency range;
- Three.js preview includes dynamic grid lines and X/Y/Z axis labels;
- Three.js preview renders coordinate axes, motion-type segment metadata,
  active toolhead coordinates, renderer revision, and canvas size into the
  preview state API for smoke validation;
- Three.js preview samples `ARC_FEED` display curves from LinuxCNC-produced
  canonical center, rotation, active-plane, and endpoint fields, while keeping
  SVG fallback state endpoint-compatible for existing browser tests;
- Three.js preview now renders AXIS-style tool geometry with a visible tool
  body, optional LinuxCNC-produced tool-length-offset indicator, current tool
  label when available, and a viewport scale bar surfaced through
  `getPreviewState()`;
- Three.js preview renders a compact X/Y/Z orientation triad in the viewport
  and surfaces its structured display state through `getPreviewState()`;
- AXIS-style preview HUD overlays the Three.js stage with view/zoom/pan,
  toolhead XYZ, machine envelope, grid step, and renderer/path counts derived
  from LinuxCNC canonical motion and Three.js render state;
- AXIS-style bottom status bar is structured into E-stop, tool, program
  source, active line, position/run mode, and preview state fields, with
  `getAxisStatusbarState()` exposing the same browser state for smoke tests;
- fit, zoom in, zoom out, pan, Top/Front/Side/Iso view modes, and reset view
  controls;
- AXIS-style preview layer toggles can show/hide rapid traverse, feed, arc,
  tool, envelope, and scale-bar display layers without changing the
  LinuxCNC-produced motion state;
- Three.js canvas exposes an AXIS-style cursor crosshair and live X/Y preview
  coordinate readout derived from the current rendered viewBox; this is display
  state only and does not change machine or G-code state;
- Three.js canvas supports wheel zoom, pointer-drag pan, and double-click fit;
- browser resize refreshes the Three.js viewport while preserving playback,
  zoom, pan, and view-mode state;
- dedicated desktop/mobile screenshot smoke validates the Three.js viewport,
  preview legend, diagnostics, compact tool table, status history, and
  G-code/Machine State layout in real browser viewports;
- screenshot smoke captures Preview, DRO, and MDI entry states through stable
  URL view parameters;
- screenshot smoke can persist desktop/mobile PNGs and screenshot metadata JSON
  when `AXIS_SCREENSHOT_ARTIFACT_DIR` is provided; full
  `exportDiagnosticsArtifact()` JSON is available behind
  `AXIS_SCREENSHOT_FULL_DIAGNOSTICS=1` for CI jobs that opt into CDP capture;
- release-readiness artifact generation summarizes persisted AXIS screenshot
  diagnostics when `AXIS_SCREENSHOT_ARTIFACT_DIR` points at the screenshot
  artifact directory, without changing the fixed release gate count;
- browser API exposes `fitPreview()`, `zoomPreview(factor)`,
  `panPreview(dx, dy)`, `setPreviewViewMode(mode)`, `getPreviewState()`, and
  `resetPreview()`;
- browser API exposes `getPreviewLayerState()` and
  `setPreviewLayer(name, enabled)` for AXIS-style display layer controls.
- browser API exposes `getPreviewCursorState()` for the current preview
  cursor/crosshair readout.

Validation:

- browser smoke verifies nonblank preview, executed path growth, toolhead
  movement, Three.js canvas pixels, sampled canonical arc rendering, structured
  tool geometry, scale bar state, AXIS preview layer toggles, and stable
  viewport controls;
- browser smoke verifies pointer-driven preview cursor/crosshair coordinate
  readout and pointer-leave clearing;
- mobile and desktop screenshot smoke verifies nonblank Three.js rendering,
  visible compact panels, and non-overlapping AXIS shell sections.

### Phase 5: Machine/Session Integration

Status: partially implemented; OPFS machine/session readiness, session
staging, and loaded-session program execution are visible in the AXIS shell.

Connect the AXIS-style shell to the existing OPFS/session workflow:

- load machine session from OPFS;
- show machine file readiness;
- show loaded INI/parameter/tool-table/program paths;
- stage files into WASM before execution;
- block run controls with explicit reasons when session readiness is missing.

Current implemented behavior:

- Manual Control panel includes machine id, session id, and G-code filename
  inputs;
- Manual Control also renders explicit blocked state for real jog/spindle and
  coolant controls that are not backed by the browser runtime;
- `Check Session` calls existing `readMachineSessionReadiness()`;
- readiness phase, missing checks, INI path, and G-code path render in the
  AXIS shell;
- browser API exposes `getMachineReadiness()` and
  `checkMachineSessionReadiness(options)`;
- AXIS panels show LinuxCNC canonical machine status for spindle, coolant,
  tool, and override fields when available;
- `Load Session` calls existing LinuxCNC-backed `loadMachineSessionFromOpfs()`
  after readiness passes;
- staged INI, parameter, and tool-table WASM paths render in the AXIS shell;
- browser API exposes `getMachineSessionLoadState()` and
  `loadReadyMachineSession(options)`;
- staged LinuxCNC tool-table load results are summarized in Machine State with
  OPFS/WASM paths, tool count, and the first loaded tool row;
- staged LinuxCNC tool-table rows also render in a compact AXIS-style table
  with T/P/Z/D/comment columns;
- editor/custom/built-in program execution uses `runProgramWithIni()` with the
  loaded session INI path when a session has been staged;
- toolbar and status bar expose the current run mode as standalone or
  session-backed with the active INI path;
- Manual Control exposes a `Use Session` toggle so a staged machine session can
  be enabled or bypassed for subsequent runs;
- Machine State includes a compact run summary for execution mode, session INI,
  OPFS program path, readiness phase, and session load phase;
- browser API exposes `getRunMode()`, `setUseLoadedSession(value)`, and
  `getRunSummary()`;
- toolbar and Machine State expose run-control ready/fallback state so operators
  can see whether a requested session-backed run will actually use a loaded INI
  or fall back to standalone LinuxCNC WASM;
- AXIS-style keyboard bindings route F3/F5/F6/F7, run, playback stepping,
  finish/reset, and preview fit through the same browser API as toolbar
  controls;
- Machine State includes a compact diagnostics panel for the last LinuxCNC
  canonical event, last surfaced error, readiness/load phase, and most recent
  shortcut action;
- Machine State includes a compact status-history ring for recent run,
  readiness, session, OPFS, shortcut, and error events;
- Machine State includes a compact limits/home panel that explicitly reports
  `n/a` until LinuxCNC-produced runtime fields are available;
- diagnostics can be exported as a structured browser artifact containing run
  summary, preview state, status history, tool-table summary, and limits/home
  state;
- browser API exposes `getDiagnosticsState()`, `getStatusHistory()`,
  `getToolTableSummary()`, `getLimitsHomeState()`,
  `exportDiagnosticsArtifact()`, and `handleAxisShortcut(event)`;
- browser smoke covers run and play/pause keyboard shortcuts after waiting for
  their async UI/API side effects;
- browser smoke verifies blocked readiness before OPFS files exist, then writes
  machine files, G-code, and a session snapshot through existing OPFS helpers
  and verifies readiness becomes `ready`;
- browser smoke verifies a ready machine session stages INI, parameter, and
  tool-table files into the WASM filesystem through LinuxCNC-backed SDKs.
- browser smoke verifies editor G-code execution uses the loaded session INI
  path and still renders LinuxCNC canonical output/playback state.
- browser smoke verifies OPFS-loaded G-code also uses the loaded session INI
  path and that run-mode UI/API state switches to session-backed.
- browser smoke verifies disabling `Use Session` returns editor execution to
  standalone mode while leaving the staged session available.
- browser smoke verifies run summary API/DOM values for standalone,
  session-backed, and OPFS program execution.
- browser smoke verifies AXIS URL view parameters can open Manual/MDI and
  Preview/DRO panels without bypassing the normal tab controls.

Validation:

- OPFS/session browser smoke covers load -> run -> playback;
- release gate includes the AXIS-style simulation workflow.

## Required APIs

The page should expose a stable browser API under:

```js
window.linuxCncRealSimulationApi
```

Required methods:

- `getState()`;
- `getPrograms()`;
- `runProgramById(programId)`;
- `reloadCurrentProgram()`;
- `getPlaybackFrame()`;
- `isPlaybackRunning()`;
- `resetPlayback()`;
- `stepPlayback(delta)`;
- `play()`;
- `pause()`;
- `finishPlayback()`;
- `runProgramText(programText, metadata)`;
- `loadProgramText(text, metadata)`;
- `loadProgramFile(file)`;
- `getProgramText()`;
- `setProgramText(text, metadata)`;
- `runEditorProgramText()`;
- `runMdiProgramText(text)`;
- `getMdiHistory()`;
- `clearMdiHistory()`;
- `getRecentPrograms()`;
- `clearRecentPrograms()`;
- `getOpfsProgramState()`;
- `saveProgramToOpfs(filename)`;
- `loadProgramFromOpfs(filename)`;
- `getDroState()`;
- `getModalState()`;
- `getMachineStatusState()`;
- `fitPreview()`;
- `zoomPreview(factor)`;
- `panPreview(dx, dy)`;
- `setPreviewViewMode(mode)`;
- `getPreviewState()`;
- `getPreviewLayerState()`;
- `setPreviewLayer(name, enabled)`;
- `getPreviewCursorState()`;
- `resetPreview()`;
- `getMachineReadiness()`;
- `checkMachineSessionReadiness(options)`;
- `getMachineSessionLoadState()`;
- `loadReadyMachineSession(options)`;
- `getRunMode()`;
- `getRunControlState()`;
- `getAxisStatusbarState()`;
- `setUseLoadedSession(value)`;
- `getRunSummary()`;
- `getDiagnosticsState()`;
- `getStatusHistory()`;
- `getToolTableSummary()`;
- `getLimitsHomeState()`;
- `exportDiagnosticsArtifact()`;
- `handleAxisShortcut(event)`;
- `applyAxisViewFromUrl(search)`;

API additions must be covered by Node or browser smoke before being relied on
by UI controls.

## Visual Design Rules

Use the reference as an operational model, not a pixel-perfect clone.

- Keep controls dense and tool-like.
- Prefer plain borders, compact buttons, tabs, and status strips.
- Avoid landing-page composition, marketing cards, oversized hero text, and
  decorative backgrounds.
- Keep preview large and central.
- Keep G-code visible during playback.
- Keep status and blocked reasons visible without modal interruptions.
- Use icons where available, but text labels are acceptable until a stable icon
  dependency exists.

## Acceptance Gates

Every implementation batch must keep these passing:

```bash
wasm-port/tests/ui/node/verify_real_simulation_programs.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_axis_screenshot_browser.sh
wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh
wasm-port/tests/host/verify_project_release_gate.sh
```

When AXIS-style shell work starts, add or extend browser assertions for:

- menu bar;
- toolbar;
- Manual Control tab;
- MDI tab;
- Preview tab;
- DRO tab;
- active G-code pane;
- status bar;
- playback controls driving the same LinuxCNC-backed state as the API.

## Immediate Next Batch

Continue AXIS by deepening operator workflow coverage without adding
browser-side CNC semantics:

1. Add limits/home real data mapping only when LinuxCNC-produced runtime fields
   are available.
2. Keep unavailable DRO and machine fields explicit `n/a` until
   LinuxCNC-produced runtime data exists.
3. Promote AXIS screenshot artifact summaries into CI retention once the
   release job has a stable artifact directory.
4. Harden the optional CDP diagnostics capture path before making full
   `exportDiagnosticsArtifact()` retention mandatory in CI.
