@react-native-motion-kit/text-motion 0.0.0-stage → 0.1.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.
Files changed (252) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +20 -0
  3. package/README.ko.md +698 -0
  4. package/README.md +696 -2
  5. package/lib/module/accessibility/index.js +4 -0
  6. package/lib/module/accessibility/index.js.map +1 -0
  7. package/lib/module/accessibility/policies.js +39 -0
  8. package/lib/module/accessibility/policies.js.map +1 -0
  9. package/lib/module/controls/brand.js +4 -0
  10. package/lib/module/controls/brand.js.map +1 -0
  11. package/lib/module/controls/controls.js +28 -0
  12. package/lib/module/controls/controls.js.map +1 -0
  13. package/lib/module/controls/descriptors.js +55 -0
  14. package/lib/module/controls/descriptors.js.map +1 -0
  15. package/lib/module/controls/index.js +4 -0
  16. package/lib/module/controls/index.js.map +1 -0
  17. package/lib/module/effects/compose.js +48 -0
  18. package/lib/module/effects/compose.js.map +1 -0
  19. package/lib/module/effects/fade.js +15 -0
  20. package/lib/module/effects/fade.js.map +1 -0
  21. package/lib/module/effects/index.js +12 -0
  22. package/lib/module/effects/index.js.map +1 -0
  23. package/lib/module/effects/lineReveal.js +17 -0
  24. package/lib/module/effects/lineReveal.js.map +1 -0
  25. package/lib/module/effects/pulse.js +14 -0
  26. package/lib/module/effects/pulse.js.map +1 -0
  27. package/lib/module/effects/rise.js +14 -0
  28. package/lib/module/effects/rise.js.map +1 -0
  29. package/lib/module/effects/scale.js +15 -0
  30. package/lib/module/effects/scale.js.map +1 -0
  31. package/lib/module/effects/shake.js +14 -0
  32. package/lib/module/effects/shake.js.map +1 -0
  33. package/lib/module/effects/slide.js +15 -0
  34. package/lib/module/effects/slide.js.map +1 -0
  35. package/lib/module/effects/typewriter.js +19 -0
  36. package/lib/module/effects/typewriter.js.map +1 -0
  37. package/lib/module/effects/validation.js +10 -0
  38. package/lib/module/effects/validation.js.map +1 -0
  39. package/lib/module/index.js +10 -0
  40. package/lib/module/index.js.map +1 -0
  41. package/lib/module/package.json +1 -0
  42. package/lib/module/presets/index.js +35 -0
  43. package/lib/module/presets/index.js.map +1 -0
  44. package/lib/module/recipe/component.js +60 -0
  45. package/lib/module/recipe/component.js.map +1 -0
  46. package/lib/module/recipe/defineTextMotion.js +9 -0
  47. package/lib/module/recipe/defineTextMotion.js.map +1 -0
  48. package/lib/module/recipe/descriptors.js +60 -0
  49. package/lib/module/recipe/descriptors.js.map +1 -0
  50. package/lib/module/recipe/index.js +5 -0
  51. package/lib/module/recipe/index.js.map +1 -0
  52. package/lib/module/recipe/recipe.js +163 -0
  53. package/lib/module/recipe/recipe.js.map +1 -0
  54. package/lib/module/renderers/index.js +5 -0
  55. package/lib/module/renderers/index.js.map +1 -0
  56. package/lib/module/renderers/nativeText.js +494 -0
  57. package/lib/module/renderers/nativeText.js.map +1 -0
  58. package/lib/module/renderers/nativeTextControls.js +96 -0
  59. package/lib/module/renderers/nativeTextControls.js.map +1 -0
  60. package/lib/module/renderers/nativeTextLineLayout.js +290 -0
  61. package/lib/module/renderers/nativeTextLineLayout.js.map +1 -0
  62. package/lib/module/renderers/nativeTextPlayback.js +91 -0
  63. package/lib/module/renderers/nativeTextPlayback.js.map +1 -0
  64. package/lib/module/renderers/overlayText.js +543 -0
  65. package/lib/module/renderers/overlayText.js.map +1 -0
  66. package/lib/module/renderers/overlayTextLayout.js +182 -0
  67. package/lib/module/renderers/overlayTextLayout.js.map +1 -0
  68. package/lib/module/renderers/rendererMotion.js +264 -0
  69. package/lib/module/renderers/rendererMotion.js.map +1 -0
  70. package/lib/module/split/createToken.js +19 -0
  71. package/lib/module/split/createToken.js.map +1 -0
  72. package/lib/module/split/custom.js +57 -0
  73. package/lib/module/split/custom.js.map +1 -0
  74. package/lib/module/split/graphemes.js +25 -0
  75. package/lib/module/split/graphemes.js.map +1 -0
  76. package/lib/module/split/index.js +7 -0
  77. package/lib/module/split/index.js.map +1 -0
  78. package/lib/module/split/lines.js +43 -0
  79. package/lib/module/split/lines.js.map +1 -0
  80. package/lib/module/split/segmenter.js +74 -0
  81. package/lib/module/split/segmenter.js.map +1 -0
  82. package/lib/module/split/words.js +28 -0
  83. package/lib/module/split/words.js.map +1 -0
  84. package/lib/module/timeline/index.js +7 -0
  85. package/lib/module/timeline/index.js.map +1 -0
  86. package/lib/module/timeline/parallel.js +21 -0
  87. package/lib/module/timeline/parallel.js.map +1 -0
  88. package/lib/module/timeline/sequence.js +18 -0
  89. package/lib/module/timeline/sequence.js.map +1 -0
  90. package/lib/module/timeline/stagger.js +43 -0
  91. package/lib/module/timeline/stagger.js.map +1 -0
  92. package/lib/module/timeline/validation.js +24 -0
  93. package/lib/module/timeline/validation.js.map +1 -0
  94. package/lib/module/timeline/wave.js +25 -0
  95. package/lib/module/timeline/wave.js.map +1 -0
  96. package/lib/module/types/index.js +4 -0
  97. package/lib/module/types/index.js.map +1 -0
  98. package/lib/module/types/recipe.js +4 -0
  99. package/lib/module/types/recipe.js.map +1 -0
  100. package/lib/module/types/renderer.js +27 -0
  101. package/lib/module/types/renderer.js.map +1 -0
  102. package/lib/module/types/token.js +2 -0
  103. package/lib/module/types/token.js.map +1 -0
  104. package/lib/typescript/package.json +1 -0
  105. package/lib/typescript/src/accessibility/index.d.ts +2 -0
  106. package/lib/typescript/src/accessibility/index.d.ts.map +1 -0
  107. package/lib/typescript/src/accessibility/policies.d.ts +13 -0
  108. package/lib/typescript/src/accessibility/policies.d.ts.map +1 -0
  109. package/lib/typescript/src/controls/brand.d.ts +2 -0
  110. package/lib/typescript/src/controls/brand.d.ts.map +1 -0
  111. package/lib/typescript/src/controls/controls.d.ts +28 -0
  112. package/lib/typescript/src/controls/controls.d.ts.map +1 -0
  113. package/lib/typescript/src/controls/descriptors.d.ts +14 -0
  114. package/lib/typescript/src/controls/descriptors.d.ts.map +1 -0
  115. package/lib/typescript/src/controls/index.d.ts +3 -0
  116. package/lib/typescript/src/controls/index.d.ts.map +1 -0
  117. package/lib/typescript/src/effects/compose.d.ts +11 -0
  118. package/lib/typescript/src/effects/compose.d.ts.map +1 -0
  119. package/lib/typescript/src/effects/fade.d.ts +10 -0
  120. package/lib/typescript/src/effects/fade.d.ts.map +1 -0
  121. package/lib/typescript/src/effects/index.d.ts +10 -0
  122. package/lib/typescript/src/effects/index.d.ts.map +1 -0
  123. package/lib/typescript/src/effects/lineReveal.d.ts +11 -0
  124. package/lib/typescript/src/effects/lineReveal.d.ts.map +1 -0
  125. package/lib/typescript/src/effects/pulse.d.ts +8 -0
  126. package/lib/typescript/src/effects/pulse.d.ts.map +1 -0
  127. package/lib/typescript/src/effects/rise.d.ts +8 -0
  128. package/lib/typescript/src/effects/rise.d.ts.map +1 -0
  129. package/lib/typescript/src/effects/scale.d.ts +10 -0
  130. package/lib/typescript/src/effects/scale.d.ts.map +1 -0
  131. package/lib/typescript/src/effects/shake.d.ts +8 -0
  132. package/lib/typescript/src/effects/shake.d.ts.map +1 -0
  133. package/lib/typescript/src/effects/slide.d.ts +10 -0
  134. package/lib/typescript/src/effects/slide.d.ts.map +1 -0
  135. package/lib/typescript/src/effects/typewriter.d.ts +13 -0
  136. package/lib/typescript/src/effects/typewriter.d.ts.map +1 -0
  137. package/lib/typescript/src/effects/validation.d.ts +2 -0
  138. package/lib/typescript/src/effects/validation.d.ts.map +1 -0
  139. package/lib/typescript/src/index.d.ts +15 -0
  140. package/lib/typescript/src/index.d.ts.map +1 -0
  141. package/lib/typescript/src/presets/index.d.ts +10 -0
  142. package/lib/typescript/src/presets/index.d.ts.map +1 -0
  143. package/lib/typescript/src/recipe/component.d.ts +3 -0
  144. package/lib/typescript/src/recipe/component.d.ts.map +1 -0
  145. package/lib/typescript/src/recipe/defineTextMotion.d.ts +4 -0
  146. package/lib/typescript/src/recipe/defineTextMotion.d.ts.map +1 -0
  147. package/lib/typescript/src/recipe/descriptors.d.ts +15 -0
  148. package/lib/typescript/src/recipe/descriptors.d.ts.map +1 -0
  149. package/lib/typescript/src/recipe/index.d.ts +4 -0
  150. package/lib/typescript/src/recipe/index.d.ts.map +1 -0
  151. package/lib/typescript/src/recipe/recipe.d.ts +67 -0
  152. package/lib/typescript/src/recipe/recipe.d.ts.map +1 -0
  153. package/lib/typescript/src/renderers/index.d.ts +3 -0
  154. package/lib/typescript/src/renderers/index.d.ts.map +1 -0
  155. package/lib/typescript/src/renderers/nativeText.d.ts +13 -0
  156. package/lib/typescript/src/renderers/nativeText.d.ts.map +1 -0
  157. package/lib/typescript/src/renderers/nativeTextControls.d.ts +16 -0
  158. package/lib/typescript/src/renderers/nativeTextControls.d.ts.map +1 -0
  159. package/lib/typescript/src/renderers/nativeTextLineLayout.d.ts +84 -0
  160. package/lib/typescript/src/renderers/nativeTextLineLayout.d.ts.map +1 -0
  161. package/lib/typescript/src/renderers/nativeTextPlayback.d.ts +21 -0
  162. package/lib/typescript/src/renderers/nativeTextPlayback.d.ts.map +1 -0
  163. package/lib/typescript/src/renderers/overlayText.d.ts +13 -0
  164. package/lib/typescript/src/renderers/overlayText.d.ts.map +1 -0
  165. package/lib/typescript/src/renderers/overlayTextLayout.d.ts +36 -0
  166. package/lib/typescript/src/renderers/overlayTextLayout.d.ts.map +1 -0
  167. package/lib/typescript/src/renderers/rendererMotion.d.ts +53 -0
  168. package/lib/typescript/src/renderers/rendererMotion.d.ts.map +1 -0
  169. package/lib/typescript/src/split/createToken.d.ts +11 -0
  170. package/lib/typescript/src/split/createToken.d.ts.map +1 -0
  171. package/lib/typescript/src/split/custom.d.ts +17 -0
  172. package/lib/typescript/src/split/custom.d.ts.map +1 -0
  173. package/lib/typescript/src/split/graphemes.d.ts +4 -0
  174. package/lib/typescript/src/split/graphemes.d.ts.map +1 -0
  175. package/lib/typescript/src/split/index.d.ts +5 -0
  176. package/lib/typescript/src/split/index.d.ts.map +1 -0
  177. package/lib/typescript/src/split/lines.d.ts +4 -0
  178. package/lib/typescript/src/split/lines.d.ts.map +1 -0
  179. package/lib/typescript/src/split/segmenter.d.ts +18 -0
  180. package/lib/typescript/src/split/segmenter.d.ts.map +1 -0
  181. package/lib/typescript/src/split/words.d.ts +4 -0
  182. package/lib/typescript/src/split/words.d.ts.map +1 -0
  183. package/lib/typescript/src/timeline/index.d.ts +5 -0
  184. package/lib/typescript/src/timeline/index.d.ts.map +1 -0
  185. package/lib/typescript/src/timeline/parallel.d.ts +4 -0
  186. package/lib/typescript/src/timeline/parallel.d.ts.map +1 -0
  187. package/lib/typescript/src/timeline/sequence.d.ts +4 -0
  188. package/lib/typescript/src/timeline/sequence.d.ts.map +1 -0
  189. package/lib/typescript/src/timeline/stagger.d.ts +11 -0
  190. package/lib/typescript/src/timeline/stagger.d.ts.map +1 -0
  191. package/lib/typescript/src/timeline/validation.d.ts +4 -0
  192. package/lib/typescript/src/timeline/validation.d.ts.map +1 -0
  193. package/lib/typescript/src/timeline/wave.d.ts +11 -0
  194. package/lib/typescript/src/timeline/wave.d.ts.map +1 -0
  195. package/lib/typescript/src/types/index.d.ts +6 -0
  196. package/lib/typescript/src/types/index.d.ts.map +1 -0
  197. package/lib/typescript/src/types/recipe.d.ts +123 -0
  198. package/lib/typescript/src/types/recipe.d.ts.map +1 -0
  199. package/lib/typescript/src/types/renderer.d.ts +51 -0
  200. package/lib/typescript/src/types/renderer.d.ts.map +1 -0
  201. package/lib/typescript/src/types/token.d.ts +35 -0
  202. package/lib/typescript/src/types/token.d.ts.map +1 -0
  203. package/package.json +116 -3
  204. package/src/accessibility/index.ts +7 -0
  205. package/src/accessibility/policies.ts +60 -0
  206. package/src/controls/brand.ts +3 -0
  207. package/src/controls/controls.ts +41 -0
  208. package/src/controls/descriptors.ts +83 -0
  209. package/src/controls/index.ts +2 -0
  210. package/src/effects/compose.ts +98 -0
  211. package/src/effects/fade.ts +22 -0
  212. package/src/effects/index.ts +9 -0
  213. package/src/effects/lineReveal.ts +28 -0
  214. package/src/effects/pulse.ts +19 -0
  215. package/src/effects/rise.ts +19 -0
  216. package/src/effects/scale.ts +22 -0
  217. package/src/effects/shake.ts +19 -0
  218. package/src/effects/slide.ts +22 -0
  219. package/src/effects/typewriter.ts +16 -0
  220. package/src/effects/validation.ts +9 -0
  221. package/src/index.tsx +46 -0
  222. package/src/presets/index.ts +42 -0
  223. package/src/recipe/component.tsx +88 -0
  224. package/src/recipe/defineTextMotion.ts +6 -0
  225. package/src/recipe/descriptors.ts +158 -0
  226. package/src/recipe/index.ts +3 -0
  227. package/src/recipe/recipe.ts +359 -0
  228. package/src/renderers/index.ts +2 -0
  229. package/src/renderers/nativeText.tsx +813 -0
  230. package/src/renderers/nativeTextControls.ts +131 -0
  231. package/src/renderers/nativeTextLineLayout.ts +495 -0
  232. package/src/renderers/nativeTextPlayback.ts +204 -0
  233. package/src/renderers/overlayText.tsx +945 -0
  234. package/src/renderers/overlayTextLayout.ts +312 -0
  235. package/src/renderers/rendererMotion.ts +483 -0
  236. package/src/split/createToken.ts +26 -0
  237. package/src/split/custom.ts +92 -0
  238. package/src/split/graphemes.ts +27 -0
  239. package/src/split/index.ts +9 -0
  240. package/src/split/lines.ts +35 -0
  241. package/src/split/segmenter.ts +111 -0
  242. package/src/split/words.ts +28 -0
  243. package/src/timeline/index.ts +4 -0
  244. package/src/timeline/parallel.ts +30 -0
  245. package/src/timeline/sequence.ts +29 -0
  246. package/src/timeline/stagger.ts +60 -0
  247. package/src/timeline/validation.ts +29 -0
  248. package/src/timeline/wave.ts +34 -0
  249. package/src/types/index.ts +34 -0
  250. package/src/types/recipe.ts +195 -0
  251. package/src/types/renderer.ts +91 -0
  252. package/src/types/token.ts +39 -0
