@solidrt/core 0.0.54 → 0.0.56

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/src/transform.ts CHANGED
@@ -26,14 +26,18 @@ const ENGAGE = 0.6
26
26
  const QUIET_SMOOTH = 0.19
27
27
 
28
28
  export type TransformDelta = {
29
- /** Focal-point movement since the previous frame, logical px. */
29
+ /** Focal-point movement since the previous frame, in the handler node's
30
+ * PARENT frame (the frame its own x/y live in: window pixels under no
31
+ * scaling ancestor, design pixels under a designSize fit), so it applies
32
+ * 1:1 to the node or to content inside it. */
30
33
  dx: number
31
34
  dy: number
32
35
  /** Multiplicative span change since the previous frame (1 = unchanged, >1 = fingers spreading). */
33
36
  scale: number
34
37
  /** Rotation of the pointer pair since the previous frame, radians (0 with a single pointer). */
35
38
  rotation: number
36
- /** Current focal point in window coordinates (the clientX/clientY frame) - the zoom-about anchor. */
39
+ /** Current focal point in the handler node's LOCAL frame (its own
40
+ * pixels) - the zoom-about anchor for content inside it. */
37
41
  x: number
38
42
  y: number
39
43
  /** How many pointers are down right now. Consumers that give one- and
@@ -106,12 +110,25 @@ export interface TransformOptions {
106
110
  // path, so an active transform survives leaving the node or the window.
107
111
  // cancel() is the external-cancel hook; it ends an active gesture without
108
112
  // onTransformEnd. Options are read at event time.
113
+ //
114
+ // Frames: the slop, the span filter and its rate gates are finger-travel
115
+ // thresholds, so they are measured in window pixels (clientX/clientY);
116
+ // the focal movement (dx/dy) and the pair angle are measured in the node's
117
+ // parent frame (parentX/parentY), the frame the node's own coordinates
118
+ // live in, so a scaled ancestor (a designSize fit) is accounted for and
119
+ // the delta applies unchanged to the node or to content inside it; the
120
+ // focal point (x/y) is reported in the node's local frame (localX/localY),
121
+ // the anchor a zoom-about-the-fingers needs. Scale and rotation are ratios
122
+ // and angles, the same in every uniformly scaled frame. The event carries
123
+ // all three frames exactly, on and off the node.
109
124
  export function createTransform(options: TransformOptions) {
125
+ type Tracked = { cx: number; cy: number; px: number; py: number; lx: number; ly: number }
126
+ let at = (e: PointerEvent): Tracked => ({ cx: e.clientX, cy: e.clientY, px: e.parentX, py: e.parentY, lx: e.localX, ly: e.localY })
110
127
  // All tracked pointers, in down order (the first two define the rotation pair).
111
- let pointers = new Map<number, { x: number; y: number }>()
128
+ let pointers = new Map<number, Tracked>()
112
129
  let active = false
113
130
  // Slop baseline while armed; last delivered configuration while active.
114
- let ref: { x: number; y: number; span: number; angle: number } | null = null
131
+ let ref: { cx: number; cy: number; px: number; py: number; span: number; angle: number } | null = null
115
132
  // The zoom gate and filter (see the zoom-noise note above): scale stays 1
116
133
  // until the SMOOTHED span leaves spanBase by SLOP, then streams smoothed
117
134
  // ratios for the rest of the gesture.
@@ -128,27 +145,41 @@ export function createTransform(options: TransformOptions) {
128
145
  // emits nothing.
129
146
  let rebase = false
130
147
 
148
+ // Centroids in all three frames, the span in window pixels (it feeds the
149
+ // finger-travel gates), the pair angle in the parent frame.
131
150
  let measure = () => {
132
151
  let n = pointers.size
133
- let x = 0
134
- let y = 0
152
+ let cx = 0
153
+ let cy = 0
154
+ let px = 0
155
+ let py = 0
156
+ let lx = 0
157
+ let ly = 0
135
158
  for (let p of pointers.values()) {
136
- x += p.x
137
- y += p.y
159
+ cx += p.cx
160
+ cy += p.cy
161
+ px += p.px
162
+ py += p.py
163
+ lx += p.lx
164
+ ly += p.ly
138
165
  }
139
- x /= n
140
- y /= n
166
+ cx /= n
167
+ cy /= n
168
+ px /= n
169
+ py /= n
170
+ lx /= n
171
+ ly /= n
141
172
  let span = 0
142
- for (let p of pointers.values()) span += Math.hypot(p.x - x, p.y - y)
173
+ for (let p of pointers.values()) span += Math.hypot(p.cx - cx, p.cy - cy)
143
174
  span /= n
144
175
  let angle = 0
145
176
  if (n >= 2) {
146
177
  let pair = pointers.values()
147
178
  let a = pair.next().value!
148
179
  let b = pair.next().value!
149
- angle = Math.atan2(b.y - a.y, b.x - a.x)
180
+ angle = Math.atan2(b.py - a.py, b.px - a.px)
150
181
  }
151
- return { x, y, span, angle }
182
+ return { cx, cy, px, py, lx, ly, span, angle }
152
183
  }
153
184
 
154
185
  let reset = () => {
@@ -202,10 +233,10 @@ export function createTransform(options: TransformOptions) {
202
233
  if (rotation > Math.PI) rotation -= 2 * Math.PI
203
234
  else if (rotation < -Math.PI) rotation += 2 * Math.PI
204
235
  }
205
- let dx = m.x - ref.x
206
- let dy = m.y - ref.y
236
+ let dx = m.px - ref.px
237
+ let dy = m.py - ref.py
207
238
  ref = m
208
- options.onTransformMove?.({ dx, dy, scale, rotation, x: m.x, y: m.y, pointers: pointers.size })
239
+ options.onTransformMove?.({ dx, dy, scale, rotation, x: m.lx, y: m.ly, pointers: pointers.size })
209
240
  }
210
241
 
211
242
  // An unmount mid-gesture must not leave resolved claims behind (or a live
@@ -226,24 +257,22 @@ export function createTransform(options: TransformOptions) {
226
257
  // A finger joining an established gesture belongs to it outright; if
227
258
  // the arena refuses (resolved elsewhere) the finger stays out.
228
259
  if (!arena.steal(e.pointerId, owner)) return
229
- pointers.set(e.pointerId, { x: e.clientX, y: e.clientY })
260
+ pointers.set(e.pointerId, at(e))
230
261
  rebase = true
231
262
  return
232
263
  }
233
- pointers.set(e.pointerId, { x: e.clientX, y: e.clientY })
264
+ pointers.set(e.pointerId, at(e))
234
265
  ref = measure()
235
266
  },
236
267
  onPointerMove: (e: PointerEvent) => {
237
- let p = pointers.get(e.pointerId)
238
- if (!p || !ref) return
239
- p.x = e.clientX
240
- p.y = e.clientY
268
+ if (!pointers.has(e.pointerId) || !ref) return
269
+ pointers.set(e.pointerId, at(e))
241
270
  if (!active) {
242
271
  // Arming runs per event: the slop test tolerates a mixed-age
243
272
  // measure (8px against <=1 frame of staleness), and the arena
244
273
  // steal must happen synchronously inside the dispatch walk.
245
274
  let m = measure()
246
- let travel = Math.hypot(m.x - ref.x, m.y - ref.y)
275
+ let travel = Math.hypot(m.cx - ref.cx, m.cy - ref.cy)
247
276
  if (travel < SLOP && Math.abs(m.span - ref.span) < SLOP) return
248
277
  let taken: number[] = []
249
278
  let refused = false
package/src/types.d.ts CHANGED
@@ -148,21 +148,75 @@ export interface LayoutProps extends FlexboxProps, GridProps {
148
148
  clear?: "left" | "right" | "both"
149
149
  }
150
150
 
151
- /** Colors are CSS color strings, parsed to a packed u32 by `parseColor`. */
151
+ /**
152
+ * A CSS color string: hex (`#rgb`, `#rrggbb`, `#rrggbbaa`), `rgb()`/`rgba()`,
153
+ * `hsl()`/`hsla()`, `hwb()`, or a named color. The CSS Color 4 functions -
154
+ * `color-mix()`, `oklch()`, `lab()`, `color()` - are not parsed and throw
155
+ * `Invalid color`. To mix two colors, `mixColors(a, b, t)` (oklab, returns
156
+ * hex); for a color at a given opacity, `withAlpha(color, a)` (returns
157
+ * `#rrggbbaa`); `parseColor` gives the packed u32 the prop also accepts.
158
+ */
152
159
  export type Color = string
153
160
 
161
+ export type BlendMode = "clear" | "source" | "destination" | "source-over" | "destination-over" | "source-in" | "destination-in" | "source-out" | "destination-out" | "source-atop" | "destination-atop" | "xor" | "plus" | "modulate" | "screen" | "overlay" | "darken" | "lighten" | "color-dodge" | "color-burn" | "hard-light" | "soft-light" | "difference" | "exclusion" | "multiply" | "hue" | "saturation" | "color" | "luminosity"
162
+ export type DrawStyle = "fill" | "stroke" | "stroke-and-fill"
163
+ export type StrokeCap = "butt" | "round" | "square"
164
+ export type StrokeJoin = "miter" | "round" | "bevel"
165
+
154
166
  export interface PaintProps {
155
167
  // A solid color, or a gradient from createLinearGradient/createRadialGradient.
156
168
  color?: Color | Gradient
157
- blendMode?: "clear" | "source" | "destination" | "source-over" | "destination-over" | "source-in" | "destination-in" | "source-out" | "destination-out" | "source-atop" | "destination-atop" | "xor" | "plus" | "modulate" | "screen" | "overlay" | "darken" | "lighten" | "color-dodge" | "color-burn" | "hard-light" | "soft-light" | "difference" | "exclusion" | "multiply" | "hue" | "saturation" | "color" | "luminosity"
169
+ blendMode?: BlendMode
158
170
  /** Default "fill"; "stroke" on line, whose segment has no interior (see LineProps). */
159
- drawStyle?: "fill" | "stroke" | "stroke-and-fill"
160
- strokeCap?: "butt" | "round" | "square"
161
- strokeJoin?: "miter" | "round" | "bevel"
171
+ drawStyle?: DrawStyle
172
+ strokeCap?: StrokeCap
173
+ strokeJoin?: StrokeJoin
162
174
  strokeMiter?: number
163
175
  strokeWidth?: number
164
176
  }
165
177
 
178
+ /**
179
+ * A drop shadow behind a shape (CSS box-shadow field semantics, one shadow):
180
+ * the shape's outer geometry, offset by x/y, grown by `spread`, softened by
181
+ * `blur` (a CSS-style radius in logical px), painted in `color` under the
182
+ * shape. It casts from the shape's outer box whatever the draw style (strokes
183
+ * paint inside, so the box is the outer edge) - except on `path`, where the
184
+ * shadow mirrors the element's own fill/stroke silhouette and `spread` is
185
+ * rejected (an arbitrary path cannot be inflated exactly). `color` is
186
+ * required; there is no currentColor to inherit.
187
+ */
188
+ export interface ShadowProps {
189
+ x?: number
190
+ y?: number
191
+ blur?: number
192
+ spread?: number
193
+ color: Color
194
+ }
195
+
196
+ /**
197
+ * A subtree filter (the CSS filter functions, object form): the view's
198
+ * children are composited together, run through the set color operations -
199
+ * fused into one color matrix, applied in the fixed order grayscale, sepia,
200
+ * saturate, hueRotate, brightness, contrast, invert - and then blurred.
201
+ * `blur` is a CSS-style radius in logical px; `hueRotate` is radians (this
202
+ * API's angle convention); the rest are the CSS amounts (1 = unchanged for
203
+ * saturate/brightness/contrast, 0 = unchanged for the others; grayscale,
204
+ * sepia and invert saturate at 1). There is no `opacity` key - the view's
205
+ * own `opacity` prop is the same compositing layer. Like opacity, a filter
206
+ * on a non-boundary view costs a save_layer; on a repaintBoundary view it
207
+ * rides the composite for free. Hit testing is unaffected.
208
+ */
209
+ export interface FilterProps {
210
+ blur?: number
211
+ grayscale?: number
212
+ sepia?: number
213
+ saturate?: number
214
+ hueRotate?: number
215
+ brightness?: number
216
+ contrast?: number
217
+ invert?: number
218
+ }
219
+
166
220
  /** A percentage value, from `pct(50)`. Resolves against the element box. */
167
221
  export type Pct = { readonly __unit: "pct"; v: number }
168
222
 
@@ -357,22 +411,41 @@ export interface OvalGeometryProps extends PositionProps {
357
411
  h?: number
358
412
  }
359
413
 
360
- /** See {@link PositionProps}: detached-only, never affects layout. */
414
+ /**
415
+ * See {@link PositionProps}: detached-only, never affects layout. A d-text
416
+ * is boxed or anchored. Boxed (no `anchor`): it wraps at `w`, or at the box
417
+ * inherited from the nearest laid-out ancestor, and `textAlign` aligns
418
+ * lines inside that width - so a right-aligned label needs a `w`. Anchored:
419
+ * `x` is a point on the line (SVG's text-anchor), which is what a label on a
420
+ * chart, an axis or a diagram wants. Either way the reported bounds
421
+ * (getBoundingBox, the tree) are the laid-out paragraph - widest line by
422
+ * line stack - unless `w`/`h` override a side.
423
+ */
361
424
  export interface TextGeometryProps extends PositionProps {
362
- // Shaping (wrap) width. Detached text wraps at the inherited ancestor size
363
- // by default; set w for an unwrapped natural line or an explicit wrap width.
425
+ /**
426
+ * Where `x` sits on each line: its start, middle or end. With `w` unset
427
+ * the text shapes at its natural width (no wrap; `\n` still breaks), and
428
+ * `textAlign` defaults to the anchor's side. With `w` set, the `w`-wide
429
+ * box is what gets anchored at `x`.
430
+ */
431
+ anchor?: "start" | "middle" | "end"
432
+ // Shaping (wrap) width; unset, boxed text wraps at the inherited box and
433
+ // anchored text does not wrap.
364
434
  w?: number
365
- // Reported-bounds height only; paragraph height always falls out of the text.
435
+ // Reported-bounds height override only; paragraph height always falls out
436
+ // of the text.
366
437
  h?: number
367
438
  }
368
439
 
369
440
  /**
370
- * See {@link PositionProps}: detached-only, never affects layout. A line's
371
- * reported bounds (getBoundingBox, the tree, a detached capture) are its
372
- * painted box: the geometry's extent plus the stroke's reach, not the
441
+ * See {@link PositionProps}: detached-only, never affects layout. The
442
+ * endpoints (or `points`) are the line's own geometry; `x`/`y` offset all of
443
+ * it, the way a `d-path`'s offset its `d`, so one write moves a polyline. A
444
+ * line's reported bounds (getBoundingBox, the tree, a detached capture) are
445
+ * its painted box: the geometry's extent plus the stroke's reach, not the
373
446
  * inherited box.
374
447
  */
375
- export interface LineGeometryProps {
448
+ export interface LineGeometryProps extends PositionProps {
376
449
  /** Endpoints default to spanning the box: (0,0) to (box width, box height). */
377
450
  x1?: number
378
451
  y1?: number
@@ -520,7 +593,9 @@ export interface TransitionProps {
520
593
  * its transform); the initial value never animates unless the entry sets
521
594
  * `from` (an enter animation), and a non-numeric write (e.g. null)
522
595
  * cancels the running animation and snaps. `null` clears the
523
- * declaration; already-running animations finish.
596
+ * declaration; already-running animations finish. A spec built in a
597
+ * conditional widens `curve` to `string` for TypeScript; write
598
+ * `satisfies Transition` on the branch.
524
599
  */
525
600
  transition?:
526
601
  | ({
@@ -543,7 +618,7 @@ export interface TransitionProps {
543
618
 
544
619
  // Primitives
545
620
 
546
- export interface WindowProps extends LayoutProps, PointerProps {
621
+ export interface WindowProps extends LayoutProps, PointerProps, TransitionProps {
547
622
  children?: Children
548
623
  title?: string
549
624
  fullscreen?: boolean
@@ -563,21 +638,24 @@ export interface WindowProps extends LayoutProps, PointerProps {
563
638
  * `uniform sampler2D uSource` (top-left origin, like every sampled texture -
564
639
  * so a vertex stage mapping it onto the window flips the v coordinate) and
565
640
  * is drawn attributeless as triangles, `vertexCount` vertices fetched via
566
- * gl_VertexID. `iResolution`, filled by name, is the window size in physical
567
- * pixels (the pass covers exactly that). The window is cleared to opaque
568
- * black first, so geometry that does not cover it still presents a defined
569
- * frame.
641
+ * gl_VertexID. `iResolution`, the window size in physical pixels (the pass
642
+ * covers exactly that), is filled by name into a uniform the program declares
643
+ * itself or gets from compileShader's `header` option. The window is cleared
644
+ * to opaque black first, so geometry that does not cover it still presents a
645
+ * defined frame.
570
646
  */
571
647
  export interface WindowShaderProps {
572
648
  /** Linked program handle from linkProgram. */
573
649
  program: ProgramId
574
650
  /**
575
651
  * Uniforms filled by name, paced to the next real repaint. A number drives
576
- * a scalar (`float`/`int`); a flat number array drives the declared GLSL
577
- * type: 2/3/4 for `vec2`/`vec3`/`vec4`, 16 (column-major) for `mat4`.
652
+ * a scalar (`float`/`int`); a flat number array (or a Float32Array /
653
+ * Float64Array) drives the declared GLSL type: 2/3/4 for
654
+ * `vec2`/`vec3`/`vec4`, 16 (column-major) for `mat4`. An undefined or null
655
+ * entry is skipped.
578
656
  */
579
- params?: Record<string, number | number[]>
580
- /** Extra sampler2D inputs: uniform name to texture id, or `{ id, filter?, wrap? }` for a per-binding sampling override. */
657
+ params?: Record<string, number | number[] | Float32Array | Float64Array>
658
+ /** Extra sampler2D inputs: uniform name to texture id, or `{ id, filter?, wrap? }` for a per-binding sampling override. An undefined or null entry is skipped. */
581
659
  textures?: TextureBindings
582
660
  /** Vertices drawn (attributeless triangles). Default 3, the covering triangle. */
583
661
  vertexCount?: number
@@ -637,7 +715,9 @@ export interface ViewOwnProps extends TransformProps, PointerProps {
637
715
  * skipping rasterization entirely. Costs texture memory and re-rasterizes
638
716
  * on layout-size or display-scale changes. Content painted outside the
639
717
  * element's layout box is cropped, and ancestor scale animations smear the
640
- * bitmap; best for screen-aligned, static, raster-expensive content.
718
+ * bitmap; best for screen-aligned, static, raster-expensive content. On a
719
+ * d-view the box is the inherited frame (the nearest laid-out ancestor's
720
+ * box, or the design size under a designSize view), same as a capture.
641
721
  *
642
722
  * "snapshot-no-aa" is "snapshot" rasterized without anti-aliasing: cheaper
643
723
  * (no multisampled scratch, one render pass), but vector content - svg
@@ -667,25 +747,41 @@ export interface ViewOwnProps extends TransformProps, PointerProps {
667
747
  * unrotated, opaque content.
668
748
  */
669
749
  shader?: ViewShaderProps | null
750
+ filter?: FilterProps
751
+ /**
752
+ * Filters the pixels already painted BENEATH the view's box, in place,
753
+ * before the view's own content draws - frosted glass over live content
754
+ * (CSS `backdrop-filter`). Same keys and semantics as `filter`. The
755
+ * region is the layout box; give the view an overflow clip with
756
+ * `clipRadius` for rounded glass. Costs an offscreen capture of the
757
+ * pixels beneath at that point in the frame, so treat it as a deliberate
758
+ * panel, not a casual style. Inside a `repaintBoundary="snapshot"`
759
+ * subtree it sees only that boundary's own offscreen content, never what
760
+ * is behind the boundary (the same containment `blendMode` has there).
761
+ */
762
+ backdropFilter?: FilterProps
670
763
  }
671
764
 
672
765
  /**
673
766
  * A boundary shader declaration. The program contract matches shader targets,
674
767
  * not the window pass: the subtree's rasterization binds as
675
768
  * `uniform sampler2D uSource` (top-left origin, like every sampled texture)
676
- * and the pass draws the covering triangle attributeless. `iResolution`,
677
- * filled by name, is the boundary in physical pixels.
769
+ * and the pass draws the covering triangle attributeless. `iResolution`, the
770
+ * boundary in physical pixels, is filled by name into a uniform the program
771
+ * declares itself or gets from compileShader's `header` option.
678
772
  */
679
773
  export interface ViewShaderProps {
680
774
  /** Linked program handle from linkProgram. */
681
775
  program: ProgramId
682
776
  /**
683
777
  * Uniforms filled by name, paced to the next real repaint. A number drives
684
- * a scalar (`float`/`int`); a flat number array drives the declared GLSL
685
- * type: 2/3/4 for `vec2`/`vec3`/`vec4`, 16 (column-major) for `mat4`.
778
+ * a scalar (`float`/`int`); a flat number array (or a Float32Array /
779
+ * Float64Array) drives the declared GLSL type: 2/3/4 for
780
+ * `vec2`/`vec3`/`vec4`, 16 (column-major) for `mat4`. An undefined or null
781
+ * entry is skipped.
686
782
  */
687
- params?: Record<string, number | number[]>
688
- /** Extra sampler2D inputs: uniform name to texture id, or `{ id, filter?, wrap? }` for a per-binding sampling override. */
783
+ params?: Record<string, number | number[] | Float32Array | Float64Array>
784
+ /** Extra sampler2D inputs: uniform name to texture id, or `{ id, filter?, wrap? }` for a per-binding sampling override. An undefined or null entry is skipped. */
689
785
  textures?: TextureBindings
690
786
  /**
691
787
  * Transparent margin in logical px on every side of the layout box, for
@@ -719,20 +815,28 @@ export interface ViewProps extends ViewOwnProps, LayoutProps {}
719
815
  // A stroked rect paints inside its box, like a CSS border: the stroke's outer
720
816
  // edge sits on the box edge rather than straddling it, so nothing bleeds past
721
817
  // the box for a clip to cut. `path` and `line` strokes stay centered on their
722
- // geometry - there the geometry is the stroke, not a box.
723
- export interface RectProps extends PaintProps, PointerProps {
818
+ // geometry - there the geometry is the stroke, not a box. A dashed stroke
819
+ // dashes that same inset outline (see DashProps).
820
+ export interface RectProps extends PaintProps, PointerProps, DashProps {
724
821
  // Corner radius, measured on the box (the stroke's outer edge). A single
725
822
  // number applies to all four corners; an array is [top-left, top-right,
726
823
  // bottom-right, bottom-left] (CSS border-radius order).
727
824
  radius?: number | [number, number, number, number]
825
+ shadow?: ShadowProps
728
826
  }
729
827
 
730
828
  // Strokes paint inside the box, same as `RectProps`.
731
- export interface OvalProps extends PaintProps, PointerProps {}
829
+ export interface OvalProps extends PaintProps, PointerProps, DashProps {
830
+ shadow?: ShadowProps
831
+ }
732
832
 
733
833
  /**
734
- * A stroke's dash pattern, on `line` and `path`. Both lengths must be set
735
- * to dash; with either unset, or a gap of 0, the stroke is solid.
834
+ * A stroke's dash pattern, on every stroked primitive (`rect`, `oval`,
835
+ * `line`, `path`). Both lengths must be set to dash; with either unset, or
836
+ * a gap of 0, the stroke is solid. A box primitive dashes its inset
837
+ * outline, the pattern starting where SVG's does (a rect on the top edge
838
+ * after the top-left corner, an oval at 3 o'clock) and running clockwise;
839
+ * its dashes stay inside the box like the solid stroke.
736
840
  */
737
841
  export interface DashProps {
738
842
  /**
@@ -807,15 +911,24 @@ export interface LineProps extends PaintProps, PointerProps, DashProps {
807
911
  export interface PathProps extends PaintProps, PointerProps, DashProps {
808
912
  d?: string
809
913
  fillRule?: "nonzero" | "evenodd"
914
+ /** Shadows the drawn silhouette (fill and/or stroke); `spread` is rejected here. */
915
+ shadow?: ShadowProps
810
916
  }
811
917
 
918
+ export type FontFamily = "sans" | "serif" | "mono" | (string & {})
919
+ export type FontStyle = "normal" | "italic"
920
+ export type FontWeight = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
921
+ export type TextDecoration = "none" | "underline"
922
+ export type TextAlign = "left" | "right" | "center" | "justify"
923
+ export type TextOverflow = "clip" | "ellipsis" | (string & {})
924
+
812
925
  /**
813
926
  * Per-run text style: the paragraph default on <text>, an override on <span>.
814
927
  * The cascade is intra-paragraph only: a span inherits from its enclosing
815
928
  * span, then from the <text>; nothing inherits across the tree.
816
929
  */
817
930
  export interface TextRunProps {
818
- fontFamily?: "sans" | "serif" | "mono" | (string & {})
931
+ fontFamily?: FontFamily
819
932
  fontSize?: number
820
933
  /**
821
934
  * Line height as a MULTIPLIER of fontSize, not pixels (the theme uses
@@ -823,15 +936,15 @@ export interface TextRunProps {
823
936
  * font size, rendering the text as blank space.
824
937
  */
825
938
  lineHeight?: number
826
- fontStyle?: "normal" | "italic"
827
- fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
939
+ fontStyle?: FontStyle
940
+ fontWeight?: FontWeight
828
941
  /**
829
942
  * Underline in the run's own color, drawn straight through descenders
830
943
  * (no skip-ink). Position and thickness come from the font's own metrics
831
944
  * unless overridden; a font Impeller resolves through the system fallback
832
945
  * gets the shipped Noto values.
833
946
  */
834
- textDecoration?: "none" | "underline"
947
+ textDecoration?: TextDecoration
835
948
  /** Pixels from the baseline to the top of the underline. */
836
949
  textUnderlineOffset?: number
837
950
  /** Underline thickness in pixels. */
@@ -851,14 +964,14 @@ export interface SpanProps extends TextRunProps, PointerProps {
851
964
 
852
965
  export interface TextProps extends PaintProps, PointerProps, TextRunProps {
853
966
  children?: Children
854
- textAlign?: "left" | "right" | "center" | "justify"
967
+ textAlign?: TextAlign
855
968
  maxLines?: number
856
969
  /**
857
970
  * What happens to text cut off by maxLines: "clip" (default), "ellipsis"
858
971
  * (a U+2026 at the end of the last line), or any other string to use as
859
972
  * the ellipsis. Drawn in the paragraph's default style.
860
973
  */
861
- textOverflow?: "clip" | "ellipsis" | (string & {})
974
+ textOverflow?: TextOverflow
862
975
  /**
863
976
  * A word (wrap unit) wider than the line: "anywhere" (default) splits it
864
977
  * at grapheme boundaries so it stays inside the box, "normal" keeps it
@@ -910,7 +1023,8 @@ export interface TextureProps extends PaintProps, PointerProps {
910
1023
  // throws, and set src before params: a write with no src to route to
911
1024
  // throws). However often a signal writes, the target renders once per
912
1025
  // frame at the raster flush. A number drives a scalar (`float`/`int`); a
913
- // flat number array drives the declared GLSL type: 2/3/4 for
914
- // `vec2`/`vec3`/`vec4`, 16 (column-major) for `mat4`.
915
- params?: Record<string, number | number[]>
1026
+ // flat number array (or a Float32Array / Float64Array) drives the declared
1027
+ // GLSL type: 2/3/4 for `vec2`/`vec3`/`vec4`, 16 (column-major) for `mat4`.
1028
+ // An undefined or null entry is skipped.
1029
+ params?: Record<string, number | number[] | Float32Array | Float64Array>
916
1030
  }