Skip to content

feat(renderer): interactive view capture() → Blob (DD-030 D1) - #418

Merged
sobechestnut-dev merged 1 commit into
devfrom
feat/dd030-capture
Aug 27, 2026
Merged

sobechestnut-dev merged 1 commit into
devfrom
feat/dd030-capture

Conversation

@sobechestnut-dev

Copy link
Copy Markdown
Contributor

DD-030 increment 3/3 — interactive capture() (completes the batch)

Final DD-030 increment (bed ✅ → per-plate ✅ → capture). Gates AnyBridge's built-and-waiting PR #1371.

What

capture(opts?) returns the currently displayed view as an image Blob — user-selected thumbnails, large-file fallback, screenshots. opts = { width?, height?, format?, quality?, background? } (all optional; defaults match the live view).

Surfaces (all inherit one implementation):

  • GcodePreviewControls.capture() → the Vue / React / Svelte adapters inherit it via the existing controls passthrough (zero adapter code); the Web Component adds an imperative capture().
  • ModelViewer.capture() on the model-viewer handle.
  • Carried by InteractiveStage (impl) + ToolpathRenderer (delegate); PreviewRenderer gains an optional capture?.

Mechanism — render-to-target

Renders the current scene + active camera into an off-screen WebGLRenderTarget at the requested size and reads it back — so it never flips the interactive preserveDrawingBuffer default (no per-frame tax), never disturbs the live view, and supports an arbitrary output size + independent/transparent background. Reuses the headless still path's "single render, then read pixels" recipe; frames the thumbnail at its own aspect (no distortion); repaints the live view afterward. Returns the Blob; never triggers a download (caller owns the pixels, same contract as renderStill).

Honest & additive

Rejects with a typed CaptureUnsupportedError (code: 'E_CAPTURE_UNSUPPORTED') when the renderer can't render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage is disposed / context-lost. Purely additive — a new optional method on the renderer contract; no existing signature changed.

Tests

The GL-free halves are unit-tested (flipRowsRGBA, resolveCaptureSize), and the unsupported path is verified through ToolpathRenderer.capture() with a stub GL (rejects E_CAPTURE_UNSUPPORTED). The full pixel path (render-target readback + canvas encode) is browser-only — consistent with this package's stub-GL testing convention — and is validated end-to-end by the gated consumer integration (AnyBridge PR #1371). Full suites green: renderer-three 156, model-renderer 61, core 35; all 6 built packages compile.

This completes DD-030. Additive → the whole batch rides one lockstep minor (v0.18.0).

🤖 Generated with Claude Code

Add capture(opts?) that returns the currently displayed view as an image Blob,
for user-selected thumbnails, large-file fallback, and screenshots. Available on
GcodePreviewControls.capture (all 4 adapters inherit it; the Web Component adds
an imperative method) and ModelViewer.capture; implemented on InteractiveStage
and delegated by ToolpathRenderer.

Mechanism = render-to-target: render the current scene + active camera into an
off-screen WebGLRenderTarget at the requested size and read it back, so it never
flips the interactive preserveDrawingBuffer default nor disturbs the live view,
and supports an arbitrary output size + independent/transparent background. The
library returns the Blob and never triggers a download (caller owns the pixels,
same contract as renderStill).

Honest: capture() rejects with a typed CaptureUnsupportedError
(E_CAPTURE_UNSUPPORTED) when the renderer cannot render-to-target (2D renderer,
stub GL / no WebGL) or the stage is disposed/context-lost. Purely additive (new
optional method on the renderer contract). Final increment of the DD-030 batch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sobechestnut-dev
sobechestnut-dev merged commit c950339 into dev Aug 27, 2026
4 checks passed
@sobechestnut-dev
sobechestnut-dev deleted the feat/dd030-capture branch August 27, 2026 20:42
@sobechestnut-dev sobechestnut-dev mentioned this pull request Aug 27, 2026
4 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant