@grahlnn/comps 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.
- package/README.md +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4070 -0
- package/dist/torph/index.d.ts +1 -0
- package/dist/torph/src/components/Torph.d.ts +102 -0
- package/dist/torph/src/utils/text-layout/pretext.d.ts +2 -0
- package/dist/torph/src/utils/text-layout/pretextMorph.d.ts +37 -0
- package/dist/torph/src/vendor/pretext/analysis.d.ts +31 -0
- package/dist/torph/src/vendor/pretext/bidi.d.ts +1 -0
- package/dist/torph/src/vendor/pretext/layout.d.ts +70 -0
- package/dist/torph/src/vendor/pretext/line-break.d.ts +32 -0
- package/dist/torph/src/vendor/pretext/measurement.d.ts +28 -0
- package/package.json +38 -0
- package/torph/src/vendor/pretext/LICENSE +21 -0
- package/torph/src/vendor/pretext/NOTICE.md +11 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4070 @@
|
|
|
1
|
+
// torph/src/components/Torph.tsx
|
|
2
|
+
import {
|
|
3
|
+
useLayoutEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState
|
|
7
|
+
} from "react";
|
|
8
|
+
|
|
9
|
+
// torph/src/vendor/pretext/bidi.ts
|
|
10
|
+
var baseTypes = [
|
|
11
|
+
"BN",
|
|
12
|
+
"BN",
|
|
13
|
+
"BN",
|
|
14
|
+
"BN",
|
|
15
|
+
"BN",
|
|
16
|
+
"BN",
|
|
17
|
+
"BN",
|
|
18
|
+
"BN",
|
|
19
|
+
"BN",
|
|
20
|
+
"S",
|
|
21
|
+
"B",
|
|
22
|
+
"S",
|
|
23
|
+
"WS",
|
|
24
|
+
"B",
|
|
25
|
+
"BN",
|
|
26
|
+
"BN",
|
|
27
|
+
"BN",
|
|
28
|
+
"BN",
|
|
29
|
+
"BN",
|
|
30
|
+
"BN",
|
|
31
|
+
"BN",
|
|
32
|
+
"BN",
|
|
33
|
+
"BN",
|
|
34
|
+
"BN",
|
|
35
|
+
"BN",
|
|
36
|
+
"BN",
|
|
37
|
+
"BN",
|
|
38
|
+
"BN",
|
|
39
|
+
"B",
|
|
40
|
+
"B",
|
|
41
|
+
"B",
|
|
42
|
+
"S",
|
|
43
|
+
"WS",
|
|
44
|
+
"ON",
|
|
45
|
+
"ON",
|
|
46
|
+
"ET",
|
|
47
|
+
"ET",
|
|
48
|
+
"ET",
|
|
49
|
+
"ON",
|
|
50
|
+
"ON",
|
|
51
|
+
"ON",
|
|
52
|
+
"ON",
|
|
53
|
+
"ON",
|
|
54
|
+
"ON",
|
|
55
|
+
"CS",
|
|
56
|
+
"ON",
|
|
57
|
+
"CS",
|
|
58
|
+
"ON",
|
|
59
|
+
"EN",
|
|
60
|
+
"EN",
|
|
61
|
+
"EN",
|
|
62
|
+
"EN",
|
|
63
|
+
"EN",
|
|
64
|
+
"EN",
|
|
65
|
+
"EN",
|
|
66
|
+
"EN",
|
|
67
|
+
"EN",
|
|
68
|
+
"EN",
|
|
69
|
+
"ON",
|
|
70
|
+
"ON",
|
|
71
|
+
"ON",
|
|
72
|
+
"ON",
|
|
73
|
+
"ON",
|
|
74
|
+
"ON",
|
|
75
|
+
"ON",
|
|
76
|
+
"L",
|
|
77
|
+
"L",
|
|
78
|
+
"L",
|
|
79
|
+
"L",
|
|
80
|
+
"L",
|
|
81
|
+
"L",
|
|
82
|
+
"L",
|
|
83
|
+
"L",
|
|
84
|
+
"L",
|
|
85
|
+
"L",
|
|
86
|
+
"L",
|
|
87
|
+
"L",
|
|
88
|
+
"L",
|
|
89
|
+
"L",
|
|
90
|
+
"L",
|
|
91
|
+
"L",
|
|
92
|
+
"L",
|
|
93
|
+
"L",
|
|
94
|
+
"L",
|
|
95
|
+
"L",
|
|
96
|
+
"L",
|
|
97
|
+
"L",
|
|
98
|
+
"L",
|
|
99
|
+
"L",
|
|
100
|
+
"L",
|
|
101
|
+
"L",
|
|
102
|
+
"ON",
|
|
103
|
+
"ON",
|
|
104
|
+
"ON",
|
|
105
|
+
"ON",
|
|
106
|
+
"ON",
|
|
107
|
+
"ON",
|
|
108
|
+
"L",
|
|
109
|
+
"L",
|
|
110
|
+
"L",
|
|
111
|
+
"L",
|
|
112
|
+
"L",
|
|
113
|
+
"L",
|
|
114
|
+
"L",
|
|
115
|
+
"L",
|
|
116
|
+
"L",
|
|
117
|
+
"L",
|
|
118
|
+
"L",
|
|
119
|
+
"L",
|
|
120
|
+
"L",
|
|
121
|
+
"L",
|
|
122
|
+
"L",
|
|
123
|
+
"L",
|
|
124
|
+
"L",
|
|
125
|
+
"L",
|
|
126
|
+
"L",
|
|
127
|
+
"L",
|
|
128
|
+
"L",
|
|
129
|
+
"L",
|
|
130
|
+
"L",
|
|
131
|
+
"L",
|
|
132
|
+
"L",
|
|
133
|
+
"L",
|
|
134
|
+
"ON",
|
|
135
|
+
"ON",
|
|
136
|
+
"ON",
|
|
137
|
+
"ON",
|
|
138
|
+
"BN",
|
|
139
|
+
"BN",
|
|
140
|
+
"BN",
|
|
141
|
+
"BN",
|
|
142
|
+
"BN",
|
|
143
|
+
"BN",
|
|
144
|
+
"B",
|
|
145
|
+
"BN",
|
|
146
|
+
"BN",
|
|
147
|
+
"BN",
|
|
148
|
+
"BN",
|
|
149
|
+
"BN",
|
|
150
|
+
"BN",
|
|
151
|
+
"BN",
|
|
152
|
+
"BN",
|
|
153
|
+
"BN",
|
|
154
|
+
"BN",
|
|
155
|
+
"BN",
|
|
156
|
+
"BN",
|
|
157
|
+
"BN",
|
|
158
|
+
"BN",
|
|
159
|
+
"BN",
|
|
160
|
+
"BN",
|
|
161
|
+
"BN",
|
|
162
|
+
"BN",
|
|
163
|
+
"BN",
|
|
164
|
+
"BN",
|
|
165
|
+
"BN",
|
|
166
|
+
"BN",
|
|
167
|
+
"BN",
|
|
168
|
+
"BN",
|
|
169
|
+
"BN",
|
|
170
|
+
"BN",
|
|
171
|
+
"CS",
|
|
172
|
+
"ON",
|
|
173
|
+
"ET",
|
|
174
|
+
"ET",
|
|
175
|
+
"ET",
|
|
176
|
+
"ET",
|
|
177
|
+
"ON",
|
|
178
|
+
"ON",
|
|
179
|
+
"ON",
|
|
180
|
+
"ON",
|
|
181
|
+
"L",
|
|
182
|
+
"ON",
|
|
183
|
+
"ON",
|
|
184
|
+
"ON",
|
|
185
|
+
"ON",
|
|
186
|
+
"ON",
|
|
187
|
+
"ET",
|
|
188
|
+
"ET",
|
|
189
|
+
"EN",
|
|
190
|
+
"EN",
|
|
191
|
+
"ON",
|
|
192
|
+
"L",
|
|
193
|
+
"ON",
|
|
194
|
+
"ON",
|
|
195
|
+
"ON",
|
|
196
|
+
"EN",
|
|
197
|
+
"L",
|
|
198
|
+
"ON",
|
|
199
|
+
"ON",
|
|
200
|
+
"ON",
|
|
201
|
+
"ON",
|
|
202
|
+
"ON",
|
|
203
|
+
"L",
|
|
204
|
+
"L",
|
|
205
|
+
"L",
|
|
206
|
+
"L",
|
|
207
|
+
"L",
|
|
208
|
+
"L",
|
|
209
|
+
"L",
|
|
210
|
+
"L",
|
|
211
|
+
"L",
|
|
212
|
+
"L",
|
|
213
|
+
"L",
|
|
214
|
+
"L",
|
|
215
|
+
"L",
|
|
216
|
+
"L",
|
|
217
|
+
"L",
|
|
218
|
+
"L",
|
|
219
|
+
"L",
|
|
220
|
+
"L",
|
|
221
|
+
"L",
|
|
222
|
+
"L",
|
|
223
|
+
"L",
|
|
224
|
+
"L",
|
|
225
|
+
"L",
|
|
226
|
+
"ON",
|
|
227
|
+
"L",
|
|
228
|
+
"L",
|
|
229
|
+
"L",
|
|
230
|
+
"L",
|
|
231
|
+
"L",
|
|
232
|
+
"L",
|
|
233
|
+
"L",
|
|
234
|
+
"L",
|
|
235
|
+
"L",
|
|
236
|
+
"L",
|
|
237
|
+
"L",
|
|
238
|
+
"L",
|
|
239
|
+
"L",
|
|
240
|
+
"L",
|
|
241
|
+
"L",
|
|
242
|
+
"L",
|
|
243
|
+
"L",
|
|
244
|
+
"L",
|
|
245
|
+
"L",
|
|
246
|
+
"L",
|
|
247
|
+
"L",
|
|
248
|
+
"L",
|
|
249
|
+
"L",
|
|
250
|
+
"L",
|
|
251
|
+
"L",
|
|
252
|
+
"L",
|
|
253
|
+
"L",
|
|
254
|
+
"L",
|
|
255
|
+
"L",
|
|
256
|
+
"L",
|
|
257
|
+
"L",
|
|
258
|
+
"ON",
|
|
259
|
+
"L",
|
|
260
|
+
"L",
|
|
261
|
+
"L",
|
|
262
|
+
"L",
|
|
263
|
+
"L",
|
|
264
|
+
"L",
|
|
265
|
+
"L",
|
|
266
|
+
"L"
|
|
267
|
+
];
|
|
268
|
+
var arabicTypes = [
|
|
269
|
+
"AL",
|
|
270
|
+
"AL",
|
|
271
|
+
"AL",
|
|
272
|
+
"AL",
|
|
273
|
+
"AL",
|
|
274
|
+
"AL",
|
|
275
|
+
"AL",
|
|
276
|
+
"AL",
|
|
277
|
+
"AL",
|
|
278
|
+
"AL",
|
|
279
|
+
"AL",
|
|
280
|
+
"AL",
|
|
281
|
+
"CS",
|
|
282
|
+
"AL",
|
|
283
|
+
"ON",
|
|
284
|
+
"ON",
|
|
285
|
+
"NSM",
|
|
286
|
+
"NSM",
|
|
287
|
+
"NSM",
|
|
288
|
+
"NSM",
|
|
289
|
+
"NSM",
|
|
290
|
+
"NSM",
|
|
291
|
+
"AL",
|
|
292
|
+
"AL",
|
|
293
|
+
"AL",
|
|
294
|
+
"AL",
|
|
295
|
+
"AL",
|
|
296
|
+
"AL",
|
|
297
|
+
"AL",
|
|
298
|
+
"AL",
|
|
299
|
+
"AL",
|
|
300
|
+
"AL",
|
|
301
|
+
"AL",
|
|
302
|
+
"AL",
|
|
303
|
+
"AL",
|
|
304
|
+
"AL",
|
|
305
|
+
"AL",
|
|
306
|
+
"AL",
|
|
307
|
+
"AL",
|
|
308
|
+
"AL",
|
|
309
|
+
"AL",
|
|
310
|
+
"AL",
|
|
311
|
+
"AL",
|
|
312
|
+
"AL",
|
|
313
|
+
"AL",
|
|
314
|
+
"AL",
|
|
315
|
+
"AL",
|
|
316
|
+
"AL",
|
|
317
|
+
"AL",
|
|
318
|
+
"AL",
|
|
319
|
+
"AL",
|
|
320
|
+
"AL",
|
|
321
|
+
"AL",
|
|
322
|
+
"AL",
|
|
323
|
+
"AL",
|
|
324
|
+
"AL",
|
|
325
|
+
"AL",
|
|
326
|
+
"AL",
|
|
327
|
+
"AL",
|
|
328
|
+
"AL",
|
|
329
|
+
"AL",
|
|
330
|
+
"AL",
|
|
331
|
+
"AL",
|
|
332
|
+
"AL",
|
|
333
|
+
"AL",
|
|
334
|
+
"AL",
|
|
335
|
+
"AL",
|
|
336
|
+
"AL",
|
|
337
|
+
"AL",
|
|
338
|
+
"AL",
|
|
339
|
+
"AL",
|
|
340
|
+
"AL",
|
|
341
|
+
"AL",
|
|
342
|
+
"AL",
|
|
343
|
+
"AL",
|
|
344
|
+
"NSM",
|
|
345
|
+
"NSM",
|
|
346
|
+
"NSM",
|
|
347
|
+
"NSM",
|
|
348
|
+
"NSM",
|
|
349
|
+
"NSM",
|
|
350
|
+
"NSM",
|
|
351
|
+
"NSM",
|
|
352
|
+
"NSM",
|
|
353
|
+
"NSM",
|
|
354
|
+
"NSM",
|
|
355
|
+
"NSM",
|
|
356
|
+
"NSM",
|
|
357
|
+
"NSM",
|
|
358
|
+
"AL",
|
|
359
|
+
"AL",
|
|
360
|
+
"AL",
|
|
361
|
+
"AL",
|
|
362
|
+
"AL",
|
|
363
|
+
"AL",
|
|
364
|
+
"AL",
|
|
365
|
+
"AN",
|
|
366
|
+
"AN",
|
|
367
|
+
"AN",
|
|
368
|
+
"AN",
|
|
369
|
+
"AN",
|
|
370
|
+
"AN",
|
|
371
|
+
"AN",
|
|
372
|
+
"AN",
|
|
373
|
+
"AN",
|
|
374
|
+
"AN",
|
|
375
|
+
"ET",
|
|
376
|
+
"AN",
|
|
377
|
+
"AN",
|
|
378
|
+
"AL",
|
|
379
|
+
"AL",
|
|
380
|
+
"AL",
|
|
381
|
+
"NSM",
|
|
382
|
+
"AL",
|
|
383
|
+
"AL",
|
|
384
|
+
"AL",
|
|
385
|
+
"AL",
|
|
386
|
+
"AL",
|
|
387
|
+
"AL",
|
|
388
|
+
"AL",
|
|
389
|
+
"AL",
|
|
390
|
+
"AL",
|
|
391
|
+
"AL",
|
|
392
|
+
"AL",
|
|
393
|
+
"AL",
|
|
394
|
+
"AL",
|
|
395
|
+
"AL",
|
|
396
|
+
"AL",
|
|
397
|
+
"AL",
|
|
398
|
+
"AL",
|
|
399
|
+
"AL",
|
|
400
|
+
"AL",
|
|
401
|
+
"AL",
|
|
402
|
+
"AL",
|
|
403
|
+
"AL",
|
|
404
|
+
"AL",
|
|
405
|
+
"AL",
|
|
406
|
+
"AL",
|
|
407
|
+
"AL",
|
|
408
|
+
"AL",
|
|
409
|
+
"AL",
|
|
410
|
+
"AL",
|
|
411
|
+
"AL",
|
|
412
|
+
"AL",
|
|
413
|
+
"AL",
|
|
414
|
+
"AL",
|
|
415
|
+
"AL",
|
|
416
|
+
"AL",
|
|
417
|
+
"AL",
|
|
418
|
+
"AL",
|
|
419
|
+
"AL",
|
|
420
|
+
"AL",
|
|
421
|
+
"AL",
|
|
422
|
+
"AL",
|
|
423
|
+
"AL",
|
|
424
|
+
"AL",
|
|
425
|
+
"AL",
|
|
426
|
+
"AL",
|
|
427
|
+
"AL",
|
|
428
|
+
"AL",
|
|
429
|
+
"AL",
|
|
430
|
+
"AL",
|
|
431
|
+
"AL",
|
|
432
|
+
"AL",
|
|
433
|
+
"AL",
|
|
434
|
+
"AL",
|
|
435
|
+
"AL",
|
|
436
|
+
"AL",
|
|
437
|
+
"AL",
|
|
438
|
+
"AL",
|
|
439
|
+
"AL",
|
|
440
|
+
"AL",
|
|
441
|
+
"AL",
|
|
442
|
+
"AL",
|
|
443
|
+
"AL",
|
|
444
|
+
"AL",
|
|
445
|
+
"AL",
|
|
446
|
+
"AL",
|
|
447
|
+
"AL",
|
|
448
|
+
"AL",
|
|
449
|
+
"AL",
|
|
450
|
+
"AL",
|
|
451
|
+
"AL",
|
|
452
|
+
"AL",
|
|
453
|
+
"AL",
|
|
454
|
+
"AL",
|
|
455
|
+
"AL",
|
|
456
|
+
"AL",
|
|
457
|
+
"AL",
|
|
458
|
+
"AL",
|
|
459
|
+
"AL",
|
|
460
|
+
"AL",
|
|
461
|
+
"AL",
|
|
462
|
+
"AL",
|
|
463
|
+
"AL",
|
|
464
|
+
"AL",
|
|
465
|
+
"AL",
|
|
466
|
+
"AL",
|
|
467
|
+
"AL",
|
|
468
|
+
"AL",
|
|
469
|
+
"AL",
|
|
470
|
+
"AL",
|
|
471
|
+
"AL",
|
|
472
|
+
"AL",
|
|
473
|
+
"AL",
|
|
474
|
+
"AL",
|
|
475
|
+
"AL",
|
|
476
|
+
"AL",
|
|
477
|
+
"AL",
|
|
478
|
+
"AL",
|
|
479
|
+
"AL",
|
|
480
|
+
"AL",
|
|
481
|
+
"AL",
|
|
482
|
+
"AL",
|
|
483
|
+
"NSM",
|
|
484
|
+
"NSM",
|
|
485
|
+
"NSM",
|
|
486
|
+
"NSM",
|
|
487
|
+
"NSM",
|
|
488
|
+
"NSM",
|
|
489
|
+
"NSM",
|
|
490
|
+
"NSM",
|
|
491
|
+
"NSM",
|
|
492
|
+
"NSM",
|
|
493
|
+
"NSM",
|
|
494
|
+
"NSM",
|
|
495
|
+
"NSM",
|
|
496
|
+
"NSM",
|
|
497
|
+
"NSM",
|
|
498
|
+
"NSM",
|
|
499
|
+
"NSM",
|
|
500
|
+
"NSM",
|
|
501
|
+
"NSM",
|
|
502
|
+
"ON",
|
|
503
|
+
"NSM",
|
|
504
|
+
"NSM",
|
|
505
|
+
"NSM",
|
|
506
|
+
"NSM",
|
|
507
|
+
"AL",
|
|
508
|
+
"AL",
|
|
509
|
+
"AL",
|
|
510
|
+
"AL",
|
|
511
|
+
"AL",
|
|
512
|
+
"AL",
|
|
513
|
+
"AL",
|
|
514
|
+
"AL",
|
|
515
|
+
"AL",
|
|
516
|
+
"AL",
|
|
517
|
+
"AL",
|
|
518
|
+
"AL",
|
|
519
|
+
"AL",
|
|
520
|
+
"AL",
|
|
521
|
+
"AL",
|
|
522
|
+
"AL",
|
|
523
|
+
"AL",
|
|
524
|
+
"AL"
|
|
525
|
+
];
|
|
526
|
+
function classifyChar(charCode) {
|
|
527
|
+
if (charCode <= 255)
|
|
528
|
+
return baseTypes[charCode];
|
|
529
|
+
if (1424 <= charCode && charCode <= 1524)
|
|
530
|
+
return "R";
|
|
531
|
+
if (1536 <= charCode && charCode <= 1791)
|
|
532
|
+
return arabicTypes[charCode & 255];
|
|
533
|
+
if (1792 <= charCode && charCode <= 2220)
|
|
534
|
+
return "AL";
|
|
535
|
+
return "L";
|
|
536
|
+
}
|
|
537
|
+
function computeBidiLevels(str) {
|
|
538
|
+
const len = str.length;
|
|
539
|
+
if (len === 0)
|
|
540
|
+
return null;
|
|
541
|
+
const types = new Array(len);
|
|
542
|
+
let numBidi = 0;
|
|
543
|
+
for (let i = 0;i < len; i++) {
|
|
544
|
+
const t = classifyChar(str.charCodeAt(i));
|
|
545
|
+
if (t === "R" || t === "AL" || t === "AN")
|
|
546
|
+
numBidi++;
|
|
547
|
+
types[i] = t;
|
|
548
|
+
}
|
|
549
|
+
if (numBidi === 0)
|
|
550
|
+
return null;
|
|
551
|
+
const startLevel = len / numBidi < 0.3 ? 0 : 1;
|
|
552
|
+
const levels = new Int8Array(len);
|
|
553
|
+
for (let i = 0;i < len; i++)
|
|
554
|
+
levels[i] = startLevel;
|
|
555
|
+
const e = startLevel & 1 ? "R" : "L";
|
|
556
|
+
const sor = e;
|
|
557
|
+
let lastType = sor;
|
|
558
|
+
for (let i = 0;i < len; i++) {
|
|
559
|
+
if (types[i] === "NSM")
|
|
560
|
+
types[i] = lastType;
|
|
561
|
+
else
|
|
562
|
+
lastType = types[i];
|
|
563
|
+
}
|
|
564
|
+
lastType = sor;
|
|
565
|
+
for (let i = 0;i < len; i++) {
|
|
566
|
+
const t = types[i];
|
|
567
|
+
if (t === "EN")
|
|
568
|
+
types[i] = lastType === "AL" ? "AN" : "EN";
|
|
569
|
+
else if (t === "R" || t === "L" || t === "AL")
|
|
570
|
+
lastType = t;
|
|
571
|
+
}
|
|
572
|
+
for (let i = 0;i < len; i++) {
|
|
573
|
+
if (types[i] === "AL")
|
|
574
|
+
types[i] = "R";
|
|
575
|
+
}
|
|
576
|
+
for (let i = 1;i < len - 1; i++) {
|
|
577
|
+
if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
|
|
578
|
+
types[i] = "EN";
|
|
579
|
+
}
|
|
580
|
+
if (types[i] === "CS" && (types[i - 1] === "EN" || types[i - 1] === "AN") && types[i + 1] === types[i - 1]) {
|
|
581
|
+
types[i] = types[i - 1];
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
for (let i = 0;i < len; i++) {
|
|
585
|
+
if (types[i] !== "EN")
|
|
586
|
+
continue;
|
|
587
|
+
let j;
|
|
588
|
+
for (j = i - 1;j >= 0 && types[j] === "ET"; j--)
|
|
589
|
+
types[j] = "EN";
|
|
590
|
+
for (j = i + 1;j < len && types[j] === "ET"; j++)
|
|
591
|
+
types[j] = "EN";
|
|
592
|
+
}
|
|
593
|
+
for (let i = 0;i < len; i++) {
|
|
594
|
+
const t = types[i];
|
|
595
|
+
if (t === "WS" || t === "ES" || t === "ET" || t === "CS")
|
|
596
|
+
types[i] = "ON";
|
|
597
|
+
}
|
|
598
|
+
lastType = sor;
|
|
599
|
+
for (let i = 0;i < len; i++) {
|
|
600
|
+
const t = types[i];
|
|
601
|
+
if (t === "EN")
|
|
602
|
+
types[i] = lastType === "L" ? "L" : "EN";
|
|
603
|
+
else if (t === "R" || t === "L")
|
|
604
|
+
lastType = t;
|
|
605
|
+
}
|
|
606
|
+
for (let i = 0;i < len; i++) {
|
|
607
|
+
if (types[i] !== "ON")
|
|
608
|
+
continue;
|
|
609
|
+
let end = i + 1;
|
|
610
|
+
while (end < len && types[end] === "ON")
|
|
611
|
+
end++;
|
|
612
|
+
const before = i > 0 ? types[i - 1] : sor;
|
|
613
|
+
const after = end < len ? types[end] : sor;
|
|
614
|
+
const bDir = before !== "L" ? "R" : "L";
|
|
615
|
+
const aDir = after !== "L" ? "R" : "L";
|
|
616
|
+
if (bDir === aDir) {
|
|
617
|
+
for (let j = i;j < end; j++)
|
|
618
|
+
types[j] = bDir;
|
|
619
|
+
}
|
|
620
|
+
i = end - 1;
|
|
621
|
+
}
|
|
622
|
+
for (let i = 0;i < len; i++) {
|
|
623
|
+
if (types[i] === "ON")
|
|
624
|
+
types[i] = e;
|
|
625
|
+
}
|
|
626
|
+
for (let i = 0;i < len; i++) {
|
|
627
|
+
const t = types[i];
|
|
628
|
+
if ((levels[i] & 1) === 0) {
|
|
629
|
+
if (t === "R")
|
|
630
|
+
levels[i]++;
|
|
631
|
+
else if (t === "AN" || t === "EN")
|
|
632
|
+
levels[i] += 2;
|
|
633
|
+
} else if (t === "L" || t === "AN" || t === "EN") {
|
|
634
|
+
levels[i]++;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return levels;
|
|
638
|
+
}
|
|
639
|
+
function computeSegmentLevels(normalized, segStarts) {
|
|
640
|
+
const bidiLevels = computeBidiLevels(normalized);
|
|
641
|
+
if (bidiLevels === null)
|
|
642
|
+
return null;
|
|
643
|
+
const segLevels = new Int8Array(segStarts.length);
|
|
644
|
+
for (let i = 0;i < segStarts.length; i++) {
|
|
645
|
+
segLevels[i] = bidiLevels[segStarts[i]];
|
|
646
|
+
}
|
|
647
|
+
return segLevels;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// torph/src/vendor/pretext/analysis.ts
|
|
651
|
+
var collapsibleWhitespaceRunRe = /[ \t\n\r\f]+/g;
|
|
652
|
+
var needsWhitespaceNormalizationRe = /[\t\n\r\f]| {2,}|^ | $/;
|
|
653
|
+
function getWhiteSpaceProfile(whiteSpace) {
|
|
654
|
+
const mode = whiteSpace ?? "normal";
|
|
655
|
+
return mode === "pre-wrap" ? { mode, preserveOrdinarySpaces: true, preserveHardBreaks: true } : { mode, preserveOrdinarySpaces: false, preserveHardBreaks: false };
|
|
656
|
+
}
|
|
657
|
+
function normalizeWhitespaceNormal(text) {
|
|
658
|
+
if (!needsWhitespaceNormalizationRe.test(text))
|
|
659
|
+
return text;
|
|
660
|
+
let normalized = text.replace(collapsibleWhitespaceRunRe, " ");
|
|
661
|
+
if (normalized.charCodeAt(0) === 32) {
|
|
662
|
+
normalized = normalized.slice(1);
|
|
663
|
+
}
|
|
664
|
+
if (normalized.length > 0 && normalized.charCodeAt(normalized.length - 1) === 32) {
|
|
665
|
+
normalized = normalized.slice(0, -1);
|
|
666
|
+
}
|
|
667
|
+
return normalized;
|
|
668
|
+
}
|
|
669
|
+
function normalizeWhitespacePreWrap(text) {
|
|
670
|
+
if (!/[\r\f]/.test(text))
|
|
671
|
+
return text.replace(/\r\n/g, `
|
|
672
|
+
`);
|
|
673
|
+
return text.replace(/\r\n/g, `
|
|
674
|
+
`).replace(/[\r\f]/g, `
|
|
675
|
+
`);
|
|
676
|
+
}
|
|
677
|
+
var sharedWordSegmenter = null;
|
|
678
|
+
var segmenterLocale;
|
|
679
|
+
function getSharedWordSegmenter() {
|
|
680
|
+
if (sharedWordSegmenter === null) {
|
|
681
|
+
sharedWordSegmenter = new Intl.Segmenter(segmenterLocale, { granularity: "word" });
|
|
682
|
+
}
|
|
683
|
+
return sharedWordSegmenter;
|
|
684
|
+
}
|
|
685
|
+
function clearAnalysisCaches() {
|
|
686
|
+
sharedWordSegmenter = null;
|
|
687
|
+
}
|
|
688
|
+
var arabicScriptRe = /\p{Script=Arabic}/u;
|
|
689
|
+
var combiningMarkRe = /\p{M}/u;
|
|
690
|
+
var decimalDigitRe = /\p{Nd}/u;
|
|
691
|
+
function containsArabicScript(text) {
|
|
692
|
+
return arabicScriptRe.test(text);
|
|
693
|
+
}
|
|
694
|
+
function isCJK(s) {
|
|
695
|
+
for (const ch of s) {
|
|
696
|
+
const c = ch.codePointAt(0);
|
|
697
|
+
if (c >= 19968 && c <= 40959 || c >= 13312 && c <= 19903 || c >= 131072 && c <= 173791 || c >= 173824 && c <= 177983 || c >= 177984 && c <= 178207 || c >= 178208 && c <= 183983 || c >= 183984 && c <= 191471 || c >= 196608 && c <= 201551 || c >= 63744 && c <= 64255 || c >= 194560 && c <= 195103 || c >= 12288 && c <= 12351 || c >= 12352 && c <= 12447 || c >= 12448 && c <= 12543 || c >= 44032 && c <= 55215 || c >= 65280 && c <= 65519) {
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
var kinsokuStart = new Set([
|
|
704
|
+
",",
|
|
705
|
+
".",
|
|
706
|
+
"!",
|
|
707
|
+
":",
|
|
708
|
+
";",
|
|
709
|
+
"?",
|
|
710
|
+
"、",
|
|
711
|
+
"。",
|
|
712
|
+
"・",
|
|
713
|
+
")",
|
|
714
|
+
"〕",
|
|
715
|
+
"〉",
|
|
716
|
+
"》",
|
|
717
|
+
"」",
|
|
718
|
+
"』",
|
|
719
|
+
"】",
|
|
720
|
+
"〗",
|
|
721
|
+
"〙",
|
|
722
|
+
"〛",
|
|
723
|
+
"ー",
|
|
724
|
+
"々",
|
|
725
|
+
"〻",
|
|
726
|
+
"ゝ",
|
|
727
|
+
"ゞ",
|
|
728
|
+
"ヽ",
|
|
729
|
+
"ヾ"
|
|
730
|
+
]);
|
|
731
|
+
var kinsokuEnd = new Set([
|
|
732
|
+
'"',
|
|
733
|
+
"(",
|
|
734
|
+
"[",
|
|
735
|
+
"{",
|
|
736
|
+
"“",
|
|
737
|
+
"‘",
|
|
738
|
+
"«",
|
|
739
|
+
"‹",
|
|
740
|
+
"(",
|
|
741
|
+
"〔",
|
|
742
|
+
"〈",
|
|
743
|
+
"《",
|
|
744
|
+
"「",
|
|
745
|
+
"『",
|
|
746
|
+
"【",
|
|
747
|
+
"〖",
|
|
748
|
+
"〘",
|
|
749
|
+
"〚"
|
|
750
|
+
]);
|
|
751
|
+
var forwardStickyGlue = new Set(["'", "’"]);
|
|
752
|
+
var leftStickyPunctuation = new Set([
|
|
753
|
+
".",
|
|
754
|
+
",",
|
|
755
|
+
"!",
|
|
756
|
+
"?",
|
|
757
|
+
":",
|
|
758
|
+
";",
|
|
759
|
+
"،",
|
|
760
|
+
"؛",
|
|
761
|
+
"؟",
|
|
762
|
+
"।",
|
|
763
|
+
"॥",
|
|
764
|
+
"၊",
|
|
765
|
+
"။",
|
|
766
|
+
"၌",
|
|
767
|
+
"၍",
|
|
768
|
+
"၏",
|
|
769
|
+
")",
|
|
770
|
+
"]",
|
|
771
|
+
"}",
|
|
772
|
+
"%",
|
|
773
|
+
'"',
|
|
774
|
+
"”",
|
|
775
|
+
"’",
|
|
776
|
+
"»",
|
|
777
|
+
"›",
|
|
778
|
+
"…"
|
|
779
|
+
]);
|
|
780
|
+
var arabicNoSpaceTrailingPunctuation = new Set([":", ".", "،", "؛"]);
|
|
781
|
+
var myanmarMedialGlue = new Set(["၏"]);
|
|
782
|
+
var closingQuoteChars = new Set([
|
|
783
|
+
"”",
|
|
784
|
+
"’",
|
|
785
|
+
"»",
|
|
786
|
+
"›",
|
|
787
|
+
"」",
|
|
788
|
+
"』",
|
|
789
|
+
"】",
|
|
790
|
+
"》",
|
|
791
|
+
"〉",
|
|
792
|
+
"〕",
|
|
793
|
+
")"
|
|
794
|
+
]);
|
|
795
|
+
function isLeftStickyPunctuationSegment(segment) {
|
|
796
|
+
if (isEscapedQuoteClusterSegment(segment))
|
|
797
|
+
return true;
|
|
798
|
+
let sawPunctuation = false;
|
|
799
|
+
for (const ch of segment) {
|
|
800
|
+
if (leftStickyPunctuation.has(ch)) {
|
|
801
|
+
sawPunctuation = true;
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (sawPunctuation && combiningMarkRe.test(ch))
|
|
805
|
+
continue;
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
return sawPunctuation;
|
|
809
|
+
}
|
|
810
|
+
function isCJKLineStartProhibitedSegment(segment) {
|
|
811
|
+
for (const ch of segment) {
|
|
812
|
+
if (!kinsokuStart.has(ch) && !leftStickyPunctuation.has(ch))
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
return segment.length > 0;
|
|
816
|
+
}
|
|
817
|
+
function isForwardStickyClusterSegment(segment) {
|
|
818
|
+
if (isEscapedQuoteClusterSegment(segment))
|
|
819
|
+
return true;
|
|
820
|
+
for (const ch of segment) {
|
|
821
|
+
if (!kinsokuEnd.has(ch) && !forwardStickyGlue.has(ch) && !combiningMarkRe.test(ch))
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
return segment.length > 0;
|
|
825
|
+
}
|
|
826
|
+
function isEscapedQuoteClusterSegment(segment) {
|
|
827
|
+
let sawQuote = false;
|
|
828
|
+
for (const ch of segment) {
|
|
829
|
+
if (ch === "\\" || combiningMarkRe.test(ch))
|
|
830
|
+
continue;
|
|
831
|
+
if (kinsokuEnd.has(ch) || leftStickyPunctuation.has(ch) || forwardStickyGlue.has(ch)) {
|
|
832
|
+
sawQuote = true;
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
return sawQuote;
|
|
838
|
+
}
|
|
839
|
+
function splitTrailingForwardStickyCluster(text) {
|
|
840
|
+
const chars = Array.from(text);
|
|
841
|
+
let splitIndex = chars.length;
|
|
842
|
+
while (splitIndex > 0) {
|
|
843
|
+
const ch = chars[splitIndex - 1];
|
|
844
|
+
if (combiningMarkRe.test(ch)) {
|
|
845
|
+
splitIndex--;
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (kinsokuEnd.has(ch) || forwardStickyGlue.has(ch)) {
|
|
849
|
+
splitIndex--;
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
if (splitIndex <= 0 || splitIndex === chars.length)
|
|
855
|
+
return null;
|
|
856
|
+
return {
|
|
857
|
+
head: chars.slice(0, splitIndex).join(""),
|
|
858
|
+
tail: chars.slice(splitIndex).join("")
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
function isRepeatedSingleCharRun(segment, ch) {
|
|
862
|
+
if (segment.length === 0)
|
|
863
|
+
return false;
|
|
864
|
+
for (const part of segment) {
|
|
865
|
+
if (part !== ch)
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
function endsWithArabicNoSpacePunctuation(segment) {
|
|
871
|
+
if (!containsArabicScript(segment) || segment.length === 0)
|
|
872
|
+
return false;
|
|
873
|
+
return arabicNoSpaceTrailingPunctuation.has(segment[segment.length - 1]);
|
|
874
|
+
}
|
|
875
|
+
function endsWithMyanmarMedialGlue(segment) {
|
|
876
|
+
if (segment.length === 0)
|
|
877
|
+
return false;
|
|
878
|
+
return myanmarMedialGlue.has(segment[segment.length - 1]);
|
|
879
|
+
}
|
|
880
|
+
function splitLeadingSpaceAndMarks(segment) {
|
|
881
|
+
if (segment.length < 2 || segment[0] !== " ")
|
|
882
|
+
return null;
|
|
883
|
+
const marks = segment.slice(1);
|
|
884
|
+
if (/^\p{M}+$/u.test(marks)) {
|
|
885
|
+
return { space: " ", marks };
|
|
886
|
+
}
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
function endsWithClosingQuote(text) {
|
|
890
|
+
for (let i = text.length - 1;i >= 0; i--) {
|
|
891
|
+
const ch = text[i];
|
|
892
|
+
if (closingQuoteChars.has(ch))
|
|
893
|
+
return true;
|
|
894
|
+
if (!leftStickyPunctuation.has(ch))
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
function classifySegmentBreakChar(ch, whiteSpaceProfile) {
|
|
900
|
+
if (whiteSpaceProfile.preserveOrdinarySpaces || whiteSpaceProfile.preserveHardBreaks) {
|
|
901
|
+
if (ch === " ")
|
|
902
|
+
return "preserved-space";
|
|
903
|
+
if (ch === "\t")
|
|
904
|
+
return "tab";
|
|
905
|
+
if (whiteSpaceProfile.preserveHardBreaks && ch === `
|
|
906
|
+
`)
|
|
907
|
+
return "hard-break";
|
|
908
|
+
}
|
|
909
|
+
if (ch === " ")
|
|
910
|
+
return "space";
|
|
911
|
+
if (ch === " " || ch === " " || ch === "" || ch === "\uFEFF") {
|
|
912
|
+
return "glue";
|
|
913
|
+
}
|
|
914
|
+
if (ch === "")
|
|
915
|
+
return "zero-width-break";
|
|
916
|
+
if (ch === "")
|
|
917
|
+
return "soft-hyphen";
|
|
918
|
+
return "text";
|
|
919
|
+
}
|
|
920
|
+
function splitSegmentByBreakKind(segment, isWordLike, start, whiteSpaceProfile) {
|
|
921
|
+
const pieces = [];
|
|
922
|
+
let currentKind = null;
|
|
923
|
+
let currentText = "";
|
|
924
|
+
let currentStart = start;
|
|
925
|
+
let currentWordLike = false;
|
|
926
|
+
let offset = 0;
|
|
927
|
+
for (const ch of segment) {
|
|
928
|
+
const kind = classifySegmentBreakChar(ch, whiteSpaceProfile);
|
|
929
|
+
const wordLike = kind === "text" && isWordLike;
|
|
930
|
+
if (currentKind !== null && kind === currentKind && wordLike === currentWordLike) {
|
|
931
|
+
currentText += ch;
|
|
932
|
+
offset += ch.length;
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
if (currentKind !== null) {
|
|
936
|
+
pieces.push({
|
|
937
|
+
text: currentText,
|
|
938
|
+
isWordLike: currentWordLike,
|
|
939
|
+
kind: currentKind,
|
|
940
|
+
start: currentStart
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
currentKind = kind;
|
|
944
|
+
currentText = ch;
|
|
945
|
+
currentStart = start + offset;
|
|
946
|
+
currentWordLike = wordLike;
|
|
947
|
+
offset += ch.length;
|
|
948
|
+
}
|
|
949
|
+
if (currentKind !== null) {
|
|
950
|
+
pieces.push({
|
|
951
|
+
text: currentText,
|
|
952
|
+
isWordLike: currentWordLike,
|
|
953
|
+
kind: currentKind,
|
|
954
|
+
start: currentStart
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
return pieces;
|
|
958
|
+
}
|
|
959
|
+
function isTextRunBoundary(kind) {
|
|
960
|
+
return kind === "space" || kind === "preserved-space" || kind === "zero-width-break" || kind === "hard-break";
|
|
961
|
+
}
|
|
962
|
+
var urlSchemeSegmentRe = /^[A-Za-z][A-Za-z0-9+.-]*:$/;
|
|
963
|
+
function isUrlLikeRunStart(segmentation, index) {
|
|
964
|
+
const text = segmentation.texts[index];
|
|
965
|
+
if (text.startsWith("www."))
|
|
966
|
+
return true;
|
|
967
|
+
return urlSchemeSegmentRe.test(text) && index + 1 < segmentation.len && segmentation.kinds[index + 1] === "text" && segmentation.texts[index + 1] === "//";
|
|
968
|
+
}
|
|
969
|
+
function isUrlQueryBoundarySegment(text) {
|
|
970
|
+
return text.includes("?") && (text.includes("://") || text.startsWith("www."));
|
|
971
|
+
}
|
|
972
|
+
function mergeUrlLikeRuns(segmentation) {
|
|
973
|
+
const texts = segmentation.texts.slice();
|
|
974
|
+
const isWordLike = segmentation.isWordLike.slice();
|
|
975
|
+
const kinds = segmentation.kinds.slice();
|
|
976
|
+
const starts = segmentation.starts.slice();
|
|
977
|
+
for (let i = 0;i < segmentation.len; i++) {
|
|
978
|
+
if (kinds[i] !== "text" || !isUrlLikeRunStart(segmentation, i))
|
|
979
|
+
continue;
|
|
980
|
+
let j = i + 1;
|
|
981
|
+
while (j < segmentation.len && !isTextRunBoundary(kinds[j])) {
|
|
982
|
+
texts[i] += texts[j];
|
|
983
|
+
isWordLike[i] = true;
|
|
984
|
+
const endsQueryPrefix = texts[j].includes("?");
|
|
985
|
+
kinds[j] = "text";
|
|
986
|
+
texts[j] = "";
|
|
987
|
+
j++;
|
|
988
|
+
if (endsQueryPrefix)
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
let compactLen = 0;
|
|
993
|
+
for (let read = 0;read < texts.length; read++) {
|
|
994
|
+
const text = texts[read];
|
|
995
|
+
if (text.length === 0)
|
|
996
|
+
continue;
|
|
997
|
+
if (compactLen !== read) {
|
|
998
|
+
texts[compactLen] = text;
|
|
999
|
+
isWordLike[compactLen] = isWordLike[read];
|
|
1000
|
+
kinds[compactLen] = kinds[read];
|
|
1001
|
+
starts[compactLen] = starts[read];
|
|
1002
|
+
}
|
|
1003
|
+
compactLen++;
|
|
1004
|
+
}
|
|
1005
|
+
texts.length = compactLen;
|
|
1006
|
+
isWordLike.length = compactLen;
|
|
1007
|
+
kinds.length = compactLen;
|
|
1008
|
+
starts.length = compactLen;
|
|
1009
|
+
return {
|
|
1010
|
+
len: compactLen,
|
|
1011
|
+
texts,
|
|
1012
|
+
isWordLike,
|
|
1013
|
+
kinds,
|
|
1014
|
+
starts
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function mergeUrlQueryRuns(segmentation) {
|
|
1018
|
+
const texts = [];
|
|
1019
|
+
const isWordLike = [];
|
|
1020
|
+
const kinds = [];
|
|
1021
|
+
const starts = [];
|
|
1022
|
+
for (let i = 0;i < segmentation.len; i++) {
|
|
1023
|
+
const text = segmentation.texts[i];
|
|
1024
|
+
texts.push(text);
|
|
1025
|
+
isWordLike.push(segmentation.isWordLike[i]);
|
|
1026
|
+
kinds.push(segmentation.kinds[i]);
|
|
1027
|
+
starts.push(segmentation.starts[i]);
|
|
1028
|
+
if (!isUrlQueryBoundarySegment(text))
|
|
1029
|
+
continue;
|
|
1030
|
+
const nextIndex = i + 1;
|
|
1031
|
+
if (nextIndex >= segmentation.len || isTextRunBoundary(segmentation.kinds[nextIndex])) {
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
let queryText = "";
|
|
1035
|
+
const queryStart = segmentation.starts[nextIndex];
|
|
1036
|
+
let j = nextIndex;
|
|
1037
|
+
while (j < segmentation.len && !isTextRunBoundary(segmentation.kinds[j])) {
|
|
1038
|
+
queryText += segmentation.texts[j];
|
|
1039
|
+
j++;
|
|
1040
|
+
}
|
|
1041
|
+
if (queryText.length > 0) {
|
|
1042
|
+
texts.push(queryText);
|
|
1043
|
+
isWordLike.push(true);
|
|
1044
|
+
kinds.push("text");
|
|
1045
|
+
starts.push(queryStart);
|
|
1046
|
+
i = j - 1;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return {
|
|
1050
|
+
len: texts.length,
|
|
1051
|
+
texts,
|
|
1052
|
+
isWordLike,
|
|
1053
|
+
kinds,
|
|
1054
|
+
starts
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
var numericJoinerChars = new Set([":", "-", "/", "×", ",", ".", "+", "–", "—"]);
|
|
1058
|
+
var asciiPunctuationChainSegmentRe = /^[A-Za-z0-9_]+[,:;]*$/;
|
|
1059
|
+
var asciiPunctuationChainTrailingJoinersRe = /[,:;]+$/;
|
|
1060
|
+
function segmentContainsDecimalDigit(text) {
|
|
1061
|
+
for (const ch of text) {
|
|
1062
|
+
if (decimalDigitRe.test(ch))
|
|
1063
|
+
return true;
|
|
1064
|
+
}
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
function isNumericRunSegment(text) {
|
|
1068
|
+
if (text.length === 0)
|
|
1069
|
+
return false;
|
|
1070
|
+
for (const ch of text) {
|
|
1071
|
+
if (decimalDigitRe.test(ch) || numericJoinerChars.has(ch))
|
|
1072
|
+
continue;
|
|
1073
|
+
return false;
|
|
1074
|
+
}
|
|
1075
|
+
return true;
|
|
1076
|
+
}
|
|
1077
|
+
function mergeNumericRuns(segmentation) {
|
|
1078
|
+
const texts = [];
|
|
1079
|
+
const isWordLike = [];
|
|
1080
|
+
const kinds = [];
|
|
1081
|
+
const starts = [];
|
|
1082
|
+
for (let i = 0;i < segmentation.len; i++) {
|
|
1083
|
+
const text = segmentation.texts[i];
|
|
1084
|
+
const kind = segmentation.kinds[i];
|
|
1085
|
+
if (kind === "text" && isNumericRunSegment(text) && segmentContainsDecimalDigit(text)) {
|
|
1086
|
+
let mergedText = text;
|
|
1087
|
+
let j = i + 1;
|
|
1088
|
+
while (j < segmentation.len && segmentation.kinds[j] === "text" && isNumericRunSegment(segmentation.texts[j])) {
|
|
1089
|
+
mergedText += segmentation.texts[j];
|
|
1090
|
+
j++;
|
|
1091
|
+
}
|
|
1092
|
+
texts.push(mergedText);
|
|
1093
|
+
isWordLike.push(true);
|
|
1094
|
+
kinds.push("text");
|
|
1095
|
+
starts.push(segmentation.starts[i]);
|
|
1096
|
+
i = j - 1;
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
texts.push(text);
|
|
1100
|
+
isWordLike.push(segmentation.isWordLike[i]);
|
|
1101
|
+
kinds.push(kind);
|
|
1102
|
+
starts.push(segmentation.starts[i]);
|
|
1103
|
+
}
|
|
1104
|
+
return {
|
|
1105
|
+
len: texts.length,
|
|
1106
|
+
texts,
|
|
1107
|
+
isWordLike,
|
|
1108
|
+
kinds,
|
|
1109
|
+
starts
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
function mergeAsciiPunctuationChains(segmentation) {
|
|
1113
|
+
const texts = [];
|
|
1114
|
+
const isWordLike = [];
|
|
1115
|
+
const kinds = [];
|
|
1116
|
+
const starts = [];
|
|
1117
|
+
for (let i = 0;i < segmentation.len; i++) {
|
|
1118
|
+
const text = segmentation.texts[i];
|
|
1119
|
+
const kind = segmentation.kinds[i];
|
|
1120
|
+
const wordLike = segmentation.isWordLike[i];
|
|
1121
|
+
if (kind === "text" && wordLike && asciiPunctuationChainSegmentRe.test(text)) {
|
|
1122
|
+
let mergedText = text;
|
|
1123
|
+
let j = i + 1;
|
|
1124
|
+
while (asciiPunctuationChainTrailingJoinersRe.test(mergedText) && j < segmentation.len && segmentation.kinds[j] === "text" && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe.test(segmentation.texts[j])) {
|
|
1125
|
+
mergedText += segmentation.texts[j];
|
|
1126
|
+
j++;
|
|
1127
|
+
}
|
|
1128
|
+
texts.push(mergedText);
|
|
1129
|
+
isWordLike.push(true);
|
|
1130
|
+
kinds.push("text");
|
|
1131
|
+
starts.push(segmentation.starts[i]);
|
|
1132
|
+
i = j - 1;
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
texts.push(text);
|
|
1136
|
+
isWordLike.push(wordLike);
|
|
1137
|
+
kinds.push(kind);
|
|
1138
|
+
starts.push(segmentation.starts[i]);
|
|
1139
|
+
}
|
|
1140
|
+
return {
|
|
1141
|
+
len: texts.length,
|
|
1142
|
+
texts,
|
|
1143
|
+
isWordLike,
|
|
1144
|
+
kinds,
|
|
1145
|
+
starts
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
function splitHyphenatedNumericRuns(segmentation) {
|
|
1149
|
+
const texts = [];
|
|
1150
|
+
const isWordLike = [];
|
|
1151
|
+
const kinds = [];
|
|
1152
|
+
const starts = [];
|
|
1153
|
+
for (let i = 0;i < segmentation.len; i++) {
|
|
1154
|
+
const text = segmentation.texts[i];
|
|
1155
|
+
if (segmentation.kinds[i] === "text" && text.includes("-")) {
|
|
1156
|
+
const parts = text.split("-");
|
|
1157
|
+
let shouldSplit = parts.length > 1;
|
|
1158
|
+
for (let j = 0;j < parts.length; j++) {
|
|
1159
|
+
const part = parts[j];
|
|
1160
|
+
if (!shouldSplit)
|
|
1161
|
+
break;
|
|
1162
|
+
if (part.length === 0 || !segmentContainsDecimalDigit(part) || !isNumericRunSegment(part)) {
|
|
1163
|
+
shouldSplit = false;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
if (shouldSplit) {
|
|
1167
|
+
let offset = 0;
|
|
1168
|
+
for (let j = 0;j < parts.length; j++) {
|
|
1169
|
+
const part = parts[j];
|
|
1170
|
+
const splitText = j < parts.length - 1 ? `${part}-` : part;
|
|
1171
|
+
texts.push(splitText);
|
|
1172
|
+
isWordLike.push(true);
|
|
1173
|
+
kinds.push("text");
|
|
1174
|
+
starts.push(segmentation.starts[i] + offset);
|
|
1175
|
+
offset += splitText.length;
|
|
1176
|
+
}
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
texts.push(text);
|
|
1181
|
+
isWordLike.push(segmentation.isWordLike[i]);
|
|
1182
|
+
kinds.push(segmentation.kinds[i]);
|
|
1183
|
+
starts.push(segmentation.starts[i]);
|
|
1184
|
+
}
|
|
1185
|
+
return {
|
|
1186
|
+
len: texts.length,
|
|
1187
|
+
texts,
|
|
1188
|
+
isWordLike,
|
|
1189
|
+
kinds,
|
|
1190
|
+
starts
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
function mergeGlueConnectedTextRuns(segmentation) {
|
|
1194
|
+
const texts = [];
|
|
1195
|
+
const isWordLike = [];
|
|
1196
|
+
const kinds = [];
|
|
1197
|
+
const starts = [];
|
|
1198
|
+
let read = 0;
|
|
1199
|
+
while (read < segmentation.len) {
|
|
1200
|
+
let text = segmentation.texts[read];
|
|
1201
|
+
let wordLike = segmentation.isWordLike[read];
|
|
1202
|
+
let kind = segmentation.kinds[read];
|
|
1203
|
+
let start = segmentation.starts[read];
|
|
1204
|
+
if (kind === "glue") {
|
|
1205
|
+
let glueText = text;
|
|
1206
|
+
const glueStart = start;
|
|
1207
|
+
read++;
|
|
1208
|
+
while (read < segmentation.len && segmentation.kinds[read] === "glue") {
|
|
1209
|
+
glueText += segmentation.texts[read];
|
|
1210
|
+
read++;
|
|
1211
|
+
}
|
|
1212
|
+
if (read < segmentation.len && segmentation.kinds[read] === "text") {
|
|
1213
|
+
text = glueText + segmentation.texts[read];
|
|
1214
|
+
wordLike = segmentation.isWordLike[read];
|
|
1215
|
+
kind = "text";
|
|
1216
|
+
start = glueStart;
|
|
1217
|
+
read++;
|
|
1218
|
+
} else {
|
|
1219
|
+
texts.push(glueText);
|
|
1220
|
+
isWordLike.push(false);
|
|
1221
|
+
kinds.push("glue");
|
|
1222
|
+
starts.push(glueStart);
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
} else {
|
|
1226
|
+
read++;
|
|
1227
|
+
}
|
|
1228
|
+
if (kind === "text") {
|
|
1229
|
+
while (read < segmentation.len && segmentation.kinds[read] === "glue") {
|
|
1230
|
+
let glueText = "";
|
|
1231
|
+
while (read < segmentation.len && segmentation.kinds[read] === "glue") {
|
|
1232
|
+
glueText += segmentation.texts[read];
|
|
1233
|
+
read++;
|
|
1234
|
+
}
|
|
1235
|
+
if (read < segmentation.len && segmentation.kinds[read] === "text") {
|
|
1236
|
+
text += glueText + segmentation.texts[read];
|
|
1237
|
+
wordLike = wordLike || segmentation.isWordLike[read];
|
|
1238
|
+
read++;
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
text += glueText;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
texts.push(text);
|
|
1245
|
+
isWordLike.push(wordLike);
|
|
1246
|
+
kinds.push(kind);
|
|
1247
|
+
starts.push(start);
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
len: texts.length,
|
|
1251
|
+
texts,
|
|
1252
|
+
isWordLike,
|
|
1253
|
+
kinds,
|
|
1254
|
+
starts
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
function carryTrailingForwardStickyAcrossCJKBoundary(segmentation) {
|
|
1258
|
+
const texts = segmentation.texts.slice();
|
|
1259
|
+
const isWordLike = segmentation.isWordLike.slice();
|
|
1260
|
+
const kinds = segmentation.kinds.slice();
|
|
1261
|
+
const starts = segmentation.starts.slice();
|
|
1262
|
+
for (let i = 0;i < texts.length - 1; i++) {
|
|
1263
|
+
if (kinds[i] !== "text" || kinds[i + 1] !== "text")
|
|
1264
|
+
continue;
|
|
1265
|
+
if (!isCJK(texts[i]) || !isCJK(texts[i + 1]))
|
|
1266
|
+
continue;
|
|
1267
|
+
const split = splitTrailingForwardStickyCluster(texts[i]);
|
|
1268
|
+
if (split === null)
|
|
1269
|
+
continue;
|
|
1270
|
+
texts[i] = split.head;
|
|
1271
|
+
texts[i + 1] = split.tail + texts[i + 1];
|
|
1272
|
+
starts[i + 1] = starts[i] + split.head.length;
|
|
1273
|
+
}
|
|
1274
|
+
return {
|
|
1275
|
+
len: texts.length,
|
|
1276
|
+
texts,
|
|
1277
|
+
isWordLike,
|
|
1278
|
+
kinds,
|
|
1279
|
+
starts
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
function buildMergedSegmentation(normalized, profile, whiteSpaceProfile) {
|
|
1283
|
+
const wordSegmenter = getSharedWordSegmenter();
|
|
1284
|
+
let mergedLen = 0;
|
|
1285
|
+
const mergedTexts = [];
|
|
1286
|
+
const mergedWordLike = [];
|
|
1287
|
+
const mergedKinds = [];
|
|
1288
|
+
const mergedStarts = [];
|
|
1289
|
+
for (const s of wordSegmenter.segment(normalized)) {
|
|
1290
|
+
for (const piece of splitSegmentByBreakKind(s.segment, s.isWordLike ?? false, s.index, whiteSpaceProfile)) {
|
|
1291
|
+
const isText = piece.kind === "text";
|
|
1292
|
+
if (profile.carryCJKAfterClosingQuote && isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && isCJK(piece.text) && isCJK(mergedTexts[mergedLen - 1]) && endsWithClosingQuote(mergedTexts[mergedLen - 1])) {
|
|
1293
|
+
mergedTexts[mergedLen - 1] += piece.text;
|
|
1294
|
+
mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
|
|
1295
|
+
} else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && isCJKLineStartProhibitedSegment(piece.text) && isCJK(mergedTexts[mergedLen - 1])) {
|
|
1296
|
+
mergedTexts[mergedLen - 1] += piece.text;
|
|
1297
|
+
mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
|
|
1298
|
+
} else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && endsWithMyanmarMedialGlue(mergedTexts[mergedLen - 1])) {
|
|
1299
|
+
mergedTexts[mergedLen - 1] += piece.text;
|
|
1300
|
+
mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
|
|
1301
|
+
} else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && piece.isWordLike && containsArabicScript(piece.text) && endsWithArabicNoSpacePunctuation(mergedTexts[mergedLen - 1])) {
|
|
1302
|
+
mergedTexts[mergedLen - 1] += piece.text;
|
|
1303
|
+
mergedWordLike[mergedLen - 1] = true;
|
|
1304
|
+
} else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && piece.text.length === 1 && piece.text !== "-" && piece.text !== "—" && isRepeatedSingleCharRun(mergedTexts[mergedLen - 1], piece.text)) {
|
|
1305
|
+
mergedTexts[mergedLen - 1] += piece.text;
|
|
1306
|
+
} else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === "text" && (isLeftStickyPunctuationSegment(piece.text) || piece.text === "-" && mergedWordLike[mergedLen - 1])) {
|
|
1307
|
+
mergedTexts[mergedLen - 1] += piece.text;
|
|
1308
|
+
} else {
|
|
1309
|
+
mergedTexts[mergedLen] = piece.text;
|
|
1310
|
+
mergedWordLike[mergedLen] = piece.isWordLike;
|
|
1311
|
+
mergedKinds[mergedLen] = piece.kind;
|
|
1312
|
+
mergedStarts[mergedLen] = piece.start;
|
|
1313
|
+
mergedLen++;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
for (let i = 1;i < mergedLen; i++) {
|
|
1318
|
+
if (mergedKinds[i] === "text" && !mergedWordLike[i] && isEscapedQuoteClusterSegment(mergedTexts[i]) && mergedKinds[i - 1] === "text") {
|
|
1319
|
+
mergedTexts[i - 1] += mergedTexts[i];
|
|
1320
|
+
mergedWordLike[i - 1] = mergedWordLike[i - 1] || mergedWordLike[i];
|
|
1321
|
+
mergedTexts[i] = "";
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
for (let i = mergedLen - 2;i >= 0; i--) {
|
|
1325
|
+
if (mergedKinds[i] === "text" && !mergedWordLike[i] && isForwardStickyClusterSegment(mergedTexts[i])) {
|
|
1326
|
+
let j = i + 1;
|
|
1327
|
+
while (j < mergedLen && mergedTexts[j] === "")
|
|
1328
|
+
j++;
|
|
1329
|
+
if (j < mergedLen && mergedKinds[j] === "text") {
|
|
1330
|
+
mergedTexts[j] = mergedTexts[i] + mergedTexts[j];
|
|
1331
|
+
mergedStarts[j] = mergedStarts[i];
|
|
1332
|
+
mergedTexts[i] = "";
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
let compactLen = 0;
|
|
1337
|
+
for (let read = 0;read < mergedLen; read++) {
|
|
1338
|
+
const text = mergedTexts[read];
|
|
1339
|
+
if (text.length === 0)
|
|
1340
|
+
continue;
|
|
1341
|
+
if (compactLen !== read) {
|
|
1342
|
+
mergedTexts[compactLen] = text;
|
|
1343
|
+
mergedWordLike[compactLen] = mergedWordLike[read];
|
|
1344
|
+
mergedKinds[compactLen] = mergedKinds[read];
|
|
1345
|
+
mergedStarts[compactLen] = mergedStarts[read];
|
|
1346
|
+
}
|
|
1347
|
+
compactLen++;
|
|
1348
|
+
}
|
|
1349
|
+
mergedTexts.length = compactLen;
|
|
1350
|
+
mergedWordLike.length = compactLen;
|
|
1351
|
+
mergedKinds.length = compactLen;
|
|
1352
|
+
mergedStarts.length = compactLen;
|
|
1353
|
+
const compacted = mergeGlueConnectedTextRuns({
|
|
1354
|
+
len: compactLen,
|
|
1355
|
+
texts: mergedTexts,
|
|
1356
|
+
isWordLike: mergedWordLike,
|
|
1357
|
+
kinds: mergedKinds,
|
|
1358
|
+
starts: mergedStarts
|
|
1359
|
+
});
|
|
1360
|
+
const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary(mergeAsciiPunctuationChains(splitHyphenatedNumericRuns(mergeNumericRuns(mergeUrlQueryRuns(mergeUrlLikeRuns(compacted))))));
|
|
1361
|
+
for (let i = 0;i < withMergedUrls.len - 1; i++) {
|
|
1362
|
+
const split = splitLeadingSpaceAndMarks(withMergedUrls.texts[i]);
|
|
1363
|
+
if (split === null)
|
|
1364
|
+
continue;
|
|
1365
|
+
if (withMergedUrls.kinds[i] !== "space" && withMergedUrls.kinds[i] !== "preserved-space" || withMergedUrls.kinds[i + 1] !== "text" || !containsArabicScript(withMergedUrls.texts[i + 1])) {
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
withMergedUrls.texts[i] = split.space;
|
|
1369
|
+
withMergedUrls.isWordLike[i] = false;
|
|
1370
|
+
withMergedUrls.kinds[i] = withMergedUrls.kinds[i] === "preserved-space" ? "preserved-space" : "space";
|
|
1371
|
+
withMergedUrls.texts[i + 1] = split.marks + withMergedUrls.texts[i + 1];
|
|
1372
|
+
withMergedUrls.starts[i + 1] = withMergedUrls.starts[i] + split.space.length;
|
|
1373
|
+
}
|
|
1374
|
+
return withMergedUrls;
|
|
1375
|
+
}
|
|
1376
|
+
function compileAnalysisChunks(segmentation, whiteSpaceProfile) {
|
|
1377
|
+
if (segmentation.len === 0)
|
|
1378
|
+
return [];
|
|
1379
|
+
if (!whiteSpaceProfile.preserveHardBreaks) {
|
|
1380
|
+
return [
|
|
1381
|
+
{
|
|
1382
|
+
startSegmentIndex: 0,
|
|
1383
|
+
endSegmentIndex: segmentation.len,
|
|
1384
|
+
consumedEndSegmentIndex: segmentation.len
|
|
1385
|
+
}
|
|
1386
|
+
];
|
|
1387
|
+
}
|
|
1388
|
+
const chunks = [];
|
|
1389
|
+
let startSegmentIndex = 0;
|
|
1390
|
+
for (let i = 0;i < segmentation.len; i++) {
|
|
1391
|
+
if (segmentation.kinds[i] !== "hard-break")
|
|
1392
|
+
continue;
|
|
1393
|
+
chunks.push({
|
|
1394
|
+
startSegmentIndex,
|
|
1395
|
+
endSegmentIndex: i,
|
|
1396
|
+
consumedEndSegmentIndex: i + 1
|
|
1397
|
+
});
|
|
1398
|
+
startSegmentIndex = i + 1;
|
|
1399
|
+
}
|
|
1400
|
+
if (startSegmentIndex < segmentation.len) {
|
|
1401
|
+
chunks.push({
|
|
1402
|
+
startSegmentIndex,
|
|
1403
|
+
endSegmentIndex: segmentation.len,
|
|
1404
|
+
consumedEndSegmentIndex: segmentation.len
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
return chunks;
|
|
1408
|
+
}
|
|
1409
|
+
function analyzeText(text, profile, whiteSpace = "normal") {
|
|
1410
|
+
const whiteSpaceProfile = getWhiteSpaceProfile(whiteSpace);
|
|
1411
|
+
const normalized = whiteSpaceProfile.mode === "pre-wrap" ? normalizeWhitespacePreWrap(text) : normalizeWhitespaceNormal(text);
|
|
1412
|
+
if (normalized.length === 0) {
|
|
1413
|
+
return {
|
|
1414
|
+
normalized,
|
|
1415
|
+
chunks: [],
|
|
1416
|
+
len: 0,
|
|
1417
|
+
texts: [],
|
|
1418
|
+
isWordLike: [],
|
|
1419
|
+
kinds: [],
|
|
1420
|
+
starts: []
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
const segmentation = buildMergedSegmentation(normalized, profile, whiteSpaceProfile);
|
|
1424
|
+
return {
|
|
1425
|
+
normalized,
|
|
1426
|
+
chunks: compileAnalysisChunks(segmentation, whiteSpaceProfile),
|
|
1427
|
+
...segmentation
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// torph/src/vendor/pretext/measurement.ts
|
|
1432
|
+
var measureContext = null;
|
|
1433
|
+
var segmentMetricCaches = new Map;
|
|
1434
|
+
var cachedEngineProfile = null;
|
|
1435
|
+
var emojiPresentationRe = /\p{Emoji_Presentation}/u;
|
|
1436
|
+
var maybeEmojiRe = /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Regional_Indicator}\uFE0F\u20E3]/u;
|
|
1437
|
+
var sharedGraphemeSegmenter = null;
|
|
1438
|
+
var emojiCorrectionCache = new Map;
|
|
1439
|
+
function getMeasureContext() {
|
|
1440
|
+
if (measureContext !== null)
|
|
1441
|
+
return measureContext;
|
|
1442
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
1443
|
+
measureContext = new OffscreenCanvas(1, 1).getContext("2d");
|
|
1444
|
+
return measureContext;
|
|
1445
|
+
}
|
|
1446
|
+
if (typeof document !== "undefined") {
|
|
1447
|
+
measureContext = document.createElement("canvas").getContext("2d");
|
|
1448
|
+
return measureContext;
|
|
1449
|
+
}
|
|
1450
|
+
throw new Error("Text measurement requires OffscreenCanvas or a DOM canvas context.");
|
|
1451
|
+
}
|
|
1452
|
+
function getSegmentMetricCache(font) {
|
|
1453
|
+
let cache = segmentMetricCaches.get(font);
|
|
1454
|
+
if (!cache) {
|
|
1455
|
+
cache = new Map;
|
|
1456
|
+
segmentMetricCaches.set(font, cache);
|
|
1457
|
+
}
|
|
1458
|
+
return cache;
|
|
1459
|
+
}
|
|
1460
|
+
function getSegmentMetrics(seg, cache) {
|
|
1461
|
+
let metrics = cache.get(seg);
|
|
1462
|
+
if (metrics === undefined) {
|
|
1463
|
+
const ctx = getMeasureContext();
|
|
1464
|
+
metrics = {
|
|
1465
|
+
width: ctx.measureText(seg).width,
|
|
1466
|
+
containsCJK: isCJK(seg)
|
|
1467
|
+
};
|
|
1468
|
+
cache.set(seg, metrics);
|
|
1469
|
+
}
|
|
1470
|
+
return metrics;
|
|
1471
|
+
}
|
|
1472
|
+
function getEngineProfile() {
|
|
1473
|
+
if (cachedEngineProfile !== null)
|
|
1474
|
+
return cachedEngineProfile;
|
|
1475
|
+
if (typeof navigator === "undefined") {
|
|
1476
|
+
cachedEngineProfile = {
|
|
1477
|
+
lineFitEpsilon: 0.005,
|
|
1478
|
+
carryCJKAfterClosingQuote: false,
|
|
1479
|
+
preferPrefixWidthsForBreakableRuns: false,
|
|
1480
|
+
preferEarlySoftHyphenBreak: false
|
|
1481
|
+
};
|
|
1482
|
+
return cachedEngineProfile;
|
|
1483
|
+
}
|
|
1484
|
+
const ua = navigator.userAgent;
|
|
1485
|
+
const vendor = navigator.vendor;
|
|
1486
|
+
const isSafari = vendor === "Apple Computer, Inc." && ua.includes("Safari/") && !ua.includes("Chrome/") && !ua.includes("Chromium/") && !ua.includes("CriOS/") && !ua.includes("FxiOS/") && !ua.includes("EdgiOS/");
|
|
1487
|
+
const isChromium = ua.includes("Chrome/") || ua.includes("Chromium/") || ua.includes("CriOS/") || ua.includes("Edg/");
|
|
1488
|
+
cachedEngineProfile = {
|
|
1489
|
+
lineFitEpsilon: isSafari ? 1 / 64 : 0.005,
|
|
1490
|
+
carryCJKAfterClosingQuote: isChromium,
|
|
1491
|
+
preferPrefixWidthsForBreakableRuns: isSafari,
|
|
1492
|
+
preferEarlySoftHyphenBreak: isSafari
|
|
1493
|
+
};
|
|
1494
|
+
return cachedEngineProfile;
|
|
1495
|
+
}
|
|
1496
|
+
function parseFontSize(font) {
|
|
1497
|
+
const m = font.match(/(\d+(?:\.\d+)?)\s*px/);
|
|
1498
|
+
return m ? parseFloat(m[1]) : 16;
|
|
1499
|
+
}
|
|
1500
|
+
function getSharedGraphemeSegmenter() {
|
|
1501
|
+
if (sharedGraphemeSegmenter === null) {
|
|
1502
|
+
sharedGraphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
1503
|
+
}
|
|
1504
|
+
return sharedGraphemeSegmenter;
|
|
1505
|
+
}
|
|
1506
|
+
function isEmojiGrapheme(g) {
|
|
1507
|
+
return emojiPresentationRe.test(g) || g.includes("️");
|
|
1508
|
+
}
|
|
1509
|
+
function textMayContainEmoji(text) {
|
|
1510
|
+
return maybeEmojiRe.test(text);
|
|
1511
|
+
}
|
|
1512
|
+
function getEmojiCorrection(font, fontSize) {
|
|
1513
|
+
let correction = emojiCorrectionCache.get(font);
|
|
1514
|
+
if (correction !== undefined)
|
|
1515
|
+
return correction;
|
|
1516
|
+
const ctx = getMeasureContext();
|
|
1517
|
+
ctx.font = font;
|
|
1518
|
+
const canvasW = ctx.measureText("\uD83D\uDE00").width;
|
|
1519
|
+
correction = 0;
|
|
1520
|
+
if (canvasW > fontSize + 0.5 && typeof document !== "undefined" && document.body !== null) {
|
|
1521
|
+
const span = document.createElement("span");
|
|
1522
|
+
span.style.font = font;
|
|
1523
|
+
span.style.display = "inline-block";
|
|
1524
|
+
span.style.visibility = "hidden";
|
|
1525
|
+
span.style.position = "absolute";
|
|
1526
|
+
span.textContent = "\uD83D\uDE00";
|
|
1527
|
+
document.body.appendChild(span);
|
|
1528
|
+
const domW = span.getBoundingClientRect().width;
|
|
1529
|
+
document.body.removeChild(span);
|
|
1530
|
+
if (canvasW - domW > 0.5) {
|
|
1531
|
+
correction = canvasW - domW;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
emojiCorrectionCache.set(font, correction);
|
|
1535
|
+
return correction;
|
|
1536
|
+
}
|
|
1537
|
+
function countEmojiGraphemes(text) {
|
|
1538
|
+
let count = 0;
|
|
1539
|
+
const graphemeSegmenter = getSharedGraphemeSegmenter();
|
|
1540
|
+
for (const g of graphemeSegmenter.segment(text)) {
|
|
1541
|
+
if (isEmojiGrapheme(g.segment))
|
|
1542
|
+
count++;
|
|
1543
|
+
}
|
|
1544
|
+
return count;
|
|
1545
|
+
}
|
|
1546
|
+
function getEmojiCount(seg, metrics) {
|
|
1547
|
+
if (metrics.emojiCount === undefined) {
|
|
1548
|
+
metrics.emojiCount = countEmojiGraphemes(seg);
|
|
1549
|
+
}
|
|
1550
|
+
return metrics.emojiCount;
|
|
1551
|
+
}
|
|
1552
|
+
function getCorrectedSegmentWidth(seg, metrics, emojiCorrection) {
|
|
1553
|
+
if (emojiCorrection === 0)
|
|
1554
|
+
return metrics.width;
|
|
1555
|
+
return metrics.width - getEmojiCount(seg, metrics) * emojiCorrection;
|
|
1556
|
+
}
|
|
1557
|
+
function getSegmentGraphemeWidths(seg, metrics, cache, emojiCorrection) {
|
|
1558
|
+
if (metrics.graphemeWidths !== undefined)
|
|
1559
|
+
return metrics.graphemeWidths;
|
|
1560
|
+
const widths = [];
|
|
1561
|
+
const graphemeSegmenter = getSharedGraphemeSegmenter();
|
|
1562
|
+
for (const gs of graphemeSegmenter.segment(seg)) {
|
|
1563
|
+
const graphemeMetrics = getSegmentMetrics(gs.segment, cache);
|
|
1564
|
+
widths.push(getCorrectedSegmentWidth(gs.segment, graphemeMetrics, emojiCorrection));
|
|
1565
|
+
}
|
|
1566
|
+
metrics.graphemeWidths = widths.length > 1 ? widths : null;
|
|
1567
|
+
return metrics.graphemeWidths;
|
|
1568
|
+
}
|
|
1569
|
+
function getSegmentGraphemePrefixWidths(seg, metrics, cache, emojiCorrection) {
|
|
1570
|
+
if (metrics.graphemePrefixWidths !== undefined)
|
|
1571
|
+
return metrics.graphemePrefixWidths;
|
|
1572
|
+
const prefixWidths = [];
|
|
1573
|
+
const graphemeSegmenter = getSharedGraphemeSegmenter();
|
|
1574
|
+
let prefix = "";
|
|
1575
|
+
for (const gs of graphemeSegmenter.segment(seg)) {
|
|
1576
|
+
prefix += gs.segment;
|
|
1577
|
+
const prefixMetrics = getSegmentMetrics(prefix, cache);
|
|
1578
|
+
prefixWidths.push(getCorrectedSegmentWidth(prefix, prefixMetrics, emojiCorrection));
|
|
1579
|
+
}
|
|
1580
|
+
metrics.graphemePrefixWidths = prefixWidths.length > 1 ? prefixWidths : null;
|
|
1581
|
+
return metrics.graphemePrefixWidths;
|
|
1582
|
+
}
|
|
1583
|
+
function getFontMeasurementState(font, needsEmojiCorrection) {
|
|
1584
|
+
const ctx = getMeasureContext();
|
|
1585
|
+
ctx.font = font;
|
|
1586
|
+
const cache = getSegmentMetricCache(font);
|
|
1587
|
+
const fontSize = parseFontSize(font);
|
|
1588
|
+
const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection(font, fontSize) : 0;
|
|
1589
|
+
return { cache, fontSize, emojiCorrection };
|
|
1590
|
+
}
|
|
1591
|
+
function clearMeasurementCaches() {
|
|
1592
|
+
segmentMetricCaches.clear();
|
|
1593
|
+
emojiCorrectionCache.clear();
|
|
1594
|
+
sharedGraphemeSegmenter = null;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// torph/src/vendor/pretext/line-break.ts
|
|
1598
|
+
function canBreakAfter(kind) {
|
|
1599
|
+
return kind === "space" || kind === "preserved-space" || kind === "tab" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
1600
|
+
}
|
|
1601
|
+
function getTabAdvance(lineWidth, tabStopAdvance) {
|
|
1602
|
+
if (tabStopAdvance <= 0)
|
|
1603
|
+
return 0;
|
|
1604
|
+
const remainder = lineWidth % tabStopAdvance;
|
|
1605
|
+
if (Math.abs(remainder) <= 0.000001)
|
|
1606
|
+
return tabStopAdvance;
|
|
1607
|
+
return tabStopAdvance - remainder;
|
|
1608
|
+
}
|
|
1609
|
+
function getBreakableAdvance(graphemeWidths, graphemePrefixWidths, graphemeIndex, preferPrefixWidths) {
|
|
1610
|
+
if (!preferPrefixWidths || graphemePrefixWidths === null) {
|
|
1611
|
+
return graphemeWidths[graphemeIndex];
|
|
1612
|
+
}
|
|
1613
|
+
return graphemePrefixWidths[graphemeIndex] - (graphemeIndex > 0 ? graphemePrefixWidths[graphemeIndex - 1] : 0);
|
|
1614
|
+
}
|
|
1615
|
+
function fitSoftHyphenBreak(graphemeWidths, initialWidth, maxWidth, lineFitEpsilon, discretionaryHyphenWidth, cumulativeWidths) {
|
|
1616
|
+
let fitCount = 0;
|
|
1617
|
+
let fittedWidth = initialWidth;
|
|
1618
|
+
while (fitCount < graphemeWidths.length) {
|
|
1619
|
+
const nextWidth = cumulativeWidths ? initialWidth + graphemeWidths[fitCount] : fittedWidth + graphemeWidths[fitCount];
|
|
1620
|
+
const nextLineWidth = fitCount + 1 < graphemeWidths.length ? nextWidth + discretionaryHyphenWidth : nextWidth;
|
|
1621
|
+
if (nextLineWidth > maxWidth + lineFitEpsilon)
|
|
1622
|
+
break;
|
|
1623
|
+
fittedWidth = nextWidth;
|
|
1624
|
+
fitCount++;
|
|
1625
|
+
}
|
|
1626
|
+
return { fitCount, fittedWidth };
|
|
1627
|
+
}
|
|
1628
|
+
function walkPreparedLinesSimple(prepared, maxWidth, onLine) {
|
|
1629
|
+
const { widths, kinds, breakableWidths, breakablePrefixWidths } = prepared;
|
|
1630
|
+
if (widths.length === 0)
|
|
1631
|
+
return 0;
|
|
1632
|
+
const engineProfile = getEngineProfile();
|
|
1633
|
+
const lineFitEpsilon = engineProfile.lineFitEpsilon;
|
|
1634
|
+
let lineCount = 0;
|
|
1635
|
+
let lineW = 0;
|
|
1636
|
+
let hasContent = false;
|
|
1637
|
+
let lineStartSegmentIndex = 0;
|
|
1638
|
+
let lineStartGraphemeIndex = 0;
|
|
1639
|
+
let lineEndSegmentIndex = 0;
|
|
1640
|
+
let lineEndGraphemeIndex = 0;
|
|
1641
|
+
let pendingBreakSegmentIndex = -1;
|
|
1642
|
+
let pendingBreakPaintWidth = 0;
|
|
1643
|
+
function clearPendingBreak() {
|
|
1644
|
+
pendingBreakSegmentIndex = -1;
|
|
1645
|
+
pendingBreakPaintWidth = 0;
|
|
1646
|
+
}
|
|
1647
|
+
function emitCurrentLine(endSegmentIndex = lineEndSegmentIndex, endGraphemeIndex = lineEndGraphemeIndex, width = lineW) {
|
|
1648
|
+
lineCount++;
|
|
1649
|
+
onLine?.({
|
|
1650
|
+
startSegmentIndex: lineStartSegmentIndex,
|
|
1651
|
+
startGraphemeIndex: lineStartGraphemeIndex,
|
|
1652
|
+
endSegmentIndex,
|
|
1653
|
+
endGraphemeIndex,
|
|
1654
|
+
width
|
|
1655
|
+
});
|
|
1656
|
+
lineW = 0;
|
|
1657
|
+
hasContent = false;
|
|
1658
|
+
clearPendingBreak();
|
|
1659
|
+
}
|
|
1660
|
+
function startLineAtSegment(segmentIndex, width) {
|
|
1661
|
+
hasContent = true;
|
|
1662
|
+
lineStartSegmentIndex = segmentIndex;
|
|
1663
|
+
lineStartGraphemeIndex = 0;
|
|
1664
|
+
lineEndSegmentIndex = segmentIndex + 1;
|
|
1665
|
+
lineEndGraphemeIndex = 0;
|
|
1666
|
+
lineW = width;
|
|
1667
|
+
}
|
|
1668
|
+
function startLineAtGrapheme(segmentIndex, graphemeIndex, width) {
|
|
1669
|
+
hasContent = true;
|
|
1670
|
+
lineStartSegmentIndex = segmentIndex;
|
|
1671
|
+
lineStartGraphemeIndex = graphemeIndex;
|
|
1672
|
+
lineEndSegmentIndex = segmentIndex;
|
|
1673
|
+
lineEndGraphemeIndex = graphemeIndex + 1;
|
|
1674
|
+
lineW = width;
|
|
1675
|
+
}
|
|
1676
|
+
function appendWholeSegment(segmentIndex, width) {
|
|
1677
|
+
if (!hasContent) {
|
|
1678
|
+
startLineAtSegment(segmentIndex, width);
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
lineW += width;
|
|
1682
|
+
lineEndSegmentIndex = segmentIndex + 1;
|
|
1683
|
+
lineEndGraphemeIndex = 0;
|
|
1684
|
+
}
|
|
1685
|
+
function updatePendingBreak(segmentIndex, segmentWidth) {
|
|
1686
|
+
if (!canBreakAfter(kinds[segmentIndex]))
|
|
1687
|
+
return;
|
|
1688
|
+
pendingBreakSegmentIndex = segmentIndex + 1;
|
|
1689
|
+
pendingBreakPaintWidth = lineW - segmentWidth;
|
|
1690
|
+
}
|
|
1691
|
+
function appendBreakableSegment(segmentIndex) {
|
|
1692
|
+
appendBreakableSegmentFrom(segmentIndex, 0);
|
|
1693
|
+
}
|
|
1694
|
+
function appendBreakableSegmentFrom(segmentIndex, startGraphemeIndex) {
|
|
1695
|
+
const gWidths = breakableWidths[segmentIndex];
|
|
1696
|
+
const gPrefixWidths = breakablePrefixWidths[segmentIndex] ?? null;
|
|
1697
|
+
for (let g = startGraphemeIndex;g < gWidths.length; g++) {
|
|
1698
|
+
const gw = getBreakableAdvance(gWidths, gPrefixWidths, g, engineProfile.preferPrefixWidthsForBreakableRuns);
|
|
1699
|
+
if (!hasContent) {
|
|
1700
|
+
startLineAtGrapheme(segmentIndex, g, gw);
|
|
1701
|
+
continue;
|
|
1702
|
+
}
|
|
1703
|
+
if (lineW + gw > maxWidth + lineFitEpsilon) {
|
|
1704
|
+
emitCurrentLine();
|
|
1705
|
+
startLineAtGrapheme(segmentIndex, g, gw);
|
|
1706
|
+
} else {
|
|
1707
|
+
lineW += gw;
|
|
1708
|
+
lineEndSegmentIndex = segmentIndex;
|
|
1709
|
+
lineEndGraphemeIndex = g + 1;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
if (hasContent && lineEndSegmentIndex === segmentIndex && lineEndGraphemeIndex === gWidths.length) {
|
|
1713
|
+
lineEndSegmentIndex = segmentIndex + 1;
|
|
1714
|
+
lineEndGraphemeIndex = 0;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
let i = 0;
|
|
1718
|
+
while (i < widths.length) {
|
|
1719
|
+
const w = widths[i];
|
|
1720
|
+
const kind = kinds[i];
|
|
1721
|
+
if (!hasContent) {
|
|
1722
|
+
if (w > maxWidth && breakableWidths[i] !== null) {
|
|
1723
|
+
appendBreakableSegment(i);
|
|
1724
|
+
} else {
|
|
1725
|
+
startLineAtSegment(i, w);
|
|
1726
|
+
}
|
|
1727
|
+
updatePendingBreak(i, w);
|
|
1728
|
+
i++;
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
const newW = lineW + w;
|
|
1732
|
+
if (newW > maxWidth + lineFitEpsilon) {
|
|
1733
|
+
if (canBreakAfter(kind)) {
|
|
1734
|
+
appendWholeSegment(i, w);
|
|
1735
|
+
emitCurrentLine(i + 1, 0, lineW - w);
|
|
1736
|
+
i++;
|
|
1737
|
+
continue;
|
|
1738
|
+
}
|
|
1739
|
+
if (pendingBreakSegmentIndex >= 0) {
|
|
1740
|
+
if (lineEndSegmentIndex > pendingBreakSegmentIndex || lineEndSegmentIndex === pendingBreakSegmentIndex && lineEndGraphemeIndex > 0) {
|
|
1741
|
+
emitCurrentLine();
|
|
1742
|
+
continue;
|
|
1743
|
+
}
|
|
1744
|
+
emitCurrentLine(pendingBreakSegmentIndex, 0, pendingBreakPaintWidth);
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1747
|
+
if (w > maxWidth && breakableWidths[i] !== null) {
|
|
1748
|
+
emitCurrentLine();
|
|
1749
|
+
appendBreakableSegment(i);
|
|
1750
|
+
i++;
|
|
1751
|
+
continue;
|
|
1752
|
+
}
|
|
1753
|
+
emitCurrentLine();
|
|
1754
|
+
continue;
|
|
1755
|
+
}
|
|
1756
|
+
appendWholeSegment(i, w);
|
|
1757
|
+
updatePendingBreak(i, w);
|
|
1758
|
+
i++;
|
|
1759
|
+
}
|
|
1760
|
+
if (hasContent)
|
|
1761
|
+
emitCurrentLine();
|
|
1762
|
+
return lineCount;
|
|
1763
|
+
}
|
|
1764
|
+
function walkPreparedLines(prepared, maxWidth, onLine) {
|
|
1765
|
+
if (prepared.simpleLineWalkFastPath) {
|
|
1766
|
+
return walkPreparedLinesSimple(prepared, maxWidth, onLine);
|
|
1767
|
+
}
|
|
1768
|
+
const {
|
|
1769
|
+
widths,
|
|
1770
|
+
lineEndFitAdvances,
|
|
1771
|
+
lineEndPaintAdvances,
|
|
1772
|
+
kinds,
|
|
1773
|
+
breakableWidths,
|
|
1774
|
+
breakablePrefixWidths,
|
|
1775
|
+
discretionaryHyphenWidth,
|
|
1776
|
+
tabStopAdvance,
|
|
1777
|
+
chunks
|
|
1778
|
+
} = prepared;
|
|
1779
|
+
if (widths.length === 0 || chunks.length === 0)
|
|
1780
|
+
return 0;
|
|
1781
|
+
const engineProfile = getEngineProfile();
|
|
1782
|
+
const lineFitEpsilon = engineProfile.lineFitEpsilon;
|
|
1783
|
+
let lineCount = 0;
|
|
1784
|
+
let lineW = 0;
|
|
1785
|
+
let hasContent = false;
|
|
1786
|
+
let lineStartSegmentIndex = 0;
|
|
1787
|
+
let lineStartGraphemeIndex = 0;
|
|
1788
|
+
let lineEndSegmentIndex = 0;
|
|
1789
|
+
let lineEndGraphemeIndex = 0;
|
|
1790
|
+
let pendingBreakSegmentIndex = -1;
|
|
1791
|
+
let pendingBreakFitWidth = 0;
|
|
1792
|
+
let pendingBreakPaintWidth = 0;
|
|
1793
|
+
let pendingBreakKind = null;
|
|
1794
|
+
function clearPendingBreak() {
|
|
1795
|
+
pendingBreakSegmentIndex = -1;
|
|
1796
|
+
pendingBreakFitWidth = 0;
|
|
1797
|
+
pendingBreakPaintWidth = 0;
|
|
1798
|
+
pendingBreakKind = null;
|
|
1799
|
+
}
|
|
1800
|
+
function emitCurrentLine(endSegmentIndex = lineEndSegmentIndex, endGraphemeIndex = lineEndGraphemeIndex, width = lineW) {
|
|
1801
|
+
lineCount++;
|
|
1802
|
+
onLine?.({
|
|
1803
|
+
startSegmentIndex: lineStartSegmentIndex,
|
|
1804
|
+
startGraphemeIndex: lineStartGraphemeIndex,
|
|
1805
|
+
endSegmentIndex,
|
|
1806
|
+
endGraphemeIndex,
|
|
1807
|
+
width
|
|
1808
|
+
});
|
|
1809
|
+
lineW = 0;
|
|
1810
|
+
hasContent = false;
|
|
1811
|
+
clearPendingBreak();
|
|
1812
|
+
}
|
|
1813
|
+
function startLineAtSegment(segmentIndex, width) {
|
|
1814
|
+
hasContent = true;
|
|
1815
|
+
lineStartSegmentIndex = segmentIndex;
|
|
1816
|
+
lineStartGraphemeIndex = 0;
|
|
1817
|
+
lineEndSegmentIndex = segmentIndex + 1;
|
|
1818
|
+
lineEndGraphemeIndex = 0;
|
|
1819
|
+
lineW = width;
|
|
1820
|
+
}
|
|
1821
|
+
function startLineAtGrapheme(segmentIndex, graphemeIndex, width) {
|
|
1822
|
+
hasContent = true;
|
|
1823
|
+
lineStartSegmentIndex = segmentIndex;
|
|
1824
|
+
lineStartGraphemeIndex = graphemeIndex;
|
|
1825
|
+
lineEndSegmentIndex = segmentIndex;
|
|
1826
|
+
lineEndGraphemeIndex = graphemeIndex + 1;
|
|
1827
|
+
lineW = width;
|
|
1828
|
+
}
|
|
1829
|
+
function appendWholeSegment(segmentIndex, width) {
|
|
1830
|
+
if (!hasContent) {
|
|
1831
|
+
startLineAtSegment(segmentIndex, width);
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
lineW += width;
|
|
1835
|
+
lineEndSegmentIndex = segmentIndex + 1;
|
|
1836
|
+
lineEndGraphemeIndex = 0;
|
|
1837
|
+
}
|
|
1838
|
+
function updatePendingBreakForWholeSegment(segmentIndex, segmentWidth) {
|
|
1839
|
+
if (!canBreakAfter(kinds[segmentIndex]))
|
|
1840
|
+
return;
|
|
1841
|
+
const fitAdvance = kinds[segmentIndex] === "tab" ? 0 : lineEndFitAdvances[segmentIndex];
|
|
1842
|
+
const paintAdvance = kinds[segmentIndex] === "tab" ? segmentWidth : lineEndPaintAdvances[segmentIndex];
|
|
1843
|
+
pendingBreakSegmentIndex = segmentIndex + 1;
|
|
1844
|
+
pendingBreakFitWidth = lineW - segmentWidth + fitAdvance;
|
|
1845
|
+
pendingBreakPaintWidth = lineW - segmentWidth + paintAdvance;
|
|
1846
|
+
pendingBreakKind = kinds[segmentIndex];
|
|
1847
|
+
}
|
|
1848
|
+
function appendBreakableSegment(segmentIndex) {
|
|
1849
|
+
appendBreakableSegmentFrom(segmentIndex, 0);
|
|
1850
|
+
}
|
|
1851
|
+
function appendBreakableSegmentFrom(segmentIndex, startGraphemeIndex) {
|
|
1852
|
+
const gWidths = breakableWidths[segmentIndex];
|
|
1853
|
+
const gPrefixWidths = breakablePrefixWidths[segmentIndex] ?? null;
|
|
1854
|
+
for (let g = startGraphemeIndex;g < gWidths.length; g++) {
|
|
1855
|
+
const gw = getBreakableAdvance(gWidths, gPrefixWidths, g, engineProfile.preferPrefixWidthsForBreakableRuns);
|
|
1856
|
+
if (!hasContent) {
|
|
1857
|
+
startLineAtGrapheme(segmentIndex, g, gw);
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
if (lineW + gw > maxWidth + lineFitEpsilon) {
|
|
1861
|
+
emitCurrentLine();
|
|
1862
|
+
startLineAtGrapheme(segmentIndex, g, gw);
|
|
1863
|
+
} else {
|
|
1864
|
+
lineW += gw;
|
|
1865
|
+
lineEndSegmentIndex = segmentIndex;
|
|
1866
|
+
lineEndGraphemeIndex = g + 1;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
if (hasContent && lineEndSegmentIndex === segmentIndex && lineEndGraphemeIndex === gWidths.length) {
|
|
1870
|
+
lineEndSegmentIndex = segmentIndex + 1;
|
|
1871
|
+
lineEndGraphemeIndex = 0;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
function continueSoftHyphenBreakableSegment(segmentIndex) {
|
|
1875
|
+
if (pendingBreakKind !== "soft-hyphen")
|
|
1876
|
+
return false;
|
|
1877
|
+
const gWidths = breakableWidths[segmentIndex];
|
|
1878
|
+
if (gWidths === null)
|
|
1879
|
+
return false;
|
|
1880
|
+
const fitWidths = engineProfile.preferPrefixWidthsForBreakableRuns ? breakablePrefixWidths[segmentIndex] ?? gWidths : gWidths;
|
|
1881
|
+
const usesPrefixWidths = fitWidths !== gWidths;
|
|
1882
|
+
const { fitCount, fittedWidth } = fitSoftHyphenBreak(fitWidths, lineW, maxWidth, lineFitEpsilon, discretionaryHyphenWidth, usesPrefixWidths);
|
|
1883
|
+
if (fitCount === 0)
|
|
1884
|
+
return false;
|
|
1885
|
+
lineW = fittedWidth;
|
|
1886
|
+
lineEndSegmentIndex = segmentIndex;
|
|
1887
|
+
lineEndGraphemeIndex = fitCount;
|
|
1888
|
+
clearPendingBreak();
|
|
1889
|
+
if (fitCount === gWidths.length) {
|
|
1890
|
+
lineEndSegmentIndex = segmentIndex + 1;
|
|
1891
|
+
lineEndGraphemeIndex = 0;
|
|
1892
|
+
return true;
|
|
1893
|
+
}
|
|
1894
|
+
emitCurrentLine(segmentIndex, fitCount, fittedWidth + discretionaryHyphenWidth);
|
|
1895
|
+
appendBreakableSegmentFrom(segmentIndex, fitCount);
|
|
1896
|
+
return true;
|
|
1897
|
+
}
|
|
1898
|
+
function emitEmptyChunk(chunk) {
|
|
1899
|
+
lineCount++;
|
|
1900
|
+
onLine?.({
|
|
1901
|
+
startSegmentIndex: chunk.startSegmentIndex,
|
|
1902
|
+
startGraphemeIndex: 0,
|
|
1903
|
+
endSegmentIndex: chunk.consumedEndSegmentIndex,
|
|
1904
|
+
endGraphemeIndex: 0,
|
|
1905
|
+
width: 0
|
|
1906
|
+
});
|
|
1907
|
+
clearPendingBreak();
|
|
1908
|
+
}
|
|
1909
|
+
for (let chunkIndex = 0;chunkIndex < chunks.length; chunkIndex++) {
|
|
1910
|
+
const chunk = chunks[chunkIndex];
|
|
1911
|
+
if (chunk.startSegmentIndex === chunk.endSegmentIndex) {
|
|
1912
|
+
emitEmptyChunk(chunk);
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
hasContent = false;
|
|
1916
|
+
lineW = 0;
|
|
1917
|
+
lineStartSegmentIndex = chunk.startSegmentIndex;
|
|
1918
|
+
lineStartGraphemeIndex = 0;
|
|
1919
|
+
lineEndSegmentIndex = chunk.startSegmentIndex;
|
|
1920
|
+
lineEndGraphemeIndex = 0;
|
|
1921
|
+
clearPendingBreak();
|
|
1922
|
+
let i = chunk.startSegmentIndex;
|
|
1923
|
+
while (i < chunk.endSegmentIndex) {
|
|
1924
|
+
const kind = kinds[i];
|
|
1925
|
+
const w = kind === "tab" ? getTabAdvance(lineW, tabStopAdvance) : widths[i];
|
|
1926
|
+
if (kind === "soft-hyphen") {
|
|
1927
|
+
if (hasContent) {
|
|
1928
|
+
lineEndSegmentIndex = i + 1;
|
|
1929
|
+
lineEndGraphemeIndex = 0;
|
|
1930
|
+
pendingBreakSegmentIndex = i + 1;
|
|
1931
|
+
pendingBreakFitWidth = lineW + discretionaryHyphenWidth;
|
|
1932
|
+
pendingBreakPaintWidth = lineW + discretionaryHyphenWidth;
|
|
1933
|
+
pendingBreakKind = kind;
|
|
1934
|
+
}
|
|
1935
|
+
i++;
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
if (!hasContent) {
|
|
1939
|
+
if (w > maxWidth && breakableWidths[i] !== null) {
|
|
1940
|
+
appendBreakableSegment(i);
|
|
1941
|
+
} else {
|
|
1942
|
+
startLineAtSegment(i, w);
|
|
1943
|
+
}
|
|
1944
|
+
updatePendingBreakForWholeSegment(i, w);
|
|
1945
|
+
i++;
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
const newW = lineW + w;
|
|
1949
|
+
if (newW > maxWidth + lineFitEpsilon) {
|
|
1950
|
+
const currentBreakFitWidth = lineW + (kind === "tab" ? 0 : lineEndFitAdvances[i]);
|
|
1951
|
+
const currentBreakPaintWidth = lineW + (kind === "tab" ? w : lineEndPaintAdvances[i]);
|
|
1952
|
+
if (pendingBreakKind === "soft-hyphen" && engineProfile.preferEarlySoftHyphenBreak && pendingBreakFitWidth <= maxWidth + lineFitEpsilon) {
|
|
1953
|
+
emitCurrentLine(pendingBreakSegmentIndex, 0, pendingBreakPaintWidth);
|
|
1954
|
+
continue;
|
|
1955
|
+
}
|
|
1956
|
+
if (pendingBreakKind === "soft-hyphen" && continueSoftHyphenBreakableSegment(i)) {
|
|
1957
|
+
i++;
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
if (canBreakAfter(kind) && currentBreakFitWidth <= maxWidth + lineFitEpsilon) {
|
|
1961
|
+
appendWholeSegment(i, w);
|
|
1962
|
+
emitCurrentLine(i + 1, 0, currentBreakPaintWidth);
|
|
1963
|
+
i++;
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
if (pendingBreakSegmentIndex >= 0 && pendingBreakFitWidth <= maxWidth + lineFitEpsilon) {
|
|
1967
|
+
if (lineEndSegmentIndex > pendingBreakSegmentIndex || lineEndSegmentIndex === pendingBreakSegmentIndex && lineEndGraphemeIndex > 0) {
|
|
1968
|
+
emitCurrentLine();
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
const nextSegmentIndex = pendingBreakSegmentIndex;
|
|
1972
|
+
emitCurrentLine(nextSegmentIndex, 0, pendingBreakPaintWidth);
|
|
1973
|
+
i = nextSegmentIndex;
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
if (w > maxWidth && breakableWidths[i] !== null) {
|
|
1977
|
+
emitCurrentLine();
|
|
1978
|
+
appendBreakableSegment(i);
|
|
1979
|
+
i++;
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
emitCurrentLine();
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
appendWholeSegment(i, w);
|
|
1986
|
+
updatePendingBreakForWholeSegment(i, w);
|
|
1987
|
+
i++;
|
|
1988
|
+
}
|
|
1989
|
+
if (hasContent) {
|
|
1990
|
+
const finalPaintWidth = pendingBreakSegmentIndex === chunk.consumedEndSegmentIndex ? pendingBreakPaintWidth : lineW;
|
|
1991
|
+
emitCurrentLine(chunk.consumedEndSegmentIndex, 0, finalPaintWidth);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
return lineCount;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// torph/src/vendor/pretext/layout.ts
|
|
1998
|
+
var sharedGraphemeSegmenter2 = null;
|
|
1999
|
+
var sharedLineTextCaches = new WeakMap;
|
|
2000
|
+
function getSharedGraphemeSegmenter2() {
|
|
2001
|
+
if (sharedGraphemeSegmenter2 === null) {
|
|
2002
|
+
sharedGraphemeSegmenter2 = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
2003
|
+
}
|
|
2004
|
+
return sharedGraphemeSegmenter2;
|
|
2005
|
+
}
|
|
2006
|
+
function createEmptyPrepared(includeSegments) {
|
|
2007
|
+
if (includeSegments) {
|
|
2008
|
+
return {
|
|
2009
|
+
widths: [],
|
|
2010
|
+
lineEndFitAdvances: [],
|
|
2011
|
+
lineEndPaintAdvances: [],
|
|
2012
|
+
kinds: [],
|
|
2013
|
+
simpleLineWalkFastPath: true,
|
|
2014
|
+
segLevels: null,
|
|
2015
|
+
breakableWidths: [],
|
|
2016
|
+
breakablePrefixWidths: [],
|
|
2017
|
+
discretionaryHyphenWidth: 0,
|
|
2018
|
+
tabStopAdvance: 0,
|
|
2019
|
+
chunks: [],
|
|
2020
|
+
segments: []
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
return {
|
|
2024
|
+
widths: [],
|
|
2025
|
+
lineEndFitAdvances: [],
|
|
2026
|
+
lineEndPaintAdvances: [],
|
|
2027
|
+
kinds: [],
|
|
2028
|
+
simpleLineWalkFastPath: true,
|
|
2029
|
+
segLevels: null,
|
|
2030
|
+
breakableWidths: [],
|
|
2031
|
+
breakablePrefixWidths: [],
|
|
2032
|
+
discretionaryHyphenWidth: 0,
|
|
2033
|
+
tabStopAdvance: 0,
|
|
2034
|
+
chunks: []
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
function measureAnalysis(analysis, font, includeSegments) {
|
|
2038
|
+
const graphemeSegmenter = getSharedGraphemeSegmenter2();
|
|
2039
|
+
const engineProfile = getEngineProfile();
|
|
2040
|
+
const { cache, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
|
|
2041
|
+
const discretionaryHyphenWidth = getCorrectedSegmentWidth("-", getSegmentMetrics("-", cache), emojiCorrection);
|
|
2042
|
+
const spaceWidth = getCorrectedSegmentWidth(" ", getSegmentMetrics(" ", cache), emojiCorrection);
|
|
2043
|
+
const tabStopAdvance = spaceWidth * 8;
|
|
2044
|
+
if (analysis.len === 0)
|
|
2045
|
+
return createEmptyPrepared(includeSegments);
|
|
2046
|
+
const widths = [];
|
|
2047
|
+
const lineEndFitAdvances = [];
|
|
2048
|
+
const lineEndPaintAdvances = [];
|
|
2049
|
+
const kinds = [];
|
|
2050
|
+
let simpleLineWalkFastPath = analysis.chunks.length <= 1;
|
|
2051
|
+
const segStarts = includeSegments ? [] : null;
|
|
2052
|
+
const breakableWidths = [];
|
|
2053
|
+
const breakablePrefixWidths = [];
|
|
2054
|
+
const segments = includeSegments ? [] : null;
|
|
2055
|
+
const preparedStartByAnalysisIndex = Array.from({ length: analysis.len });
|
|
2056
|
+
const preparedEndByAnalysisIndex = Array.from({ length: analysis.len });
|
|
2057
|
+
function pushMeasuredSegment(text, width, lineEndFitAdvance, lineEndPaintAdvance, kind, start, breakable, breakablePrefix) {
|
|
2058
|
+
if (kind !== "text" && kind !== "space" && kind !== "zero-width-break") {
|
|
2059
|
+
simpleLineWalkFastPath = false;
|
|
2060
|
+
}
|
|
2061
|
+
widths.push(width);
|
|
2062
|
+
lineEndFitAdvances.push(lineEndFitAdvance);
|
|
2063
|
+
lineEndPaintAdvances.push(lineEndPaintAdvance);
|
|
2064
|
+
kinds.push(kind);
|
|
2065
|
+
segStarts?.push(start);
|
|
2066
|
+
breakableWidths.push(breakable);
|
|
2067
|
+
breakablePrefixWidths.push(breakablePrefix);
|
|
2068
|
+
if (segments !== null)
|
|
2069
|
+
segments.push(text);
|
|
2070
|
+
}
|
|
2071
|
+
for (let mi = 0;mi < analysis.len; mi++) {
|
|
2072
|
+
preparedStartByAnalysisIndex[mi] = widths.length;
|
|
2073
|
+
const segText = analysis.texts[mi];
|
|
2074
|
+
const segWordLike = analysis.isWordLike[mi];
|
|
2075
|
+
const segKind = analysis.kinds[mi];
|
|
2076
|
+
const segStart = analysis.starts[mi];
|
|
2077
|
+
if (segKind === "soft-hyphen") {
|
|
2078
|
+
pushMeasuredSegment(segText, 0, discretionaryHyphenWidth, discretionaryHyphenWidth, segKind, segStart, null, null);
|
|
2079
|
+
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
2080
|
+
continue;
|
|
2081
|
+
}
|
|
2082
|
+
if (segKind === "hard-break") {
|
|
2083
|
+
pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
|
|
2084
|
+
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
if (segKind === "tab") {
|
|
2088
|
+
pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
|
|
2089
|
+
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
2090
|
+
continue;
|
|
2091
|
+
}
|
|
2092
|
+
const segMetrics = getSegmentMetrics(segText, cache);
|
|
2093
|
+
if (segKind === "text" && segMetrics.containsCJK) {
|
|
2094
|
+
let unitText = "";
|
|
2095
|
+
let unitStart = 0;
|
|
2096
|
+
for (const gs of graphemeSegmenter.segment(segText)) {
|
|
2097
|
+
const grapheme = gs.segment;
|
|
2098
|
+
if (unitText.length === 0) {
|
|
2099
|
+
unitText = grapheme;
|
|
2100
|
+
unitStart = gs.index;
|
|
2101
|
+
continue;
|
|
2102
|
+
}
|
|
2103
|
+
if (kinsokuEnd.has(unitText) || kinsokuStart.has(grapheme) || leftStickyPunctuation.has(grapheme) || engineProfile.carryCJKAfterClosingQuote && isCJK(grapheme) && endsWithClosingQuote(unitText)) {
|
|
2104
|
+
unitText += grapheme;
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
2107
|
+
const unitMetrics = getSegmentMetrics(unitText, cache);
|
|
2108
|
+
const w2 = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
|
|
2109
|
+
pushMeasuredSegment(unitText, w2, w2, w2, "text", segStart + unitStart, null, null);
|
|
2110
|
+
unitText = grapheme;
|
|
2111
|
+
unitStart = gs.index;
|
|
2112
|
+
}
|
|
2113
|
+
if (unitText.length > 0) {
|
|
2114
|
+
const unitMetrics = getSegmentMetrics(unitText, cache);
|
|
2115
|
+
const w2 = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
|
|
2116
|
+
pushMeasuredSegment(unitText, w2, w2, w2, "text", segStart + unitStart, null, null);
|
|
2117
|
+
}
|
|
2118
|
+
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
2119
|
+
continue;
|
|
2120
|
+
}
|
|
2121
|
+
const w = getCorrectedSegmentWidth(segText, segMetrics, emojiCorrection);
|
|
2122
|
+
const lineEndFitAdvance = segKind === "space" || segKind === "preserved-space" || segKind === "zero-width-break" ? 0 : w;
|
|
2123
|
+
const lineEndPaintAdvance = segKind === "space" || segKind === "zero-width-break" ? 0 : w;
|
|
2124
|
+
if (segWordLike && segText.length > 1) {
|
|
2125
|
+
const graphemeWidths = getSegmentGraphemeWidths(segText, segMetrics, cache, emojiCorrection);
|
|
2126
|
+
const graphemePrefixWidths = engineProfile.preferPrefixWidthsForBreakableRuns ? getSegmentGraphemePrefixWidths(segText, segMetrics, cache, emojiCorrection) : null;
|
|
2127
|
+
pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, graphemeWidths, graphemePrefixWidths);
|
|
2128
|
+
} else {
|
|
2129
|
+
pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, null, null);
|
|
2130
|
+
}
|
|
2131
|
+
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
2132
|
+
}
|
|
2133
|
+
const chunks = mapAnalysisChunksToPreparedChunks(analysis.chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex);
|
|
2134
|
+
const segLevels = segStarts === null ? null : computeSegmentLevels(analysis.normalized, segStarts);
|
|
2135
|
+
if (segments !== null) {
|
|
2136
|
+
return {
|
|
2137
|
+
widths,
|
|
2138
|
+
lineEndFitAdvances,
|
|
2139
|
+
lineEndPaintAdvances,
|
|
2140
|
+
kinds,
|
|
2141
|
+
simpleLineWalkFastPath,
|
|
2142
|
+
segLevels,
|
|
2143
|
+
breakableWidths,
|
|
2144
|
+
breakablePrefixWidths,
|
|
2145
|
+
discretionaryHyphenWidth,
|
|
2146
|
+
tabStopAdvance,
|
|
2147
|
+
chunks,
|
|
2148
|
+
segments
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
return {
|
|
2152
|
+
widths,
|
|
2153
|
+
lineEndFitAdvances,
|
|
2154
|
+
lineEndPaintAdvances,
|
|
2155
|
+
kinds,
|
|
2156
|
+
simpleLineWalkFastPath,
|
|
2157
|
+
segLevels,
|
|
2158
|
+
breakableWidths,
|
|
2159
|
+
breakablePrefixWidths,
|
|
2160
|
+
discretionaryHyphenWidth,
|
|
2161
|
+
tabStopAdvance,
|
|
2162
|
+
chunks
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
function mapAnalysisChunksToPreparedChunks(chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex) {
|
|
2166
|
+
const preparedChunks = [];
|
|
2167
|
+
for (let i = 0;i < chunks.length; i++) {
|
|
2168
|
+
const chunk = chunks[i];
|
|
2169
|
+
const startSegmentIndex = chunk.startSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.startSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
|
|
2170
|
+
const endSegmentIndex = chunk.endSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.endSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
|
|
2171
|
+
const consumedEndSegmentIndex = chunk.consumedEndSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.consumedEndSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
|
|
2172
|
+
preparedChunks.push({
|
|
2173
|
+
startSegmentIndex,
|
|
2174
|
+
endSegmentIndex,
|
|
2175
|
+
consumedEndSegmentIndex
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
return preparedChunks;
|
|
2179
|
+
}
|
|
2180
|
+
function prepareInternal(text, font, includeSegments, options) {
|
|
2181
|
+
const analysis = analyzeText(text, getEngineProfile(), options?.whiteSpace);
|
|
2182
|
+
return measureAnalysis(analysis, font, includeSegments);
|
|
2183
|
+
}
|
|
2184
|
+
function prepareWithSegments(text, font, options) {
|
|
2185
|
+
return prepareInternal(text, font, true, options);
|
|
2186
|
+
}
|
|
2187
|
+
function getInternalPrepared(prepared) {
|
|
2188
|
+
return prepared;
|
|
2189
|
+
}
|
|
2190
|
+
function toLayoutLineRange(line) {
|
|
2191
|
+
return {
|
|
2192
|
+
width: line.width,
|
|
2193
|
+
start: {
|
|
2194
|
+
segmentIndex: line.startSegmentIndex,
|
|
2195
|
+
graphemeIndex: line.startGraphemeIndex
|
|
2196
|
+
},
|
|
2197
|
+
end: {
|
|
2198
|
+
segmentIndex: line.endSegmentIndex,
|
|
2199
|
+
graphemeIndex: line.endGraphemeIndex
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
function walkLineRanges(prepared, maxWidth, onLine) {
|
|
2204
|
+
if (prepared.widths.length === 0)
|
|
2205
|
+
return 0;
|
|
2206
|
+
return walkPreparedLines(getInternalPrepared(prepared), maxWidth, (line) => {
|
|
2207
|
+
onLine(toLayoutLineRange(line));
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
function clearCache() {
|
|
2211
|
+
clearAnalysisCaches();
|
|
2212
|
+
sharedGraphemeSegmenter2 = null;
|
|
2213
|
+
sharedLineTextCaches = new WeakMap;
|
|
2214
|
+
clearMeasurementCaches();
|
|
2215
|
+
}
|
|
2216
|
+
// torph/src/utils/text-layout/pretextMorph.ts
|
|
2217
|
+
var UNBOUNDED_LAYOUT_WIDTH = Number.MAX_SAFE_INTEGER / 4;
|
|
2218
|
+
var PREPARED_TEXT_CACHE_LIMIT = 256;
|
|
2219
|
+
var PROBE_COMPLEX_WHITESPACE_RE = /[\t\n\r\f]| {2,}|^ | $/;
|
|
2220
|
+
var FAST_PATH_RTL_RE = /[\u0590-\u08FF\uFB1D-\uFDFD\uFE70-\uFEFC]/u;
|
|
2221
|
+
var PROBE_SYSTEM_UI_RE = /\bsystem-ui\b/i;
|
|
2222
|
+
var preparedTextCache = new Map;
|
|
2223
|
+
var preparedSegmentGraphemeCache = new WeakMap;
|
|
2224
|
+
var sharedGraphemeSegmenter3 = null;
|
|
2225
|
+
function getSharedGraphemeSegmenter3() {
|
|
2226
|
+
if (sharedGraphemeSegmenter3 !== null) {
|
|
2227
|
+
return sharedGraphemeSegmenter3;
|
|
2228
|
+
}
|
|
2229
|
+
sharedGraphemeSegmenter3 = new Intl.Segmenter(undefined, {
|
|
2230
|
+
granularity: "grapheme"
|
|
2231
|
+
});
|
|
2232
|
+
return sharedGraphemeSegmenter3;
|
|
2233
|
+
}
|
|
2234
|
+
function readPretextWhiteSpace(whiteSpace) {
|
|
2235
|
+
return whiteSpace === "pre-wrap" ? "pre-wrap" : "normal";
|
|
2236
|
+
}
|
|
2237
|
+
function getPreparedTextCacheKey(renderText, layoutContext) {
|
|
2238
|
+
return `${layoutContext.font}\x00${layoutContext.whiteSpace}\x00${renderText}`;
|
|
2239
|
+
}
|
|
2240
|
+
function getPreparedText(text, renderText, layoutContext) {
|
|
2241
|
+
const cacheKey = getPreparedTextCacheKey(renderText, layoutContext);
|
|
2242
|
+
const cached = preparedTextCache.get(cacheKey);
|
|
2243
|
+
if (cached !== undefined) {
|
|
2244
|
+
preparedTextCache.delete(cacheKey);
|
|
2245
|
+
preparedTextCache.set(cacheKey, cached);
|
|
2246
|
+
return cached;
|
|
2247
|
+
}
|
|
2248
|
+
const prepared = prepareWithSegments(text, layoutContext.font, {
|
|
2249
|
+
whiteSpace: readPretextWhiteSpace(layoutContext.whiteSpace)
|
|
2250
|
+
});
|
|
2251
|
+
preparedTextCache.set(cacheKey, prepared);
|
|
2252
|
+
if (preparedTextCache.size > PREPARED_TEXT_CACHE_LIMIT) {
|
|
2253
|
+
const oldest = preparedTextCache.keys().next();
|
|
2254
|
+
if (!oldest.done) {
|
|
2255
|
+
preparedTextCache.delete(oldest.value);
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
return prepared;
|
|
2259
|
+
}
|
|
2260
|
+
function clearPretextMorphCaches() {
|
|
2261
|
+
preparedTextCache.clear();
|
|
2262
|
+
sharedGraphemeSegmenter3 = null;
|
|
2263
|
+
clearCache();
|
|
2264
|
+
}
|
|
2265
|
+
function getPreparedSegmentGraphemeTexts(prepared, segmentIndex) {
|
|
2266
|
+
let cache = preparedSegmentGraphemeCache.get(prepared);
|
|
2267
|
+
if (cache === undefined) {
|
|
2268
|
+
cache = new Map;
|
|
2269
|
+
preparedSegmentGraphemeCache.set(prepared, cache);
|
|
2270
|
+
}
|
|
2271
|
+
const cached = cache.get(segmentIndex);
|
|
2272
|
+
if (cached !== undefined) {
|
|
2273
|
+
return cached;
|
|
2274
|
+
}
|
|
2275
|
+
const graphemes = [];
|
|
2276
|
+
for (const segment of getSharedGraphemeSegmenter3().segment(prepared.segments[segmentIndex])) {
|
|
2277
|
+
graphemes.push(segment.segment);
|
|
2278
|
+
}
|
|
2279
|
+
cache.set(segmentIndex, graphemes);
|
|
2280
|
+
return graphemes;
|
|
2281
|
+
}
|
|
2282
|
+
function deriveAdvancesFromPrefixWidths(prefixWidths) {
|
|
2283
|
+
const advances = [];
|
|
2284
|
+
for (let index = 0;index < prefixWidths.length; index += 1) {
|
|
2285
|
+
const previous = index === 0 ? 0 : prefixWidths[index - 1];
|
|
2286
|
+
advances.push(prefixWidths[index] - previous);
|
|
2287
|
+
}
|
|
2288
|
+
return advances;
|
|
2289
|
+
}
|
|
2290
|
+
function getSegmentGraphemeAdvances(prepared, segmentIndex, layoutContext) {
|
|
2291
|
+
const segmentText = prepared.segments[segmentIndex];
|
|
2292
|
+
const graphemes = getPreparedSegmentGraphemeTexts(prepared, segmentIndex);
|
|
2293
|
+
if (graphemes.length <= 1) {
|
|
2294
|
+
return {
|
|
2295
|
+
graphemes,
|
|
2296
|
+
advances: [prepared.widths[segmentIndex]]
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
const cachedPrefixWidths = prepared.breakablePrefixWidths[segmentIndex];
|
|
2300
|
+
if (cachedPrefixWidths !== null) {
|
|
2301
|
+
return {
|
|
2302
|
+
graphemes,
|
|
2303
|
+
advances: deriveAdvancesFromPrefixWidths(cachedPrefixWidths)
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
const cachedWidths = prepared.breakableWidths[segmentIndex];
|
|
2307
|
+
if (cachedWidths !== null) {
|
|
2308
|
+
return {
|
|
2309
|
+
graphemes,
|
|
2310
|
+
advances: cachedWidths
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
const { cache, emojiCorrection } = getFontMeasurementState(layoutContext.font, textMayContainEmoji(segmentText));
|
|
2314
|
+
const segmentMetrics = getSegmentMetrics(segmentText, cache);
|
|
2315
|
+
const prefixWidths = getSegmentGraphemePrefixWidths(segmentText, segmentMetrics, cache, emojiCorrection);
|
|
2316
|
+
return {
|
|
2317
|
+
graphemes,
|
|
2318
|
+
advances: prefixWidths === null ? [prepared.widths[segmentIndex]] : deriveAdvancesFromPrefixWidths(prefixWidths)
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
function getPretextMaxWidth(layoutContext) {
|
|
2322
|
+
return layoutContext.whiteSpace === "nowrap" ? UNBOUNDED_LAYOUT_WIDTH : Math.max(0, layoutContext.width);
|
|
2323
|
+
}
|
|
2324
|
+
function normalizeFeatureSetting(value) {
|
|
2325
|
+
return value.length === 0 ? "normal" : value;
|
|
2326
|
+
}
|
|
2327
|
+
function hasUnsupportedPretextMorphFeatures(text, layoutContext) {
|
|
2328
|
+
if (layoutContext === null) {
|
|
2329
|
+
return true;
|
|
2330
|
+
}
|
|
2331
|
+
if (text.length === 0) {
|
|
2332
|
+
return false;
|
|
2333
|
+
}
|
|
2334
|
+
if (layoutContext.font.length === 0 || layoutContext.lineHeightPx <= 0) {
|
|
2335
|
+
return true;
|
|
2336
|
+
}
|
|
2337
|
+
if (layoutContext.direction !== "ltr") {
|
|
2338
|
+
return true;
|
|
2339
|
+
}
|
|
2340
|
+
if (layoutContext.textTransform !== "none") {
|
|
2341
|
+
return true;
|
|
2342
|
+
}
|
|
2343
|
+
if (Math.abs(layoutContext.letterSpacingPx) > 0.01) {
|
|
2344
|
+
return true;
|
|
2345
|
+
}
|
|
2346
|
+
if (Math.abs(layoutContext.wordSpacingPx) > 0.01) {
|
|
2347
|
+
return true;
|
|
2348
|
+
}
|
|
2349
|
+
if (normalizeFeatureSetting(layoutContext.fontFeatureSettings) !== "normal" || normalizeFeatureSetting(layoutContext.fontVariationSettings) !== "normal") {
|
|
2350
|
+
return true;
|
|
2351
|
+
}
|
|
2352
|
+
if (text.includes("") || FAST_PATH_RTL_RE.test(text)) {
|
|
2353
|
+
return true;
|
|
2354
|
+
}
|
|
2355
|
+
return false;
|
|
2356
|
+
}
|
|
2357
|
+
function shouldProbePretextMorph(text, layoutContext) {
|
|
2358
|
+
return PROBE_COMPLEX_WHITESPACE_RE.test(text) || PROBE_SYSTEM_UI_RE.test(layoutContext.font);
|
|
2359
|
+
}
|
|
2360
|
+
function getPretextMorphRenderedText(text, layoutContext) {
|
|
2361
|
+
if (layoutContext === null) {
|
|
2362
|
+
return text;
|
|
2363
|
+
}
|
|
2364
|
+
return layoutContext.whiteSpace === "pre-wrap" ? normalizeWhitespacePreWrap(text) : normalizeWhitespaceNormal(text);
|
|
2365
|
+
}
|
|
2366
|
+
function getPretextMorphStyleSignature(layoutContext) {
|
|
2367
|
+
if (layoutContext === null) {
|
|
2368
|
+
return null;
|
|
2369
|
+
}
|
|
2370
|
+
const engineProfile = getEngineProfile();
|
|
2371
|
+
return [
|
|
2372
|
+
layoutContext.font,
|
|
2373
|
+
layoutContext.whiteSpace,
|
|
2374
|
+
layoutContext.direction,
|
|
2375
|
+
layoutContext.textTransform,
|
|
2376
|
+
layoutContext.letterSpacingPx.toFixed(4),
|
|
2377
|
+
layoutContext.wordSpacingPx.toFixed(4),
|
|
2378
|
+
normalizeFeatureSetting(layoutContext.fontFeatureSettings),
|
|
2379
|
+
normalizeFeatureSetting(layoutContext.fontVariationSettings),
|
|
2380
|
+
String(engineProfile.lineFitEpsilon),
|
|
2381
|
+
engineProfile.carryCJKAfterClosingQuote ? "1" : "0",
|
|
2382
|
+
engineProfile.preferPrefixWidthsForBreakableRuns ? "1" : "0",
|
|
2383
|
+
engineProfile.preferEarlySoftHyphenBreak ? "1" : "0"
|
|
2384
|
+
].join("\x00");
|
|
2385
|
+
}
|
|
2386
|
+
function getPretextMorphMeasurementBackend(text, layoutContext) {
|
|
2387
|
+
if (layoutContext === null) {
|
|
2388
|
+
return "dom";
|
|
2389
|
+
}
|
|
2390
|
+
if (text.length === 0) {
|
|
2391
|
+
return "pretext";
|
|
2392
|
+
}
|
|
2393
|
+
if (hasUnsupportedPretextMorphFeatures(text, layoutContext)) {
|
|
2394
|
+
return "dom";
|
|
2395
|
+
}
|
|
2396
|
+
return shouldProbePretextMorph(text, layoutContext) ? "probe" : "pretext";
|
|
2397
|
+
}
|
|
2398
|
+
function pushSegmentGraphemeRange({
|
|
2399
|
+
advances,
|
|
2400
|
+
graphemes,
|
|
2401
|
+
startGraphemeIndex,
|
|
2402
|
+
endGraphemeIndex,
|
|
2403
|
+
top,
|
|
2404
|
+
left,
|
|
2405
|
+
ordinal,
|
|
2406
|
+
output,
|
|
2407
|
+
lineHeightPx
|
|
2408
|
+
}) {
|
|
2409
|
+
let nextLeft = left;
|
|
2410
|
+
let nextOrdinal = ordinal;
|
|
2411
|
+
for (let graphemeIndex = startGraphemeIndex;graphemeIndex < endGraphemeIndex; graphemeIndex += 1) {
|
|
2412
|
+
const glyph = graphemes[graphemeIndex];
|
|
2413
|
+
const advance = advances[graphemeIndex];
|
|
2414
|
+
output.push({
|
|
2415
|
+
glyph,
|
|
2416
|
+
key: `${glyph}:${nextOrdinal}`,
|
|
2417
|
+
left: nextLeft,
|
|
2418
|
+
top,
|
|
2419
|
+
width: advance,
|
|
2420
|
+
height: lineHeightPx
|
|
2421
|
+
});
|
|
2422
|
+
nextOrdinal += 1;
|
|
2423
|
+
nextLeft += advance;
|
|
2424
|
+
}
|
|
2425
|
+
return {
|
|
2426
|
+
left: nextLeft,
|
|
2427
|
+
ordinal: nextOrdinal
|
|
2428
|
+
};
|
|
2429
|
+
}
|
|
2430
|
+
function measureMorphSnapshotWithPretext(text, layoutContext) {
|
|
2431
|
+
const renderText = getPretextMorphRenderedText(text, layoutContext);
|
|
2432
|
+
if (text.length === 0) {
|
|
2433
|
+
return {
|
|
2434
|
+
text,
|
|
2435
|
+
renderText,
|
|
2436
|
+
width: 0,
|
|
2437
|
+
height: 0,
|
|
2438
|
+
graphemes: []
|
|
2439
|
+
};
|
|
2440
|
+
}
|
|
2441
|
+
const prepared = getPreparedText(text, renderText, layoutContext);
|
|
2442
|
+
const graphemes = [];
|
|
2443
|
+
let width = 0;
|
|
2444
|
+
let ordinal = 0;
|
|
2445
|
+
let lineIndex = 0;
|
|
2446
|
+
const lineCount = walkLineRanges(prepared, getPretextMaxWidth(layoutContext), (line) => {
|
|
2447
|
+
const top = lineIndex * layoutContext.lineHeightPx;
|
|
2448
|
+
let left = 0;
|
|
2449
|
+
for (let segmentIndex = line.start.segmentIndex;segmentIndex < line.end.segmentIndex; segmentIndex += 1) {
|
|
2450
|
+
const startGraphemeIndex = segmentIndex === line.start.segmentIndex ? line.start.graphemeIndex : 0;
|
|
2451
|
+
const { advances, graphemes: segmentGraphemes } = getSegmentGraphemeAdvances(prepared, segmentIndex, layoutContext);
|
|
2452
|
+
const next = pushSegmentGraphemeRange({
|
|
2453
|
+
advances,
|
|
2454
|
+
graphemes: segmentGraphemes,
|
|
2455
|
+
startGraphemeIndex,
|
|
2456
|
+
endGraphemeIndex: segmentGraphemes.length,
|
|
2457
|
+
top,
|
|
2458
|
+
left,
|
|
2459
|
+
ordinal,
|
|
2460
|
+
output: graphemes,
|
|
2461
|
+
lineHeightPx: layoutContext.lineHeightPx
|
|
2462
|
+
});
|
|
2463
|
+
left = next.left;
|
|
2464
|
+
ordinal = next.ordinal;
|
|
2465
|
+
}
|
|
2466
|
+
if (line.end.graphemeIndex > 0) {
|
|
2467
|
+
const startGraphemeIndex = line.start.segmentIndex === line.end.segmentIndex ? line.start.graphemeIndex : 0;
|
|
2468
|
+
const { advances, graphemes: segmentGraphemes } = getSegmentGraphemeAdvances(prepared, line.end.segmentIndex, layoutContext);
|
|
2469
|
+
const next = pushSegmentGraphemeRange({
|
|
2470
|
+
advances,
|
|
2471
|
+
graphemes: segmentGraphemes,
|
|
2472
|
+
startGraphemeIndex,
|
|
2473
|
+
endGraphemeIndex: line.end.graphemeIndex,
|
|
2474
|
+
top,
|
|
2475
|
+
left,
|
|
2476
|
+
ordinal,
|
|
2477
|
+
output: graphemes,
|
|
2478
|
+
lineHeightPx: layoutContext.lineHeightPx
|
|
2479
|
+
});
|
|
2480
|
+
left = next.left;
|
|
2481
|
+
ordinal = next.ordinal;
|
|
2482
|
+
}
|
|
2483
|
+
width = Math.max(width, left);
|
|
2484
|
+
lineIndex += 1;
|
|
2485
|
+
});
|
|
2486
|
+
return {
|
|
2487
|
+
text,
|
|
2488
|
+
renderText,
|
|
2489
|
+
width,
|
|
2490
|
+
height: lineCount * layoutContext.lineHeightPx,
|
|
2491
|
+
graphemes
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
// torph/src/components/Torph.tsx
|
|
2496
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
2497
|
+
var MORPH = {
|
|
2498
|
+
durationMs: 280,
|
|
2499
|
+
maxFadeMs: 150,
|
|
2500
|
+
ease: "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
2501
|
+
geometryEpsilon: 0.5,
|
|
2502
|
+
lineGroupingEpsilon: 1
|
|
2503
|
+
};
|
|
2504
|
+
var MORPH_SEGMENT_CACHE_LIMIT = 256;
|
|
2505
|
+
var DOM_MEASUREMENT_SNAPSHOT_CACHE_LIMIT = 8;
|
|
2506
|
+
var EMPTY_STATE = {
|
|
2507
|
+
stage: "idle",
|
|
2508
|
+
measurement: null,
|
|
2509
|
+
plan: null
|
|
2510
|
+
};
|
|
2511
|
+
var EMPTY_SESSION = {
|
|
2512
|
+
committed: null,
|
|
2513
|
+
target: null,
|
|
2514
|
+
animating: false
|
|
2515
|
+
};
|
|
2516
|
+
var EMPTY_TIMELINE = {
|
|
2517
|
+
prepareFrame: null,
|
|
2518
|
+
animateFrame: null,
|
|
2519
|
+
finalizeTimer: null
|
|
2520
|
+
};
|
|
2521
|
+
var ZERO_BRIDGE = {
|
|
2522
|
+
offsetX: 0,
|
|
2523
|
+
offsetY: 0
|
|
2524
|
+
};
|
|
2525
|
+
var EMPTY_SEGMENTS = [];
|
|
2526
|
+
var morphSegmentCache = new Map;
|
|
2527
|
+
var pretextMorphTrustCache = new Map;
|
|
2528
|
+
var morphMeasurementEpoch = 1;
|
|
2529
|
+
var activeMorphMeasurementConsumers = 0;
|
|
2530
|
+
var detachMorphMeasurementInvalidationListeners = null;
|
|
2531
|
+
var SCREEN_READER_ONLY_STYLE = {
|
|
2532
|
+
position: "absolute",
|
|
2533
|
+
width: "1px",
|
|
2534
|
+
height: "1px",
|
|
2535
|
+
margin: "-1px",
|
|
2536
|
+
padding: 0,
|
|
2537
|
+
border: 0,
|
|
2538
|
+
clip: "rect(0 0 0 0)",
|
|
2539
|
+
clipPath: "inset(50%)",
|
|
2540
|
+
overflow: "hidden",
|
|
2541
|
+
whiteSpace: "nowrap"
|
|
2542
|
+
};
|
|
2543
|
+
var MEASUREMENT_LAYER_STYLE = {
|
|
2544
|
+
pointerEvents: "none",
|
|
2545
|
+
visibility: "hidden",
|
|
2546
|
+
position: "absolute",
|
|
2547
|
+
top: 0,
|
|
2548
|
+
left: 0,
|
|
2549
|
+
right: 0,
|
|
2550
|
+
display: "block"
|
|
2551
|
+
};
|
|
2552
|
+
var FALLBACK_TEXT_STYLE = {
|
|
2553
|
+
display: "block",
|
|
2554
|
+
gridArea: "1 / 1"
|
|
2555
|
+
};
|
|
2556
|
+
var OVERLAY_STYLE = {
|
|
2557
|
+
position: "absolute",
|
|
2558
|
+
inset: 0,
|
|
2559
|
+
minWidth: 0,
|
|
2560
|
+
pointerEvents: "none"
|
|
2561
|
+
};
|
|
2562
|
+
var SHARED_GLYPH_TYPOGRAPHY_STYLE = {
|
|
2563
|
+
font: "inherit",
|
|
2564
|
+
fontKerning: "inherit",
|
|
2565
|
+
fontFeatureSettings: "inherit",
|
|
2566
|
+
fontOpticalSizing: "inherit",
|
|
2567
|
+
fontStretch: "inherit",
|
|
2568
|
+
fontStyle: "inherit",
|
|
2569
|
+
fontVariant: "inherit",
|
|
2570
|
+
fontVariantNumeric: "inherit",
|
|
2571
|
+
fontVariationSettings: "inherit",
|
|
2572
|
+
fontWeight: "inherit",
|
|
2573
|
+
letterSpacing: "inherit",
|
|
2574
|
+
textTransform: "inherit",
|
|
2575
|
+
wordSpacing: "inherit",
|
|
2576
|
+
direction: "inherit"
|
|
2577
|
+
};
|
|
2578
|
+
var MEASUREMENT_GLYPH_STYLE = {
|
|
2579
|
+
...SHARED_GLYPH_TYPOGRAPHY_STYLE,
|
|
2580
|
+
display: "inline"
|
|
2581
|
+
};
|
|
2582
|
+
var ABSOLUTE_GLYPH_STYLE = {
|
|
2583
|
+
...SHARED_GLYPH_TYPOGRAPHY_STYLE,
|
|
2584
|
+
position: "absolute",
|
|
2585
|
+
display: "block",
|
|
2586
|
+
overflow: "visible",
|
|
2587
|
+
transformOrigin: "left top",
|
|
2588
|
+
whiteSpace: "pre"
|
|
2589
|
+
};
|
|
2590
|
+
var graphemeSegmenter = null;
|
|
2591
|
+
var domMeasurementService = null;
|
|
2592
|
+
function parsePx(value) {
|
|
2593
|
+
const parsed = Number.parseFloat(value);
|
|
2594
|
+
if (Number.isFinite(parsed)) {
|
|
2595
|
+
return parsed;
|
|
2596
|
+
}
|
|
2597
|
+
return null;
|
|
2598
|
+
}
|
|
2599
|
+
function readFont(styles) {
|
|
2600
|
+
if (styles.font.length > 0) {
|
|
2601
|
+
return styles.font;
|
|
2602
|
+
}
|
|
2603
|
+
return `${styles.fontStyle} ${styles.fontVariant} ${styles.fontWeight} ${styles.fontSize} / ${styles.lineHeight} ${styles.fontFamily}`;
|
|
2604
|
+
}
|
|
2605
|
+
function readLineHeightPx(styles) {
|
|
2606
|
+
const lineHeightPx = parsePx(styles.lineHeight);
|
|
2607
|
+
if (lineHeightPx !== null) {
|
|
2608
|
+
return lineHeightPx;
|
|
2609
|
+
}
|
|
2610
|
+
const fontSizePx = parsePx(styles.fontSize);
|
|
2611
|
+
return (fontSizePx ?? 0) * 1.2;
|
|
2612
|
+
}
|
|
2613
|
+
function readSpacingPx(value) {
|
|
2614
|
+
if (value === "normal") {
|
|
2615
|
+
return 0;
|
|
2616
|
+
}
|
|
2617
|
+
return parsePx(value) ?? 0;
|
|
2618
|
+
}
|
|
2619
|
+
function readWhiteSpace(value) {
|
|
2620
|
+
if (value === "normal" || value === "nowrap" || value === "pre-wrap") {
|
|
2621
|
+
return value;
|
|
2622
|
+
}
|
|
2623
|
+
throw new Error(`Torph only supports white-space: normal | nowrap | pre-wrap. Received: ${value}`);
|
|
2624
|
+
}
|
|
2625
|
+
function readContentWidth(node, styles) {
|
|
2626
|
+
const rectWidth = node.getBoundingClientRect().width;
|
|
2627
|
+
const paddingLeft = parsePx(styles.paddingLeft) ?? 0;
|
|
2628
|
+
const paddingRight = parsePx(styles.paddingRight) ?? 0;
|
|
2629
|
+
const borderLeft = parsePx(styles.borderLeftWidth) ?? 0;
|
|
2630
|
+
const borderRight = parsePx(styles.borderRightWidth) ?? 0;
|
|
2631
|
+
return Math.max(0, rectWidth - paddingLeft - paddingRight - borderLeft - borderRight);
|
|
2632
|
+
}
|
|
2633
|
+
function readLayoutContext(node, width) {
|
|
2634
|
+
const styles = getComputedStyle(node);
|
|
2635
|
+
const parentDisplay = (node.parentElement && getComputedStyle(node.parentElement).display) ?? "block";
|
|
2636
|
+
return {
|
|
2637
|
+
display: styles.display,
|
|
2638
|
+
direction: styles.direction,
|
|
2639
|
+
font: readFont(styles),
|
|
2640
|
+
fontFeatureSettings: styles.fontFeatureSettings,
|
|
2641
|
+
fontVariationSettings: styles.fontVariationSettings,
|
|
2642
|
+
letterSpacingPx: readSpacingPx(styles.letterSpacing),
|
|
2643
|
+
lineHeightPx: readLineHeightPx(styles),
|
|
2644
|
+
parentDisplay,
|
|
2645
|
+
textTransform: styles.textTransform,
|
|
2646
|
+
whiteSpace: readWhiteSpace(styles.whiteSpace),
|
|
2647
|
+
width: width ?? readContentWidth(node, styles),
|
|
2648
|
+
wordSpacingPx: readSpacingPx(styles.wordSpacing),
|
|
2649
|
+
measurementVersion: 0
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
function sameLayoutContext(a, b) {
|
|
2653
|
+
if (a === null) {
|
|
2654
|
+
return false;
|
|
2655
|
+
}
|
|
2656
|
+
return a.display === b.display && a.direction === b.direction && a.font === b.font && a.fontFeatureSettings === b.fontFeatureSettings && a.fontVariationSettings === b.fontVariationSettings && Math.abs(a.letterSpacingPx - b.letterSpacingPx) < MORPH.geometryEpsilon && Math.abs(a.lineHeightPx - b.lineHeightPx) < MORPH.geometryEpsilon && a.parentDisplay === b.parentDisplay && a.textTransform === b.textTransform && a.whiteSpace === b.whiteSpace && Math.abs(a.width - b.width) < MORPH.geometryEpsilon && Math.abs(a.wordSpacingPx - b.wordSpacingPx) < MORPH.geometryEpsilon;
|
|
2657
|
+
}
|
|
2658
|
+
function clearPretextMorphTrustCache() {
|
|
2659
|
+
pretextMorphTrustCache.clear();
|
|
2660
|
+
}
|
|
2661
|
+
function clearMorphMeasurementCaches() {
|
|
2662
|
+
morphSegmentCache.clear();
|
|
2663
|
+
clearPretextMorphTrustCache();
|
|
2664
|
+
clearPretextMorphCaches();
|
|
2665
|
+
}
|
|
2666
|
+
function bumpMorphMeasurementEpoch() {
|
|
2667
|
+
morphMeasurementEpoch += 1;
|
|
2668
|
+
}
|
|
2669
|
+
function getMorphMeasurementEpoch() {
|
|
2670
|
+
return morphMeasurementEpoch;
|
|
2671
|
+
}
|
|
2672
|
+
function isSingleLineSnapshot(snapshot) {
|
|
2673
|
+
if (snapshot.graphemes.length <= 1) {
|
|
2674
|
+
return true;
|
|
2675
|
+
}
|
|
2676
|
+
const firstTop = snapshot.graphemes[0].top;
|
|
2677
|
+
return snapshot.graphemes.every((grapheme) => nearlyEqual(grapheme.top, firstTop, MORPH.lineGroupingEpsilon));
|
|
2678
|
+
}
|
|
2679
|
+
function acquireMorphMeasurementInvalidationListeners() {
|
|
2680
|
+
activeMorphMeasurementConsumers += 1;
|
|
2681
|
+
if (detachMorphMeasurementInvalidationListeners === null) {
|
|
2682
|
+
const handleFontChange = () => {
|
|
2683
|
+
clearMorphMeasurementCaches();
|
|
2684
|
+
bumpMorphMeasurementEpoch();
|
|
2685
|
+
};
|
|
2686
|
+
document.fonts.ready.then(handleFontChange);
|
|
2687
|
+
if (typeof document.fonts.addEventListener === "function") {
|
|
2688
|
+
document.fonts.addEventListener("loadingdone", handleFontChange);
|
|
2689
|
+
}
|
|
2690
|
+
detachMorphMeasurementInvalidationListeners = () => {
|
|
2691
|
+
if (typeof document.fonts.removeEventListener === "function") {
|
|
2692
|
+
document.fonts.removeEventListener("loadingdone", handleFontChange);
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
function releaseMorphMeasurementInvalidationListeners() {
|
|
2698
|
+
activeMorphMeasurementConsumers = Math.max(0, activeMorphMeasurementConsumers - 1);
|
|
2699
|
+
if (activeMorphMeasurementConsumers === 0 && detachMorphMeasurementInvalidationListeners !== null) {
|
|
2700
|
+
detachMorphMeasurementInvalidationListeners();
|
|
2701
|
+
detachMorphMeasurementInvalidationListeners = null;
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
function useObservedLayoutContext(deps) {
|
|
2705
|
+
const ref = useRef(null);
|
|
2706
|
+
const [layoutContext, setLayoutContext] = useState(null);
|
|
2707
|
+
useLayoutEffect(() => {
|
|
2708
|
+
const node = ref.current;
|
|
2709
|
+
if (node === null) {
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
let disposed = false;
|
|
2713
|
+
const commitLayoutContext = (next, refreshMeasurements = false) => {
|
|
2714
|
+
setLayoutContext((previous) => {
|
|
2715
|
+
if (sameLayoutContext(previous, next) && !refreshMeasurements) {
|
|
2716
|
+
return previous;
|
|
2717
|
+
}
|
|
2718
|
+
const measurementVersion = (previous?.measurementVersion ?? 0) + 1;
|
|
2719
|
+
return {
|
|
2720
|
+
...next,
|
|
2721
|
+
measurementVersion
|
|
2722
|
+
};
|
|
2723
|
+
});
|
|
2724
|
+
};
|
|
2725
|
+
const syncLayoutContext = ({
|
|
2726
|
+
width,
|
|
2727
|
+
refreshMeasurements = false
|
|
2728
|
+
} = {}) => {
|
|
2729
|
+
if (disposed) {
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
const next = readLayoutContext(node, width);
|
|
2733
|
+
commitLayoutContext(next, refreshMeasurements);
|
|
2734
|
+
};
|
|
2735
|
+
const initialLayoutContext = readLayoutContext(node);
|
|
2736
|
+
const shouldObserveWrappingWidth = initialLayoutContext.whiteSpace !== "nowrap" && !supportsIntrinsicWidthLock(initialLayoutContext.display, initialLayoutContext.parentDisplay);
|
|
2737
|
+
commitLayoutContext(initialLayoutContext, true);
|
|
2738
|
+
let resizeObserver = null;
|
|
2739
|
+
if (shouldObserveWrappingWidth) {
|
|
2740
|
+
resizeObserver = new ResizeObserver(([entry]) => {
|
|
2741
|
+
syncLayoutContext({
|
|
2742
|
+
width: entry?.contentRect.width
|
|
2743
|
+
});
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2746
|
+
resizeObserver?.observe(node);
|
|
2747
|
+
acquireMorphMeasurementInvalidationListeners();
|
|
2748
|
+
return () => {
|
|
2749
|
+
disposed = true;
|
|
2750
|
+
resizeObserver?.disconnect();
|
|
2751
|
+
releaseMorphMeasurementInvalidationListeners();
|
|
2752
|
+
};
|
|
2753
|
+
}, deps);
|
|
2754
|
+
return { ref, layoutContext };
|
|
2755
|
+
}
|
|
2756
|
+
function getSegmenter() {
|
|
2757
|
+
if (graphemeSegmenter !== null) {
|
|
2758
|
+
return graphemeSegmenter;
|
|
2759
|
+
}
|
|
2760
|
+
const segmenterConstructor = Intl.Segmenter;
|
|
2761
|
+
if (segmenterConstructor === undefined) {
|
|
2762
|
+
throw new Error("Torph requires Intl.Segmenter for grapheme-safe pairing.");
|
|
2763
|
+
}
|
|
2764
|
+
graphemeSegmenter = new segmenterConstructor(undefined, {
|
|
2765
|
+
granularity: "grapheme"
|
|
2766
|
+
});
|
|
2767
|
+
return graphemeSegmenter;
|
|
2768
|
+
}
|
|
2769
|
+
function createMeasurementGlyphNode() {
|
|
2770
|
+
const node = document.createElement("span");
|
|
2771
|
+
node.style.font = "inherit";
|
|
2772
|
+
node.style.fontKerning = "inherit";
|
|
2773
|
+
node.style.fontFeatureSettings = "inherit";
|
|
2774
|
+
node.style.fontOpticalSizing = "inherit";
|
|
2775
|
+
node.style.fontStretch = "inherit";
|
|
2776
|
+
node.style.fontStyle = "inherit";
|
|
2777
|
+
node.style.fontVariant = "inherit";
|
|
2778
|
+
node.style.fontVariantNumeric = "inherit";
|
|
2779
|
+
node.style.fontVariationSettings = "inherit";
|
|
2780
|
+
node.style.fontWeight = "inherit";
|
|
2781
|
+
node.style.letterSpacing = "inherit";
|
|
2782
|
+
node.style.textTransform = "inherit";
|
|
2783
|
+
node.style.wordSpacing = "inherit";
|
|
2784
|
+
node.style.direction = "inherit";
|
|
2785
|
+
node.style.display = "inline";
|
|
2786
|
+
return node;
|
|
2787
|
+
}
|
|
2788
|
+
function getDomMeasurementService() {
|
|
2789
|
+
if (domMeasurementService !== null) {
|
|
2790
|
+
return domMeasurementService;
|
|
2791
|
+
}
|
|
2792
|
+
const root = document.createElement("div");
|
|
2793
|
+
root.setAttribute("aria-hidden", "true");
|
|
2794
|
+
root.style.position = "fixed";
|
|
2795
|
+
root.style.left = "0";
|
|
2796
|
+
root.style.top = "0";
|
|
2797
|
+
root.style.width = "0";
|
|
2798
|
+
root.style.height = "0";
|
|
2799
|
+
root.style.overflow = "hidden";
|
|
2800
|
+
root.style.visibility = "hidden";
|
|
2801
|
+
root.style.pointerEvents = "none";
|
|
2802
|
+
root.style.zIndex = "-1";
|
|
2803
|
+
root.style.contain = "layout style paint";
|
|
2804
|
+
const host = document.createElement("span");
|
|
2805
|
+
root.appendChild(host);
|
|
2806
|
+
document.body.appendChild(root);
|
|
2807
|
+
domMeasurementService = {
|
|
2808
|
+
root,
|
|
2809
|
+
host,
|
|
2810
|
+
glyphNodes: []
|
|
2811
|
+
};
|
|
2812
|
+
return domMeasurementService;
|
|
2813
|
+
}
|
|
2814
|
+
function syncMeasurementGlyphNodes(service, segments) {
|
|
2815
|
+
while (service.glyphNodes.length < segments.length) {
|
|
2816
|
+
const node = createMeasurementGlyphNode();
|
|
2817
|
+
service.host.appendChild(node);
|
|
2818
|
+
service.glyphNodes.push(node);
|
|
2819
|
+
}
|
|
2820
|
+
while (service.glyphNodes.length > segments.length) {
|
|
2821
|
+
const node = service.glyphNodes.pop();
|
|
2822
|
+
node?.remove();
|
|
2823
|
+
}
|
|
2824
|
+
for (let index = 0;index < segments.length; index += 1) {
|
|
2825
|
+
service.glyphNodes[index].textContent = segments[index].glyph;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
function applyMeasurementHostStyle({
|
|
2829
|
+
host,
|
|
2830
|
+
root,
|
|
2831
|
+
layoutContext,
|
|
2832
|
+
useContentInlineSize
|
|
2833
|
+
}) {
|
|
2834
|
+
const styles = getComputedStyle(root);
|
|
2835
|
+
host.style.position = "absolute";
|
|
2836
|
+
host.style.top = "0";
|
|
2837
|
+
host.style.left = "0";
|
|
2838
|
+
host.style.right = "auto";
|
|
2839
|
+
host.style.display = "block";
|
|
2840
|
+
host.style.margin = "0";
|
|
2841
|
+
host.style.padding = "0";
|
|
2842
|
+
host.style.border = "0";
|
|
2843
|
+
host.style.minWidth = "0";
|
|
2844
|
+
host.style.boxSizing = "content-box";
|
|
2845
|
+
host.style.font = readFont(styles);
|
|
2846
|
+
host.style.fontKerning = styles.fontKerning;
|
|
2847
|
+
host.style.fontFeatureSettings = styles.fontFeatureSettings;
|
|
2848
|
+
host.style.fontOpticalSizing = styles.fontOpticalSizing;
|
|
2849
|
+
host.style.fontSynthesis = styles.fontSynthesis;
|
|
2850
|
+
host.style.fontStretch = styles.fontStretch;
|
|
2851
|
+
host.style.fontStyle = styles.fontStyle;
|
|
2852
|
+
host.style.fontVariant = styles.fontVariant;
|
|
2853
|
+
host.style.fontVariantAlternates = styles.fontVariantAlternates;
|
|
2854
|
+
host.style.fontVariantCaps = styles.fontVariantCaps;
|
|
2855
|
+
host.style.fontVariantEastAsian = styles.fontVariantEastAsian;
|
|
2856
|
+
host.style.fontVariantLigatures = styles.fontVariantLigatures;
|
|
2857
|
+
host.style.fontVariantNumeric = styles.fontVariantNumeric;
|
|
2858
|
+
host.style.fontVariantPosition = styles.fontVariantPosition;
|
|
2859
|
+
host.style.fontVariationSettings = styles.fontVariationSettings;
|
|
2860
|
+
host.style.fontWeight = styles.fontWeight;
|
|
2861
|
+
host.style.letterSpacing = styles.letterSpacing;
|
|
2862
|
+
host.style.lineHeight = styles.lineHeight;
|
|
2863
|
+
host.style.textRendering = styles.textRendering;
|
|
2864
|
+
host.style.textTransform = styles.textTransform;
|
|
2865
|
+
host.style.whiteSpace = styles.whiteSpace;
|
|
2866
|
+
host.style.wordSpacing = styles.wordSpacing;
|
|
2867
|
+
host.style.direction = styles.direction;
|
|
2868
|
+
host.style.width = `${layoutContext.width}px`;
|
|
2869
|
+
if (useContentInlineSize || layoutContext.whiteSpace === "nowrap") {
|
|
2870
|
+
host.style.width = "max-content";
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
function segmentTorphText(text) {
|
|
2874
|
+
const segments = [];
|
|
2875
|
+
let ordinal = 0;
|
|
2876
|
+
for (const segment of getSegmenter().segment(text)) {
|
|
2877
|
+
segments.push({
|
|
2878
|
+
glyph: segment.segment,
|
|
2879
|
+
key: `${segment.segment}:${ordinal}`
|
|
2880
|
+
});
|
|
2881
|
+
ordinal += 1;
|
|
2882
|
+
}
|
|
2883
|
+
return segments;
|
|
2884
|
+
}
|
|
2885
|
+
function readCachedMorphSegments(text) {
|
|
2886
|
+
const cached = morphSegmentCache.get(text);
|
|
2887
|
+
if (cached !== undefined) {
|
|
2888
|
+
morphSegmentCache.delete(text);
|
|
2889
|
+
morphSegmentCache.set(text, cached);
|
|
2890
|
+
return cached;
|
|
2891
|
+
}
|
|
2892
|
+
const segments = segmentTorphText(text);
|
|
2893
|
+
morphSegmentCache.set(text, segments);
|
|
2894
|
+
if (morphSegmentCache.size > MORPH_SEGMENT_CACHE_LIMIT) {
|
|
2895
|
+
const oldest = morphSegmentCache.keys().next();
|
|
2896
|
+
if (!oldest.done) {
|
|
2897
|
+
morphSegmentCache.delete(oldest.value);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
return segments;
|
|
2901
|
+
}
|
|
2902
|
+
function getDomMeasurementRequestKey(text, renderText, layoutContext, useContentInlineSize) {
|
|
2903
|
+
let inlineSizeMode = "container";
|
|
2904
|
+
if (useContentInlineSize) {
|
|
2905
|
+
inlineSizeMode = "content";
|
|
2906
|
+
}
|
|
2907
|
+
return `dom\x00${inlineSizeMode}\x00${text}\x00${renderText}\x00${layoutContext.measurementVersion}\x00${getMorphMeasurementEpoch()}`;
|
|
2908
|
+
}
|
|
2909
|
+
function readCachedMorphSnapshot(cache, cacheKey) {
|
|
2910
|
+
const cached = cache.get(cacheKey);
|
|
2911
|
+
if (cached === undefined) {
|
|
2912
|
+
return null;
|
|
2913
|
+
}
|
|
2914
|
+
cache.delete(cacheKey);
|
|
2915
|
+
cache.set(cacheKey, cached);
|
|
2916
|
+
return cached;
|
|
2917
|
+
}
|
|
2918
|
+
function rememberCachedMorphSnapshot(cache, cacheKey, snapshot) {
|
|
2919
|
+
cache.delete(cacheKey);
|
|
2920
|
+
cache.set(cacheKey, snapshot);
|
|
2921
|
+
if (cache.size > DOM_MEASUREMENT_SNAPSHOT_CACHE_LIMIT) {
|
|
2922
|
+
const oldest = cache.keys().next();
|
|
2923
|
+
if (!oldest.done) {
|
|
2924
|
+
cache.delete(oldest.value);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
function assertMeasurementLayer(layer, segments) {
|
|
2929
|
+
if (layer === null) {
|
|
2930
|
+
throw new Error("Torph measurement layer is missing.");
|
|
2931
|
+
}
|
|
2932
|
+
if (layer.children.length !== segments.length) {
|
|
2933
|
+
throw new Error(`Torph measurement layer is out of sync. Expected ${segments.length} glyph nodes, received ${layer.children.length}.`);
|
|
2934
|
+
}
|
|
2935
|
+
return layer;
|
|
2936
|
+
}
|
|
2937
|
+
function readMeasuredGlyphLayouts(layer, layerRect, segments) {
|
|
2938
|
+
const measuredGlyphs = [];
|
|
2939
|
+
const layerOffsetTop = layer.offsetTop;
|
|
2940
|
+
for (let index = 0;index < segments.length; index += 1) {
|
|
2941
|
+
const segment = segments[index];
|
|
2942
|
+
const child = layer.children[index];
|
|
2943
|
+
if (!(child instanceof HTMLElement)) {
|
|
2944
|
+
throw new Error(`Torph glyph node ${index} is not an HTMLElement.`);
|
|
2945
|
+
}
|
|
2946
|
+
const rect = child.getBoundingClientRect();
|
|
2947
|
+
measuredGlyphs.push({
|
|
2948
|
+
glyph: segment.glyph,
|
|
2949
|
+
key: segment.key,
|
|
2950
|
+
left: rect.left - layerRect.left,
|
|
2951
|
+
top: child.offsetTop - layerOffsetTop,
|
|
2952
|
+
width: rect.width
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
return measuredGlyphs;
|
|
2956
|
+
}
|
|
2957
|
+
function assignMeasuredGlyphLineIndices(measuredGlyphs) {
|
|
2958
|
+
const lineIndices = [];
|
|
2959
|
+
let lineCount = 0;
|
|
2960
|
+
let currentLineTop = null;
|
|
2961
|
+
for (const glyph of measuredGlyphs) {
|
|
2962
|
+
if (currentLineTop === null || Math.abs(glyph.top - currentLineTop) > MORPH.lineGroupingEpsilon) {
|
|
2963
|
+
currentLineTop = glyph.top;
|
|
2964
|
+
lineCount += 1;
|
|
2965
|
+
}
|
|
2966
|
+
lineIndices.push(lineCount - 1);
|
|
2967
|
+
}
|
|
2968
|
+
return {
|
|
2969
|
+
lineCount,
|
|
2970
|
+
lineIndices
|
|
2971
|
+
};
|
|
2972
|
+
}
|
|
2973
|
+
function measureMorphSnapshotFromLayer(text, renderText, segments, layer) {
|
|
2974
|
+
if (renderText.length === 0) {
|
|
2975
|
+
return {
|
|
2976
|
+
text,
|
|
2977
|
+
renderText,
|
|
2978
|
+
width: 0,
|
|
2979
|
+
height: 0,
|
|
2980
|
+
graphemes: []
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
const measurementLayer = assertMeasurementLayer(layer, segments);
|
|
2984
|
+
const layerRect = measurementLayer.getBoundingClientRect();
|
|
2985
|
+
const measuredGlyphs = readMeasuredGlyphLayouts(measurementLayer, layerRect, segments);
|
|
2986
|
+
const { lineCount, lineIndices } = assignMeasuredGlyphLineIndices(measuredGlyphs);
|
|
2987
|
+
let lineHeight = 0;
|
|
2988
|
+
if (lineCount !== 0) {
|
|
2989
|
+
lineHeight = layerRect.height / lineCount;
|
|
2990
|
+
}
|
|
2991
|
+
let width = 0;
|
|
2992
|
+
const graphemes = measuredGlyphs.map((glyph, index) => {
|
|
2993
|
+
const lineIndex = lineIndices[index];
|
|
2994
|
+
if (lineIndex === undefined) {
|
|
2995
|
+
throw new Error("Torph failed to assign a line index.");
|
|
2996
|
+
}
|
|
2997
|
+
width = Math.max(width, glyph.left + glyph.width);
|
|
2998
|
+
return {
|
|
2999
|
+
glyph: glyph.glyph,
|
|
3000
|
+
key: glyph.key,
|
|
3001
|
+
left: glyph.left,
|
|
3002
|
+
top: lineIndex * lineHeight,
|
|
3003
|
+
width: glyph.width,
|
|
3004
|
+
height: lineHeight
|
|
3005
|
+
};
|
|
3006
|
+
});
|
|
3007
|
+
return {
|
|
3008
|
+
text,
|
|
3009
|
+
renderText,
|
|
3010
|
+
width,
|
|
3011
|
+
height: layerRect.height,
|
|
3012
|
+
graphemes
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
3015
|
+
function measureMorphSnapshotWithDomService({
|
|
3016
|
+
root,
|
|
3017
|
+
layoutContext,
|
|
3018
|
+
text,
|
|
3019
|
+
renderText,
|
|
3020
|
+
segments,
|
|
3021
|
+
useContentInlineSize
|
|
3022
|
+
}) {
|
|
3023
|
+
if (renderText.length === 0) {
|
|
3024
|
+
return {
|
|
3025
|
+
text,
|
|
3026
|
+
renderText,
|
|
3027
|
+
width: 0,
|
|
3028
|
+
height: 0,
|
|
3029
|
+
graphemes: []
|
|
3030
|
+
};
|
|
3031
|
+
}
|
|
3032
|
+
const service = getDomMeasurementService();
|
|
3033
|
+
applyMeasurementHostStyle({
|
|
3034
|
+
host: service.host,
|
|
3035
|
+
root,
|
|
3036
|
+
layoutContext,
|
|
3037
|
+
useContentInlineSize
|
|
3038
|
+
});
|
|
3039
|
+
syncMeasurementGlyphNodes(service, segments);
|
|
3040
|
+
return measureMorphSnapshotFromLayer(text, renderText, segments, service.host);
|
|
3041
|
+
}
|
|
3042
|
+
function readRootOrigin(node) {
|
|
3043
|
+
const rect = node.getBoundingClientRect();
|
|
3044
|
+
return { left: rect.left, top: rect.top };
|
|
3045
|
+
}
|
|
3046
|
+
function getTrustedPretextMeasurementBackend(text, layoutContext) {
|
|
3047
|
+
const backend = getPretextMorphMeasurementBackend(text, layoutContext);
|
|
3048
|
+
if (backend !== "probe") {
|
|
3049
|
+
return backend;
|
|
3050
|
+
}
|
|
3051
|
+
const signature = getPretextMorphStyleSignature(layoutContext);
|
|
3052
|
+
if (signature === null) {
|
|
3053
|
+
return "dom";
|
|
3054
|
+
}
|
|
3055
|
+
const trusted = pretextMorphTrustCache.get(signature);
|
|
3056
|
+
if (trusted === undefined) {
|
|
3057
|
+
return "probe";
|
|
3058
|
+
}
|
|
3059
|
+
if (trusted) {
|
|
3060
|
+
return "pretext";
|
|
3061
|
+
}
|
|
3062
|
+
return "dom";
|
|
3063
|
+
}
|
|
3064
|
+
function shouldMeasureUsingContentInlineSize(layoutContext, layoutHint) {
|
|
3065
|
+
if (supportsIntrinsicWidthLock(layoutContext.display, layoutContext.parentDisplay)) {
|
|
3066
|
+
return true;
|
|
3067
|
+
}
|
|
3068
|
+
if (layoutContext.whiteSpace === "nowrap") {
|
|
3069
|
+
return true;
|
|
3070
|
+
}
|
|
3071
|
+
if (layoutHint === null || !isSingleLineSnapshot(layoutHint.snapshot)) {
|
|
3072
|
+
return false;
|
|
3073
|
+
}
|
|
3074
|
+
return nearlyEqual(layoutHint.layoutInlineSize, layoutHint.snapshot.width);
|
|
3075
|
+
}
|
|
3076
|
+
function createMorphMeasurementRequest({
|
|
3077
|
+
text,
|
|
3078
|
+
layoutContext,
|
|
3079
|
+
layoutHint
|
|
3080
|
+
}) {
|
|
3081
|
+
if (layoutContext === null) {
|
|
3082
|
+
return null;
|
|
3083
|
+
}
|
|
3084
|
+
const renderText = getPretextMorphRenderedText(text, layoutContext);
|
|
3085
|
+
const useContentInlineSize = shouldMeasureUsingContentInlineSize(layoutContext, layoutHint);
|
|
3086
|
+
const measurementBackend = getTrustedPretextMeasurementBackend(text, layoutContext);
|
|
3087
|
+
let segments = readCachedMorphSegments(renderText);
|
|
3088
|
+
if (measurementBackend === "pretext") {
|
|
3089
|
+
segments = EMPTY_SEGMENTS;
|
|
3090
|
+
}
|
|
3091
|
+
let domMeasurementKey = null;
|
|
3092
|
+
if (measurementBackend === "dom" && renderText.length > 0) {
|
|
3093
|
+
domMeasurementKey = getDomMeasurementRequestKey(text, renderText, layoutContext, useContentInlineSize);
|
|
3094
|
+
}
|
|
3095
|
+
return {
|
|
3096
|
+
text,
|
|
3097
|
+
renderText,
|
|
3098
|
+
segments,
|
|
3099
|
+
measurementBackend,
|
|
3100
|
+
useContentInlineSize,
|
|
3101
|
+
domMeasurementKey
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
function rememberPretextMeasurementTrust(layoutContext, trusted) {
|
|
3105
|
+
const signature = getPretextMorphStyleSignature(layoutContext);
|
|
3106
|
+
if (signature === null) {
|
|
3107
|
+
return;
|
|
3108
|
+
}
|
|
3109
|
+
pretextMorphTrustCache.set(signature, trusted);
|
|
3110
|
+
}
|
|
3111
|
+
function areSnapshotsEquivalentForPretextTrust(left, right) {
|
|
3112
|
+
if (left.renderText !== right.renderText || left.graphemes.length !== right.graphemes.length) {
|
|
3113
|
+
return false;
|
|
3114
|
+
}
|
|
3115
|
+
if (Math.abs(left.width - right.width) > MORPH.geometryEpsilon || Math.abs(left.height - right.height) > MORPH.geometryEpsilon) {
|
|
3116
|
+
return false;
|
|
3117
|
+
}
|
|
3118
|
+
for (let index = 0;index < left.graphemes.length; index += 1) {
|
|
3119
|
+
const from = left.graphemes[index];
|
|
3120
|
+
const to = right.graphemes[index];
|
|
3121
|
+
if (from.glyph !== to.glyph) {
|
|
3122
|
+
return false;
|
|
3123
|
+
}
|
|
3124
|
+
if (Math.abs(from.left - to.left) > MORPH.geometryEpsilon || Math.abs(from.top - to.top) > MORPH.geometryEpsilon || Math.abs(from.width - to.width) > MORPH.geometryEpsilon || Math.abs(from.height - to.height) > MORPH.geometryEpsilon) {
|
|
3125
|
+
return false;
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
return true;
|
|
3129
|
+
}
|
|
3130
|
+
function measureFromNodes({
|
|
3131
|
+
root,
|
|
3132
|
+
layoutContext,
|
|
3133
|
+
layoutHint,
|
|
3134
|
+
layer,
|
|
3135
|
+
measurementBackend,
|
|
3136
|
+
snapshotOverride,
|
|
3137
|
+
text,
|
|
3138
|
+
renderText,
|
|
3139
|
+
segments
|
|
3140
|
+
}) {
|
|
3141
|
+
const useContentInlineSize = shouldMeasureUsingContentInlineSize(layoutContext, layoutHint);
|
|
3142
|
+
let measurementLayoutContext = layoutContext;
|
|
3143
|
+
if (useContentInlineSize && layoutContext.whiteSpace !== "nowrap") {
|
|
3144
|
+
measurementLayoutContext = {
|
|
3145
|
+
...layoutContext,
|
|
3146
|
+
width: Number.MAX_SAFE_INTEGER / 4
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
const snapshot = snapshotOverride ?? (() => {
|
|
3150
|
+
let pretextSnapshot = null;
|
|
3151
|
+
if (measurementBackend !== "dom") {
|
|
3152
|
+
pretextSnapshot = measureMorphSnapshotWithPretext(text, measurementLayoutContext);
|
|
3153
|
+
}
|
|
3154
|
+
let domSnapshot = null;
|
|
3155
|
+
if (measurementBackend === "dom") {
|
|
3156
|
+
domSnapshot = measureMorphSnapshotFromLayer(text, renderText, segments, layer);
|
|
3157
|
+
} else if (measurementBackend !== "pretext") {
|
|
3158
|
+
domSnapshot = measureMorphSnapshotWithDomService({
|
|
3159
|
+
root,
|
|
3160
|
+
layoutContext,
|
|
3161
|
+
text,
|
|
3162
|
+
renderText,
|
|
3163
|
+
segments,
|
|
3164
|
+
useContentInlineSize
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
if (measurementBackend === "probe" && pretextSnapshot !== null && domSnapshot !== null) {
|
|
3168
|
+
const trusted = areSnapshotsEquivalentForPretextTrust(pretextSnapshot, domSnapshot);
|
|
3169
|
+
rememberPretextMeasurementTrust(layoutContext, trusted);
|
|
3170
|
+
if (trusted) {
|
|
3171
|
+
return pretextSnapshot;
|
|
3172
|
+
}
|
|
3173
|
+
return domSnapshot;
|
|
3174
|
+
}
|
|
3175
|
+
const resolvedSnapshot = pretextSnapshot ?? domSnapshot;
|
|
3176
|
+
if (resolvedSnapshot === null) {
|
|
3177
|
+
throw new Error("Torph failed to resolve a measurement snapshot.");
|
|
3178
|
+
}
|
|
3179
|
+
return resolvedSnapshot;
|
|
3180
|
+
})();
|
|
3181
|
+
let layoutInlineSize = layoutContext.width;
|
|
3182
|
+
if (useContentInlineSize) {
|
|
3183
|
+
layoutInlineSize = snapshot.width;
|
|
3184
|
+
}
|
|
3185
|
+
let reservedInlineSize = null;
|
|
3186
|
+
if (supportsIntrinsicWidthLock(layoutContext.display, layoutContext.parentDisplay)) {
|
|
3187
|
+
reservedInlineSize = snapshot.width;
|
|
3188
|
+
}
|
|
3189
|
+
return {
|
|
3190
|
+
snapshot,
|
|
3191
|
+
layoutInlineSize,
|
|
3192
|
+
reservedInlineSize,
|
|
3193
|
+
rootOrigin: readRootOrigin(root)
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
function pinMeasurementToCurrentOrigin(measurement, origin) {
|
|
3197
|
+
if (nearlyEqual(measurement.rootOrigin.left, origin.left) && nearlyEqual(measurement.rootOrigin.top, origin.top)) {
|
|
3198
|
+
return measurement;
|
|
3199
|
+
}
|
|
3200
|
+
return {
|
|
3201
|
+
snapshot: measurement.snapshot,
|
|
3202
|
+
layoutInlineSize: measurement.layoutInlineSize,
|
|
3203
|
+
reservedInlineSize: measurement.reservedInlineSize,
|
|
3204
|
+
rootOrigin: origin
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
function bucketByGlyph(graphemes) {
|
|
3208
|
+
const buckets = new Map;
|
|
3209
|
+
for (const grapheme of graphemes) {
|
|
3210
|
+
const bucket = buckets.get(grapheme.glyph);
|
|
3211
|
+
if (bucket) {
|
|
3212
|
+
bucket.push(grapheme);
|
|
3213
|
+
} else {
|
|
3214
|
+
buckets.set(grapheme.glyph, [grapheme]);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
return buckets;
|
|
3218
|
+
}
|
|
3219
|
+
function pairMorphCharacters(previous, next) {
|
|
3220
|
+
const previousBuckets = bucketByGlyph(previous);
|
|
3221
|
+
const nextBuckets = bucketByGlyph(next);
|
|
3222
|
+
const pairings = [];
|
|
3223
|
+
for (const [glyph, previousItems] of previousBuckets) {
|
|
3224
|
+
const nextItems = nextBuckets.get(glyph) ?? [];
|
|
3225
|
+
const shared = Math.min(previousItems.length, nextItems.length);
|
|
3226
|
+
for (let index = 0;index < shared; index += 1) {
|
|
3227
|
+
pairings.push({
|
|
3228
|
+
kind: "move",
|
|
3229
|
+
from: previousItems[index],
|
|
3230
|
+
to: nextItems[index]
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
for (let index = shared;index < previousItems.length; index += 1) {
|
|
3234
|
+
pairings.push({
|
|
3235
|
+
kind: "exit",
|
|
3236
|
+
from: previousItems[index]
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
for (const [glyph, nextItems] of nextBuckets) {
|
|
3241
|
+
const previousItems = previousBuckets.get(glyph) ?? [];
|
|
3242
|
+
const shared = Math.min(previousItems.length, nextItems.length);
|
|
3243
|
+
for (let index = shared;index < nextItems.length; index += 1) {
|
|
3244
|
+
pairings.push({
|
|
3245
|
+
kind: "enter",
|
|
3246
|
+
to: nextItems[index]
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
return pairings;
|
|
3251
|
+
}
|
|
3252
|
+
function resolveMorphFrameBounds(previous, next) {
|
|
3253
|
+
return {
|
|
3254
|
+
width: Math.max(previous.width, next.width),
|
|
3255
|
+
height: Math.max(previous.height, next.height)
|
|
3256
|
+
};
|
|
3257
|
+
}
|
|
3258
|
+
function buildMorphVisualBridge(previous, next) {
|
|
3259
|
+
return {
|
|
3260
|
+
offsetX: previous.rootOrigin.left - next.rootOrigin.left,
|
|
3261
|
+
offsetY: previous.rootOrigin.top - next.rootOrigin.top
|
|
3262
|
+
};
|
|
3263
|
+
}
|
|
3264
|
+
function buildMorphPlan(previous, next, visualBridge = ZERO_BRIDGE) {
|
|
3265
|
+
const pairings = pairMorphCharacters(previous.snapshot.graphemes, next.snapshot.graphemes);
|
|
3266
|
+
const movesByDestinationKey = new Map;
|
|
3267
|
+
const exitItems = [];
|
|
3268
|
+
for (const pairing of pairings) {
|
|
3269
|
+
if (pairing.kind === "move") {
|
|
3270
|
+
movesByDestinationKey.set(pairing.to.key, pairing);
|
|
3271
|
+
continue;
|
|
3272
|
+
}
|
|
3273
|
+
if (pairing.kind === "exit") {
|
|
3274
|
+
exitItems.push(pairing.from);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
const frame = resolveMorphFrameBounds(previous.snapshot, next.snapshot);
|
|
3278
|
+
return {
|
|
3279
|
+
frameWidth: frame.width,
|
|
3280
|
+
frameHeight: frame.height,
|
|
3281
|
+
layoutInlineSizeFrom: previous.layoutInlineSize,
|
|
3282
|
+
layoutInlineSizeTo: next.layoutInlineSize,
|
|
3283
|
+
visualBridge,
|
|
3284
|
+
liveItems: next.snapshot.graphemes.map((grapheme) => {
|
|
3285
|
+
const move = movesByDestinationKey.get(grapheme.key);
|
|
3286
|
+
if (move) {
|
|
3287
|
+
return {
|
|
3288
|
+
...grapheme,
|
|
3289
|
+
kind: "move",
|
|
3290
|
+
fromLeft: move.from.left,
|
|
3291
|
+
fromTop: move.from.top
|
|
3292
|
+
};
|
|
3293
|
+
}
|
|
3294
|
+
return {
|
|
3295
|
+
...grapheme,
|
|
3296
|
+
kind: "enter",
|
|
3297
|
+
fromLeft: null,
|
|
3298
|
+
fromTop: null
|
|
3299
|
+
};
|
|
3300
|
+
}),
|
|
3301
|
+
exitItems
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
function nearlyEqual(a, b, epsilon = MORPH.geometryEpsilon) {
|
|
3305
|
+
return Math.abs(a - b) <= epsilon;
|
|
3306
|
+
}
|
|
3307
|
+
function sameSnapshot(a, b) {
|
|
3308
|
+
if (a === b) {
|
|
3309
|
+
return true;
|
|
3310
|
+
}
|
|
3311
|
+
if (a.text !== b.text || a.renderText !== b.renderText || a.graphemes.length !== b.graphemes.length) {
|
|
3312
|
+
return false;
|
|
3313
|
+
}
|
|
3314
|
+
if (!nearlyEqual(a.width, b.width) || !nearlyEqual(a.height, b.height)) {
|
|
3315
|
+
return false;
|
|
3316
|
+
}
|
|
3317
|
+
for (let index = 0;index < a.graphemes.length; index += 1) {
|
|
3318
|
+
const left = a.graphemes[index];
|
|
3319
|
+
const right = b.graphemes[index];
|
|
3320
|
+
if (left.glyph !== right.glyph || left.key !== right.key) {
|
|
3321
|
+
return false;
|
|
3322
|
+
}
|
|
3323
|
+
if (!nearlyEqual(left.left, right.left) || !nearlyEqual(left.top, right.top) || !nearlyEqual(left.width, right.width) || !nearlyEqual(left.height, right.height)) {
|
|
3324
|
+
return false;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
return true;
|
|
3328
|
+
}
|
|
3329
|
+
function sameMeasurement(a, b) {
|
|
3330
|
+
if (a === b) {
|
|
3331
|
+
return true;
|
|
3332
|
+
}
|
|
3333
|
+
return sameSnapshot(a.snapshot, b.snapshot) && nearlyEqual(a.layoutInlineSize, b.layoutInlineSize) && (a.reservedInlineSize === null && b.reservedInlineSize === null || a.reservedInlineSize !== null && b.reservedInlineSize !== null && nearlyEqual(a.reservedInlineSize, b.reservedInlineSize)) && nearlyEqual(a.rootOrigin.left, b.rootOrigin.left) && nearlyEqual(a.rootOrigin.top, b.rootOrigin.top);
|
|
3334
|
+
}
|
|
3335
|
+
function refreshAnimatingTarget(activeTarget, measurement) {
|
|
3336
|
+
if (sameMeasurement(activeTarget, measurement)) {
|
|
3337
|
+
return activeTarget;
|
|
3338
|
+
}
|
|
3339
|
+
return {
|
|
3340
|
+
snapshot: activeTarget.snapshot,
|
|
3341
|
+
layoutInlineSize: measurement.layoutInlineSize,
|
|
3342
|
+
reservedInlineSize: measurement.reservedInlineSize,
|
|
3343
|
+
rootOrigin: measurement.rootOrigin
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
function reuseCommittedMeasurement(committed, measurement) {
|
|
3347
|
+
if (sameMeasurement(committed, measurement)) {
|
|
3348
|
+
return committed;
|
|
3349
|
+
}
|
|
3350
|
+
return measurement;
|
|
3351
|
+
}
|
|
3352
|
+
function createStaticState(measurement) {
|
|
3353
|
+
return {
|
|
3354
|
+
stage: "idle",
|
|
3355
|
+
measurement,
|
|
3356
|
+
plan: null
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
function areFontsReady() {
|
|
3360
|
+
return document.fonts.status === "loaded";
|
|
3361
|
+
}
|
|
3362
|
+
function cancelTimeline(timeline) {
|
|
3363
|
+
if (timeline.prepareFrame !== null) {
|
|
3364
|
+
cancelAnimationFrame(timeline.prepareFrame);
|
|
3365
|
+
timeline.prepareFrame = null;
|
|
3366
|
+
}
|
|
3367
|
+
if (timeline.animateFrame !== null) {
|
|
3368
|
+
cancelAnimationFrame(timeline.animateFrame);
|
|
3369
|
+
timeline.animateFrame = null;
|
|
3370
|
+
}
|
|
3371
|
+
if (timeline.finalizeTimer !== null) {
|
|
3372
|
+
window.clearTimeout(timeline.finalizeTimer);
|
|
3373
|
+
timeline.finalizeTimer = null;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
function resetMorph(session, timeline, setState) {
|
|
3377
|
+
cancelTimeline(timeline);
|
|
3378
|
+
session.committed = null;
|
|
3379
|
+
session.target = null;
|
|
3380
|
+
session.animating = false;
|
|
3381
|
+
setState(EMPTY_STATE);
|
|
3382
|
+
}
|
|
3383
|
+
function commitStaticMeasurement(session, measurement, setState) {
|
|
3384
|
+
session.committed = measurement;
|
|
3385
|
+
session.target = null;
|
|
3386
|
+
session.animating = false;
|
|
3387
|
+
setState(createStaticState(measurement));
|
|
3388
|
+
}
|
|
3389
|
+
function scheduleMorphTimeline({
|
|
3390
|
+
session,
|
|
3391
|
+
timeline,
|
|
3392
|
+
measurement,
|
|
3393
|
+
plan,
|
|
3394
|
+
setState
|
|
3395
|
+
}) {
|
|
3396
|
+
timeline.prepareFrame = requestAnimationFrame(() => {
|
|
3397
|
+
timeline.prepareFrame = null;
|
|
3398
|
+
setState((current) => {
|
|
3399
|
+
if (current.measurement !== measurement || current.plan !== plan) {
|
|
3400
|
+
return current;
|
|
3401
|
+
}
|
|
3402
|
+
return {
|
|
3403
|
+
stage: "animate",
|
|
3404
|
+
measurement,
|
|
3405
|
+
plan
|
|
3406
|
+
};
|
|
3407
|
+
});
|
|
3408
|
+
timeline.animateFrame = requestAnimationFrame(() => {
|
|
3409
|
+
timeline.animateFrame = null;
|
|
3410
|
+
timeline.finalizeTimer = window.setTimeout(() => {
|
|
3411
|
+
timeline.finalizeTimer = null;
|
|
3412
|
+
commitStaticMeasurement(session, session.target ?? measurement, setState);
|
|
3413
|
+
}, MORPH.durationMs);
|
|
3414
|
+
});
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
function startMorph({
|
|
3418
|
+
nextMeasurement,
|
|
3419
|
+
session,
|
|
3420
|
+
timeline,
|
|
3421
|
+
setState
|
|
3422
|
+
}) {
|
|
3423
|
+
let sourceMeasurement = session.committed;
|
|
3424
|
+
if (session.animating && session.target) {
|
|
3425
|
+
sourceMeasurement = session.target;
|
|
3426
|
+
}
|
|
3427
|
+
if (sourceMeasurement === null) {
|
|
3428
|
+
commitStaticMeasurement(session, nextMeasurement, setState);
|
|
3429
|
+
return;
|
|
3430
|
+
}
|
|
3431
|
+
const previousMeasurement = pinMeasurementToCurrentOrigin(sourceMeasurement, nextMeasurement.rootOrigin);
|
|
3432
|
+
const visualBridge = buildMorphVisualBridge(previousMeasurement, nextMeasurement);
|
|
3433
|
+
const plan = buildMorphPlan(previousMeasurement, nextMeasurement, visualBridge);
|
|
3434
|
+
session.target = nextMeasurement;
|
|
3435
|
+
session.animating = true;
|
|
3436
|
+
setState({
|
|
3437
|
+
stage: "prepare",
|
|
3438
|
+
measurement: nextMeasurement,
|
|
3439
|
+
plan
|
|
3440
|
+
});
|
|
3441
|
+
scheduleMorphTimeline({
|
|
3442
|
+
session,
|
|
3443
|
+
timeline,
|
|
3444
|
+
measurement: nextMeasurement,
|
|
3445
|
+
plan,
|
|
3446
|
+
setState
|
|
3447
|
+
});
|
|
3448
|
+
}
|
|
3449
|
+
function reconcileMorphChange({
|
|
3450
|
+
root,
|
|
3451
|
+
measurementLayer,
|
|
3452
|
+
measurementBackend,
|
|
3453
|
+
snapshotOverride,
|
|
3454
|
+
text,
|
|
3455
|
+
renderText,
|
|
3456
|
+
segments,
|
|
3457
|
+
layoutContext,
|
|
3458
|
+
session,
|
|
3459
|
+
timeline,
|
|
3460
|
+
setState
|
|
3461
|
+
}) {
|
|
3462
|
+
if (root === null || layoutContext === null) {
|
|
3463
|
+
resetMorph(session, timeline, setState);
|
|
3464
|
+
return null;
|
|
3465
|
+
}
|
|
3466
|
+
if (measurementBackend === null) {
|
|
3467
|
+
throw new Error("Torph measurement backend is missing.");
|
|
3468
|
+
}
|
|
3469
|
+
let layoutHint = session.committed;
|
|
3470
|
+
if (session.animating && session.target !== null) {
|
|
3471
|
+
layoutHint = session.target;
|
|
3472
|
+
}
|
|
3473
|
+
const nextMeasurement = measureFromNodes({
|
|
3474
|
+
root,
|
|
3475
|
+
layoutContext,
|
|
3476
|
+
layoutHint,
|
|
3477
|
+
layer: measurementLayer,
|
|
3478
|
+
measurementBackend,
|
|
3479
|
+
snapshotOverride,
|
|
3480
|
+
text,
|
|
3481
|
+
renderText,
|
|
3482
|
+
segments
|
|
3483
|
+
});
|
|
3484
|
+
if (session.animating && session.target !== null) {
|
|
3485
|
+
if (nextMeasurement.snapshot.renderText === session.target.snapshot.renderText) {
|
|
3486
|
+
session.target = refreshAnimatingTarget(session.target, nextMeasurement);
|
|
3487
|
+
return nextMeasurement;
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
cancelTimeline(timeline);
|
|
3491
|
+
if (session.committed === null) {
|
|
3492
|
+
commitStaticMeasurement(session, nextMeasurement, setState);
|
|
3493
|
+
return nextMeasurement;
|
|
3494
|
+
}
|
|
3495
|
+
if (!areFontsReady()) {
|
|
3496
|
+
commitStaticMeasurement(session, nextMeasurement, setState);
|
|
3497
|
+
return nextMeasurement;
|
|
3498
|
+
}
|
|
3499
|
+
if (session.committed.snapshot.renderText === nextMeasurement.snapshot.renderText) {
|
|
3500
|
+
commitStaticMeasurement(session, reuseCommittedMeasurement(session.committed, nextMeasurement), setState);
|
|
3501
|
+
return nextMeasurement;
|
|
3502
|
+
}
|
|
3503
|
+
startMorph({
|
|
3504
|
+
nextMeasurement,
|
|
3505
|
+
session,
|
|
3506
|
+
timeline,
|
|
3507
|
+
setState
|
|
3508
|
+
});
|
|
3509
|
+
return nextMeasurement;
|
|
3510
|
+
}
|
|
3511
|
+
function syncCommittedRootOriginWhenIdle({
|
|
3512
|
+
root,
|
|
3513
|
+
layoutContext,
|
|
3514
|
+
state,
|
|
3515
|
+
session
|
|
3516
|
+
}) {
|
|
3517
|
+
if (root === null || layoutContext === null) {
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
if (state.stage !== "idle" || state.measurement === null) {
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
const nextRootOrigin = readRootOrigin(root);
|
|
3524
|
+
const committedMeasurement = state.measurement;
|
|
3525
|
+
if (nearlyEqual(committedMeasurement.rootOrigin.left, nextRootOrigin.left) && nearlyEqual(committedMeasurement.rootOrigin.top, nextRootOrigin.top)) {
|
|
3526
|
+
session.committed = committedMeasurement;
|
|
3527
|
+
return;
|
|
3528
|
+
}
|
|
3529
|
+
session.committed = {
|
|
3530
|
+
snapshot: committedMeasurement.snapshot,
|
|
3531
|
+
layoutInlineSize: committedMeasurement.layoutInlineSize,
|
|
3532
|
+
reservedInlineSize: committedMeasurement.reservedInlineSize,
|
|
3533
|
+
rootOrigin: nextRootOrigin
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
function getFadeDuration(fraction) {
|
|
3537
|
+
return Math.min(MORPH.durationMs * fraction, MORPH.maxFadeMs);
|
|
3538
|
+
}
|
|
3539
|
+
function getLiveTransform(item, stage, visualBridge) {
|
|
3540
|
+
if (stage !== "prepare") {
|
|
3541
|
+
return "translate(0px, 0px)";
|
|
3542
|
+
}
|
|
3543
|
+
if (item.kind === "move") {
|
|
3544
|
+
return `translate(${(item.fromLeft ?? item.left) - item.left + visualBridge.offsetX}px, ${(item.fromTop ?? item.top) - item.top + visualBridge.offsetY}px)`;
|
|
3545
|
+
}
|
|
3546
|
+
return `translate(${visualBridge.offsetX}px, ${visualBridge.offsetY}px)`;
|
|
3547
|
+
}
|
|
3548
|
+
function getLiveOpacity(item, stage) {
|
|
3549
|
+
if (stage === "prepare" && item.kind === "enter") {
|
|
3550
|
+
return 0;
|
|
3551
|
+
}
|
|
3552
|
+
return 1;
|
|
3553
|
+
}
|
|
3554
|
+
function getLiveTransition(item, stage) {
|
|
3555
|
+
if (stage !== "animate") {
|
|
3556
|
+
return;
|
|
3557
|
+
}
|
|
3558
|
+
if (item.kind === "enter") {
|
|
3559
|
+
return `opacity ${getFadeDuration(0.5)}ms linear ${getFadeDuration(0.25)}ms`;
|
|
3560
|
+
}
|
|
3561
|
+
return `transform ${MORPH.durationMs}ms ${MORPH.ease}, opacity ${getFadeDuration(0.25)}ms linear`;
|
|
3562
|
+
}
|
|
3563
|
+
function getExitOpacity(stage) {
|
|
3564
|
+
if (stage === "animate") {
|
|
3565
|
+
return 0;
|
|
3566
|
+
}
|
|
3567
|
+
return 1;
|
|
3568
|
+
}
|
|
3569
|
+
function getExitTransform(visualBridge) {
|
|
3570
|
+
return `translate(${visualBridge.offsetX}px, ${visualBridge.offsetY}px)`;
|
|
3571
|
+
}
|
|
3572
|
+
function getExitTransition(stage) {
|
|
3573
|
+
if (stage !== "animate") {
|
|
3574
|
+
return;
|
|
3575
|
+
}
|
|
3576
|
+
return `transform ${MORPH.durationMs}ms ${MORPH.ease}, opacity ${getFadeDuration(0.25)}ms linear`;
|
|
3577
|
+
}
|
|
3578
|
+
function supportsIntrinsicWidthLock(display, parentDisplay) {
|
|
3579
|
+
const parentNeedsReservation = parentDisplay === "flex" || parentDisplay === "inline-flex" || parentDisplay === "grid" || parentDisplay === "inline-grid";
|
|
3580
|
+
return display === "inline" || display === "inline-block" || display === "inline-flex" || display === "inline-grid" || parentNeedsReservation;
|
|
3581
|
+
}
|
|
3582
|
+
function getRootDisplay(layoutContext) {
|
|
3583
|
+
if (layoutContext === null) {
|
|
3584
|
+
return "grid";
|
|
3585
|
+
}
|
|
3586
|
+
if (supportsIntrinsicWidthLock(layoutContext.display, layoutContext.parentDisplay)) {
|
|
3587
|
+
return "inline-grid";
|
|
3588
|
+
}
|
|
3589
|
+
return "grid";
|
|
3590
|
+
}
|
|
3591
|
+
function getRootStyle(stage, plan, measurement, layoutContext) {
|
|
3592
|
+
let width = measurement?.reservedInlineSize ?? undefined;
|
|
3593
|
+
if (plan !== null) {
|
|
3594
|
+
width = plan.layoutInlineSizeTo;
|
|
3595
|
+
if (stage === "prepare") {
|
|
3596
|
+
width = plan.layoutInlineSizeFrom;
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
const height = plan?.frameHeight ?? measurement?.snapshot.height;
|
|
3600
|
+
const shouldTransitionWidth = stage === "animate" && plan !== null && !nearlyEqual(plan.layoutInlineSizeFrom, plan.layoutInlineSizeTo);
|
|
3601
|
+
const style = {
|
|
3602
|
+
position: "relative",
|
|
3603
|
+
display: getRootDisplay(layoutContext)
|
|
3604
|
+
};
|
|
3605
|
+
if (width !== undefined) {
|
|
3606
|
+
style.width = width;
|
|
3607
|
+
}
|
|
3608
|
+
if (height !== undefined) {
|
|
3609
|
+
style.height = height;
|
|
3610
|
+
}
|
|
3611
|
+
if (shouldTransitionWidth) {
|
|
3612
|
+
style.transition = `width ${MORPH.durationMs}ms ${MORPH.ease}`;
|
|
3613
|
+
}
|
|
3614
|
+
return style;
|
|
3615
|
+
}
|
|
3616
|
+
function getMeasurementLayerStyle(layoutContext, useContentInlineSize = false) {
|
|
3617
|
+
const intrinsicWidthLock = layoutContext !== null && (useContentInlineSize || supportsIntrinsicWidthLock(layoutContext.display, layoutContext.parentDisplay));
|
|
3618
|
+
if (!intrinsicWidthLock) {
|
|
3619
|
+
return MEASUREMENT_LAYER_STYLE;
|
|
3620
|
+
}
|
|
3621
|
+
return {
|
|
3622
|
+
...MEASUREMENT_LAYER_STYLE,
|
|
3623
|
+
right: "auto",
|
|
3624
|
+
width: "max-content"
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
function resolveFlowText(committedMeasurement, stateMeasurement, text) {
|
|
3628
|
+
return committedMeasurement?.snapshot.text ?? stateMeasurement?.snapshot.text ?? text;
|
|
3629
|
+
}
|
|
3630
|
+
function resolveVisibleFlowText(pendingBootstrapText, committedMeasurement, stateMeasurement, text) {
|
|
3631
|
+
return pendingBootstrapText ?? resolveFlowText(committedMeasurement, stateMeasurement, text);
|
|
3632
|
+
}
|
|
3633
|
+
function resolveActivationBootstrapText(isActivated, previousText, nextText) {
|
|
3634
|
+
if (!isActivated && nextText !== previousText) {
|
|
3635
|
+
return previousText;
|
|
3636
|
+
}
|
|
3637
|
+
return null;
|
|
3638
|
+
}
|
|
3639
|
+
function getOverlayStyle(plan) {
|
|
3640
|
+
return {
|
|
3641
|
+
...OVERLAY_STYLE,
|
|
3642
|
+
right: "auto",
|
|
3643
|
+
bottom: "auto",
|
|
3644
|
+
width: plan.frameWidth,
|
|
3645
|
+
height: plan.frameHeight
|
|
3646
|
+
};
|
|
3647
|
+
}
|
|
3648
|
+
function getFallbackTextStyle(shouldRenderOverlay) {
|
|
3649
|
+
if (!shouldRenderOverlay) {
|
|
3650
|
+
return FALLBACK_TEXT_STYLE;
|
|
3651
|
+
}
|
|
3652
|
+
return {
|
|
3653
|
+
...FALLBACK_TEXT_STYLE,
|
|
3654
|
+
visibility: "hidden",
|
|
3655
|
+
pointerEvents: "none"
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3658
|
+
function getLiveGlyphStyle(item, stage, visualBridge) {
|
|
3659
|
+
return {
|
|
3660
|
+
...ABSOLUTE_GLYPH_STYLE,
|
|
3661
|
+
left: item.left,
|
|
3662
|
+
top: item.top,
|
|
3663
|
+
width: item.width,
|
|
3664
|
+
height: item.height,
|
|
3665
|
+
lineHeight: `${item.height}px`,
|
|
3666
|
+
opacity: getLiveOpacity(item, stage),
|
|
3667
|
+
transform: getLiveTransform(item, stage, visualBridge),
|
|
3668
|
+
transition: getLiveTransition(item, stage)
|
|
3669
|
+
};
|
|
3670
|
+
}
|
|
3671
|
+
function getExitGlyphStyle(item, stage, visualBridge) {
|
|
3672
|
+
return {
|
|
3673
|
+
...ABSOLUTE_GLYPH_STYLE,
|
|
3674
|
+
left: item.left,
|
|
3675
|
+
top: item.top,
|
|
3676
|
+
width: item.width,
|
|
3677
|
+
height: item.height,
|
|
3678
|
+
lineHeight: `${item.height}px`,
|
|
3679
|
+
opacity: getExitOpacity(stage),
|
|
3680
|
+
transform: getExitTransform(visualBridge),
|
|
3681
|
+
transition: getExitTransition(stage)
|
|
3682
|
+
};
|
|
3683
|
+
}
|
|
3684
|
+
function MorphOverlay({ stage, plan }) {
|
|
3685
|
+
let exitItems = [];
|
|
3686
|
+
if (stage !== "idle") {
|
|
3687
|
+
exitItems = plan.exitItems;
|
|
3688
|
+
}
|
|
3689
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
3690
|
+
"aria-hidden": "true",
|
|
3691
|
+
style: getOverlayStyle(plan),
|
|
3692
|
+
children: [
|
|
3693
|
+
exitItems.map((item) => /* @__PURE__ */ jsxDEV("span", {
|
|
3694
|
+
style: getExitGlyphStyle(item, stage, plan.visualBridge),
|
|
3695
|
+
children: item.glyph
|
|
3696
|
+
}, `exit-${item.key}`, false, undefined, this)),
|
|
3697
|
+
plan.liveItems.map((item) => /* @__PURE__ */ jsxDEV("span", {
|
|
3698
|
+
style: getLiveGlyphStyle(item, stage, plan.visualBridge),
|
|
3699
|
+
children: item.glyph
|
|
3700
|
+
}, item.key, false, undefined, this))
|
|
3701
|
+
]
|
|
3702
|
+
}, undefined, true, undefined, this);
|
|
3703
|
+
}
|
|
3704
|
+
function MeasurementLayer({
|
|
3705
|
+
layerRef,
|
|
3706
|
+
layoutContext,
|
|
3707
|
+
text,
|
|
3708
|
+
segments,
|
|
3709
|
+
useContentInlineSize
|
|
3710
|
+
}) {
|
|
3711
|
+
let glyphs = segments;
|
|
3712
|
+
if (text.length === 0) {
|
|
3713
|
+
glyphs = EMPTY_SEGMENTS;
|
|
3714
|
+
}
|
|
3715
|
+
return /* @__PURE__ */ jsxDEV("span", {
|
|
3716
|
+
ref: layerRef,
|
|
3717
|
+
"aria-hidden": "true",
|
|
3718
|
+
style: getMeasurementLayerStyle(layoutContext, useContentInlineSize),
|
|
3719
|
+
children: glyphs.map((segment) => /* @__PURE__ */ jsxDEV("span", {
|
|
3720
|
+
"data-morph-key": segment.key,
|
|
3721
|
+
style: MEASUREMENT_GLYPH_STYLE,
|
|
3722
|
+
children: segment.glyph
|
|
3723
|
+
}, segment.key, false, undefined, this))
|
|
3724
|
+
}, undefined, false, undefined, this);
|
|
3725
|
+
}
|
|
3726
|
+
function useMorphTransition(text, className, bootstrapText = null) {
|
|
3727
|
+
const { ref, layoutContext } = useObservedLayoutContext([className]);
|
|
3728
|
+
const measurementLayerRef = useRef(null);
|
|
3729
|
+
const bootstrapMeasurementLayerRef = useRef(null);
|
|
3730
|
+
const completedDomMeasurementKeyRef = useRef(null);
|
|
3731
|
+
const domMeasurementSnapshotCacheRef = useRef(new Map);
|
|
3732
|
+
const pendingBootstrapTextRef = useRef(bootstrapText);
|
|
3733
|
+
const sessionRef = useRef({ ...EMPTY_SESSION });
|
|
3734
|
+
const timelineRef = useRef({ ...EMPTY_TIMELINE });
|
|
3735
|
+
const [domMeasurementRequestKey, setDomMeasurementRequestKey] = useState(null);
|
|
3736
|
+
const [state, setState] = useState(EMPTY_STATE);
|
|
3737
|
+
if (pendingBootstrapTextRef.current === null && bootstrapText !== null && bootstrapText !== text && sessionRef.current.committed === null) {
|
|
3738
|
+
pendingBootstrapTextRef.current = bootstrapText;
|
|
3739
|
+
}
|
|
3740
|
+
let measurementHint = sessionRef.current.committed;
|
|
3741
|
+
if (sessionRef.current.animating) {
|
|
3742
|
+
measurementHint = sessionRef.current.target ?? sessionRef.current.committed;
|
|
3743
|
+
}
|
|
3744
|
+
const measurementRequest = useMemo(() => createMorphMeasurementRequest({
|
|
3745
|
+
text,
|
|
3746
|
+
layoutContext,
|
|
3747
|
+
layoutHint: measurementHint
|
|
3748
|
+
}), [text, layoutContext, measurementHint]);
|
|
3749
|
+
let pendingBootstrapText = null;
|
|
3750
|
+
if (sessionRef.current.committed === null) {
|
|
3751
|
+
pendingBootstrapText = pendingBootstrapTextRef.current;
|
|
3752
|
+
}
|
|
3753
|
+
const bootstrapMeasurementRequest = useMemo(() => {
|
|
3754
|
+
if (pendingBootstrapText === null) {
|
|
3755
|
+
return null;
|
|
3756
|
+
}
|
|
3757
|
+
return createMorphMeasurementRequest({
|
|
3758
|
+
text: pendingBootstrapText,
|
|
3759
|
+
layoutContext,
|
|
3760
|
+
layoutHint: null
|
|
3761
|
+
});
|
|
3762
|
+
}, [pendingBootstrapText, layoutContext]);
|
|
3763
|
+
const renderText = measurementRequest?.renderText ?? text;
|
|
3764
|
+
const useContentInlineSize = measurementRequest?.useContentInlineSize ?? false;
|
|
3765
|
+
const measurementBackend = measurementRequest?.measurementBackend ?? null;
|
|
3766
|
+
const segments = measurementRequest?.segments ?? EMPTY_SEGMENTS;
|
|
3767
|
+
const domMeasurementKey = measurementRequest?.domMeasurementKey ?? null;
|
|
3768
|
+
const shouldRenderBootstrapSourceMeasurementLayer = bootstrapMeasurementRequest?.measurementBackend === "dom" && bootstrapMeasurementRequest.renderText.length > 0;
|
|
3769
|
+
useLayoutEffect(() => {
|
|
3770
|
+
if (ref.current === null || layoutContext === null) {
|
|
3771
|
+
completedDomMeasurementKeyRef.current = null;
|
|
3772
|
+
if (domMeasurementRequestKey !== null) {
|
|
3773
|
+
setDomMeasurementRequestKey(null);
|
|
3774
|
+
}
|
|
3775
|
+
reconcileMorphChange({
|
|
3776
|
+
root: ref.current,
|
|
3777
|
+
measurementLayer: measurementLayerRef.current,
|
|
3778
|
+
measurementBackend,
|
|
3779
|
+
snapshotOverride: null,
|
|
3780
|
+
text,
|
|
3781
|
+
renderText,
|
|
3782
|
+
segments,
|
|
3783
|
+
layoutContext,
|
|
3784
|
+
session: sessionRef.current,
|
|
3785
|
+
timeline: timelineRef.current,
|
|
3786
|
+
setState
|
|
3787
|
+
});
|
|
3788
|
+
return;
|
|
3789
|
+
}
|
|
3790
|
+
if (pendingBootstrapText !== null && pendingBootstrapText !== text && bootstrapMeasurementRequest !== null && measurementRequest !== null) {
|
|
3791
|
+
let bootstrapDomSnapshot = null;
|
|
3792
|
+
if (bootstrapMeasurementRequest.domMeasurementKey !== null) {
|
|
3793
|
+
bootstrapDomSnapshot = readCachedMorphSnapshot(domMeasurementSnapshotCacheRef.current, bootstrapMeasurementRequest.domMeasurementKey);
|
|
3794
|
+
}
|
|
3795
|
+
if (bootstrapMeasurementRequest.domMeasurementKey !== null && bootstrapDomSnapshot === null && bootstrapMeasurementLayerRef.current === null) {
|
|
3796
|
+
return;
|
|
3797
|
+
}
|
|
3798
|
+
const bootstrapMeasurement = measureFromNodes({
|
|
3799
|
+
root: ref.current,
|
|
3800
|
+
layoutContext,
|
|
3801
|
+
layoutHint: null,
|
|
3802
|
+
layer: bootstrapMeasurementLayerRef.current,
|
|
3803
|
+
measurementBackend: bootstrapMeasurementRequest.measurementBackend,
|
|
3804
|
+
snapshotOverride: bootstrapDomSnapshot,
|
|
3805
|
+
text: bootstrapMeasurementRequest.text,
|
|
3806
|
+
renderText: bootstrapMeasurementRequest.renderText,
|
|
3807
|
+
segments: bootstrapMeasurementRequest.segments
|
|
3808
|
+
});
|
|
3809
|
+
if (bootstrapMeasurementRequest.domMeasurementKey !== null && bootstrapDomSnapshot === null) {
|
|
3810
|
+
rememberCachedMorphSnapshot(domMeasurementSnapshotCacheRef.current, bootstrapMeasurementRequest.domMeasurementKey, bootstrapMeasurement.snapshot);
|
|
3811
|
+
}
|
|
3812
|
+
completedDomMeasurementKeyRef.current = null;
|
|
3813
|
+
pendingBootstrapTextRef.current = null;
|
|
3814
|
+
if (domMeasurementRequestKey !== null) {
|
|
3815
|
+
setDomMeasurementRequestKey(null);
|
|
3816
|
+
}
|
|
3817
|
+
commitStaticMeasurement(sessionRef.current, bootstrapMeasurement, setState);
|
|
3818
|
+
return;
|
|
3819
|
+
}
|
|
3820
|
+
if (domMeasurementKey !== null) {
|
|
3821
|
+
const cachedSnapshot = readCachedMorphSnapshot(domMeasurementSnapshotCacheRef.current, domMeasurementKey);
|
|
3822
|
+
if (cachedSnapshot !== null) {
|
|
3823
|
+
completedDomMeasurementKeyRef.current = domMeasurementKey;
|
|
3824
|
+
if (domMeasurementRequestKey !== null) {
|
|
3825
|
+
setDomMeasurementRequestKey(null);
|
|
3826
|
+
}
|
|
3827
|
+
reconcileMorphChange({
|
|
3828
|
+
root: ref.current,
|
|
3829
|
+
measurementLayer: null,
|
|
3830
|
+
measurementBackend,
|
|
3831
|
+
snapshotOverride: cachedSnapshot,
|
|
3832
|
+
text,
|
|
3833
|
+
renderText,
|
|
3834
|
+
segments,
|
|
3835
|
+
layoutContext,
|
|
3836
|
+
session: sessionRef.current,
|
|
3837
|
+
timeline: timelineRef.current,
|
|
3838
|
+
setState
|
|
3839
|
+
});
|
|
3840
|
+
return;
|
|
3841
|
+
}
|
|
3842
|
+
if (completedDomMeasurementKeyRef.current !== domMeasurementKey) {
|
|
3843
|
+
if (domMeasurementRequestKey !== domMeasurementKey) {
|
|
3844
|
+
setDomMeasurementRequestKey(domMeasurementKey);
|
|
3845
|
+
return;
|
|
3846
|
+
}
|
|
3847
|
+
if (measurementLayerRef.current === null) {
|
|
3848
|
+
return;
|
|
3849
|
+
}
|
|
3850
|
+
const nextMeasurement = reconcileMorphChange({
|
|
3851
|
+
root: ref.current,
|
|
3852
|
+
measurementLayer: measurementLayerRef.current,
|
|
3853
|
+
measurementBackend,
|
|
3854
|
+
snapshotOverride: null,
|
|
3855
|
+
text,
|
|
3856
|
+
renderText,
|
|
3857
|
+
segments,
|
|
3858
|
+
layoutContext,
|
|
3859
|
+
session: sessionRef.current,
|
|
3860
|
+
timeline: timelineRef.current,
|
|
3861
|
+
setState
|
|
3862
|
+
});
|
|
3863
|
+
if (nextMeasurement !== null) {
|
|
3864
|
+
rememberCachedMorphSnapshot(domMeasurementSnapshotCacheRef.current, domMeasurementKey, nextMeasurement.snapshot);
|
|
3865
|
+
}
|
|
3866
|
+
completedDomMeasurementKeyRef.current = domMeasurementKey;
|
|
3867
|
+
if (domMeasurementRequestKey !== null) {
|
|
3868
|
+
setDomMeasurementRequestKey(null);
|
|
3869
|
+
}
|
|
3870
|
+
return;
|
|
3871
|
+
}
|
|
3872
|
+
if (domMeasurementRequestKey !== null) {
|
|
3873
|
+
setDomMeasurementRequestKey(null);
|
|
3874
|
+
}
|
|
3875
|
+
return;
|
|
3876
|
+
}
|
|
3877
|
+
completedDomMeasurementKeyRef.current = null;
|
|
3878
|
+
if (domMeasurementRequestKey !== null) {
|
|
3879
|
+
setDomMeasurementRequestKey(null);
|
|
3880
|
+
}
|
|
3881
|
+
reconcileMorphChange({
|
|
3882
|
+
root: ref.current,
|
|
3883
|
+
measurementLayer: measurementLayerRef.current,
|
|
3884
|
+
measurementBackend,
|
|
3885
|
+
snapshotOverride: null,
|
|
3886
|
+
text,
|
|
3887
|
+
renderText,
|
|
3888
|
+
segments,
|
|
3889
|
+
layoutContext,
|
|
3890
|
+
session: sessionRef.current,
|
|
3891
|
+
timeline: timelineRef.current,
|
|
3892
|
+
setState
|
|
3893
|
+
});
|
|
3894
|
+
}, [
|
|
3895
|
+
text,
|
|
3896
|
+
renderText,
|
|
3897
|
+
segments,
|
|
3898
|
+
layoutContext,
|
|
3899
|
+
measurementBackend,
|
|
3900
|
+
measurementRequest,
|
|
3901
|
+
pendingBootstrapText,
|
|
3902
|
+
bootstrapMeasurementRequest,
|
|
3903
|
+
domMeasurementKey,
|
|
3904
|
+
domMeasurementRequestKey
|
|
3905
|
+
]);
|
|
3906
|
+
useLayoutEffect(() => {
|
|
3907
|
+
syncCommittedRootOriginWhenIdle({
|
|
3908
|
+
root: ref.current,
|
|
3909
|
+
layoutContext,
|
|
3910
|
+
state,
|
|
3911
|
+
session: sessionRef.current
|
|
3912
|
+
});
|
|
3913
|
+
}, [layoutContext, state]);
|
|
3914
|
+
useLayoutEffect(() => {
|
|
3915
|
+
return () => {
|
|
3916
|
+
cancelTimeline(timelineRef.current);
|
|
3917
|
+
};
|
|
3918
|
+
}, []);
|
|
3919
|
+
return {
|
|
3920
|
+
committedMeasurement: sessionRef.current.committed,
|
|
3921
|
+
domMeasurementRequestKey,
|
|
3922
|
+
ref,
|
|
3923
|
+
bootstrapMeasurementLayerRef,
|
|
3924
|
+
measurementBackend,
|
|
3925
|
+
measurementLayerRef,
|
|
3926
|
+
renderText,
|
|
3927
|
+
segments,
|
|
3928
|
+
layoutContext,
|
|
3929
|
+
state,
|
|
3930
|
+
pendingBootstrapText,
|
|
3931
|
+
shouldRenderBootstrapSourceMeasurementLayer,
|
|
3932
|
+
bootstrapMeasurementRequest,
|
|
3933
|
+
useContentInlineSize
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3936
|
+
function StaticTorph({ text, className }) {
|
|
3937
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
3938
|
+
className,
|
|
3939
|
+
children: [
|
|
3940
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
3941
|
+
style: SCREEN_READER_ONLY_STYLE,
|
|
3942
|
+
children: text
|
|
3943
|
+
}, undefined, false, undefined, this),
|
|
3944
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
3945
|
+
"aria-hidden": "true",
|
|
3946
|
+
style: FALLBACK_TEXT_STYLE,
|
|
3947
|
+
children: text
|
|
3948
|
+
}, undefined, false, undefined, this)
|
|
3949
|
+
]
|
|
3950
|
+
}, undefined, true, undefined, this);
|
|
3951
|
+
}
|
|
3952
|
+
function ActiveTorph({
|
|
3953
|
+
text,
|
|
3954
|
+
className,
|
|
3955
|
+
bootstrapText
|
|
3956
|
+
}) {
|
|
3957
|
+
const {
|
|
3958
|
+
committedMeasurement,
|
|
3959
|
+
domMeasurementRequestKey,
|
|
3960
|
+
ref,
|
|
3961
|
+
bootstrapMeasurementLayerRef,
|
|
3962
|
+
measurementBackend,
|
|
3963
|
+
measurementLayerRef,
|
|
3964
|
+
renderText,
|
|
3965
|
+
segments,
|
|
3966
|
+
layoutContext,
|
|
3967
|
+
state,
|
|
3968
|
+
pendingBootstrapText,
|
|
3969
|
+
shouldRenderBootstrapSourceMeasurementLayer,
|
|
3970
|
+
bootstrapMeasurementRequest,
|
|
3971
|
+
useContentInlineSize
|
|
3972
|
+
} = useMorphTransition(text, className, bootstrapText);
|
|
3973
|
+
const plan = state.plan;
|
|
3974
|
+
const shouldRenderOverlay = state.stage !== "idle" && plan !== null;
|
|
3975
|
+
const shouldRenderMeasurementLayer = measurementBackend === "dom" && domMeasurementRequestKey !== null;
|
|
3976
|
+
const flowText = resolveVisibleFlowText(pendingBootstrapText, committedMeasurement, state.measurement, text);
|
|
3977
|
+
let bootstrapMeasurementLayer = null;
|
|
3978
|
+
if (shouldRenderBootstrapSourceMeasurementLayer && bootstrapMeasurementRequest !== null) {
|
|
3979
|
+
bootstrapMeasurementLayer = /* @__PURE__ */ jsxDEV(MeasurementLayer, {
|
|
3980
|
+
layerRef: bootstrapMeasurementLayerRef,
|
|
3981
|
+
layoutContext,
|
|
3982
|
+
text: bootstrapMeasurementRequest.renderText,
|
|
3983
|
+
segments: bootstrapMeasurementRequest.segments,
|
|
3984
|
+
useContentInlineSize: bootstrapMeasurementRequest.useContentInlineSize
|
|
3985
|
+
}, undefined, false, undefined, this);
|
|
3986
|
+
}
|
|
3987
|
+
let measurementLayer = null;
|
|
3988
|
+
if (shouldRenderMeasurementLayer) {
|
|
3989
|
+
measurementLayer = /* @__PURE__ */ jsxDEV(MeasurementLayer, {
|
|
3990
|
+
layerRef: measurementLayerRef,
|
|
3991
|
+
layoutContext,
|
|
3992
|
+
text: renderText,
|
|
3993
|
+
segments,
|
|
3994
|
+
useContentInlineSize
|
|
3995
|
+
}, undefined, false, undefined, this);
|
|
3996
|
+
}
|
|
3997
|
+
let overlay = null;
|
|
3998
|
+
if (shouldRenderOverlay) {
|
|
3999
|
+
overlay = /* @__PURE__ */ jsxDEV(MorphOverlay, {
|
|
4000
|
+
stage: state.stage,
|
|
4001
|
+
plan
|
|
4002
|
+
}, undefined, false, undefined, this);
|
|
4003
|
+
}
|
|
4004
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
4005
|
+
ref,
|
|
4006
|
+
className,
|
|
4007
|
+
style: getRootStyle(state.stage, plan, state.measurement, layoutContext),
|
|
4008
|
+
children: [
|
|
4009
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
4010
|
+
style: SCREEN_READER_ONLY_STYLE,
|
|
4011
|
+
children: text
|
|
4012
|
+
}, undefined, false, undefined, this),
|
|
4013
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
4014
|
+
"aria-hidden": "true",
|
|
4015
|
+
style: getFallbackTextStyle(shouldRenderOverlay),
|
|
4016
|
+
children: flowText
|
|
4017
|
+
}, undefined, false, undefined, this),
|
|
4018
|
+
bootstrapMeasurementLayer,
|
|
4019
|
+
measurementLayer,
|
|
4020
|
+
overlay
|
|
4021
|
+
]
|
|
4022
|
+
}, undefined, true, undefined, this);
|
|
4023
|
+
}
|
|
4024
|
+
function Torph({
|
|
4025
|
+
text,
|
|
4026
|
+
className
|
|
4027
|
+
}) {
|
|
4028
|
+
const [isActivated, setIsActivated] = useState(false);
|
|
4029
|
+
const previousTextRef = useRef(text);
|
|
4030
|
+
const bootstrapText = resolveActivationBootstrapText(isActivated, previousTextRef.current, text);
|
|
4031
|
+
const shouldActivate = isActivated || bootstrapText !== null;
|
|
4032
|
+
useLayoutEffect(() => {
|
|
4033
|
+
if (!isActivated && text !== previousTextRef.current) {
|
|
4034
|
+
previousTextRef.current = text;
|
|
4035
|
+
setIsActivated(true);
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
4038
|
+
previousTextRef.current = text;
|
|
4039
|
+
}, [isActivated, text]);
|
|
4040
|
+
if (shouldActivate) {
|
|
4041
|
+
return /* @__PURE__ */ jsxDEV(ActiveTorph, {
|
|
4042
|
+
text,
|
|
4043
|
+
className,
|
|
4044
|
+
bootstrapText
|
|
4045
|
+
}, undefined, false, undefined, this);
|
|
4046
|
+
}
|
|
4047
|
+
return /* @__PURE__ */ jsxDEV(StaticTorph, {
|
|
4048
|
+
text,
|
|
4049
|
+
className
|
|
4050
|
+
}, undefined, false, undefined, this);
|
|
4051
|
+
}
|
|
4052
|
+
export {
|
|
4053
|
+
supportsIntrinsicWidthLock,
|
|
4054
|
+
resolveVisibleFlowText,
|
|
4055
|
+
resolveMorphFrameBounds,
|
|
4056
|
+
resolveFlowText,
|
|
4057
|
+
resolveActivationBootstrapText,
|
|
4058
|
+
pairMorphCharacters,
|
|
4059
|
+
measureMorphSnapshotFromLayer,
|
|
4060
|
+
getRootStyle,
|
|
4061
|
+
getRootDisplay,
|
|
4062
|
+
getMeasurementLayerStyle,
|
|
4063
|
+
getLiveTransition,
|
|
4064
|
+
getLiveTransform,
|
|
4065
|
+
getExitTransition,
|
|
4066
|
+
getExitTransform,
|
|
4067
|
+
buildMorphVisualBridge,
|
|
4068
|
+
buildMorphPlan,
|
|
4069
|
+
Torph
|
|
4070
|
+
};
|