@glissade/scene 0.57.1-pre.0 → 0.58.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/describe.js CHANGED
@@ -23,7 +23,7 @@ import { easings, listValueTypes } from "@glissade/core";
23
23
  * never pulled onto the base embed path — a scene that never calls `describe()`
24
24
  * pays zero bytes for it.
25
25
  */
26
- const RAW_VERSION = "0.57.1-pre.0";
26
+ const RAW_VERSION = "0.58.0-pre.0";
27
27
  const PACKAGE_VERSION = RAW_VERSION.includes("GLISSADE_".concat("VERSION")) ? "0.0.0-dev" : RAW_VERSION;
28
28
  /**
29
29
  * Parse the documented positional-arg count from a helper `usage` string — the
@@ -682,7 +682,7 @@ const HELPERS = [
682
682
  name: "typeOn",
683
683
  summary: "Kinetic type: one-call typewriter over the shipped typewriter(). DEFAULT emits a STRING hold-key track on `<id>/text` (round-trips to Lottie as stepped text docs). { cursor: true } adds a render-only caret sibling (export warns+drops it); { mask: true } swaps to a render-only `<id>/reveal` grapheme mask (export warns 'reveal not exported'). Factory (no `new`). Inject with tl.tracks([r.track]); draw r.node (+ r.cursor). On @glissade/scene/type.",
684
684
  import: "@glissade/scene/type",
685
- usage: "typeOn(source: Text | TextProps, opts?: { perChar?, start?, cursor?: boolean, mask?: boolean, cursorWidth?, blinkPeriod? }): { node: Text, cursor?: TextCursor, track: Track, marks, duration }"
685
+ usage: "typeOn(source: Text | TextProps, opts?: { perChar?, start?, cursor?: boolean, mask?: boolean, cursorWidth?, blinkPeriod?, cursorFill?, cursorProps? }): { node: Text, cursor?: TextCursor, track: Track, marks, duration } — cursorFill sets a contrasting caret color (default follows text fill); cursorProps forwards any other TextCursor prop"
686
686
  },
687
687
  {
688
688
  name: "revealWords",
@@ -0,0 +1,16 @@
1
+ import { ColorStop, GradientInterpolation } from "@glissade/core";
2
+
3
+ //#region src/gradient.d.ts
4
+
5
+ /** Resolution of the densified ramp — enough that the piecewise-linear blit of
6
+ * the easing curve shows no banding, cheap enough to rebuild per frame. */
7
+ declare const GRADIENT_RAMP_STEPS = 64;
8
+ /**
9
+ * Densify `stops` into a `smooth`/`gaussian` oklab ramp. The output spans the
10
+ * input's offset range with GRADIENT_RAMP_STEPS uniformly-spaced stops; each
11
+ * point eases its blend within the authored segment it falls in. Returns the
12
+ * input unchanged for `linear` (or a single stop) — the canvas-native path.
13
+ */
14
+ declare function densifyStops(stops: ColorStop[], mode: GradientInterpolation): ColorStop[];
15
+ //#endregion
16
+ export { GRADIENT_RAMP_STEPS, densifyStops };
@@ -0,0 +1,48 @@
1
+ import { lerpColor } from "@glissade/core";
2
+ //#region src/gradient.ts
3
+ /**
4
+ * Gradient stop densification (§3 Paint, 0.10.1). Canvas gradients interpolate
5
+ * BETWEEN stops linearly in the canvas color space, which Mach-bands a 2-3 stop
6
+ * soft-light fill. For `smooth`/`gaussian` interpolation we resample the stops
7
+ * into a dense ramp eased per-segment with oklab `lerpColor`, so the blit melts
8
+ * like a wide blur with no banding and no filter. Pure + deterministic: the same
9
+ * (stops, mode) always produce the same dense stops, so Skia stays byte-exact.
10
+ * `linear` is the canvas-native ramp and is never densified (byte-identical).
11
+ */
12
+ /** Resolution of the densified ramp — enough that the piecewise-linear blit of
13
+ * the easing curve shows no banding, cheap enough to rebuild per frame. */
14
+ const GRADIENT_RAMP_STEPS = 64;
15
+ const smoothstep = (u) => u * u * (3 - 2 * u);
16
+ const GAUSS_K = 2.4;
17
+ const GAUSS_NORM = 1 - Math.exp(-5.76 / 2);
18
+ const gaussianEase = (u) => (1 - Math.exp(-((u * GAUSS_K) ** 2) / 2)) / GAUSS_NORM;
19
+ /**
20
+ * Densify `stops` into a `smooth`/`gaussian` oklab ramp. The output spans the
21
+ * input's offset range with GRADIENT_RAMP_STEPS uniformly-spaced stops; each
22
+ * point eases its blend within the authored segment it falls in. Returns the
23
+ * input unchanged for `linear` (or a single stop) — the canvas-native path.
24
+ */
25
+ function densifyStops(stops, mode) {
26
+ if (mode === "linear" || stops.length < 2) return stops;
27
+ const ease = mode === "gaussian" ? gaussianEase : smoothstep;
28
+ const o0 = stops[0].offset;
29
+ const span = stops[stops.length - 1].offset - o0;
30
+ if (span <= 0) return stops;
31
+ const out = [];
32
+ let seg = 0;
33
+ for (let i = 0; i < 64; i++) {
34
+ const offset = o0 + span * (i / 63);
35
+ while (seg < stops.length - 2 && offset > stops[seg + 1].offset) seg++;
36
+ const a = stops[seg];
37
+ const b = stops[seg + 1];
38
+ const w = b.offset - a.offset;
39
+ const u = w > 0 ? Math.min(1, Math.max(0, (offset - a.offset) / w)) : 0;
40
+ out.push({
41
+ offset,
42
+ color: lerpColor(a.color, b.color, ease(u))
43
+ });
44
+ }
45
+ return out;
46
+ }
47
+ //#endregion
48
+ export { GRADIENT_RAMP_STEPS, densifyStops };
package/dist/index.js CHANGED
@@ -3,8 +3,9 @@ import { a as evaluate, i as createScene, n as ReservedNodeIdError, r as bindSce
3
3
  import { i as setLayoutEngine, n as getLayoutEngine, r as requireLayoutEngine, t as LayoutEngineMissingError } from "./layoutEngine.js";
4
4
  import { n as TextCursor, r as textCursor, t as typewriter } from "./typewriter.js";
5
5
  import { i as echo, n as motionBlur, r as Echo, t as MotionBlur } from "./motionBlur.js";
6
+ import { densifyStops } from "./gradient.js";
6
7
  import { n as each, t as EachError } from "./each.js";
7
- import { emitDevWarning, key, lerpColor, oklabToRgba, parseColor, rgbaToOklab, signal, stagger, track } from "@glissade/core";
8
+ import { emitDevWarning, key, oklabToRgba, parseColor, rgbaToOklab, signal, stagger, track } from "@glissade/core";
8
9
  //#region src/taxonomy.ts
9
10
  /**
10
11
  * The CLOSED node taxonomy (DESIGN.md §3.1): exactly nine built-in node TYPES.
@@ -323,38 +324,6 @@ function trackMatte(content, matte, props = {}) {
323
324
  matte
324
325
  });
325
326
  }
326
- const smoothstep = (u) => u * u * (3 - 2 * u);
327
- const GAUSS_K = 2.4;
328
- const GAUSS_NORM = 1 - Math.exp(-5.76 / 2);
329
- const gaussianEase = (u) => (1 - Math.exp(-((u * GAUSS_K) ** 2) / 2)) / GAUSS_NORM;
330
- /**
331
- * Densify `stops` into a `smooth`/`gaussian` oklab ramp. The output spans the
332
- * input's offset range with GRADIENT_RAMP_STEPS uniformly-spaced stops; each
333
- * point eases its blend within the authored segment it falls in. Returns the
334
- * input unchanged for `linear` (or a single stop) — the canvas-native path.
335
- */
336
- function densifyStops(stops, mode) {
337
- if (mode === "linear" || stops.length < 2) return stops;
338
- const ease = mode === "gaussian" ? gaussianEase : smoothstep;
339
- const o0 = stops[0].offset;
340
- const span = stops[stops.length - 1].offset - o0;
341
- if (span <= 0) return stops;
342
- const out = [];
343
- let seg = 0;
344
- for (let i = 0; i < 64; i++) {
345
- const offset = o0 + span * (i / 63);
346
- while (seg < stops.length - 2 && offset > stops[seg + 1].offset) seg++;
347
- const a = stops[seg];
348
- const b = stops[seg + 1];
349
- const w = b.offset - a.offset;
350
- const u = w > 0 ? Math.min(1, Math.max(0, (offset - a.offset) / w)) : 0;
351
- out.push({
352
- offset,
353
- color: lerpColor(a.color, b.color, ease(u))
354
- });
355
- }
356
- return out;
357
- }
358
327
  //#endregion
359
328
  //#region src/meshGradient.ts
360
329
  /**
package/dist/type.d.ts CHANGED
@@ -1,5 +1,5 @@
1
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
- import { o as TextCursor, t as EditMark } from "./typewriter.js";
2
+ import { o as TextCursor, s as TextCursorProps, t as EditMark } from "./typewriter.js";
3
3
  import { EaseSpec, Track } from "@glissade/core";
4
4
 
5
5
  //#region src/type.d.ts
@@ -139,6 +139,20 @@ interface TypeOnOpts {
139
139
  cursorWidth?: number;
140
140
  /** Caret blink period seconds when `cursor: true` (passthrough to textCursor). */
141
141
  blinkPeriod?: number;
142
+ /**
143
+ * Caret COLOR when `cursor: true` (passthrough to the textCursor sibling's `fill`).
144
+ * Default '' = follow the Text's own fill; set a hex/PropInit for a deliberately
145
+ * contrasting caret (bindable via the `<id>/cursor/fill` track). Ignored without `cursor`.
146
+ */
147
+ cursorFill?: TextCursorProps['fill'];
148
+ /**
149
+ * Escape hatch: any other {@link TextCursor} construction prop (e.g. `blinkPeriod`,
150
+ * `width`, or a NodeProp) forwarded to the caret sibling when `cursor: true`. The
151
+ * explicit `cursorWidth`/`blinkPeriod`/`cursorFill` options win over the matching
152
+ * key here, and the caret's `id` (`<id>/cursor`) is always set by typeOn. Ignored
153
+ * without `cursor`.
154
+ */
155
+ cursorProps?: Omit<TextCursorProps, 'text' | 'id'>;
142
156
  }
