Repository navigation
release: version packages (lockstep) - #412
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/dev
branch
from
August 27, 2026 16:01
7ad1a33 to
9ba0e78
Compare
github-actions
Bot
force-pushed
the
changeset-release/dev
branch
6 times, most recently
from
August 27, 2026 18:23
5bc2ca8 to
e5ab43e
Compare
github-actions
Bot
force-pushed
the
changeset-release/dev
branch
from
August 27, 2026 20:42
e5ab43e to
7995cfd
Compare
4 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to dev, this PR will be updated.
Releases
@chestnutlabs/gcode-model-renderer@0.18.0
Minor Changes
#418
c950339Thanks @sobechestnut-dev! - feat(renderer): interactive view capture() → Blob (DD-030 D1)The interactive viewer can now hand back what is on screen right now as an image
Blob— for auser-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New
capture(opts?)whereoptsis{ width?, height?, format?, quality?, background? }(all optional; defaults match the liveview).
Available on every interactive surface:
GcodePreviewControls.capture()(so the Vue, React, Svelte, andWeb-Component adapters all inherit it — the Web Component also exposes an imperative
capture()method),and
ModelViewer.capture()on the model-viewer handle. The toolpathToolpathRendererand the sharedInteractiveStagecarry the implementation.Mechanism (render-to-target). Capture renders the current scene + active camera into an off-screen
WebGLRenderTargetat the requested size and reads it back, rather than flipping the interactivecontext's
preserveDrawingBuffer(which would tax every interactive frame). That gives an arbitraryoutput size and an independent/transparent background without disturbing the live view, and reuses the
headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect
so it isn't distorted; the live view is repainted afterward. The library returns the
Bloband nevertriggers a download — the caller owns the pixels (same contract as
renderStill).Honest. When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage
is disposed / its context is lost,
capture()rejects with a typedCaptureUnsupportedError(
code: 'E_CAPTURE_UNSUPPORTED') — never fabricated output. Purely additive (a new optional method on therenderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer
interoperability batch (bed + per-plate scope + capture).
#417
5dc8079Thanks @sobechestnut-dev! - feat(model-renderer): per-plate / object-subset render scope (DD-030 D2)A caller can now render just one plate (or any object/placement subset) of a multi-plate / multi-object
source, instead of always the whole project — so a single-plate variant thumbnail looks like that plate.
New
RenderScopeselector —{ plateId },{ objectIds }, or{ instanceFilter }— with a pure,three-free
applyRenderScope(scene, scope)that returns a filteredModelScene(dropping non-matchingobjects/placements) with
boundsrecomputed from the kept placements, so framing follows the subset.{ plateId }is sugar over the placement-levelplateIds(DD-025); it's generic (a plate is just one wayto derive the subset) and vendor-neutral.
Wired into both surfaces:
renderModelStillgains arenderScope?option; it also folds the scope into the stillcacheKeyso aplate-1 and a plate-2 thumbnail of the same source key distinctly (an opaque
instanceFilteris markednon-cacheable).
createModelViewergains an initialrenderScope?and asetRenderScope(scope | null)handle methodthat rebuilds and reframes to the subset (
nullrestores the whole source).Honest and additive: omitting
renderScoperenders the whole project exactly as before. A{ plateId }that matches nothing (e.g. a source with no declared plate structure,
capabilities.plates !== 'known')renders an empty scene by design — the honest result of selecting a plate that isn't declared;
consumers gate
{ plateId }onplates: 'known'. The source's declaredplates/capabilitiesarepreserved (only what is rendered is narrowed). Second increment of the DD-030 renderer/viewer interop
batch.
Patch Changes
0ebeadf,c950339]:@chestnutlabs/gcode-parser@0.18.0
Minor Changes
#410
5144890Thanks @sobechestnut-dev! - feat(parser): RS274NGC parameters + expressions (DD-017 Phase 1, #189 phase 7)The parser now understands the RS274NGC programming layer that FDM slicers never use but CAM /
LinuxCNC output routinely does: numbered/named/global parameters (
[#100](https://github.com/ChestnutLabs/gcode-preview/issues/100),#<name>,#<_global>),assignment (
#n = <expr>), and bracket expressions ([ … ]) usable wherever a numeric word value isexpected (
X[[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)+2],F[#feed],Z#<depth>). Previously these were silently dropped, so parametricprograms rendered honest-but-empty; they now resolve to real geometry.
A new self-contained module (
rs274.ts) provides a pure recursive-descent evaluator — the RS274NGCoperator + function set with correct precedence (
**>*/MOD>+-> comparisons > logical),degree-based trig, LinuxCNC
MOD/EQsemantics,FIX/FUP/ROUND, indirect##nand computed#[expr]references, and a read-only system-parameter allow-list ([#5420](https://github.com/ChestnutLabs/gcode-preview/issues/5420)–[#5422](https://github.com/ChestnutLabs/gcode-preview/issues/5422)= current position).It is bounded and safe: a depth guard on nested brackets, no
eval/Function, and a cappedparameter store, so a hostile program can waste only bounded CPU. Malformed input never throws out of
the parse — the offending word is dropped with a specific disclosure (
rs274-bad-expression,rs274-uninitialized-param,rs274-unsupported-sysparam) and parsing continues.Honest and additive: a new
parametricProgramcapability (knownwhen executed,unavailableotherwise). Engaged only on lines that use
#/[— every FDM and simple-CNC line takes the untouchedlexer and is byte-identical (native goldens regenerated for the additive capability key only; the
detection gate adds ~2.7% to a large FDM parse, geometry unchanged).
Scope: Phase 1 (parameters + expressions) only. O-word control flow (
if/while/sub/call) is alater phase and is not yet supported — such constructs are ignored (and will be disclosed) for now.
#413
bcb4b78Thanks @sobechestnut-dev! - feat(parser): RS274NGC O-word control flow (DD-017 Phase 2, #189 phase 7)Building on Phase 1's parameters + expressions, the parser now executes the RS274NGC O-word control
structures that CAM / LinuxCNC programs use to generate geometry:
if/elseif/else/endif,while/endwhile,do/while,repeat/endrepeat, andbreak/continue. A bolt-circle written asa
whileloop over a computed angle, a pocket cut by arepeat, anifthat selects a tool path —these previously ran once (or not at all) because the parser is forward-only; they now resolve to the
full, correct toolpath.
A new module (
rs274-flow.ts) is the program-buffered interpreter of DD-017 D1: engaged only when aprogram actually contains an O-word control line (a cheap detection scan), it buffers the program, builds
a block tree, and executes it — feeding every non-control line back through the same engine so
geometry/params/expressions stay on one code path. Loops evaluate their conditions/counts against the
same shared parameter store, so
#<i> = [#<i> + 1]inside a loop body does what you expect.Bounded and safe (this is now a small interpreter). A new
maxProgramIterationslimit (default1_000_000) bounds total loop work — charged per loop pass and per statement executed inside aloop body, so a large body cannot multiply the real work past the cap — and stops a runaway
o while [1]with a partial result and a
rs274-iteration-limitdisclosure. Structural nesting is capped; loops areordinary iteration, not recursion, so an adversarial program can waste only bounded CPU — it cannot hang,
blow the stack, or OOM the worker. There is no
evaland no I/O.Honest and additive. Clean flow keeps
parametricProgram: 'known'; a degraded run (a hit iterationcap, an unbalanced or unsupported O-word, or a malformed condition) reports
'approximated'and alwaysemits a specific disclosure — never silently wrong. Subroutines (
sub/call/return) are a later phaseand are explicitly disclosed as unsupported and not executed inline. Non-parametric input never
enters the interpreter and stays byte-identical (both golden suites unchanged). Streaming input, which
cannot re-run a line range from a partial stream, discloses and runs linearly.
#415
dc89056Thanks @sobechestnut-dev! - feat(parser): RS274NGC subroutines (DD-017 Phase 3, #189 phase 7)Completes the RS274NGC programming layer: the parser now executes in-file O-word subroutines —
o<id> sub/endsub,o<id> call [args], ando<id> return. Acallbinds its arguments to[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)..[#30](https://github.com/ChestnutLabs/gcode-preview/issues/30)in a fresh call frame, runs the named sub's body, and returns;
returnexits early. Subroutines may beforward-referenced (a
callbefore thesubthat defines it) and may recurse. This is how CAM/LinuxCNCprograms factor repeated geometry (a "stamp" drilled at many positions, a parametric feature reused
across a part).
Correct scoping (recursion-safe). Numbered parameters
[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)..[#30](https://github.com/ChestnutLabs/gcode-preview/issues/30)and non-underscore named parameters(
#<local>) are LOCAL to each call frame;[#31](https://github.com/ChestnutLabs/gcode-preview/issues/31)+ and underscore-named (#<_global>) are shared globals —matching LinuxCNC. A recursive sub's
[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)therefore cannot clobber its caller's, and a sub's locals do notleak out. The main program runs in the base scope, so a program that uses no subroutines behaves exactly
as before (byte-identical; both golden suites unchanged).
Bounded and safe. A new
maxCallDepthlimit (default50) caps subroutine recursion — an unboundedor mutually-recursive
callis disclosed (rs274-call-depth) and skipped, never a stack overflow. Aruntime execution-depth backstop additionally bounds the combined structural-nesting + call-depth JS
recursion. A sub called inside a loop still charges its body work against
maxProgramIterations, so a hugesub body cannot multiply work past the cap.
break/continue/returnare frame-local — they never jumpacross a call boundary.
Honest. Clean subroutine execution keeps
parametricProgram: 'known'; a degraded run (unknown sub,recursion-limit hit, duplicate definition, misplaced
return) reports'approximated'with a specificdisclosure — never silently wrong. Subroutine return VALUES are not yet propagated to the caller (a
documented Phase-3 limitation;
returnacts as an early exit). External subroutine files (M98,o<name> callresolving to a separate.ngc) remain an explicit non-goal (no I/O in the parser).Patch Changes
@chestnutlabs/gcode-preview-core@0.18.0
Minor Changes
#418
c950339Thanks @sobechestnut-dev! - feat(renderer): interactive view capture() → Blob (DD-030 D1)The interactive viewer can now hand back what is on screen right now as an image
Blob— for auser-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New
capture(opts?)whereoptsis{ width?, height?, format?, quality?, background? }(all optional; defaults match the liveview).
Available on every interactive surface:
GcodePreviewControls.capture()(so the Vue, React, Svelte, andWeb-Component adapters all inherit it — the Web Component also exposes an imperative
capture()method),and
ModelViewer.capture()on the model-viewer handle. The toolpathToolpathRendererand the sharedInteractiveStagecarry the implementation.Mechanism (render-to-target). Capture renders the current scene + active camera into an off-screen
WebGLRenderTargetat the requested size and reads it back, rather than flipping the interactivecontext's
preserveDrawingBuffer(which would tax every interactive frame). That gives an arbitraryoutput size and an independent/transparent background without disturbing the live view, and reuses the
headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect
so it isn't distorted; the live view is repainted afterward. The library returns the
Bloband nevertriggers a download — the caller owns the pixels (same contract as
renderStill).Honest. When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage
is disposed / its context is lost,
capture()rejects with a typedCaptureUnsupportedError(
code: 'E_CAPTURE_UNSUPPORTED') — never fabricated output. Purely additive (a new optional method on therenderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer
interoperability batch (bed + per-plate scope + capture).
Patch Changes
5144890,bcb4b78,dc89056,0ebeadf,c950339]:@chestnutlabs/gcode-preview-element@0.18.0
Minor Changes
#418
c950339Thanks @sobechestnut-dev! - feat(renderer): interactive view capture() → Blob (DD-030 D1)The interactive viewer can now hand back what is on screen right now as an image
Blob— for auser-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New
capture(opts?)whereoptsis{ width?, height?, format?, quality?, background? }(all optional; defaults match the liveview).
Available on every interactive surface:
GcodePreviewControls.capture()(so the Vue, React, Svelte, andWeb-Component adapters all inherit it — the Web Component also exposes an imperative
capture()method),and
ModelViewer.capture()on the model-viewer handle. The toolpathToolpathRendererand the sharedInteractiveStagecarry the implementation.Mechanism (render-to-target). Capture renders the current scene + active camera into an off-screen
WebGLRenderTargetat the requested size and reads it back, rather than flipping the interactivecontext's
preserveDrawingBuffer(which would tax every interactive frame). That gives an arbitraryoutput size and an independent/transparent background without disturbing the live view, and reuses the
headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect
so it isn't distorted; the live view is repainted afterward. The library returns the
Bloband nevertriggers a download — the caller owns the pixels (same contract as
renderStill).Honest. When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage
is disposed / its context is lost,
capture()rejects with a typedCaptureUnsupportedError(
code: 'E_CAPTURE_UNSUPPORTED') — never fabricated output. Purely additive (a new optional method on therenderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer
interoperability batch (bed + per-plate scope + capture).
Patch Changes
5144890,bcb4b78,dc89056,0ebeadf,c950339]:@chestnutlabs/gcode-renderer-three@0.18.0
Minor Changes
#416
0ebeadfThanks @sobechestnut-dev! - feat(renderer): non-rectangular build-bed geometry (DD-030 D3)The build volume can now draw an honest non-rectangular bed — delta, round, or irregular — instead of
only a rectangle.
BuildVolumeDefgains an optionalshape?: BedShape(
{kind:'rect'} | {kind:'circular', center, diameter} | {kind:'polygon', points}): a circle ispolygonized, the outline is filled and drawn, and the floor grid is clipped to the outline so the
printable area reads correctly (a round bed is no longer a square with a circle floating over it). The
meshescape hatch for a fully custom bed mesh is reserved for a later phase.machineToVolume()now maps a discoveredMachineGeometry.bedof kindcircular/polygononto thatshape, so a discovered round/delta bed renders as its true outline instead of being collapsed to itsbounding rectangle (a visible improvement for delta/round printers). Callers supply the shape (from a
machine profile, a config, wherever); the renderer just draws the polygon — no profile parsing in the
library, no vendor semantics baked in.
Additive and safe: a bed with no
shape(or{kind:'rect'}) takes the original rectangular path and isbyte-identical — the rectangular grid, plate, cage, excluded-region outlines, and origin tripod are
unchanged. The volume cage stays a bounding box (the bed outline carries the shape). First increment of
the DD-030 renderer/viewer interoperability batch.
#418
c950339Thanks @sobechestnut-dev! - feat(renderer): interactive view capture() → Blob (DD-030 D1)The interactive viewer can now hand back what is on screen right now as an image
Blob— for auser-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New
capture(opts?)whereoptsis{ width?, height?, format?, quality?, background? }(all optional; defaults match the liveview).
Available on every interactive surface:
GcodePreviewControls.capture()(so the Vue, React, Svelte, andWeb-Component adapters all inherit it — the Web Component also exposes an imperative
capture()method),and
ModelViewer.capture()on the model-viewer handle. The toolpathToolpathRendererand the sharedInteractiveStagecarry the implementation.Mechanism (render-to-target). Capture renders the current scene + active camera into an off-screen
WebGLRenderTargetat the requested size and reads it back, rather than flipping the interactivecontext's
preserveDrawingBuffer(which would tax every interactive frame). That gives an arbitraryoutput size and an independent/transparent background without disturbing the live view, and reuses the
headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect
so it isn't distorted; the live view is repainted afterward. The library returns the
Bloband nevertriggers a download — the caller owns the pixels (same contract as
renderStill).Honest. When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage
is disposed / its context is lost,
capture()rejects with a typedCaptureUnsupportedError(
code: 'E_CAPTURE_UNSUPPORTED') — never fabricated output. Purely additive (a new optional method on therenderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer
interoperability batch (bed + per-plate scope + capture).
Patch Changes
@chestnutlabs/gcode-bgcode@0.18.0
Patch Changes
@chestnutlabs/gcode-colors@0.18.0
Patch Changes
@chestnutlabs/gcode-containers@0.18.0
Patch Changes
@chestnutlabs/gcode-dialects@0.18.0
Patch Changes
@chestnutlabs/gcode-preview-react@0.18.0
Patch Changes
5144890,bcb4b78,dc89056,0ebeadf,c950339]:@chestnutlabs/gcode-preview-svelte@0.18.0
Patch Changes
5144890,bcb4b78,dc89056,0ebeadf,c950339]:@chestnutlabs/gcode-preview-vue@0.18.0
Patch Changes
5144890,bcb4b78,dc89056,0ebeadf,c950339]:@chestnutlabs/gcode-renderer-2d@0.18.0
Patch Changes
@chestnutlabs/toolpath-core@0.18.0