@glissade/scene 0.33.0-pre.0 → 0.34.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/chart.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { V as NodeProps, i as Group, u as Rect } from "./nodes.js";
1
+ import { H as NodeProps, a as Group, d as Rect } from "./nodes.js";
2
2
 
3
3
  //#region src/chart.d.ts
4
4
 
package/dist/describe.js CHANGED
@@ -22,7 +22,7 @@ import { easings, listValueTypes } from "@glissade/core";
22
22
  * never pulled onto the base embed path — a scene that never calls `describe()`
23
23
  * pays zero bytes for it.
24
24
  */
25
- const RAW_VERSION = "0.33.0-pre.0";
25
+ const RAW_VERSION = "0.34.0-pre.0";
26
26
  const PACKAGE_VERSION = RAW_VERSION.includes("GLISSADE_".concat("VERSION")) ? "0.0.0-dev" : RAW_VERSION;
27
27
  /** Arity of a value type's numeric repr: vec2/vec2-arc → 2, number → 1; others (color/paint/path/string/boolean) carry no scalar arity. */
28
28
  function arityOf(type) {
@@ -52,7 +52,10 @@ function expectsToType(expects) {
52
52
  * only each node's OWN construction surface.
53
53
  */
54
54
  const CONSTRUCTION_PROP_META = {
55
- Group: { children: { type: "Node[]" } },
55
+ Group: {
56
+ children: { type: "Node[]" },
57
+ clip: { type: "ClipRegion" }
58
+ },
56
59
  Rect: {
57
60
  sketch: { type: "SketchStyle" },
58
61
  sketchFill: { type: "HachureSpec" },
@@ -190,6 +193,7 @@ const LAYOUT_CONSTRUCTION_META = {
190
193
  function describeLayoutNode() {
191
194
  const props = describeNode(new Group(), "Group").props;
192
195
  delete props.children;
196
+ delete props["clip"];
193
197
  for (const [prop, spec] of Object.entries(LAYOUT_ANIMATABLE)) {
194
198
  const arity = arityOf(spec.type);
195
199
  props[prop] = {
@@ -351,6 +355,12 @@ const HELPERS = [
351
355
  import: "@glissade/scene",
352
356
  usage: "motionBlur(child: Node, opts?: { id?, shutter?: number, samples?: number }): MotionBlur"
353
357
  },
358
+ {
359
+ name: "trackMatte",
360
+ summary: "Track-matte: mask CONTENT by a MATTE layer's alpha (default) or luminance ('luma'). Content renders into an isolated layer, then the matte composites destination-in — pixels survive only where the matte is opaque. Both subtrees animate like ordinary nodes (a sliding shape wipes text in, a scaling blob irises a photo). Byte-exact on Skia; browser-vs-Skia pixel parity is perceptual at anti-aliased matte edges; backend-dom (preview tier) degrades with data-approx.",
361
+ import: "@glissade/scene",
362
+ usage: "trackMatte(content: Node, matte: Node, opts?: { id?, mode?: 'alpha' | 'luma' }): TrackMatte"
363
+ },
354
364
  {
355
365
  name: "clip",
356
366
  summary: "A reusable, target-agnostic motion captured once as a relative-time key schedule, then applied to a node at a wall-clock start time. Build-time sugar: clip.apply() compiles to ordinary Track[].",
@@ -174,6 +174,17 @@ type DrawCommand = {
174
174
  filters: FilterSpec[];
175
175
  shader?: ShaderRef;
176
176
  cacheKey?: string;
177
+ /**
178
+ * 0.34 track-matte: this layer is a MATTE for the layer it composites
179
+ * onto — 'alpha' keeps destination pixels where this layer is opaque
180
+ * (native destination-in, byte-exact); 'luma' first converts this
181
+ * layer's luminance to alpha via the shared straight-alpha CPU kernel
182
+ * (the mesh-kernel discipline), then applies destination-in. Emitted
183
+ * by trackMatte() inside its isolated outer group; an optional field
184
+ * on the shader?/cacheKey? extension precedent — BlendMode stays a
185
+ * closed union.
186
+ */
187
+ matte?: 'alpha' | 'luma';
177
188
  } | {
178
189
  op: 'popGroup';
179
190
  };
package/dist/grid.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { V as NodeProps, i as Group, z as Node } from "./nodes.js";
1
+ import { B as Node, H as NodeProps, a as Group } from "./nodes.js";
2
2
 
3
3
  //#region src/grid.d.ts
4
4
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as Mat2x3, D as matEquals, E as invert, O as multiply, S as IDENTITY, T as fromTRS, _ as StrokeStyle, a as FilterSpec, b as glow, c as MeshInterpolation, d as Paint, f as PathSeg, g as ShaderRef, h as ResourceId, i as DrawCommand, l as MeshPaint, m as Resource, n as DisplayList, o as FilterValidationError, p as Rect$1, r as DisplayListBuilder, s as FontSpec, t as BlendMode, u as MeshPoint, v as createDisplayListBuilder, w as applyToPoint, x as validateFilters, y as filtersToCanvasFilter } from "./displayList.js";
2
- import { $ as measureWrappedText, A as resolveSketch, B as NodeConstructionError, C as Polyline, D as arcLength, E as SketchValidationError, F as AnchorSpec, G as TextMeasurer, H as PropInit, I as BindablePropTarget, J as __resetEstimateWarnings, K as TextMetricsLite, L as EvalContext, M as sketchStrokes, N as validateHachure, O as flatten, P as validateSketch, Q as isEstimatingMeasurer, R as HitArea, S as HachureSpec, T as SketchStyle, U as resolveAnchor, V as NodeProps, W as MEASURE_QUANTUM_PX, X as breakLines, Y as assertFiniteFontSize, Z as estimatingMeasurer, _ as WordBox, a as ImageNode, b as revealSchedule, c as Path, d as RevealMark, et as quantize, f as ShapeProps, g as VideoProps, h as Video, i as Group, j as roughen, k as hachureLines, l as PathProps, m as TextProps, n as Custom, nt as segmentWords, o as ImageProps, p as Text, q as WrappedTextMetrics, r as GraphemeBox, rt as setDefaultMeasurer, s as LineBox, t as Circle, tt as segmentGraphemes, u as Rect, v as coercePathData, w as ResolvedSketch, x as roundedRectSegs, y as pathFromSegs, z as Node } from "./nodes.js";
2
+ import { $ as isEstimatingMeasurer, A as hachureLines, B as Node, C as HachureSpec, D as SketchValidationError, E as SketchStyle, F as validateSketch, G as MEASURE_QUANTUM_PX, H as NodeProps, I as AnchorSpec, J as WrappedTextMetrics, K as TextMeasurer, L as BindablePropTarget, M as roughen, N as sketchStrokes, O as arcLength, P as validateHachure, Q as estimatingMeasurer, R as EvalContext, S as roundedRectSegs, T as ResolvedSketch, U as PropInit, V as NodeConstructionError, W as resolveAnchor, X as assertFiniteFontSize, Y as __resetEstimateWarnings, Z as breakLines, _ as VideoProps, a as Group, b as pathFromSegs, c as LineBox, d as Rect, et as measureWrappedText, f as RevealMark, g as Video, h as TextProps, i as GraphemeBox, it as setDefaultMeasurer, j as resolveSketch, k as flatten, l as Path, m as Text, n as ClipRegion, nt as segmentGraphemes, o as ImageNode, p as ShapeProps, q as TextMetricsLite, r as Custom, rt as segmentWords, s as ImageProps, t as Circle, tt as quantize, u as PathProps, v as WordBox, w as Polyline, x as revealSchedule, y as coercePathData, z as HitArea } from "./nodes.js";
3
3
  import { t as collapseReplacer } from "./collapseReplacer.js";
4
4
  import { a as SceneModule, c as evaluate, i as SceneInit, n as ReservedNodeIdError, o as bindScene, r as Scene, s as createScene, t as DuplicateNodeIdError } from "./scene.js";
5
5
  import { a as LayoutEngineMissingError, c as requireLayoutEngine, i as LayoutEngine, l as setLayoutEngine, n as LayoutChildSpec, r as LayoutContainerSpec, s as getLayoutEngine, t as LayoutBox } from "./layoutEngine.js";
@@ -464,6 +464,30 @@ declare class MotionBlur extends Group {
464
464
  * sub-frame motion blur. Wrap the moving content; its background stays crisp. */
465
465
  declare function motionBlur(child: Node, props?: Omit<MotionBlurProps, 'children'>): MotionBlur;
466
466
  //#endregion
467
+ //#region src/matte.d.ts
468
+ interface TrackMatteProps extends NodeProps {
469
+ /** the visible content — masked by the matte. */
470
+ content: Node;
471
+ /** the mask — its alpha (or luma) decides which content pixels survive. */
472
+ matte: Node;
473
+ /** 'alpha' (default): matte opacity masks. 'luma': matte brightness masks
474
+ * (white = keep, black = erase), via the shared deterministic CPU kernel. */
475
+ mode?: 'alpha' | 'luma';
476
+ }
477
+ declare class TrackMatte extends Group {
478
+ get describeType(): string;
479
+ readonly content: Node;
480
+ readonly matte: Node;
481
+ readonly mode: 'alpha' | 'luma';
482
+ constructor(props: TrackMatteProps);
483
+ /** destination-in must not leak past this node — always isolate in a layer. */
484
+ protected requiresGroup(): boolean;
485
+ protected draw(out: DisplayListBuilder, ctx: EvalContext): void;
486
+ }
487
+ /** `trackMatte(photo, circleWipe)` — photo visible only inside the (animatable)
488
+ * circle. Pass `{ mode: 'luma' }` to mask by matte brightness instead of alpha. */
489
+ declare function trackMatte(content: Node, matte: Node, props?: Omit<TrackMatteProps, 'content' | 'matte'>): TrackMatte;
490
+ //#endregion
467
491
  //#region src/raster2d.d.ts
468
492
  /** A backend gradient handle (DOM CanvasGradient and @napi-rs CanvasGradient both satisfy it). */
469
493
  interface CanvasGradientLike {
@@ -669,6 +693,13 @@ declare class Raster2D<TCanvas extends CanvasLike, TPath extends PathLike, TDraw
669
693
  * the cache entry (hit); the composite params (opacity/blend/filters) always
670
694
  * come from the LIVE pushGroup command, never the cache.
671
695
  */
696
+ /**
697
+ * 0.34 luma matte: convert a layer's LUMINANCE to its alpha, in place —
698
+ * `a' = round(luma(r,g,b) × a / 255)` with Rec.709 integer coefficients over
699
+ * STRAIGHT (non-premultiplied) RGBA, the same discipline as the mesh kernel,
700
+ * so both backends run one deterministic CPU pass and the result byte-compares.
701
+ */
702
+ private lumaToAlpha;
672
703
  private composite;
673
704
  /** The command walk — order and operations identical to the pre-extraction twins. */
674
705
  render(target: TCanvas, list: DisplayList): void;
@@ -710,4 +741,4 @@ declare function meshRasterSize(bw: number, bh: number): {
710
741
  h: number;
711
742
  };
712
743
  //#endregion
713
- export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type Bounds, type CanvasLike, Circle, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, Echo, type EchoProps, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontByteLoader, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayerCacheEntry, type LayerStore, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, MotionBlur, type MotionBlurProps, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, type WrappedTextMetrics, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, motionBlur, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
744
+ export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type Bounds, type CanvasLike, Circle, type ClipRegion, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, Echo, type EchoProps, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontByteLoader, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayerCacheEntry, type LayerStore, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, MotionBlur, type MotionBlurProps, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, TrackMatte, type TrackMatteProps, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, type WrappedTextMetrics, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, motionBlur, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, trackMatte, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
package/dist/index.js CHANGED
@@ -671,6 +671,51 @@ var ShaderEffect = class extends Group {
671
671
  };
672
672
  }
673
673
  };
674
+ //#endregion
675
+ //#region src/matte.ts
676
+ var TrackMatte = class extends Group {
677
+ get describeType() {
678
+ return "TrackMatte";
679
+ }
680
+ content;
681
+ matte;
682
+ mode;
683
+ constructor(props) {
684
+ const { content, matte, mode, ...rest } = props;
685
+ super({
686
+ ...rest,
687
+ children: [content, matte]
688
+ });
689
+ this.content = content;
690
+ this.matte = matte;
691
+ this.mode = mode ?? "alpha";
692
+ }
693
+ /** destination-in must not leak past this node — always isolate in a layer. */
694
+ requiresGroup() {
695
+ return true;
696
+ }
697
+ draw(out, ctx) {
698
+ this.content.emit(out, ctx);
699
+ out.push({
700
+ op: "pushGroup",
701
+ opacity: 1,
702
+ blend: "source-over",
703
+ filters: [],
704
+ matte: this.mode
705
+ });
706
+ this.matte.emit(out, ctx);
707
+ out.push({ op: "popGroup" });
708
+ }
709
+ };
710
+ /** `trackMatte(photo, circleWipe)` — photo visible only inside the (animatable)
711
+ * circle. Pass `{ mode: 'luma' }` to mask by matte brightness instead of alpha. */
712
+ function trackMatte(content, matte, props = {}) {
713
+ return new TrackMatte({
714
+ ...props,
715
+ content,
716
+ matte
717
+ });
718
+ }
674
719
  const smoothstep = (u) => u * u * (3 - 2 * u);
675
720
  const GAUSS_K = 2.4;
676
721
  const GAUSS_NORM = 1 - Math.exp(-5.76 / 2);
@@ -1143,6 +1188,24 @@ var Raster2D = class {
1143
1188
  * the cache entry (hit); the composite params (opacity/blend/filters) always
1144
1189
  * come from the LIVE pushGroup command, never the cache.
1145
1190
  */
1191
+ /**
1192
+ * 0.34 luma matte: convert a layer's LUMINANCE to its alpha, in place —
1193
+ * `a' = round(luma(r,g,b) × a / 255)` with Rec.709 integer coefficients over
1194
+ * STRAIGHT (non-premultiplied) RGBA, the same discipline as the mesh kernel,
1195
+ * so both backends run one deterministic CPU pass and the result byte-compares.
1196
+ */
1197
+ lumaToAlpha(ctx, w, h) {
1198
+ const t = ctx.getTransform();
1199
+ ctx.resetTransform();
1200
+ const img = ctx.getImageData(0, 0, w, h);
1201
+ const d = img.data;
1202
+ for (let i = 0; i < d.length; i += 4) {
1203
+ const luma = (d[i] * 2126 + d[i + 1] * 7152 + d[i + 2] * 722) / 255e4;
1204
+ d[i + 3] = Math.round(luma * d[i + 3]);
1205
+ }
1206
+ ctx.putImageData(img, 0, 0);
1207
+ ctx.setTransform(t);
1208
+ }
1146
1209
  composite(parent, parentLayer, drawable, bounds, unbounded, shaderReplaced, opacity, blend, filter, filters, w, h) {
1147
1210
  const hasFilter = filter !== void 0 && filter !== "none";
1148
1211
  const outset = hasFilter ? filterOutset(filters) : 0;
@@ -1295,7 +1358,7 @@ var Raster2D = class {
1295
1358
  if (lruKey !== void 0) {
1296
1359
  const hit = this.cacheTouch(lruKey);
1297
1360
  if (hit) {
1298
- this.composite(parent, top(), hit.canvas, hit.bounds, hit.unbounded, false, cmd.opacity, cmd.blend, filtersToCanvasFilter(cmd.filters), cmd.filters, w, h);
1361
+ this.composite(parent, top(), hit.canvas, hit.bounds, hit.unbounded, false, cmd.opacity, cmd.matte !== void 0 ? "destination-in" : cmd.blend, filtersToCanvasFilter(cmd.filters), cmd.filters, w, h);
1299
1362
  let depth = 1;
1300
1363
  while (depth > 0 && ++ci < commands.length) {
1301
1364
  const c = commands[ci];
@@ -1317,7 +1380,7 @@ var Raster2D = class {
1317
1380
  bounds: disk.bounds,
1318
1381
  unbounded: disk.unbounded
1319
1382
  });
1320
- this.composite(parent, top(), canvas, disk.bounds, disk.unbounded, false, cmd.opacity, cmd.blend, filtersToCanvasFilter(cmd.filters), cmd.filters, w, h);
1383
+ this.composite(parent, top(), canvas, disk.bounds, disk.unbounded, false, cmd.opacity, cmd.matte !== void 0 ? "destination-in" : cmd.blend, filtersToCanvasFilter(cmd.filters), cmd.filters, w, h);
1321
1384
  let depth = 1;
1322
1385
  while (depth > 0 && ++ci < commands.length) {
1323
1386
  const c = commands[ci];
@@ -1340,6 +1403,7 @@ var Raster2D = class {
1340
1403
  filter: filtersToCanvasFilter(cmd.filters),
1341
1404
  filters: cmd.filters,
1342
1405
  ...cmd.shader !== void 0 ? { shader: cmd.shader } : {},
1406
+ ...cmd.matte !== void 0 ? { matte: cmd.matte } : {},
1343
1407
  bounds: null,
1344
1408
  unbounded: false,
1345
1409
  ...lruKey !== void 0 ? { cacheStoreKey: lruKey } : {}
@@ -1370,7 +1434,8 @@ var Raster2D = class {
1370
1434
  emitDevWarning("ShaderEffect pass skipped: no shader runner here (headless or webgpu-less browser) — subtree composites unfiltered (§3.7 caps.shaders)");
1371
1435
  }
1372
1436
  }
1373
- this.composite(parent, top(), drawable, layer.bounds, layer.unbounded, shaderReplaced, layer.opacity, layer.blend, layer.filter, layer.filters, w, h);
1437
+ if (layer.matte === "luma" && !shaderReplaced) this.lumaToAlpha(layer.ctx, w, h);
1438
+ this.composite(parent, top(), drawable, layer.bounds, layer.unbounded, shaderReplaced, layer.opacity, layer.matte !== void 0 ? "destination-in" : layer.blend, layer.filter, layer.filters, w, h);
1374
1439
  if (layer.cacheStoreKey !== void 0 && !shaderReplaced) {
1375
1440
  this.cacheStore(layer.cacheStoreKey, {
1376
1441
  canvas: layer.canvas,
@@ -1396,4 +1461,4 @@ var Raster2D = class {
1396
1461
  }
1397
1462
  };
1398
1463
  //#endregion
1399
- export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DeterminismViolationError, DuplicateNodeIdError, EachError, Echo, FilterValidationError, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, MotionBlur, NODE_TAXONOMY, Node, NodeConstructionError, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, Video, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, motionBlur, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
1464
+ export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DeterminismViolationError, DuplicateNodeIdError, EachError, Echo, FilterValidationError, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, MotionBlur, NODE_TAXONOMY, Node, NodeConstructionError, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, TrackMatte, Video, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, motionBlur, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, trackMatte, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
@@ -1,5 +1,5 @@
1
1
  import { r as DisplayListBuilder } from "./displayList.js";
2
- import { G as TextMeasurer, H as PropInit, L as EvalContext, V as NodeProps, i as Group, z as Node } from "./nodes.js";
2
+ import { B as Node, H as NodeProps, K as TextMeasurer, R as EvalContext, U as PropInit, a as Group } from "./nodes.js";
3
3
  import { r as LayoutContainerSpec } from "./layoutEngine.js";
4
4
  import { BindableSignal } from "@glissade/core";
5
5
 
package/dist/motion.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { H as PropInit, V as NodeProps, c as Path, z as Node } from "./nodes.js";
1
+ import { B as Node, H as NodeProps, U as PropInit, l as Path } from "./nodes.js";
2
2
  import { BindableSignal, PathValue, Vec2 } from "@glissade/core";
3
3
 
4
4
  //#region src/motionPath.d.ts
package/dist/nodes.d.ts CHANGED
@@ -434,6 +434,22 @@ declare function sketchStrokes(segs: readonly PathSeg[], style: SketchStyle, see
434
434
  declare abstract class Custom extends Node {}
435
435
  /** Rounded-rect path segments — Rect's outline, shared with Highlight. */
436
436
  declare function roundedRectSegs(x: number, y: number, w: number, h: number, r: number): PathSeg[];
437
+ /**
438
+ * A Group clip region (0.34), in the group's LOCAL coordinates: a (rounded)
439
+ * rect centered on `[x ?? 0, y ?? 0]` — matching the center-anchor convention —
440
+ * or an explicit `PathSeg[]` outline (`pathFromSvg(...)` output works directly).
441
+ * Children paint only inside the region; it clips the group's own layer, so a
442
+ * rounded corner anti-aliases in LOCAL space and the whole subtree byte-compares
443
+ * on Skia. Construction-only (not a track target); clipping is render-time only
444
+ * — hit-testing is unaffected in v1 (a clipped child still hits).
445
+ */
446
+ type ClipRegion = {
447
+ w: number;
448
+ h: number;
449
+ r?: number;
450
+ x?: number;
451
+ y?: number;
452
+ } | PathSeg[];
437
453
  declare class Group extends Node {
438
454
  #private;
439
455
  /**
@@ -448,8 +464,11 @@ declare class Group extends Node {
448
464
  */
449
465
  get describeType(): string;
450
466
  readonly children: Node[];
467
+ /** Clip region for this group's children (0.34) — see {@link ClipRegion}. */
468
+ readonly clip?: ClipRegion;
451
469
  constructor(props?: NodeProps & {
452
470
  children?: Node[];
471
+ clip?: ClipRegion;
453
472
  });
454
473
  /** Record the structural version as a dependency — call inside a computed
455
474
  * that walks `children` so add()/remove() invalidate it. */
@@ -457,6 +476,10 @@ declare class Group extends Node {
457
476
  add(child: Node): this;
458
477
  /** Remove a child (the reactive counterpart to add()); no-op if absent. */
459
478
  remove(child: Node): this;
479
+ /** A clip demands a group layer: children must rasterize into an isolated
480
+ * layer the region applies to (and the clip op lands INSIDE the cacheKey'd
481
+ * draw slice, so a changed region correctly misses the layer cache). */
482
+ protected requiresGroup(): boolean;
460
483
  protected draw(out: DisplayListBuilder, ctx: EvalContext): void;
461
484
  }
462
485
  /** A color string is sugar for a solid `color` Paint; a Paint passes through. */
@@ -900,4 +923,4 @@ interface RevealMark {
900
923
  */
901
924
  declare function revealSchedule(text: Text, reveal: Track<number>, measurer?: TextMeasurer): RevealMark[];
902
925
  //#endregion
903
- export { measureWrappedText as $, resolveSketch as A, NodeConstructionError as B, Polyline as C, arcLength as D, SketchValidationError as E, AnchorSpec as F, TextMeasurer as G, PropInit as H, BindablePropTarget as I, __resetEstimateWarnings as J, TextMetricsLite as K, EvalContext as L, sketchStrokes as M, validateHachure as N, flatten as O, validateSketch as P, isEstimatingMeasurer as Q, HitArea as R, HachureSpec as S, SketchStyle as T, resolveAnchor as U, NodeProps as V, MEASURE_QUANTUM_PX as W, breakLines as X, assertFiniteFontSize as Y, estimatingMeasurer as Z, WordBox as _, ImageNode as a, revealSchedule as b, Path as c, RevealMark as d, quantize as et, ShapeProps as f, VideoProps as g, Video as h, Group as i, roughen as j, hachureLines as k, PathProps as l, TextProps as m, Custom as n, segmentWords as nt, ImageProps as o, Text as p, WrappedTextMetrics as q, GraphemeBox as r, setDefaultMeasurer as rt, LineBox as s, Circle as t, segmentGraphemes as tt, Rect as u, coercePathData as v, ResolvedSketch as w, roundedRectSegs as x, pathFromSegs as y, Node as z };
926
+ export { isEstimatingMeasurer as $, hachureLines as A, Node as B, HachureSpec as C, SketchValidationError as D, SketchStyle as E, validateSketch as F, MEASURE_QUANTUM_PX as G, NodeProps as H, AnchorSpec as I, WrappedTextMetrics as J, TextMeasurer as K, BindablePropTarget as L, roughen as M, sketchStrokes as N, arcLength as O, validateHachure as P, estimatingMeasurer as Q, EvalContext as R, roundedRectSegs as S, ResolvedSketch as T, PropInit as U, NodeConstructionError as V, resolveAnchor as W, assertFiniteFontSize as X, __resetEstimateWarnings as Y, breakLines as Z, VideoProps as _, Group as a, pathFromSegs as b, LineBox as c, Rect as d, measureWrappedText as et, RevealMark as f, Video as g, TextProps as h, GraphemeBox as i, setDefaultMeasurer as it, resolveSketch as j, flatten as k, Path as l, Text as m, ClipRegion as n, segmentGraphemes as nt, ImageNode as o, ShapeProps as p, TextMetricsLite as q, Custom as r, segmentWords as rt, ImageProps as s, Circle as t, quantize as tt, PathProps as u, WordBox as v, Polyline as w, revealSchedule as x, coercePathData as y, HitArea as z };
package/dist/nodes.js CHANGED
@@ -469,7 +469,7 @@ const LAYOUT = [
469
469
  "children"
470
470
  ];
471
471
  const NODE_CONSTRUCTION_PROP_NAMES = {
472
- Group: ["children"],
472
+ Group: ["children", "clip"],
473
473
  Rect: SKETCH,
474
474
  Circle: SKETCH,
475
475
  Path: SKETCH,
@@ -1251,6 +1251,8 @@ var Group = class Group extends Node {
1251
1251
  return "Group";
1252
1252
  }
1253
1253
  children;
1254
+ /** Clip region for this group's children (0.34) — see {@link ClipRegion}. */
1255
+ clip;
1254
1256
  /** Version bumped on structural child mutation, so a dependency-tracked memo
1255
1257
  * (e.g. Layout's computed) re-runs when the child SET changes — not only when
1256
1258
  * a participating prop signal does. */
@@ -1258,6 +1260,7 @@ var Group = class Group extends Node {
1258
1260
  constructor(props = {}) {
1259
1261
  super(props);
1260
1262
  this.children = props.children ?? [];
1263
+ if (props.clip !== void 0) this.clip = props.clip;
1261
1264
  for (const child of this.children) child.parent = this;
1262
1265
  if (new.target === Group) this.checkProps(props);
1263
1266
  }
@@ -1282,12 +1285,31 @@ var Group = class Group extends Node {
1282
1285
  }
1283
1286
  return this;
1284
1287
  }
1288
+ /** A clip demands a group layer: children must rasterize into an isolated
1289
+ * layer the region applies to (and the clip op lands INSIDE the cacheKey'd
1290
+ * draw slice, so a changed region correctly misses the layer cache). */
1291
+ requiresGroup() {
1292
+ return this.clip !== void 0 || super.requiresGroup();
1293
+ }
1285
1294
  draw(out, ctx) {
1295
+ if (this.clip !== void 0) {
1296
+ const segs = Array.isArray(this.clip) ? this.clip : roundedRectSegs((this.clip.x ?? 0) - this.clip.w / 2, (this.clip.y ?? 0) - this.clip.h / 2, this.clip.w, this.clip.h, this.clip.r ?? 0);
1297
+ out.push({ op: "save" });
1298
+ out.push({
1299
+ op: "clip",
1300
+ path: out.resource({
1301
+ kind: "path",
1302
+ segs
1303
+ }),
1304
+ rule: "nonzero"
1305
+ });
1306
+ }
1286
1307
  const sorted = this.children.map((node, i) => ({
1287
1308
  node,
1288
1309
  i
1289
1310
  })).sort((a, b) => a.node.zIndex() - b.node.zIndex() || a.i - b.i).map((e) => e.node);
1290
1311
  for (const child of sorted) child.emit(out, ctx);
1312
+ if (this.clip !== void 0) out.push({ op: "restore" });
1291
1313
  }
1292
1314
  };
1293
1315
  /** A color string is sugar for a solid `color` Paint; a Paint passes through. */
package/dist/scene.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as DisplayList, s as FontSpec } from "./displayList.js";
2
- import { G as TextMeasurer, I as BindablePropTarget, i as Group, q as WrappedTextMetrics, z as Node } from "./nodes.js";
2
+ import { B as Node, J as WrappedTextMetrics, K as TextMeasurer, L as BindablePropTarget, a as Group } from "./nodes.js";
3
3
  import { BoundTimeline, CompiledTimeline, Playhead, Timeline } from "@glissade/core";
4
4
 
5
5
  //#region src/scene.d.ts
package/dist/tokens.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { r as DisplayListBuilder } from "./displayList.js";
2
- import { H as PropInit, L as EvalContext, V as NodeProps, _ as WordBox, p as Text, z as Node } from "./nodes.js";
2
+ import { B as Node, H as NodeProps, R as EvalContext, U as PropInit, m as Text, v as WordBox } from "./nodes.js";
3
3
  import { Vec2 } from "@glissade/core";
4
4
 
5
5
  //#region src/tokenHighlight.d.ts
package/dist/type.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { G as TextMeasurer, _ as WordBox, i as Group, m as TextProps, p as Text, r as GraphemeBox, s as LineBox } from "./nodes.js";
1
+ import { K as TextMeasurer, a as Group, c as LineBox, h as TextProps, i as GraphemeBox, m as Text, v as WordBox } from "./nodes.js";
2
2
 
3
3
  //#region src/type.d.ts
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/scene",
3
- "version": "0.33.0-pre.0",
3
+ "version": "0.34.0-pre.0",
4
4
  "description": "glissade scene graph: nodes, transforms, DisplayList emission. Renderer-agnostic; zero DOM/Node dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -69,7 +69,7 @@
69
69
  ],
70
70
  "dependencies": {
71
71
  "yoga-layout": "^3.2.1",
72
- "@glissade/core": "0.33.0-pre.0"
72
+ "@glissade/core": "0.34.0-pre.0"
73
73
  },
74
74
  "repository": {
75
75
  "type": "git",