tegaki 0.14.0 → 0.15.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/CHANGELOG.md +38 -0
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +2 -2
- package/dist/{core-BYU5BEaZ.mjs → core-B7NiOWkP.mjs} +96 -29
- package/dist/core-B7NiOWkP.mjs.map +1 -0
- package/dist/{index-BBRejOMe.d.mts → index-B_c_g7Y-.d.mts} +113 -26
- package/dist/{index-DlYttK-R.d.mts → index-DMa72T_I.d.mts} +2 -2
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/react/index.d.mts +3 -3
- package/dist/react/index.mjs +3 -3
- package/dist/{react-0gtC94df.mjs → react-ZkNwynZ5.mjs} +2 -2
- package/dist/{react-0gtC94df.mjs.map → react-ZkNwynZ5.mjs.map} +1 -1
- package/dist/solid/index.d.mts +2 -2
- package/dist/solid/index.mjs +2 -2
- package/dist/wc/index.d.mts +2 -2
- package/dist/wc/index.mjs +2 -2
- package/package.json +1 -1
- package/src/core/engine.ts +49 -3
- package/src/core/index.ts +11 -2
- package/src/core/types.ts +1 -1
- package/src/lib/drawFallbackGlyph.ts +6 -6
- package/src/lib/drawGlyph.ts +19 -10
- package/src/lib/effects.test.ts +188 -0
- package/src/lib/effects.ts +106 -3
- package/src/lib/textLayout.ts +35 -0
- package/src/types.test.ts +17 -7
- package/src/types.ts +13 -2
- package/dist/core-BYU5BEaZ.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# tegaki
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ecba479: Split `gradient` into `strokeGradient` + `globalGradient`, and add render-stage hooks for layout-spanning effects. Closes #26.
|
|
8
|
+
|
|
9
|
+
**Breaking**
|
|
10
|
+
|
|
11
|
+
The `gradient` effect is renamed to `strokeGradient` with unchanged behavior (each stroke independently maps its progress to the color stops; `colors: 'rainbow'` still works). Rename the key in your `effects` prop:
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
// before
|
|
15
|
+
effects={{ gradient: { colors: ['#f00', '#00f'] } }}
|
|
16
|
+
// after
|
|
17
|
+
effects={{ strokeGradient: { colors: ['#f00', '#00f'] } }}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**New — `globalGradient`**
|
|
21
|
+
|
|
22
|
+
A canvas-space linear gradient that spans the full text bounding box — the leftmost pixel of the first glyph is `colors[0]` and the rightmost pixel of the last glyph is `colors[N]`, regardless of stroke boundaries. Matches CSS `background-clip: text` semantics.
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
effects={{
|
|
26
|
+
globalGradient: {
|
|
27
|
+
colors: ['#f00', '#00f'],
|
|
28
|
+
angle: 0, // 0 = left→right (default); 90 = top→bottom; positive = clockwise
|
|
29
|
+
},
|
|
30
|
+
}}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`strokeGradient` and `globalGradient` can be enabled independently. If both are on, `strokeGradient` wins per segment (its per-stroke color overrides `globalGradient`'s canvas-wide paint); this combination is unusual but predictable.
|
|
34
|
+
|
|
35
|
+
**New — effect render-stage hooks**
|
|
36
|
+
|
|
37
|
+
Effects can now declare optional `beforeRender(stage, config)` / `afterRender(stage, config)` hooks on their `EffectDefinition` metadata. The stage context exposes the 2D context, the `TextLayout`, a pre-computed `LayoutBBox`, base color, and seed. Hooks run once around the glyph loop (before in forward order, after in reverse), so effects spanning the whole layout — like `globalGradient` — have a natural place to set up canvas state. Built-in per-stroke effects (`glow`, `wobble`, `pressureWidth`, `taper`, `strokeGradient`) declare no hooks and are unaffected.
|
|
38
|
+
|
|
39
|
+
**New public exports from `tegaki/core`**: `EffectDefinition`, `RenderStageContext`, `LayoutBBox`, `getEffectDefinition`, `hasRenderHooks`, `computeLayoutBbox`, plus the previously-private `findEffect` / `findEffects`.
|
|
40
|
+
|
|
3
41
|
## 0.14.0
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { BBox, BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, CSSLength, CreateElementFn, FontOutput, GlyphData, LineCap, PathCommand, Point, ResolvedEffect, Stroke, TegakiBundle, TegakiEffectConfigs, TegakiEffectName, TegakiEffects, TegakiEngine, TegakiEngineOptions, TegakiGlyphData, TegakiMultiEffectName, TegakiQuality, TegakiSingletonEffectName, TextLayout, TimeControlMode, TimeControlProp, TimedPoint, Timeline, TimelineConfig, TimelineEntry, buildChildren, buildRootProps, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, getBundle, registerBundle, resolveBundle, resolveEffects };
|
|
1
|
+
import { A as computeLayoutBbox, B as Point, C as findEffect, D as resolveEffects, E as hasRenderHooks, F as CSSLength, G as TegakiEffects, H as TegakiBundle, I as FontOutput, J as TegakiSingletonEffectName, K as TegakiGlyphData, L as GlyphData, M as BBox, N as BUNDLE_VERSION, O as LayoutBBox, P as COMPATIBLE_BUNDLE_VERSIONS, R as LineCap, S as ResolvedEffect, T as getEffectDefinition, U as TegakiEffectConfigs, V as Stroke, W as TegakiEffectName, Y as TimedPoint, _ as computeTimeline, a as CreateElementFn, b as EffectDefinition, c as TimeControlMode, d as getBundle, f as registerBundle, g as TimelineEntry, h as TimelineConfig, i as TegakiEngine, j as computeTextLayout, k as TextLayout, l as TimeControlProp, m as Timeline, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, q as TegakiMultiEffectName, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as ensureFontFace, w as findEffects, x as RenderStageContext, y as drawGlyph, z as PathCommand } from "../index-B_c_g7Y-.mjs";
|
|
2
|
+
export { BBox, BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, CSSLength, CreateElementFn, EffectDefinition, FontOutput, GlyphData, LayoutBBox, LineCap, PathCommand, Point, RenderStageContext, ResolvedEffect, Stroke, TegakiBundle, TegakiEffectConfigs, TegakiEffectName, TegakiEffects, TegakiEngine, TegakiEngineOptions, TegakiGlyphData, TegakiMultiEffectName, TegakiQuality, TegakiSingletonEffectName, TextLayout, TimeControlMode, TimeControlProp, TimedPoint, Timeline, TimelineConfig, TimelineEntry, buildChildren, buildRootProps, computeLayoutBbox, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, findEffect, findEffects, getBundle, getEffectDefinition, hasRenderHooks, registerBundle, resolveBundle, resolveEffects };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createBundle, c as resolveBundle, d as computeTimeline, f as
|
|
2
|
-
export { BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, TegakiEngine, buildChildren, buildRootProps, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, getBundle, registerBundle, resolveBundle, resolveEffects };
|
|
1
|
+
import { _ as findEffect, a as createBundle, b as hasRenderHooks, c as resolveBundle, d as computeTimeline, f as computeLayoutBbox, h as drawGlyph, i as domCreateElement, l as BUNDLE_VERSION, m as ensureFontFace, n as buildChildren, o as getBundle, p as computeTextLayout, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS, v as findEffects, x as resolveEffects, y as getEffectDefinition } from "../core-B7NiOWkP.mjs";
|
|
2
|
+
export { BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, TegakiEngine, buildChildren, buildRootProps, computeLayoutBbox, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, findEffect, findEffects, getBundle, getEffectDefinition, hasRenderHooks, registerBundle, resolveBundle, resolveEffects };
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
//#region src/lib/effects.ts
|
|
2
2
|
const defaultEffects = { pressureWidth: true };
|
|
3
|
-
const knownEffects =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
const knownEffects = {
|
|
4
|
+
glow: {},
|
|
5
|
+
wobble: {},
|
|
6
|
+
pressureWidth: {},
|
|
7
|
+
taper: {},
|
|
8
|
+
strokeGradient: {},
|
|
9
|
+
globalGradient: { beforeRender(stage, config) {
|
|
10
|
+
const colors = config.colors;
|
|
11
|
+
if (!Array.isArray(colors) || colors.length === 0) return;
|
|
12
|
+
const { ctx, bbox } = stage;
|
|
13
|
+
const rad = (config.angle ?? 0) * Math.PI / 180;
|
|
14
|
+
const dx = Math.cos(rad);
|
|
15
|
+
const dy = Math.sin(rad);
|
|
16
|
+
const cx = bbox.x + bbox.width / 2;
|
|
17
|
+
const cy = bbox.y + bbox.height / 2;
|
|
18
|
+
const halfW = bbox.width / 2;
|
|
19
|
+
const halfH = bbox.height / 2;
|
|
20
|
+
const proj = Math.abs(dx * halfW) + Math.abs(dy * halfH);
|
|
21
|
+
const grad = ctx.createLinearGradient(cx - dx * proj, cy - dy * proj, cx + dx * proj, cy + dy * proj);
|
|
22
|
+
if (colors.length === 1) {
|
|
23
|
+
grad.addColorStop(0, colors[0]);
|
|
24
|
+
grad.addColorStop(1, colors[0]);
|
|
25
|
+
} else for (let i = 0; i < colors.length; i++) grad.addColorStop(i / (colors.length - 1), colors[i]);
|
|
26
|
+
stage.strokeStyle = grad;
|
|
27
|
+
} }
|
|
28
|
+
};
|
|
10
29
|
/**
|
|
11
30
|
* Normalizes an effects record into a sorted array of resolved effects.
|
|
12
31
|
* Known keys infer the effect name; custom keys read it from the `effect` field.
|
|
@@ -24,13 +43,13 @@ function resolveEffects(effects) {
|
|
|
24
43
|
let config;
|
|
25
44
|
let order;
|
|
26
45
|
if (value === true) {
|
|
27
|
-
effectName =
|
|
46
|
+
effectName = Object.hasOwn(knownEffects, key) ? key : void 0;
|
|
28
47
|
if (!effectName) continue;
|
|
29
48
|
config = {};
|
|
30
49
|
order = 0;
|
|
31
50
|
} else {
|
|
32
51
|
if (value.enabled === false) continue;
|
|
33
|
-
effectName = value.effect ?? (
|
|
52
|
+
effectName = value.effect ?? (Object.hasOwn(knownEffects, key) ? key : void 0);
|
|
34
53
|
if (!effectName) continue;
|
|
35
54
|
const { effect: _, order: o, enabled: __, ...rest } = value;
|
|
36
55
|
config = rest;
|
|
@@ -53,6 +72,18 @@ function findEffect(effects, name) {
|
|
|
53
72
|
function findEffects(effects, name) {
|
|
54
73
|
return effects.filter((e) => e.effect === name);
|
|
55
74
|
}
|
|
75
|
+
/** Look up the render-hook metadata for an effect name. Unknown names return undefined. */
|
|
76
|
+
function getEffectDefinition(name) {
|
|
77
|
+
return Object.hasOwn(knownEffects, name) ? knownEffects[name] : void 0;
|
|
78
|
+
}
|
|
79
|
+
/** True when any resolved effect defines a before/after render hook. */
|
|
80
|
+
function hasRenderHooks(effects) {
|
|
81
|
+
for (const e of effects) {
|
|
82
|
+
const def = getEffectDefinition(e.effect);
|
|
83
|
+
if (def?.beforeRender || def?.afterRender) return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
56
87
|
//#endregion
|
|
57
88
|
//#region src/lib/catmullRom.ts
|
|
58
89
|
/**
|
|
@@ -314,7 +345,8 @@ function defaultStrokeEasing(t) {
|
|
|
314
345
|
* font, fontSize, or segment size changes; if omitted here, strokes are
|
|
315
346
|
* subdivided inline each call (useful for testing).
|
|
316
347
|
*/
|
|
317
|
-
function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], seed = 0, getSubdivided, strokeEasing = defaultStrokeEasing, strokeScale = 1) {
|
|
348
|
+
function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], seed = 0, getSubdivided, strokeEasing = defaultStrokeEasing, strokeScale = 1, strokeStyleOverride) {
|
|
349
|
+
const defaultStrokePaint = strokeStyleOverride ?? color;
|
|
318
350
|
const scale = pos.fontSize / pos.unitsPerEm;
|
|
319
351
|
const ox = pos.x;
|
|
320
352
|
const oy = pos.y;
|
|
@@ -322,7 +354,7 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
322
354
|
const wobbleEffect = findEffect(effects, "wobble");
|
|
323
355
|
const pressureEffect = findEffect(effects, "pressureWidth");
|
|
324
356
|
const taperEffect = findEffect(effects, "taper");
|
|
325
|
-
const
|
|
357
|
+
const strokeGradientEffect = findEffect(effects, "strokeGradient");
|
|
326
358
|
const pressureAmount = pressureEffect ? Math.max(0, Math.min(pressureEffect.config.strength ?? 1, 1)) : 0;
|
|
327
359
|
const wobbleAmplitude = wobbleEffect ? wobbleEffect.config.amplitude ?? 1.5 : 0;
|
|
328
360
|
const wobbleFrequency = wobbleEffect ? wobbleEffect.config.frequency ?? 8 : 0;
|
|
@@ -330,12 +362,12 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
330
362
|
const hasWobble = !!wobbleEffect;
|
|
331
363
|
const taperStart = taperEffect ? Math.max(0, Math.min(taperEffect.config.startLength ?? .15, 1)) : 0;
|
|
332
364
|
const taperEnd = taperEffect ? Math.max(0, Math.min(taperEffect.config.endLength ?? .15, 1)) : 0;
|
|
333
|
-
const gradientColors =
|
|
365
|
+
const gradientColors = strokeGradientEffect?.config.colors;
|
|
334
366
|
const isRainbow = gradientColors === "rainbow";
|
|
335
367
|
const gradientColorStops = Array.isArray(gradientColors) ? gradientColors : void 0;
|
|
336
|
-
const gradientSaturation =
|
|
337
|
-
const gradientLightness =
|
|
338
|
-
const
|
|
368
|
+
const gradientSaturation = strokeGradientEffect?.config.saturation ?? 80;
|
|
369
|
+
const gradientLightness = strokeGradientEffect?.config.lightness ?? 55;
|
|
370
|
+
const hasStrokeGradient = !!strokeGradientEffect;
|
|
339
371
|
const needsPerSegment = pressureAmount > 0 || !!taperEffect;
|
|
340
372
|
const subdivide = getSubdivided ?? ((s) => subdivideStroke(s, Infinity));
|
|
341
373
|
const wobbleDx = (_x, y, idx) => {
|
|
@@ -389,7 +421,7 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
389
421
|
ctx.fill();
|
|
390
422
|
ctx.restore();
|
|
391
423
|
}
|
|
392
|
-
ctx.fillStyle = colorAt(0);
|
|
424
|
+
ctx.fillStyle = hasStrokeGradient ? colorAt(0) : defaultStrokePaint;
|
|
393
425
|
ctx.beginPath();
|
|
394
426
|
if (lineCap === "round") {
|
|
395
427
|
ctx.arc(dotX, dotY, dotWidth / 2, 0, Math.PI * 2);
|
|
@@ -459,8 +491,8 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
459
491
|
ctx.stroke();
|
|
460
492
|
ctx.restore();
|
|
461
493
|
}
|
|
462
|
-
if (!needsPerSegment && !
|
|
463
|
-
ctx.strokeStyle =
|
|
494
|
+
if (!needsPerSegment && !hasStrokeGradient) {
|
|
495
|
+
ctx.strokeStyle = defaultStrokePaint;
|
|
464
496
|
ctx.lineWidth = baseLineWidth;
|
|
465
497
|
tracePolyline();
|
|
466
498
|
ctx.stroke();
|
|
@@ -478,7 +510,7 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
478
510
|
lw = Math.max(baseLineWidth + (perPoint - baseLineWidth) * pressureAmount, .5 * scale * strokeScale) * taperMultiplier(midProgress);
|
|
479
511
|
}
|
|
480
512
|
ctx.lineWidth = lw;
|
|
481
|
-
ctx.strokeStyle =
|
|
513
|
+
ctx.strokeStyle = hasStrokeGradient ? colorAt(midProgress) : defaultStrokePaint;
|
|
482
514
|
ctx.beginPath();
|
|
483
515
|
ctx.moveTo(txs[i - 1], tys[i - 1]);
|
|
484
516
|
ctx.lineTo(txs[i], tys[i]);
|
|
@@ -514,6 +546,24 @@ function ensureFont(family, url) {
|
|
|
514
546
|
}
|
|
515
547
|
//#endregion
|
|
516
548
|
//#region src/lib/textLayout.ts
|
|
549
|
+
/**
|
|
550
|
+
* Compute the text bounding box from a measured layout. Inputs are in CSS
|
|
551
|
+
* pixels. Assumes the layout's char offsets are em-relative to the left edge
|
|
552
|
+
* of each line (as produced by `computeTextLayout`).
|
|
553
|
+
*/
|
|
554
|
+
function computeLayoutBbox(layout, fontSize, lineHeight) {
|
|
555
|
+
let maxRight = 0;
|
|
556
|
+
for (const lineIndices of layout.lines) for (const charIdx of lineIndices) {
|
|
557
|
+
const right = ((layout.charOffsets[charIdx] ?? 0) + (layout.charWidths[charIdx] ?? 0)) * fontSize;
|
|
558
|
+
if (right > maxRight) maxRight = right;
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
x: 0,
|
|
562
|
+
y: 0,
|
|
563
|
+
width: maxRight,
|
|
564
|
+
height: layout.lines.length * lineHeight
|
|
565
|
+
};
|
|
566
|
+
}
|
|
517
567
|
function computeTextLayout(elOrText, fontSize, fontFamily, lineHeight, maxWidth) {
|
|
518
568
|
if (typeof elOrText === "string") return measureWithTempElement(elOrText, fontFamily, fontSize, lineHeight, maxWidth);
|
|
519
569
|
return measureElement(elOrText, fontSize);
|
|
@@ -745,12 +795,12 @@ function registerCssProperties() {
|
|
|
745
795
|
//#endregion
|
|
746
796
|
//#region src/lib/drawFallbackGlyph.ts
|
|
747
797
|
/**
|
|
748
|
-
* Draw a fallback glyph (plain text) with applicable effects (glow,
|
|
798
|
+
* Draw a fallback glyph (plain text) with applicable effects (glow, strokeGradient, wobble).
|
|
749
799
|
*/
|
|
750
800
|
function drawFallbackGlyph(ctx, char, x, baseline, fontSize, fontFamily, color, effects = [], seed = 0) {
|
|
751
801
|
const glowEffects = findEffects(effects, "glow");
|
|
752
802
|
const wobbleEffect = findEffect(effects, "wobble");
|
|
753
|
-
const
|
|
803
|
+
const strokeGradientEffect = findEffect(effects, "strokeGradient");
|
|
754
804
|
let dx = 0;
|
|
755
805
|
let dy = 0;
|
|
756
806
|
if (wobbleEffect) {
|
|
@@ -762,11 +812,11 @@ function drawFallbackGlyph(ctx, char, x, baseline, fontSize, fontFamily, color,
|
|
|
762
812
|
const drawX = x + dx;
|
|
763
813
|
const drawY = baseline + dy;
|
|
764
814
|
let fillColor = color;
|
|
765
|
-
if (
|
|
766
|
-
const colors =
|
|
815
|
+
if (strokeGradientEffect) {
|
|
816
|
+
const colors = strokeGradientEffect.config.colors;
|
|
767
817
|
if (colors === "rainbow") {
|
|
768
|
-
const saturation =
|
|
769
|
-
const lightness =
|
|
818
|
+
const saturation = strokeGradientEffect.config.saturation ?? 80;
|
|
819
|
+
const lightness = strokeGradientEffect.config.lightness ?? 55;
|
|
770
820
|
fillColor = `hsl(${seed * 137.5 % 360}, ${saturation}%, ${lightness}%)`;
|
|
771
821
|
} else if (Array.isArray(colors) && colors.length > 0) fillColor = colors[Math.floor(seed) % colors.length];
|
|
772
822
|
}
|
|
@@ -1392,7 +1442,7 @@ var TegakiEngine = class {
|
|
|
1392
1442
|
const halfLeading = (lineHeight - (font.ascender - font.descender) / font.unitsPerEm * fontSize) / 2;
|
|
1393
1443
|
const characters = graphemes(this._text);
|
|
1394
1444
|
const currentTime = this.currentTime;
|
|
1395
|
-
const effectsNeedSubdivision = !!findEffect(this._resolvedEffects, "wobble") || !!findEffect(this._resolvedEffects, "
|
|
1445
|
+
const effectsNeedSubdivision = !!findEffect(this._resolvedEffects, "wobble") || !!findEffect(this._resolvedEffects, "strokeGradient") || !!findEffect(this._resolvedEffects, "taper") || (() => {
|
|
1396
1446
|
const p = findEffect(this._resolvedEffects, "pressureWidth");
|
|
1397
1447
|
return !!p && Math.max(0, Math.min(p.config.strength ?? 1, 1)) > 0;
|
|
1398
1448
|
})();
|
|
@@ -1416,6 +1466,19 @@ var TegakiEngine = class {
|
|
|
1416
1466
|
};
|
|
1417
1467
|
const clipText = this._quality?.clipText;
|
|
1418
1468
|
const strokeScale = typeof clipText === "number" ? clipText : 1;
|
|
1469
|
+
const stage = hasRenderHooks(this._resolvedEffects) ? {
|
|
1470
|
+
ctx,
|
|
1471
|
+
layout,
|
|
1472
|
+
fontSize,
|
|
1473
|
+
lineHeight,
|
|
1474
|
+
unitsPerEm: font.unitsPerEm,
|
|
1475
|
+
ascender: font.ascender,
|
|
1476
|
+
descender: font.descender,
|
|
1477
|
+
bbox: computeLayoutBbox(layout, fontSize, lineHeight),
|
|
1478
|
+
baseColor: color,
|
|
1479
|
+
seed: this._seed
|
|
1480
|
+
} : null;
|
|
1481
|
+
if (stage) for (const effect of this._resolvedEffects) getEffectDefinition(effect.effect)?.beforeRender?.(stage, effect.config);
|
|
1419
1482
|
let y = 0;
|
|
1420
1483
|
for (const lineIndices of layout.lines) {
|
|
1421
1484
|
for (const charIdx of lineIndices) {
|
|
@@ -1435,11 +1498,15 @@ var TegakiEngine = class {
|
|
|
1435
1498
|
unitsPerEm: font.unitsPerEm,
|
|
1436
1499
|
ascender: font.ascender,
|
|
1437
1500
|
descender: font.descender
|
|
1438
|
-
}, localTime, font.lineCap, color, this._resolvedEffects, this._seed + charIdx, getSubdivided, this._timing?.strokeEasing, strokeScale);
|
|
1501
|
+
}, localTime, font.lineCap, color, this._resolvedEffects, this._seed + charIdx, getSubdivided, this._timing?.strokeEasing, strokeScale, stage?.strokeStyle);
|
|
1439
1502
|
} else if (!entry.hasGlyph && currentTime >= entry.offset + entry.duration) drawFallbackGlyph(ctx, char, x, y + halfLeading + font.ascender / font.unitsPerEm * fontSize, fontSize, cssFontFamily(font), color, this._resolvedEffects, this._seed + charIdx);
|
|
1440
1503
|
}
|
|
1441
1504
|
y += lineHeight;
|
|
1442
1505
|
}
|
|
1506
|
+
if (stage) for (let i = this._resolvedEffects.length - 1; i >= 0; i--) {
|
|
1507
|
+
const effect = this._resolvedEffects[i];
|
|
1508
|
+
getEffectDefinition(effect.effect)?.afterRender?.(stage, effect.config);
|
|
1509
|
+
}
|
|
1443
1510
|
if (clipText) {
|
|
1444
1511
|
if (!this._maskCanvas) this._maskCanvas = document.createElement("canvas");
|
|
1445
1512
|
const maskCanvas = this._maskCanvas;
|
|
@@ -1473,6 +1540,6 @@ var TegakiEngine = class {
|
|
|
1473
1540
|
}
|
|
1474
1541
|
};
|
|
1475
1542
|
//#endregion
|
|
1476
|
-
export { createBundle as a, resolveBundle as c, computeTimeline as d,
|
|
1543
|
+
export { findEffect as _, createBundle as a, hasRenderHooks as b, resolveBundle as c, computeTimeline as d, computeLayoutBbox as f, coerceToString as g, drawGlyph as h, domCreateElement as i, BUNDLE_VERSION as l, ensureFontFace as m, buildChildren as n, getBundle as o, computeTextLayout as p, buildRootProps as r, registerBundle as s, TegakiEngine as t, COMPATIBLE_BUNDLE_VERSIONS as u, findEffects as v, resolveEffects as x, getEffectDefinition as y };
|
|
1477
1544
|
|
|
1478
|
-
//# sourceMappingURL=core-
|
|
1545
|
+
//# sourceMappingURL=core-B7NiOWkP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-B7NiOWkP.mjs","names":[],"sources":["../src/lib/effects.ts","../src/lib/catmullRom.ts","../src/lib/strokeCache.ts","../src/lib/utils.ts","../src/lib/drawGlyph.ts","../src/lib/font.ts","../src/lib/textLayout.ts","../src/lib/timeline.ts","../src/types.ts","../src/core/bundle-registry.ts","../src/core/createBundle.ts","../src/lib/css-properties.ts","../src/lib/drawFallbackGlyph.ts","../src/core/render-elements.ts","../src/core/engine.ts"],"sourcesContent":["import type { TegakiEffectConfigs, TegakiEffectName } from '../types.ts';\nimport type { LayoutBBox, TextLayout } from './textLayout.ts';\n\nexport interface ResolvedEffect<K extends TegakiEffectName = TegakiEffectName> {\n effect: K;\n order: number;\n config: TegakiEffectConfigs[K];\n}\n\n/**\n * Context passed to effect render hooks. Covers everything a hook might need\n * to prepare or finalize state that spans the whole text layout (as opposed\n * to the per-glyph state owned by `drawGlyph`).\n *\n * The `ctx` has already been translated so its origin is the top-left of the\n * content box (i.e. `(0, 0)` is the top-left of `bbox`). Hooks that mutate ctx\n * state (transforms, strokeStyle, compositing, filters) are expected to\n * save/restore as needed so they don't leak into adjacent effects or the\n * glyph loop unintentionally.\n */\nexport interface RenderStageContext {\n ctx: CanvasRenderingContext2D;\n layout: TextLayout;\n fontSize: number;\n lineHeight: number;\n unitsPerEm: number;\n ascender: number;\n descender: number;\n /** Text bounding box in the current ctx coordinate space (CSS pixels). */\n bbox: LayoutBBox;\n /** The base color string resolved from `getComputedStyle().color`. */\n baseColor: string;\n /** Engine seed, offset by whatever the caller chose (currently the engine root seed). */\n seed: number;\n /**\n * Stroke paint override for the whole glyph loop. A `beforeRender` hook sets\n * this to e.g. a `CanvasGradient` when the effect wants every stroke to share\n * a single canvas-space paint. The engine reads it after all hooks have run\n * and threads it through to `drawGlyph`. `shadowColor` (used by `glow`) still\n * reads from `baseColor` since Canvas shadows don't accept gradients.\n */\n strokeStyle?: string | CanvasGradient | CanvasPattern;\n}\n\n/**\n * Metadata describing how an effect participates in rendering. The per-glyph\n * branch of an effect still lives inside `drawGlyph`; these hooks exist for\n * effects whose behavior spans the full layout (e.g. a gradient whose color\n * stops map to canvas-space position rather than per-stroke progress).\n *\n * `beforeRender` hooks run in forward order (same order as `_resolvedEffects`\n * after sorting by `order`). `afterRender` hooks run in reverse order so\n * save/restore-style pairs nest correctly.\n */\nexport interface EffectDefinition {\n beforeRender?(stage: RenderStageContext, config: any): void;\n afterRender?(stage: RenderStageContext, config: any): void;\n}\n\nconst defaultEffects: Record<string, any> = { pressureWidth: true };\n\nconst knownEffects: Record<string, EffectDefinition> = {\n glow: {},\n wobble: {},\n pressureWidth: {},\n taper: {},\n strokeGradient: {},\n globalGradient: {\n beforeRender(stage, config: { colors?: string[]; angle?: number }) {\n const colors = config.colors;\n if (!Array.isArray(colors) || colors.length === 0) return;\n const { ctx, bbox } = stage;\n\n // Project the bbox onto the direction vector to find gradient endpoints\n // that cover the full box at any angle. y grows downward on canvas, so\n // `angle=0` (dx=1, dy=0) is left→right and `angle=90` (dx=0, dy=1) is\n // top→bottom — positive angles rotate clockwise.\n const rad = ((config.angle ?? 0) * Math.PI) / 180;\n const dx = Math.cos(rad);\n const dy = Math.sin(rad);\n const cx = bbox.x + bbox.width / 2;\n const cy = bbox.y + bbox.height / 2;\n const halfW = bbox.width / 2;\n const halfH = bbox.height / 2;\n const proj = Math.abs(dx * halfW) + Math.abs(dy * halfH);\n\n const grad = ctx.createLinearGradient(cx - dx * proj, cy - dy * proj, cx + dx * proj, cy + dy * proj);\n if (colors.length === 1) {\n grad.addColorStop(0, colors[0]!);\n grad.addColorStop(1, colors[0]!);\n } else {\n for (let i = 0; i < colors.length; i++) {\n grad.addColorStop(i / (colors.length - 1), colors[i]!);\n }\n }\n stage.strokeStyle = grad;\n },\n },\n};\n\n/**\n * Normalizes an effects record into a sorted array of resolved effects.\n * Known keys infer the effect name; custom keys read it from the `effect` field.\n * Boolean `true` becomes an empty config. `false`/absent entries are skipped.\n */\nexport function resolveEffects(effects: Record<string, any> | undefined): ResolvedEffect[] {\n const merged = { ...defaultEffects, ...effects };\n\n const result: ResolvedEffect[] = [];\n\n for (const [key, value] of Object.entries(merged)) {\n if (value === false || value == null) continue;\n\n let effectName: TegakiEffectName;\n let config: Record<string, any>;\n let order: number;\n\n if (value === true) {\n effectName = (Object.hasOwn(knownEffects, key) ? key : undefined) as TegakiEffectName;\n if (!effectName) continue;\n config = {};\n order = 0;\n } else {\n if (value.enabled === false) continue;\n effectName = value.effect ?? (Object.hasOwn(knownEffects, key) ? key : undefined);\n if (!effectName) continue;\n const { effect: _, order: o, enabled: __, ...rest } = value;\n config = rest;\n order = o ?? 0;\n }\n\n result.push({ effect: effectName, order, config });\n }\n\n result.sort((a, b) => a.order - b.order);\n return result;\n}\n\n/** Check if a specific effect is active. */\nexport function findEffect<K extends TegakiEffectName>(effects: ResolvedEffect[], name: K): ResolvedEffect<K> | undefined {\n return effects.find((e) => e.effect === name) as ResolvedEffect<K> | undefined;\n}\n\n/** Get all instances of a specific effect (for duplicates). */\nexport function findEffects<K extends TegakiEffectName>(effects: ResolvedEffect[], name: K): ResolvedEffect<K>[] {\n return effects.filter((e) => e.effect === name) as ResolvedEffect<K>[];\n}\n\n/** Look up the render-hook metadata for an effect name. Unknown names return undefined. */\nexport function getEffectDefinition(name: string): EffectDefinition | undefined {\n return Object.hasOwn(knownEffects, name) ? knownEffects[name] : undefined;\n}\n\n/** True when any resolved effect defines a before/after render hook. */\nexport function hasRenderHooks(effects: ResolvedEffect[]): boolean {\n for (const e of effects) {\n const def = getEffectDefinition(e.effect);\n if (def?.beforeRender || def?.afterRender) return true;\n }\n return false;\n}\n","/**\n * Centripetal Catmull-Rom spline sampling.\n *\n * A point is `[x, y, w]` — the same shape the stroke data uses — but the\n * third channel is carried through the interpolation opaquely, so this\n * module is self-contained and doesn't know about strokes or glyph data.\n */\n\n/** A 3-channel point `[x, y, w]`. Typed as a plain array to match the loose shape used by the glyph data format. */\nexport type Point3 = readonly number[];\n\nexport interface SmoothSample {\n x: number;\n y: number;\n /** Third channel (stroke width in the caller's use) interpolated on the same parameterization as x/y. */\n width: number;\n}\n\n/**\n * Sample `count` points along a centripetal Catmull-Rom segment from `p1` to\n * `p2`, with neighbor control points `p0` and `p3`. Samples are emitted at\n * `u = k/count` for `k = 1..count`, so the last sample equals `p2`.\n *\n * Centripetal parameterization (α = 0.5) avoids the cusps and self-loops that\n * uniform/chordal Catmull-Rom can produce on sharp corners — relevant for the\n * baked RDP-simplified polylines the renderer consumes.\n *\n * For endpoint segments where a neighbor is missing, pass a phantom point\n * built with {@link reflect}. Zero-length chords are clamped to a tiny epsilon\n * so the knot parameterization stays non-degenerate.\n */\nexport function sampleCatmullRom(p0: Point3, p1: Point3, p2: Point3, p3: Point3, count: number): SmoothSample[] {\n const d01 = Math.max(dist(p0, p1), 1e-6);\n const d12 = Math.max(dist(p1, p2), 1e-6);\n const d23 = Math.max(dist(p2, p3), 1e-6);\n const t0 = 0;\n const t1 = t0 + Math.sqrt(d01);\n const t2 = t1 + Math.sqrt(d12);\n const t3 = t2 + Math.sqrt(d23);\n\n const out: SmoothSample[] = new Array(count);\n for (let k = 1; k <= count; k++) {\n const u = k / count;\n const t = t1 + u * (t2 - t1);\n out[k - 1] = evalBarryGoldman(p0, p1, p2, p3, t0, t1, t2, t3, t);\n }\n return out;\n}\n\n/**\n * Reflect `p` across `anchor` to produce a phantom neighbor for endpoint\n * segments. The result lies on the extension of (p, anchor) past `anchor` at\n * the same distance — equivalent to a zero-curvature extrapolation, which\n * gives a natural straight start/end tangent.\n */\nexport function reflect(anchor: Point3, p: Point3): Point3 {\n return [2 * anchor[0]! - p[0]!, 2 * anchor[1]! - p[1]!, anchor[2]!];\n}\n\nfunction dist(a: Point3, b: Point3): number {\n const dx = b[0]! - a[0]!;\n const dy = b[1]! - a[1]!;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n// Barry-Goldman pyramid evaluation of a Catmull-Rom segment between p1 and p2\n// at knot value t ∈ [t1, t2]. Applies the same interpolation to x, y, and the\n// third channel so they stay smooth together.\nfunction evalBarryGoldman(\n p0: Point3,\n p1: Point3,\n p2: Point3,\n p3: Point3,\n t0: number,\n t1: number,\n t2: number,\n t3: number,\n t: number,\n): SmoothSample {\n const a1x = lerp(p0[0]!, p1[0]!, t0, t1, t);\n const a1y = lerp(p0[1]!, p1[1]!, t0, t1, t);\n const a1w = lerp(p0[2]!, p1[2]!, t0, t1, t);\n const a2x = lerp(p1[0]!, p2[0]!, t1, t2, t);\n const a2y = lerp(p1[1]!, p2[1]!, t1, t2, t);\n const a2w = lerp(p1[2]!, p2[2]!, t1, t2, t);\n const a3x = lerp(p2[0]!, p3[0]!, t2, t3, t);\n const a3y = lerp(p2[1]!, p3[1]!, t2, t3, t);\n const a3w = lerp(p2[2]!, p3[2]!, t2, t3, t);\n const b1x = lerp(a1x, a2x, t0, t2, t);\n const b1y = lerp(a1y, a2y, t0, t2, t);\n const b1w = lerp(a1w, a2w, t0, t2, t);\n const b2x = lerp(a2x, a3x, t1, t3, t);\n const b2y = lerp(a2y, a3y, t1, t3, t);\n const b2w = lerp(a2w, a3w, t1, t3, t);\n return {\n x: lerp(b1x, b2x, t1, t2, t),\n y: lerp(b1y, b2y, t1, t2, t),\n width: lerp(b1w, b2w, t1, t2, t),\n };\n}\n\nfunction lerp(a: number, b: number, ta: number, tb: number, t: number): number {\n const span = tb - ta;\n if (span === 0) return a;\n return a + (b - a) * ((t - ta) / span);\n}\n","import type { TegakiGlyphData } from '../types.ts';\nimport { reflect, sampleCatmullRom } from './catmullRom.ts';\n\ntype Stroke = TegakiGlyphData['s'][number];\n\n/**\n * A single vertex in a subdivided stroke polyline. Consecutive vertices form\n * a drawable sub-segment. Coordinates are in font units and pre-wobble: the\n * renderer applies wobble, scale, and translation at draw time so that one\n * cached subdivision can be reused across every instance of the same glyph.\n */\nexport interface SubVertex {\n /** X in font units, pre-wobble. */\n x: number;\n /** Y in font units, pre-wobble. */\n y: number;\n /** Stroke width at this vertex, in font units. */\n width: number;\n /** Accumulated length from the start of the stroke, in font units. */\n cumLen: number;\n /** Fractional original-point index (used to keep wobble phase continuous). */\n idx: number;\n}\n\nexport interface SubdividedStroke {\n /** Ordered vertices; vertices[i] → vertices[i+1] is a drawable sub-segment. */\n vertices: SubVertex[];\n /** Total polyline length in font units. */\n totalLen: number;\n /** Mean width across the original points in font units. */\n avgWidth: number;\n}\n\n/**\n * Subdivide a stroke so that no sub-segment exceeds `maxSegLen` font units.\n * Pass `Infinity` (or any non-finite value) to skip subdivision and return\n * the raw polyline.\n *\n * When `smoothing` is true, intermediate vertices are placed on a centripetal\n * Catmull-Rom spline through the original points (see `catmullRom.ts`) —\n * hiding the polyline facets that show up at large render sizes. The original\n * points remain on the curve, so endpoints and `cumLen`/`idx` semantics are\n * preserved. Has no effect when `maxSegLen` is non-finite (no subdivision).\n *\n * Output depends only on `(stroke.p, maxSegLen, smoothing)` — not on position,\n * seed, progress, or effect config — so it can be cached and shared across\n * every instance of the same glyph at the same font size.\n */\nexport function subdivideStroke(stroke: Stroke, maxSegLen: number, smoothing = false): SubdividedStroke {\n const pts = stroke.p;\n const n = pts.length;\n if (n === 0) return { vertices: [], totalLen: 0, avgWidth: 0 };\n\n const first = pts[0]!;\n const vertices: SubVertex[] = [{ x: first[0]!, y: first[1]!, width: first[2]!, cumLen: 0, idx: 0 }];\n\n let cumLen = 0;\n for (let j = 1; j < n; j++) {\n const prev = pts[j - 1]!;\n const cur = pts[j]!;\n const dx = cur[0]! - prev[0]!;\n const dy = cur[1]! - prev[1]!;\n const chordLen = Math.sqrt(dx * dx + dy * dy);\n const count = chordLen > 0 && Number.isFinite(maxSegLen) && maxSegLen > 0 ? Math.max(1, Math.ceil(chordLen / maxSegLen)) : 1;\n\n if (smoothing && count > 1) {\n const p0 = j >= 2 ? pts[j - 2]! : reflect(prev, cur);\n const p3 = j + 1 < n ? pts[j + 1]! : reflect(cur, prev);\n const samples = sampleCatmullRom(p0, prev, cur, p3, count);\n let px = prev[0]!;\n let py = prev[1]!;\n let segAccum = 0;\n for (let k = 0; k < count; k++) {\n const s = samples[k]!;\n const ex = s.x - px;\n const ey = s.y - py;\n segAccum += Math.sqrt(ex * ex + ey * ey);\n vertices.push({ x: s.x, y: s.y, width: s.width, cumLen: cumLen + segAccum, idx: j - 1 + (k + 1) / count });\n px = s.x;\n py = s.y;\n }\n cumLen += segAccum;\n } else {\n const dw = cur[2]! - prev[2]!;\n for (let k = 1; k <= count; k++) {\n const t = k / count;\n vertices.push({\n x: prev[0]! + dx * t,\n y: prev[1]! + dy * t,\n width: prev[2]! + dw * t,\n cumLen: cumLen + chordLen * t,\n idx: j - 1 + t,\n });\n }\n cumLen += chordLen;\n }\n }\n\n let widthSum = 0;\n for (const p of pts) widthSum += p[2]!;\n\n return { vertices, totalLen: cumLen, avgWidth: widthSum / n };\n}\n","import type { CSSLength, TegakiBundle } from '../types.ts';\n\nconst segmenter =\n typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function' ? new Intl.Segmenter(undefined, { granularity: 'grapheme' }) : null;\n\n/** Resolve a CSSLength to pixels. Plain numbers are px, `\"Nem\"` is N * fontSize. */\nexport function resolveCSSLength(value: CSSLength, fontSize: number): number {\n if (typeof value === 'number') return value;\n return parseFloat(value) * fontSize;\n}\n\nexport function graphemes(text: string): string[] {\n if (segmenter) return Array.from(segmenter.segment(text), (s) => s.segment);\n // Fallback for environments without Intl.Segmenter (Safari < 16.4). Splits by\n // code point so surrogate pairs stay intact; combining marks and ZWJ emoji\n // clusters degrade to one entry per code point, which is acceptable because\n // glyph data is keyed per code point anyway.\n return Array.from(text);\n}\n\n/**\n * Build the CSS `font-family` value for a bundle, including the full\n * (non-subsetted) family as fallback when the bundle was generated from a subset.\n */\nexport function cssFontFamily(bundle: TegakiBundle): string {\n if (bundle.fullFamily) return `'${bundle.family}', '${bundle.fullFamily}'`;\n return `'${bundle.family}'`;\n}\n\nexport type Coercible = string | number | boolean | null | undefined | readonly Coercible[];\n\nexport function coerceToString(value: unknown): string {\n if (value == null || typeof value === 'boolean') return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'bigint') return String(value);\n if (Array.isArray(value)) return value.map(coerceToString).join('');\n return '';\n}\n","import type { LineCap, TegakiGlyphData } from '../types.ts';\nimport { findEffect, findEffects, type ResolvedEffect } from './effects.ts';\nimport { type SubdividedStroke, subdivideStroke } from './strokeCache.ts';\nimport { resolveCSSLength } from './utils.ts';\n\ntype Stroke = TegakiGlyphData['s'][number];\n\ninterface GlyphPosition {\n /** X offset in CSS pixels */\n x: number;\n /** Y offset in CSS pixels (top of em square) */\n y: number;\n /** Font size in CSS pixels */\n fontSize: number;\n /** Units per em from the font */\n unitsPerEm: number;\n /** Font ascender in font units */\n ascender: number;\n /** Font descender in font units (negative) */\n descender: number;\n}\n\n// --- Color helpers ---\n\nfunction parseColor(color: string): [number, number, number, number] {\n const h = color.replace('#', '');\n if (h.length === 3) {\n return [parseInt(h[0]! + h[0]!, 16), parseInt(h[1]! + h[1]!, 16), parseInt(h[2]! + h[2]!, 16), 1];\n }\n if (h.length === 4) {\n return [parseInt(h[0]! + h[0]!, 16), parseInt(h[1]! + h[1]!, 16), parseInt(h[2]! + h[2]!, 16), parseInt(h[3]! + h[3]!, 16) / 255];\n }\n if (h.length === 8) {\n return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16), parseInt(h.slice(6, 8), 16) / 255];\n }\n return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16), 1];\n}\n\nfunction lerpColor(a: [number, number, number, number], b: [number, number, number, number], t: number): string {\n const r = Math.round(a[0] + (b[0] - a[0]) * t);\n const g = Math.round(a[1] + (b[1] - a[1]) * t);\n const bl = Math.round(a[2] + (b[2] - a[2]) * t);\n const al = a[3] + (b[3] - a[3]) * t;\n if (al >= 1) return `rgb(${r},${g},${bl})`;\n return `rgba(${r},${g},${bl},${al.toFixed(3)})`;\n}\n\nfunction gradientColor(progress: number, colors: string[], seed: number): string {\n if (colors.length === 0) return '#000';\n if (colors.length === 1) return colors[0]!;\n const t = (((progress + seed * 0.1) % 1) + 1) % 1;\n const scaledT = t * (colors.length - 1);\n const i = Math.min(Math.floor(scaledT), colors.length - 2);\n const frac = scaledT - i;\n return lerpColor(parseColor(colors[i]!), parseColor(colors[i + 1]!), frac);\n}\n\nfunction rainbowColor(progress: number, saturation: number, lightness: number, seed: number): string {\n const hue = (progress * 360 + seed * 137.5) % 360;\n return `hsl(${hue}, ${saturation}%, ${lightness}%)`;\n}\n\n// --- Noise helper for wobble ---\n\nfunction hash(x: number): number {\n let h = (x * 2654435761) | 0;\n h = ((h >>> 16) ^ h) * 0x45d9f3b;\n h = ((h >>> 16) ^ h) * 0x45d9f3b;\n h = (h >>> 16) ^ h;\n return (h & 0x7fffffff) / 0x7fffffff; // 0-1\n}\n\nfunction noise1d(x: number, seed: number): number {\n const i = Math.floor(x);\n const f = x - i;\n const t = f * f * (3 - 2 * f); // smoothstep\n return hash(i + seed * 7919) * (1 - t) + hash(i + 1 + seed * 7919) * t;\n}\n\n/** Default stroke easing: ease-out exponential. */\nfunction defaultStrokeEasing(t: number): number {\n return 1 - (1 - t) * (1 - t); // Ease out quad\n}\n\n/**\n * Draw a single glyph's strokes onto a canvas context, animated up to `localTime`.\n * `localTime` is seconds relative to this glyph's start (0 = glyph begins).\n *\n * `getSubdivided` returns a shared, cached subdivision of each stroke (in font\n * units, pre-wobble). The engine owns the cache and invalidates it when the\n * font, fontSize, or segment size changes; if omitted here, strokes are\n * subdivided inline each call (useful for testing).\n */\nexport function drawGlyph(\n ctx: CanvasRenderingContext2D,\n glyph: TegakiGlyphData,\n pos: GlyphPosition,\n localTime: number,\n lineCap: LineCap,\n color: string,\n effects: ResolvedEffect[] = [],\n seed = 0,\n getSubdivided?: (stroke: Stroke) => SubdividedStroke,\n strokeEasing: ((t: number) => number) | undefined = defaultStrokeEasing,\n strokeScale = 1,\n strokeStyleOverride?: string | CanvasGradient | CanvasPattern,\n) {\n // Default stroke paint. When a layout-spanning effect (e.g. `globalGradient`)\n // provides a CanvasGradient/Pattern via `strokeStyleOverride`, use it as the\n // default paint for main strokes and dots. `color` (always a string) is still\n // the source of truth for `shadowColor` — Canvas shadows don't accept\n // gradients. A per-stroke `strokeGradient` still overrides this per segment.\n const defaultStrokePaint: string | CanvasGradient | CanvasPattern = strokeStyleOverride ?? color;\n const scale = pos.fontSize / pos.unitsPerEm;\n const ox = pos.x;\n const oy = pos.y;\n\n const glowEffects = findEffects(effects, 'glow');\n const wobbleEffect = findEffect(effects, 'wobble');\n const pressureEffect = findEffect(effects, 'pressureWidth');\n const taperEffect = findEffect(effects, 'taper');\n const strokeGradientEffect = findEffect(effects, 'strokeGradient');\n\n // Pressure params (0 = uniform avg width, 1 = fully per-point width)\n const pressureAmount = pressureEffect ? Math.max(0, Math.min(pressureEffect.config.strength ?? 1, 1)) : 0;\n\n // Wobble params\n const wobbleAmplitude = wobbleEffect ? (wobbleEffect.config.amplitude ?? 1.5) : 0;\n const wobbleFrequency = wobbleEffect ? (wobbleEffect.config.frequency ?? 8) : 0;\n const wobbleMode = wobbleEffect?.config.mode ?? 'sine';\n const hasWobble = !!wobbleEffect;\n\n // Taper params\n const taperStart = taperEffect ? Math.max(0, Math.min(taperEffect.config.startLength ?? 0.15, 1)) : 0;\n const taperEnd = taperEffect ? Math.max(0, Math.min(taperEffect.config.endLength ?? 0.15, 1)) : 0;\n\n // Gradient params\n const gradientColors = strokeGradientEffect?.config.colors;\n const isRainbow = gradientColors === 'rainbow';\n const gradientColorStops = Array.isArray(gradientColors) ? gradientColors : undefined;\n const gradientSaturation = strokeGradientEffect?.config.saturation ?? 80;\n const gradientLightness = strokeGradientEffect?.config.lightness ?? 55;\n const hasStrokeGradient = !!strokeGradientEffect;\n\n // Effects that vary per-segment require splitting the polyline into\n // individual stroke() calls. Gradient also varies per-segment but via\n // strokeStyle, not lineWidth.\n const needsPerSegment = pressureAmount > 0 || !!taperEffect;\n\n // Fallback subdivider for callers that don't thread the engine's cache\n // (tests, standalone use). Engine always provides a cached version.\n const subdivide = getSubdivided ?? ((s: Stroke) => subdivideStroke(s, Infinity));\n\n // Wobble offsets (in font units). Evaluated once per rendered sub-vertex;\n // fractional `idx` keeps the wobble phase continuous across sub-segments.\n // dx depends on y; dy depends on x — the asymmetry keeps the perpendicular\n // wobble component out of phase with the along-stroke one.\n const wobbleDx = (_x: number, y: number, idx: number): number => {\n if (!hasWobble) return 0;\n if (wobbleMode === 'noise') return wobbleAmplitude * (noise1d(y * 0.1 + idx * 0.7, seed) * 2 - 1);\n return wobbleAmplitude * Math.sin(wobbleFrequency * (y * 0.01 + idx * 0.7) + seed);\n };\n const wobbleDy = (x: number, _y: number, idx: number): number => {\n if (!hasWobble) return 0;\n if (wobbleMode === 'noise') return wobbleAmplitude * (noise1d(x * 0.1 + idx * 0.5, seed * 1.3 + 1000) * 2 - 1);\n return wobbleAmplitude * Math.cos(wobbleFrequency * (x * 0.01 + idx * 0.5) + seed * 1.3);\n };\n\n // Helper: convert font-unit point to pixel\n const px = (x: number) => ox + x * scale;\n const py = (y: number) => oy + (y + pos.ascender) * scale;\n\n // Helper: get color for a given stroke progress\n const colorAt = (progress: number): string => {\n if (isRainbow) return rainbowColor(progress, gradientSaturation, gradientLightness, seed);\n if (gradientColorStops) return gradientColor(progress, gradientColorStops, seed);\n return color;\n };\n\n // Helper: taper multiplier (0-1) for a given stroke progress\n const taperMultiplier = (progress: number): number => {\n let m = 1;\n if (taperStart > 0 && progress < taperStart) m = Math.min(m, progress / taperStart);\n if (taperEnd > 0 && progress > 1 - taperEnd) m = Math.min(m, (1 - progress) / taperEnd);\n return m;\n };\n\n for (const stroke of glyph.s) {\n if (localTime < stroke.d) continue;\n const elapsed = localTime - stroke.d;\n const linearProgress = Math.min(elapsed / stroke.a, 1);\n const progress = strokeEasing ? strokeEasing(linearProgress) : linearProgress;\n\n const rawPts = stroke.p;\n if (rawPts.length === 0) continue;\n\n // --- Single-point dot (bypass cache; there is nothing to subdivide) ---\n if (rawPts.length === 1) {\n if (progress <= 0) continue;\n const p = rawPts[0]!;\n const dotX = px(p[0]! + wobbleDx(p[0]!, p[1]!, 0));\n const dotY = py(p[1]! + wobbleDy(p[0]!, p[1]!, 0));\n const baseLineWidth = Math.max(p[2]!, 0.5) * scale * strokeScale;\n const perPointDot = Math.max(p[2]!, 0.5) * scale * strokeScale;\n let dotWidth = baseLineWidth + (perPointDot - baseLineWidth) * pressureAmount;\n dotWidth *= taperMultiplier(0.5);\n\n // Glow passes for dots\n for (const glow of glowEffects) {\n ctx.save();\n ctx.shadowBlur = resolveCSSLength(glow.config.radius ?? 8, pos.fontSize);\n ctx.shadowColor = glow.config.color ?? color;\n ctx.shadowOffsetX = (glow.config.offsetX ?? 0) * scale;\n ctx.shadowOffsetY = (glow.config.offsetY ?? 0) * scale;\n ctx.fillStyle = glow.config.color ?? color;\n ctx.beginPath();\n if (lineCap === 'round') {\n ctx.arc(dotX, dotY, dotWidth / 2, 0, Math.PI * 2);\n } else {\n ctx.rect(dotX - dotWidth / 2, dotY - dotWidth / 2, dotWidth, dotWidth);\n }\n ctx.fill();\n ctx.restore();\n }\n\n // Main dot. strokeGradient needs a per-point color (rainbow hue or array\n // stop 0); otherwise let the default paint apply — a CanvasGradient from\n // globalGradient samples by dot position automatically.\n ctx.fillStyle = hasStrokeGradient ? colorAt(0) : defaultStrokePaint;\n ctx.beginPath();\n if (lineCap === 'round') {\n ctx.arc(dotX, dotY, dotWidth / 2, 0, Math.PI * 2);\n ctx.fill();\n } else {\n ctx.fillRect(dotX - dotWidth / 2, dotY - dotWidth / 2, dotWidth, dotWidth);\n }\n continue;\n }\n\n // --- Multi-point stroke: consume cached subdivision ---\n const cached = subdivide(stroke);\n const { vertices, totalLen, avgWidth } = cached;\n if (vertices.length < 2 || totalLen <= 0) continue;\n\n const drawLen = totalLen * progress;\n if (drawLen <= 0) continue;\n\n const baseLineWidth = Math.max(avgWidth, 0.5) * scale * strokeScale;\n\n // Binary search for the last fully-included vertex — i.e. the largest i\n // with vertices[i].cumLen <= drawLen.\n let lo = 0;\n let hi = vertices.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (vertices[mid]!.cumLen <= drawLen) lo = mid;\n else hi = mid - 1;\n }\n const lastIdx = lo;\n\n // Interpolate the tail of the last, partially-drawn sub-segment.\n let tailX = 0;\n let tailY = 0;\n let tailWidth = 0;\n let tailIdx = 0;\n let tailCumLen = 0;\n let hasTail = false;\n if (lastIdx + 1 < vertices.length && drawLen > vertices[lastIdx]!.cumLen) {\n const a = vertices[lastIdx]!;\n const b = vertices[lastIdx + 1]!;\n const segLen = b.cumLen - a.cumLen;\n const t = segLen > 0 ? (drawLen - a.cumLen) / segLen : 0;\n tailX = a.x + (b.x - a.x) * t;\n tailY = a.y + (b.y - a.y) * t;\n tailWidth = a.width + (b.width - a.width) * t;\n tailIdx = a.idx + (b.idx - a.idx) * t;\n tailCumLen = drawLen;\n hasTail = true;\n }\n\n // Pre-transform every visible vertex (raw + wobble + scale + translate)\n // exactly once — glow and main passes both iterate this array, and the\n // per-segment case needs stable endpoints across its N stroke() calls.\n const tcount = lastIdx + 1 + (hasTail ? 1 : 0);\n const txs: number[] = new Array(tcount);\n const tys: number[] = new Array(tcount);\n for (let i = 0; i <= lastIdx; i++) {\n const v = vertices[i]!;\n txs[i] = px(v.x + wobbleDx(v.x, v.y, v.idx));\n tys[i] = py(v.y + wobbleDy(v.x, v.y, v.idx));\n }\n if (hasTail) {\n txs[tcount - 1] = px(tailX + wobbleDx(tailX, tailY, tailIdx));\n tys[tcount - 1] = py(tailY + wobbleDy(tailX, tailY, tailIdx));\n }\n\n ctx.lineCap = lineCap;\n ctx.lineJoin = 'round';\n\n // Trace the full visible polyline as one Path2D primitive. Used for both\n // glow (where it's critical — shadowBlur cost is per stroke() call, so\n // coalescing into one call matters) and the no-per-segment-effect main\n // draw.\n const tracePolyline = () => {\n ctx.beginPath();\n ctx.moveTo(txs[0]!, tys[0]!);\n for (let i = 1; i < tcount; i++) ctx.lineTo(txs[i]!, tys[i]!);\n };\n\n // --- Glow passes (one stroke() call per glow over the full polyline) ---\n for (const glow of glowEffects) {\n ctx.save();\n ctx.shadowBlur = resolveCSSLength(glow.config.radius ?? 8, pos.fontSize);\n ctx.shadowColor = glow.config.color ?? color;\n ctx.shadowOffsetX = (glow.config.offsetX ?? 0) * scale;\n ctx.shadowOffsetY = (glow.config.offsetY ?? 0) * scale;\n ctx.strokeStyle = glow.config.color ?? color;\n ctx.lineWidth = baseLineWidth;\n tracePolyline();\n ctx.stroke();\n ctx.restore();\n }\n\n // --- Main stroke ---\n if (!needsPerSegment && !hasStrokeGradient) {\n // Fast path: single stroke() over the whole truncated polyline.\n ctx.strokeStyle = defaultStrokePaint;\n ctx.lineWidth = baseLineWidth;\n tracePolyline();\n ctx.stroke();\n } else {\n // Per-segment path: each sub-segment is its own mini-stroke so\n // lineWidth / strokeStyle can vary. Adjacent round-capped endpoints\n // overlap to read as a continuous line.\n const invTotalLen = 1 / totalLen;\n for (let i = 1; i < tcount; i++) {\n const aCum = i - 1 <= lastIdx ? vertices[i - 1]!.cumLen : tailCumLen;\n const bCum = i <= lastIdx ? vertices[i]!.cumLen : tailCumLen;\n const aWidth = i - 1 <= lastIdx ? vertices[i - 1]!.width : tailWidth;\n const bWidth = i <= lastIdx ? vertices[i]!.width : tailWidth;\n const midProgress = (aCum + bCum) * 0.5 * invTotalLen;\n\n let lw = baseLineWidth;\n if (needsPerSegment) {\n const perPoint = (aWidth + bWidth) * 0.5 * scale * strokeScale;\n const w = Math.max(baseLineWidth + (perPoint - baseLineWidth) * pressureAmount, 0.5 * scale * strokeScale);\n lw = w * taperMultiplier(midProgress);\n }\n ctx.lineWidth = lw;\n ctx.strokeStyle = hasStrokeGradient ? colorAt(midProgress) : defaultStrokePaint;\n ctx.beginPath();\n ctx.moveTo(txs[i - 1]!, tys[i - 1]!);\n ctx.lineTo(txs[i]!, tys[i]!);\n ctx.stroke();\n }\n }\n }\n}\n","import type { TegakiBundle } from '../types.ts';\n\nconst fontFaceCache = new Map<string, Promise<void>>();\n\n/**\n * Ensures the bundle's font face is loaded and available for rendering.\n * Resolves immediately if the font is already loaded.\n */\nexport async function ensureFontFace(bundle: TegakiBundle): Promise<void> {\n await ensureFont(bundle.family, bundle.fontUrl);\n}\n\nexport function ensureFont(family: string, url: string): Promise<void> | null {\n if (typeof document === 'undefined') return Promise.resolve();\n for (const face of document.fonts) {\n if (face.family === family) {\n if (face.status === 'loaded') return null;\n if (face.status === 'loading') return face.loaded.then(() => {});\n }\n }\n let cached = fontFaceCache.get(url);\n if (!cached) {\n cached = new FontFace(family, `url(${url})`, { featureSettings: \"'calt' 0, 'liga' 0\" }).load().then((loaded) => {\n document.fonts.add(loaded);\n });\n fontFaceCache.set(url, cached);\n }\n return cached;\n}\n","import { graphemes } from './utils.ts';\n\nexport interface TextLayout {\n /** Character indices per line */\n lines: number[][];\n /** X offset within line in em per character index */\n charOffsets: number[];\n /** Width in em per character index */\n charWidths: number[];\n}\n\n/**\n * Axis-aligned bounding box of the laid-out text in the ctx coordinate space\n * used by the engine's glyph loop (i.e. after `padH`/`padV` translation).\n * `width` is the max line advance; `height` is `lines.length * lineHeight`.\n */\nexport interface LayoutBBox {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Compute the text bounding box from a measured layout. Inputs are in CSS\n * pixels. Assumes the layout's char offsets are em-relative to the left edge\n * of each line (as produced by `computeTextLayout`).\n */\nexport function computeLayoutBbox(layout: TextLayout, fontSize: number, lineHeight: number): LayoutBBox {\n let maxRight = 0;\n for (const lineIndices of layout.lines) {\n for (const charIdx of lineIndices) {\n const offset = layout.charOffsets[charIdx] ?? 0;\n const width = layout.charWidths[charIdx] ?? 0;\n const right = (offset + width) * fontSize;\n if (right > maxRight) maxRight = right;\n }\n }\n return {\n x: 0,\n y: 0,\n width: maxRight,\n height: layout.lines.length * lineHeight,\n };\n}\n\n/**\n * Measure text layout using the Range API on an existing DOM element.\n * The element must already be in the document with correct text content,\n * font, line-height, white-space, and width styles applied.\n */\nexport function computeTextLayout(el: HTMLElement, fontSize: number): TextLayout;\n/**\n * Measure text layout by creating a temporary off-screen DOM element.\n */\nexport function computeTextLayout(text: string, fontSize: number, fontFamily: string, lineHeight: number, maxWidth: number): TextLayout;\nexport function computeTextLayout(\n elOrText: HTMLElement | string,\n fontSize: number,\n fontFamily?: string,\n lineHeight?: number,\n maxWidth?: number,\n): TextLayout {\n if (typeof elOrText === 'string') {\n return measureWithTempElement(elOrText, fontFamily!, fontSize, lineHeight!, maxWidth!);\n }\n return measureElement(elOrText, fontSize);\n}\n\nfunction measureElement(el: HTMLElement, fontSize: number): TextLayout {\n const textNode = el.firstChild;\n if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {\n return { lines: [], charOffsets: [], charWidths: [] };\n }\n\n const text = textNode.textContent ?? '';\n const chars = graphemes(text);\n if (!chars.length) return { lines: [], charOffsets: [], charWidths: [] };\n\n // Use element's left edge as reference so offsets are direction-agnostic.\n // For LTR the first char is near the left edge; for RTL it's near the right —\n // either way, subtracting elLeft produces correct visual x-positions.\n const elRect = el.getBoundingClientRect();\n const elLeft = elRect.left;\n // Ancestor CSS transforms (e.g. Remotion Studio's preview-fit scale) make\n // getClientRects() return pre-scale pixel values while getComputedStyle()\n // returns unscaled fontSize. Divide measured widths by the scale so the em\n // conversion matches fontSize. offsetWidth is layout-box width (unscaled).\n const scale = el.offsetWidth > 0 ? elRect.width / el.offsetWidth : 1;\n const range = document.createRange();\n\n const charOffsets: number[] = [];\n const charWidths: number[] = [];\n const lines: number[][] = [];\n let currentLine: number[] = [];\n let prevTop = -Infinity;\n let utf16Offset = 0;\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n\n if (char === '\\n') {\n charOffsets.push(0);\n charWidths.push(0);\n currentLine.push(i);\n lines.push(currentLine);\n currentLine = [];\n prevTop = -Infinity;\n utf16Offset += char.length;\n continue;\n }\n\n range.setStart(textNode, utf16Offset);\n range.setEnd(textNode, utf16Offset + char.length);\n const rects = range.getClientRects();\n utf16Offset += char.length;\n\n if (rects.length === 0) {\n charOffsets.push(0);\n charWidths.push(0);\n currentLine.push(i);\n continue;\n }\n\n const rect = rects[rects.length - 1]!;\n\n // A significant vertical shift signals a new line. Both rect.top and\n // prevTop are in scaled pixels, so compare against a scaled threshold.\n if (currentLine.length > 0 && rect.top - prevTop > fontSize * 0.25 * scale) {\n lines.push(currentLine);\n currentLine = [];\n }\n\n if (currentLine.length === 0) {\n prevTop = rect.top;\n }\n\n charOffsets.push((rect.left - elLeft) / scale / fontSize);\n charWidths.push(rect.width / scale / fontSize);\n currentLine.push(i);\n }\n if (currentLine.length > 0) lines.push(currentLine);\n\n return { lines, charOffsets, charWidths };\n}\n\nfunction measureWithTempElement(text: string, fontFamily: string, fontSize: number, lineHeight: number, maxWidth: number): TextLayout {\n const el = document.createElement('div');\n el.style.position = 'absolute';\n el.style.left = '-9999px';\n el.style.top = '-9999px';\n el.style.visibility = 'hidden';\n el.style.fontFamily = fontFamily;\n el.style.fontSize = `${fontSize}px`;\n el.style.lineHeight = `${lineHeight}px`;\n el.style.whiteSpace = 'pre-wrap';\n el.style.overflowWrap = 'break-word';\n el.style.width = `${maxWidth}px`;\n el.textContent = text;\n document.body.appendChild(el);\n\n const result = measureElement(el, fontSize);\n\n document.body.removeChild(el);\n return result;\n}\n","import type { TegakiBundle } from '../types.ts';\nimport { graphemes } from './utils.ts';\n\nexport interface TimelineConfig {\n /** Pause between glyphs (seconds). Default: `0.1` */\n glyphGap?: number;\n /** Pause after a space character (seconds). Default: `0.15` */\n wordGap?: number;\n /** Pause after a newline / line break (seconds). Default: `0.3` */\n lineGap?: number;\n /** Duration for characters without glyph data (seconds). Default: `0.2` */\n unknownDuration?: number;\n /**\n * Easing function for each stroke's animation progress `(0–1) → (0–1)`.\n * Applied per-stroke to map linear draw progress to eased progress.\n * Default: ease-out exponential (`1 - 2^(-10t)`).\n */\n strokeEasing?: (t: number) => number;\n /**\n * Easing function for each glyph's local time progress `(0–1) → (0–1)`.\n * Applied per-glyph to map linear time within the glyph to eased time.\n * Default: linear (no easing).\n */\n glyphEasing?: (t: number) => number;\n}\n\nconst DEFAULTS = {\n glyphGap: 0.1,\n wordGap: 0.15,\n lineGap: 0.3,\n unknownDuration: 0.2,\n};\n\nexport interface TimelineEntry {\n char: string;\n offset: number;\n duration: number;\n hasGlyph: boolean;\n}\n\nexport interface Timeline {\n entries: TimelineEntry[];\n totalDuration: number;\n}\n\nexport function computeTimeline(text: string, font: TegakiBundle, config?: TimelineConfig): Timeline {\n const glyphGap = config?.glyphGap ?? DEFAULTS.glyphGap;\n const wordGap = config?.wordGap ?? DEFAULTS.wordGap;\n const lineGap = config?.lineGap ?? DEFAULTS.lineGap;\n const unknownDuration = config?.unknownDuration ?? DEFAULTS.unknownDuration;\n\n const chars = graphemes(text);\n const entries: TimelineEntry[] = [];\n let offset = 0;\n for (const char of chars) {\n const glyph = font.glyphData[char];\n const hasGlyph = !!glyph;\n const isLineBreak = char === '\\n';\n const isWhitespace = isLineBreak || /^\\s+$/.test(char);\n const duration = isWhitespace ? 0 : hasGlyph ? (glyph.t ?? unknownDuration) : unknownDuration;\n entries.push({ char, offset, duration, hasGlyph });\n offset += duration;\n\n // Gap after this character\n if (isLineBreak) {\n offset += lineGap;\n } else if (isWhitespace) {\n offset += wordGap;\n } else {\n offset += glyphGap;\n }\n }\n // Remove trailing gap\n if (entries.length > 0) {\n const lastChar = chars[chars.length - 1]!;\n const trailingGap = lastChar === '\\n' ? lineGap : /^\\s+$/.test(lastChar) ? wordGap : glyphGap;\n offset -= trailingGap;\n }\n return { entries, totalDuration: Math.max(0, offset) };\n}\n","/**\n * Current bundle format version. Incremented when the bundle format changes\n * in a way that older engines cannot consume.\n */\nexport const BUNDLE_VERSION = 0;\n\n/**\n * Set of bundle versions that this engine can consume. The engine logs a\n * console warning (once per bundle) when it encounters a version outside\n * this set.\n */\nexport const COMPATIBLE_BUNDLE_VERSIONS: ReadonlySet<number> = new Set([BUNDLE_VERSION]);\n\nexport type LineCap = 'round' | 'butt' | 'square';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface TimedPoint extends Point {\n t: number;\n width: number;\n}\n\nexport interface BBox {\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n}\n\nexport interface Stroke {\n points: TimedPoint[];\n order: number;\n length: number;\n animationDuration: number;\n delay: number;\n}\n\nexport interface GlyphData {\n char: string;\n unicode: number;\n advanceWidth: number;\n boundingBox: BBox;\n path: string;\n skeleton: Point[][];\n strokes: Stroke[];\n totalLength: number;\n totalAnimationDuration: number;\n}\n\nexport interface FontOutput {\n font: {\n family: string;\n style: string;\n unitsPerEm: number;\n ascender: number;\n descender: number;\n lineCap: LineCap;\n };\n glyphs: Record<string, GlyphData>;\n}\n\nexport interface PathCommand {\n type: 'M' | 'L' | 'Q' | 'C' | 'Z';\n x: number;\n y: number;\n x1?: number;\n y1?: number;\n x2?: number;\n y2?: number;\n}\n\n/**\n * Compact glyph data for rendering.\n * - `w`: advance width\n * - `t`: total animation duration\n * - `s`: strokes, each with `p` (points as `[x, y, width]` tuples), `d` (delay), `a` (animation duration)\n */\nexport interface TegakiGlyphData {\n w: number;\n t: number;\n s: {\n p: ([x: number, y: number, width: number] | number[])[];\n d: number;\n a: number;\n }[];\n}\n\ntype BaseEffectConfig = { enabled?: boolean };\n\n/** A length value: plain number is pixels, string `\"${number}em\"` is relative to font size. */\nexport type CSSLength = number | `${number}em`;\n\nexport type TegakiEffectConfigs = {\n glow: BaseEffectConfig & { radius?: CSSLength; color?: string; offsetX?: number; offsetY?: number };\n wobble: BaseEffectConfig & { amplitude?: number; frequency?: number; mode?: 'sine' | 'noise' };\n pressureWidth: BaseEffectConfig & { strength?: number };\n taper: BaseEffectConfig & { startLength?: number; endLength?: number };\n strokeGradient: BaseEffectConfig & { colors?: string[] | 'rainbow'; saturation?: number; lightness?: number };\n /**\n * Linear gradient spanning the whole text bounding box (CSS `background-clip: text` style).\n * Unlike `strokeGradient`, the color stops map to canvas-space position — the leftmost pixel of\n * the first glyph is `colors[0]` and the rightmost pixel of the last glyph is `colors[N]`,\n * regardless of stroke boundaries.\n *\n * - `colors`: ordered color stops (2+ stops).\n * - `angle`: direction in degrees. `0` = left→right (default). `90` = top→bottom.\n * Positive values rotate clockwise (y-down screen convention).\n */\n globalGradient: BaseEffectConfig & { colors?: string[]; angle?: number };\n};\n\nexport type TegakiEffectName = keyof TegakiEffectConfigs;\n\n/** Effects that can only appear once (cannot be used with custom keys). */\nexport type TegakiSingletonEffectName = 'pressureWidth' | 'wobble' | 'taper' | 'strokeGradient' | 'globalGradient';\n\n/** Effects that can be duplicated with custom keys. */\nexport type TegakiMultiEffectName = Exclude<TegakiEffectName, TegakiSingletonEffectName>;\n\ntype TegakiCustomEffect = {\n [K in TegakiMultiEffectName]: TegakiEffectConfigs[K] & { effect: K; order?: number };\n}[TegakiMultiEffectName];\n\n/** Validates an effects object: known keys infer `effect`, unknown keys require it (singleton effects excluded). */\nexport type TegakiEffects<T> = {\n [K in keyof T]: K extends TegakiEffectName\n ? (TegakiEffectConfigs[K] & { effect?: K; order?: number }) | boolean\n : TegakiCustomEffect | boolean;\n};\n\nexport interface TegakiBundle {\n /** Bundle format version. Used by the engine to warn about incompatible bundles. */\n version?: number;\n family: string;\n /**\n * Original font family name, used as a CSS fallback for characters not in\n * the generated glyph set. Present when the bundle was generated from a\n * subset of the font (the default). When absent, `family` is the original\n * name (full-font bundle).\n */\n fullFamily?: string;\n lineCap: LineCap;\n fontUrl: string;\n /** URL to the full (non-subsetted) font file bundled for fallback rendering. */\n fullFontUrl?: string;\n fontFaceCSS: string;\n unitsPerEm: number;\n ascender: number;\n descender: number;\n glyphData: Record<string, TegakiGlyphData>;\n}\n","import { COMPATIBLE_BUNDLE_VERSIONS, type TegakiBundle } from '../types.ts';\n\nconst bundles = new Map<string, TegakiBundle>();\nconst warnedBundles = new Set<TegakiBundle>();\n\nfunction checkBundleVersion(bundle: TegakiBundle): void {\n if (warnedBundles.has(bundle)) return;\n if (bundle.version == null || !COMPATIBLE_BUNDLE_VERSIONS.has(bundle.version)) {\n warnedBundles.add(bundle);\n console.warn(\n `[tegaki] Bundle \"${bundle.family}\" has version ${bundle.version ?? 'undefined'}, ` +\n `but this engine supports versions [${[...COMPATIBLE_BUNDLE_VERSIONS].join(', ')}]. ` +\n 'The bundle may not render correctly. Regenerate it with a compatible version of tegaki-generator.',\n );\n }\n}\n\n/** Register a font bundle so it can be referenced by family name. */\nexport function registerBundle(bundle: TegakiBundle): void {\n checkBundleVersion(bundle);\n bundles.set(bundle.family, bundle);\n}\n\n/** Look up a registered bundle by family name. */\nexport function getBundle(family: string): TegakiBundle | undefined {\n return bundles.get(family);\n}\n\nexport function resolveBundle(font: TegakiBundle | string | undefined): TegakiBundle | undefined {\n if (typeof font === 'string') {\n const bundle = getBundle(font);\n if (!bundle) throw new Error(`TegakiEngine: no bundle registered for \"${font}\". Call TegakiEngine.registerBundle() first.`);\n return bundle;\n }\n if (font) checkBundleVersion(font);\n return font;\n}\n","import { BUNDLE_VERSION, type LineCap, type TegakiBundle, type TegakiGlyphData } from '../types.ts';\n\n/**\n * Creates a {@link TegakiBundle} from its constituent parts.\n *\n * Useful when loading font data from a CDN or other source where the\n * pre-built bundle modules aren't available:\n *\n * ```js\n * const glyphData = await fetch('.../glyphData.json').then(r => r.json());\n * const bundle = createBundle({\n * family: 'Caveat',\n * fontUrl: '.../caveat.ttf',\n * glyphData,\n * });\n * ```\n */\nexport function createBundle({\n family,\n fullFamily,\n fontUrl,\n fullFontUrl,\n glyphData,\n lineCap = 'round',\n unitsPerEm = 1000,\n ascender = 800,\n descender = -200,\n}: {\n family: string;\n fullFamily?: string;\n fontUrl: string;\n fullFontUrl?: string;\n glyphData: Record<string, TegakiGlyphData>;\n lineCap?: LineCap;\n unitsPerEm?: number;\n ascender?: number;\n descender?: number;\n}): TegakiBundle {\n const rules = [`@font-face { font-family: '${family}'; src: url(${fontUrl}); }`];\n if (fullFamily && fullFontUrl) {\n rules.push(`@font-face { font-family: '${fullFamily}'; src: url(${fullFontUrl}); }`);\n }\n return {\n version: BUNDLE_VERSION,\n family,\n fullFamily,\n lineCap,\n fontUrl,\n fullFontUrl,\n fontFaceCSS: rules.join(' '),\n unitsPerEm,\n ascender,\n descender,\n glyphData,\n };\n}\n","export const CSS_TIME = '--tegaki-time';\nexport const CSS_PROGRESS = '--tegaki-progress';\nexport const CSS_DURATION = '--tegaki-duration';\n\nexport const PADDING_H_EM = 0.2;\nexport const MIN_LINE_HEIGHT_EM = 1.8;\nexport const MIN_PADDING_V_EM = 0.2;\n\n// Register custom properties so they are animatable (typed as <number>).\n// Deferred to first use to avoid running at import time during SSR.\nlet cssPropertiesRegistered = false;\nexport function registerCssProperties() {\n if (cssPropertiesRegistered) return;\n cssPropertiesRegistered = true;\n if (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {\n for (const prop of [CSS_TIME, CSS_PROGRESS, CSS_DURATION]) {\n try {\n CSS.registerProperty({ name: prop, syntax: '<number>', inherits: true, initialValue: '0' });\n } catch {\n // Already registered — ignore.\n }\n }\n }\n}\n","import { findEffect, findEffects, type ResolvedEffect } from './effects.ts';\nimport { resolveCSSLength } from './utils.ts';\n\n/**\n * Draw a fallback glyph (plain text) with applicable effects (glow, strokeGradient, wobble).\n */\nexport function drawFallbackGlyph(\n ctx: CanvasRenderingContext2D,\n char: string,\n x: number,\n baseline: number,\n fontSize: number,\n fontFamily: string,\n color: string,\n effects: ResolvedEffect[] = [],\n seed = 0,\n) {\n const glowEffects = findEffects(effects, 'glow');\n const wobbleEffect = findEffect(effects, 'wobble');\n const strokeGradientEffect = findEffect(effects, 'strokeGradient');\n\n // Wobble offsets\n let dx = 0;\n let dy = 0;\n if (wobbleEffect) {\n const amplitude = (wobbleEffect.config.amplitude ?? 1.5) * (fontSize / 100);\n const frequency = wobbleEffect.config.frequency ?? 8;\n dx = amplitude * Math.sin(frequency * (baseline * 0.01) + seed);\n dy = amplitude * Math.cos(frequency * (x * 0.01) + seed * 1.3);\n }\n\n const drawX = x + dx;\n const drawY = baseline + dy;\n\n // Gradient / rainbow color\n let fillColor = color;\n if (strokeGradientEffect) {\n const colors = strokeGradientEffect.config.colors;\n if (colors === 'rainbow') {\n const saturation = strokeGradientEffect.config.saturation ?? 80;\n const lightness = strokeGradientEffect.config.lightness ?? 55;\n const hue = (seed * 137.5) % 360;\n fillColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`;\n } else if (Array.isArray(colors) && colors.length > 0) {\n fillColor = colors[Math.floor(seed) % colors.length]!;\n }\n }\n\n ctx.save();\n ctx.font = `${fontSize}px ${fontFamily}`;\n ctx.textBaseline = 'alphabetic';\n\n // Glow passes\n for (const glow of glowEffects) {\n ctx.save();\n ctx.shadowBlur = resolveCSSLength(glow.config.radius ?? 8, fontSize);\n ctx.shadowColor = glow.config.color ?? color;\n ctx.shadowOffsetX = glow.config.offsetX ?? 0;\n ctx.shadowOffsetY = glow.config.offsetY ?? 0;\n ctx.fillStyle = glow.config.color ?? color;\n ctx.fillText(char, drawX, drawY);\n ctx.restore();\n }\n\n // Main text\n ctx.fillStyle = fillColor;\n ctx.fillText(char, drawX, drawY);\n\n ctx.restore();\n}\n","import { CSS_DURATION, CSS_PROGRESS, CSS_TIME } from '../lib/css-properties.ts';\nimport { computeTimeline } from '../lib/timeline.ts';\nimport { cssFontFamily } from '../lib/utils.ts';\nimport { resolveBundle } from './bundle-registry.ts';\nimport type { CreateElementFn, TegakiEngineOptions } from './types.ts';\n\nexport const PAD_V_CSS = 'max(0.2em, 0.9em - 0.5lh)';\n\nexport function buildRootProps(options: TegakiEngineOptions): Record<string, any> {\n const text = options.text ?? '';\n const font = resolveBundle(options.font);\n const fontFamily = font ? cssFontFamily(font) : undefined;\n\n const duration = text && font ? computeTimeline(text, font, options.timing).totalDuration : 0;\n const timeObj = typeof options.time === 'object' ? options.time : null;\n const rawTime =\n typeof options.time === 'number'\n ? options.time\n : timeObj?.mode === 'controlled'\n ? timeObj.unit === 'progress'\n ? timeObj.value * duration\n : timeObj.value\n : timeObj?.mode === 'uncontrolled'\n ? (timeObj.initialTime ?? 0)\n : 0;\n const easing = timeObj?.mode === 'uncontrolled' ? timeObj.easing : undefined;\n const time = easing && duration > 0 ? easing(rawTime / duration) * duration : rawTime;\n const progress = duration > 0 ? time / duration : 0;\n\n return {\n 'data-tegaki': 'root',\n style: {\n position: 'relative',\n maxWidth: '100%',\n width: 'auto',\n height: 'auto',\n fontFamily,\n direction: options.direction ?? undefined,\n [CSS_DURATION]: duration,\n [CSS_TIME]: time,\n [CSS_PROGRESS]: progress,\n },\n };\n}\n\nexport function buildChildren<T>(options: TegakiEngineOptions, h: CreateElementFn<T>): T {\n const text = options.text ?? '';\n const isCss = options.time === 'css' || (typeof options.time === 'object' && options.time?.mode === 'css');\n const showOverlay = options.showOverlay;\n\n return h(\n 'span',\n { style: { display: 'block', position: 'relative' } },\n h('span', {\n 'data-tegaki': 'sentinel',\n 'aria-hidden': 'true',\n style: {\n position: 'absolute',\n width: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n fontSize: 'inherit',\n lineHeight: 'inherit',\n visibility: 'hidden',\n transition: isCss\n ? `font-size 0.001s, line-height 0.001s, color 0.001s, ${CSS_PROGRESS} 0.001s`\n : 'font-size 0.001s, line-height 0.001s, color 0.001s',\n },\n }),\n h(\n 'canvas',\n {\n 'data-tegaki': 'canvas',\n 'aria-hidden': 'true',\n style: {\n // `inset` shorthand not supported by Safari < 14.1 — expand to longhand.\n position: 'absolute',\n top: `calc(-1 * ${PAD_V_CSS})`,\n right: '-0.2em',\n bottom: `calc(-1 * ${PAD_V_CSS})`,\n left: '-0.2em',\n width: 'calc(100% + 0.4em)',\n height: `calc(100% + 2 * ${PAD_V_CSS})`,\n pointerEvents: 'none',\n overflow: 'visible',\n },\n },\n h(\n 'span',\n {\n 'data-tegaki': 'canvas-fallback',\n style: { display: 'inline-block', padding: `${PAD_V_CSS} 0.2em` },\n },\n text,\n ),\n ),\n h(\n 'span',\n {\n 'data-tegaki': 'overlay',\n style: {\n display: 'block',\n userSelect: 'auto',\n whiteSpace: 'pre-wrap',\n overflowWrap: 'break-word',\n paddingInlineEnd: 1,\n WebkitTextFillColor: showOverlay ? undefined : 'transparent',\n color: showOverlay ? 'rgba(255, 0, 0, 0.4)' : undefined,\n },\n },\n text,\n ),\n );\n}\n\n// ---------------------------------------------------------------------------\n// DOM createElement helper (for vanilla JS constructor)\n// ---------------------------------------------------------------------------\n\nexport function domCreateElement(tag: string, props: Record<string, any>, ...children: (HTMLElement | string)[]): HTMLElement {\n const el = document.createElement(tag);\n for (const [key, value] of Object.entries(props)) {\n if (key === 'style' && typeof value === 'object') {\n for (const [k, v] of Object.entries(value as Record<string, any>)) {\n if (v !== undefined && v !== null) {\n if (k.startsWith('--')) {\n el.style.setProperty(k, String(v));\n } else {\n (el.style as any)[k] = typeof v === 'number' && k !== 'opacity' && k !== 'zIndex' ? `${v}px` : v;\n }\n }\n }\n } else if (key === 'aria-hidden') {\n el.setAttribute('aria-hidden', String(value));\n } else if (key.startsWith('data-')) {\n el.setAttribute(key, String(value));\n }\n }\n for (const child of children) {\n if (typeof child === 'string') {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n return el;\n}\n","import {\n CSS_DURATION,\n CSS_PROGRESS,\n CSS_TIME,\n MIN_LINE_HEIGHT_EM,\n MIN_PADDING_V_EM,\n PADDING_H_EM,\n registerCssProperties,\n} from '../lib/css-properties.ts';\nimport { drawFallbackGlyph } from '../lib/drawFallbackGlyph.ts';\nimport { drawGlyph } from '../lib/drawGlyph.ts';\nimport {\n findEffect,\n getEffectDefinition,\n hasRenderHooks,\n type RenderStageContext,\n type ResolvedEffect,\n resolveEffects,\n} from '../lib/effects.ts';\nimport { ensureFont } from '../lib/font.ts';\nimport { type SubdividedStroke, subdivideStroke } from '../lib/strokeCache.ts';\nimport type { TextLayout } from '../lib/textLayout.ts';\nimport { computeLayoutBbox, computeTextLayout } from '../lib/textLayout.ts';\nimport type { Timeline, TimelineConfig, TimelineEntry } from '../lib/timeline.ts';\nimport { computeTimeline } from '../lib/timeline.ts';\nimport { cssFontFamily, graphemes } from '../lib/utils.ts';\nimport type { TegakiBundle, TegakiGlyphData } from '../types.ts';\nimport { getBundle, registerBundle as registryRegisterBundle, resolveBundle } from './bundle-registry.ts';\nimport { buildChildren, buildRootProps, domCreateElement } from './render-elements.ts';\nimport type { CreateElementFn, TegakiEngineOptions, TegakiQuality, TimeControlMode, TimeControlProp } from './types.ts';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction resolveTimeControl(prop: TimeControlProp): TimeControlMode[keyof TimeControlMode] {\n if (prop == null) return { mode: 'uncontrolled' };\n if (typeof prop === 'number') return { mode: 'controlled', value: prop };\n if (prop === 'css') return { mode: 'css' };\n return prop;\n}\n\n// ---------------------------------------------------------------------------\n// TegakiEngine\n// ---------------------------------------------------------------------------\n\nexport class TegakiEngine {\n // --- Bundle registry (delegates to bundle-registry module) ---\n\n /** Register a font bundle so it can be referenced by family name. */\n static registerBundle(bundle: TegakiBundle): void {\n registryRegisterBundle(bundle);\n }\n\n /** Look up a registered bundle by family name. */\n static getBundle(family: string): TegakiBundle | undefined {\n return getBundle(family);\n }\n\n // --- DOM elements ---\n private _rootEl: HTMLElement;\n private _contentEl: HTMLElement | null = null; // non-null only in non-adopt mode\n private _sentinelEl: HTMLSpanElement;\n private _canvasEl: HTMLCanvasElement;\n private _overlayEl: HTMLElement;\n private _canvasFallbackEl: HTMLSpanElement;\n private _maskCanvas: HTMLCanvasElement | null = null;\n\n // --- Options ---\n private _text = '';\n private _font: TegakiBundle | null = null;\n private _timeControl: TimeControlMode[keyof TimeControlMode] = { mode: 'uncontrolled' };\n private _effects: Record<string, any> | undefined;\n private _timing: TimelineConfig | undefined;\n private _quality: TegakiQuality | undefined;\n private _showOverlay = false;\n private _onComplete: (() => void) | undefined;\n private _direction: 'ltr' | 'rtl' | undefined;\n\n // --- Derived / cached ---\n private _resolvedEffects: ResolvedEffect[] = resolveEffects(undefined);\n private _seed: number;\n private _timeline: Timeline = { entries: [] as TimelineEntry[], totalDuration: 0 };\n private _layout: TextLayout | null = null;\n private _layoutKey = '';\n private _fontReady = false;\n\n // Stroke subdivision cache. Shared across every instance of the same glyph\n // at the current (font, fontSize, segmentSize, effects-need-subdivision)\n // state. Replaced wholesale when that state changes — entries in the old\n // WeakMap are orphaned and GC'd along with the map.\n private _strokeCache: WeakMap<TegakiGlyphData['s'][number], SubdividedStroke> = new WeakMap();\n private _strokeCacheKey = '';\n\n // --- Measured from DOM ---\n private _containerWidth = 0;\n private _fontSize = 0;\n private _lineHeight = 0;\n private _currentColor = '';\n\n // --- Playback state ---\n private _internalTime = 0;\n private _cssTime = 0;\n private _playing = true;\n private _smoothedBoost = 0;\n private _delayRemaining = 0;\n private _loopGapRemaining = 0;\n private _lastTs: number | null = null;\n private _rafId = 0;\n private _prevCompleted = false;\n private _prefersReducedMotion = false;\n private _destroyed = false;\n\n // --- Observers & listeners ---\n private _resizeObserver: ResizeObserver;\n private _mql: MediaQueryList | null = null;\n\n /**\n * Returns the props (including style) that should be applied to the container element,\n * plus the inner content tree rendered via a framework `createElement` callback.\n *\n * Each child element receives a `data-tegaki` attribute so the engine can adopt\n * pre-rendered elements later via `new TegakiEngine(container, { adopt: true })`.\n */\n static renderElements<T>(\n options: TegakiEngineOptions,\n createElement: CreateElementFn<T>,\n ): { rootProps: Record<string, any>; content: T } {\n return {\n rootProps: buildRootProps(options),\n content: buildChildren(options, createElement),\n };\n }\n\n constructor(container: HTMLElement, options?: TegakiEngineOptions & { adopt?: boolean }) {\n registerCssProperties();\n this._seed = Math.random() * 1000;\n\n // --- Resolve DOM elements ---\n // The container itself is the root element. In adopt mode, the adapter has\n // already rendered children inside it. In non-adopt mode, we create them.\n this._rootEl = container;\n\n if (options?.adopt) {\n // Adopt pre-rendered children (created by renderElements)\n } else {\n // Create DOM from scratch\n const content = buildChildren(options ?? {}, domCreateElement);\n container.appendChild(content);\n this._contentEl = content;\n // Apply root styles to the container\n const rootProps = buildRootProps(options ?? {});\n for (const [key, value] of Object.entries(rootProps.style as Record<string, any>)) {\n if (value !== undefined && value !== null) {\n if (key.startsWith('--')) {\n container.style.setProperty(key, String(value));\n } else {\n (container.style as any)[key] = typeof value === 'number' && key !== 'opacity' && key !== 'zIndex' ? `${value}px` : value;\n }\n }\n }\n container.dataset.tegaki = 'root';\n }\n\n this._sentinelEl = container.querySelector('[data-tegaki=\"sentinel\"]') as HTMLSpanElement;\n this._canvasEl = container.querySelector('[data-tegaki=\"canvas\"]') as HTMLCanvasElement;\n this._canvasFallbackEl = container.querySelector('[data-tegaki=\"canvas-fallback\"]') as HTMLSpanElement;\n this._overlayEl = container.querySelector('[data-tegaki=\"overlay\"]') as HTMLElement;\n\n // --- ResizeObserver ---\n this._resizeObserver = new ResizeObserver(this._onResize);\n this._resizeObserver.observe(this._rootEl);\n\n // --- Sentinel transitions ---\n this._sentinelEl.addEventListener('transitionend', this._onSentinelTransition);\n\n // --- Reduced motion ---\n if (typeof window !== 'undefined') {\n this._mql = window.matchMedia('(prefers-reduced-motion: reduce)');\n this._prefersReducedMotion = this._mql.matches;\n if (this._mql.addEventListener) this._mql.addEventListener('change', this._onReducedMotionChange);\n // Safari < 14 only exposes the deprecated addListener API.\n else this._mql.addListener(this._onReducedMotionChange);\n }\n\n // --- Initial measurement (must run before update so layout has valid dimensions) ---\n this._measure();\n\n // --- Apply initial options ---\n if (options) this.update(options);\n }\n\n // =========================================================================\n // Public API\n // =========================================================================\n\n get currentTime(): number {\n const tc = this._timeControl;\n if (tc.mode === 'css') return this._cssTime;\n if (tc.mode === 'controlled') return tc.unit === 'progress' ? tc.value * this._timeline.totalDuration : tc.value;\n const totalDur = this._timeline.totalDuration;\n if (tc.easing && totalDur > 0) {\n return tc.easing(this._internalTime / totalDur) * totalDur;\n }\n return this._internalTime;\n }\n\n get duration(): number {\n return this._timeline.totalDuration;\n }\n\n get isPlaying(): boolean {\n return this._playing;\n }\n\n get isComplete(): boolean {\n const totalDur = this._timeline.totalDuration;\n if (totalDur === 0) return false;\n // For uncontrolled, check linear time so easing curves that overshoot/undershoot\n // the endpoints do not prematurely/belatedly trip completion.\n const tc = this._timeControl;\n if (tc.mode === 'uncontrolled') return this._internalTime >= totalDur;\n return this.currentTime >= totalDur;\n }\n\n get element(): HTMLElement {\n return this._rootEl;\n }\n\n play(): void {\n if (this._timeControl.mode !== 'uncontrolled') return;\n this._playing = true;\n this._evaluatePlayback();\n }\n\n pause(): void {\n if (this._timeControl.mode !== 'uncontrolled') return;\n this._playing = false;\n this._evaluatePlayback();\n }\n\n seek(time: number): void {\n if (this._timeControl.mode !== 'uncontrolled') return;\n this._internalTime = Math.max(0, Math.min(time, this._timeline.totalDuration));\n this._delayRemaining = 0;\n this._loopGapRemaining = 0;\n this._checkCompletion();\n this._notifyTimeChange();\n this._render();\n this._updateCssProperties();\n }\n\n restart(): void {\n if (this._timeControl.mode !== 'uncontrolled') return;\n this._internalTime = 0;\n this._playing = true;\n this._prevCompleted = false;\n this._delayRemaining = this._timeControl.delay ?? 0;\n this._loopGapRemaining = 0;\n this._notifyTimeChange();\n this._evaluatePlayback();\n }\n\n update(options: Partial<TegakiEngineOptions>): void {\n if (this._destroyed) return;\n\n let dirtyTimeline = false;\n let dirtyLayout = false;\n let dirtyRender = false;\n let dirtyPlayback = false;\n\n if ('text' in options) {\n const nextText = (options.text ?? '').replace(/\\r\\n?/g, '\\n');\n if (nextText !== this._text) {\n this._text = nextText;\n dirtyTimeline = true;\n dirtyLayout = true;\n }\n }\n\n if ('font' in options) {\n const resolved = resolveBundle(options.font) ?? null;\n if (resolved !== this._font) {\n this._loadFont(resolved);\n dirtyTimeline = true;\n dirtyLayout = true;\n dirtyPlayback = true;\n }\n }\n\n if ('time' in options) {\n const newTc = resolveTimeControl(options.time);\n const oldTc = this._timeControl;\n\n // Detect meaningful changes\n const modeChanged = newTc.mode !== oldTc.mode;\n const controlledValueChanged =\n newTc.mode === 'controlled' && oldTc.mode === 'controlled' && (newTc.value !== oldTc.value || newTc.unit !== oldTc.unit);\n const uncontrolledChanged =\n newTc.mode === 'uncontrolled' &&\n oldTc.mode === 'uncontrolled' &&\n (newTc.speed !== oldTc.speed ||\n newTc.duration !== oldTc.duration ||\n newTc.playing !== oldTc.playing ||\n newTc.loop !== oldTc.loop ||\n newTc.delay !== oldTc.delay ||\n newTc.loopGap !== oldTc.loopGap ||\n newTc.catchUp !== oldTc.catchUp ||\n newTc.easing !== oldTc.easing);\n\n if (modeChanged || controlledValueChanged || uncontrolledChanged) {\n this._timeControl = newTc;\n\n if (newTc.mode === 'uncontrolled') {\n this._playing = newTc.playing ?? true;\n const oldDelay = oldTc.mode === 'uncontrolled' ? (oldTc.delay ?? 0) : 0;\n const newDelay = newTc.delay ?? 0;\n if (modeChanged || oldDelay !== newDelay) {\n this._delayRemaining = newDelay;\n this._loopGapRemaining = 0;\n }\n }\n\n dirtyPlayback = true;\n dirtyRender = true;\n\n // Update sentinel transition for css mode\n this._updateSentinelTransition();\n }\n }\n\n if ('effects' in options && options.effects !== this._effects) {\n this._effects = options.effects as Record<string, any>;\n this._resolvedEffects = resolveEffects(this._effects);\n dirtyRender = true;\n }\n\n if ('timing' in options && options.timing !== this._timing) {\n this._timing = options.timing;\n dirtyTimeline = true;\n }\n\n if ('quality' in options && options.quality !== this._quality) {\n this._quality = options.quality;\n dirtyRender = true;\n }\n\n if ('direction' in options && options.direction !== this._direction) {\n this._direction = options.direction;\n dirtyLayout = true;\n dirtyRender = true;\n }\n\n if ('showOverlay' in options && options.showOverlay !== this._showOverlay) {\n this._showOverlay = options.showOverlay ?? false;\n this._updateOverlayStyle();\n dirtyRender = true;\n }\n\n if ('onComplete' in options) {\n this._onComplete = options.onComplete;\n }\n\n // --- Recompute ---\n if (dirtyTimeline) this._recomputeTimeline();\n if (dirtyRender || dirtyTimeline || dirtyLayout) this._updateDom();\n if (dirtyLayout) this._recomputeLayout();\n if (dirtyPlayback) this._evaluatePlayback();\n if (dirtyRender || dirtyTimeline || dirtyLayout) this._render();\n }\n\n destroy(): void {\n this._destroyed = true;\n this._stopLoop();\n this._resizeObserver.disconnect();\n this._sentinelEl.removeEventListener('transitionend', this._onSentinelTransition);\n if (this._mql) {\n if (this._mql.removeEventListener) this._mql.removeEventListener('change', this._onReducedMotionChange);\n else this._mql.removeListener(this._onReducedMotionChange);\n }\n // Only remove content we created (non-adopt mode). The container is owned by the caller.\n this._contentEl?.remove();\n // Drop the subdivision cache so the font's strokes aren't kept keyed\n // against this (dead) engine if a caller holds a stale reference.\n this._strokeCache = new WeakMap();\n this._strokeCacheKey = '';\n this._maskCanvas = null;\n }\n\n // =========================================================================\n // Internal: DOM updates\n // =========================================================================\n\n /** Estimate line-height from font metrics when CSS returns \"normal\". */\n private _fallbackLineHeight(fontSize: number): number {\n if (this._font) {\n return ((this._font.ascender - this._font.descender) / this._font.unitsPerEm) * fontSize;\n }\n return fontSize * 1.2;\n }\n\n private _measure(): void {\n const styles = getComputedStyle(this._rootEl);\n this._containerWidth = this._rootEl.getBoundingClientRect().width;\n this._fontSize = Number.parseFloat(styles.fontSize);\n const parsedLh = Number.parseFloat(styles.lineHeight);\n this._lineHeight = Number.isNaN(parsedLh) ? this._fallbackLineHeight(this._fontSize) : parsedLh;\n this._currentColor = styles.color;\n }\n\n private _updateDom(): void {\n // Font family\n this._rootEl.style.fontFamily = this._font ? cssFontFamily(this._font) : '';\n\n // Direction\n this._rootEl.style.direction = this._direction ?? '';\n\n // CSS custom properties\n this._updateCssProperties();\n\n // Overlay text (guard to preserve cursor position when contentEditable)\n if (this._overlayEl.textContent !== this._text) {\n this._overlayEl.textContent = this._text;\n }\n this._canvasFallbackEl.textContent = this._text;\n }\n\n private _updateCssProperties(): void {\n const time = this.currentTime;\n const dur = this._timeline.totalDuration;\n this._rootEl.style.setProperty(CSS_DURATION, String(dur));\n this._rootEl.style.setProperty(CSS_TIME, String(time));\n this._rootEl.style.setProperty(CSS_PROGRESS, String(dur > 0 ? time / dur : 0));\n }\n\n private _updateOverlayStyle(): void {\n if (this._showOverlay) {\n this._overlayEl.style.webkitTextFillColor = '';\n this._overlayEl.style.color = 'rgba(255, 0, 0, 0.4)';\n } else {\n this._overlayEl.style.webkitTextFillColor = 'transparent';\n this._overlayEl.style.color = '';\n }\n }\n\n private _updateSentinelTransition(): void {\n const isCss = this._timeControl.mode === 'css';\n this._sentinelEl.style.transition = isCss\n ? `font-size 0.001s, line-height 0.001s, color 0.001s, ${CSS_PROGRESS} 0.001s`\n : 'font-size 0.001s, line-height 0.001s, color 0.001s';\n }\n\n // =========================================================================\n // Internal: Resize & sentinel observers\n // =========================================================================\n\n private _onResize = (entries: ResizeObserverEntry[]): void => {\n const entry = entries[0];\n if (!entry) return;\n const newWidth = entry.contentRect.width;\n const styles = getComputedStyle(this._rootEl);\n const newFontSize = Number.parseFloat(styles.fontSize);\n const parsedLh = Number.parseFloat(styles.lineHeight);\n const newLineHeight = Number.isNaN(parsedLh) ? this._fallbackLineHeight(newFontSize) : parsedLh;\n const newColor = styles.color;\n\n let changed = false;\n let layoutChanged = false;\n\n if (newWidth !== this._containerWidth) {\n this._containerWidth = newWidth;\n layoutChanged = true;\n changed = true;\n }\n if (newFontSize !== this._fontSize) {\n this._fontSize = newFontSize;\n layoutChanged = true;\n changed = true;\n }\n if (newLineHeight !== this._lineHeight) {\n this._lineHeight = newLineHeight;\n layoutChanged = true;\n changed = true;\n }\n if (newColor !== this._currentColor) {\n this._currentColor = newColor;\n changed = true;\n }\n\n if (layoutChanged) this._recomputeLayout();\n if (changed) this._render();\n };\n\n private _onSentinelTransition = (e: TransitionEvent): void => {\n const styles = getComputedStyle(this._sentinelEl);\n let changed = false;\n\n if (e.propertyName === 'font-size' || e.propertyName === 'line-height') {\n const newFontSize = Number.parseFloat(styles.fontSize);\n const parsedLh = Number.parseFloat(styles.lineHeight);\n const newLineHeight = Number.isNaN(parsedLh) ? this._fallbackLineHeight(newFontSize) : parsedLh;\n if (newFontSize !== this._fontSize || newLineHeight !== this._lineHeight) {\n this._fontSize = newFontSize;\n this._lineHeight = newLineHeight;\n this._recomputeLayout();\n changed = true;\n }\n }\n\n if (e.propertyName === 'color') {\n const newColor = styles.color;\n if (newColor !== this._currentColor) {\n this._currentColor = newColor;\n changed = true;\n }\n }\n\n if (e.propertyName === CSS_PROGRESS) {\n const rawProgress = Number(styles.getPropertyValue(CSS_PROGRESS));\n this._cssTime = rawProgress * this._timeline.totalDuration;\n changed = true;\n }\n\n if (changed) this._render();\n };\n\n // =========================================================================\n // Internal: Reduced motion\n // =========================================================================\n\n private _onReducedMotionChange = (e: MediaQueryListEvent): void => {\n this._prefersReducedMotion = e.matches;\n if (this._prefersReducedMotion && this._timeControl.mode === 'uncontrolled' && this._timeline.totalDuration > 0) {\n this._internalTime = this._timeline.totalDuration;\n }\n this._evaluatePlayback();\n this._render();\n };\n\n // =========================================================================\n // Internal: Font loading\n // =========================================================================\n\n private _loadFont(font: TegakiBundle | null): void {\n this._font = font;\n this._fontReady = false;\n\n if (!font) return;\n\n const pending = ensureFont(font.family, font.fontUrl);\n if (pending === null) {\n this._fontReady = true;\n return;\n }\n\n const currentFont = font;\n pending.then(() => {\n if (this._font === currentFont && !this._destroyed) {\n this._fontReady = true;\n this._recomputeTimeline();\n this._updateDom();\n this._recomputeLayout();\n this._evaluatePlayback();\n this._render();\n }\n });\n }\n\n // =========================================================================\n // Internal: Recomputation\n // =========================================================================\n\n private _recomputeTimeline(): void {\n if (this._font && this._text) {\n this._timeline = computeTimeline(this._text, this._font, this._timing);\n } else {\n this._timeline = { entries: [] as TimelineEntry[], totalDuration: 0 };\n }\n }\n\n private _recomputeLayout(): void {\n if (this._fontReady && this._font?.family && this._fontSize && this._containerWidth && this._text) {\n const key = `${this._text}\\0${this._font.family}\\0${this._fontSize}\\0${this._lineHeight}\\0${this._containerWidth}\\0${this._direction ?? ''}`;\n if (key === this._layoutKey) return;\n this._layoutKey = key;\n this._layout = computeTextLayout(this._overlayEl, this._fontSize);\n } else {\n this._layoutKey = '';\n this._layout = null;\n }\n }\n\n // =========================================================================\n // Internal: Playback loop\n // =========================================================================\n\n private _evaluatePlayback(): void {\n const tc = this._timeControl;\n const shouldRun = tc.mode === 'uncontrolled' && this._playing && !!this._font && this._fontReady && !this._prefersReducedMotion;\n\n if (shouldRun) {\n this._startLoop();\n } else {\n this._stopLoop();\n }\n }\n\n private _startLoop(): void {\n if (this._rafId) return;\n this._lastTs = null;\n this._smoothedBoost = 0;\n this._rafId = requestAnimationFrame(this._tick);\n }\n\n private _stopLoop(): void {\n if (this._rafId) {\n cancelAnimationFrame(this._rafId);\n this._rafId = 0;\n }\n }\n\n private _tick = (ts: number): void => {\n if (this._destroyed) return;\n\n if (this._lastTs === null) this._lastTs = ts;\n const dtSec = (ts - this._lastTs) / 1000;\n this._lastTs = ts;\n\n const tc = this._timeControl;\n if (tc.mode !== 'uncontrolled') return;\n\n const loop = tc.loop ?? false;\n const totalDur = this._timeline.totalDuration;\n const durationOverride = tc.duration;\n const useDuration = durationOverride !== undefined && durationOverride > 0;\n\n if (totalDur === 0 || (!loop && this._internalTime >= totalDur)) {\n this._internalTime = totalDur;\n this._rafId = requestAnimationFrame(this._tick);\n return;\n }\n\n // --- Initial delay ---\n if (this._delayRemaining > 0) {\n this._delayRemaining = Math.max(0, this._delayRemaining - dtSec);\n this._rafId = requestAnimationFrame(this._tick);\n return;\n }\n\n // --- Loop gap (waiting between iterations) ---\n if (this._loopGapRemaining > 0) {\n this._loopGapRemaining = Math.max(0, this._loopGapRemaining - dtSec);\n if (this._loopGapRemaining <= 0) {\n this._internalTime = 0;\n this._prevCompleted = false;\n this._smoothedBoost = 0;\n }\n this._notifyTimeChange();\n this._render();\n this._updateCssProperties();\n this._rafId = requestAnimationFrame(this._tick);\n return;\n }\n\n // Compute effective speed. `duration` stretches the natural timeline to fit\n // a fixed wall-clock slot; otherwise use `speed` + optional `catchUp`.\n let effectiveSpeed: number;\n if (useDuration) {\n effectiveSpeed = totalDur / durationOverride;\n } else {\n const speed = tc.speed ?? 1;\n const catchUp = tc.catchUp ?? 0;\n effectiveSpeed = speed;\n if (catchUp > 0) {\n const remaining = Math.max(0, totalDur - this._internalTime);\n const excess = Math.max(0, remaining - 2);\n const targetBoost = catchUp * excess;\n const attackRate = 4;\n const releaseRate = loop ? 30 : 2;\n const rate = targetBoost > this._smoothedBoost ? attackRate : releaseRate;\n this._smoothedBoost += (targetBoost - this._smoothedBoost) * (1 - Math.exp(-rate * dtSec));\n effectiveSpeed = speed + this._smoothedBoost;\n }\n }\n\n let next = this._internalTime + dtSec * effectiveSpeed;\n if (next >= totalDur) {\n if (loop) {\n const loopGap = tc.loopGap ?? 0;\n if (loopGap > 0) {\n // Hold at the end and start the loop gap countdown\n next = totalDur;\n this._loopGapRemaining = loopGap;\n } else if (this._internalTime < totalDur) {\n // Render one frame at totalDur so every entry (including the\n // last fallback character) satisfies its reveal condition\n // before the animation wraps back to the start.\n next = totalDur;\n } else {\n next %= totalDur;\n }\n } else {\n next = totalDur;\n }\n this._smoothedBoost = 0;\n }\n this._internalTime = next;\n\n this._notifyTimeChange();\n this._checkCompletion();\n this._render();\n this._updateCssProperties();\n\n this._rafId = requestAnimationFrame(this._tick);\n };\n\n private _notifyTimeChange(): void {\n const tc = this._timeControl;\n if (tc.mode === 'uncontrolled' && tc.onTimeChange) {\n // Emit eased time so it matches what's drawn and what CSS variables expose.\n tc.onTimeChange(this.currentTime);\n }\n }\n\n private _checkCompletion(): void {\n const complete = this.isComplete;\n if (complete && !this._prevCompleted) {\n this._prevCompleted = true;\n this._onComplete?.();\n } else if (!complete) {\n this._prevCompleted = false;\n }\n }\n\n // =========================================================================\n // Internal: Canvas rendering\n // =========================================================================\n\n private _render(): void {\n const canvas = this._canvasEl;\n const font = this._font;\n const layout = this._layout;\n const fontSize = this._fontSize;\n\n const dpr = window.devicePixelRatio || 1;\n // Supersampling: draw into a backing canvas larger than the displayed CSS\n // size, then let the browser downsample. Improves antialiasing at a\n // quadratic cost in pixels filled.\n const pixelRatio = Math.max(this._quality?.pixelRatio ?? 1, 0);\n const effectiveDpr = dpr * pixelRatio;\n const w = canvas.offsetWidth;\n const h = canvas.offsetHeight;\n\n const needsResize = canvas.width !== Math.round(w * effectiveDpr) || canvas.height !== Math.round(h * effectiveDpr);\n if (needsResize) {\n canvas.width = Math.round(w * effectiveDpr);\n canvas.height = Math.round(h * effectiveDpr);\n }\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n ctx.setTransform(effectiveDpr, 0, 0, effectiveDpr, 0, 0);\n ctx.clearRect(0, 0, w, h);\n\n // Nothing to draw (e.g. empty text) — but the clear above still needs to\n // run so stale pixels from the previous render don't linger.\n if (!font?.glyphData || !layout || !fontSize) return;\n\n const padH = PADDING_H_EM * fontSize;\n const lineHeight = this._lineHeight;\n const padV = Math.max(MIN_PADDING_V_EM * fontSize, (MIN_LINE_HEIGHT_EM * fontSize - lineHeight) / 2);\n ctx.translate(padH, padV);\n\n const color = this._currentColor || 'black';\n const emHeight = (font.ascender - font.descender) / font.unitsPerEm;\n const emHeightPx = emHeight * fontSize;\n const halfLeading = (lineHeight - emHeightPx) / 2;\n const characters = graphemes(this._text);\n const currentTime = this.currentTime;\n\n // --- Subdivision cache setup ---\n // `maxSegLenFU` is the subdivision threshold in font units. It collapses\n // every input that matters (segmentSize in CSS px, fontSize, unitsPerEm,\n // whether any effect needs subdivision) into a single value, so the cache\n // key is just (font family, maxSegLenFU). When anything that affects\n // subdivision changes, the key changes and the WeakMap is swapped out.\n const effectsNeedSubdivision =\n !!findEffect(this._resolvedEffects, 'wobble') ||\n !!findEffect(this._resolvedEffects, 'strokeGradient') ||\n !!findEffect(this._resolvedEffects, 'taper') ||\n (() => {\n const p = findEffect(this._resolvedEffects, 'pressureWidth');\n return !!p && Math.max(0, Math.min(p.config.strength ?? 1, 1)) > 0;\n })();\n const smoothing = this._quality?.smoothing === true;\n const userSegmentSize = this._quality?.segmentSize;\n const resolvedSegmentSize = userSegmentSize ?? (effectsNeedSubdivision || smoothing ? 2 : undefined);\n const scale = fontSize / font.unitsPerEm;\n const maxSegLenFU = resolvedSegmentSize != null ? resolvedSegmentSize / scale : Infinity;\n const cacheKey = `${font.family}|${maxSegLenFU}|${smoothing ? 's' : 'l'}`;\n if (cacheKey !== this._strokeCacheKey) {\n this._strokeCache = new WeakMap();\n this._strokeCacheKey = cacheKey;\n }\n const strokeCache = this._strokeCache;\n const getSubdivided = (stroke: TegakiGlyphData['s'][number]): SubdividedStroke => {\n let sub = strokeCache.get(stroke);\n if (!sub) {\n sub = subdivideStroke(stroke, maxSegLenFU, smoothing);\n strokeCache.set(stroke, sub);\n }\n return sub;\n };\n\n const clipText = this._quality?.clipText;\n const strokeScale = typeof clipText === 'number' ? clipText : 1;\n\n // --- Render-stage hooks (pre) ---\n // Effects that span the whole layout (vs. per-stroke) can hook the\n // render pipeline here. The stage context is only computed when at\n // least one resolved effect declares a hook, so the common case pays\n // nothing.\n const runHooks = hasRenderHooks(this._resolvedEffects);\n const stage: RenderStageContext | null = runHooks\n ? {\n ctx,\n layout,\n fontSize,\n lineHeight,\n unitsPerEm: font.unitsPerEm,\n ascender: font.ascender,\n descender: font.descender,\n bbox: computeLayoutBbox(layout, fontSize, lineHeight),\n baseColor: color,\n seed: this._seed,\n }\n : null;\n\n if (stage) {\n for (const effect of this._resolvedEffects) {\n getEffectDefinition(effect.effect)?.beforeRender?.(stage, effect.config);\n }\n }\n\n let y = 0;\n for (const lineIndices of layout.lines) {\n for (const charIdx of lineIndices) {\n const char = characters[charIdx]!;\n if (char === '\\n') continue;\n const entry = this._timeline.entries[charIdx]!;\n const x = (layout.charOffsets[charIdx] ?? 0) * fontSize;\n const glyph = font.glyphData[char];\n\n if (glyph && entry.hasGlyph) {\n let localTime = Math.max(0, Math.min(currentTime - entry.offset, entry.duration));\n const glyphEasing = this._timing?.glyphEasing;\n if (glyphEasing && entry.duration > 0) {\n localTime = glyphEasing(localTime / entry.duration) * entry.duration;\n }\n const glyphY = y + halfLeading;\n drawGlyph(\n ctx,\n glyph,\n {\n x,\n y: glyphY,\n fontSize,\n unitsPerEm: font.unitsPerEm,\n ascender: font.ascender,\n descender: font.descender,\n },\n localTime,\n font.lineCap,\n color,\n this._resolvedEffects,\n this._seed + charIdx,\n getSubdivided,\n this._timing?.strokeEasing,\n strokeScale,\n stage?.strokeStyle,\n );\n } else if (!entry.hasGlyph && currentTime >= entry.offset + entry.duration) {\n const baseline = y + halfLeading + (font.ascender / font.unitsPerEm) * fontSize;\n drawFallbackGlyph(ctx, char, x, baseline, fontSize, cssFontFamily(font), color, this._resolvedEffects, this._seed + charIdx);\n }\n }\n y += lineHeight;\n }\n\n // --- Render-stage hooks (post) ---\n // Reverse order so save/restore-style pairs nest correctly with their\n // `beforeRender` counterparts. Runs before the clipText mask so any\n // post-processing still gets constrained to the text shape.\n if (stage) {\n for (let i = this._resolvedEffects.length - 1; i >= 0; i--) {\n const effect = this._resolvedEffects[i]!;\n getEffectDefinition(effect.effect)?.afterRender?.(stage, effect.config);\n }\n }\n\n // --- Clip strokes to the filled text shape ---\n // All text characters are rendered onto a cached offscreen canvas so the\n // mask can be applied as a single destination-in drawImage call. Doing\n // fillText per-character with destination-in would erase previously-clipped\n // strokes.\n if (clipText) {\n if (!this._maskCanvas) this._maskCanvas = document.createElement('canvas');\n const maskCanvas = this._maskCanvas;\n if (maskCanvas.width !== canvas.width || maskCanvas.height !== canvas.height) {\n maskCanvas.width = canvas.width;\n maskCanvas.height = canvas.height;\n }\n const maskCtx = maskCanvas.getContext('2d')!;\n maskCtx.setTransform(effectiveDpr, 0, 0, effectiveDpr, 0, 0);\n maskCtx.clearRect(0, 0, w, h);\n maskCtx.translate(padH, padV);\n maskCtx.font = `${fontSize}px ${cssFontFamily(font)}`;\n maskCtx.textBaseline = 'alphabetic';\n let clipY = 0;\n for (const lineIndices of layout.lines) {\n for (const charIdx of lineIndices) {\n const char = characters[charIdx]!;\n if (char === '\\n') continue;\n const x = (layout.charOffsets[charIdx] ?? 0) * fontSize;\n const baseline = clipY + halfLeading + (font.ascender / font.unitsPerEm) * fontSize;\n maskCtx.fillText(char, x, baseline);\n }\n clipY += lineHeight;\n }\n\n ctx.save();\n ctx.setTransform(1, 0, 0, 1, 0, 0);\n ctx.globalCompositeOperation = 'destination-in';\n ctx.drawImage(maskCanvas, 0, 0);\n ctx.restore();\n }\n }\n}\n"],"mappings":"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"}
|