tegaki 0.12.0 → 0.13.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 +13 -0
- package/dist/core/index.d.mts +1 -1
- package/dist/core/index.mjs +1 -1
- package/dist/{core-CIzLDu_Q.mjs → core-Ds9ohwR7.mjs} +68 -22
- package/dist/core-Ds9ohwR7.mjs.map +1 -0
- package/dist/{index-d5-O-2U1.d.mts → index-BwhATGJw.d.mts} +2 -2
- package/dist/{index-DtvcCtXG.d.mts → index-e-RN9Gi3.d.mts} +15 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/react/index.d.mts +2 -2
- package/dist/react/index.mjs +2 -2
- package/dist/{react-CVoAINjv.mjs → react-D3lP9bU2.mjs} +2 -2
- package/dist/{react-CVoAINjv.mjs.map → react-D3lP9bU2.mjs.map} +1 -1
- package/dist/solid/index.d.mts +1 -1
- package/dist/solid/index.mjs +1 -1
- package/dist/wc/index.d.mts +1 -1
- package/dist/wc/index.mjs +1 -1
- package/package.json +1 -1
- package/src/core/engine.ts +57 -6
- package/src/core/render-elements.ts +5 -1
- package/src/core/types.ts +12 -0
- package/src/lib/drawGlyph.ts +6 -5
- package/src/lib/textLayout.ts +1 -1
- package/src/lib/timeline.ts +6 -4
- package/src/lib/utils.ts +8 -2
- package/dist/core-CIzLDu_Q.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# tegaki
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8fd875a: Add `clipText` quality option that clips handwriting strokes to the filled text shape using canvas composite operations. Accepts `true` for clipping with normal stroke widths, or a number to scale stroke widths (e.g. `2` for 2x wider strokes that fill more of the glyph interior).
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 2a46c09: fix compatibility with old Safari versions, and a bug with text layout when text is wrapped. Fixes [#29](https://github.com/KurtGokhan/tegaki/issues/29)
|
|
12
|
+
- cdb2993: Fix timing around whitespace characters. Spaces and line breaks no longer consume `unknownDuration` on top of `wordGap`/`lineGap` — the gap alone now represents the full pause. `\r\n` and `\r` are normalized to `\n`, and all Unicode whitespace (NBSP, tab, ideographic space, etc.) is treated as a word gap.
|
|
13
|
+
|
|
14
|
+
Fixes [#28](https://github.com/KurtGokhan/tegaki/issues/28)
|
|
15
|
+
|
|
3
16
|
## 0.12.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as LineCap, B as TegakiSingletonEffectName, C as resolveEffects, D as CSSLength, E as COMPATIBLE_BUNDLE_VERSIONS, F as TegakiEffectConfigs, I as TegakiEffectName, L as TegakiEffects, M as Point, N as Stroke, O as FontOutput, P as TegakiBundle, R as TegakiGlyphData, S as ResolvedEffect, T as BUNDLE_VERSION, V as TimedPoint, _ as computeTimeline, a as CreateElementFn, b as ensureFontFace, c as TimeControlMode, d as getBundle, f as registerBundle, g as TimelineEntry, h as TimelineConfig, i as TegakiEngine, j as PathCommand, k as GlyphData, l as TimeControlProp, m as Timeline, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as TextLayout, w as BBox, x as drawGlyph, y as computeTextLayout, z as TegakiMultiEffectName } from "../index-
|
|
1
|
+
import { A as LineCap, B as TegakiSingletonEffectName, C as resolveEffects, D as CSSLength, E as COMPATIBLE_BUNDLE_VERSIONS, F as TegakiEffectConfigs, I as TegakiEffectName, L as TegakiEffects, M as Point, N as Stroke, O as FontOutput, P as TegakiBundle, R as TegakiGlyphData, S as ResolvedEffect, T as BUNDLE_VERSION, V as TimedPoint, _ as computeTimeline, a as CreateElementFn, b as ensureFontFace, c as TimeControlMode, d as getBundle, f as registerBundle, g as TimelineEntry, h as TimelineConfig, i as TegakiEngine, j as PathCommand, k as GlyphData, l as TimeControlProp, m as Timeline, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as TextLayout, w as BBox, x as drawGlyph, y as computeTextLayout, z as TegakiMultiEffectName } from "../index-e-RN9Gi3.mjs";
|
|
2
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 };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createBundle, c as resolveBundle, d as computeTimeline, f as computeTextLayout, g as resolveEffects, i as domCreateElement, l as BUNDLE_VERSION, m as drawGlyph, n as buildChildren, o as getBundle, p as ensureFontFace, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS } from "../core-
|
|
1
|
+
import { a as createBundle, c as resolveBundle, d as computeTimeline, f as computeTextLayout, g as resolveEffects, i as domCreateElement, l as BUNDLE_VERSION, m as drawGlyph, n as buildChildren, o as getBundle, p as ensureFontFace, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS } from "../core-Ds9ohwR7.mjs";
|
|
2
2
|
export { BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, TegakiEngine, buildChildren, buildRootProps, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, getBundle, registerBundle, resolveBundle, resolveEffects };
|
|
@@ -111,14 +111,15 @@ function subdivideStroke(stroke, maxSegLen) {
|
|
|
111
111
|
}
|
|
112
112
|
//#endregion
|
|
113
113
|
//#region src/lib/utils.ts
|
|
114
|
-
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
114
|
+
const segmenter = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function" ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
|
|
115
115
|
/** Resolve a CSSLength to pixels. Plain numbers are px, `"Nem"` is N * fontSize. */
|
|
116
116
|
function resolveCSSLength(value, fontSize) {
|
|
117
117
|
if (typeof value === "number") return value;
|
|
118
118
|
return parseFloat(value) * fontSize;
|
|
119
119
|
}
|
|
120
120
|
function graphemes(text) {
|
|
121
|
-
return Array.from(segmenter.segment(text), (s) => s.segment);
|
|
121
|
+
if (segmenter) return Array.from(segmenter.segment(text), (s) => s.segment);
|
|
122
|
+
return Array.from(text);
|
|
122
123
|
}
|
|
123
124
|
/**
|
|
124
125
|
* Build the CSS `font-family` value for a bundle, including the full
|
|
@@ -209,7 +210,7 @@ function defaultStrokeEasing(t) {
|
|
|
209
210
|
* font, fontSize, or segment size changes; if omitted here, strokes are
|
|
210
211
|
* subdivided inline each call (useful for testing).
|
|
211
212
|
*/
|
|
212
|
-
function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], seed = 0, getSubdivided, strokeEasing = defaultStrokeEasing) {
|
|
213
|
+
function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], seed = 0, getSubdivided, strokeEasing = defaultStrokeEasing, strokeScale = 1) {
|
|
213
214
|
const scale = pos.fontSize / pos.unitsPerEm;
|
|
214
215
|
const ox = pos.x;
|
|
215
216
|
const oy = pos.y;
|
|
@@ -268,8 +269,8 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
268
269
|
const p = rawPts[0];
|
|
269
270
|
const dotX = px(p[0] + wobbleDx(p[0], p[1], 0));
|
|
270
271
|
const dotY = py(p[1] + wobbleDy(p[0], p[1], 0));
|
|
271
|
-
const baseLineWidth = Math.max(p[2], .5) * scale;
|
|
272
|
-
let dotWidth = baseLineWidth + (Math.max(p[2], .5) * scale - baseLineWidth) * pressureAmount;
|
|
272
|
+
const baseLineWidth = Math.max(p[2], .5) * scale * strokeScale;
|
|
273
|
+
let dotWidth = baseLineWidth + (Math.max(p[2], .5) * scale * strokeScale - baseLineWidth) * pressureAmount;
|
|
273
274
|
dotWidth *= taperMultiplier(.5);
|
|
274
275
|
for (const glow of glowEffects) {
|
|
275
276
|
ctx.save();
|
|
@@ -296,7 +297,7 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
296
297
|
if (vertices.length < 2 || totalLen <= 0) continue;
|
|
297
298
|
const drawLen = totalLen * progress;
|
|
298
299
|
if (drawLen <= 0) continue;
|
|
299
|
-
const baseLineWidth = Math.max(avgWidth, .5) * scale;
|
|
300
|
+
const baseLineWidth = Math.max(avgWidth, .5) * scale * strokeScale;
|
|
300
301
|
let lo = 0;
|
|
301
302
|
let hi = vertices.length - 1;
|
|
302
303
|
while (lo < hi) {
|
|
@@ -369,8 +370,8 @@ function drawGlyph(ctx, glyph, pos, localTime, lineCap, color, effects = [], see
|
|
|
369
370
|
const midProgress = (aCum + bCum) * .5 * invTotalLen;
|
|
370
371
|
let lw = baseLineWidth;
|
|
371
372
|
if (needsPerSegment) {
|
|
372
|
-
const perPoint = (aWidth + bWidth) * .5 * scale;
|
|
373
|
-
lw = Math.max(baseLineWidth + (perPoint - baseLineWidth) * pressureAmount, .5 * scale) * taperMultiplier(midProgress);
|
|
373
|
+
const perPoint = (aWidth + bWidth) * .5 * scale * strokeScale;
|
|
374
|
+
lw = Math.max(baseLineWidth + (perPoint - baseLineWidth) * pressureAmount, .5 * scale * strokeScale) * taperMultiplier(midProgress);
|
|
374
375
|
}
|
|
375
376
|
ctx.lineWidth = lw;
|
|
376
377
|
ctx.strokeStyle = hasGradient ? colorAt(midProgress) : color;
|
|
@@ -456,7 +457,7 @@ function measureElement(el, fontSize) {
|
|
|
456
457
|
currentLine.push(i);
|
|
457
458
|
continue;
|
|
458
459
|
}
|
|
459
|
-
const rect = rects[
|
|
460
|
+
const rect = rects[rects.length - 1];
|
|
460
461
|
if (currentLine.length > 0 && rect.top - prevTop > fontSize * .25) {
|
|
461
462
|
lines.push(currentLine);
|
|
462
463
|
currentLine = [];
|
|
@@ -510,7 +511,9 @@ function computeTimeline(text, font, config) {
|
|
|
510
511
|
for (const char of chars) {
|
|
511
512
|
const glyph = font.glyphData[char];
|
|
512
513
|
const hasGlyph = !!glyph;
|
|
513
|
-
const
|
|
514
|
+
const isLineBreak = char === "\n";
|
|
515
|
+
const isWhitespace = isLineBreak || /^\s+$/.test(char);
|
|
516
|
+
const duration = isWhitespace ? 0 : hasGlyph ? glyph.t ?? unknownDuration : unknownDuration;
|
|
514
517
|
entries.push({
|
|
515
518
|
char,
|
|
516
519
|
offset,
|
|
@@ -518,13 +521,14 @@ function computeTimeline(text, font, config) {
|
|
|
518
521
|
hasGlyph
|
|
519
522
|
});
|
|
520
523
|
offset += duration;
|
|
521
|
-
if (
|
|
522
|
-
else if (
|
|
524
|
+
if (isLineBreak) offset += lineGap;
|
|
525
|
+
else if (isWhitespace) offset += wordGap;
|
|
523
526
|
else offset += glyphGap;
|
|
524
527
|
}
|
|
525
528
|
if (entries.length > 0) {
|
|
526
529
|
const lastChar = chars[chars.length - 1];
|
|
527
|
-
|
|
530
|
+
const trailingGap = lastChar === "\n" ? lineGap : /^\s+$/.test(lastChar) ? wordGap : glyphGap;
|
|
531
|
+
offset -= trailingGap;
|
|
528
532
|
}
|
|
529
533
|
return {
|
|
530
534
|
entries,
|
|
@@ -730,7 +734,10 @@ function buildChildren(options, h) {
|
|
|
730
734
|
"aria-hidden": "true",
|
|
731
735
|
style: {
|
|
732
736
|
position: "absolute",
|
|
733
|
-
|
|
737
|
+
top: `calc(-1 * ${PAD_V_CSS})`,
|
|
738
|
+
right: "-0.2em",
|
|
739
|
+
bottom: `calc(-1 * ${PAD_V_CSS})`,
|
|
740
|
+
left: "-0.2em",
|
|
734
741
|
width: "calc(100% + 0.4em)",
|
|
735
742
|
height: `calc(100% + 2 * ${PAD_V_CSS})`,
|
|
736
743
|
pointerEvents: "none",
|
|
@@ -792,6 +799,7 @@ var TegakiEngine = class {
|
|
|
792
799
|
_canvasEl;
|
|
793
800
|
_overlayEl;
|
|
794
801
|
_canvasFallbackEl;
|
|
802
|
+
_maskCanvas = null;
|
|
795
803
|
_text = "";
|
|
796
804
|
_font = null;
|
|
797
805
|
_timeControl = { mode: "uncontrolled" };
|
|
@@ -865,7 +873,8 @@ var TegakiEngine = class {
|
|
|
865
873
|
if (typeof window !== "undefined") {
|
|
866
874
|
this._mql = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
867
875
|
this._prefersReducedMotion = this._mql.matches;
|
|
868
|
-
this._mql.addEventListener("change", this._onReducedMotionChange);
|
|
876
|
+
if (this._mql.addEventListener) this._mql.addEventListener("change", this._onReducedMotionChange);
|
|
877
|
+
else this._mql.addListener(this._onReducedMotionChange);
|
|
869
878
|
}
|
|
870
879
|
this._measure();
|
|
871
880
|
if (options) this.update(options);
|
|
@@ -929,10 +938,13 @@ var TegakiEngine = class {
|
|
|
929
938
|
let dirtyLayout = false;
|
|
930
939
|
let dirtyRender = false;
|
|
931
940
|
let dirtyPlayback = false;
|
|
932
|
-
if ("text" in options
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
941
|
+
if ("text" in options) {
|
|
942
|
+
const nextText = (options.text ?? "").replace(/\r\n?/g, "\n");
|
|
943
|
+
if (nextText !== this._text) {
|
|
944
|
+
this._text = nextText;
|
|
945
|
+
dirtyTimeline = true;
|
|
946
|
+
dirtyLayout = true;
|
|
947
|
+
}
|
|
936
948
|
}
|
|
937
949
|
if ("font" in options) {
|
|
938
950
|
const resolved = resolveBundle(options.font) ?? null;
|
|
@@ -1000,10 +1012,12 @@ var TegakiEngine = class {
|
|
|
1000
1012
|
this._stopLoop();
|
|
1001
1013
|
this._resizeObserver.disconnect();
|
|
1002
1014
|
this._sentinelEl.removeEventListener("transitionend", this._onSentinelTransition);
|
|
1003
|
-
this._mql
|
|
1015
|
+
if (this._mql) if (this._mql.removeEventListener) this._mql.removeEventListener("change", this._onReducedMotionChange);
|
|
1016
|
+
else this._mql.removeListener(this._onReducedMotionChange);
|
|
1004
1017
|
this._contentEl?.remove();
|
|
1005
1018
|
this._strokeCache = /* @__PURE__ */ new WeakMap();
|
|
1006
1019
|
this._strokeCacheKey = "";
|
|
1020
|
+
this._maskCanvas = null;
|
|
1007
1021
|
}
|
|
1008
1022
|
/** Estimate line-height from font metrics when CSS returns "normal". */
|
|
1009
1023
|
_fallbackLineHeight(fontSize) {
|
|
@@ -1292,6 +1306,8 @@ var TegakiEngine = class {
|
|
|
1292
1306
|
}
|
|
1293
1307
|
return sub;
|
|
1294
1308
|
};
|
|
1309
|
+
const clipText = this._quality?.clipText;
|
|
1310
|
+
const strokeScale = typeof clipText === "number" ? clipText : 1;
|
|
1295
1311
|
let y = 0;
|
|
1296
1312
|
for (const lineIndices of layout.lines) {
|
|
1297
1313
|
for (const charIdx of lineIndices) {
|
|
@@ -1311,14 +1327,44 @@ var TegakiEngine = class {
|
|
|
1311
1327
|
unitsPerEm: font.unitsPerEm,
|
|
1312
1328
|
ascender: font.ascender,
|
|
1313
1329
|
descender: font.descender
|
|
1314
|
-
}, localTime, font.lineCap, color, this._resolvedEffects, this._seed + charIdx, getSubdivided, this._timing?.strokeEasing);
|
|
1330
|
+
}, localTime, font.lineCap, color, this._resolvedEffects, this._seed + charIdx, getSubdivided, this._timing?.strokeEasing, strokeScale);
|
|
1315
1331
|
} 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);
|
|
1316
1332
|
}
|
|
1317
1333
|
y += lineHeight;
|
|
1318
1334
|
}
|
|
1335
|
+
if (clipText) {
|
|
1336
|
+
if (!this._maskCanvas) this._maskCanvas = document.createElement("canvas");
|
|
1337
|
+
const maskCanvas = this._maskCanvas;
|
|
1338
|
+
if (maskCanvas.width !== canvas.width || maskCanvas.height !== canvas.height) {
|
|
1339
|
+
maskCanvas.width = canvas.width;
|
|
1340
|
+
maskCanvas.height = canvas.height;
|
|
1341
|
+
}
|
|
1342
|
+
const maskCtx = maskCanvas.getContext("2d");
|
|
1343
|
+
maskCtx.setTransform(effectiveDpr, 0, 0, effectiveDpr, 0, 0);
|
|
1344
|
+
maskCtx.clearRect(0, 0, w, h);
|
|
1345
|
+
maskCtx.translate(padH, padV);
|
|
1346
|
+
maskCtx.font = `${fontSize}px ${cssFontFamily(font)}`;
|
|
1347
|
+
maskCtx.textBaseline = "alphabetic";
|
|
1348
|
+
let clipY = 0;
|
|
1349
|
+
for (const lineIndices of layout.lines) {
|
|
1350
|
+
for (const charIdx of lineIndices) {
|
|
1351
|
+
const char = characters[charIdx];
|
|
1352
|
+
if (char === "\n") continue;
|
|
1353
|
+
const x = (layout.charOffsets[charIdx] ?? 0) * fontSize;
|
|
1354
|
+
const baseline = clipY + halfLeading + font.ascender / font.unitsPerEm * fontSize;
|
|
1355
|
+
maskCtx.fillText(char, x, baseline);
|
|
1356
|
+
}
|
|
1357
|
+
clipY += lineHeight;
|
|
1358
|
+
}
|
|
1359
|
+
ctx.save();
|
|
1360
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1361
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
1362
|
+
ctx.drawImage(maskCanvas, 0, 0);
|
|
1363
|
+
ctx.restore();
|
|
1364
|
+
}
|
|
1319
1365
|
}
|
|
1320
1366
|
};
|
|
1321
1367
|
//#endregion
|
|
1322
1368
|
export { createBundle as a, resolveBundle as c, computeTimeline as d, computeTextLayout as f, resolveEffects as g, coerceToString as h, domCreateElement as i, BUNDLE_VERSION as l, drawGlyph as m, buildChildren as n, getBundle as o, ensureFontFace as p, buildRootProps as r, registerBundle as s, TegakiEngine as t, COMPATIBLE_BUNDLE_VERSIONS as u };
|
|
1323
1369
|
|
|
1324
|
-
//# sourceMappingURL=core-
|
|
1370
|
+
//# sourceMappingURL=core-Ds9ohwR7.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-Ds9ohwR7.mjs","names":[],"sources":["../src/lib/effects.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';\n\nexport interface ResolvedEffect<K extends TegakiEffectName = TegakiEffectName> {\n effect: K;\n order: number;\n config: TegakiEffectConfigs[K];\n}\n\nconst defaultEffects: Record<string, any> = { pressureWidth: true };\nconst knownEffects: Set<string> = new Set(['glow', 'wobble', 'pressureWidth', 'taper', 'gradient']);\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 = (knownEffects.has(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 ?? (knownEffects.has(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","import type { TegakiGlyphData } from '../types.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 * Output depends only on `(stroke.p, maxSegLen)` — not on position, seed,\n * progress, or effect config — so it can be cached and shared across every\n * instance of the same glyph at the same font size.\n */\nexport function subdivideStroke(stroke: Stroke, maxSegLen: number): 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 dw = cur[2]! - prev[2]!;\n const segLen = Math.sqrt(dx * dx + dy * dy);\n const count = segLen > 0 && Number.isFinite(maxSegLen) && maxSegLen > 0 ? Math.max(1, Math.ceil(segLen / maxSegLen)) : 1;\n\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 + segLen * t,\n idx: j - 1 + t,\n });\n }\n cumLen += segLen;\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) {\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 gradientEffect = findEffect(effects, 'gradient');\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 = gradientEffect?.config.colors;\n const isRainbow = gradientColors === 'rainbow';\n const gradientColorStops = Array.isArray(gradientColors) ? gradientColors : undefined;\n const gradientSaturation = gradientEffect?.config.saturation ?? 80;\n const gradientLightness = gradientEffect?.config.lightness ?? 55;\n const hasGradient = !!gradientEffect;\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\n ctx.fillStyle = colorAt(0);\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 && !hasGradient) {\n // Fast path: single stroke() over the whole truncated polyline.\n ctx.strokeStyle = color;\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 = hasGradient ? colorAt(midProgress) : color;\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 * 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 elLeft = el.getBoundingClientRect().left;\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\n if (currentLine.length > 0 && rect.top - prevTop > fontSize * 0.25) {\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) / fontSize);\n charWidths.push(rect.width / 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 gradient: BaseEffectConfig & { colors?: string[] | 'rainbow'; saturation?: number; lightness?: 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' | 'gradient';\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, gradient, 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 gradientEffect = findEffect(effects, 'gradient');\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 (gradientEffect) {\n const colors = gradientEffect.config.colors;\n if (colors === 'rainbow') {\n const saturation = gradientEffect.config.saturation ?? 80;\n const lightness = gradientEffect.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 { findEffect, type ResolvedEffect, resolveEffects } 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 { 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 if (!font?.glyphData || !layout || !fontSize) return;\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 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, 'gradient') ||\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 userSegmentSize = this._quality?.segmentSize;\n const resolvedSegmentSize = userSegmentSize ?? (effectsNeedSubdivision ? 2 : undefined);\n const scale = fontSize / font.unitsPerEm;\n const maxSegLenFU = resolvedSegmentSize != null ? resolvedSegmentSize / scale : Infinity;\n const cacheKey = `${font.family}|${maxSegLenFU}`;\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);\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 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 );\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 // --- 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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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { L as TegakiEffects, i as TegakiEngine, o as TegakiEngineOptions } from "./index-
|
|
1
|
+
import { L as TegakiEffects, i as TegakiEngine, o as TegakiEngineOptions } from "./index-e-RN9Gi3.mjs";
|
|
2
2
|
import * as _$react from "react";
|
|
3
3
|
import { ComponentPropsWithoutRef, ElementType, Ref } from "react";
|
|
4
4
|
|
|
@@ -31,4 +31,4 @@ type TegakiRendererProps<C extends ElementType = 'div', E extends TegakiEffects<
|
|
|
31
31
|
declare function TegakiRenderer<const C extends ElementType = 'div', const E extends TegakiEffects<E> = Record<string, never>>(props: TegakiRendererProps<C, E>): _$react.ReactElement<any, string | _$react.JSXElementConstructor<any>>;
|
|
32
32
|
//#endregion
|
|
33
33
|
export { TegakiRendererHandle as n, TegakiRendererProps as r, TegakiRenderer as t };
|
|
34
|
-
//# sourceMappingURL=index-
|
|
34
|
+
//# sourceMappingURL=index-BwhATGJw.d.mts.map
|
|
@@ -210,7 +210,7 @@ interface GlyphPosition {
|
|
|
210
210
|
* font, fontSize, or segment size changes; if omitted here, strokes are
|
|
211
211
|
* subdivided inline each call (useful for testing).
|
|
212
212
|
*/
|
|
213
|
-
declare function drawGlyph(ctx: CanvasRenderingContext2D, glyph: TegakiGlyphData, pos: GlyphPosition, localTime: number, lineCap: LineCap, color: string, effects?: ResolvedEffect[], seed?: number, getSubdivided?: (stroke: Stroke) => SubdividedStroke, strokeEasing?: ((t: number) => number) | undefined): void;
|
|
213
|
+
declare function drawGlyph(ctx: CanvasRenderingContext2D, glyph: TegakiGlyphData, pos: GlyphPosition, localTime: number, lineCap: LineCap, color: string, effects?: ResolvedEffect[], seed?: number, getSubdivided?: (stroke: Stroke) => SubdividedStroke, strokeEasing?: ((t: number) => number) | undefined, strokeScale?: number): void;
|
|
214
214
|
//#endregion
|
|
215
215
|
//#region src/lib/font.d.ts
|
|
216
216
|
/**
|
|
@@ -408,6 +408,18 @@ interface TegakiQuality {
|
|
|
408
408
|
* subdivided.
|
|
409
409
|
*/
|
|
410
410
|
segmentSize?: number;
|
|
411
|
+
/**
|
|
412
|
+
* Clip handwriting strokes to the filled text shape using canvas composite
|
|
413
|
+
* operations (`destination-in`). Strokes that extend beyond the glyph
|
|
414
|
+
* outlines are masked away, producing a "drawn inside the text" effect.
|
|
415
|
+
*
|
|
416
|
+
* - `false` (default) — no clipping.
|
|
417
|
+
* - `true` — clip with no stroke width change.
|
|
418
|
+
* - A number > 0 — clip and scale stroke widths by that factor. Values
|
|
419
|
+
* around `2`–`3` make the strokes fill more of the glyph interior,
|
|
420
|
+
* producing a result closer to the original filled text.
|
|
421
|
+
*/
|
|
422
|
+
clipText?: boolean | number;
|
|
411
423
|
}
|
|
412
424
|
interface TegakiEngineOptions {
|
|
413
425
|
text?: string;
|
|
@@ -437,6 +449,7 @@ declare class TegakiEngine {
|
|
|
437
449
|
private _canvasEl;
|
|
438
450
|
private _overlayEl;
|
|
439
451
|
private _canvasFallbackEl;
|
|
452
|
+
private _maskCanvas;
|
|
440
453
|
private _text;
|
|
441
454
|
private _font;
|
|
442
455
|
private _timeControl;
|
|
@@ -524,4 +537,4 @@ declare function buildChildren<T>(options: TegakiEngineOptions, h: CreateElement
|
|
|
524
537
|
declare function domCreateElement(tag: string, props: Record<string, any>, ...children: (HTMLElement | string)[]): HTMLElement;
|
|
525
538
|
//#endregion
|
|
526
539
|
export { LineCap as A, TegakiSingletonEffectName as B, resolveEffects as C, CSSLength as D, COMPATIBLE_BUNDLE_VERSIONS as E, TegakiEffectConfigs as F, TegakiEffectName as I, TegakiEffects as L, Point as M, Stroke$1 as N, FontOutput as O, TegakiBundle as P, TegakiGlyphData as R, ResolvedEffect as S, BUNDLE_VERSION as T, TimedPoint as V, computeTimeline as _, CreateElementFn as a, ensureFontFace as b, TimeControlMode as c, getBundle as d, registerBundle as f, TimelineEntry as g, TimelineConfig as h, TegakiEngine as i, PathCommand as j, GlyphData as k, TimeControlProp as l, Timeline as m, buildRootProps as n, TegakiEngineOptions as o, resolveBundle as p, domCreateElement as r, TegakiQuality as s, buildChildren as t, createBundle as u, TextLayout as v, BBox as w, drawGlyph as x, computeTextLayout as y, TegakiMultiEffectName as z };
|
|
527
|
-
//# sourceMappingURL=index-
|
|
540
|
+
//# sourceMappingURL=index-e-RN9Gi3.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as LineCap, B as TegakiSingletonEffectName, C as resolveEffects, D as CSSLength, E as COMPATIBLE_BUNDLE_VERSIONS, F as TegakiEffectConfigs, I as TegakiEffectName, L as TegakiEffects, M as Point, N as Stroke, O as FontOutput, P as TegakiBundle, R as TegakiGlyphData, S as ResolvedEffect, T as BUNDLE_VERSION, V as TimedPoint, _ as computeTimeline, a as CreateElementFn, b as ensureFontFace, c as TimeControlMode, d as getBundle, f as registerBundle, g as TimelineEntry, h as TimelineConfig, i as TegakiEngine, j as PathCommand, k as GlyphData, l as TimeControlProp, m as Timeline, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as TextLayout, w as BBox, x as drawGlyph, y as computeTextLayout, z as TegakiMultiEffectName } from "./index-
|
|
2
|
-
import { n as TegakiRendererHandle, r as TegakiRendererProps, t as TegakiRenderer } from "./index-
|
|
1
|
+
import { A as LineCap, B as TegakiSingletonEffectName, C as resolveEffects, D as CSSLength, E as COMPATIBLE_BUNDLE_VERSIONS, F as TegakiEffectConfigs, I as TegakiEffectName, L as TegakiEffects, M as Point, N as Stroke, O as FontOutput, P as TegakiBundle, R as TegakiGlyphData, S as ResolvedEffect, T as BUNDLE_VERSION, V as TimedPoint, _ as computeTimeline, a as CreateElementFn, b as ensureFontFace, c as TimeControlMode, d as getBundle, f as registerBundle, g as TimelineEntry, h as TimelineConfig, i as TegakiEngine, j as PathCommand, k as GlyphData, l as TimeControlProp, m as Timeline, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as TextLayout, w as BBox, x as drawGlyph, y as computeTextLayout, z as TegakiMultiEffectName } from "./index-e-RN9Gi3.mjs";
|
|
2
|
+
import { n as TegakiRendererHandle, r as TegakiRendererProps, t as TegakiRenderer } from "./index-BwhATGJw.mjs";
|
|
3
3
|
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, TegakiRenderer, TegakiRendererHandle, TegakiRendererProps, TegakiSingletonEffectName, TextLayout, TimeControlMode, TimeControlProp, TimedPoint, Timeline, TimelineConfig, TimelineEntry, buildChildren, buildRootProps, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, getBundle, registerBundle, resolveBundle, resolveEffects };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as createBundle, c as resolveBundle, d as computeTimeline, f as computeTextLayout, g as resolveEffects, i as domCreateElement, l as BUNDLE_VERSION, m as drawGlyph, n as buildChildren, o as getBundle, p as ensureFontFace, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS } from "./core-
|
|
2
|
-
import { t as TegakiRenderer } from "./react-
|
|
1
|
+
import { a as createBundle, c as resolveBundle, d as computeTimeline, f as computeTextLayout, g as resolveEffects, i as domCreateElement, l as BUNDLE_VERSION, m as drawGlyph, n as buildChildren, o as getBundle, p as ensureFontFace, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS } from "./core-Ds9ohwR7.mjs";
|
|
2
|
+
import { t as TegakiRenderer } from "./react-D3lP9bU2.mjs";
|
|
3
3
|
export { BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, TegakiEngine, TegakiRenderer, buildChildren, buildRootProps, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, getBundle, registerBundle, resolveBundle, resolveEffects };
|
package/dist/react/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as LineCap, B as TegakiSingletonEffectName, C as resolveEffects, D as CSSLength, E as COMPATIBLE_BUNDLE_VERSIONS, F as TegakiEffectConfigs, I as TegakiEffectName, L as TegakiEffects, M as Point, N as Stroke, O as FontOutput, P as TegakiBundle, R as TegakiGlyphData, S as ResolvedEffect, T as BUNDLE_VERSION, V as TimedPoint, _ as computeTimeline, a as CreateElementFn, b as ensureFontFace, c as TimeControlMode, d as getBundle, f as registerBundle, g as TimelineEntry, h as TimelineConfig, i as TegakiEngine, j as PathCommand, k as GlyphData, l as TimeControlProp, m as Timeline, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as TextLayout, w as BBox, x as drawGlyph, y as computeTextLayout, z as TegakiMultiEffectName } from "../index-
|
|
2
|
-
import { n as TegakiRendererHandle, r as TegakiRendererProps, t as TegakiRenderer } from "../index-
|
|
1
|
+
import { A as LineCap, B as TegakiSingletonEffectName, C as resolveEffects, D as CSSLength, E as COMPATIBLE_BUNDLE_VERSIONS, F as TegakiEffectConfigs, I as TegakiEffectName, L as TegakiEffects, M as Point, N as Stroke, O as FontOutput, P as TegakiBundle, R as TegakiGlyphData, S as ResolvedEffect, T as BUNDLE_VERSION, V as TimedPoint, _ as computeTimeline, a as CreateElementFn, b as ensureFontFace, c as TimeControlMode, d as getBundle, f as registerBundle, g as TimelineEntry, h as TimelineConfig, i as TegakiEngine, j as PathCommand, k as GlyphData, l as TimeControlProp, m as Timeline, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as TextLayout, w as BBox, x as drawGlyph, y as computeTextLayout, z as TegakiMultiEffectName } from "../index-e-RN9Gi3.mjs";
|
|
2
|
+
import { n as TegakiRendererHandle, r as TegakiRendererProps, t as TegakiRenderer } from "../index-BwhATGJw.mjs";
|
|
3
3
|
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, TegakiRenderer, TegakiRendererHandle, TegakiRendererProps, TegakiSingletonEffectName, TextLayout, TimeControlMode, TimeControlProp, TimedPoint, Timeline, TimelineConfig, TimelineEntry, buildChildren, buildRootProps, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, getBundle, registerBundle, resolveBundle, resolveEffects };
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as createBundle, c as resolveBundle, d as computeTimeline, f as computeTextLayout, g as resolveEffects, i as domCreateElement, l as BUNDLE_VERSION, m as drawGlyph, n as buildChildren, o as getBundle, p as ensureFontFace, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS } from "../core-
|
|
2
|
-
import { t as TegakiRenderer } from "../react-
|
|
1
|
+
import { a as createBundle, c as resolveBundle, d as computeTimeline, f as computeTextLayout, g as resolveEffects, i as domCreateElement, l as BUNDLE_VERSION, m as drawGlyph, n as buildChildren, o as getBundle, p as ensureFontFace, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS } from "../core-Ds9ohwR7.mjs";
|
|
2
|
+
import { t as TegakiRenderer } from "../react-D3lP9bU2.mjs";
|
|
3
3
|
export { BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, TegakiEngine, TegakiRenderer, buildChildren, buildRootProps, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, getBundle, registerBundle, resolveBundle, resolveEffects };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h as coerceToString, t as TegakiEngine } from "./core-
|
|
1
|
+
import { h as coerceToString, t as TegakiEngine } from "./core-Ds9ohwR7.mjs";
|
|
2
2
|
import { createElement, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
3
3
|
//#region src/react/TegakiRenderer.tsx
|
|
4
4
|
function reactCreateElement(tag, props, ...children) {
|
|
@@ -86,4 +86,4 @@ function TegakiRenderer(props) {
|
|
|
86
86
|
//#endregion
|
|
87
87
|
export { TegakiRenderer as t };
|
|
88
88
|
|
|
89
|
-
//# sourceMappingURL=react-
|
|
89
|
+
//# sourceMappingURL=react-D3lP9bU2.mjs.map
|