143
157
  interface TypeOnResult {
144
158
  /** The Text to draw. In the DEFAULT (string-track) mode its `text` is driven by
package/dist/type.js CHANGED
@@ -252,9 +252,11 @@ function typeOn(source, opts = {}) {
252
252
  duration: tw.duration
253
253
  };
254
254
  if (opts.cursor) result.cursor = textCursor(node, {
255
+ ...opts.cursorProps ?? {},
255
256
  id: `${id}/cursor`,
256
257
  ...opts.cursorWidth !== void 0 ? { width: opts.cursorWidth } : {},
257
- ...opts.blinkPeriod !== void 0 ? { blinkPeriod: opts.blinkPeriod } : {}
258
+ ...opts.blinkPeriod !== void 0 ? { blinkPeriod: opts.blinkPeriod } : {},
259
+ ...opts.cursorFill !== void 0 ? { fill: opts.cursorFill } : {}
258
260
  });
259
261
  return result;
260
262
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/scene",
3
- "version": "0.57.1-pre.0",
3
+ "version": "0.58.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": {
@@ -63,6 +63,10 @@
63
63
  "types": "./dist/motion.d.ts",
64
64
  "default": "./dist/motion.js"
65
65
  },
66
+ "./gradient": {
67
+ "types": "./dist/gradient.d.ts",
68
+ "default": "./dist/gradient.js"
69
+ },
66
70
  "./identity": {
67
71
  "types": "./dist/identity.d.ts",
68
72
  "default": "./dist/identity.js"
@@ -77,7 +81,7 @@
77
81
  ],
78
82
  "dependencies": {
79
83
  "yoga-layout": "^3.2.1",
80
- "@glissade/core": "0.57.1-pre.0"
84
+ "@glissade/core": "0.58.0-pre.0"
81
85
  },
82
86
  "repository": {
83
87
  "type": "git",