@solidtv/renderer 1.2.3 → 1.2.5

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.
Files changed (30) hide show
  1. package/dist/src/common/CommonTypes.d.ts +0 -5
  2. package/dist/src/core/CoreTextNode.d.ts +0 -17
  3. package/dist/src/core/CoreTextNode.js +0 -21
  4. package/dist/src/core/CoreTextNode.js.map +1 -1
  5. package/dist/src/core/shaders/canvas/RadialProgress.js +9 -1
  6. package/dist/src/core/shaders/canvas/RadialProgress.js.map +1 -1
  7. package/dist/src/core/shaders/templates/RadialProgressTemplate.d.ts +16 -0
  8. package/dist/src/core/shaders/templates/RadialProgressTemplate.js +11 -0
  9. package/dist/src/core/shaders/templates/RadialProgressTemplate.js.map +1 -1
  10. package/dist/src/core/shaders/webgl/RadialProgress.js +30 -10
  11. package/dist/src/core/shaders/webgl/RadialProgress.js.map +1 -1
  12. package/dist/src/core/text-rendering/CanvasTextRenderer.js +0 -7
  13. package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
  14. package/dist/src/core/text-rendering/SdfTextRenderer.js +0 -11
  15. package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
  16. package/dist/src/core/text-rendering/TextRenderer.d.ts +0 -20
  17. package/dist/src/main-api/Inspector.js +20 -4
  18. package/dist/src/main-api/Inspector.js.map +1 -1
  19. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  20. package/package.json +1 -1
  21. package/src/common/CommonTypes.ts +0 -5
  22. package/src/core/CoreTextNode.ts +0 -22
  23. package/src/core/shaders/canvas/RadialProgress.ts +10 -1
  24. package/src/core/shaders/templates/RadialProgressTemplate.test.ts +26 -0
  25. package/src/core/shaders/templates/RadialProgressTemplate.ts +25 -0
  26. package/src/core/shaders/webgl/RadialProgress.ts +30 -10
  27. package/src/core/text-rendering/CanvasTextRenderer.ts +0 -9
  28. package/src/core/text-rendering/SdfTextRenderer.ts +0 -13
  29. package/src/core/text-rendering/TextRenderer.ts +0 -20
  30. package/src/main-api/Inspector.ts +23 -6
@@ -9,6 +9,7 @@ import type { WebGlRenderer } from '../../renderers/webgl/WebGlRenderer.js';
9
9
 
