Image transport codecs — binary image frames carry a versioned codec
byte, auto-selecting JPEG for 8-bit 1/3-channel images and lossless zlib
otherwise, overridable per image via ImageData(codec=EImageCodec.…,
jpeg_quality=…) (None = auto).
Tiled images — Visualizer.register_image_pyramid serves very large
images as an on-demand /image/{id}/{level}/{x}/{y} tile pyramid with a
server-side LRU, so the frontend fetches only the visible region.
Image streams — Visualizer.register_camera_stream streams 15–30 Hz
camera feeds as MJPEG over /stream/{id}.
JPEG-by-default HTML export — the export asset store embeds eligible
8-bit images as JPEG data URLs instead of raw base64, with a lossless
fallback for 16-bit/float/4-channel data.
Layout reconciliation (no full teardown on re-push) — the frontend now
reconciles the whole view_layout tree by stable view id against a single
live-view orphan registry (_viewRegistry), reusing unchanged scene panes and
controls and only creating/removing the diff, so reordering a layout re-attaches
the expensive WebGL panes instead of tearing the whole UI down.
Granular background-image update — Visualizer.set_background_image(view,
image) streams a new CameraView.background_image (a binary pixel frame plus a
view_background_image message) without re-pushing the layout, leaving other
panes untouched.
Streaming camera-view example — pinhole_calibrated_streaming.py streams
random noise at 4 fps into a calibrated 2D pane and swaps the left/right panes
at runtime via a toolbar button.
Enable/disable/hide for entities and controls — entities can be hidden or
shown at runtime (Visualizer.set_visible/hide, VizSceneHandle.set_visible/
hide, VizObjectRef.visible), action objects can be enabled/disabled
(ActSceneObject.set_enabled/enable/disable), and UI controls can be hidden,
shown, or greyed out (ControlView.set_enabled/set_visible and
Visualizer.set_control_enabled/set_control_visible) — each pushed as a
granular message (a visible aspect patch, an interaction re-push, or a
control_state message) with no layout re-push. See hide_sphere.py.
SdfObject(Cylinder(...)) now honours align_center=0.0 — the SDF
cylinder is based at origin (not centred), matching the mesh renderer and
the documented align_center semantics.
Multiple panes of the same scene — a view_layout re-push now reuses every
scene pane by its stable id (previously keyed by scene name), so two panes of
the same scene no longer tear down and rebuild an unrelated pane on every
re-push.
Cached blade-name parsing — Algebra._resolve_key_signed caches the
string → blade-id resolution per algebra, removing the per-access parse cost
of mv["e14"] / mv["e12"] = x.
Branch-free MV operator dispatch — the six GA operators are bound once
to plain/modular implementations, and the MV dunders route through them,
skipping the per-call modulus re-check.
JS dev toolchain under js/dev/ — package.json (esbuild + playwright),
node --test unit tests for the reconciliation/orphan-map core (extracted into
the pure views/reconcile.js), a node syntax gate, and a headless Playwright
smoke; wired into pre-commit and the PR checklist. JS tests/esbuild/playwright
do not run in GitHub Actions.