@@ -0,0 +1,312 @@
1
+ export type OverlayTextLineGeometry = {
2
+ height: number;
3
+ index: number;
4
+ isStructuralBlank: boolean;
5
+ motionIndex?: number;
6
+ text: string;
7
+ width: number;
8
+ x: number;
9
+ y: number;
10
+ };
11
+
12
+ export type OverlayTextMotionLine = OverlayTextLineGeometry & {
13
+ motionIndex: number;
14
+ };
15
+
16
+ export type OverlayTextLayoutReady = {
17
+ geometrySignature: string;
18
+ kind: 'ready';
19
+ lines: readonly OverlayTextLineGeometry[];
20
+ motionCount: number;
21
+ motionLines: readonly OverlayTextMotionLine[];
22
+ playbackSignature: string;
23
+ signature: string;
24
+ topologySignature: string;
25
+ };
26
+
27
+ export type OverlayTextLayoutStatic = {
28
+ kind: 'static';
29
+ reason: 'empty-source';
30
+ };
31
+
32
+ export type OverlayTextLayoutFallback = {
33
+ kind: 'fallback';
34
+ reason:
35
+ | 'malformed-payload'
36
+ | 'missing-line-text'
37
+ | 'non-finite-geometry'
38
+ | 'non-positive-visible-height'
39
+ | 'unordered-bands'
40
+ | 'overlapping-bands'
41
+ | 'missing-visible-lines';
42
+ };
43
+
44
+ export type OverlayTextLayoutResult =
45
+ | OverlayTextLayoutFallback
46
+ | OverlayTextLayoutReady
47
+ | OverlayTextLayoutStatic;
48
+
49
+ export type OverlayTextLayoutChange = 'identical' | 'geometry' | 'topology';
50
+
51
+ type OverlayTextNativeLine = {
52
+ height: unknown;
53
+ text: unknown;
54
+ width: unknown;
55
+ x: unknown;
56
+ y: unknown;
57
+ };
58
+
59
+ type OverlayTextLayoutDraft = {
60
+ lines: readonly OverlayTextLineGeometry[];
61
+ motionIndex: number;
62
+ };
63
+
64
+ const GEOMETRY_NOISE_STEP = 0.01;
65
+ const BAND_OVERLAP_TOLERANCE = 0.5;
66
+
67
+ function isRecord(value: unknown): value is Record<string, unknown> {
68
+ return typeof value === 'object' && value !== null;
69
+ }
70
+
71
+ function isFallbackResult(
72
+ value: OverlayTextLayoutDraft | OverlayTextLayoutFallback,
73
+ ): value is OverlayTextLayoutFallback {
74
+ return 'kind' in value;
75
+ }
76
+
77
+ function readPayloadRecord(payload: unknown): Record<string, unknown> | undefined {
78
+ if (!isRecord(payload)) {
79
+ return undefined;
80
+ }
81
+
82
+ if ('lines' in payload) {
83
+ return payload;
84
+ }
85
+
86
+ const nestedPayload = payload.nativeEvent;
87
+
88
+ if (!isRecord(nestedPayload)) {
89
+ return payload;
90
+ }
91
+
92
+ return 'lines' in nestedPayload ? nestedPayload : readPayloadRecord(nestedPayload);
93
+ }
94
+
95
+ function normalizeFloat(value: number): number {
96
+ const normalized = Math.round(value / GEOMETRY_NOISE_STEP) * GEOMETRY_NOISE_STEP;
97
+
98
+ return Object.is(normalized, -0) ? 0 : normalized;
99
+ }
100
+
101
+ function parseNativeLine(value: unknown): OverlayTextNativeLine | undefined {
102
+ if (!isRecord(value)) {
103
+ return undefined;
104
+ }
105
+
106
+ return {
107
+ height: value.height,
108
+ text: value.text,
109
+ width: value.width,
110
+ x: value.x,
111
+ y: value.y,
112
+ };
113
+ }
114
+
115
+ function parseNativeLines(
116
+ values: readonly unknown[],
117
+ ): readonly OverlayTextNativeLine[] | undefined {
118
+ const lines = values.map(parseNativeLine);
119
+
120
+ return lines.every((line): line is OverlayTextNativeLine => Boolean(line)) ? lines : undefined;
121
+ }
122
+
123
+ function readFiniteNumber(value: unknown): number | undefined {
124
+ return typeof value === 'number' && Number.isFinite(value) ? normalizeFloat(value) : undefined;
125
+ }
126
+
127
+ function createGeometrySignature(lines: readonly OverlayTextLineGeometry[]): string {
128
+ return JSON.stringify(lines.map((line) => [line.index, line.x, line.y, line.width, line.height]));
129
+ }
130
+
131
+ function createTopologySignature(lines: readonly OverlayTextLineGeometry[]): string {
132
+ return JSON.stringify(lines.map((line) => [line.index, line.text, line.isStructuralBlank]));
133
+ }
134
+
135
+ function createReadyLayout(
136
+ sourceText: string,
137
+ lines: readonly OverlayTextLineGeometry[],
138
+ ): OverlayTextLayoutReady {
139
+ const motionLines = lines.filter(
140
+ (line): line is OverlayTextMotionLine => typeof line.motionIndex === 'number',
141
+ );
142
+ const geometrySignature = createGeometrySignature(lines);
143
+ const topologySignature = createTopologySignature(lines);
144
+ const playbackSignature = JSON.stringify([sourceText, topologySignature]);
145
+
146
+ return {
147
+ geometrySignature,
148
+ kind: 'ready',
149
+ lines,
150
+ motionCount: motionLines.length,
151
+ motionLines,
152
+ playbackSignature,
153
+ signature: `${topologySignature}::${geometrySignature}`,
154
+ topologySignature,
155
+ };
156
+ }
157
+
158
+ function isMeaningfullyUnordered(
159
+ previous: OverlayTextLineGeometry | undefined,
160
+ current: OverlayTextLineGeometry,
161
+ ): boolean {
162
+ return Boolean(previous && current.y + BAND_OVERLAP_TOLERANCE < previous.y);
163
+ }
164
+
165
+ function isMeaningfullyOverlapping(
166
+ previous: OverlayTextLineGeometry | undefined,
167
+ current: OverlayTextLineGeometry,
168
+ ): boolean {
169
+ if (!previous) {
170
+ return false;
171
+ }
172
+
173
+ return current.y < previous.y + previous.height - BAND_OVERLAP_TOLERANCE;
174
+ }
175
+
176
+ export function compareOverlayTextLayouts(
177
+ previous: OverlayTextLayoutReady | undefined,
178
+ next: OverlayTextLayoutReady,
179
+ ): OverlayTextLayoutChange {
180
+ if (!previous) {
181
+ return 'topology';
182
+ }
183
+
184
+ if (previous.signature === next.signature) {
185
+ return 'identical';
186
+ }
187
+
188
+ if (previous.topologySignature === next.topologySignature) {
189
+ return 'geometry';
190
+ }
191
+
192
+ return 'topology';
193
+ }
194
+
195
+ export function createOverlayTextLayout(source: string, payload: unknown): OverlayTextLayoutResult {
196
+ if (source.trim().length === 0) {
197
+ return {
198
+ kind: 'static',
199
+ reason: 'empty-source',
200
+ };
201
+ }
202
+
203
+ const payloadRecord = readPayloadRecord(payload);
204
+
205
+ if (!payloadRecord || !Array.isArray(payloadRecord.lines)) {
206
+ return {
207
+ kind: 'fallback',
208
+ reason: 'malformed-payload',
209
+ };
210
+ }
211
+
212
+ const parsedLines = parseNativeLines(payloadRecord.lines);
213
+
214
+ if (!parsedLines) {
215
+ return {
216
+ kind: 'fallback',
217
+ reason: 'malformed-payload',
218
+ };
219
+ }
220
+
221
+ const initial: OverlayTextLayoutDraft = {
222
+ lines: [],
223
+ motionIndex: 0,
224
+ };
225
+ const normalized = parsedLines.reduce<OverlayTextLayoutDraft | OverlayTextLayoutFallback>(
226
+ (accumulator, line, index) => {
227
+ if (isFallbackResult(accumulator)) {
228
+ return accumulator;
229
+ }
230
+
231
+ if (typeof line.text !== 'string') {
232
+ return {
233
+ kind: 'fallback',
234
+ reason: 'missing-line-text',
235
+ };
236
+ }
237
+
238
+ const x = readFiniteNumber(line.x);
239
+ const y = readFiniteNumber(line.y);
240
+ const width = readFiniteNumber(line.width);
241
+ const height = readFiniteNumber(line.height);
242
+
243
+ if (
244
+ typeof x !== 'number' ||
245
+ typeof y !== 'number' ||
246
+ typeof width !== 'number' ||
247
+ typeof height !== 'number'
248
+ ) {
249
+ return {
250
+ kind: 'fallback',
251
+ reason: 'non-finite-geometry',
252
+ };
253
+ }
254
+
255
+ const isStructuralBlank = line.text.trim().length === 0;
256
+
257
+ if (!isStructuralBlank && height <= 0) {
258
+ return {
259
+ kind: 'fallback',
260
+ reason: 'non-positive-visible-height',
261
+ };
262
+ }
263
+
264
+ const previous = accumulator.lines[accumulator.lines.length - 1];
265
+ const current: OverlayTextLineGeometry = {
266
+ height,
267
+ index,
268
+ isStructuralBlank,
269
+ motionIndex: isStructuralBlank ? undefined : accumulator.motionIndex,
270
+ text: line.text,
271
+ width,
272
+ x,
273
+ y,
274
+ };
275
+
276
+ if (isMeaningfullyUnordered(previous, current)) {
277
+ return {
278
+ kind: 'fallback',
279
+ reason: 'unordered-bands',
280
+ };
281
+ }
282
+
283
+ if (isMeaningfullyOverlapping(previous, current)) {
284
+ return {
285
+ kind: 'fallback',
286
+ reason: 'overlapping-bands',
287
+ };
288
+ }
289
+
290
+ return {
291
+ lines: [...accumulator.lines, current],
292
+ motionIndex: isStructuralBlank ? accumulator.motionIndex : accumulator.motionIndex + 1,
293
+ };
294
+ },
295
+ initial,
296
+ );
297
+
298
+ if (isFallbackResult(normalized)) {
299
+ return normalized;
300
+ }
301
+
302
+ const ready = createReadyLayout(source, normalized.lines);
303
+
304
+ if (ready.motionCount > 0) {
305
+ return ready;
306
+ }
307
+
308
+ return {
309
+ kind: 'fallback',
310
+ reason: 'missing-visible-lines',
311
+ };
312
+ }