10
10
  export const RadialProgress: WebGlShaderType<RadialProgressProps> = {
11
11
  props: RadialProgressTemplate.props,
12
+ time: true,
12
13
  update(node: CoreNode) {
13
14
  const props = this.props!;
14
15
 
@@ -21,6 +22,8 @@ export const RadialProgress: WebGlShaderType<RadialProgressProps> = {
21
22
  this.uniform1f('u_progress', props.progress);
22
23
  this.uniform1f('u_startAngle', props.startAngle);
23
24
  this.uniform1f('u_direction', props.direction);
25
+ this.uniform1f('u_duration', props.duration);
26
+ this.uniform1f('u_countdown', props.countdown);
24
27
  this.uniform1fv('u_stops', new Float32Array(props.stops));
25
28
 
26
29
  const colors: number[] = [];
@@ -61,6 +64,7 @@ export const RadialProgress: WebGlShaderType<RadialProgressProps> = {
61
64
  #define TWO_PI 6.28318530717958647692
62
65
 
63
66
  uniform float u_alpha;
67
+ uniform float u_time;
64
68
  uniform vec2 u_dimensions;
65
69
  uniform sampler2D u_texture;
66
70
 
@@ -70,6 +74,8 @@ export const RadialProgress: WebGlShaderType<RadialProgressProps> = {
70
74
  uniform float u_progress;
71
75
  uniform float u_startAngle;
72
76
  uniform float u_direction;
77
+ uniform float u_duration;
78
+ uniform float u_countdown;
73
79
 
74
80
  uniform float u_stops[MAX_STOPS];
75
81
  uniform vec4 u_colors[MAX_STOPS];
@@ -107,6 +113,13 @@ export const RadialProgress: WebGlShaderType<RadialProgressProps> = {
107
113
  void main() {
108
114
  vec4 base = texture2D(u_texture, v_textureCoords) * v_color;
109
115
 
116
+ // Effective progress: when u_duration > 0 the shader self-animates from
117
+ // u_time, otherwise we use the static u_progress prop. countdown == 1
118
+ // drains (1 -> 0), countdown == 0 fills (0 -> 1).
119
+ float cyclePos = u_duration > 0.0 ? fract(u_time / u_duration) : 0.0;
120
+ float animProgress = u_countdown > 0.5 ? 1.0 - cyclePos : cyclePos;
121
+ float progress = u_duration > 0.0 ? animProgress : u_progress;
122
+
110
123
  vec2 p = v_nodeCoords.xy * u_dimensions - u_center;
111
124
  float dist = length(p);
112
125
  float halfW = u_width * 0.5;
@@ -122,39 +135,46 @@ export const RadialProgress: WebGlShaderType<RadialProgressProps> = {
122
135
 
123
136
  // Filled arc coverage (1 if in filled arc, else 0). When progress >= 1 the
124
137
  // whole ring is filled regardless of \`t\` -- guards against the mod() seam.
125
- float arcCoverage = u_progress >= 1.0 ? 1.0 : step(t, u_progress);
138
+ float arcCoverage = progress >= 1.0 ? 1.0 : step(t, progress);
126
139
  float fillCoverage = ringCoverage * arcCoverage;
127
140
 
128
141
  #if CAP_ROUND
129
142
  // Round caps: discs of radius halfW at the start and head of the arc
130
143
  float a0 = u_startAngle;
131
- float a1 = u_startAngle + u_direction * u_progress * TWO_PI;
144
+ float a1 = u_startAngle + u_direction * progress * TWO_PI;
132
145
  vec2 cap0 = vec2(cos(a0), sin(a0)) * u_radius;
133
146
  vec2 cap1 = vec2(cos(a1), sin(a1)) * u_radius;
134
147
  float capMask = max(discCoverage(p, cap0, halfW), discCoverage(p, cap1, halfW));
135
148
  // Caps only visible when there's something to cap (progress > 0 and < 1).
136
- float capGate = step(0.0001, u_progress) * step(u_progress, 0.9999);
149
+ float capGate = step(0.0001, progress) * step(progress, 0.9999);
137
150
  fillCoverage = max(fillCoverage, capMask * capGate);
138
151
  #endif
139
152
 
140
153
  // Sample gradient. Normalize \`t\` to the *filled* portion so the gradient
141
154
  // spans the visible arc end-to-end regardless of progress.
142
- float gradT = u_progress > 0.0 ? clamp(t / u_progress, 0.0, 1.0) : 0.0;
155
+ float gradT = progress > 0.0 ? clamp(t / progress, 0.0, 1.0) : 0.0;
143
156
  vec4 fillCol = getGradientColor(gradT);
144
157
 
145
- // Composite: track under fill (if track enabled), both gated by ringCoverage
158
+ // Composite: track under fill (if track enabled), both gated by ringCoverage.
159
+ // We work in PREMULTIPLIED-alpha space here so AA edges composite cleanly
160
+ // against \`base\` -- mix(base.rgb, layer.rgb, la) with a coverage-scaled
161
+ // \`layer\` would multiply layer.rgb by coverage a second time and darken
162
+ // the AA falloff (see issue #36). The renderer's blend func is
163
+ // (ONE, ONE_MINUS_SRC_ALPHA), which expects premultiplied output.
164
+ vec4 fillPM = vec4(fillCol.rgb * fillCol.a, fillCol.a);
146
165
  vec4 layer = vec4(0.0);
147
166
  #if HAS_TRACK
167
+ vec4 trackPM = vec4(u_trackColor.rgb * u_trackColor.a, u_trackColor.a);
148
168
  float trackCoverage = ringCoverage * (1.0 - fillCoverage);
149
- layer = u_trackColor * trackCoverage + fillCol * fillCoverage;
169
+ layer = trackPM * trackCoverage + fillPM * fillCoverage;
150
170
  #else
151
- layer = fillCol * fillCoverage;
171
+ layer = fillPM * fillCoverage;
152
172
  #endif
153
173
 
154
- // Composite layer over base. Output alpha = base.a + layer.a*(1-base.a)
155
- // so the ring is visible even when the node's base color is fully transparent.
174
+ // Premultiplied "over": out = src + dst*(1 - src.a). The output stays
175
+ // visible on a fully-transparent \`base\` because layer brings its own alpha.
156
176
  float la = clamp(layer.a, 0.0, 1.0);
157
- vec3 blended = mix(base.rgb, layer.rgb, la);
177
+ vec3 blended = base.rgb * (1.0 - la) + layer.rgb;
158
178
  float outA = base.a + la * (1.0 - base.a);
159
179
  gl_FragColor = vec4(blended, outA);
160
180
  }
@@ -87,7 +87,6 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
87
87
  return {
88
88
  width: 0,
89
89
  height: 0,
90
- trimmedHeight: 0,
91
90
  };
92
91
  }
93
92
 
@@ -192,18 +191,10 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
192
191
  if (canvas.width > 0 && canvas.height > 0) {
193
192
  imageData = context.getImageData(0, 0, canvasW, canvasH);
194
193
  }
195
- // Cap-top of first line to descender bottom of last line.
196
- // descender is negative in NormalizedFontMetrics.
197
- const trimmedHeight =
198
- lineAmount > 0
199
- ? metrics.capHeight - metrics.descender + (lineAmount - 1) * lineHeightPx
200
- : 0;
201
-
202
194
  return {
203
195
  imageData,
204
196
  width: effectiveWidth,
205
197
  height: effectiveHeight,
206
- trimmedHeight,
207
198
  remainingLines,
208
199
  hasRemainingText,
209
200
  };
@@ -52,7 +52,6 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
52
52
  return {
53
53
  width: 0,
54
54
  height: 0,
55
- trimmedHeight: 0,
56
55
  };
57
56
  }
58
57
 
@@ -64,7 +63,6 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
64
63
  hasRemainingText: false,
65
64
  width: layout.width,
66
65
  height: layout.height,
67
- trimmedHeight: layout.trimmedHeight,
68
66
  layout,
69
67
  };
70
68
  }
@@ -76,7 +74,6 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
76
74
  return {
77
75
  width: 0,
78
76
  height: 0,
79
- trimmedHeight: 0,
80
77
  };
81
78
  }
82
79
 
@@ -90,7 +87,6 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
90
87
  hasRemainingText: false,
91
88
  width: layout.width,
92
89
  height: layout.height,
93
- trimmedHeight: layout.trimmedHeight,
94
90
  layout,
95
91
  };
96
92
  };
@@ -349,21 +345,12 @@ const generateTextLayout = (
349
345
  }
350
346
  }
351
347
 
352
- // Cap-top of first line to descender bottom of last line.
353
- // descender is negative in NormalizedFontMetrics, so subtracting it
354
- // adds the descender depth. Zero when there are no rendered lines.
355
- const trimmedHeight =
356
- lineAmount > 0
357
- ? metrics.capHeight - metrics.descender + (lineAmount - 1) * lineHeightPx
358
- : 0;
359
-
360
348
  // Convert final dimensions to pixel space for the layout
361
349
  return {
362
350
  glyphs,
363
351
  distanceRange: fontScale * fontData.distanceField.distanceRange,
364
352
  width: effectiveWidth * fontScale,
365
353
  height: effectiveHeight,
366
- trimmedHeight,
367
354
  fontScale: fontScale,
368
355
  lineHeight: lineHeightPx,
369
356
  fontFamily,
@@ -320,11 +320,6 @@ export interface TextLayout {
320
320
  * Total text height
321
321
  */
322
322
  height: number;
323
- /**
324
- * Trimmed text height — cap-top of the first line to descender bottom
325
- * of the last line. See `TextRenderInfo.trimmedHeight`.
326
- */
327
- trimmedHeight: number;
328
323
  /**
329
324
  * Font scale factor
330
325
  */
@@ -421,21 +416,6 @@ export interface TextRenderProps {
421
416
  export interface TextRenderInfo {
422
417
  width: number;
423
418
  height: number;
424
- /**
425
- * Height of the visible glyph extent — from the first line's cap-top to
426
- * the last line's descender bottom. Excludes half-leading and the slack
427
- * between the font's ascender and cap-top.
428
- *
429
- * @remarks
430
- * Formula: `capHeight − descender + (lines − 1) × lineHeightPx`
431
- * (descender is negative in font metrics, so subtracting it adds the
432
- * descender depth). For empty text, this is 0.
433
- *
434
- * Use this when you want flex `alignItems: 'center'` (or any layout
435
- * that aligns by node `h`) to optically center the visible glyphs.
436
- * Set `node.h = node.trimmedHeight` after the `loaded` event.
437
- */
438
- trimmedHeight: number;
439
419
  hasRemainingText?: boolean;
440
420
  remainingLines?: number;
441
421
  imageData?: ImageData | null; // Image data for Canvas Text Renderer
@@ -848,10 +848,21 @@ export class Inspector {
848
848
  }
849
849
 
850
850
  createTextNode(node: CoreTextNode): CoreTextNode {
851
+ // CoreTextNode carries two prop bags:
852
+ // - `node.props` (inherited CoreNodeProps): x, y, w, h, mount*, alpha…
853
+ // - `node.textProps` (private): text, fontSize, fontFamily, lineHeight…
854
+ // The mirror div needs both, so we merge them for the initial paint.
855
+ // Without `node.props` the div would have no left/top and render at (0,0)
856
+ // regardless of where the canvas drew the text.
851
857
  // eslint-disable-next-line
852
858
  // @ts-ignore - textProps is a private property and keeping it that way
853
859
  // but we need it from the inspector to set the initial properties on the div element
854
- const div = this.createDiv(node.id, node.textProps);
860
+ const mergedProps = {
861
+ ...node.props,
862
+
863
+ ...(node as unknown as { textProps: CoreTextNodeProps }).textProps,
864
+ } as CoreTextNodeProps;
865
+ const div = this.createDiv(node.id, mergedProps);
855
866
  (div as HTMLElement & { node: CoreNode }).node = node;
856
867
  (node as CoreTextNode & { div: HTMLElement }).div = div;
857
868
 
@@ -952,12 +963,18 @@ export class Inspector {
952
963
  };
953
964
  // Define traps for each property in knownProperties
954
965
  knownProperties.forEach((property) => {
955
- let originalProp = Object.getOwnPropertyDescriptor(node, property);
956
-
957
- if (originalProp === undefined) {
958
- // Search the prototype chain for the property descriptor
959
- const proto = Object.getPrototypeOf(node) as CoreNode | CoreTextNode;
966
+ // Walk the entire prototype chain. CoreTextNode extends CoreNode, so
967
+ // properties like x / y / w / h / mountX / mountY are defined on
968
+ // CoreNode.prototype two hops up from a CoreTextNode instance. A
969
+ // single-level lookup (the previous behavior) returned undefined for
970
+ // those props on text nodes and skipped installing the trap, which
971
+ // meant updates to those props never reached the mirror div.
972
+ let originalProp: PropertyDescriptor | undefined =
973
+ Object.getOwnPropertyDescriptor(node, property);
974
+ let proto: object | null = Object.getPrototypeOf(node) as object | null;
975
+ while (originalProp === undefined && proto !== null) {
960
976
  originalProp = Object.getOwnPropertyDescriptor(proto, property);
977
+ proto = Object.getPrototypeOf(proto) as object | null;
961
978
  }
962
979
 
963
980
  if (originalProp === undefined) {